Example #1
0
def Closed_Field(*args, **kwargs):
    """
    Function to see if a field line is closed

    Either MagEphem or pos and date must be specified

    Parameters
    ----------
    MagEphem : Lgm_MagEphemInfo, optional
        If a populated Lgm_MagEphemInfo class is passed in the data is pulled
        from it
    pos : list, optional
        3-element list of the position in system coord_system
    date : datetime, optional
        date and time of the calculation
    height : float, optional
        height above the earth to consider a particle lost [km], default=100
    tol1 : float, optional
        TODO what do I set?  default=0.01
    tol2 : float, optional
        TODO what do I set?  default=1e-7
    bfield : str, optional
        The magnetic field model to use, default=Lgm_B_T89
    Kp : int, optional
        Kp index for the calculation, default=2
    coord_system : str
        the coordinate system of the input position, default=GSM
    extended_out : bool
        switch on extended vs regular output, see examples for details,
        default=False

    Returns
    -------
    out : str
        a string with the open of closed value for the input
            - LGM_OPEN_IMF
            - LGM_CLOSED
            - LGM_OPEN_N_LOBE
            - LGM_OPEN_S_LOBE
            - LGM_INSIDE_EARTH
            - LGM_TARGET_HEIGHT_UNREACHABLE

    Examples
    --------
    >>> from lgmpy import Closed_Field
    >>> import datetime
    >>> Closed_Field([3,1,0], datetime.datetime(2000, 12, 3))
    'LGM_CLOSED'
    >>> Closed_Field([6,1,12], datetime.datetime(2000, 12, 3))
    'LGM_OPEN_IMF'
    >>> Closed_Field([-16,1,5], datetime.datetime(2000, 12, 3))
    'LGM_OPEN_N_LOBE'
    """

    defaults = {
        'height': 100,
        'tol1': 0.01,
        'tol2': 1e-7,
        'bfield': 'Lgm_B_T89',
        'Kp': 2,
        'coord_system': 'GSM',
        'extended_out': False
    }

    #replace missing kwargs with defaults
    for dkey in defaults:
        if dkey not in kwargs:
            kwargs[dkey] = defaults[dkey]

    northern = Lgm_Vector.Lgm_Vector()
    southern = Lgm_Vector.Lgm_Vector()
    minB = Lgm_Vector.Lgm_Vector()
    #check for call w/ Lgm_MagModelInfo
    if len(args) == 1:
        MagEphemInfo = args[0]
        try:
            mmi = MagEphemInfo.LstarInfo.contents.mInfo.contents
        except AttributeError:
            raise (RuntimeError('Incorrect arguments specified'))
        dum = [MagEphemInfo.P.x, MagEphemInfo.P.y, MagEphemInfo.P.z]
        position = Lgm_Vector.Lgm_Vector(*dum)
    elif len(args) == 2:
        # input checking
        if kwargs['coord_system'] != 'GSM':
            #    raise(NotImplementedError('Different coord systems are not yet ready to use') )
            pos = magcoords.coordTrans(args[0], args[1],
                                       kwargs['coord_system'], 'GSM')
        else:
            pos = args[0]

        # could consider a Lgm_MagModelInfo param to use an existing one
        mmi = Lgm_MagModelInfo.Lgm_MagModelInfo()
        mmi.Kp = kwargs['Kp']
        try:
            Bfield_dict[kwargs['bfield']](pointer(mmi))
        except KeyError:
            raise (NotImplementedError("Only Bfield=%s currently supported" %
                                       Bfield_dict.keys()))

        datelong = Lgm_CTrans.dateToDateLong(args[1])
        utc = Lgm_CTrans.dateToFPHours(args[1])
        Lgm_Set_Coord_Transforms(datelong, utc,
                                 mmi.c)  # dont need pointer as it is one

        try:
            position = Lgm_Vector.Lgm_Vector(*pos)
        except TypeError:
            raise (TypeError('position must be an iterable'))
    else:
        raise (RuntimeError('Incorrect number of arguments specified'))

    ans = Lgm_Trace(pointer(position), pointer(southern), pointer(northern),
                    pointer(minB), kwargs['height'], kwargs['tol1'],
                    kwargs['tol2'], pointer(mmi))

    L = numpy.nan  #default to this is field not closed
    if ans == LGM_OPEN_IMF:
        retstr = 'LGM_OPEN_IMF'
    elif ans == LGM_CLOSED:
        retstr = 'LGM_CLOSED'
        L = _simpleL(northern, mmi)
    elif ans == LGM_OPEN_N_LOBE:
        retstr = 'LGM_OPEN_N_LOBE'
    elif ans == LGM_OPEN_S_LOBE:
        retstr = 'LGM_OPEN_S_LOBE'
    elif ans == LGM_INSIDE_EARTH:
        retstr = 'LGM_INSIDE_EARTH'
    elif ans == LGM_TARGET_HEIGHT_UNREACHABLE:
        retstr = 'LGM_TARGET_HEIGHT_UNREACHABLE'
    elif ans == LGM_BAD_TRACE:
        retstr = 'LGM_BAD_TRACE'
    if kwargs['extended_out']:
        return retstr, northern.tolist(), southern.tolist(), minB.tolist(), L
    else:
        return retstr
