def analyse_equil(F, R, Z):
    s = numpy.shape(F)
    nx = s[0]
    ny = s[1]

    # ;;;;;;;;;;;;;;; Find critical points ;;;;;;;;;;;;;
    #
    # Need to find starting locations for O-points (minima/maxima)
    # and X-points (saddle points)
    #
    Rr = numpy.tile(R, nx).reshape(nx, ny).T  # needed for contour
    Zz = numpy.tile(Z, ny).reshape(nx, ny)

    contour1 = contour(Rr, Zz, gradient(F)[0], levels=[0.0], colors="r")
    contour2 = contour(Rr, Zz, gradient(F)[1], levels=[0.0], colors="r")

    draw()

    ### --- line crossings ---------------------------
    lines = find_inter(contour1, contour2)

    rex = numpy.interp(lines[0], R, numpy.arange(R.size)).astype(int)
    zex = numpy.interp(lines[1], Z, numpy.arange(Z.size)).astype(int)

    w = numpy.where((rex > 2) & (rex < nx - 3) & (zex > 2) & (zex < nx - 3))
    nextrema = numpy.size(w)
    rex = rex[w].flatten()
    zex = zex[w].flatten()
    rp = lines[0][w]
    zp = lines[1][w]

    # ;;;;;;;;;;;;;; Characterise extrema ;;;;;;;;;;;;;;;;;
    # Fit a surface through local points using 6x6 matrix
    # This is to determine the type of extrema, and to
    # refine the location
    #

    n_opoint = 0
    n_xpoint = 0

    # Calculate inverse matrix
    rio = numpy.array([-1, 0, 0, 0, 1, 1])  # R index offsets
    zio = numpy.array([0, -1, 0, 1, 0, 1])  # Z index offsets

    # Fitting a + br + cz + drz + er^2 + fz^2
    A = numpy.transpose([[numpy.zeros(6, numpy.int) + 1], [rio], [zio], [rio * zio], [rio ** 2], [zio ** 2]])

    A = A[:, 0, :]

    # Needed for interp below

    Rx = numpy.linspace(R[0], R[numpy.size(R) - 1], numpy.size(R))
    Zx = numpy.linspace(Z[0], Z[numpy.size(Z) - 1], numpy.size(Z))

    for e in range(nextrema):

        # Fit in index space so result is index number
        print("Critical point " + str(e))

        localf = numpy.zeros(6)
        for i in range(6):
            # Get the f value in a stencil around this point
            xi = rex[e] + rio[i]  # > 0) < (nx-1) # Zero-gradient at edges
            yi = zex[e] + zio[i]  # > 0) < (ny-1)
            localf[i] = F[xi, yi]

        res, _, _, _ = numpy.linalg.lstsq(A, localf)

        # Res now contains [a,b,c,d,e,f]
        #                  [0,1,2,3,4,5]

        # This determines whether saddle or extremum
        det = 4.0 * res[4] * res[5] - res[3] ** 2

        if det < 0.0:
            print("   X-point")
        else:
            print("   O-point")

        rnew = rp[e]
        znew = zp[e]

        func = RectBivariateSpline(Rx, Zx, F)
        fnew = func(rnew, znew)

        print(rnew, znew, fnew)

        x = numpy.arange(numpy.size(R))
        y = numpy.arange(numpy.size(Z))
        rinew = numpy.interp(rnew, R, x)
        zinew = numpy.interp(znew, Z, y)

        print("   Position: " + str(rnew) + ", " + str(znew))
        print("   F = " + str(fnew))

        if det < 0.0:

            if n_xpoint == 0:
                xpt_ri = [rinew]
                xpt_zi = [zinew]
                xpt_f = [fnew]
                n_xpoint = n_xpoint + 1
            else:
                # Check if this duplicates an existing point

                if rinew in xpt_ri and zinew in xpt_zi:
                    print("   Duplicates existing X-point.")
                else:
                    xpt_ri = numpy.append(xpt_ri, rinew)
                    xpt_zi = numpy.append(xpt_zi, zinew)
                    xpt_f = numpy.append(xpt_f, fnew)
                    n_xpoint = n_xpoint + 1

            scatter(rnew, znew, s=100, marker="x", color="r")

            annotate(
                numpy.str(n_xpoint - 1),
                xy=(rnew, znew),
                xytext=(10, 10),
                textcoords="offset points",
                size="large",
                color="r",
            )

            draw()
        else:

            if n_opoint == 0:
                opt_ri = [rinew]
                opt_zi = [zinew]
                opt_f = [fnew]
                n_opoint = n_opoint + 1
            else:
                # Check if this duplicates an existing point

                if rinew in opt_ri and zinew in opt_zi:
                    print("   Duplicates existing O-point")
                else:
                    opt_ri = numpy.append(opt_ri, rinew)
                    opt_zi = numpy.append(opt_zi, zinew)
                    opt_f = numpy.append(opt_f, fnew)
                    n_opoint = n_opoint + 1

            scatter(rnew, znew, s=100, marker="o", color="r")

            annotate(
                numpy.str(n_opoint - 1),
                xy=(rnew, znew),
                xytext=(10, 10),
                textcoords="offset points",
                size="large",
                color="b",
            )
            draw()

    print("Number of O-points: " + numpy.str(n_opoint))
    print("Number of X-points: " + numpy.str(n_xpoint))

    if n_opoint == 0:
        opt_ri = [rinew]
        opt_zi = [zinew]
        opt_f = [fnew]
        n_opoint = n_opoint + 1

    print("Number of O-points: " + str(n_opoint))

    if n_opoint == 0:
        print("No O-points! Giving up on this equilibrium")
        return Bunch(n_opoint=0, n_xpoint=0, primary_opt=-1)

    # ;;;;;;;;;;;;;; Find plasma centre ;;;;;;;;;;;;;;;;;;;
    # Find the O-point closest to the middle of the grid

    mind = (opt_ri[0] - (old_div(numpy.float(nx), 2.0))) ** 2 + (opt_zi[0] - (old_div(numpy.float(ny), 2.0))) ** 2
    ind = 0
    for i in range(1, n_opoint):
        d = (opt_ri[i] - (old_div(numpy.float(nx), 2.0))) ** 2 + (opt_zi[i] - (old_div(numpy.float(ny), 2.0))) ** 2
        if d < mind:
            ind = i
            mind = d

    primary_opt = ind
    print("Primary O-point is at " + str(numpy.interp(opt_ri[ind], x, R)) + ", " + str(numpy.interp(opt_zi[ind], y, Z)))
    print("")

    if n_xpoint > 0:

        # Find the primary separatrix

        # First remove non-monotonic separatrices
        nkeep = 0
        for i in range(n_xpoint):
            # Draw a line between the O-point and X-point

            n = 100  # Number of points
            farr = numpy.zeros(n)
            dr = old_div((xpt_ri[i] - opt_ri[ind]), numpy.float(n))
            dz = old_div((xpt_zi[i] - opt_zi[ind]), numpy.float(n))
            for j in range(n):
                # interpolate f at this location
                func = RectBivariateSpline(x, y, F)

                farr[j] = func(opt_ri[ind] + dr * numpy.float(j), opt_zi[ind] + dz * numpy.float(j))

            # farr should be monotonic, and shouldn't cross any other separatrices

            maxind = numpy.argmax(farr)
            minind = numpy.argmin(farr)
            if maxind < minind:
                maxind, minind = minind, maxind

            # Allow a little leeway to account for errors
            # NOTE: This needs a bit of refining
            if (maxind > (n - 3)) and (minind < 3):
                # Monotonic, so add this to a list of x-points to keep
                if nkeep == 0:
                    keep = [i]
                else:
                    keep = numpy.append(keep, i)

                nkeep = nkeep + 1

        if nkeep > 0:
            print("Keeping x-points ", keep)
            xpt_ri = xpt_ri[keep]
            xpt_zi = xpt_zi[keep]
            xpt_f = xpt_f[keep]
        else:
            "No x-points kept"

        n_xpoint = nkeep

        # Now find x-point closest to primary O-point
        s = numpy.argsort(numpy.abs(opt_f[ind] - xpt_f))
        xpt_ri = xpt_ri[s]
        xpt_zi = xpt_zi[s]
        xpt_f = xpt_f[s]
        inner_sep = 0
    else:

        # No x-points. Pick mid-point in f

        xpt_f = 0.5 * (numpy.max(F) + numpy.min(F))

        print("WARNING: No X-points. Setting separatrix to F = " + str(xpt_f))

        xpt_ri = 0
        xpt_zi = 0
        inner_sep = 0

    # ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    # Put results into a structure

    result = Bunch(
        n_opoint=n_opoint,
        n_xpoint=n_xpoint,  # Number of O- and X-points
        primary_opt=primary_opt,  # Which O-point is the plasma centre
        inner_sep=inner_sep,  # Innermost X-point separatrix
        opt_ri=opt_ri,
        opt_zi=opt_zi,
        opt_f=opt_f,  # O-point location (indices) and psi values
        xpt_ri=xpt_ri,
        xpt_zi=xpt_zi,
        xpt_f=xpt_f,
    )  # X-point locations and psi values

    return result
