import numpy as np

def t01(parmod, ps, x, y, z):
    """
    Release date of this version: August 8, 2001.

    Latest modifications/bugs removed: June 24, 2006:  replaced coefficients in:
        (i)   data statement in function ap,
        (ii)  data c_sy statement in subroutine full_rc, and
        (iii) data a statement in subroutine t01_01.
    This correction was needed because of a bug found in the symmetric ring current module.
    Its impact is a minor (a few percent) change of the model field in the inner magnetosphere.

    Attention: The model is based on data taken sunward from x=-15Re, and hence becomes
    invalid at larger tailward distances !!!


    A data-based model of the external (i.e., without earth's contribution) part of the
    magnetospheric magnetic field, calibrated by
        (1) solar wind pressure pdyn (nanopascals),
        (2) dst (nanotesla)
        (3) byimf (nanotesla)
        (4) bzimf (nanotesla)
        (5) g1-index
        (6) g2-index  (see Tsyganenko [2001] for an exact definition of these two indices)

    (C) Copr. 2001, Nikolai A. Tsyganenko, USRA, Code 690.2, NASA GSFC Greenbelt, MD 20771, USA

    REFERENCE:
    N. A. Tsyganenko, A new data-based model of the near magnetosphere magnetic field:
        1. Mathematical structure. 2. Parameterization and fitting to observations. (submitted to JGR, July 2001)

    :param parmod: The elements are
        (1) solar wind pressure pdyn (nanopascals)
        (2) dst (nanotesla)
        (3) byimf (nanotesla)
        (4) bzimf (nanotesla)
        (5) g1-index
        (6) g2-index  (see Tsyganenko [2001] for an exact definition of these two indices)
        (7) the geodipole tilt angle ps (radians)
        (8-10) x,y,z -  GSM position (Re)
    :param ps: geo-dipole tilt angle in radius.
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return: bx,by,bz. Field components in GSM system, in nT.
        Computed as a sum of contributions from principal field sources.
    """

    a = np.array([
        1.00000,2.48341,.58315,.31917,-.08796,-1.17266,3.57478,
        -.06143,-.01113,.70924,-.01675,-.46056,-.87754,-.03025,
        .18933,.28089,.16636,-.02932,.02592,-.23537,-.07659,
        .09117,-.02492,.06816,.55417,.68918,-.04604,2.33521,
        3.90147,1.28978,.03139,.98751,.21824,41.60182,1.12761,
        .01376,1.02751,.02969,.15790,8.94335,28.31280,1.24364,.38013])

    # The disclaimer below is temporarily disabled:
    if x < -20:
       print('Attention: the model is valid sunward from x=-15 re only, while you are trying to use it at x=', x)
       raise ValueError

    pdyn = parmod[0]
    dst_ast = parmod[1]*0.8-13.*np.sqrt(pdyn)
    byimf,bzimf =parmod[2:4]
    g1,g2 = parmod[4:6]
    pss = ps
    xx,yy,zz = [x,y,z]

    bbx,bby,bbz = extall(0,0,0,0,a,43,pdyn,dst_ast,byimf,bzimf,g1,g2,pss,xx,yy,zz)

    return bbx,bby,bbz



def extall(iopgen,iopt,iopb,iopr,a,ntot,pdyn,dst,byimf,bzimf,vbimf1,vbimf2,ps,x,y,z):
    """

    :param iopgen: general option flag:
        iopgen=0 - calculate total field
        iopgen=1 - dipole shielding only
        iopgen=2 - tail field only
        iopgen=3 - birkeland field only
        iopgen=4 - ring current field only
        iopgen=5 - interconnection field only
    :param iopt: tail field flag:
        iopt=0  -  both modes
        iopt=1  -  mode 1 only
        iopt=2  -  mode 2 only
    :param iopb: birkeland field flag:
        iopb=0  -  all 4 terms
        iopb=1  -  region 1, modes 1 and 2
        iopb=2  -  region 2, modes 1 and 2
    :param iopr: ring current flag:
        iopr=0  -  both src and prc
        iopr=1  -  src only
        iopr=2  -  prc only
    :param a:
    :param ntot:
    :param pdyn:
    :param dst:
    :param byimf:
    :param bzimf:
    :param vbimf1:
    :param vbimf2:
    :param ps:
    :param x:
    :param y:
    :param z:
    :return:
    """

    # common /tail/ dxshift1,dxshift2,d,deltady  ! the common blocks forward nonlinear parameters
    # common /birkpar/ xkappa1,xkappa2
    # common /rcpar/ sc_sy,sc_pr,phi
    # common /g/ g
    # common /rh0/ rh0
    global dxshift1, dxshift2, d, deltady
    global xkappa1, xkappa2
    global sc_sy, sc_pr, phi
    global g
    global rh0


    a0_a,a0_s0,a0_x0 = [34.586,1.1960,3.4397]   # Shue et al. parameters
    dsig = 0.003
    rh0,rh2 = [8.0,-5.2]

    xappa = (pdyn/2.)**a[38]    # now this is a variable parameter
    rh0=a[39]
    g=a[40]

    xappa3=xappa**3

    xx=x*xappa
    yy=y*xappa
    zz=z*xappa

    sps=np.sin(ps)

    x0=a0_x0/xappa
    am=a0_a/xappa
    s0=a0_s0

    bperp=np.sqrt(byimf**2+bzimf**2)

    # calculate the imf clock angle:
    if (byimf == 0) & (bzimf == 0):
        theta = 0.
    else:
        theta=np.arctan2(byimf,bzimf)
        if theta <= 0: theta = 2*np.pi

    ct=np.cos(theta)
    st=np.sin(theta)
    ys=y*ct-z*st
    zs=z*ct+y*st

    sthetah=np.sin(theta/2.)**2

    # Calculate "imf" components outside the magnetopause layer (hence begin with "o")
    # They are needed only if the point (x,y,z) is within the transition magnetopause layer or outside the magnetosphere:
    factimf=a[23]+a[24]*sthetah

    oimfx=0.
    oimfy=byimf*factimf
    oimfz=bzimf*factimf

    r=np.sqrt(x**2+y**2+z**2)
    xss=x
    zss=z

    # begin iterative search of unwarped coords (to find sigma)
    dd = 1.
    while dd > 1e-6:
        xsold=xss
        zsold=zss

        rh=rh0+rh2*(zss/r)**2
        sinpsas=sps/(1+(r/rh)**3)**0.33333333
        cospsas=np.sqrt(1-sinpsas**2)
        zss=x*sinpsas+z*cospsas
        xss=x*cospsas-z*sinpsas
        dd=np.abs(xss-xsold)+np.abs(zss-zsold)

    rho2=y**2+zss**2
    asq=am**2
    xmxm=am+xss-x0
    if xmxm < 0: xmxm = 0   # the boundary is a cylinder tailward of x=x0-am
    axx0=xmxm**2
    aro=asq+rho2
    sigma=np.sqrt((aro+axx0+np.sqrt((aro+axx0)**2-4.*asq*axx0))/(2.*asq))

    # Now, there are three possible cases:
    #    (1) inside the magnetosphere
    #    (2) in the boundary layer
    #    (3) outside the magnetosphere and b.layer

    # First of all, consider the cases (1) and (2):
    if sigma < (s0+dsig):     # cases (1) or (2); calculate the model field (with the potential "penetrated" interconnection field):

        bxcf,bycf,bzcf = [0.]*3
        if iopgen <= 1:
            cfx,cfy,cfz = shlcar3x3(xx,yy,zz,ps)    # dipole shielding field
            bxcf=cfx*xappa3
            bycf=cfy*xappa3
            bzcf=cfz*xappa3

        bxt1,byt1,tzt1,bxt2,byt2,bzt2 = [0.]*6
        if (iopgen == 0) | (iopgen == 2):
            dxshift1=a[25]+a[26]*vbimf2
            dxshift2=0.
            d=a[27]
            deltady=a[28]
            # tail field (three modes)
            bxt1,byt1,bzt1,bxt2,byt2,bzt2 = deformed(iopt,ps,xx,yy,zz)

        bxr11,byr11,bzr11, bxr12,byr12,bzr12, bxr21,byr21,bzr21, bxr22,byr22,bzr22 = [0.]*12
        if (iopgen == 0) | (iopgen == 3):
            xkappa1=a[34]+a[35]*vbimf2
            xkappa2=a[36]+a[37]*vbimf2
            # Birkeland field (two modes for r1 and two modes for r2)
            bxr11,byr11,bzr11, bxr12,byr12,bzr12, bxr21,byr21,bzr21, bxr22,byr22,bzr22 = \
                birk_tot(iopb,ps,xx,yy,zz)

        bxsrc,bysrc,bzsrc, bxprc,byprc,bzprc = [0.]*6
        if (iopgen == 0) | (iopgen == 4):
            phi=1.5707963*np.tanh(np.abs(dst)/a[33])
            znam=np.abs(dst)
            if znam < 20: znam=20
            sc_sy=a[29]*(20/znam)**a[30]*xappa
            sc_pr=a[31]*(20/znam)**a[32]*xappa
            # shielded ring current (src and prc)
            bxsrc,bysrc,bzsrc, bxprc,byprc,bzprc = full_rc(iopr,ps,xx,yy,zz)

        hximf,hyimf,hzimf = [0.,0,0]
        if (iopgen == 0) | (iopgen == 5):
            # These are components of the penetrated field per unit of the penetration coefficient.
            # In other words, these are derivatives of the penetration field components with respect
            # to the penetration coefficient. We assume that only the transverse component of the
            # field penetrates inside.
            hximf,hyimf,hzimf = [0.,byimf,bzimf]

        # Now, add up all the components:
        dlp1=(pdyn/2)**a[41]
        dlp2=(pdyn/2)**a[42]

        tamp1=a[1]+a[2]*dlp1+a[3]*vbimf1+a[4]*dst
        tamp2=a[5]+a[6]*dlp2+a[7]*vbimf1+a[8]*dst
        a_src=a[9] +a[10]*dst+a[11]*np.sqrt(pdyn)
        a_prc=a[12]+a[13]*dst+a[14]*np.sqrt(pdyn)
        a_r11=a[15]+a[16]*vbimf2
        a_r12=a[17]+a[18]*vbimf2
        a_r21=a[19]+a[20]*vbimf2
        a_r22=a[21]+a[22]*vbimf2

        bbx=a[0]*bxcf+tamp1*bxt1+tamp2*bxt2+a_src*bxsrc+a_prc*bxprc \
            +a_r11*bxr11+a_r12*bxr12+a_r21*bxr21+a_r22*bxr22 \
            +a[23]*hximf+a[24]*hximf*sthetah

        bby=a[0]*bycf+tamp1*byt1+tamp2*byt2+a_src*bysrc+a_prc*byprc \
            +a_r11*byr11+a_r12*byr12+a_r21*byr21+a_r22*byr22 \
            +a[23]*hyimf+a[24]*hyimf*sthetah

        bbz=a[0]*bzcf+tamp1*bzt1+tamp2*bzt2+a_src*bzsrc+a_prc*bzprc \
            +a_r11*bzr11+a_r12*bzr12+a_r21*bzr21+a_r22*bzr22 \
            +a[23]*hzimf+a[24]*hzimf*sthetah

        # And we have the total external field.
        #  Now, let us check whether we have the case (1). if yes - we are done:
        if sigma < (s0-dsig):   # (x,y,z) is inside the magnetosphere
            bx,by,bz = [bbx,bby,bbz]
        else:                   # this is the most complex case: we are inside the interpolation region
            fint=0.5*(1.-(sigma-s0)/dsig)
            fext=0.5*(1.+(sigma-s0)/dsig)

            qx,qy,qz = dipole(ps,x,y,z)
            bx=(bbx+qx)*fint+oimfx*fext -qx
            by=(bby+qy)*fint+oimfy*fext -qy
            bz=(bbz+qz)*fint+oimfz*fext -qz
    # The cases (1) and (2) are exhausted; the only remaining possibility is now the case (3):
    else:
        qx,qy,qz = dipole(ps,x,y,z)
        bx=oimfx-qx
        by=oimfy-qy
        bz=oimfz-qz

    return bx,by,bz