Example #2
0
def get_Lstar(pos, date, alpha = 90.,
                  Kp = 2, coord_system='GSM',
                  Bfield = 'Lgm_B_OP77',
                  LstarThresh = 10.0,  # beyond this Lsimple don't compute Lstar
                  extended_out = False,
                  LstarQuality = 3):
    """
    This method does all the work for the calculation of Lstar.

    There are many options to set and a lot of output.  We have tried to describe
    it well here but certainly have missed something, if you can't understand something
    please contact the authors.

    Parameters
    ==========
    pos : list or array
        The position that Lstar should be calculated for
    date : datetime
        The date and time that Lstar should be calculated for
    alpha : float, optional
        Local pitch angle at ``pos`` to calculate Lstar, default (90)
    Kp : int, optional
        The Kp index to pass to the magnetic field model, used in T89, ignored
        in OP77, default (2)
    coord_system : str, optional
        The coordinate system of the input position, default (GSM)
    Bfield : str, optional
        The Magnetic field model to use for the calculation, default (Lgm_B_T89)
    LstarThresh : float, optional
        The calculation computes a simple L value and does not calculation Lstar
        if simple L is beyond this, default (10)
    extended_out : bool, optional
        Keyword that enables the output of significantly more information into
        the Lstar_Data output, default (False).  The extra information allows
        the points along the Lstar trace to be used since Lstar is the same at
        each point along the trace.
    LstarQuality : int
        The quality flag for the integrators in the calculation, this can have
        serious impact on run speed and Lstar value

    Returns
    =======
    out : Lstar_Data
        Returns a datamodel object that contains all the information for the run.
        See examples for the contents of this object for extended_out=False and True

    Examples
    ========
    >>> from lgmpy import Lstar
    >>> import datetime
    >>> dat = Lstar.get_Lstar([1,2,0], datetime.datetime(2000, 1, 2), extended_out=False)
    >>> print(dat)
    +
    |____90.0 (spacepy.datamodel.SpaceData [8])
         |____Bmin (spacepy.datamodel.dmarray ())
             :|____units (str [2])
         |____Bmirror (spacepy.datamodel.dmarray ())
             :|____coord_system (str [3])
             :|____units (str [2])
         |____I (spacepy.datamodel.dmarray ())
         |____LHilton (float)
         |____LMcIlwain (float)
         |____Lsimple (spacepy.datamodel.dmarray (1,))
         |____Lstar (spacepy.datamodel.dmarray (1,))
             :|____info (str [4])
         |____Pmin (spacepy.datamodel.dmarray (3,))
             :|____coord_system (str [3])
             :|____units (str [3])
    |____Bcalc (spacepy.datamodel.dmarray (3,))
        :|____Kp (int)
        :|____coord_system (str [3])
        :|____model (str [10])
        :|____units (str [2])
    |____Epoch (spacepy.datamodel.dmarray (1,))
    |____Kp (spacepy.datamodel.dmarray (1,))
    |____MLT (spacepy.datamodel.dmarray ())
        :|____coord_system (str [5])
    |____position (dict [1])
         |____GSM (spacepy.datamodel.dmarray (3,))
             :|____units (str [2])
    ...

    The data object is a dictionary with keys:
        - 90.0 : this is the pitch angle specified (multiple can be specified)
            - Bmin : the minimum B for that pitch angle and position
            - BMirror : the position of the mirror point for that position
            - I : the I value for that position
            - LHilton : L value calculated with the Hilton approximation
            - LMcIlwain: L values calculated with the McIlwain formula
            - Lsimple : a simple L value
            - Lstar : the value of Lstar for that position and pitch angle
            - Pmin : TODO what is Pmin?
        - Bcalc : information about the magnetic field model
        - Epoch : the time of the calculation
        - Kp : Kp used for the calculation
        - MLT : MLT value for the position and coord_system
        - position : the position of the calculation
            - GSM : the value in this system, if conversions are done they all appear here

    >>> from lgmpy import Lstar
    >>> import datetime
    >>> dat = Lstar.get_Lstar([1,2,0], datetime.datetime(2000, 1, 2), extended_out=True)
    >>> print(dat)
    +
    |____90.0 (spacepy.datamodel.SpaceData [21])
        :|____Calc_Time (float)
         |____Bmag (numpy.ndarray (100, 1000))
         |____Bmin (spacepy.datamodel.dmarray ())
             :|____units (str [2])
         |____Bmirror (spacepy.datamodel.dmarray ())
             :|____coord_system (str [3])
             :|____units (str [2])
         |____I (spacepy.datamodel.dmarray ())
         |____LHilton (float)
         |____LMcIlwain (float)
         |____Lsimple (spacepy.datamodel.dmarray (1,))
         |____Lstar (spacepy.datamodel.dmarray (1,))
             :|____info (str [4])
         |____Pmin (spacepy.datamodel.dmarray (3,))
             :|____coord_system (str [3])
             :|____units (str [3])
         |____ShellEllipsoidFootprint_Pn (numpy.ndarray (100,))
         |____ShellEllipsoidFootprint_Ps (numpy.ndarray (100,))
         |____ShellI (spacepy.datamodel.dmarray (100,))
         |____ShellMirror_Pn (numpy.ndarray (100,))
         |____ShellMirror_Ps (numpy.ndarray (100,))
         |____ShellMirror_Sn (float)
         |____ShellMirror_Ss (float)
         |____nFieldPnts (numpy.ndarray (100,))
         |____s_gsm (numpy.ndarray (100, 1000))
         |____x_gsm (numpy.ndarray (100, 1000))
         |____y_gsm (numpy.ndarray (100, 1000))
         |____z_gsm (numpy.ndarray (100, 1000))
    |____Bcalc (spacepy.datamodel.dmarray (3,))
        :|____Kp (int)
        :|____coord_system (str [3])
        :|____model (str [10])
        :|____units (str [2])
    |____Epoch (spacepy.datamodel.dmarray (1,))
    |____Kp (spacepy.datamodel.dmarray (1,))
    |____MLT (spacepy.datamodel.dmarray ())
        :|____coord_system (str [5])
    |____position (dict [1])
         |____GSM (spacepy.datamodel.dmarray (3,))
             :|____units (str [2])
    ...

    The data object is a dictionary with keys:
        - 90.0 : this is the pitch angle specified (multiple can be specified)
            - Calc_Time : the clock time that the calculation took to run
            - Bmag : the magnitude of B along the Lstar trace
            - Bmin : the minimum B for that pitch angle and position
            - BMirror : the position of the mirror point for that position
            - I : the I value for that position
            - LHilton : L value calculated with the Hilton approximation
            - LMcIlwain: L values calculated with the McIlwain formula
            - Lsimple : a simple L value
            - Lstar : the value of Lstar for that position and pitch angle
            - Pmin : TODO what am I?
            - ShellEllipsoidFootprint_Pn : TODO what am I?
            - ShellEllipsoidFootprint_Ps : TODO what am I?
            - ShellI : TODO what am I?
            - ShellMirror_Pn : TODO what am I?
            - ShellMirror_Ps : TODO what am I?
            - ShellMirror_Sn : TODO what am I?
            - ShellMirror_Ss : TODO what am I?
            - nFieldPnts : TODO what am I?
            - s_gsm : TODO what am I?
            - x_gsm : TODO what am I?
            - y_gsm : TODO what am I?
            - z_gsm : TODO what am I?
        - Bcalc : information about the magnetic field model
        - Epoch : the time of the calculation
        - Kp : Kp used for the calculation
        - MLT : MLT value for the position and coord_system
        - position : the position of the calculation
            - GSM : the value in this system, if conversions are done they all appear here


    """

    # setup a datamodel object to hold the answer
    ans = Lstar_Data()

    # change datetime to Lgm Datelong and UTC
    try:
        datelong = Lgm_CTrans.dateToDateLong(date)
        utc = Lgm_CTrans.dateToFPHours(date)
    except AttributeError:
        raise(TypeError("Date must be a datetime object"))
    else:
        ans['Epoch'] = datamodel.dmarray([date])

    # pitch angles to calculate
    try:
        Alpha = list(alpha)
    except TypeError:
        Alpha = [alpha]

    # required setup
    MagEphemInfo = Lgm_MagEphemInfo.Lgm_MagEphemInfo(len(Alpha), 0)

    # setup a shortcut to MagModelInfo
    mmi = MagEphemInfo.LstarInfo.contents.mInfo.contents
    Lgm_Set_Coord_Transforms( datelong, utc, mmi.c) # dont need pointer as it is one

    # convert to **GSM**
    if coord_system == 'GSM':
        try:
            Pgsm = Lgm_Vector.Lgm_Vector(*pos)
        except TypeError:
            raise(TypeError("Position must be listlike" ) )
        ans['position']['GSM'] = datamodel.dmarray(pos, attrs={'units':'Re'})
    elif coord_system == 'SM':
        try:
            Psm = Lgm_Vector.Lgm_Vector(*pos)
        except TypeError:
            raise(TypeError("Position must be listlike" ) )
        Pgsm = Lgm_Vector.Lgm_Vector()
        Lgm_Convert_Coords( pointer(Psm), pointer(Pgsm), SM_TO_GSM, mmi.c )
        ans['position']['SM'] = datamodel.dmarray(pos, attrs={'units':'Re'})
        ans['position']['GSM'] = datamodel.dmarray(Pgsm.tolist(), attrs={'units':'Re'})
    else:
        raise(NotImplementedError("Only GSM or SM input currently supported"))

    Pwgs = Lgm_Vector.Lgm_Vector()
    Pmlt = Lgm_Vector.Lgm_Vector()
    Lgm_Convert_Coords( pointer(Pgsm), pointer(Pwgs), GSM_TO_WGS84, mmi.c )
    Lgm_Convert_Coords( pointer(Pwgs), pointer(Pmlt), WGS84_TO_EDMAG, mmi.c )
    R, MLat, MLon, MLT = c_double(), c_double(), c_double(), c_double(),
    Lgm_EDMAG_to_R_MLAT_MLON_MLT( pointer(Pmlt),  pointer(R), pointer(MLat), pointer(MLon),
        pointer(MLT), mmi.c)
    ans['MLT'] = datamodel.dmarray(MLT.value, attrs={'coord_system': 'EDMAG'})

    # save Kp
    # TODO maybe add some Kp checking
    ans['Kp'] = datamodel.dmarray([Kp])

    # Set the LstarQuality, TODO add a comment here on what each does
    MagEphemInfo.LstarQuality = LstarQuality

    # L* in one place is L* in lots of places (for GPS set to False)
    MagEphemInfo.SaveShellLines = extended_out
    # TODO maybe not hardcoded, but for now its fine
    MagEphemInfo.LstarInfo.contents.VerbosityLevel = 0
    MagEphemInfo.LstarInfo.contents.mInfo.contents.VerbosityLevel = 0

    #MagEphemInfo->LstarInfo->mInfo->Bfield        = Lgm_B_T89;
    #MagEphemInfo->LstarInfo->mInfo->Bfield        = Lgm_B_cdip;
    #MagEphemInfo->LstarInfo->mInfo->Bfield        = Lgm_B_OP77;
    #MagEphemInfo->LstarInfo->mInfo->InternalModel = LGM_CDIP;

    ## decide which field model to use, this is a keyword
    try:
        Bfield_dict[Bfield](MagEphemInfo.LstarInfo.contents.mInfo)
    except KeyError:
        raise(NotImplementedError("Only Bfield=%s currently supported" % Bfield_dict.keys()))

    # Save Date, UTC to MagEphemInfo structure ** is this needed?
    MagEphemInfo.Date   = datelong
    MagEphemInfo.UTC    = utc
    MagEphemInfo.LstarInfo.contents.mInfo.contents.Kp = Kp

    # Save nAlpha, and Alpha array to MagEphemInfo structure
    MagEphemInfo.nAlpha = len(Alpha)
    for i in range(len(Alpha)):
        MagEphemInfo.Alpha[i] = Alpha[i]

    # Set Tolerances
    Lgm_SetLstarTolerances(LstarQuality, 24, MagEphemInfo.LstarInfo )
    # *  Blocal at sat location
    MagEphemInfo.P = Pgsm

    Bvec = Lgm_Vector.Lgm_Vector()
    # Get B at the point in question
    MagEphemInfo.LstarInfo.contents.mInfo.contents.Bfield(pointer(Pgsm), pointer(Bvec),
                                                          MagEphemInfo.LstarInfo.contents.mInfo)
    ans['Bcalc'] = datamodel.dmarray(Bvec.tolist(), attrs={'units':'nT'})
    ans['Bcalc'].attrs['model'] = Bfield
    ans['Bcalc'].attrs['Kp'] = Kp
    ans['Bcalc'].attrs['coord_system'] = 'GSM'

    # save its magnitude in the structure
    MagEphemInfo.B = Bvec.magnitude()

    # check and see if the field line is closed before doing much work
    trace, northern, southern, minB, Lsimple = Closed_Field.Closed_Field(MagEphemInfo, extended_out=True)

    # presetup the ans[Angle] so that it can be filled correctly
    for pa in Alpha:
        ans[pa] = datamodel.SpaceData()
        ans[pa]['Lsimple'] = datamodel.dmarray([Lsimple])
        #sets up nans in case of Lstar failure
        ans[pa]['I'] = datamodel.dmarray([numpy.nan])
        ans[pa]['Lstar'] = datamodel.dmarray([numpy.nan], attrs={'info':trace})
        ans[pa]['LMcIlwain'] = datamodel.dmarray([numpy.nan])
        ans[pa]['LHilton'] = datamodel.dmarray([numpy.nan])
        ans[pa]['Bmin'] = datamodel.dmarray([numpy.nan], attrs={'units':'nT'})
        ans[pa]['Bmirror'] = datamodel.dmarray([numpy.nan], attrs={'units':'nT'})
    
    if trace != 'LGM_CLOSED':
        return ans
        # if this is not LGM_CLOSED then don't both with any pitch angle?  true?

    #  Save field-related quantities for each Pitch Angle.
    MagEphemInfo.Pmin = Lgm_Vector.Lgm_Vector(*minB)
    MagEphemInfo.Bmin = MagEphemInfo.LstarInfo.contents.mInfo.contents.Bmin
    ans[pa]['Pmin'] = datamodel.dmarray(minB, attrs={'units':'R_E'})
    ans[pa]['Pmin'].attrs['coord_system'] = 'GSM'
    ans[pa]['Bmin'] = datamodel.dmarray(MagEphemInfo.Bmin, attrs={'units':'nT'})
    # LOOP OVER PITCH ANGLES
    for i, pa in enumerate(Alpha):

        tnow = datetime.datetime.now()
        PreStr = MagEphemInfo.LstarInfo.contents.PreStr
        PostStr = MagEphemInfo.LstarInfo.contents.PostStr

        # ***********************************************
        # *** not sure I fully understand this chunk, B at the mirror point*****
        # Set Pitch Angle, sin, sin^2, and Bmirror
        sa = math.sin( pa*RadPerDeg )
        sa2 = sa*sa

        # print("{0}Computing L* for Pitch Angle: Alpha[{1}] = {2} Date: {3}   UTC: {4}   Lsimple = {5:4.3}{6}\n").format(PreStr, i, MagEphemInfo.Alpha[i], date, utc, Lsimple, PostStr )
        
        if sa2!=0: #non-zero pitch angle
            MagEphemInfo.LstarInfo.contents.mInfo.contents.Bm = MagEphemInfo.B/sa2
        else:
            continue
        ans[pa]['Bmirror'] = datamodel.dmarray(MagEphemInfo.LstarInfo.contents.mInfo.contents.Bm, attrs={'units':'nT'})
        ans[pa]['Bmirror'].attrs['coord_system'] = 'GSM'
        # ***********************************************
        
        # I tink this is already done
        # Lgm_Set_Coord_Transforms( Date, UTC, MagEphemInfo.LstarInfo.contents.mInfo.contents.c )

        MagEphemInfo.LstarInfo.contents.PitchAngle = pa
        MagEphemInfo.Bm[i] = MagEphemInfo.LstarInfo.contents.mInfo.contents.Bm
        # Compute L*
        if Lsimple < LstarThresh:
            Ls_vec = Lgm_Vector.Lgm_Vector(*minB)

            LS_Flag = Lgm_Lstar( pointer(Ls_vec), MagEphemInfo.LstarInfo)

            lstarinf = MagEphemInfo.LstarInfo.contents #shortcut
            MagEphemInfo.LHilton.contents.value = LFromIBmM_Hilton(c_double(lstarinf.I[0]),
                                                c_double(MagEphemInfo.Bm[i]),
                                                c_double(lstarinf.mInfo.contents.c.contents.M_cd))
            ans[pa]['LHilton'] = MagEphemInfo.LHilton.contents.value
            MagEphemInfo.LMcIlwain.contents.value = LFromIBmM_McIlwain(c_double(lstarinf.I[0]),
                                                c_double(MagEphemInfo.Bm[i]),
                                                c_double(lstarinf.mInfo.contents.c.contents.M_cd))
            ans[pa]['LMcIlwain'] = MagEphemInfo.LMcIlwain.contents.value
            if LS_Flag == -2: # mirror below southern hemisphere mirror alt
                ans[pa]['Lstar'] = datamodel.dmarray([numpy.nan], attrs={'info':'S_LOSS'})
            elif LS_Flag == -1: # mirror below northern hemisphere mirror alt
                ans[pa]['Lstar'] = datamodel.dmarray([numpy.nan], attrs={'info':'N_LOSS'})
            elif LS_Flag >= 0: # valid calc
                ans[pa]['Lstar'] = datamodel.dmarray([lstarinf.LS], attrs={'info':'GOOD'}) # want better word?

            MagEphemInfo.Lstar[i] = lstarinf.LS

            # Save results to the MagEphemInfo structure.
            MagEphemInfo.nShellPoints[i] = lstarinf.nPnts
            ## pull all this good extra info into numpy arrays
            ans[pa]['I'] = datamodel.dmarray(lstarinf.I[0])

            if extended_out:
                ans[pa]['ShellI'] = \
                    datamodel.dmarray(numpy.ctypeslib.ndarray([len(lstarinf.I)],
                                            dtype=c_double, buffer=lstarinf.I) ).copy()
                ans[pa]['ShellEllipsoidFootprint_Pn'] = \
                    numpy.ctypeslib.ndarray(len(lstarinf.Ellipsoid_Footprint_Pn),
                                            dtype=c_double,
                                            buffer=lstarinf.Ellipsoid_Footprint_Pn).copy()
                ans[pa]['ShellEllipsoidFootprint_Ps'] = \
                    numpy.ctypeslib.ndarray(len(lstarinf.Ellipsoid_Footprint_Ps),
                                            dtype=c_double,
                                            buffer=lstarinf.Ellipsoid_Footprint_Ps).copy()

                ans[pa]['ShellMirror_Pn'] = \
                    numpy.ctypeslib.ndarray(len(lstarinf.Mirror_Pn),
                                            dtype=c_double,
                                            buffer=lstarinf.Mirror_Pn).copy()
                ans[pa]['ShellMirror_Ps'] = \
                    numpy.ctypeslib.ndarray(len(lstarinf.Mirror_Ps),
                                            dtype=c_double,
                                            buffer=lstarinf.Mirror_Ps).copy()
                ans[pa]['ShellMirror_Ss'] = lstarinf.mInfo.contents.Sm_South
                ans[pa]['ShellMirror_Sn'] = lstarinf.mInfo.contents.Sm_North
                ans[pa]['nFieldPnts'] = \
                    numpy.ctypeslib.ndarray(len(lstarinf.nFieldPnts),
                                            dtype=c_int,
                                            buffer=lstarinf.nFieldPnts).copy()

                ans[pa]['s_gsm'] = \
                    numpy.ctypeslib.ndarray([len(lstarinf.s_gsm),
                                             len(lstarinf.s_gsm[0])],
                                            dtype=c_double,
                                            buffer=lstarinf.s_gsm).copy()
                ans[pa]['Bmag'] = \
                    numpy.ctypeslib.ndarray([len(lstarinf.Bmag),
                                             len(lstarinf.Bmag[0])],
                                            dtype=c_double,
                                            buffer=lstarinf.Bmag).copy()
                ans[pa]['x_gsm'] = \
                    numpy.ctypeslib.ndarray([len(lstarinf.x_gsm),
                                             len(lstarinf.x_gsm[0])],
                                            dtype=c_double,
                                            buffer=lstarinf.x_gsm).copy()
                ans[pa]['y_gsm'] = \
                    numpy.ctypeslib.ndarray([len(lstarinf.y_gsm),
                                             len(lstarinf.y_gsm[0])],
                                            dtype=c_double,
                                            buffer=lstarinf.y_gsm).copy()
                ans[pa]['z_gsm'] = \
                    numpy.ctypeslib.ndarray([len(lstarinf.z_gsm),
                                             len(lstarinf.z_gsm[0])],
                                            dtype=c_double,
                                            buffer=lstarinf.z_gsm).copy()
                delT = datetime.datetime.now() - tnow
                ans[pa].attrs['Calc_Time'] = delT.seconds + delT.microseconds/1e6

    Lgm_FreeMagEphemInfo_Children(pointer(MagEphemInfo))
    return ans