Exemple #2
0
def analyse_equil(F, R, Z):
    """Takes an RZ psi grid, and finds x-points and o-points

    Parameters
    ----------
    F : array_like
        2-D array of psi values
    R : array_like
        1-D array of major radii, its length should be the same as the
        first dimension of F
    Z : array_like
        1-D array of heights, its length should be the same as the
        second dimension of F

    Returns
    -------
    object
        An object of critical points containing:

          n_opoint, n_xpoint   - Number of O- and X-points
          primary_opt          - Index of plasma centre O-point
          inner_sep            - X-point index of inner separatrix
          opt_ri, opt_zi       - R and Z indices for each O-point
          opt_f                - Psi value at each O-point
          xpt_ri, xpt_zi       - R and Z indices for each X-point
          xpt_f                - Psi value of each X-point

    """
    s = numpy.shape(F)
    nx = s[0]
    ny = s[1]

    # ;;;;;;;;;;;;;;; Find critical points ;;;;;;;;;;;;;
    #
    # Need to find starting locations for O-points (minima/maxima)
    # and X-points (saddle points)
    #
    Rr = numpy.tile(R, nx).reshape(nx, ny).T
    Zz = numpy.tile(Z, ny).reshape(nx, ny)

    contour1 = contour(Rr, Zz, gradient(F)[0], levels=[0.0], colors="r")
    contour2 = contour(Rr, Zz, gradient(F)[1], levels=[0.0], colors="r")

    draw()

    # ## 1st method - line crossings ---------------------------
    res = find_inter(contour1, contour2)

    # rex1=numpy.interp(res[0],  R, numpy.arange(R.size)).astype(int)
    # zex1=numpy.interp(res[1],  Z, numpy.arange(Z.size)).astype(int)

    rex1 = res[0]
    zex1 = res[1]

    w = numpy.where((rex1 > R[2]) & (rex1 < R[nx - 3]) & (zex1 > Z[2])
                    & (zex1 < Z[nx - 3]))
    nextrema = numpy.size(w)
    rex1 = rex1[w].flatten()
    zex1 = zex1[w].flatten()

    # ## 2nd method - local maxima_minima -----------------------
    res1 = local_min_max.detect_local_minima(F)
    res2 = local_min_max.detect_local_maxima(F)
    res = numpy.append(res1, res2, 1)

    rex2 = res[0, :].flatten()
    zex2 = res[1, :].flatten()

    w = numpy.where((rex2 > 2) & (rex2 < nx - 3) & (zex2 > 2)
                    & (zex2 < nx - 3))
    nextrema = numpy.size(w)
    rex2 = rex2[w].flatten()
    zex2 = zex2[w].flatten()

    n_opoint = nextrema
    n_xpoint = numpy.size(rex1) - n_opoint

    # Needed for interp below

    Rx = numpy.arange(numpy.size(R))
    Zx = numpy.arange(numpy.size(Z))

    print("Number of O-points: " + numpy.str(n_opoint))
    print("Number of X-points: " + numpy.str(n_xpoint))

    # Deduce the O & X points

    x = R[rex2]
    y = Z[zex2]

    dr = old_div((R[numpy.size(R) - 1] - R[0]), numpy.size(R))
    dz = old_div((Z[numpy.size(Z) - 1] - Z[0]), numpy.size(Z))

    repeated = set()
    for i in range(numpy.size(rex1)):
        for j in range(numpy.size(x)):
            if (numpy.abs(rex1[i] - x[j]) < 2 * dr
                    and numpy.abs(zex1[i] - y[j]) < 2 * dz):
                repeated.add(i)

    # o-points

    o_ri = numpy.take(rex1, numpy.array(list(repeated)))
    opt_ri = numpy.interp(o_ri, R, Rx)
    o_zi = numpy.take(zex1, numpy.array(list(repeated)))
    opt_zi = numpy.interp(o_zi, Z, Zx)
    opt_f = numpy.zeros(numpy.size(opt_ri))
    func = RectBivariateSpline(Rx, Zx, F)
    for i in range(numpy.size(opt_ri)):
        opt_f[i] = func(opt_ri[i], opt_zi[i])

    n_opoint = numpy.size(opt_ri)

    # x-points

    x_ri = numpy.delete(rex1, numpy.array(list(repeated)))
    xpt_ri = numpy.interp(x_ri, R, Rx)
    x_zi = numpy.delete(zex1, numpy.array(list(repeated)))
    xpt_zi = numpy.interp(x_zi, Z, Zx)
    xpt_f = numpy.zeros(numpy.size(xpt_ri))
    func = RectBivariateSpline(Rx, Zx, F)
    for i in range(numpy.size(xpt_ri)):
        xpt_f[i] = func(xpt_ri[i], xpt_zi[i])

    n_xpoint = numpy.size(xpt_ri)

    #  plot o-points

    plot(o_ri, o_zi, "o", markersize=10)

    labels = ["{0}".format(i) for i in range(o_ri.size)]
    for label, xp, yp in zip(labels, o_ri, o_zi):
        annotate(
            label,
            xy=(xp, yp),
            xytext=(10, 10),
            textcoords="offset points",
            size="large",
            color="b",
        )

    draw()

    #  plot x-points

    plot(x_ri, x_zi, "x", markersize=10)

    labels = ["{0}".format(i) for i in range(x_ri.size)]
    for label, xp, yp in zip(labels, x_ri, x_zi):
        annotate(
            label,
            xy=(xp, yp),
            xytext=(10, 10),
            textcoords="offset points",
            size="large",
            color="r",
        )

    draw()

    print("Number of O-points: " + str(n_opoint))

    if n_opoint == 0:
        raise RuntimeError("No O-points! Giving up on this equilibrium")

    # ;;;;;;;;;;;;;; Find plasma centre ;;;;;;;;;;;;;;;;;;;
    # Find the O-point closest to the middle of the grid

    mind = (opt_ri[0] - (old_div(numpy.float(nx), 2.0)))**2 + (
        opt_zi[0] - (old_div(numpy.float(ny), 2.0)))**2
    ind = 0
    for i in range(1, n_opoint):
        d = (opt_ri[i] - (old_div(numpy.float(nx), 2.0)))**2 + (
            opt_zi[i] - (old_div(numpy.float(ny), 2.0)))**2
        if d < mind:
            ind = i
            mind = d

    primary_opt = ind
    print(
        "Primary O-point is at " +
        numpy.str(numpy.interp(opt_ri[ind], numpy.arange(numpy.size(R)), R)) +
        ", " +
        numpy.str(numpy.interp(opt_zi[ind], numpy.arange(numpy.size(Z)), Z)))
    print("")

    if n_xpoint > 0:

        # Find the primary separatrix

        # First remove non-monotonic separatrices
        nkeep = 0
        for i in range(n_xpoint):
            # Draw a line between the O-point and X-point

            n = 100  # Number of points
            farr = numpy.zeros(n)
            dr = old_div((xpt_ri[i] - opt_ri[ind]), numpy.float(n))
            dz = old_div((xpt_zi[i] - opt_zi[ind]), numpy.float(n))
            for j in range(n):
                # interpolate f at this location
                func = RectBivariateSpline(Rx, Zx, F)

                farr[j] = func(opt_ri[ind] + dr * numpy.float(j),
                               opt_zi[ind] + dz * numpy.float(j))

            # farr should be monotonic, and shouldn't cross any other separatrices

            maxind = numpy.argmax(farr)
            minind = numpy.argmin(farr)
            if maxind < minind:
                maxind, minind = minind, maxind

            # Allow a little leeway to account for errors
            # NOTE: This needs a bit of refining
            if (maxind > (n - 3)) and (minind < 3):
                # Monotonic, so add this to a list of x-points to keep
                if nkeep == 0:
                    keep = [i]
                else:
                    keep = numpy.append(keep, i)

                nkeep = nkeep + 1

        if nkeep > 0:
            print("Keeping x-points ", keep)
            xpt_ri = xpt_ri[keep]
            xpt_zi = xpt_zi[keep]
            xpt_f = xpt_f[keep]
        else:
            "No x-points kept"

        n_xpoint = nkeep

        # Now find x-point closest to primary O-point
        s = numpy.argsort(numpy.abs(opt_f[ind] - xpt_f))
        xpt_ri = xpt_ri[s]
        xpt_zi = xpt_zi[s]
        xpt_f = xpt_f[s]
        inner_sep = 0

    else:

        # No x-points. Pick mid-point in f

        xpt_f = 0.5 * (numpy.max(F) + numpy.min(F))

        print("WARNING: No X-points. Setting separatrix to F = " + str(xpt_f))

        xpt_ri = 0
        xpt_zi = 0
        inner_sep = 0

    # ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    # Put results into a structure

    result = Bunch(
        n_opoint=n_opoint,
        n_xpoint=n_xpoint,  # Number of O- and X-points
        primary_opt=primary_opt,  # Which O-point is the plasma centre
        inner_sep=inner_sep,  # Innermost X-point separatrix
        opt_ri=opt_ri,
        opt_zi=opt_zi,
        opt_f=opt_f,  # O-point location (indices) and psi values
        xpt_ri=xpt_ri,
        xpt_zi=xpt_zi,
        xpt_f=xpt_f,
    )  # X-point locations and psi values

    return result