def shlcar3x3(x,y,z,ps):
    """
    This subroutine returns the shielding field for the earth's dipole, represented by
    2x3x3=18 "cartesian" harmonics, tilted with respect to the z=0 plane  (nb#4, p.74)

    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :param ps: geo-dipole tilt angle in radius.
    :return: bx,by,bz. Field components in GSM system, in nT.
    """
    # The 36 coefficients enter in pairs in the amplitudes of the "cartesian" harmonics (A(1)-A(36).
    # The 14 nonlinear parameters (A(37)-A(50) are the scales Pi,Ri,Qi,and Si entering the arguments of exponents, sines, and cosines in each of the
    # 18 "cartesian" harmonics  plus two tilt angles for the cartesian harmonics (one for the psi=0 mode and another for the psi=90 mode)

    a = np.array([
        -901.2327248,895.8011176,817.6208321,-845.5880889,-83.73539535,
        86.58542841,336.8781402,-329.3619944,-311.2947120,308.6011161,
        31.94469304,-31.30824526,125.8739681,-372.3384278,-235.4720434,
        286.7594095,21.86305585,-27.42344605,-150.4874688,2.669338538,
        1.395023949,-.5540427503,-56.85224007,3.681827033,-43.48705106,
        5.103131905,1.073551279,-.6673083508,12.21404266,4.177465543,
        5.799964188,-.3977802319,-1.044652977,.5703560010,3.536082962,
        -3.222069852,9.620648151,6.082014949,27.75216226,12.44199571,
        5.122226936,6.982039615,20.12149582,6.150973118,4.663639687,
        15.73319647,2.303504968,5.840511214,.8385953499E-01,.3477844929])

    p1,p2,p3, r1,r2,r3, q1,q2,q3, s1,s2,s3 = a[36:48]
    t1,t2 = a[48:50]

    cps=np.cos(ps)
    sps=np.sin(ps)
    s2ps=2*cps  # modified here (sin(2*ps) instead of sin(3*ps))

    st1=np.sin(ps*t1)
    ct1=np.cos(ps*t1)
    st2=np.sin(ps*t2)
    ct2=np.cos(ps*t2)

    x1=x*ct1-z*st1
    z1=x*st1+z*ct1
    x2=x*ct2-z*st2
    z2=x*st2+z*ct2

    # make the terms in the 1st sum ("perpendicular" symmetry):
    # i=1:
    sqpr= np.sqrt(1/p1**2+1/r1**2)
    cyp = np.cos(y/p1)
    syp = np.sin(y/p1)
    czr = np.cos(z1/r1)
    szr = np.sin(z1/r1)
    expr= np.exp(sqpr*x1)
    fx1 =-sqpr*expr*cyp*szr
    hy1 = expr/p1*syp*szr
    fz1 =-expr*cyp/r1*czr
    hx1 = fx1*ct1+fz1*st1
    hz1 =-fx1*st1+fz1*ct1

    sqpr= np.sqrt(1/p1**2+1/r2**2)
    cyp = np.cos(y/p1)
    syp = np.sin(y/p1)
    czr = np.cos(z1/r2)
    szr = np.sin(z1/r2)
    expr= np.exp(sqpr*x1)
    fx2 =-sqpr*expr*cyp*szr
    hy2 = expr/p1*syp*szr
    fz2 =-expr*cyp/r2*czr
    hx2 = fx2*ct1+fz2*st1
    hz2 =-fx2*st1+fz2*ct1

    sqpr= np.sqrt(1/p1**2+1/r3**2)
    cyp = np.cos(y/p1)
    syp = np.sin(y/p1)
    czr = np.cos(z1/r3)
    szr = np.sin(z1/r3)
    expr= np.exp(sqpr*x1)
    fx3 =-expr*cyp*(sqpr*z1*czr+szr/r3*(x1+1/sqpr))
    hy3 = expr/p1*syp*(z1*czr+x1/r3*szr/sqpr)
    fz3 =-expr*cyp*(czr*(1+x1/r3**2/sqpr)-z1/r3*szr)
    hx3 = fx3*ct1+fz3*st1
    hz3 =-fx3*st1+fz3*ct1

    # i=2:
    sqpr= np.sqrt(1/p2**2+1/r1**2)
    cyp = np.cos(y/p2)
    syp = np.sin(y/p2)
    czr = np.cos(z1/r1)
    szr = np.sin(z1/r1)
    expr= np.exp(sqpr*x1)
    fx4 =-sqpr*expr*cyp*szr
    hy4 = expr/p2*syp*szr
    fz4 =-expr*cyp/r1*czr
    hx4 = fx4*ct1+fz4*st1
    hz4 =-fx4*st1+fz4*ct1

    sqpr= np.sqrt(1/p2**2+1/r2**2)
    cyp = np.cos(y/p2)
    syp = np.sin(y/p2)
    czr = np.cos(z1/r2)
    szr = np.sin(z1/r2)
    expr= np.exp(sqpr*x1)
    fx5 =-sqpr*expr*cyp*szr
    hy5 = expr/p2*syp*szr
    fz5 =-expr*cyp/r2*czr
    hx5 = fx5*ct1+fz5*st1
    hz5 =-fx5*st1+fz5*ct1

    sqpr= np.sqrt(1/p2**2+1/r3**2)
    cyp = np.cos(y/p2)
    syp = np.sin(y/p2)
    czr = np.cos(z1/r3)
    szr = np.sin(z1/r3)
    expr= np.exp(sqpr*x1)
    fx6 =-expr*cyp*(sqpr*z1*czr+szr/r3*(x1+1/sqpr))
    hy6 = expr/p2*syp*(z1*czr+x1/r3*szr/sqpr)
    fz6 =-expr*cyp*(czr*(1+x1/r3**2/sqpr)-z1/r3*szr)
    hx6 = fx6*ct1+fz6*st1
    hz6 =-fx6*st1+fz6*ct1

    # i=3:
    sqpr= np.sqrt(1/p3**2+1/r1**2)
    cyp = np.cos(y/p3)
    syp = np.sin(y/p3)
    czr = np.cos(z1/r1)
    szr = np.sin(z1/r1)
    expr= np.exp(sqpr*x1)
    fx7 =-sqpr*expr*cyp*szr
    hy7 = expr/p3*syp*szr
    fz7 =-expr*cyp/r1*czr
    hx7 = fx7*ct1+fz7*st1
    hz7 =-fx7*st1+fz7*ct1

    sqpr= np.sqrt(1/p3**2+1/r2**2)
    cyp = np.cos(y/p3)
    syp = np.sin(y/p3)
    czr = np.cos(z1/r2)
    szr = np.sin(z1/r2)
    expr= np.exp(sqpr*x1)
    fx8 =-sqpr*expr*cyp*szr
    hy8 = expr/p3*syp*szr
    fz8 =-expr*cyp/r2*czr
    hx8 = fx8*ct1+fz8*st1
    hz8 =-fx8*st1+fz8*ct1

    sqpr= np.sqrt(1/p3**2+1/r3**2)
    cyp = np.cos(y/p3)
    syp = np.sin(y/p3)
    czr = np.cos(z1/r3)
    szr = np.sin(z1/r3)
    expr= np.exp(sqpr*x1)
    fx9 =-expr*cyp*(sqpr*z1*czr+szr/r3*(x1+1/sqpr))
    hy9 = expr/p3*syp*(z1*czr+x1/r3*szr/sqpr)
    fz9 =-expr*cyp*(czr*(1+x1/r3**2/sqpr)-z1/r3*szr)
    hx9 = fx9*ct1+fz9*st1
    hz9 =-fx9*st1+fz9*ct1

    a1=a[0]+a[1]*cps
    a2=a[2]+a[3]*cps
    a3=a[4]+a[5]*cps
    a4=a[6]+a[7]*cps
    a5=a[8]+a[9]*cps
    a6=a[10]+a[11]*cps
    a7=a[12]+a[13]*cps
    a8=a[14]+a[15]*cps
    a9=a[16]+a[17]*cps
    bx=a1*hx1+a2*hx2+a3*hx3+a4*hx4+a5*hx5+a6*hx6+a7*hx7+a8*hx8+a9*hx9
    by=a1*hy1+a2*hy2+a3*hy3+a4*hy4+a5*hy5+a6*hy6+a7*hy7+a8*hy8+a9*hy9
    bz=a1*hz1+a2*hz2+a3*hz3+a4*hz4+a5*hz5+a6*hz6+a7*hz7+a8*hz8+a9*hz9


    # make the terms in the 2nd sum ("parallel" symmetry):
    # i=1
    sqqs= np.sqrt(1/q1**2+1/s1**2)
    cyq = np.cos(y/q1)
    syq = np.sin(y/q1)
    czs = np.cos(z2/s1)
    szs = np.sin(z2/s1)
    exqs= np.exp(sqqs*x2)
    fx1 =-sqqs*exqs*cyq*czs *sps
    hy1 = exqs/q1*syq*czs   *sps
    fz1 = exqs*cyq/s1*szs   *sps
    hx1 = fx1*ct2+fz1*st2
    hz1 =-fx1*st2+fz1*ct2

    sqqs= np.sqrt(1/q1**2+1/s2**2)
    cyq = np.cos(y/q1)
    syq = np.sin(y/q1)
    czs = np.cos(z2/s2)
    szs = np.sin(z2/s2)
    exqs= np.exp(sqqs*x2)
    fx2 =-sqqs*exqs*cyq*czs *sps
    hy2 = exqs/q1*syq*czs   *sps
    fz2 = exqs*cyq/s2*szs   *sps
    hx2 = fx2*ct2+fz2*st2
    hz2 =-fx2*st2+fz2*ct2

    sqqs= np.sqrt(1/q1**2+1/s3**2)
    cyq = np.cos(y/q1)
    syq = np.sin(y/q1)
    czs = np.cos(z2/s3)
    szs = np.sin(z2/s3)
    exqs= np.exp(sqqs*x2)
    fx3 =-sqqs*exqs*cyq*czs *sps
    hy3 = exqs/q1*syq*czs   *sps
    fz3 = exqs*cyq/s3*szs   *sps
    hx3 = fx3*ct2+fz3*st2
    hz3 =-fx3*st2+fz3*ct2

    # i=2:
    sqqs= np.sqrt(1/q2**2+1/s1**2)
    cyq = np.cos(y/q2)
    syq = np.sin(y/q2)
    czs = np.cos(z2/s1)
    szs = np.sin(z2/s1)
    exqs= np.exp(sqqs*x2)
    fx4 =-sqqs*exqs*cyq*czs *sps
    hy4 = exqs/q2*syq*czs   *sps
    fz4 = exqs*cyq/s1*szs   *sps
    hx4 = fx4*ct2+fz4*st2
    hz4 =-fx4*st2+fz4*ct2

    sqqs= np.sqrt(1/q2**2+1/s2**2)
    cyq = np.cos(y/q2)
    syq = np.sin(y/q2)
    czs = np.cos(z2/s2)
    szs = np.sin(z2/s2)
    exqs= np.exp(sqqs*x2)
    fx5 =-sqqs*exqs*cyq*czs *sps
    hy5 = exqs/q2*syq*czs   *sps
    fz5 = exqs*cyq/s2*szs   *sps
    hx5 = fx5*ct2+fz5*st2
    hz5 =-fx5*st2+fz5*ct2

    sqqs= np.sqrt(1/q2**2+1/s3**2)
    cyq = np.cos(y/q2)
    syq = np.sin(y/q2)
    czs = np.cos(z2/s3)
    szs = np.sin(z2/s3)
    exqs= np.exp(sqqs*x2)
    fx6 =-sqqs*exqs*cyq*czs *sps
    hy6 = exqs/q2*syq*czs   *sps
    fz6 = exqs*cyq/s3*szs   *sps
    hx6 = fx6*ct2+fz6*st2
    hz6 =-fx6*st2+fz6*ct2

    # i=3:
    sqqs= np.sqrt(1/q3**2+1/s1**2)
    cyq = np.cos(y/q3)
    syq = np.sin(y/q3)
    czs = np.cos(z2/s1)
    szs = np.sin(z2/s1)
    exqs= np.exp(sqqs*x2)
    fx7 =-sqqs*exqs*cyq*czs *sps
    hy7 = exqs/q3*syq*czs   *sps
    fz7 = exqs*cyq/s1*szs   *sps
    hx7 = fx7*ct2+fz7*st2
    hz7 =-fx7*st2+fz7*ct2

    sqqs= np.sqrt(1/q3**2+1/s2**2)
    cyq = np.cos(y/q3)
    syq = np.sin(y/q3)
    czs = np.cos(z2/s2)
    szs = np.sin(z2/s2)
    exqs= np.exp(sqqs*x2)
    fx8 =-sqqs*exqs*cyq*czs *sps
    hy8 = exqs/q3*syq*czs   *sps
    fz8 = exqs*cyq/s2*szs   *sps
    hx8 = fx8*ct2+fz8*st2
    hz8 =-fx8*st2+fz8*ct2

    sqqs= np.sqrt(1/q3**2+1/s3**2)
    cyq = np.cos(y/q3)
    syq = np.sin(y/q3)
    czs = np.cos(z2/s3)
    szs = np.sin(z2/s3)
    exqs= np.exp(sqqs*x2)
    fx9 =-sqqs*exqs*cyq*czs *sps
    hy9 = exqs/q3*syq*czs   *sps
    fz9 = exqs*cyq/s3*szs   *sps
    hx9 = fx9*ct2+fz9*st2
    hz9 =-fx9*st2+fz9*ct2

    a1=a[18]+a[19]*s2ps
    a2=a[20]+a[21]*s2ps
    a3=a[22]+a[23]*s2ps
    a4=a[24]+a[25]*s2ps
    a5=a[26]+a[27]*s2ps
    a6=a[28]+a[29]*s2ps
    a7=a[30]+a[31]*s2ps
    a8=a[32]+a[33]*s2ps
    a9=a[34]+a[35]*s2ps

    bx=bx+a1*hx1+a2*hx2+a3*hx3+a4*hx4+a5*hx5+a6*hx6+a7*hx7+a8*hx8+a9*hx9
    by=by+a1*hy1+a2*hy2+a3*hy3+a4*hy4+a5*hy5+a6*hy6+a7*hy7+a8*hy8+a9*hy9
    bz=bz+a1*hz1+a2*hz2+a3*hz3+a4*hz4+a5*hz5+a6*hz6+a7*hz7+a8*hz8+a9*hz9

    return bx, by, bz