Example #3
0
def Lvalue(*args, **kwargs):
    '''
    Function to return the L-value of a position using either McIlwain or Hilton
    approximation

    Parameters
    ==========
    pos : list
        3-element position int he specified coord_system
    time : datetime
        date and time for the calculation
    alpha : float, optional
        the pitch angle for the L calculation, default=90
    Bfield : str, optional
        the magnetic field model to use, default=Lgm_B_T89
    method : str, optional
        the L-value formula to use, McIlwain or Hilton, default=Hilton
    Kp : int
        Kp index value for the calculation
    coord_system : str
        the input coordinate system, default=GSM
    extended_out : bool
        keyword specifying short or extended output, default=False

    Returns
    =======
    out : dict
        dictionary of the values, see examples for documentation of dictionary

    Examples
    ========
    >>> from lgmpy import magcoords
    >>> import datetime
    >>> magcoords.Lvalue([3, 0, 1], datetime.datetime(2000, 1, 1), extended_out=False)
    {'I': 0.2434969602..., 'L': 3.195481841...}

    The ``extended_out=False`` output is:
        - I : I value at the given point
        - L : L value at the given point

    >>> from lgmpy import magcoords
    >>> import datetime
    >>> magcoords.Lvalue([3, 0, 1], datetime.datetime(2000, 1, 1), extended_out=True)
    {'Blocal': 1024.1142193703838,
    'Bmin': 921.8869150...,
    'Bmirr': 1024.1142193...,
    'I': 0.24349696021...,
    'L': 3.1954818410...,
    'M': 30119.614287...}

    The ``extended_out=True`` output is:
        - I : I value at the given point
        - L : L value at the given point
        - Bmin : minimum B at the input position (nT)
        - Bmirror : mirror B at the input position (nT)
        - M : TODO what exactly and I, magnetic moment?
    '''
    defaults = {'alpha': 90.,
                'Bfield': 'Lgm_B_T89',
                'method': 'Hilton',
                'Kp': 2,
                'coord_system': 'GSM',
                'extended_out': False}

    #replace missing kwargs with defaults
    for dkey in defaults:
        if dkey not in kwargs:
            kwargs[dkey] = defaults[dkey]

    method_dict = {'Hilton': 1, 'McIlwain': 0}

    # change datetime to Lgm Datelong and UTC
    mInfo = Lgm_MagModelInfo.Lgm_MagModelInfo()
    mInfo.Kp = kwargs['Kp']
    try:
        Bfield_dict[kwargs['Bfield']](pointer(mInfo))
    except KeyError:
        raise(NotImplementedError("Only Bfield=%s currently supported" % Bfield_dict.keys()))
    try:
        datelong = Lgm_CTrans.dateToDateLong(args[1])
        utc = Lgm_CTrans.dateToFPHours(args[1])
        Lgm_Set_Coord_Transforms( datelong, utc, mInfo.c) # dont need pointer as it is one
    except AttributeError:
        raise(TypeError("Date must be a datetime object"))
    #else:
        #ans['Epoch'] = datamodel.dmarray([args[1]])

    if kwargs['coord_system'] != 'GSM':
        in_sys = kwargs['coord_system']
        Pout = coordTrans(args[0], args[1], in_sys, 'GSM', de_eph=False)
        Pgsm = Lgm_Vector.Lgm_Vector(*Pout)
    else:
        Pgsm = Lgm_Vector.Lgm_Vector(*args[0])

    Iout = c_double()
    Bm = c_double()
    M = c_double()
    ans = Lgm_McIlwain_L(datelong, utc, pointer(Pgsm), kwargs['alpha'],
                            method_dict[kwargs['method']],
                            pointer(Iout), pointer(Bm), pointer(M),
                            pointer(mInfo))

    #TODO: decide on format for output -- perhaps use datamodel and have method as attribute?
    #maybe only return I for extended_out flag=True
    if kwargs['extended_out']:
        sunPos_GSM = coordTrans(mInfo.c.contents.Sun, args[1], 'MOD', kwargs['coord_system'])
        SunMlon = np.rad2deg(np.arctan2( sunPos_GSM[1], sunPos_GSM[0]))
        SunMlon += 180.0 # flip to midnight

        MLON = np.rad2deg(np.arctan2( Pgsm.y, Pgsm.x))
        if (MLON < 0.0):
            MLON += 360.0

        MLT = np.mod( (MLON-SunMlon)/15.0+24.0, 24.0 )
        return {'L': ans, 'I': Iout.value, 'Bmin': mInfo.Bmin, 'Blocal': mInfo.Blocal,
                'Bmirr': mInfo.Bm, 'M': M.value, 'MLon':MLON, 'MLT':MLT}
    else:
        return {'L': ans, 'I': Iout.value}