def analyse_equil ( F, R, Z):
    s = numpy.shape(F)
    nx = s[0]
    ny = s[1]
  
  #;;;;;;;;;;;;;;; Find critical points ;;;;;;;;;;;;;
  #
  # Need to find starting locations for O-points (minima/maxima)
  # and X-points (saddle points)
  #
    Rr=numpy.tile(R,nx).reshape(nx,ny).T  # needed for contour
    Zz=numpy.tile(Z,ny).reshape(nx,ny)
    
 
    contour1=contour(Rr,Zz,gradient(F)[0], levels=[0.0], colors='r')
    contour2=contour(Rr,Zz,gradient(F)[1], levels=[0.0], colors='r')    

    draw()
    

### --- line crossings ---------------------------
    res=find_inter( contour1, contour2)
            
    rex=numpy.interp(res[0],  R, numpy.arange(R.size)).astype(int)
    zex=numpy.interp(res[1],  Z, numpy.arange(Z.size)).astype(int)
   
      
    w=numpy.where((rex > 2) & (rex < nx-3) & (zex >2) & (zex < nx-3))
    nextrema = numpy.size(w)
    rex=rex[w].flatten()
    zex=zex[w].flatten()
  
  #;;;;;;;;;;;;;; Characterise extrema ;;;;;;;;;;;;;;;;;
  # Fit a surface through local points using 6x6 matrix
  # This is to determine the type of extrema, and to
  # refine the location
  # 
  
    
    n_opoint = 0
    n_xpoint = 0

  # Calculate inverse matrix
    rio = numpy.array([-1, 0, 0, 0, 1, 1]) # R index offsets
    zio = numpy.array([ 0,-1, 0, 1, 0, 1]) # Z index offsets
  
  # Fitting a + br + cz + drz + er^2 + fz^2
    A = numpy.transpose([[numpy.zeros(6,numpy.int)+1],
                 [rio], 
                 [zio], 
                 [rio*zio], 
                 [rio**2], 
                 [zio**2]])
                 
    A=A[:,0,:]    
  
    for e in range (nextrema) :
      
      # Fit in index space so result is index number
        print("Critical point "+str(e))
    
     
        valid = 1

        localf = numpy.zeros(6)
        for i in range (6) :
      # Get the f value in a stencil around this point
            xi = (rex[e]+rio[i]) #> 0) < (nx-1) # Zero-gradient at edges
            yi = (zex[e]+zio[i]) #> 0) < (ny-1)
            localf[i] = F[xi, yi]
   
        res, _, _, _ = numpy.linalg.lstsq(A,localf)
  
          
    # Res now contains [a,b,c,d,e,f]
    #                  [0,1,2,3,4,5]

    # This determines whether saddle or extremum
        det = 4.*res[4]*res[5] - res[3]**2
    
        if det < 0.0 :
            print("   X-point")
        else:
            print("   O-point")
    
    
    # Get location (2x2 matrix of coefficients)
    
        rinew = old_div((res[3]*res[2] - 2.*res[1]*res[5]), det)
        zinew = old_div((res[3]*res[1] - 2.*res[4]*res[2]), det)

        if (numpy.abs(rinew) > 1.) or (numpy.abs(zinew) > 1.0) :
      #; Method has gone slightly wrong. Try a different method.
      #; Get a contour line starting at this point. Should
      #; produce a circle around the real o-point. 
            print("   Fitted location deviates too much")
            if det < 0.0 :
                print("   => X-point probably not valid")
                print("      deviation = "+numpy.str(rinew)+","+numpy.str(zinew))
                #valid = 0