def deformed(iopt,ps,x,y,z):
    """
    Calculates gsm components of two unit-amplitude tail field modes, taking into account
        both effects of dipole tilt: warping in y-z (done by the subroutine warped) and bending
        in x-z (done by this subroutine)

    :param iopt: tail field mode flag:   iopt=0 - the two tail modes are added up; iopt=1 - mode 1 only; iopt=2 - mode 2 only
    :param ps: geo-dipole tilt angle in radius.
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return:
    """

    #  rh0,rh1,rh2, and ieps control the tilt-related deformation of the tail field
    # common /rh0/ rh0
    global rh0
    rh2,ieps = [-5.2,3]

    sps = np.sin(ps)
    r2 = x**2+y**2+z**2
    r = np.sqrt(r2)
    zr = z/r
    rh = rh0+rh2*zr**2
    drhdr = -zr/r*2*rh2*zr
    drhdz = 2*rh2*zr/r
    rrh = r/rh
    f = 1/(1+rrh**ieps)**(1/ieps)
    dfdr = -rrh**(ieps-1)*f**(ieps+1)/rh
    dfdrh = -rrh*dfdr

    spsas = sps*f
    cpsas = np.sqrt(1-spsas**2)

    xas = x*cpsas-z*spsas
    zas = x*spsas+z*cpsas

    facps = sps/cpsas*(dfdr+dfdrh*drhdr)/r
    psasx = facps*x
    psasy = facps*y
    psasz = facps*z+sps/cpsas*dfdrh*drhdz

    dxasdx = cpsas-zas*psasx
    dxasdy =-zas*psasy
    dxasdz =-spsas-zas*psasz
    dzasdx = spsas+xas*psasx
    dzasdy = xas*psasy
    dzasdz = cpsas+xas*psasz
    fac1 = dxasdz*dzasdy-dxasdy*dzasdz
    fac2 = dxasdx*dzasdz-dxasdz*dzasdx
    fac3 = dzasdx*dxasdy-dxasdx*dzasdy

    # deform:
    bxas1,byas1,bzas1, bxas2,byas2,bzas2 = warped(iopt,ps,xas,y,zas)

    bx1=bxas1*dzasdz-bzas1*dxasdz +byas1*fac1
    by1=byas1*fac2
    bz1=bzas1*dxasdx-bxas1*dzasdx +byas1*fac3

    bx2=bxas2*dzasdz-bzas2*dxasdz +byas2*fac1
    by2=byas2*fac2
    bz2=bzas2*dxasdx-bxas2*dzasdx +byas2*fac3

    return bx1,by1,bz1, bx2,by2,bz2


def warped(iopt,ps, x,y,z):
    """
    Calculates GSM components of the warped field for two tail unit modes. The warping deformation
    is imposed on the unwarped field, computed by the subroutine "unwarped". The warping parameter
    g was obtained by least squares fitting to the entire dataset.

    :param iopt: tail field mode flag: iopt=0 - the two tail modes are added up; iopt=1 - mode 1 only; iopt=2 - mode 2 only
    :param ps: geo-dipole tilt angle in radius.
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return:
    """

    # common /g/ g
    global g
    dgdx,xl,dxldx = [0.,20,0]

    sps=np.sin(ps)
    rho2=y**2+z**2
    rho=np.sqrt(rho2)

    if (y == 0) & (z == 0):
        phi=0.
        cphi=1.
        sphi=0.
    else:
        phi=np.arctan2(z,y)
        cphi=y/rho
        sphi=z/rho

    rr4l4=rho/(rho2**2+xl**4)

    f=phi+g*rho2*rr4l4*cphi*sps
    dfdphi=1-g*rho2*rr4l4*sphi*sps
    dfdrho=g*rr4l4**2*(3*xl**4-rho2**2)*cphi*sps
    dfdx=rr4l4*cphi*sps*(dgdx*rho2-g*rho*rr4l4*4*xl**3*dxldx)

    cf=np.cos(f)
    sf=np.sin(f)
    yas=rho*cf
    zas=rho*sf

    bx_as1,by_as1,bz_as1, bx_as2,by_as2,bz_as2 = unwarped(iopt,x,yas,zas)

    brho_as =  by_as1*cf+bz_as1*sf      # deform the 1st mode
    bphi_as = -by_as1*sf+bz_as1*cf
    brho_s = brho_as*dfdphi
    bphi_s = bphi_as-rho*(bx_as1*dfdx+brho_as*dfdrho)
    bx1    = bx_as1*dfdphi
    by1    = brho_s*cphi-bphi_s*sphi
    bz1    = brho_s*sphi+bphi_s*cphi    # done

    brho_as =  by_as2*cf+bz_as2*sf      # deform the 2nd mode
    bphi_as = -by_as2*sf+bz_as2*cf
    brho_s = brho_as*dfdphi
    bphi_s = bphi_as-rho*(bx_as2*dfdx+brho_as*dfdrho)
    bx2    = bx_as2*dfdphi
    by2    = brho_s*cphi-bphi_s*sphi
    bz2    = brho_s*sphi+bphi_s*cphi    # done

    return bx1,by1,bz1, bx2,by2,bz2



def unwarped(iopt, x,y,z):
    """
    Calculates GSM components of the shielded field of two tail modes with unit amplitudes, without any
        warping or bending. Nonlinear parameters of the modes are forwarded here via a common block /tail/.
    :param iopt: tail field mode flag: iopt=0 - the two tail modes are added up; iopt=1 - mode 1 only; iopt=2 - mode 2 only
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return:
    """

    # common /tail/ dxshift1,dxshift2,d,deltady
    global dxshift1, dxshift2, d, deltady

    deltadx1,alpha1,xshift1 = [1.,1.1,6]
    deltadx2,alpha2,xshift2 = [0.,.25,4]

    a1 = np.array([
        -25.45869857,57.35899080,317.5501869,-2.626756717,-93.38053698,
        -199.6467926,-858.8129729,34.09192395,845.4214929,-29.07463068,
        47.10678547,-128.9797943,-781.7512093,6.165038619,167.8905046,
        492.0680410,1654.724031,-46.77337920,-1635.922669,40.86186772,
        -.1349775602,-.9661991179e-01,-.1662302354,.002810467517,.2487355077,
        .1025565237,-14.41750229,-.8185333989,11.07693629,.7569503173,
        -9.655264745,112.2446542,777.5948964,-5.745008536,-83.03921993,
        -490.2278695,-1155.004209,39.08023320,1172.780574,-39.44349797,
        -14.07211198,-40.41201127,-313.2277343,2.203920979,8.232835341,
        197.7065115,391.2733948,-18.57424451,-437.2779053,23.04976898,
        11.75673963,13.60497313,4.691927060,18.20923547,27.59044809,
        6.677425469,1.398283308,2.839005878,31.24817706,24.53577264])

    a2 = np.array([
        -287187.1962,4970.499233,410490.1952,-1347.839052,-386370.3240,
        3317.983750,-143462.3895,5706.513767,171176.2904,250.8882750,
        -506570.8891,5733.592632,397975.5842,9771.762168,-941834.2436,
        7990.975260,54313.10318,447.5388060,528046.3449,12751.04453,
        -21920.98301,-21.05075617,31971.07875,3012.641612,-301822.9103,
        -3601.107387,1797.577552,-6.315855803,142578.8406,13161.93640,
        804184.8410,-14168.99698,-851926.6360,-1890.885671,972475.6869,
        -8571.862853,26432.49197,-2554.752298,-482308.3431,-4391.473324,
        105155.9160,-1134.622050,-74353.53091,-5382.670711,695055.0788,
        -916.3365144,-12111.06667,67.20923358,-367200.9285,-21414.14421,
        14.75567902,20.75638190,59.78601609,16.86431444,32.58482365,
        23.69472951,17.24977936,13.64902647,68.40989058,11.67828167])

    xm1,xm2 = [-12.,-12]
    bx1,by1,bz1, bx2,by2,bz2 = [0.]*6

    if iopt < 2:    # iopt = 0 or 1
        xsc1 = (x-xshift1-dxshift1)*alpha1-xm1*(alpha1-1)
        ysc1 = y*alpha1
        zsc1 = z*alpha1
        d0sc1 = d*alpha1   # here we use a single value d0 of the thickness for both modes

        fx1,fy1,fz1 = taildisk(d0sc1,deltadx1,deltady,xsc1,ysc1,zsc1)
        hx1,hy1,hz1 = shlcar5x5(a1,x,y,z,dxshift1)

        bx1=fx1+hx1
        by1=fy1+hy1
        bz1=fz1+hz1

    if iopt != 1:   # iop = 0 or 2
        xsc2 = (x-xshift2-dxshift2)*alpha2-xm2*(alpha2-1)
        ysc2 = y*alpha2
        zsc2 = z*alpha2
        d0sc2 = d*alpha2   # here we use a single value d0 of the thickness for both modes

        fx2,fy2,fz2 = taildisk(d0sc2,deltadx2,deltady,xsc2,ysc2,zsc2)
        hx2,hy2,hz2 = shlcar5x5(a2,x,y,z,dxshift2)

        bx2=fx2+hx2
        by2=fy2+hy2
        bz2=fz2+hz2

    return bx1,by1,bz1, bx2,by2,bz2



def shlcar5x5(a,x,y,z,dshift):
    """
    This code returns the shielding field represented by  5x5=25 "cartesian" harmonics
    
    :param a: 
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :param dshift: 
    :return:
    """

    # The nlin coefficients are the amplitudes of the "cartesian" harmonics (a(1)-a(nlin).
    # The nnp nonlinear parameters (a(nlin+1)-a(ntot) are the scales pi and ri entering the arguments of exponents, sines,
    # and cosines in each of the nlin "cartesian" harmonics

    dhx,dhy,dhz = [0.]*3

    l=0
    for i in range(5):
        rp=1/a[50+i]
        cypi=np.cos(y*rp)
        sypi=np.sin(y*rp)

        for k in range(5):
            rr=1/a[55+k]
            szrk=np.sin(z*rr)
            czrk=np.cos(z*rr)
            sqpr=np.sqrt(rp**2+rr**2)
            epr= np.exp(x*sqpr)

            dbx=-sqpr*epr*cypi*szrk
            dby= rp*epr*sypi*szrk
            dbz=-rr*epr*cypi*czrk

            coef=a[l]+a[l+1]*dshift
            l += 2

            dhx=dhx+coef*dbx
            dhy=dhy+coef*dby
            dhz=dhz+coef*dbz

    return dhx,dhy,dhz