Example #4
0
def Closed_Field(*args, **kwargs):
    """
    Function to see if a field line is closed

    Either MagEphem or pos and date must be specified

    Parameters
    ----------
    MagEphem : Lgm_MagEphemInfo, optional
        If a populated Lgm_MagEphemInfo class is passed in the data is pulled
        from it
    pos : list, optional
        3-element list of the position in system coord_system
    date : datetime, optional
        date and time of the calculation
    height : float, optional
        height above the earth to consider a particle lost [km], default=100
    tol1 : float, optional
        TODO what do I set?  default=0.01
    tol2 : float, optional
        TODO what do I set?  default=1e-7
    bfield : str, optional
        The magnetic field model to use, default=Lgm_B_T89
    Kp : int, optional
        Kp index for the calculation, default=2
    coord_system : str
        the coordinate system of the input position, default=GSM
    extended_out : bool
        switch on extended vs regular output, see examples for details,
        default=False

    Returns
    -------
    out : str
        a string with the open of closed value for the input
            - LGM_OPEN_IMF
            - LGM_CLOSED
            - LGM_OPEN_N_LOBE
            - LGM_OPEN_S_LOBE
            - LGM_INSIDE_EARTH
            - LGM_TARGET_HEIGHT_UNREACHABLE

    Examples
    --------
    >>> from lgmpy import Closed_Field
    >>> import datetime
    >>> Closed_Field([3,1,0], datetime.datetime(2000, 12, 3))
    'LGM_CLOSED'
    >>> Closed_Field([6,1,12], datetime.datetime(2000, 12, 3))
    'LGM_OPEN_IMF'
    >>> Closed_Field([-16,1,5], datetime.datetime(2000, 12, 3))
    'LGM_OPEN_N_LOBE'
    """

    defaults = {'height': 100,
                'tol1': 0.01,
                'tol2': 1e-7,
                'bfield': 'Lgm_B_T89',
                'Kp': 2,
                'coord_system': 'GSM',
                'extended_out': False}

    #replace missing kwargs with defaults
    for dkey in defaults:
        if dkey not in kwargs:
            kwargs[dkey] = defaults[dkey]

    northern = Lgm_Vector.Lgm_Vector()
    southern = Lgm_Vector.Lgm_Vector()
    minB = Lgm_Vector.Lgm_Vector()
    #check for call w/ Lgm_MagModelInfo
    if len(args) == 1:
        MagEphemInfo = args[0]
        try:
            mmi = MagEphemInfo.LstarInfo.contents.mInfo.contents
        except AttributeError:
            raise(RuntimeError('Incorrect arguments specified'))
        dum = [MagEphemInfo.P.x, MagEphemInfo.P.y, MagEphemInfo.P.z]
        position = Lgm_Vector.Lgm_Vector(*dum)
    elif len(args) == 2:
        # input checking
        if kwargs['coord_system'] != 'GSM':
        #    raise(NotImplementedError('Different coord systems are not yet ready to use') )
            pos = magcoords.coordTrans(args[0], args[1], kwargs['coord_system'],'GSM')
        else:
            pos = args[0]
        
        # could consider a Lgm_MagModelInfo param to use an existing one
        mmi = Lgm_MagModelInfo.Lgm_MagModelInfo()
        mmi.Kp = kwargs['Kp']
        try:
            Bfield_dict[kwargs['bfield']](pointer(mmi))
        except KeyError:
            raise(NotImplementedError("Only Bfield=%s currently supported" % Bfield_dict.keys()))

        datelong = Lgm_CTrans.dateToDateLong(args[1])
        utc = Lgm_CTrans.dateToFPHours(args[1])
        Lgm_Set_Coord_Transforms( datelong, utc, mmi.c) # dont need pointer as it is one

        try:
            position = Lgm_Vector.Lgm_Vector(*pos)
        except TypeError:
            raise(TypeError('position must be an iterable') )
    else:
        raise(RuntimeError('Incorrect number of arguments specified'))

    ans = Lgm_Trace(pointer(position),
            pointer(southern),
            pointer(northern),
            pointer(minB), kwargs['height'], kwargs['tol1'], kwargs['tol2'], pointer(mmi) )

    L = numpy.nan #default to this is field not closed
    if ans == LGM_OPEN_IMF:
        retstr = 'LGM_OPEN_IMF'
    elif ans == LGM_CLOSED:
        retstr = 'LGM_CLOSED'
        L = _simpleL(northern, mmi)
    elif ans == LGM_OPEN_N_LOBE:
        retstr = 'LGM_OPEN_N_LOBE'
    elif ans == LGM_OPEN_S_LOBE:
        retstr = 'LGM_OPEN_S_LOBE'
    elif ans == LGM_INSIDE_EARTH:
        retstr = 'LGM_INSIDE_EARTH'
    elif ans == LGM_TARGET_HEIGHT_UNREACHABLE:
        retstr = 'LGM_TARGET_HEIGHT_UNREACHABLE'
    elif ans == LGM_BAD_TRACE:
        retstr = 'LGM_BAD_TRACE'
    if kwargs['extended_out']:
        return retstr, northern.tolist(), southern.tolist(), minB.tolist(), L
    else:
        return retstr