#            else:
#    
#                contour_lines, F, findgen(nx), findgen(ny), levels=[F[rex[e], zex[e]]], \
#                    path_info=info, path_xy=xy
#    
#                if np.size(info) > 1 :
#                # More than one contour. Select the one closest
#                    ind = closest_line(info, xy, rex[e], zex[e])
#                    info = info[ind]
#                else: info = info[0]
#    
#                rinew = 0.5*(MAX(xy[0, info.offset:(info.offset + info.n - 1)]) + \
#                     MIN(xy[0, info.offset:(info.offset + info.n - 1)])) - rex[e]
#                zinew = 0.5*(MAX(xy[1, info.offset:(info.offset + info.n - 1)]) + \
#                     MIN(xy[1, info.offset:(info.offset + info.n - 1)])) - zex[e]
#    
#                if (np.abs(rinew) > 2.) or (np.abs(zinew) > 2.0) :
#                    print "   Backup method also failed. Keeping initial guess"
#                    rinew = 0.
#                    zinew = 0.
#                 
#

        if valid :
            fnew = (res[0] + res[1]*rinew + res[2]*zinew + 
                res[3]*rinew*zinew + res[4]*rinew**2 + res[5]*zinew**2)
      
            rinew = rinew + rex[e]
            zinew = zinew + zex[e]
      
            print("   Starting index: " + str(rex[e])+", "+str(zex[e]))
            print("   Refined  index: " + str(rinew)+", "+str(zinew))
      
            x=numpy.arange(numpy.size(R))
            y=numpy.arange(numpy.size(Z))
            rnew = numpy.interp(rinew,x,R)
            znew = numpy.interp(zinew, y, Z)
      
            print("   Position: " + str(rnew)+", "+str(znew))
            print("   F = "+str(fnew))
      
            if det < 0.0 :

                if n_xpoint == 0 :
                    xpt_ri = [rinew]
                    xpt_zi = [zinew]
                    xpt_f = [fnew]
                    n_xpoint = n_xpoint + 1
                else:
            # Check if this duplicates an existing point
                    
                    if rinew in xpt_ri and zinew in xpt_zi :
                        print("   Duplicates existing X-point.")
                    else:
                        xpt_ri = numpy.append(xpt_ri, rinew)
                        xpt_zi = numpy.append(xpt_zi, zinew)
                        xpt_f = numpy.append(xpt_f, fnew)
                        n_xpoint = n_xpoint + 1
                
                                                                                
                scatter(rnew,znew,s=100, marker='x', color='r')
                
                annotate(numpy.str(n_xpoint-1),  xy = (rnew, znew), xytext = (10, 10), textcoords = 'offset points',size='large', color='r')

                draw()
            else:

                if n_opoint == 0 :
                    opt_ri = [rinew]
                    opt_zi = [zinew]
                    opt_f = [fnew]
                    n_opoint = n_opoint + 1
                else:
            # Check if this duplicates an existing point
        
                    if rinew in opt_ri and zinew in opt_zi :
                        print("   Duplicates existing O-point")
                    else:
                        opt_ri = numpy.append(opt_ri, rinew)
                        opt_zi = numpy.append(opt_zi, zinew)
                        opt_f = numpy.append(opt_f, fnew)
                        n_opoint = n_opoint + 1
 
                scatter(rnew,znew,s=100, marker='o',color='r')
               
                annotate(numpy.str(n_opoint-1),  xy = (rnew, znew), xytext = (10, 10), textcoords = 'offset points', size='large', color='b')
                draw()
     
                      
    print("Number of O-points: "+numpy.str(n_opoint))
    print("Number of X-points: "+numpy.str(n_xpoint))

    
    if n_opoint == 0 :
            opt_ri = [rinew]
            opt_zi = [zinew]
            opt_f = [fnew]
            n_opoint = n_opoint + 1

    print("Number of O-points: "+str(n_opoint))

    if n_opoint == 0 :
        print("No O-points! Giving up on this equilibrium")
        return Bunch(n_opoint=0, n_xpoint=0, primary_opt=-1)
  

  #;;;;;;;;;;;;;; Find plasma centre ;;;;;;;;;;;;;;;;;;;
  # Find the O-point closest to the middle of the grid
  
    mind = (opt_ri[0] - (old_div(numpy.float(nx),2.)))**2 + (opt_zi[0] - (old_div(numpy.float(ny),2.)))**2
    ind = 0
    for i in range (1, n_opoint) :
        d = (opt_ri[i] - (old_div(numpy.float(nx),2.)))**2 + (opt_zi[i] - (old_div(numpy.float(ny),2.)))**2
        if d < mind :
            ind = i
            mind = d
    
    primary_opt = ind
    print("Primary O-point is at "+str(numpy.interp(opt_ri[ind],x,R)) + ", " + str(numpy.interp(opt_zi[ind],y,Z)))
    print("")
  
    if n_xpoint > 0 :

    # Find the primary separatrix

    # First remove non-monotonic separatrices
        nkeep = 0
        for i in range (n_xpoint) :
      # Draw a line between the O-point and X-point

            n = 100 # Number of points
            farr = numpy.zeros(n)
            dr = old_div((xpt_ri[i] - opt_ri[ind]), numpy.float(n))
            dz = old_div((xpt_zi[i] - opt_zi[ind]), numpy.float(n))
            for j in range (n) :
                # interpolate f at this location
                func = RectBivariateSpline(x, y, F)

                farr[j] = func(opt_ri[ind] + dr*numpy.float(j), opt_zi[ind] + dz*numpy.float(j))
       

            # farr should be monotonic, and shouldn't cross any other separatrices

            maxind = numpy.argmax(farr)
            minind = numpy.argmin(farr)
            if (maxind < minind) : maxind, minind = minind, maxind

        # Allow a little leeway to account for errors
        # NOTE: This needs a bit of refining
            if (maxind > (n-3)) and (minind < 3) :
            # Monotonic, so add this to a list of x-points to keep
                if nkeep == 0 :
                    keep = [i]
                else:
                    keep = numpy.append(keep, i)
                
                
                nkeep = nkeep + 1
       

        if nkeep > 0 :
            print("Keeping x-points ", keep)
            xpt_ri = xpt_ri[keep]
            xpt_zi = xpt_zi[keep]
            xpt_f = xpt_f[keep]
        else:
            "No x-points kept"
        
        n_xpoint = nkeep

        # Now find x-point closest to primary O-point
        s = numpy.argsort(numpy.abs(opt_f[ind] - xpt_f))
        xpt_ri = xpt_ri[s]
        xpt_zi = xpt_zi[s]
        xpt_f = xpt_f[s]
        inner_sep = 0
    else:

    # No x-points. Pick mid-point in f
   
        xpt_f = 0.5*(numpy.max(F) + numpy.min(F))
    
        print("WARNING: No X-points. Setting separatrix to F = "+str(xpt_f))

        xpt_ri = 0
        xpt_zi = 0
        inner_sep = 0
    


  #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  # Put results into a structure
  
    result = Bunch(n_opoint=n_opoint, n_xpoint=n_xpoint, # Number of O- and X-points
            primary_opt=primary_opt, # Which O-point is the plasma centre
            inner_sep=inner_sep, #Innermost X-point separatrix
            opt_ri=opt_ri, opt_zi=opt_zi, opt_f=opt_f, # O-point location (indices) and psi values
            xpt_ri=xpt_ri, xpt_zi=xpt_zi, xpt_f=xpt_f) # X-point locations and psi values
  
    return result
def analyse_equil ( F, R, Z):
    s = numpy.shape(F)
    nx = s[0]
    ny = s[1]
  
  #;;;;;;;;;;;;;;; Find critical points ;;;;;;;;;;;;;
  #
  # Need to find starting locations for O-points (minima/maxima)
  # and X-points (saddle points)
  #
    Rr=numpy.tile(R,nx).reshape(nx,ny).T
    Zz=numpy.tile(Z,ny).reshape(nx,ny)
    
    contour1=contour(Rr,Zz,gradient(F)[0], levels=[0.0], colors='r')
    contour2=contour(Rr,Zz,gradient(F)[1], levels=[0.0], colors='r')    

    draw()


### 1st method - line crossings ---------------------------
    res=find_inter( contour1, contour2)
            
    #rex1=numpy.interp(res[0],  R, numpy.arange(R.size)).astype(int)
    #zex1=numpy.interp(res[1],  Z, numpy.arange(Z.size)).astype(int)

    rex1=res[0]
    zex1=res[1]    
            
    w=numpy.where((rex1 > R[2]) & (rex1 < R[nx-3]) & (zex1 > Z[2]) & (zex1 < Z[nx-3]))
    nextrema = numpy.size(w)
    rex1=rex1[w].flatten()
    zex1=zex1[w].flatten()
    