def taildisk(d0,deltadx,deltady, x,y,z):
    """
    This subroutine computes the components of the tail current field, similar to that described by
        Tsyganenko and peredo (1994). The difference is that now we use spacewarping, as described in
        our paper on modeling Birkeland currents (Tsyganenko and stern, 1996) instead of shearing it in
        the spirit of the T89 tail model.

    :param d0:
    :param deltadx:
    :param deltady:
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return: bx,by,bz. Field components in GSM system, in nT.
    """

    f = np.array([-71.09346626,-1014.308601,-1272.939359,-3224.935936,-44546.86232])
    b = np.array([10.90101242,12.68393898,13.51791954,14.86775017,15.12306404])
    c = np.array([.7954069972,.6716601849,1.174866319,2.565249920,10.01986790])

    rho=np.sqrt(x**2+y**2)
    drhodx=x/rho
    drhody=y/rho

    dex=np.exp(x/7)
    d=d0+deltady*(y/20)**2+deltadx*dex  # The last term (introduced 10/11/2000) makes the sheet thicken sunward, to avoid problems in the subsolar region
    dddy=deltady*y*0.005
    dddx=deltadx/7*dex

    dzeta=np.sqrt(z**2+d**2)  #  this is the same simple way to spread out the sheet, as that used in t89
    ddzetadx=d*dddx/dzeta
    ddzetady=d*dddy/dzeta
    ddzetadz=z/dzeta

    dbx,dby,dbz = [0.0,0,0]

    for i in range(5):
        bi=b[i]
        ci=c[i]

        s1=np.sqrt((rho+bi)**2+(dzeta+ci)**2)
        s2=np.sqrt((rho-bi)**2+(dzeta+ci)**2)

        ds1drho=(rho+bi)/s1
        ds2drho=(rho-bi)/s2
        ds1ddz=(dzeta+ci)/s1
        ds2ddz=(dzeta+ci)/s2

        ds1dx=ds1drho*drhodx+ds1ddz*ddzetadx
        ds1dy=ds1drho*drhody+ds1ddz*ddzetady
        ds1dz=               ds1ddz*ddzetadz

        ds2dx=ds2drho*drhodx+ds2ddz*ddzetadx
        ds2dy=ds2drho*drhody+ds2ddz*ddzetady
        ds2dz=               ds2ddz*ddzetadz

        s1ts2=s1*s2
        s1ps2=s1+s2
        s1ps2sq=s1ps2**2

        fac1=np.sqrt(s1ps2sq-(2*bi)**2)
        asas=fac1/(s1ts2*s1ps2sq)
        dasds1=(1/(fac1*s2)-asas/s1ps2*(s2*s2+s1*(3*s1+4*s2)))/(s1*s1ps2)
        dasds2=(1/(fac1*s1)-asas/s1ps2*(s1*s1+s2*(3*s2+4*s1)))/(s2*s1ps2)

        dasdx=dasds1*ds1dx+dasds2*ds2dx
        dasdy=dasds1*ds1dy+dasds2*ds2dy
        dasdz=dasds1*ds1dz+dasds2*ds2dz

        dbx=dbx-f[i]*x*dasdz
        dby=dby-f[i]*y*dasdz
        dbz=dbz+f[i]*(2*asas+x*dasdx+y*dasdy)

    return dbx, dby, dbz



def birk_tot(iopb,ps,x,y,z):
    """
    
    :param iopb: birkeland field mode flag:
        iopb=0 - all components; iopb=1 - region 1, modes 1 & 2; iopb=2 - region 2, modes 1 & 2
    :param ps: geo-dipole tilt angle in radius.
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return: bx11,by11,bz11, bx12,by12,bz12, bx21,by21,bz21, bx22,by22,bz22.
    """

    # common /birkpar/ xkappa1,xkappa2   !  input parameters, specified from s/r extall
    # common /dphi_b_rho0/ dphi,b,rho_0,xkappa ! parameters, controlling the day-night asymmetry of f.a.c.
    global xkappa1, xkappa2
    global dphi, b, rho_0, xkappa

    sh11 = np.array([
        46488.84663,-15541.95244,-23210.09824,-32625.03856,-109894.4551,
        -71415.32808,58168.94612,55564.87578,-22890.60626,-6056.763968,
        5091.368100,239.7001538,-13899.49253,4648.016991,6971.310672,
        9699.351891,32633.34599,21028.48811,-17395.96190,-16461.11037,
        7447.621471,2528.844345,-1934.094784,-588.3108359,-32588.88216,
        10894.11453,16238.25044,22925.60557,77251.11274,50375.97787,
        -40763.78048,-39088.60660,15546.53559,3559.617561,-3187.730438,
        309.1487975,88.22153914,-243.0721938,-63.63543051,191.1109142,
        69.94451996,-187.9539415,-49.89923833,104.0902848,-120.2459738,
        253.5572433,89.25456949,-205.6516252,-44.93654156,124.7026309,
        32.53005523,-98.85321751,-36.51904756,98.88241690,24.88493459,
        -55.04058524,61.14493565,-128.4224895,-45.35023460,105.0548704,
        -43.66748755,119.3284161,31.38442798,-92.87946767,-33.52716686,
        89.98992001,25.87341323,-48.86305045,59.69362881,-126.5353789,
        -44.39474251,101.5196856,59.41537992,41.18892281,80.86101200,
        3.066809418,7.893523804,30.56212082,10.36861082,8.222335945,
        19.97575641,2.050148531,4.992657093,2.300564232,.2256245602,-.05841594319])

    sh12 = np.array([
        210260.4816,-1443587.401,-1468919.281,281939.2993,-1131124.839,
        729331.7943,2573541.307,304616.7457,468887.5847,181554.7517,
        -1300722.650,-257012.8601,645888.8041,-2048126.412,-2529093.041,
        571093.7972,-2115508.353,1122035.951,4489168.802,75234.22743,
        823905.6909,147926.6121,-2276322.876,-155528.5992,-858076.2979,
        3474422.388,3986279.931,-834613.9747,3250625.781,-1818680.377,
        -7040468.986,-414359.6073,-1295117.666,-346320.6487,3565527.409,
        430091.9496,-.1565573462,7.377619826,.4115646037,-6.146078880,
        3.808028815,-.5232034932,1.454841807,-12.32274869,-4.466974237,
        -2.941184626,-.6172620658,12.64613490,1.494922012,-21.35489898,
        -1.652256960,16.81799898,-1.404079922,-24.09369677,-10.99900839,
        45.94237820,2.248579894,31.91234041,7.575026816,-45.80833339,
        -1.507664976,14.60016998,1.348516288,-11.05980247,-5.402866968,
        31.69094514,12.28261196,-37.55354174,4.155626879,-33.70159657,
        -8.437907434,36.22672602,145.0262164,70.73187036,85.51110098,
        21.47490989,24.34554406,31.34405345,4.655207476,5.747889264,
        7.802304187,1.844169801,4.867254550,2.941393119,.1379899178,.06607020029])

    sh21 = np.array([
        162294.6224,503885.1125,-27057.67122,-531450.1339,84747.05678,
        -237142.1712,84133.61490,259530.0402,69196.05160,-189093.5264,
        -19278.55134,195724.5034,-263082.6367,-818899.6923,43061.10073,
        863506.6932,-139707.9428,389984.8850,-135167.5555,-426286.9206,
        -109504.0387,295258.3531,30415.07087,-305502.9405,100785.3400,
        315010.9567,-15999.50673,-332052.2548,54964.34639,-152808.3750,
        51024.67566,166720.0603,40389.67945,-106257.7272,-11126.14442,
        109876.2047,2.978695024,558.6019011,2.685592939,-338.0004730,
        -81.99724090,-444.1102659,89.44617716,212.0849592,-32.58562625,
        -982.7336105,-35.10860935,567.8931751,-1.917212423,-260.2023543,
        -1.023821735,157.5533477,23.00200055,232.0603673,-36.79100036,
        -111.9110936,18.05429984,447.0481000,15.10187415,-258.7297813,
        -1.032340149,-298.6402478,-1.676201415,180.5856487,64.52313024,
        209.0160857,-53.85574010,-98.52164290,14.35891214,536.7666279,
        20.09318806,-309.7349530,58.54144539,67.45226850,97.92374406,
        4.752449760,10.46824379,32.91856110,12.05124381,9.962933904,
        15.91258637,1.804233877,6.578149088,2.515223491,.1930034238,-.02261109942])

    sh22 = np.array([
        -131287.8986,-631927.6885,-318797.4173,616785.8782,-50027.36189,
        863099.9833,47680.20240,-1053367.944,-501120.3811,-174400.9476,
        222328.6873,333551.7374,-389338.7841,-1995527.467,-982971.3024,
        1960434.268,297239.7137,2676525.168,-147113.4775,-3358059.979,
        -2106979.191,-462827.1322,1017607.960,1039018.475,520266.9296,
        2627427.473,1301981.763,-2577171.706,-238071.9956,-3539781.111,
        94628.16420,4411304.724,2598205.733,637504.9351,-1234794.298,
        -1372562.403,-2.646186796,-31.10055575,2.295799273,19.20203279,
        30.01931202,-302.1028550,-14.78310655,162.1561899,.4943938056,
        176.8089129,-.2444921680,-100.6148929,9.172262228,137.4303440,
        -8.451613443,-84.20684224,-167.3354083,1321.830393,76.89928813,
        -705.7586223,18.28186732,-770.1665162,-9.084224422,436.3368157,
        -6.374255638,-107.2730177,6.080451222,65.53843753,143.2872994,
        -1028.009017,-64.22739330,547.8536586,-20.58928632,597.3893669,
        10.17964133,-337.7800252,159.3532209,76.34445954,84.74398828,
        12.76722651,27.63870691,32.69873634,5.145153451,6.310949163,
        6.996159733,1.971629939,4.436299219,2.904964304,.1486276863,.06859991529])

    xkappa=xkappa1      # forwarded in birk_1n2
    x_sc=xkappa1-1.1    # forwarded in birk_shl

    bx11,by11,bz11, bx12,by12,bz12, bx21,by21,bz21, bx22,by22,bz22 = [0]*12

    if (iopb == 0) | (iopb == 1):
        fx11,fy11,fz11 = birk_1n2(1,1,ps,x,y,z) # region 1, mode 1
        hx11,hy11,hz11 = birk_shl(sh11,ps,x_sc,x,y,z)
        bx11=fx11+hx11
        by11=fy11+hy11
        bz11=fz11+hz11


        fx12,fy12,fz12 = birk_1n2(1,2,ps,x,y,z) # region 1, mode 2
        hx12,hy12,hz12 = birk_shl(sh12,ps,x_sc,x,y,z)
        bx12=fx12+hx12
        by12=fy12+hy12
        bz12=fz12+hz12

    xkappa=xkappa2      # forwarded in birk_1n2
    x_sc=xkappa2-1.0    # forwarded in birk_shl

    if (iopb == 0) | (iopb == 2):
        fx21,fy21,fz21 = birk_1n2(2,1,ps,x,y,z) # region 2, mode 1
        hx21,hy21,hz21 = birk_shl(sh21,ps,x_sc,x,y,z)
        bx21=fx21+hx21
        by21=fy21+hy21
        bz21=fz21+hz21

        fx22,fy22,fz22 = birk_1n2(2,2,ps,x,y,z) # region 2, mode 2
        hx22,hy22,hz22 = birk_shl(sh22,ps,x_sc,x,y,z)
        bx22=fx22+hx22
        by22=fy22+hy22
        bz22=fz22+hz22

    return bx11,by11,bz11, bx12,by12,bz12, bx21,by21,bz21, bx22,by22,bz22