Example #5
0
def Lvalue(*args, **kwargs):
    '''
    Function to return the L-value of a position using either McIlwain or Hilton
    approximation

    Parameters
    ==========
    pos : list
        3-element position int he specified coord_system
    time : datetime
        date and time for the calculation
    alpha : float, optional
        the pitch angle for the L calculation, default=90
    Bfield : str, optional
        the magnetic field model to use, default=Lgm_B_T89
    method : str, optional
        the L-value formula to use, McIlwain or Hilton, default=Hilton
    Kp : int
        Kp index value for the calculation
    coord_system : str
        the input coordinate system, default=GSM
    extended_out : bool
        keyword specifying short or extended output, default=False

    Returns
    =======
    out : dict
        dictionary of the values, see examples for documentation of dictionary

    Examples
    ========
    >>> from lgmpy import magcoords
    >>> import datetime
    >>> magcoords.Lvalue([3, 0, 1], datetime.datetime(2000, 1, 1), extended_out=False)
    {'I': 0.2434969602..., 'L': 3.195481841...}

    The ``extended_out=False`` output is:
        - I : I value at the given point
        - L : L value at the given point

    >>> from lgmpy import magcoords
    >>> import datetime
    >>> magcoords.Lvalue([3, 0, 1], datetime.datetime(2000, 1, 1), extended_out=True)
    {'Blocal': 1024.1142193703838,
    'Bmin': 921.8869150...,
    'Bmirr': 1024.1142193...,
    'I': 0.24349696021...,
    'L': 3.1954818410...,
    'M': 30119.614287...}

    The ``extended_out=True`` output is:
        - I : I value at the given point
        - L : L value at the given point
        - Bmin : minimum B at the input position (nT)
        - Bmirror : mirror B at the input position (nT)
        - M : TODO what exactly and I, magnetic moment?
    '''
    defaults = {
        'alpha': 90.,
        'Bfield': 'Lgm_B_T89',
        'method': 'Hilton',
        'Kp': 2,
        'coord_system': 'GSM',
        'extended_out': False
    }

    #replace missing kwargs with defaults
    for dkey in defaults:
        if dkey not in kwargs:
            kwargs[dkey] = defaults[dkey]

    method_dict = {'Hilton': 1, 'McIlwain': 0}

    # change datetime to Lgm Datelong and UTC
    mInfo = Lgm_MagModelInfo.Lgm_MagModelInfo()
    mInfo.Kp = kwargs['Kp']
    try:
        Bfield_dict[kwargs['Bfield']](pointer(mInfo))
    except KeyError:
        raise (NotImplementedError("Only Bfield=%s currently supported" %
                                   Bfield_dict.keys()))
    try:
        datelong = Lgm_CTrans.dateToDateLong(args[1])
        utc = Lgm_CTrans.dateToFPHours(args[1])
        Lgm_Set_Coord_Transforms(datelong, utc,
                                 mInfo.c)  # dont need pointer as it is one
    except AttributeError:
        raise (TypeError("Date must be a datetime object"))
    #else:
    #ans['Epoch'] = datamodel.dmarray([args[1]])

    if kwargs['coord_system'] != 'GSM':
        in_sys = kwargs['coord_system']
        Pout = coordTrans(args[0], args[1], in_sys, 'GSM', de_eph=False)
        Pgsm = Lgm_Vector.Lgm_Vector(*Pout)
    else:
        Pgsm = Lgm_Vector.Lgm_Vector(*args[0])

    Iout = c_double()
    Bm = c_double()
    M = c_double()
    ans = Lgm_McIlwain_L(datelong, utc, pointer(Pgsm), kwargs['alpha'],
                         method_dict[kwargs['method']], pointer(Iout),
                         pointer(Bm), pointer(M), pointer(mInfo))

    #TODO: decide on format for output -- perhaps use datamodel and have method as attribute?
    #maybe only return I for extended_out flag=True
    if kwargs['extended_out']:
        sunPos_GSM = coordTrans(mInfo.c.contents.Sun, args[1], 'MOD',
                                kwargs['coord_system'])
        SunMlon = np.rad2deg(np.arctan2(sunPos_GSM[1], sunPos_GSM[0]))
        SunMlon += 180.0  # flip to midnight

        MLON = np.rad2deg(np.arctan2(Pgsm.y, Pgsm.x))
        if (MLON < 0.0):
            MLON += 360.0

        MLT = np.mod((MLON - SunMlon) / 15.0 + 24.0, 24.0)
        return {
            'L': ans,
            'I': Iout.value,
            'Bmin': mInfo.Bmin,
            'Blocal': mInfo.Blocal,
            'Bmirr': mInfo.Bm,
            'M': M.value,
            'MLon': MLON,
            'MLT': MLT
        }
    else:
        return {'L': ans, 'I': Iout.value}