### 2nd method - local maxima_minima -----------------------
    res1=local_min_max.detect_local_minima(F)
    res2=local_min_max.detect_local_maxima(F)
    res=numpy.append(res1,res2,1)

    rex2=res[0,:].flatten()
    zex2=res[1,:].flatten()
     
      
    w=numpy.where((rex2 > 2) & (rex2 < nx-3) & (zex2 >2) & (zex2 < nx-3))
    nextrema = numpy.size(w)
    rex2=rex2[w].flatten()
    zex2=zex2[w].flatten()
    

    n_opoint=nextrema
    n_xpoint=numpy.size(rex1)-n_opoint
    
 # Needed for interp below   
    
    Rx=numpy.arange(numpy.size(R))
    Zx=numpy.arange(numpy.size(Z))
                                                               

                                                                                          
    print "Number of O-points: "+numpy.str(n_opoint)
    print "Number of X-points: "+numpy.str(n_xpoint)
    
 # Deduce the O & X points   
    
    x=R[rex2]
    y=Z[zex2]
    
    dr=(R[numpy.size(R)-1]-R[0])/numpy.size(R)
    dz=(Z[numpy.size(Z)-1]-Z[0])/numpy.size(Z)


    repeated=set()
    for i in xrange(numpy.size(rex1)):
        for j in xrange(numpy.size(x)):
            if numpy.abs(rex1[i]-x[j]) < 2*dr and numpy.abs(zex1[i]-y[j]) < 2*dz : repeated.add(i)
        
 # o-points
 
    o_ri=numpy.take(rex1,numpy.array(list(repeated)))
    opt_ri=numpy.interp(o_ri,R,Rx)
    o_zi=numpy.take(zex1,numpy.array(list(repeated)))  
    opt_zi=numpy.interp(o_zi,Z,Zx)      
    opt_f=numpy.zeros(numpy.size(opt_ri))
    func = RectBivariateSpline(Rx, Zx, F)
    for i in xrange(numpy.size(opt_ri)): opt_f[i]=func(opt_ri[i], opt_zi[i])

    n_opoint=numpy.size(opt_ri)
    
 # x-points
                       
    x_ri=numpy.delete(rex1, numpy.array(list(repeated)))
    xpt_ri=numpy.interp(x_ri,R,Rx)
    x_zi=numpy.delete(zex1, numpy.array(list(repeated)))
    xpt_zi=numpy.interp(x_zi,Z,Zx)
    xpt_f=numpy.zeros(numpy.size(xpt_ri))
    func = RectBivariateSpline(Rx, Zx, F)
    for i in xrange(numpy.size(xpt_ri)): xpt_f[i]=func(xpt_ri[i], xpt_zi[i])
    
    n_xpoint=numpy.size(xpt_ri)
    
 #  plot o-points 

    plot(o_ri,o_zi,'o', markersize=10)
     
    labels = ['{0}'.format(i) for i in range(o_ri.size)]
    for label, xp, yp in zip(labels, o_ri, o_zi):
        annotate(label,  xy = (xp, yp), xytext = (10, 10), textcoords = 'offset points',size='large', color='b')

    draw()
  
 #  plot x-points     
    
    plot(x_ri,x_zi,'x', markersize=10)
     
    labels = ['{0}'.format(i) for i in range(x_ri.size)]
    for label, xp, yp in zip(labels, x_ri, x_zi):
        annotate(label,  xy = (xp, yp), xytext = (10, 10), textcoords = 'offset points',size='large', color='r')

    draw()

    print "Number of O-points: "+str(n_opoint)

    if n_opoint == 0 :
        print "No O-points! Giving up on this equilibrium"
        return Bunch(n_opoint=0, n_xpoint=0, primary_opt=-1)


  #;;;;;;;;;;;;;; Find plasma centre ;;;;;;;;;;;;;;;;;;;
  # Find the O-point closest to the middle of the grid
  
    mind = (opt_ri[0] - (numpy.float(nx)/2.))**2 + (opt_zi[0] - (numpy.float(ny)/2.))**2
    ind = 0
    for i in range (1, n_opoint) :
        d = (opt_ri[i] - (numpy.float(nx)/2.))**2 + (opt_zi[i] - (numpy.float(ny)/2.))**2
        if d < mind :
            ind = i
            mind = d
    
    primary_opt = ind
    print "Primary O-point is at "+ numpy.str(numpy.interp(opt_ri[ind],numpy.arange(numpy.size(R)),R)) + ", " + numpy.str(numpy.interp(opt_zi[ind],numpy.arange(numpy.size(Z)),Z))
    print ""
  
    if n_xpoint > 0 :

    # Find the primary separatrix

    # First remove non-monotonic separatrices
        nkeep = 0
        for i in xrange (n_xpoint) :
      # Draw a line between the O-point and X-point

            n = 100 # Number of points
            farr = numpy.zeros(n)
            dr = (xpt_ri[i] - opt_ri[ind]) / numpy.float(n)
            dz = (xpt_zi[i] - opt_zi[ind]) / numpy.float(n)
            for j in xrange (n) :
                # interpolate f at this location
                func = RectBivariateSpline(Rx, Zx, F)

                farr[j] = func(opt_ri[ind] + dr*numpy.float(j), opt_zi[ind] + dz*numpy.float(j))
       

            # farr should be monotonic, and shouldn't cross any other separatrices

            maxind = numpy.argmax(farr)
            minind = numpy.argmin(farr)
            if (maxind < minind) : maxind, minind = minind, maxind

        # Allow a little leeway to account for errors
        # NOTE: This needs a bit of refining
            if (maxind > (n-3)) and (minind < 3) :
            # Monotonic, so add this to a list of x-points to keep
                if nkeep == 0 :
                    keep = [i]
                else:
                    keep = numpy.append(keep, i)
                
                
                nkeep = nkeep + 1
       

        if nkeep > 0 :
            print "Keeping x-points ", keep
            xpt_ri = xpt_ri[keep]
            xpt_zi = xpt_zi[keep]
            xpt_f = xpt_f[keep]
        else:
            "No x-points kept"
        
        n_xpoint = nkeep

     
        # Now find x-point closest to primary O-point
        s = numpy.argsort(numpy.abs(opt_f[ind] - xpt_f))
        xpt_ri = xpt_ri[s]
        xpt_zi = xpt_zi[s]
        xpt_f = xpt_f[s]
        inner_sep = 0
       
    else:

    # No x-points. Pick mid-point in f
   
        xpt_f = 0.5*(numpy.max(F) + numpy.min(F))
    
        print "WARNING: No X-points. Setting separatrix to F = "+str(xpt_f)

        xpt_ri = 0
        xpt_zi = 0
        inner_sep = 0
    


  #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  # Put results into a structure
  
    result = Bunch(n_opoint=n_opoint, n_xpoint=n_xpoint, # Number of O- and X-points
            primary_opt=primary_opt, # Which O-point is the plasma centre
            inner_sep=inner_sep, #Innermost X-point separatrix
            opt_ri=opt_ri, opt_zi=opt_zi, opt_f=opt_f, # O-point location (indices) and psi values
            xpt_ri=xpt_ri, xpt_zi=xpt_zi, xpt_f=xpt_f) # X-point locations and psi values
  
    return result
def analyse_equil(F, R, Z):
    s = numpy.shape(F)
    nx = s[0]
    ny = s[1]

    #;;;;;;;;;;;;;;; Find critical points ;;;;;;;;;;;;;
    #
    # Need to find starting locations for O-points (minima/maxima)
    # and X-points (saddle points)
    #
    Rr = numpy.tile(R, nx).reshape(nx, ny).T  # needed for contour
    Zz = numpy.tile(Z, ny).reshape(nx, ny)

    contour1 = contour(Rr, Zz, gradient(F)[0], levels=[0.0], colors='r')
    contour2 = contour(Rr, Zz, gradient(F)[1], levels=[0.0], colors='r')

    draw()

    ### --- line crossings ---------------------------
    res = find_inter(contour1, contour2)

    rex = numpy.interp(res[0], R, numpy.arange(R.size)).astype(int)
    zex = numpy.interp(res[1], Z, numpy.arange(Z.size)).astype(int)

    w = numpy.where((rex > 2) & (rex < nx - 3) & (zex > 2) & (zex < nx - 3))
    nextrema = numpy.size(w)
    rex = rex[w].flatten()
    zex = zex[w].flatten()

    #;;;;;;;;;;;;;; Characterise extrema ;;;;;;;;;;;;;;;;;
    # Fit a surface through local points using 6x6 matrix
    # This is to determine the type of extrema, and to
    # refine the location
    #

    n_opoint = 0
    n_xpoint = 0

    # Calculate inverse matrix
    rio = numpy.array([-1, 0, 0, 0, 1, 1])  # R index offsets
    zio = numpy.array([0, -1, 0, 1, 0, 1])  # Z index offsets

    # Fitting a + br + cz + drz + er^2 + fz^2
    A = numpy.transpose([[numpy.zeros(6, numpy.int) + 1], [rio], [zio],
                         [rio * zio], [rio**2], [zio**2]])

    A = A[:, 0, :]

    for e in range(nextrema):

        # Fit in index space so result is index number
        print("Critical point " + str(e))

        valid = 1

        localf = numpy.zeros(6)
        for i in range(6):
            # Get the f value in a stencil around this point
            xi = (rex[e] + rio[i])  #> 0) < (nx-1) # Zero-gradient at edges
            yi = (zex[e] + zio[i])  #> 0) < (ny-1)
            localf[i] = F[xi, yi]

        res, _, _, _ = numpy.linalg.lstsq(A, localf)

        # Res now contains [a,b,c,d,e,f]
        #                  [0,1,2,3,4,5]

        # This determines whether saddle or extremum
        det = 4. * res[4] * res[5] - res[3]**2

        if det < 0.0:
            print("   X-point")
        else:
            print("   O-point")

    # Get location (2x2 matrix of coefficients)

        rinew = old_div((res[3] * res[2] - 2. * res[1] * res[5]), det)
        zinew = old_div((res[3] * res[1] - 2. * res[4] * res[2]), det)

        if (numpy.abs(rinew) > 1.) or (numpy.abs(zinew) > 1.0):
            #; Method has gone slightly wrong. Try a different method.
            #; Get a contour line starting at this point. Should
            #; produce a circle around the real o-point.
            print("   Fitted location deviates too much")
            if det < 0.0:
                print("   => X-point probably not valid")
                print("      deviation = " + numpy.str(rinew) + "," +
                      numpy.str(zinew))
                #valid = 0