def birk_1n2(numb,mode,ps,x,y,z):        # NB# 6, p.60
    """
    Calculates components of region 1/2 field in spherical coords. Derived from the s/r dipdef2c
        (which does the same job, but input/output there was in spherical coords, while here we use cartesian ones)
    :param numb: numb=1 (2) for region 1 (2) currents
    :param mode: mode=1 yields simple sinusoidal mlt variation, with maximum current at dawn/dusk meridian
        while mode=2 yields the second harmonic.
    :param ps: geo-dipole tilt angle in radius.
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return: bx,by,bz. Field components in GSM system, in nT.
    """

    # common /dphi_b_rho0/ dphi,b,rho_0,xkappa ! these parameters control day-night asymmetry of f.a.c., as follows:
    #  (1) dphi:   half-difference (in radians) between day and night latitude of fac oval at ionospheric altitude; typical value: 0.06
    #  (2) b:      an asymmetry factor at high-altitudes;  for b=0, the only asymmetry is that from dphi; typical values: 0.35-0.70
    #  (3) rho_0:  a fixed parameter, defining the distance rho, at which the latitude shift gradually saturates and stops increasing; its value was assumed fixed, equal to 7.0.
    #  (4) xkappa: an overall scaling factor, which can be used for changing the size of the f.a.c. oval


    global dtheta, m, dphi, b, rho_0, xkappa

    # parameters of the tilt-dependent deformation of the untilted F.A.C. field
    beta = 0.9
    rh = 10.
    eps = 3.
    b=0.5
    rho_0=7.0

    a11 = np.array([
        .1618068350, -.1797957553, 2.999642482, -.9322708978, -.6811059760,
        .2099057262, -8.358815746, -14.86033550, .3838362986, -16.30945494,
        4.537022847, 2.685836007, 27.97833029, 6.330871059, 1.876532361,
        18.95619213, .9651528100, .4217195118, -.08957770020, -1.823555887,
        .7457045438, -.5785916524, -1.010200918, .01112389357, .09572927448,
        -.3599292276, 8.713700514, .9763932955, 3.834602998, 2.492118385, .7113544659])
    a12 = np.array([
        .7058026940, -.2845938535, 5.715471266, -2.472820880, -.7738802408,
        .3478293930, -11.37653694, -38.64768867, .6932927651, -212.4017288,
        4.944204937, 3.071270411, 33.05882281, 7.387533799, 2.366769108,
        79.22572682, .6154290178, .5592050551, -.1796585105, -1.654932210,
        .7309108776, -.4926292779, -1.130266095, -.009613974555, .1484586169,
        -.2215347198, 7.883592948, .02768251655, 2.950280953, 1.212634762, .5567714182])
    a21 = np.array([
        .1278764024, -.2320034273, 1.805623266, -32.37241440, -.9931490648,
        .3175085630, -2.492465814, -16.21600096, .2695393416, -6.752691265,
        3.971794901, 14.54477563, 41.10158386, 7.912889730, 1.258297372,
        9.583547721, 1.014141963, .5104134759, -.1790430468, -1.756358428,
        .7561986717, -.6775248254, -.04014016420, .01446794851, .1200521731,
        -.2203584559, 4.508963850, .8221623576, 1.779933730, 1.102649543, .8867880020])
    a22 = np.array([
        .4036015198, -.3302974212, 2.827730930, -45.44405830, -1.611103927,
        .4927112073, -.003258457559, -49.59014949, .3796217108, -233.7884098,
        4.312666980, 18.05051709, 28.95320323, 11.09948019, .7471649558,
        67.10246193, .5667096597, .6468519751, -.1560665317, -1.460805289,
        .7719653528, -.6658988668, .2515179349E-05, .02426021891, .1195003324,
        -.2625739255, 4.377172556, .2421190547, 2.503482679, 1.071587299, .7247997430])


    m=mode
    if numb == 1:
        dphi=0.055
        dtheta=0.06
    elif numb == 2:
        dphi=0.030
        dtheta=0.09
    else:
        raise ValueError


    xsc=x*xkappa
    ysc=y*xkappa
    zsc=z*xkappa
    rho=np.sqrt(xsc**2+zsc**2)

    rsc=np.sqrt(xsc**2+ysc**2+zsc**2)   # scaled
    rho2=rho_0**2

    if (xsc == 0) & (zsc == 0):
        phi=0.
    else:
        phi=np.arctan2(-zsc,xsc)  # from cartesian to cylindrical (rho,phi,y)

    sphic=np.sin(phi)
    cphic=np.cos(phi)   # "c" means "cylindrical", to distinguish from spherical phi

    brack=dphi+b*rho2/(rho2+1)*(rho**2-1)/(rho2+rho**2)
    r1rh=(rsc-1)/rh
    psias=beta*ps/(1+r1rh**eps)**(1/eps)

    phis=phi-brack*np.sin(phi) -psias
    dphisphi=1-brack*np.cos(phi)
    dphisrho=-2*b*rho2*rho/(rho2+rho**2)**2*np.sin(phi) \
        +beta*ps*r1rh**(eps-1)*rho/(rh*rsc*(1+r1rh**eps)**(1/eps+1))
    dphisdy= beta*ps*r1rh**(eps-1)*ysc/(rh*rsc*(1+r1rh**eps)**(1/eps+1))

    sphics=np.sin(phis)
    cphics=np.cos(phis)

    xs= rho*cphics
    zs=-rho*sphics

    if numb ==1:
        if mode == 1: [bxs,byas,bzs] = twocones(a11,xs,ysc,zs)
        elif mode == 2: [bxs,byas,bzs] = twocones(a12,xs,ysc,zs)
        else: raise ValueError
    else:
        if mode == 1: [bxs,byas,bzs] = twocones(a21,xs,ysc,zs)
        elif mode == 2: [bxs,byas,bzs] = twocones(a22,xs,ysc,zs)
        else: raise ValueError

    brhoas =  bxs*cphics-bzs*sphics
    bphias = -bxs*sphics-bzs*cphics

    brho_s=brhoas*dphisphi                             *xkappa        # scaling
    bphi_s=(bphias-rho*(byas*dphisdy+brhoas*dphisrho)) *xkappa
    by_s=byas*dphisphi                                 *xkappa

    bx=brho_s*cphic-bphi_s*sphic
    by=by_s
    bz=-brho_s*sphic-bphi_s*cphic

    return bx,by,bz



def twocones (a,x,y,z):
    """
    Adds fields from two cones (northern and southern), with a proper symmetry of the current and field,
        corresponding to the region 1 Birkeland currents. (NB #6, p.58).

    :param a:
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return: bx,by,bz. Field components in GSM system, in nT.
    """

    bxn,byn,bzn = one_cone(a,x, y, z)
    bxs,bys,bzs = one_cone(a,x,-y,-z)
    bx=bxn-bxs
    by=byn+bys
    bz=bzn+bzs

    return bx,by,bz

def one_cone(a,x,y,z):
    """
    Returns field components for a deformed conical current system, fitted to a Biosavart field.
    Here only the northern cone is taken into account.

    :param a: dimension a(31)
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return: bx,by,bz. Field components in GSM system, in nT.
    """

    #  common /dtheta/ dtheta
    #  common /modenum/ m
    global dtheta, m

    # just for numerical differentiation
    dr = 1e-6
    dt = 1e-6

    theta0=a[30]

    rho2=x**2+y**2
    rho=np.sqrt(rho2)
    r=np.sqrt(rho2+z**2)
    theta=np.arctan2(rho,z)
    phi=np.arctan2(y,x)

    # make the deformation of coordinates:
    rs=r_s(a,r,theta)
    thetas=theta_s(a,r,theta)
    phis=phi

    # calculate field components at the new position (asterisked):
    btast,bfast = fialcos(rs,thetas,phis,m,theta0,dtheta)   # mode #m

    # now transform b{r,t,f}_ast by the deformation tensor:
    # first of all, find the derivatives:
    drsdr=(r_s(a,r+dr,theta)-r_s(a,r-dr,theta))/(2*dr)
    drsdt=(r_s(a,r,theta+dt)-r_s(a,r,theta-dt))/(2*dt)
    dtsdr=(theta_s(a,r+dr,theta)-theta_s(a,r-dr,theta))/(2*dr)
    dtsdt=(theta_s(a,r,theta+dt)-theta_s(a,r,theta-dt))/(2*dt)

    stsst=np.sin(thetas)/np.sin(theta)
    rsr=rs/r

    br     =-rsr/r*stsst*btast*drsdt        #   NB#6, p.43    brast does not enter here
    btheta = rsr*stsst*btast*drsdr          #   (it is identically zero in our case)
    bphi   = rsr*bfast*(drsdr*dtsdt-drsdt*dtsdr)

    s=rho/r
    c=z/r
    sf=y/rho
    cf=x/rho

    be=br*s+btheta*c

    bx=a[0]*(be*cf-bphi*sf)
    by=a[0]*(be*sf+bphi*cf)
    bz=a[0]*(br*c-btheta*s)

    return bx,by,bz

def r_s(a,r,theta):

    # dimension a(31)
    return r+a[1]/r+a[2]*r/np.sqrt(r**2+a[10]**2)+a[3]*r/(r**2+a[11]**2) \
        +(a[4]+a[5]/r+a[6]*r/np.sqrt(r**2+a[12]**2)+a[7]*r/(r**2+a[13]**2))*np.cos(theta) \
        +(a[8]*r/np.sqrt(r**2+a[14]**2)+a[9]*r/(r**2+a[15]**2)**2)*np.cos(2*theta)

def theta_s(a,r,theta):
    # dimension a(31)
    return theta+(a[16]+a[17]/r+a[18]/r**2+a[19]*r/np.sqrt(r**2+a[26]**2))*np.sin(theta) \
        +(a[20]+a[21]*r/np.sqrt(r**2+a[27]**2)+a[22]*r/(r**2+a[28]**2))*np.sin(2*theta) \
        +(a[23]+a[24]/r+a[25]*r/(r**2+a[29]**2))*np.sin(3*theta)


def fialcos(r,theta,phi,n,theta0,dt):
    """
    Conical model of Birkeland current field; based on the old s/r fialco (of 1990-91) NB of 1985-86-88,
    note of March 5, but here both input and output are in spherical CDS.
    :param r:
    :param theta:
    :param phi:
    :param n:
    :param theta0:
    :param dt:
    :return: btheta,bphi.
    """

    # btn, and bpn are the arrays of btheta and bphi (btn(i), bpn(i) correspond to i-th mode).
    # only first n mode amplitudes are computed (n<=10).
    # theta0 is the angular half-width of the cone, dt is the angular h.-w. of the current layer
    # note: br=0  (because only radial currents are present in this model)

    # dimension  btn(10),bpn(10),ccos(10),ssin(10)
    btn = np.empty(10)
    bpn = np.empty(10)
    ccos = np.empty(10)
    ssin = np.empty(10)

    sinte=np.sin(theta)
    ro=r*sinte
    coste=np.cos(theta)
    sinfi=np.sin(phi)
    cosfi=np.cos(phi)
    tg=sinte/(1+coste)   # tan(theta/2)
    ctg=sinte/(1-coste)  # cot(theta/2)


    tetanp=theta0+dt
    tetanm=theta0-dt
    if theta >= tetanm:
        tgp=np.tan(tetanp*0.5)
        tgm=np.tan(tetanm*0.5)
        tgm2=tgm*tgm
        tgp2=tgp*tgp

    [cosm1, sinm1] = [1.,0]
    tm = 1
    [tgm2m,tgp2m] = [1.,1]

    for m in range(1,n+1):
        tm=tm*tg
        ccos[m-1]=cosm1*cosfi-sinm1*sinfi
        ssin[m-1]=sinm1*cosfi+cosm1*sinfi
        cosm1=ccos[m-1]
        sinm1=ssin[m-1]
        if theta < tetanm:
            t=tm
            dtt=0.5*m*tm*(tg+ctg)
            dtt0=0
        elif theta < tetanp:
            tgm2m=tgm2m*tgm2
            fc=1/(tgp-tgm)
            fc1=1/(2*m+1)
            tgm2m1=tgm2m*tgm
            tg21=1+tg*tg
            t=fc*(tm*(tgp-tg)+fc1*(tm*tg-tgm2m1/tm))
            dtt=0.5*m*fc*tg21*(tm/tg*(tgp-tg)-fc1*(tm-tgm2m1/(tm*tg)))
            dtt0=0.5*fc*((tgp+tgm)*(tm*tg-fc1*(tm*tg-tgm2m1/tm))+tm*(1-tgp*tgm)-(1+tgm2)*tgm2m/tm)
        else:
            tgp2m=tgp2m*tgp2
            tgm2m=tgm2m*tgm2
            fc=1/(tgp-tgm)
            fc1=1/(2*m+1)
            t=fc*fc1*(tgp2m*tgp-tgm2m*tgm)/tm
            dtt=-t*m*0.5*(tg+ctg)

        btn[m-1]=m*t*ccos[m-1]/ro
        bpn[m-1]=-dtt*ssin[m-1]/r

    btheta=btn[n-1] *800.
    bphi  =bpn[n-1] *800.

    return btheta, bphi