Example #6
0
def get_Lstar(pos, date, alpha = 90.,
                  Kp = 2, coord_system='GSM',
                  Bfield = 'Lgm_B_OP77',
                  LstarThresh = 10.0,  # beyond this Lsimple don't compute Lstar
                  extended_out = False,
                  LstarQuality = 3):
    """
    This method does all the work for the calculation of Lstar.

    There are many options to set and a lot of output.  We have tried to describe
    it well here but certainly have missed something, if you can't understand something
    please contact the authors.

    Parameters
    ==========
    pos : list or array
        The position that Lstar should be calculated for
    date : datetime
        The date and time that Lstar should be calculated for
    alpha : float, optional
        Local pitch angle at ``pos`` to calculate Lstar, default (90)
    Kp : int, optional
        The Kp index to pass to the magnetic field model, used in T89, ignored
        in OP77, default (2)
    coord_system : str, optional
        The coordinate system of the input position, default (GSM)
    Bfield : str, optional
        The Magnetic field model to use for the calculation, default (Lgm_B_T89)
    LstarThresh : float, optional
        The calculation computes a simple L value and does not calculation Lstar
        if simple L is beyond this, default (10)
    extended_out : bool, optional
        Keyword that enables the output of significantly more information into
        the Lstar_Data output, default (False).  The extra information allows
        the points along the Lstar trace to be used since Lstar is the same at
        each point along the trace.
    LstarQuality : int
        The quality flag for the integrators in the calculation, this can have
        serious impact on run speed and Lstar value

    Returns
    =======
    out : Lstar_Data
        Returns a datamodel object that contains all the information for the run.
        See examples for the contents of this object for extended_out=False and True

    Examples
    ========
    >>> from lgmpy import Lstar
    >>> import datetime
    >>> dat = Lstar.get_Lstar([1,2,0], datetime.datetime(2000, 1, 2), extended_out=False)
    >>> print(dat)
    +
    |____90.0 (spacepy.datamodel.SpaceData [8])
         |____Bmin (spacepy.datamodel.dmarray ())
             :|____units (str [2])
         |____Bmirror (spacepy.datamodel.dmarray ())
             :|____coord_system (str [3])
             :|____units (str [2])
         |____I (spacepy.datamodel.dmarray ())
         |____LHilton (float)
         |____LMcIlwain (float)
         |____Lsimple (spacepy.datamodel.dmarray (1,))
         |____Lstar (spacepy.datamodel.dmarray (1,))
             :|____info (str [4])
         |____Pmin (spacepy.datamodel.dmarray (3,))
             :|____coord_system (str [3])
             :|____units (str [3])
    |____Bcalc (spacepy.datamodel.dmarray (3,))
        :|____Kp (int)
        :|____coord_system (str [3])
        :|____model (str [10])
        :|____units (str [2])
    |____Epoch (spacepy.datamodel.dmarray (1,))
    |____Kp (spacepy.datamodel.dmarray (1,))
    |____MLT (spacepy.datamodel.dmarray ())
        :|____coord_system (str [5])
    |____position (dict [1])
         |____GSM (spacepy.datamodel.dmarray (3,))
             :|____units (str [2])
    ...

    The data object is a dictionary with keys:
        - 90.0 : this is the pitch angle specified (multiple can be specified)
            - Bmin : the minimum B for that pitch angle and position
            - BMirror : the position of the mirror point for that position
            - I : the I value for that position
            - LHilton : L value calculated with the Hilton approximation
            - LMcIlwain: L values calculated with the McIlwain formula
            - Lsimple : a simple L value
            - Lstar : the value of Lstar for that position and pitch angle
            - Pmin : TODO what is Pmin?
        - Bcalc : information about the magnetic field model
        - Epoch : the time of the calculation
        - Kp : Kp used for the calculation
        - MLT : MLT value for the position and coord_system
        - position : the position of the calculation
            - GSM : the value in this system, if conversions are done they all appear here

    >>> from lgmpy import Lstar
    >>> import datetime
    >>> dat = Lstar.get_Lstar([1,2,0], datetime.datetime(2000, 1, 2), extended_out=True)
    >>> print(dat)
    +
    |____90.0 (spacepy.datamodel.SpaceData [21])
        :|____Calc_Time (float)
         |____Bmag (numpy.ndarray (100, 1000))
         |____Bmin (spacepy.datamodel.dmarray ())
             :|____units (str [2])
         |____Bmirror (spacepy.datamodel.dmarray ())
             :|____coord_system (str [3])
             :|____units (str [2])
         |____I (spacepy.datamodel.dmarray ())
         |____LHilton (float)
         |____LMcIlwain (float)
         |____Lsimple (spacepy.datamodel.dmarray (1,))
         |____Lstar (spacepy.datamodel.dmarray (1,))
             :|____info (str [4])
         |____Pmin (spacepy.datamodel.dmarray (3,))
             :|____coord_system (str [3])
             :|____units (str [3])
         |____ShellEllipsoidFootprint_Pn (numpy.ndarray (100,))
         |____ShellEllipsoidFootprint_Ps (numpy.ndarray (100,))
         |____ShellI (spacepy.datamodel.dmarray (100,))
         |____ShellMirror_Pn (numpy.ndarray (100,))
         |____ShellMirror_Ps (numpy.ndarray (100,))
         |____ShellMirror_Sn (float)
         |____ShellMirror_Ss (float)
         |____nFieldPnts (numpy.ndarray (100,))
         |____s_gsm (numpy.ndarray (100, 1000))
         |____x_gsm (numpy.ndarray (100, 1000))
         |____y_gsm (numpy.ndarray (100, 1000))
         |____z_gsm (numpy.ndarray (100, 1000))
    |____Bcalc (spacepy.datamodel.dmarray (3,))
        :|____Kp (int)
        :|____coord_system (str [3])
        :|____model (str [10])
        :|____units (str [2])
    |____Epoch (spacepy.datamodel.dmarray (1,))
    |____Kp (spacepy.datamodel.dmarray (1,))
    |____MLT (spacepy.datamodel.dmarray ())
        :|____coord_system (str [5])
    |____position (dict [1])
         |____GSM (spacepy.datamodel.dmarray (3,))
             :|____units (str [2])
    ...

    The data object is a dictionary with keys:
        - 90.0 : this is the pitch angle specified (multiple can be specified)
            - Calc_Time : the clock time that the calculation took to run
            - Bmag : the magnitude of B along the Lstar trace
            - Bmin : the minimum B for that pitch angle and position
            - BMirror : the position of the mirror point for that position
            - I : the I value for that position
            - LHilton : L value calculated with the Hilton approximation
            - LMcIlwain: L values calculated with the McIlwain formula
            - Lsimple : a simple L value
            - Lstar : the value of Lstar for that position and pitch angle
            - Pmin : TODO what am I?
            - ShellEllipsoidFootprint_Pn : TODO what am I?
            - ShellEllipsoidFootprint_Ps : TODO what am I?
            - ShellI : TODO what am I?
            - ShellMirror_Pn : TODO what am I?
            - ShellMirror_Ps : TODO what am I?
            - ShellMirror_Sn : TODO what am I?
            - ShellMirror_Ss : TODO what am I?
            - nFieldPnts : TODO what am I?
            - s_gsm : TODO what am I?
            - x_gsm : TODO what am I?
            - y_gsm : TODO what am I?
            - z_gsm : TODO what am I?
        - Bcalc : information about the magnetic field model
        - Epoch : the time of the calculation
        - Kp : Kp used for the calculation
        - MLT : MLT value for the position and coord_system
        - position : the position of the calculation
            - GSM : the value in this system, if conversions are done they all appear here


    """

    # setup a datamodel object to hold the answer
    ans = Lstar_Data()

    # change datetime to Lgm Datelong and UTC
    try:
        datelong = Lgm_CTrans.dateToDateLong(date)
        utc = Lgm_CTrans.dateToFPHours(date)
    except AttributeError:
        raise(TypeError("Date must be a datetime object"))
    else:
        ans['Epoch'] = datamodel.dmarray([date])

    # pitch angles to calculate
    try:
        Alpha = list(alpha)
    except TypeError:
        Alpha = [alpha]

    # required setup
    MagEphemInfo = Lgm_MagEphemInfo.Lgm_MagEphemInfo(len(Alpha), 0)

    # setup a shortcut to MagModelInfo
    mmi = MagEphemInfo.LstarInfo.contents.mInfo.contents
    Lgm_Set_Coord_Transforms( datelong, utc, mmi.c) # dont need pointer as it is one

    # convert to **GSM**
    if coord_system == 'GSM':
        try:
            Pgsm = Lgm_Vector.Lgm_Vector(*pos)
        except TypeError:
            raise(TypeError("Position must be listlike" ) )
        ans['position']['GSM'] = datamodel.dmarray(pos, attrs={'units':'Re'})
    elif coord_system == 'SM':
        try:
            Psm = Lgm_Vector.Lgm_Vector(*pos)
        except TypeError:
            raise(TypeError("Position must be listlike" ) )
        Pgsm = Lgm_Vector.Lgm_Vector()
        Lgm_Convert_Coords( pointer(Psm), pointer(Pgsm), SM_TO_GSM, mmi.c )
        ans['position']['SM'] = datamodel.dmarray(pos, attrs={'units':'Re'})
        ans['position']['GSM'] = datamodel.dmarray(Pgsm.tolist(), attrs={'units':'Re'})
    else:
        raise(NotImplementedError("Only GSM or SM input currently supported"))

    Pwgs = Lgm_Vector.Lgm_Vector()
    Pmlt = Lgm_Vector.Lgm_Vector()
    Lgm_Convert_Coords( pointer(Pgsm), pointer(Pwgs), GSM_TO_WGS84, mmi.c )
    Lgm_Convert_Coords( pointer(Pwgs), pointer(Pmlt), WGS84_TO_EDMAG, mmi.c )
    R, MLat, MLon, MLT = c_double(), c_double(), c_double(), c_double(),
    Lgm_EDMAG_to_R_MLAT_MLON_MLT( pointer(Pmlt),  pointer(R), pointer(MLat), pointer(MLon),
        pointer(MLT), mmi.c)
    ans['MLT'] = datamodel.dmarray(MLT.value, attrs={'coord_system': 'EDMAG'})

    # save Kp
    # TODO maybe add some Kp checking
    ans['Kp'] = datamodel.dmarray([Kp])

    # Set the LstarQuality, TODO add a comment here on what each does
    MagEphemInfo.LstarQuality = LstarQuality

    # L* in one place is L* in lots of places (for GPS set to False)
    MagEphemInfo.SaveShellLines = extended_out
    # TODO maybe not hardcoded, but for now its fine
    MagEphemInfo.LstarInfo.contents.VerbosityLevel = 0
    MagEphemInfo.LstarInfo.contents.mInfo.contents.VerbosityLevel = 0

    #MagEphemInfo->LstarInfo->mInfo->Bfield        = Lgm_B_T89;
    #MagEphemInfo->LstarInfo->mInfo->Bfield        = Lgm_B_cdip;
    #MagEphemInfo->LstarInfo->mInfo->Bfield        = Lgm_B_OP77;
    #MagEphemInfo->LstarInfo->mInfo->InternalModel = LGM_CDIP;

    ## decide which field model to use, this is a keyword
    try:
        Bfield_dict[Bfield](MagEphemInfo.LstarInfo.contents.mInfo)
    except KeyError:
        raise(NotImplementedError("Only Bfield=%s currently supported" % Bfield_dict.keys()))

    # Save Date, UTC to MagEphemInfo structure ** is this needed?
    MagEphemInfo.Date   = datelong
    MagEphemInfo.UTC    = utc
    MagEphemInfo.LstarInfo.contents.mInfo.contents.Kp = Kp

    # Save nAlpha, and Alpha array to MagEphemInfo structure
    MagEphemInfo.nAlpha = len(Alpha)
    for i in range(len(Alpha)):
        MagEphemInfo.Alpha[i] = Alpha[i]

    # Set Tolerances
    Lgm_SetLstarTolerances(LstarQuality, 24, MagEphemInfo.LstarInfo )
    # *  Blocal at sat location
    MagEphemInfo.P = Pgsm

    Bvec = Lgm_Vector.Lgm_Vector()
    # Get B at the point in question
    MagEphemInfo.LstarInfo.contents.mInfo.contents.Bfield(pointer(Pgsm), pointer(Bvec),
                                                          MagEphemInfo.LstarInfo.contents.mInfo)
    ans['Bcalc'] = datamodel.dmarray(Bvec.tolist(), attrs={'units':'nT'})
    ans['Bcalc'].attrs['model'] = Bfield
    ans['Bcalc'].attrs['Kp'] = Kp
    ans['Bcalc'].attrs['coord_system'] = 'GSM'

    # save its magnitude in the structure
    MagEphemInfo.B = Bvec.magnitude()

    # check and see if the field line is closed before doing much work
    trace, northern, southern, minB, Lsimple = Closed_Field.Closed_Field(MagEphemInfo, extended_out=True)

    # presetup the ans[Angle] so that it can be filled correctly
    for pa in Alpha:
        ans[pa] = datamodel.SpaceData()
        ans[pa]['Lsimple'] = datamodel.dmarray([Lsimple])
        #sets up nans in case of Lstar failure
        ans[pa]['I'] = datamodel.dmarray([numpy.nan])
        ans[pa]['Lstar'] = datamodel.dmarray([numpy.nan], attrs={'info':trace})
        ans[pa]['LMcIlwain'] = datamodel.dmarray([numpy.nan])
        ans[pa]['LHilton'] = datamodel.dmarray([numpy.nan])
        ans[pa]['Bmin'] = datamodel.dmarray([numpy.nan], attrs={'units':'nT'})
        ans[pa]['Bmirror'] = datamodel.dmarray([numpy.nan], attrs={'units':'nT'})
    
    if trace != 'LGM_CLOSED':
        return ans
        # if this is not LGM_CLOSED then don't both with any pitch angle?  true?

    #  Save field-related quantities for each Pitch Angle.
    MagEphemInfo.Pmin = Lgm_Vector.Lgm_Vector(*minB)
    MagEphemInfo.Bmin = MagEphemInfo.LstarInfo.contents.mInfo.contents.Bmin
    ans[pa]['Pmin'] = datamodel.dmarray(minB, attrs={'units':'R_E'})
    ans[pa]['Pmin'].attrs['coord_system'] = 'GSM'
    ans[pa]['Bmin'] = datamodel.dmarray(MagEphemInfo.Bmin, attrs={'units':'nT'})
    # LOOP OVER PITCH ANGLES
    for i, pa in enumerate(Alpha):

        tnow = datetime.datetime.now()
        PreStr = MagEphemInfo.LstarInfo.contents.PreStr
        PostStr = MagEphemInfo.LstarInfo.contents.PostStr

        # ***********************************************
        # *** not sure I fully understand this chunk, B at the mirror point*****
        # Set Pitch Angle, sin, sin^2, and Bmirror
        sa = math.sin( pa*RadPerDeg )
        sa2 = sa*sa

        # print("{0}Computing L* for Pitch Angle: Alpha[{1}] = {2} Date: {3}   UTC: {4}   Lsimple = {5:4.3}{6}\n").format(PreStr, i, MagEphemInfo.Alpha[i], date, utc, Lsimple, PostStr )
        
        if sa2!=0: #non-zero pitch angle
            MagEphemInfo.LstarInfo.contents.mInfo.contents.Bm = MagEphemInfo.B/sa2
        else:
            continue
        ans[pa]['Bmirror'] = datamodel.dmarray(MagEphemInfo.LstarInfo.contents.mInfo.contents.Bm, attrs={'units':'nT'})
        ans[pa]['Bmirror'].attrs['coord_system'] = 'GSM'
        # ***********************************************
        
        # I tink this is already done
        # Lgm_Set_Coord_Transforms( Date, UTC, MagEphemInfo.LstarInfo.contents.mInfo.contents.c )

        MagEphemInfo.LstarInfo.contents.PitchAngle = pa
        MagEphemInfo.Bm[i] = MagEphemInfo.LstarInfo.contents.mInfo.contents.Bm
        # Compute L*
        if Lsimple < LstarThresh:
            Ls_vec = Lgm_Vector.Lgm_Vector(*minB)

            LS_Flag = Lgm_Lstar( pointer(Ls_vec), MagEphemInfo.LstarInfo)

            lstarinf = MagEphemInfo.LstarInfo.contents #shortcut
            MagEphemInfo.LHilton.contents.value = LFromIBmM_Hilton(c_double(lstarinf.I[0]),
                                                c_double(MagEphemInfo.Bm[i]),
                                                c_double(lstarinf.mInfo.contents.c.contents.M_cd))
            ans[pa]['LHilton'] = MagEphemInfo.LHilton.contents.value
            MagEphemInfo.LMcIlwain.contents.value = LFromIBmM_McIlwain(c_double(lstarinf.I[0]),
                                                c_double(MagEphemInfo.Bm[i]),
                                                c_double(lstarinf.mInfo.contents.c.contents.M_cd))
            ans[pa]['LMcIlwain'] = MagEphemInfo.LMcIlwain.contents.value
            if LS_Flag == -2: # mirror below southern hemisphere mirror alt
                ans[pa]['Lstar'] = datamodel.dmarray([numpy.nan], attrs={'info':'S_LOSS'})
            elif LS_Flag == -1: # mirror below northern hemisphere mirror alt
                ans[pa]['Lstar'] = datamodel.dmarray([numpy.nan], attrs={'info':'N_LOSS'})
            elif LS_Flag >= 0: # valid calc
                ans[pa]['Lstar'] = datamodel.dmarray([lstarinf.LS], attrs={'info':'GOOD'}) # want better word?

            MagEphemInfo.Lstar[i] = lstarinf.LS

            # Save results to the MagEphemInfo structure.
            MagEphemInfo.nShellPoints[i] = lstarinf.nPnts
            ## pull all this good extra info into numpy arrays
            ans[pa]['I'] = datamodel.dmarray(lstarinf.I[0])

            if extended_out:
                ans[pa]['ShellI'] = \
                    datamodel.dmarray(numpy.ctypeslib.ndarray([len(lstarinf.I)],
                                            dtype=c_double, buffer=lstarinf.I) ).copy()
                ans[pa]['ShellEllipsoidFootprint_Pn'] = \
                    numpy.ctypeslib.ndarray(len(lstarinf.Ellipsoid_Footprint_Pn),
                                            dtype=c_double,
                                            buffer=lstarinf.Ellipsoid_Footprint_Pn).copy()
                ans[pa]['ShellEllipsoidFootprint_Ps'] = \
                    numpy.ctypeslib.ndarray(len(lstarinf.Ellipsoid_Footprint_Ps),
                                            dtype=c_double,
                                            buffer=lstarinf.Ellipsoid_Footprint_Ps).copy()

                ans[pa]['ShellMirror_Pn'] = \
                    numpy.ctypeslib.ndarray(len(lstarinf.Mirror_Pn),
                                            dtype=c_double,
                                            buffer=lstarinf.Mirror_Pn).copy()
                ans[pa]['ShellMirror_Ps'] = \
                    numpy.ctypeslib.ndarray(len(lstarinf.Mirror_Ps),
                                            dtype=c_double,
                                            buffer=lstarinf.Mirror_Ps).copy()
                ans[pa]['ShellMirror_Ss'] = lstarinf.mInfo.contents.Sm_South
                ans[pa]['ShellMirror_Sn'] = lstarinf.mInfo.contents.Sm_North
                ans[pa]['nFieldPnts'] = \
                    numpy.ctypeslib.ndarray(len(lstarinf.nFieldPnts),
                                            dtype=c_int,
                                            buffer=lstarinf.nFieldPnts).copy()

                ans[pa]['s_gsm'] = \
                    numpy.ctypeslib.ndarray([len(lstarinf.s_gsm),
                                             len(lstarinf.s_gsm[0])],
                                            dtype=c_double,
                                            buffer=lstarinf.s_gsm).copy()
                ans[pa]['Bmag'] = \
                    numpy.ctypeslib.ndarray([len(lstarinf.Bmag),
                                             len(lstarinf.Bmag[0])],
                                            dtype=c_double,
                                            buffer=lstarinf.Bmag).copy()
                ans[pa]['x_gsm'] = \
                    numpy.ctypeslib.ndarray([len(lstarinf.x_gsm),
                                             len(lstarinf.x_gsm[0])],
                                            dtype=c_double,
                                            buffer=lstarinf.x_gsm).copy()
                ans[pa]['y_gsm'] = \
                    numpy.ctypeslib.ndarray([len(lstarinf.y_gsm),
                                             len(lstarinf.y_gsm[0])],
                                            dtype=c_double,
                                            buffer=lstarinf.y_gsm).copy()
                ans[pa]['z_gsm'] = \
                    numpy.ctypeslib.ndarray([len(lstarinf.z_gsm),
                                             len(lstarinf.z_gsm[0])],
                                            dtype=c_double,
                                            buffer=lstarinf.z_gsm).copy()
                delT = datetime.datetime.now() - tnow
                ans[pa].attrs['Calc_Time'] = delT.seconds + delT.microseconds/1e6

    Lgm_FreeMagEphemInfo_Children(pointer(MagEphemInfo))
    return ans