Source code for boutpy.boutdata.pol_slice

"""Takes a 3D variable, and returns a 2D slice at fixed toroidal angle.

N sets the number of times the data must be repeated for a full torus,
e.g. n=2 is half a torus zangle gives the (real) toroidal angle of the result
"""

from __future__ import (absolute_import, division,
                        print_function, unicode_literals)

__all__ = ['pol_slice', 'polslice']

import numpy as np

from boutpy.boutdata.boutgrid import boutgrid

def zinterp(var3d, yind, zind):
    """3points Polynomial Interpolation

    Parameters
    ----------
    var3d : 3d-array
        target 3d-array which has shape (nx, ny, nz)
    yind : 1d-array, scalar
        the basic y index, (ny, )
    zind : 2d-array
        target zind for interpolation, in shape (nx, n) if ny=1, or
        (nx, ny) if ny != 1.

    Returns
    -------
    var_interp : ndarray
        in shape (nx, n)

    """

    nx, n = zind.shape
    if not isinstance(yind, int):
        ny = np.asarray(yind).shape[1]
        assert ny == n
    z0 = np.floor(zind)

    z0 = z0.astype(int)
    # Make z0 between 0 and (nz-2)
    z0 = ((z0 % (nz - 1)) + (nz - 1)) % (nz - 1)

    # Get z+ and z-
    zp = (z0 + 1) % (nz - 1)
    zm = (z0 - 1 + (nz - 1)) % (nz - 1)

    dz = (zind - z0)

    # 3-Points Polynomial Interpolation
    var_interp = np.zeros([nx, n])
    for j in range(n):
        for i in range(nx):
            p = dz[i, j]
            iyind = yind if isinstance(yind, int) else j
            var_interp[i, j] = (
                0.5 * p * (p - 1.0) * var3d[i, iyind, zm[i, j]] +
                (1.0 - p * p) * var3d[i, iyind, z0[i, j]] +
                0.5 * p * (p + 1.0) * var3d[i, iyind, zp[i, j]])
    return var_interp