def birk_shl(a,ps,x_sc, x,y,z):
    """
    B due to the Birkeland current shield.
    :param a: coefficient.
    :param ps: geo-dipole tilt angle in radius.
    :param x_sc:
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return: bx,by,bz. Field components in GSM system, in nT.
    """

    cps=np.cos(ps)
    sps=np.sin(ps)

    s3ps=2*cps

    pst1=ps*a[84]
    pst2=ps*a[85]

    st1=np.sin(pst1)
    ct1=np.cos(pst1)
    st2=np.sin(pst2)
    ct2=np.cos(pst2)

    x1=x*ct1-z*st1
    z1=x*st1+z*ct1
    x2=x*ct2-z*st2
    z2=x*st2+z*ct2

    l=0
    [bx,by,bz] = [0,0,0]

    for m in range(1,3):     # m=1 is for the 1st sum ("perp." symmetry) and m=2 is for the second sum ("parall." symmetry)
        for i in range(1,4):
            p = a[71 + i]
            q = a[77 + i]
            cypi = np.cos(y/p)
            cyqi = np.cos(y/q)
            sypi = np.sin(y/p)
            syqi = np.sin(y/q)

            for k in range(1,4):
                r=a[74+k]
                s=a[80+k]
                szrk=np.sin(z1/r)
                czsk=np.cos(z2/s)
                czrk=np.cos(z1/r)
                szsk=np.sin(z2/s)
                sqpr=np.sqrt(1/p**2+1/r**2)
                sqqs=np.sqrt(1/q**2+1/s**2)
                epr=np.exp(x1*sqpr)
                eqs=np.exp(x2*sqqs)

                for n in range(1,3): # n=1 is for the first part of each coefficient and n=2 is for the second one
                    for nn in range(1,3): # nn = 1,2 further splits the coefficients into 2 parts, to take into account the scale factor dependence
                        if m == 1:
                            fx = -sqpr*epr*cypi*szrk
                            fy =  epr*sypi*szrk/p
                            fz = -epr*cypi*czrk/r
                            if n == 1:
                                if nn == 1:
                                    [hx,hy,hz] = [fx,fy,fz]
                                else:
                                    [hx,hy,hz] = [fx*x_sc, fy*x_sc, fz*x_sc]
                            else:
                                if nn == 1:
                                    [hx,hy,hz] = [fx*cps, fy*cps, fz*cps]
                                else:
                                    [hx,hy,hz] = [fx*cps*x_sc, fy*cps*x_sc, fz*cps*x_sc]
                        else: # m == 2
                            fx = -sps*sqqs*eqs*cyqi*czsk
                            fy =  sps/q*eqs*syqi*czsk
                            fz =  sps/s*eqs*cyqi*szsk
                            if n == 1:
                                if nn == 1:
                                    [hx,hy,hz] = [fx,fy,fz]
                                else:
                                    [hx,hy,hz] = [fx*x_sc, fy*x_sc, fz*x_sc]
                            else:
                                if nn == 1:
                                    [hx,hy,hz] = [fx*s3ps,fy*s3ps,fz*s3ps]
                                else:
                                    [hx,hy,hz] = [fx*s3ps*x_sc, fy*s3ps*x_sc, fz*s3ps*x_sc]
                        l=l+1
                        if m == 1:
                            hxr =  hx*ct1+hz*st1
                            hzr = -hx*st1+hz*ct1
                        else:
                            hxr =  hx*ct2+hz*st2
                            hzr = -hx*st2+hz*ct2

                        bx = bx+hxr*a[l-1]
                        by = by+hy *a[l-1]
                        bz = bz+hzr*a[l-1]

    return bx,by,bz



def full_rc(iopr,ps,x,y,z):
    """
    Calculates GSM field components of the symmetric (src) and partial (prc) components of the ring current
    :param iopr: a ring current calculation flag (for least-squares fitting only):
        iopr=0 - both src and prc fields are calculated; opr=1 - src only; opr=2 - prc only
    :param ps: geo-dipole tilt angle in radius.
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return:
    """

    # src  provides a depression of -28 nt at earth
    # prc  corresponds to the pressure difference of 2 npa between midnight and noon ring current particle pressure and yields a depression of -17 nt at x=-6re

    # sc_sy and sc_pr are scaling factors for the symmetric and partial components: values larger than 1 result in spatially larger currents
    # phi is the rotation angle in radians of the partial ring current (measured from midnight toward dusk)
    # common /rcpar/ sc_sy,sc_pr,phi
    global sc_sy, sc_pr, phi


    # corrected values(as of may 2006)
    c_sy = np.array([   # sy short for symmetric
        1675.694858, 1780.006388, -961.6082149, -1668.914259,
        -27.40437029, -107.4169670, 27.76189943, 92.89740503, -43.92949274,
        -403.6444072, 6.167161865, 298.2779761, -1680.779044, -1780.933039,
        964.1861088, 1670.988659, 27.48864650, 107.7809519, -27.84600972,
        -93.20691865, 44.28496784, 404.4537249, -6.281958730, -298.6050952,
        -7.971914848, 2.017383761, -1.492230168, -1.957411655, -.08525523181,
        -.3811813235, .08446716725, .3215044399, -.7141912767, -.9086294596,
        .2966677742, -.04736679933, -11.38731325, .1719795189, 1.356233066,
        .8613438429, -.09143823092, -.2593979098, .04244838338, .06318383319,
        -.5861372726, -.03368780733, -.07104470269, -.06909052953,
        -60.18659631, -32.87563877, 11.76450433, 5.891673644, 2.562360333,
        6.215377232, -1.273945165, -1.864704763, -5.394837143, -8.799382627,
        3.743066561, -.7649164511, 57.09210569, 32.61236511, -11.28688017,
        -5.849523392, -2.470635922, -5.961417272, 1.230031099, 1.793192595,
        5.383736074, 8.369895153, -3.611544412, .7898988697, 7.970609948,
        7.981216562, 35.16822497, 12.45651654, 1.689755359, 3.678712366,
        23.66117284, 6.987136092, 6.886678677, 20.91245928, 1.650064156,
        3.474068566, .3474715765, .6564043111 ])

    c_pr = np.array([   # pr short for partial
        -64820.58481, -63965.62048, 66267.93413, 135049.7504, -36.56316878,
        124.6614669, 56.75637955, -87.56841077, 5848.631425, 4981.097722,
        -6233.712207, -10986.40188, 68716.52057, 65682.69473, -69673.32198,
        -138829.3568, 43.45817708, -117.9565488, -62.14836263, 79.83651604,
        -6211.451069, -5151.633113, 6544.481271, 11353.03491, 23.72352603,
        -256.4846331, 25.77629189, 145.2377187, -4.472639098, -3.554312754,
        2.936973114, 2.682302576, 2.728979958, 26.43396781, -9.312348296,
        -29.65427726, -247.5855336, -206.9111326, 74.25277664, 106.4069993,
        15.45391072, 16.35943569, -5.965177750, -6.079451700, 115.6748385,
        -35.27377307, -32.28763497, -32.53122151, 93.74409310, 84.25677504,
        -29.23010465, -43.79485175, -6.434679514, -6.620247951, 2.443524317,
        2.266538956, -43.82903825, 6.904117876, 12.24289401, 17.62014361,
        152.3078796, 124.5505289, -44.58690290, -63.02382410, -8.999368955,
        -9.693774119, 3.510930306, 3.770949738, -77.96705716, 22.07730961,
        20.46491655, 18.67728847, 9.451290614, 9.313661792, 644.7620970,
        418.2515954, 7.183754387, 35.62128817, 19.43180682, 39.57218411,
        15.69384715, 7.123215241, 2.300635346, 21.90881131, -.01775839370, .3996346710])


    hxsrc,hysrc,hzsrc, hxprc,hyprc,hzprc = src_prc(iopr, sc_sy,sc_pr, phi, ps, x,y,z)

    x_sc=sc_sy-1
    fsx,fsy,fsz = [0.]*3
    if (iopr == 0) | (iopr == 1):
        fsx,fsy,fsz = rc_shield(c_sy,ps,x_sc, x,y,z)

    x_sc=sc_pr-1
    fpx,fpy,fpz = [0.]*3
    if (iopr == 0) | (iopr == 2):
        fpx,fpy,fpz = rc_shield(c_pr,ps,x_sc, x,y,z)

    bxsrc=hxsrc+fsx
    bysrc=hysrc+fsy
    bzsrc=hzsrc+fsz

    bxprc=hxprc+fpx
    byprc=hyprc+fpy
    bzprc=hzprc+fpz

    return bxsrc,bysrc,bzsrc,bxprc,byprc,bzprc



def src_prc(iopr,sc_sy,sc_pr,phi,ps, x,y,z):
    """
    Returns field components from a model ring current, including its symmetric part and a partial ring current,
        closed via birkeland currents. based on results, described in a paper "modeling the inner magnetosphere:
        asymmetric ring current and region 2 birkeland currents revisited" (jgr, dec.2000).
    :param iopr: a ring current calculation flag (for least-squares fitting only):
        iopr=0 - both src and prc fields are calculated; opr=1 - src only; opr=2 - prc only
    :param sc_sy, sc_pr: scale factors for the above components; taking sc<1 or sc>1 makes the currents shrink or expand, respectively.
    :param phi: the rotation angle (radians) of the partial ring current (measured from midnight toward dusk)
    :param ps: geo-dipole tilt angle in radius.
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return: bxsrc,bysrc,bzsrc, bxprc,byprc,bzprc. Field components in GSM system, in nT. For the symmetric part and partial ring current.
    """

    # 1. transform to tilted coordinates (i.e., sm coordinates):
    cps=np.cos(ps)
    sps=np.sin(ps)

    xt=x*cps-z*sps
    zt=z*cps+x*sps

    # 2. scale the coordinates for the symmetric and partial rc components:
    xts=xt/sc_sy    # symmetric
    yts=y /sc_sy
    zts=zt/sc_sy

    xta=xt/sc_pr    # partial
    yta=y /sc_pr
    zta=zt/sc_pr

    # 3. calculate components of the total field in the tilted (solar-magnetic) coordinate system:

    # only for least squares fitting:
    bxs,bys,bzs = [0.]*3
    bxa_s,bya_s,bza_s = [0.]*3
    bxa_qr,bya_qr,bza_q = [0.]*3

    # 3a. symmetric field:
    if iopr <= 1:
        bxs,bys,bzs = rc_symm(xts,yts,zts)
    if (iopr == 0) | (iopr == 2):
        bxa_s,bya_s,bza_s = prc_symm(xta,yta,zta)

    # 3b. rotate the scaled sm coordinates by phi around zsm axis and calculate quadrupole prc field in those coords:
    cp=np.cos(phi)
    sp=np.sin(phi)
    xr=xta*cp-yta*sp
    yr=xta*sp+yta*cp
    if (iopr == 0) | (iopr == 2):
        bxa_qr,bya_qr,bza_q = prc_quad(xr,yr,zta)

    # 3c.transform the quadrupole field components back to the sm coords:
    bxa_q= bxa_qr*cp+bya_qr*sp
    bya_q=-bxa_qr*sp+bya_qr*cp

    # 3d. find the total field of prc (symm.+quadr.) in the sm coords:
    bxp=bxa_s+bxa_q
    byp=bya_s+bya_q
    bzp=bza_s+bza_q

    # 4. transform the fields of both parts of the ring current back to the gsm system:
    bxsrc=bxs*cps+bzs*sps   # symmetric rc
    bysrc=bys
    bzsrc=bzs*cps-bxs*sps

    bxprc=bxp*cps+bzp*sps   # partial rc
    byprc=byp
    bzprc=bzp*cps-bxp*sps

    return bxsrc,bysrc,bzsrc, bxprc,byprc,bzprc


def rc_symm(x,y,z):
    """
    Calculates the field components from a model ring current, due to its symmetric part.
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return: bx,by,bz. Field components in GSM system, in nT.
    """

    # ds=sin(theta) at the boundary of the linearity region; dc=sqrt(1-ds**2);  drd=1/(2*d)
    ds = 1e-2
    dc = 0.99994999875
    d = 1e-4
    drd = 5e3

    rho2=x**2+y**2
    r2=rho2+z**2
    r=np.sqrt(r2)
    rp=r+d
    rm=r-d
    sint=np.sqrt(rho2)/r
    cost=z/r

    # too close to the z-axis; using a linear approximation a_phi~sint to avoid the singularity problem
    if sint < ds:
        a=ap(r,ds,dc)/ds
        dardr=(rp*ap(rp,ds,dc)-rm*ap(rm,ds,dc))*drd
        fxy=z*(2*a-dardr)/(r*r2)
        bx=fxy*x
        by=fxy*y
        bz=(2*a*cost**2+dardr*sint**2)/r
    else:
        theta=np.arctan2(sint,cost)
        tp=theta+d
        tm=theta-d
        sintp=np.sin(tp)
        sintm=np.sin(tm)
        costp=np.cos(tp)
        costm=np.cos(tm)
        br=(sintp*ap(r,sintp,costp)-sintm*ap(r,sintm,costm))/(r*sint)*drd
        bt=(rm*ap(rm,sint,cost)-rp*ap(rp,sint,cost))/r*drd
        fxy=(br+bt*cost/sint)/r
        bx=fxy*x
        by=fxy*y
        bz=br*cost-bt*sint

    return bx, by, bz

def ap(r,sint,cost):
    """
    Calculates azimuthal component of the vector potential of the symmetric part of the model ring current.
    :param r:
    :param sint:
    :param cost:
    :return:
    """

    a1,a2,rrc1,dd1,rrc2,dd2,p1,r1,dr1,dla1,p2,r2,dr2,dla2,p3,r3,dr3 = [
        -563.3722359,425.0891691,4.150588549,2.266150226,3.334503403,
        3.079071195,.02602428295,8.937790598,3.327934895,.4487061833,
        .09125832351,6.243029867,1.750145910,.4181957162,.06106691992,
        2.079908581,.6828548533]