#            else:
#
#                contour_lines, F, findgen(nx), findgen(ny), levels=[F[rex[e], zex[e]]], \
#                    path_info=info, path_xy=xy
#
#                if np.size(info) > 1 :
#                # More than one contour. Select the one closest
#                    ind = closest_line(info, xy, rex[e], zex[e])
#                    info = info[ind]
#                else: info = info[0]
#
#                rinew = 0.5*(MAX(xy[0, info.offset:(info.offset + info.n - 1)]) + \
#                     MIN(xy[0, info.offset:(info.offset + info.n - 1)])) - rex[e]
#                zinew = 0.5*(MAX(xy[1, info.offset:(info.offset + info.n - 1)]) + \
#                     MIN(xy[1, info.offset:(info.offset + info.n - 1)])) - zex[e]
#
#                if (np.abs(rinew) > 2.) or (np.abs(zinew) > 2.0) :
#                    print "   Backup method also failed. Keeping initial guess"
#                    rinew = 0.
#                    zinew = 0.
#
#

        if valid:
            fnew = (res[0] + res[1] * rinew + res[2] * zinew +
                    res[3] * rinew * zinew + res[4] * rinew**2 +
                    res[5] * zinew**2)

            rinew = rinew + rex[e]
            zinew = zinew + zex[e]

            print("   Starting index: " + str(rex[e]) + ", " + str(zex[e]))
            print("   Refined  index: " + str(rinew) + ", " + str(zinew))

            x = numpy.arange(numpy.size(R))
            y = numpy.arange(numpy.size(Z))
            rnew = numpy.interp(rinew, x, R)
            znew = numpy.interp(zinew, y, Z)

            print("   Position: " + str(rnew) + ", " + str(znew))
            print("   F = " + str(fnew))

            if det < 0.0:

                if n_xpoint == 0:
                    xpt_ri = [rinew]
                    xpt_zi = [zinew]
                    xpt_f = [fnew]
                    n_xpoint = n_xpoint + 1
                else:
                    # Check if this duplicates an existing point

                    if rinew in xpt_ri and zinew in xpt_zi:
                        print("   Duplicates existing X-point.")
                    else:
                        xpt_ri = numpy.append(xpt_ri, rinew)
                        xpt_zi = numpy.append(xpt_zi, zinew)
                        xpt_f = numpy.append(xpt_f, fnew)
                        n_xpoint = n_xpoint + 1

                scatter(rnew, znew, s=100, marker='x', color='r')

                annotate(numpy.str(n_xpoint - 1),
                         xy=(rnew, znew),
                         xytext=(10, 10),
                         textcoords='offset points',
                         size='large',
                         color='r')

                draw()
            else:

                if n_opoint == 0:
                    opt_ri = [rinew]
                    opt_zi = [zinew]
                    opt_f = [fnew]
                    n_opoint = n_opoint + 1
                else:
                    # Check if this duplicates an existing point

                    if rinew in opt_ri and zinew in opt_zi:
                        print("   Duplicates existing O-point")
                    else:
                        opt_ri = numpy.append(opt_ri, rinew)
                        opt_zi = numpy.append(opt_zi, zinew)
                        opt_f = numpy.append(opt_f, fnew)
                        n_opoint = n_opoint + 1

                scatter(rnew, znew, s=100, marker='o', color='r')

                annotate(numpy.str(n_opoint - 1),
                         xy=(rnew, znew),
                         xytext=(10, 10),
                         textcoords='offset points',
                         size='large',
                         color='b')
                draw()

    print("Number of O-points: " + numpy.str(n_opoint))
    print("Number of X-points: " + numpy.str(n_xpoint))

    if n_opoint == 0:
        opt_ri = [rinew]
        opt_zi = [zinew]
        opt_f = [fnew]
        n_opoint = n_opoint + 1

    print("Number of O-points: " + str(n_opoint))

    if n_opoint == 0:
        print("No O-points! Giving up on this equilibrium")
        return Bunch(n_opoint=0, n_xpoint=0, primary_opt=-1)

#;;;;;;;;;;;;;; Find plasma centre ;;;;;;;;;;;;;;;;;;;
# Find the O-point closest to the middle of the grid

    mind = (opt_ri[0] - (old_div(numpy.float(nx), 2.)))**2 + (
        opt_zi[0] - (old_div(numpy.float(ny), 2.)))**2
    ind = 0
    for i in range(1, n_opoint):
        d = (opt_ri[i] - (old_div(numpy.float(nx), 2.)))**2 + (
            opt_zi[i] - (old_div(numpy.float(ny), 2.)))**2
        if d < mind:
            ind = i
            mind = d

    primary_opt = ind
    print("Primary O-point is at " + str(numpy.interp(opt_ri[ind], x, R)) +
          ", " + str(numpy.interp(opt_zi[ind], y, Z)))
    print("")

    if n_xpoint > 0:

        # Find the primary separatrix

        # First remove non-monotonic separatrices
        nkeep = 0
        for i in range(n_xpoint):
            # Draw a line between the O-point and X-point

            n = 100  # Number of points
            farr = numpy.zeros(n)
            dr = old_div((xpt_ri[i] - opt_ri[ind]), numpy.float(n))
            dz = old_div((xpt_zi[i] - opt_zi[ind]), numpy.float(n))
            for j in range(n):
                # interpolate f at this location
                func = RectBivariateSpline(x, y, F)

                farr[j] = func(opt_ri[ind] + dr * numpy.float(j),
                               opt_zi[ind] + dz * numpy.float(j))

            # farr should be monotonic, and shouldn't cross any other separatrices

            maxind = numpy.argmax(farr)
            minind = numpy.argmin(farr)
            if (maxind < minind): maxind, minind = minind, maxind

            # Allow a little leeway to account for errors
            # NOTE: This needs a bit of refining
            if (maxind > (n - 3)) and (minind < 3):
                # Monotonic, so add this to a list of x-points to keep
                if nkeep == 0:
                    keep = [i]
                else:
                    keep = numpy.append(keep, i)

                nkeep = nkeep + 1

        if nkeep > 0:
            print("Keeping x-points ", keep)
            xpt_ri = xpt_ri[keep]
            xpt_zi = xpt_zi[keep]
            xpt_f = xpt_f[keep]
        else:
            "No x-points kept"

        n_xpoint = nkeep

        # Now find x-point closest to primary O-point
        s = numpy.argsort(numpy.abs(opt_f[ind] - xpt_f))
        xpt_ri = xpt_ri[s]
        xpt_zi = xpt_zi[s]
        xpt_f = xpt_f[s]
        inner_sep = 0
    else:

        # No x-points. Pick mid-point in f

        xpt_f = 0.5 * (numpy.max(F) + numpy.min(F))

        print("WARNING: No X-points. Setting separatrix to F = " + str(xpt_f))

        xpt_ri = 0
        xpt_zi = 0
        inner_sep = 0

#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
# Put results into a structure

    result = Bunch(
        n_opoint=n_opoint,
        n_xpoint=n_xpoint,  # Number of O- and X-points
        primary_opt=primary_opt,  # Which O-point is the plasma centre
        inner_sep=inner_sep,  #Innermost X-point separatrix
        opt_ri=opt_ri,
        opt_zi=opt_zi,
        opt_f=opt_f,  # O-point location (indices) and psi values
        xpt_ri=xpt_ri,
        xpt_zi=xpt_zi,
        xpt_f=xpt_f)  # X-point locations and psi values

    return result
Exemple #6
0
def analyse_equil ( F, R, Z):
    s = numpy.shape(F)
    nx = s[0]
    ny = s[1]
  
  #;;;;;;;;;;;;;;; Find critical points ;;;;;;;;;;;;;
  #
  # Need to find starting locations for O-points (minima/maxima)
  # and X-points (saddle points)
  #
    Rr=numpy.tile(R,ny).reshape(ny,nx).T  # needed for contour
    Zz=numpy.tile(Z,nx).reshape(nx,ny)
    
    contour1=contour(Rr,Zz,gradient(F)[0], levels=[0.0], colors='r')
    contour2=contour(Rr,Zz,gradient(F)[1], levels=[0.0], colors='r')    

    draw()
    