[docs]def polslice(var3d, gridfile, n=1, zangle=0.0, profile=None): """Return a 2D slice by interpolation. Parameters ---------- var3d : 3D-array 3D vars in [x, y, z] order gridfile : str | dict path to gridfile (srt) or dict data of grid (dict) n : int, optional, default: 1 zperiod used in simulation zangle : float, optional, default: 0 get result at toroidal angle ``zangle`` (in degree). profile : ndarray, optional, default: None equilibrium profile. Returns ------- var2d : 2D-array value of poloidal cross-section at toroidal angle ``zangle``. """ raise NotImplementedError("Double check required!") n = int(n) zangle = np.pi * float(zangle) / 180.0 s = np.shape(var3d) if len(s) != 3: raise ValueError("pol_slice expects a 3D variable") nx, ny, nz = s # TODO: should be n * nz dz = 2. * np.pi / float(n * (nz - 1)) # Open the grid file gf = boutgrid(gridfile) Rxy = gf['Rxy'] Zxy = gf['Zxy'] # Check the grid size is correct if [gf["nx"], gf["ny"]] != [nx, ny]: raise ValueError("Missmatched grid size: {} --> {}".format( [gf['nx'], gf['ny']], [nx, ny])) # Get the toroidal shift try: if "qinty" in gf.keys(): zShift = gf['qinty'] print("Using qinty as toroidal shift angle") else: zShift = gf["zShift"] print("Using zShift as toroidal shift angle") except: raise ValueError("ERROR: Neither qinty nor zShift found") var2d = np.zeros([nx, ny]) ###################################### # Perform 2D slice zind = (zangle - zShift) / dz # number of points between grid points: y & y + 1 nskip = (np.abs(zShift[:, 1:] - zShift[:, :-1]) / dz - 1).max(axis=0) nskip = nskip.round().astype(int) ny_interp = int(np.sum(nskip)) + ny print("Number of poloidal points in output: ", ny_interp) var_interp = np.zeros([nx, ny_interp]) rxy_interp = np.zeros([nx, ny_interp]) zxy_interp = np.zeros([nx, ny_interp]) # map original grid points ypos = np.append([0], nskip.cumsum()) + np.arange(ny) var_interp[:, ypos] = zinterp(var3d, np.arange(ny), zind) rxy_interp[:, ypos] = Rxy zxy_interp[:, ypos] = Zxy # map points between grid points dzi = (zind[:, 1:] - zind[:, :-1]) / (nskip + 1.) for y in range(ny - 1): y_yind = np.arange(nskip[y]).reshape(1, nskip[y]) print(y, ypos[y] + 1) # zindex zi = zind[:, y].reshape(nx, 1) + y_yind * dzi[:, y][:, None] # weighting: (1, nskip[y]) w = (y_yind + 1) / (nskip[y] + 1.0) var_interp[:, ypos[y] + 1:ypos[y + 1]] = ( w * zinterp(var3d, y + 1, zi) + (1 - w) * zinterp(var3d, y, zi)) if profile is not None: var_interp[:, ypos[y] + 1:ypos[y + 1]] += ( w * profile[:, y + 1][:, None] + (1 - w) * profile[:, y][:, None]) rxy_interp[:, ypos[y] + 1:ypos[y + 1]] = (w * Rxy[:, y + 1][:, None] + (1 - w) * Rxy[:, y][:, None]) zxy_interp[:, ypos[y] + 1:ypos[y + 1]] = (w * Zxy[:, y + 1][:, None] + (1 - w) * Zxy[:, y][:, None]) return rxy_interp, zxy_interp, var_interp
[docs]def pol_slice(var3d, gridfile, n=1, zangle=0.0): """data2d = pol_slice(data3d, 'gridfile', n=1, zangle=0.0) Parameters ---------- var3d : 3D-array 3D vars in [x, y, z [, t]] order gridfile : str path to gridfile n : int, optional, default: 1 zperiod used in simulation zangle : float, optional, default: 0 get result at toroidal angle ``zangle`` (in degree) Returns ------- var2d : 2D-array value of poloidal cross-section at toroidal angle ``zangle``. """ n = int(n) zangle = np.pi * float(zangle) / 180. s = np.shape(var3d) if len(s) != 3: raise ValueError("pol_slice expects a 3D variable") nx, ny, nz = s dz = 2. * np.pi / float(n * (nz - 1)) # Open the grid file gf = boutgrid(gridfile) Rxy = gf['Rxy'] Zxy = gf['Zxy'] # Check the grid size is correct if [gf["nx"], gf["ny"]] != [nx, ny]: raise ValueError("Missmatched grid size: {} --> {}".format( [gf['nx'], gf['ny']], [nx, ny])) # Get the toroidal shift try: if "qinty" in gf.keys(): zShift = gf['qinty'] print("Using qinty as toroidal shift angle") else: zShift = gf["zShift"] print("Using zShift as toroidal shift angle") except: raise ValueError("ERROR: Neither qinty nor zShift found") var2d = np.zeros([nx, ny]) ###################################### # Perform 2D slice zind = (zangle - zShift) / dz z0 = np.floor(zind) print(z0.shape) z0 = z0.astype(int) p = zind - z0.astype(float) # Make z0 between 0 and (nz-2) z0 = ((z0 % (nz - 1)) + (nz - 1)) % (nz - 1) # Get z+ and z- zp = (z0 + 1) % (nz - 1) zm = (z0 - 1 + (nz - 1)) % (nz - 1) # There may be some more cunning way to do this indexing for x in np.arange(nx): for y in np.arange(ny): var2d[x, y] = 0.5 * p[x, y] * (p[x, y] - 1.0) * \ var3d[x, y, zm[x, y]] + \ (1.0 - p[x, y] * p[x, y]) * var3d[x, y, z0[x, y]] + \ 0.5 * p[x, y] * (p[x, y] + 1.0) * var3d[x, y, zp[x, y]] return var2d