# indicates whether we are too close to the axis of symmetry, where the inversion of dipolar coordinates becomes inaccurate
    prox = False
    sint1=sint
    cost1=cost
    # too close to z-axis; use linear interpolation between sint=0 & sint=0.01
    if (sint1 < 1.e-2):
        sint1=1.e-2
        cost1=0.99994999875
        prox=True

    alpha=sint1**2/r    # r,theta -> alpha,gamma
    gamma=cost1/r**2

    arg1=-((r-r1)/dr1)**2-(cost1/dla1)**2
    arg2=-((r-r2)/dr2)**2-(cost1/dla2)**2
    arg3=-((r-r3)/dr3)**2

    if arg1 < -500:     # to prevent "floating underflow" crashes
        dexp1=0.
    else:
        dexp1=np.exp(arg1)

    if arg2 < -500:     # to prevent "floating underflow" crashes
        dexp2=0.
    else:
        dexp2=np.exp(arg2)

    if arg3 < -500:     # to prevent "floating underflow" crashes
        dexp3=0.
    else:
        dexp3=np.exp(arg3)

    # alpha -> alpha_s  (deformed)
    alpha_s=alpha*(1+p1*dexp1+p2*dexp2+p3*dexp3)
    gamma_s=gamma
    gammas2=gamma_s**2

    # alpha_s,gamma_s -> rs,sints,costs
    alsqh=alpha_s**2/2
    f=64/27*gammas2+alsqh**2
    q=(np.sqrt(f)+alsqh)**(1/3)
    c=q-4*gammas2**(1/3)/(3*q)
    if c < 0: c=0
    g=np.sqrt(c**2+4*gammas2**(1/3))
    rs=4/((np.sqrt(2*g-c)+np.sqrt(c))*(g+c))
    costs=gamma_s*rs**2
    sints=np.sqrt(1-costs**2)
    rhos=rs*sints
    rhos2=rhos**2
    zs=rs*costs

    # TODO looks like this part is repetative.
    p=(rrc1+rhos)**2+zs**2+dd1**2
    xk2=4*rrc1*rhos/p
    xk=np.sqrt(xk2)
    xkrho12=xk*np.sqrt(rhos)    # see nb#4, p.3

    xk2s = 1-xk2
    dl = np.log(1/xk2s)
    elk = 1.38629436112 + xk2s*(0.09666344259+xk2s*(0.03590092383+xk2s*(0.03742563713+xk2s*0.01451196212)))\
        + dl*(0.5+xk2s*(0.12498593597+xk2s*(0.06880248576+xk2s*(0.03328355346+xk2s*0.00441787012))))
    ele = 1+xk2s*(0.44325141463+xk2s*(0.0626060122+xk2s*(0.04757383546+xk2s*0.01736506451)))\
        + dl*xk2s*(0.2499836831+xk2s*(0.09200180037+xk2s*(0.04069697526+xk2s*0.00526449639)))
    aphi1=((1-xk2*0.5)*elk-ele)/xkrho12


    p=(rrc2+rhos)**2+zs**2+dd2**2
    xk2=4*rrc2*rhos/p
    xk=np.sqrt(xk2)
    xkrho12=xk*np.sqrt(rhos)    # see nb#4, p.3

    xk2s = 1-xk2
    dl = np.log(1/xk2s)
    elk = 1.38629436112 + xk2s*(0.09666344259+xk2s*(0.03590092383+xk2s*(0.03742563713+xk2s*0.01451196212)))\
        + dl*(0.5+xk2s*(0.12498593597+xk2s*(0.06880248576+xk2s*(0.03328355346+xk2s*0.00441787012))))
    ele = 1+xk2s*(0.44325141463+xk2s*(0.0626060122+xk2s*(0.04757383546+xk2s*0.01736506451)))\
        + dl*xk2s*(0.2499836831+xk2s*(0.09200180037+xk2s*(0.04069697526+xk2s*0.00526449639)))
    aphi2=((1-xk2*0.5)*elk-ele)/xkrho12

    ap=a1*aphi1+a2*aphi2
    if prox:
        ap=ap*sint/sint1    # linear interpolation, if too close to the z-axis

    return ap


def prc_symm(x,y,z):
    """
    Calculates the field components from a model ring current, due to a partial ring current.
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return: bx,by,bz. Field components in GSM system, in nT.
    """

    # ds=sin(theta) at the boundary of the linearity region; dc=sqrt(1-ds**2);  drd=1/(2*d)
    ds = 1e-2
    dc = 0.99994999875
    d = 1e-4
    drd = 5e3

    rho2=x**2+y**2
    r2=rho2+z**2
    r=np.sqrt(r2)
    rp=r+d
    rm=r-d
    sint=np.sqrt(rho2)/r
    cost=z/r

    # too close to the z-axis; using a linear approximation a_phi~sint to avoid the singularity problem
    if sint < ds:
        a=apprc(r,ds,dc)/ds
        dardr=(rp*apprc(rp,ds,dc)-rm*apprc(rm,ds,dc))*drd
        fxy=z*(2*a-dardr)/(r*r2)
        bx=fxy*x
        by=fxy*y
        bz=(2*a*cost**2+dardr*sint**2)/r
    else:
        theta=np.arctan2(sint,cost)
        tp=theta+d
        tm=theta-d
        sintp=np.sin(tp)
        sintm=np.sin(tm)
        costp=np.cos(tp)
        costm=np.cos(tm)
        br=(sintp*apprc(r,sintp,costp)-sintm*apprc(r,sintm,costm))/(r*sint)*drd
        bt=(rm*apprc(rm,sint,cost)-rp*apprc(rp,sint,cost))/r*drd
        fxy=(br+bt*cost/sint)/r
        bx=fxy*x
        by=fxy*y
        bz=br*cost-bt*sint

    return bx, by, bz

def apprc(r,sint,cost):
    """
    Calculates azimuthal component of the vector potential of the symmetric part of the model partial ring current.
    :param r:
    :param sint:
    :param cost:
    :return:
    """

    a1,a2,rrc1,dd1,rrc2,dd2,p1,alpha1,dal1,beta1,dg1,p2,alpha2,dal2,beta2,dg2,beta3,p3,\
    alpha3,dal3,beta4,dg3,beta5,q0,q1,alpha4,dal4,dg4,q2,alpha5,dal5,dg5,beta6,beta7 = [
        -80.11202281,12.58246758,6.560486035,1.930711037,3.827208119,
        .7789990504,.3058309043,.1817139853,.1257532909,3.422509402,
        .04742939676,-4.800458958,-.02845643596,.2188114228,2.545944574,
        .00813272793,.35868244,103.1601001,-.00764731187,.1046487459,
        2.958863546,.01172314188,.4382872938,.01134908150,14.51339943,
        .2647095287,.07091230197,.01512963586,6.861329631,.1677400816,
        .04433648846,.05553741389,.7665599464,.7277854652]

    prox=False
    sint1=sint
    cost1=cost
    # too close to z-axis; use linear interpolation between sint=0 & sint=0.01
    if (sint1 < 1.e-2):
        sint1=1.e-2
        cost1=0.99994999875
        prox=True

    alpha=sint1**2/r    # r,theta -> alpha,gamma
    gamma=cost1/r**2

    arg1=-(gamma/dg1)**2
    arg2=-((alpha-alpha4)/dal4)**2-(gamma/dg4)**2

    if arg1 < -500:     # to prevent "floating underflow" crashes
        dexp1=0.
    else:
        dexp1=np.exp(arg1)

    if arg2 < -500:     # to prevent "floating underflow" crashes
        dexp2=0.
    else:
        dexp2=np.exp(arg2)

    # alpha -> alpha_s  (deformed)
    alpha_s = alpha*(1 + p1/(1+((alpha-alpha1)/dal1)**2)**beta1*dexp1
        + p2*(alpha-alpha2)/(1+((alpha-alpha2)/dal2)**2)**beta2/(1+(gamma/dg2)**2)**beta3
        + p3*(alpha-alpha3)**2/(1.+((alpha-alpha3)/dal3)**2)**beta4/(1+(gamma/dg3)**2)**beta5)
    # gamma -> gamma_s  (deformed)
    gamma_s = gamma*(1 + q0 + q1*(alpha-alpha4)*dexp2
        + q2*(alpha-alpha5)/(1+((alpha-alpha5)/dal5)**2)**beta6/(1+(gamma/dg5)**2)**beta7)

    gammas2 = gamma_s**2

    # alpha_s,gamma_s -> rs,sints,costs
    alsqh=alpha_s**2/2.
    f=64./27.*gammas2+alsqh**2
    q=(np.sqrt(f)+alsqh)**(1/3)
    c=q-4.*gammas2**(1/3)/(3.*q)
    if c < 0: c=0
    g=np.sqrt(c**2+4*gammas2**(1/3))
    rs=4./((np.sqrt(2*g-c)+np.sqrt(c))*(g+c))
    costs=gamma_s*rs**2
    sints=np.sqrt(1-costs**2)
    rhos=rs*sints
    rhos2=rhos**2
    zs=rs*costs


    # TODO looks like this part is repetative.
    p=(rrc1+rhos)**2+zs**2+dd1**2
    xk2=4*rrc1*rhos/p
    xk=np.sqrt(xk2)
    xkrho12=xk*np.sqrt(rhos)    # see nb#4, p.3

    xk2s = 1-xk2
    dl = np.log(1/xk2s)
    elk = 1.38629436112 + xk2s*(0.09666344259+xk2s*(0.03590092383+xk2s*(0.03742563713+xk2s*0.01451196212)))\
        + dl*(0.5+xk2s*(0.12498593597+xk2s*(0.06880248576+xk2s*(0.03328355346+xk2s*0.00441787012))))
    ele = 1 + xk2s*(0.44325141463+xk2s*(0.0626060122+xk2s*(0.04757383546+xk2s*0.01736506451)))\
        + dl*xk2s*(0.2499836831+xk2s*(0.09200180037+xk2s*(0.04069697526+xk2s*0.00526449639)))
    aphi1=((1-xk2*0.5)*elk-ele)/xkrho12


    p=(rrc2+rhos)**2+zs**2+dd2**2
    xk2=4*rrc2*rhos/p
    xk=np.sqrt(xk2)
    xkrho12=xk*np.sqrt(rhos)    # see nb#4, p.3

    xk2s = 1-xk2
    dl = np.log(1/xk2s)
    elk = 1.38629436112 + xk2s*(0.09666344259+xk2s*(0.03590092383+xk2s*(0.03742563713+xk2s*0.01451196212)))\
        + dl*(0.5+xk2s*(0.12498593597+xk2s*(0.06880248576+xk2s*(0.03328355346+xk2s*0.00441787012))))
    ele = 1 + xk2s*(0.44325141463+xk2s*(0.0626060122+xk2s*(0.04757383546+xk2s*0.01736506451)))\
        + dl*xk2s*(0.2499836831+xk2s*(0.09200180037+xk2s*(0.04069697526+xk2s*0.00526449639)))
    aphi2=((1-xk2*0.5)*elk-ele)/xkrho12

    apprc=a1*aphi1+a2*aphi2
    if prox:
        apprc=apprc*sint/sint1  # linear interpolation, if too close to the z-axis

    return apprc


def prc_quad(x,y,z):
    """
    Calculates components of the field from the "quadrupole" component of the partial ring current.
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return: bx,by,bz. Field components in GSM system, in nT.
    """
    d  = 1e-4
    dd = 2e-4
    ds = 1e-2
    dc = 0.99994999875

    rho2=x**2+y**2
    r=np.sqrt(rho2+z**2)
    rho=np.sqrt(rho2)
    sint=rho/r
    cost=z/r
    rp=r+d
    rm=r-d

    if sint > ds:
        cphi=x/rho
        sphi=y/rho
        br=br_prc_q(r,sint,cost)
        bt=bt_prc_q(r,sint,cost)
        dbrr=(br_prc_q(rp,sint,cost)-br_prc_q(rm,sint,cost))/dd
        theta=np.arctan2(sint,cost)
        tp=theta+d
        tm=theta-d
        sintp=np.sin(tp)
        costp=np.cos(tp)
        sintm=np.sin(tm)
        costm=np.cos(tm)
        dbtt=(bt_prc_q(r,sintp,costp)-bt_prc_q(r,sintm,costm))/dd
        bx=sint*(br+(br+r*dbrr+dbtt)*sphi**2)+cost*bt
        by=-sint*sphi*cphi*(br+r*dbrr+dbtt)
        bz=(br*cost-bt*sint)*cphi
    else:
        st=ds
        ct=dc
        if z < 0: ct=-dc
        theta=np.arctan2(st,ct)
        tp=theta+d
        tm=theta-d
        sintp=np.sin(tp)
        costp=np.cos(tp)
        sintm=np.sin(tm)
        costm=np.cos(tm)
        br=br_prc_q(r,st,ct)
        bt=bt_prc_q(r,st,ct)
        dbrr=(br_prc_q(rp,st,ct)-br_prc_q(rm,st,ct))/dd
        dbtt=(bt_prc_q(r,sintp,costp)-bt_prc_q(r,sintm,costm))/dd
        fcxy=r*dbrr+dbtt
        bx=(br*(x**2+2.*y**2)+fcxy*y**2)/(r*st)**2+bt*cost
        by=-(br+fcxy)*x*y/(r*st)**2
        bz=(br*cost/st-bt)*x/r

    return bx,by,bz