### --- line crossings ---------------------------
    lines=find_inter( contour1, contour2)
            
    rex=numpy.interp(lines[0],  R, numpy.arange(R.size)).astype(int)
    zex=numpy.interp(lines[1],  Z, numpy.arange(Z.size)).astype(int)
   
      
    w=numpy.where((rex > 2) & (rex < nx-3) & (zex >2) & (zex < nx-3))
    nextrema = numpy.size(w)
    rex=rex[w].flatten()
    zex=zex[w].flatten()
    rp=lines[0][w]
    zp=lines[1][w]
  
  #;;;;;;;;;;;;;; Characterise extrema ;;;;;;;;;;;;;;;;;
  # Fit a surface through local points using 6x6 matrix
  # This is to determine the type of extrema, and to
  # refine the location
  # 
  
    
    n_opoint = 0
    n_xpoint = 0

  # Calculate inverse matrix
    rio = numpy.array([-1, 0, 0, 0, 1, 1]) # R index offsets
    zio = numpy.array([ 0,-1, 0, 1, 0, 1]) # Z index offsets
  
  # Fitting a + br + cz + drz + er^2 + fz^2
    A = numpy.transpose([[numpy.zeros(6,numpy.int)+1],
                 [rio], 
                 [zio], 
                 [rio*zio], 
                 [rio**2], 
                 [zio**2]])
                 
    A=A[:,0,:]    
    
    
     # Needed for interp below   
    
    Rx=numpy.linspace(R[0],R[numpy.size(R)-1],numpy.size(R))
    Zx=numpy.linspace(Z[0],Z[numpy.size(Z)-1],numpy.size(Z))
                                                               

  
    for e in range (nextrema) :
      
      # Fit in index space so result is index number
        print("Critical point "+str(e))
    
        localf = numpy.zeros(6)
        for i in range (6) :
      # Get the f value in a stencil around this point
            xi = (rex[e]+rio[i]) #> 0) < (nx-1) # Zero-gradient at edges
            yi = (zex[e]+zio[i]) #> 0) < (ny-1)
            localf[i] = F[xi, yi]
   
        res, _, _, _ = numpy.linalg.lstsq(A,localf)
  
          
    # Res now contains [a,b,c,d,e,f]
    #                  [0,1,2,3,4,5]

    # This determines whether saddle or extremum
        det = 4.*res[4]*res[5] - res[3]**2
    
        if det < 0.0 :
            print("   X-point")
        else:
            print("   O-point")
        
      
        rnew = rp[e]
        znew = zp[e]
        
        
        func = RectBivariateSpline(Rx, Zx, F)
        fnew=func(rnew, znew)
        
        print(rnew, znew, fnew)
        
        x=numpy.arange(numpy.size(R))
        y=numpy.arange(numpy.size(Z))
        rinew = numpy.interp(rnew,R,x)
        zinew = numpy.interp(znew, Z, y)
      
        print("   Position: " + str(rnew)+", "+str(znew))
        print("   F = "+str(fnew))
      
        if det < 0.0 :

                if n_xpoint == 0 :
                    xpt_ri = [rinew]
                    xpt_zi = [zinew]
                    xpt_f = [fnew]
                    n_xpoint = n_xpoint + 1
                else:
            # Check if this duplicates an existing point
                    
                    if rinew in xpt_ri and zinew in xpt_zi :
                        print("   Duplicates existing X-point.")
                    else:
                        xpt_ri = numpy.append(xpt_ri, rinew)
                        xpt_zi = numpy.append(xpt_zi, zinew)
                        xpt_f = numpy.append(xpt_f, fnew)
                        n_xpoint = n_xpoint + 1
                
                                                                                
                scatter(rnew,znew,s=100, marker='x', color='r')
                
                annotate(numpy.str(n_xpoint-1),  xy = (rnew, znew), xytext = (10, 10), textcoords = 'offset points',size='large', color='r')

                draw()
        else:

                if n_opoint == 0 :
                    opt_ri = [rinew]
                    opt_zi = [zinew]
                    opt_f = [fnew]
                    n_opoint = n_opoint + 1
                else:
            # Check if this duplicates an existing point
        
                    if rinew in opt_ri and zinew in opt_zi :
                        print("   Duplicates existing O-point")
                    else:
                        opt_ri = numpy.append(opt_ri, rinew)
                        opt_zi = numpy.append(opt_zi, zinew)
                        opt_f = numpy.append(opt_f, fnew)
                        n_opoint = n_opoint + 1
 
                scatter(rnew,znew,s=100, marker='o',color='r')
               
                annotate(numpy.str(n_opoint-1),  xy = (rnew, znew), xytext = (10, 10), textcoords = 'offset points', size='large', color='b')
                draw()
     
                      
    print("Number of O-points: "+numpy.str(n_opoint))
    print("Number of X-points: "+numpy.str(n_xpoint))

    
    if n_opoint == 0 :
            opt_ri = [rinew]
            opt_zi = [zinew]
            opt_f = [fnew]
            n_opoint = n_opoint + 1

    print("Number of O-points: "+str(n_opoint))

    if n_opoint == 0 :
        print("No O-points! Giving up on this equilibrium")
        return Bunch(n_opoint=0, n_xpoint=0, primary_opt=-1)
  

  #;;;;;;;;;;;;;; Find plasma centre ;;;;;;;;;;;;;;;;;;;
  # Find the O-point closest to the middle of the grid
  
    mind = (opt_ri[0] - (old_div(numpy.float(nx),2.)))**2 + (opt_zi[0] - (old_div(numpy.float(ny),2.)))**2
    ind = 0
    for i in range (1, n_opoint) :
        d = (opt_ri[i] - (old_div(numpy.float(nx),2.)))**2 + (opt_zi[i] - (old_div(numpy.float(ny),2.)))**2
        if d < mind :
            ind = i
            mind = d
    
    primary_opt = ind
    print("Primary O-point is at "+str(numpy.interp(opt_ri[ind],x,R)) + ", " + str(numpy.interp(opt_zi[ind],y,Z)))
    print("")
  
    if n_xpoint > 0 :

    # Find the primary separatrix

    # First remove non-monotonic separatrices
        nkeep = 0
        for i in range (n_xpoint) :
      # Draw a line between the O-point and X-point

            n = 100 # Number of points
            farr = numpy.zeros(n)
            dr = old_div((xpt_ri[i] - opt_ri[ind]), numpy.float(n))
            dz = old_div((xpt_zi[i] - opt_zi[ind]), numpy.float(n))
            for j in range (n) :
                # interpolate f at this location
                func = RectBivariateSpline(x, y, F)

                farr[j] = func(opt_ri[ind] + dr*numpy.float(j), opt_zi[ind] + dz*numpy.float(j))
       

            # farr should be monotonic, and shouldn't cross any other separatrices

            maxind = numpy.argmax(farr)
            minind = numpy.argmin(farr)
            if (maxind < minind) : maxind, minind = minind, maxind

        # Allow a little leeway to account for errors
        # NOTE: This needs a bit of refining
            if (maxind > (n-3)) and (minind < 3) :
            # Monotonic, so add this to a list of x-points to keep
                if nkeep == 0 :
                    keep = [i]
                else:
                    keep = numpy.append(keep, i)
                
                
                nkeep = nkeep + 1
       

        if nkeep > 0 :
            print("Keeping x-points ", keep)
            xpt_ri = xpt_ri[keep]
            xpt_zi = xpt_zi[keep]
            xpt_f = xpt_f[keep]
        else:
            "No x-points kept"
        
        n_xpoint = nkeep

        # Now find x-point closest to primary O-point
        s = numpy.argsort(numpy.abs(opt_f[ind] - xpt_f))
        xpt_ri = xpt_ri[s]
        xpt_zi = xpt_zi[s]
        xpt_f = xpt_f[s]
        inner_sep = 0
    else:

    # No x-points. Pick mid-point in f
   
        xpt_f = 0.5*(numpy.max(F) + numpy.min(F))
    
        print("WARNING: No X-points. Setting separatrix to F = "+str(xpt_f))

        xpt_ri = 0
        xpt_zi = 0
        inner_sep = 0
    


  #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  # Put results into a structure
  
    result = Bunch(n_opoint=n_opoint, n_xpoint=n_xpoint, # Number of O- and X-points
            primary_opt=primary_opt, # Which O-point is the plasma centre
            inner_sep=inner_sep, #Innermost X-point separatrix
            opt_ri=opt_ri, opt_zi=opt_zi, opt_f=opt_f, # O-point location (indices) and psi values
            xpt_ri=xpt_ri, xpt_zi=xpt_zi, xpt_f=xpt_f) # X-point locations and psi values
  
    return result