def br_prc_q(r,sint,cost):
    """
    Calculates the radial component of the "quadrupole" part of the model partial ring current.
    :param r:
    :param sint:
    :param cost:
    :return:
    """

    a1   = -21.2666329
    a2   = 32.24527521
    a3   = -6.062894078
    a4   = 7.515660734
    a5   = 233.7341288
    a6   = -227.1195714
    a7   = 8.483233889
    a8   = 16.80642754
    a9   = -24.63534184
    a10  = 9.067120578
    a11  = -1.052686913
    a12  = -12.08384538
    a13  = 18.61969572
    a14  = -12.71686069
    a15  = 47017.35679
    a16  = -50646.71204
    a17  = 7746.058231
    a18  = 1.531069371
    xk1  = 2.318824273
    al1  = 0.1417519429
    dal1 = 0.6388013110e-02
    b1   = 5.303934488
    be1  = 4.213397467
    xk2  = 0.7955534018
    al2  = 0.1401142771
    dal2 = 0.2306094179e-01
    b2   = 3.462235072
    be2  = 2.568743010
    xk3  = 3.477425908
    xk4  = 1.922155110
    al3  = 0.1485233485
    dal3 = 0.2319676273e-01
    b3   = 7.830223587
    be3  = 8.492933868
    al4  = 0.1295221828
    dal4 = 0.01753008801
    dg1  = 0.01125504083
    al5  = 0.1811846095
    dal5 = 0.04841237481
    dg2  = 0.01981805097
    c1   = 6.557801891
    c2   = 6.348576071
    c3   = 5.744436687
    al6  = 0.2265212965
    dal6 = 0.1301957209
    drm  = 0.5654023158

    sint2=sint**2
    cost2=cost**2
    sc=sint*cost
    alpha=sint2/r
    gamma=cost/r**2

    f,fa,fs = ffs(alpha,al1,dal1)
    d1=sc*f**xk1/((r/b1)**be1+1.)
    d2=d1*cost2

    f,fa,fs = ffs(alpha,al2,dal2)
    d3=sc*fs**xk2/((r/b2)**be2+1.)
    d4=d3*cost2

    f,fa,fs = ffs(alpha,al3,dal3)
    d5=sc*(alpha**xk3)*(fs**xk4)/((r/b3)**be3+1.)
    d6=d5*cost2

    arga=((alpha-al4)/dal4)**2+1.
    argg=1.+(gamma/dg1)**2

    d7=sc/arga/argg
    d8=d7/arga
    d9=d8/arga
    d10=d9/arga

    arga=((alpha-al5)/dal5)**2+1.
    argg=1.+(gamma/dg2)**2

    d11=sc/arga/argg
    d12=d11/arga
    d13=d12/arga
    d14=d13/arga

    d15=sc/(r**4+c1**4)
    d16=sc/(r**4+c2**4)*cost2
    d17=sc/(r**4+c3**4)*cost2**2

    f,fa,fs = ffs(alpha,al6,dal6)
    d18=sc*fs/(1.+((r-1.2)/drm)**2)

    br_prc_q=a1*d1+a2*d2+a3*d3+a4*d4+a5*d5+a6*d6+a7*d7+a8*d8+a9*d9+\
             a10*d10+a11*d11+a12*d12+a13*d13+a14*d14+a15*d15+a16*d16+a17*d17+a18*d18

    return br_prc_q

def bt_prc_q(r,sint,cost):
    """
    Calculates the theta component of the "quadrupole" part of the model partial ring current.

    :param r:
    :param sint:
    :param cost:
    :return:
    """

    # all linear parameters here were multiplied by 0.1, so that they correspond to p_0=1 npa,
    # rather than the original value of 10 npa assumed in the biot-savart integral.
    a1   = 12.74640393
    a2   = -7.516393516
    a3   = -5.476233865
    a4   = 3.212704645
    a5   = -59.10926169
    a6   = 46.62198189
    a7   = -.01644280062
    a8   = 0.1234229112
    a9   = -.08579198697
    a10  = 0.01321366966
    a11  = 0.8970494003
    a12  = 9.136186247
    a13  = -38.19301215
    a14  = 21.73775846
    a15  = -410.0783424
    a16  = -69.90832690
    a17  = -848.8543440
    xk1  = 1.243288286
    al1  = 0.2071721360
    dal1 = 0.05030555417
    b1   = 7.471332374
    be1  = 3.180533613
    xk2  = 1.376743507
    al2  = 0.1568504222
    dal2 = 0.02092910682
    be2  = 1.985148197
    xk3  = 0.3157139940
    xk4  = 1.056309517
    al3  = 0.1701395257
    dal3 = 0.1019870070
    b3   = 6.293740981
    be3  = 5.671824276
    al4  = 0.1280772299
    dal4 = 0.02189060799
    dg1  = 0.01040696080
    al5  = 0.1648265607
    dal5 = 0.04701592613
    dg2  = 0.01526400086
    c1   = 12.88384229
    c2   = 3.361775101
    c3   = 23.44173897

    sint2=sint**2
    cost2=cost**2
    sc=sint*cost
    alpha=sint2/r
    gamma=cost/r**2

    f,fa,fs = ffs(alpha,al1,dal1)
    d1=f**xk1/((r/b1)**be1+1.)
    d2=d1*cost2

    f,fa,fs = ffs(alpha,al2,dal2)
    d3=fa**xk2/r**be2
    d4=d3*cost2

    f,fa,fs = ffs(alpha,al3,dal3)
    d5=fs**xk3*alpha**xk4/((r/b3)**be3+1.)
    d6=d5*cost2

    f,fa,fs = ffs(gamma,0.,dg1)
    fcc=(1.+((alpha-al4)/dal4)**2)
    d7 =1./fcc*fs
    d8 =d7/fcc
    d9 =d8/fcc
    d10=d9/fcc

    arg=1.+((alpha-al5)/dal5)**2
    d11=1./arg/(1.+(gamma/dg2)**2)
    d12=d11/arg
    d13=d12/arg
    d14=d13/arg

    d15=1./(r**4+c1**2)
    d16=cost2/(r**4+c2**2)
    d17=cost2**2/(r**4+c3**2)

    bt_prc_q = a1*d1+a2*d2+a3*d3+a4*d4+a5*d5+a6*d6+a7*d7+a8*d8+a9*d9+\
               a10*d10+a11*d11+a12*d12+a13*d13+a14*d14+a15*d15+a16*d16+a17*d17

    return bt_prc_q

def ffs(a, a0, da):
    sq1 = np.sqrt((a + a0) ** 2 + da ** 2)
    sq2 = np.sqrt((a - a0) ** 2 + da ** 2)
    fa = 2. / (sq1 + sq2)
    f = fa * a
    fs = 0.5 * (sq1 + sq2) / (sq1 * sq2) * (1.-f * f)

    return f, fa, fs


def rc_shield(a,ps,x_sc,x,y,z):
    """
    B due to the ring current shield.
    :param a: coefficient.
    :param ps: geo-dipole tilt angle in radius.
    :param x_sc: scaling factors.
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return: bx,by,bz. Field components in GSM system, in nT.
    """

    fac_sc = (x_sc+1)**3

    cps = np.cos(ps)
    sps = np.sin(ps)

    s3ps=2*cps

    pst1=ps*a[84]
    pst2=ps*a[85]

    st1=np.sin(pst1)
    ct1=np.cos(pst1)
    st2=np.sin(pst2)
    ct2=np.cos(pst2)

    x1=x*ct1-z*st1
    z1=x*st1+z*ct1
    x2=x*ct2-z*st2
    z2=x*st2+z*ct2

    l=0
    [bx,by,bz] = [0.]*3

    for m in range(2):     # m=1 is for the 1st sum ("perp." symmetry) and m=2 is for the second sum ("parall." symmetry)
        for i in range(3):
            p=a[72+i]
            q=a[78+i]
            cypi=np.cos(y/p)
            cyqi=np.cos(y/q)
            sypi=np.sin(y/p)
            syqi=np.sin(y/q)

            for k in range(3):
                r=a[75+k]
                s=a[81+k]
                szrk=np.sin(z1/r)
                czsk=np.cos(z2/s)
                czrk=np.cos(z1/r)
                szsk=np.sin(z2/s)
                sqpr=np.sqrt(1/p**2+1/r**2)
                sqqs=np.sqrt(1/q**2+1/s**2)
                epr=np.exp(x1*sqpr)
                eqs=np.exp(x2*sqqs)

                for n in range(2): # n=1 is for the first part of each coefficient and n=2 is for the second one
                    for nn in range(2): # nn = 1,2 further splits the coefficients into 2 parts, to take into account the scale factor dependence
                        if m == 0:
                            fx = -sqpr*epr*cypi*szrk*fac_sc
                            fy =  epr*sypi*szrk/p   *fac_sc
                            fz = -epr*cypi*czrk/r   *fac_sc
                            if n == 0:
                                if nn == 0:
                                    [hx,hy,hz] = [fx,fy,fz]
                                else:
                                    [hx,hy,hz] = [fx*x_sc, fy*x_sc, fz*x_sc]
                            else:
                                if nn == 0:
                                    [hx,hy,hz] = [fx*cps, fy*cps, fz*cps]
                                else:
                                    [hx,hy,hz] = [fx*cps*x_sc, fy*cps*x_sc, fz*cps*x_sc]
                        else: # m == 2
                            fx = -sps*sqqs*eqs*cyqi*czsk*fac_sc
                            fy =  sps/q*eqs*syqi*czsk   *fac_sc
                            fz =  sps/s*eqs*cyqi*szsk   *fac_sc
                            if n == 0:
                                if nn == 0:
                                    [hx,hy,hz] = [fx,fy,fz]
                                else:
                                    [hx,hy,hz] = [fx*x_sc,fy*x_sc,fz*x_sc]
                            else:
                                if nn == 0:
                                    [hx,hy,hz] = [fx*s3ps,fy*s3ps,fz*s3ps]
                                else:
                                    [hx,hy,hz] = [fx*s3ps*x_sc, fy*s3ps*x_sc, fz*s3ps*x_sc]

                        if m == 0:
                            hxr =  hx*ct1+hz*st1
                            hzr = -hx*st1+hz*ct1
                        else:
                            hxr =  hx*ct2+hz*st2
                            hzr = -hx*st2+hz*ct2

                        bx = bx+hxr*a[l]
                        by = by+hy *a[l]
                        bz = bz+hzr*a[l]
                        l=l+1

    return bx, by, bz


def dipole(ps, x, y, z):
    """
    Calculates GSM components of a geo-dipole field with the dipole moment corresponding to the epoch of 2000.

    :param ps: geo-dipole tilt angle in radius.
    :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
    :return: bx,by,bz. Field components in GSM system, in nT.
    """

    q0 = 30115. # nT.

    sps = np.sin(ps)
    cps = np.cos(ps)
    x2 = x ** 2
    y2 = y ** 2
    z2 = z ** 2
    xz3 = 3 * x * z
    q = q0 / np.sqrt(x2 + y2 + z2) ** 5
    bx = q * ((y2 + z2 - 2 * x2) * sps - xz3 * cps)
    by = -3 * y * q * (x * sps + z * cps)
    bz = q * ((x2 + y2 - 2 * z2) * cps - xz3 * sps)

    return bx, by, bz


# x,y,z,ps = [-5.1,0.3,2.8, -0.533585131]
# iopt = 2
# par = [2,-87,2,-5, 0,0, ps,x,y,z]
# bx,by,bz = t01(iopt, par, ps, x,y,z)
# print(bx,by,bz)