def analyse_equil(F, R, Z):
    """
    Equilibrium analysis routine
    Takes a RZ psi grid, and finds x-points and o-points
    
    F - F(nr, nz) 2D array of psi values
    R - R(nr) 1D array of major radii
    Z - Z(nz) 1D array of heights
    
    Returns a structure of critical points containing:

    n_opoint, n_xpoint   - Number of O- and X-points
    primary_opt          - Index of plasma centre O-point
    inner_sep            - X-point index of inner separatrix
    opt_ri, opt_zi       - R and Z indices for each O-point
    opt_f                - Psi value at each O-point
    xpt_ri, xpt_zi       - R and Z indices for each X-point
    xpt_f                - Psi value of each X-point

    change log:
    2017-04-27 H.SETO (QST)
    * numpy -> np, nx -> nr, ny -> nz, Rr -> Rrz, Zz -> Zrz 
    * fix indent of comment 
    * add some comments 
    """

    # get array size
    [nr, nz] = F.shape

    #;;;;;;;;;;;;;;; Find critical points ;;;;;;;;;;;;;
    #
    # Need to find starting locations for O-points (minima/maxima)
    # and X-points (saddle points)

    Rrz = np.tile(R, nz).reshape(nz, nr).T
    Zrz = np.tile(Z, nr).reshape(nr, nz)

    #; Use contour to get crossing-points where dfdr = dfdz = 0
    contour1 = contour(Rrz, Zrz, np.gradient(F)[0], levels=[0.0], colors='r')
    contour2 = contour(Rrz, Zrz, np.gradient(F)[1], levels=[0.0], colors='r')

    draw()

    #; find where these two cross

    lines = find_inter(contour1, contour2)
    rex = np.interp(lines[0], R, np.arange(R.size)).astype(int)
    zex = np.interp(lines[1], Z, np.arange(Z.size)).astype(int)

    #; check for points too close to the edge
    w = np.where((rex > 2) & (rex < nr - 3) & (zex > 2) & (zex < nr - 3))
    nextrema = np.size(w)
    rex = rex[w].flatten()
    zex = zex[w].flatten()
    rp = lines[0][w]
    zp = lines[1][w]

    #;;;;;;;;;;;;;; Characterise extrema ;;;;;;;;;;;;;;;;;
    # Fit a surface through local points using 6x6 matrix
    # This is to determine the type of extrema, and to
    # refine the location
    #

    n_opoint = 0
    n_xpoint = 0

    # Calculate inverse matrix
    rio = np.array([-1, 0, 0, 0, 1, 1])  # R index offsets
    zio = np.array([0, -1, 0, 1, 0, 1])  # Z index offsets

    # Fitting flux funcion by quadradic formulation in RZ-plane
    #
    #  F = C0 + C1*ri + C2*zi + C3*ri*zi + C4*ri*ri + C5*zi*zi
    #
    #             x---3---5  Px=(ri,zi)
    #             |   |   |  P0=(-1, 0), P1=( 0,-1)
    #             0---2---4  P2=( 0, 0), P3=( 0, 1)
    #             |   |   |  P4=( 1, 0), P5=( 1, 1)
    #             x---1---x
    #
    A = np.transpose([[np.ones(6, dtype=np.float)], [rio], [zio], [rio * zio],
                      [rio**2], [zio**2]])[:, 0, :]

    for e in range(nextrema):

        # Fit in index space so result is index number
        print("Critical point " + str(e))

        localf = np.zeros(6)
        for i in range(6):
            # Get the f value in a stencil around this point

            ri = rex[e] + rio[i]  # Zero-gradient at edges
            zi = zex[e] + zio[i]
            localf[i] = F[ri, zi]

        # solve Ax=b to obtain coefficients Cx
        # res:    x=[C0,C1,C2,C3,C4,C5].T
        # localf: b=[F0,F1,F2,F3,F4,F5].T

        res, _, _, _ = np.linalg.lstsq(A, localf)

        # This determines whether saddle or extremum
        #
        # det = d2Fdri2*dF2dzi2 - d2Fdridzi*d2Fdzidri
        #

        det = 4. * res[4] * res[5] - res[3]**2

        rinew = (res[3] * res[2] - 2. * res[1] * res[5]) / det
        zinew = (res[3] * res[1] - 2. * res[4] * res[2]) / det

        if det < 0.0:
            print("   X-point:", end="")
        else:
            print("   O-point:", end="")

        rnew = rp[e]
        znew = zp[e]

        func = RectBivariateSpline(R, Z, F)

        fnew = func(rnew, znew)

        x = np.arange(np.size(R))
        y = np.arange(np.size(Z))
        rinew = np.interp(rnew, R, x)
        zinew = np.interp(znew, Z, y)

        print("   R = %15.6e, Z= %15.6e, F = %15.6e" %
              (rnew, znew, fnew[0][0]))

        if det < 0.0:

            if n_xpoint == 0:
                xpt_ri = [rinew]
                xpt_zi = [zinew]
                xpt_f = [fnew]
                n_xpoint = n_xpoint + 1
            else:
                # Check if this duplicates an existing point

                if rinew in xpt_ri and zinew in xpt_zi:
                    print("   Duplicates existing X-point.")
                else:
                    xpt_ri = np.append(xpt_ri, rinew)
                    xpt_zi = np.append(xpt_zi, zinew)
                    xpt_f = np.append(xpt_f, fnew)
                    n_xpoint = n_xpoint + 1

            scatter(rnew, znew, s=100, marker='x', color='r')

            annotate(np.str(n_xpoint - 1),
                     xy=(rnew, znew),
                     xytext=(10, 10),
                     textcoords='offset points',
                     size='large',
                     color='r')

            draw()
        else:

            if n_opoint == 0:
                opt_ri = [rinew]
                opt_zi = [zinew]
                opt_f = [fnew]
                n_opoint = n_opoint + 1
            else:
                # Check if this duplicates an existing point

                if rinew in opt_ri and zinew in opt_zi:
                    print("   Duplicates existing O-point")
                else:
                    opt_ri = np.append(opt_ri, rinew)
                    opt_zi = np.append(opt_zi, zinew)
                    opt_f = np.append(opt_f, fnew)
                    n_opoint = n_opoint + 1

            scatter(rnew, znew, s=100, marker='o', color='r')

            annotate(np.str(n_opoint - 1),
                     xy=(rnew, znew),
                     xytext=(10, 10),
                     textcoords='offset points',
                     size='large',
                     color='b')
            draw()

    print("Number of O-points: " + np.str(n_opoint))
    print("Number of X-points: " + np.str(n_xpoint))

    if n_opoint == 0:
        opt_ri = [rinew]
        opt_zi = [zinew]
        opt_f = [fnew]
        n_opoint = n_opoint + 1

    print("Number of O-points: " + str(n_opoint))

    if n_opoint == 0:
        print("No O-points! Giving up on this equilibrium")
        return Bunch(n_opoint=0, n_xpoint=0, primary_opt=-1)

    #;;;;;;;;;;;;;; Find plasma centre ;;;;;;;;;;;;;;;;;;;
    # Find the O-point closest to the middle of the grid

    mind = (opt_ri[0] -
            (old_div(np.float(nr), 2.)))**2 + (opt_zi[0] -
                                               (old_div(np.float(nz), 2.)))**2
    ind = 0
    for i in range(1, n_opoint):
        d = (opt_ri[i] -
             (old_div(np.float(nr), 2.)))**2 + (opt_zi[i] -
                                                (old_div(np.float(nz), 2.)))**2
        if d < mind:
            ind = i
            mind = d

    primary_opt = ind
    print("Primary O-point is at " + str(np.interp(opt_ri[ind], x, R)) + ", " +
          str(np.interp(opt_zi[ind], y, Z)))
    print("")

    if n_xpoint > 0:

        # Find the primary separatrix

        # First remove non-monotonic separatrices
        nkeep = 0
        for i in range(n_xpoint):
            # Draw a line between the O-point and X-point

            n = 100  # Number of points
            farr = np.zeros(n)
            dr = old_div((xpt_ri[i] - opt_ri[ind]), np.float(n))
            dz = old_div((xpt_zi[i] - opt_zi[ind]), np.float(n))
            for j in range(n):
                # interpolate f at this location
                func = RectBivariateSpline(x, y, F)

                farr[j] = func(opt_ri[ind] + dr * np.float(j),
                               opt_zi[ind] + dz * np.float(j))

            # farr should be monotonic, and shouldn't cross any other separatrices

            maxind = np.argmax(farr)
            minind = np.argmin(farr)
            if (maxind < minind): maxind, minind = minind, maxind

            # Allow a little leeway to account for errors
            # NOTE: This needs a bit of refining
            if (maxind > (n - 3)) and (minind < 3):
                # Monotonic, so add this to a list of x-points to keep
                if nkeep == 0:
                    keep = [i]
                else:
                    keep = np.append(keep, i)

                nkeep = nkeep + 1

        if nkeep > 0:
            print("Keeping x-points ", keep)
            xpt_ri = xpt_ri[keep]
            xpt_zi = xpt_zi[keep]
            xpt_f = xpt_f[keep]
        else:
            "No x-points kept"

        n_xpoint = nkeep

        # Now find x-point closest to primary O-point
        s = np.argsort(np.abs(opt_f[ind] - xpt_f))
        xpt_ri = xpt_ri[s]
        xpt_zi = xpt_zi[s]
        xpt_f = xpt_f[s]
        inner_sep = 0
    else:

        # No x-points. Pick mid-point in f

        xpt_f = 0.5 * (np.max(F) + np.min(F))

        print("WARNING: No X-points. Setting separatrix to F = " + str(xpt_f))

        xpt_ri = 0
        xpt_zi = 0
        inner_sep = 0

    #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    # Put results into a structure

    result = Bunch(
        n_opoint=n_opoint,
        n_xpoint=n_xpoint,  # Number of O- and X-points
        primary_opt=primary_opt,  # Which O-point is the plasma centre
        inner_sep=inner_sep,  #Innermost X-point separatrix
        opt_ri=opt_ri,
        opt_zi=opt_zi,
        opt_f=opt_f,  # O-point location (indices) and psi values
        xpt_ri=xpt_ri,
        xpt_zi=xpt_zi,
        xpt_f=xpt_f)  # X-point locations and psi values

    return result