Esempio n. 1
0
def deltapsi_calc(pulse):
    """
    Calculates difference between value of psi at magnetic axis and at sepraratrix, function of time

    :param pulse: JET pulse number
    :return : difference, function of time
    """
    DATA_PATH = '/pulse/{}/ppf/signal/jetppf/efit/{}:{}'
    psi_lcfs = sal.get(DATA_PATH.format(pulse, 'fbnd', 0))
    psi_axis = sal.get(DATA_PATH.format(pulse, 'faxs', 0))
    deltapsi = (psi_lcfs.data - psi_axis.data)
    return deltapsi
Esempio n. 2
0
    def get_attr(self, source, shot, uid, seq, t_choice, interpolate):
        # Set up stem for data-access.
        defaultStem = '/pulse/{}/ppf/signal/{}'.format(shot, uid)
        branchInfo = sal.list(defaultStem)
        if seq == -1:
            seq = branchInfo.revision_latest
            self.seq = seq
        if seq in branchInfo.revision_modified:
            # Check whether sequence number exists for this UID and shot.
            seq_post = ':{}'.format(seq)
        else:
            print('Could not find sequence {} for pulse data in {}'.format(
                seq, defaultStem))
            quit()
        print('Getting data from {} for sequence {}.'.format(defaultStem, seq))

        # Get one-dimensional variables.
        # TODO check whether this FBND is actually used!
        # Signal code
        od_signals = ['fbnd', 'btor']
        # Signal uid; some signals stored in jetppf instead of our 'desired' uid.
        od_uids = ['jetppf', uid]
        # Signal sequence; take the most recent sequence for non-uid signals.
        od_seq_posts = ['', seq_post]
        # Signal DDA; The folder where we will find the signal. e.g. jsp, prfl, efit etc...
        od_ddas = ['efit', 'jst']
        # Signal tag; Used to search for the signal later on once it is stored in a dictionary.
        od_tags = ['phia', '1d_template']

        zerod = {}
        oned = {}
        twod = {}

        for i in range(len(od_signals)):
            if od_uids[i] != '':
                sigString = '/pulse/{}/ppf/signal/{}/{}/{}{}'.format(
                    shot, od_uids[i], od_ddas[i], od_signals[i],
                    od_seq_posts[i])
            else:
                sigString = defaultStem + '/{}/{}'.format(
                    od_ddas[i], od_signals[i])
            try:
                node = sal.get(sigString)
                print('Found {} at {}.'.format(od_tags[i], sigString))
                oned[od_tags[i]] = tt_structs.oned_item(
                    node, node.data, od_tags[i])
            except:
                print('! --- {} not found at {} --- !'.format(
                    od_tags[i], sigString))
                continue

        # Get two-dimensional variables.
        td_signals = [
            'te', 'ti', 'ne', 'nih', 'nid', 'nit', 'nim1', 'nim2', 'nim3',
            'nimp', 'zia1', 'zia2', 'zia3', 'dnbd', 'drfd', 'wnbd', 'wrfd',
            'q', 'r', 'ri', 'elo', 'tri', 'zeff', 'angf', 'f'
        ]
        td_ddas = [
            "jsp", "jsp", "jsp", "jsp", "jsp", "jsp", "jsp", "jsp", "jsp",
            "jsp", "jsp", "jsp", "jsp", "jsp", "jsp", "jsp", "jsp", "jsp",
            "jsp", "jsp", "jsp", "jsp", "jsp", "jsp", 'jsp'
        ]
        td_tags = [
            'temp_e', 'temp_i', 'dens_e', 'dens_h', 'dens_d', 'dens_t',
            'dens_imp1', 'dens_imp2', 'dens_imp3', 'dens_imptot', 'z_imp1',
            'z_imp2', 'z_imp3', 'dens_beam', 'dens_rf', 'endens_beam',
            'endens_rf', 'safety', 'majrad_out', 'majrad_in', 'elo', 'tri',
            'zeff', 'tor_angv', 'polcurfunc'
        ]
        td_syms = [
            r'$T$', r'$T$', r'$n$', r'$n$', r'$n$', r'$n$', r'$n$', r'$n$',
            r'$n$', r'$n_{{imptot}}$', r'$Z_{{imp1}}$', '$Z_{{imp2}}$',
            '$Z_{{imp3}}$', r'$n_{{\rm{{beam}}}}$', r'$n_{{\rm{{RF}}}}$',
            r'$W_{{\rm{{beam}}}}$', r'$W_{{\rm{{RF}}}}$', '$q$',
            r'$R_{{\rm{{out}}}}$', r'$R_{{\rm{{in}}}}$', r'$\kappa$',
            r'$\delta$', r'$Z_{{\rm{{eff}}}}$', r'$\omega_{{\rm{{tor}}}}$',
            '$I$'
        ]

        for i in range(len(td_signals)):
            sigString = defaultStem + '/{}/{}{}'.format(
                td_ddas[i], td_signals[i], seq_post)
            try:
                node = sal.get(sigString)
            except:
                print('! --- {} not found at {} --- !'.format(
                    td_tags[i], sigString))
                continue
            if not np.all(node.data == 0.0):
                # Only store data if it contained some non-zero elements.
                twod[td_tags[i]] = tt_structs.twod_item(node,
                                                        node.data,
                                                        td_tags[i],
                                                        zsym=td_syms[i])
                print('Found {} at {}.'.format(td_tags[i], sigString))
            else:
                print('! --- Array of zeros found for {} at {} --- !'.format(
                    td_tags[i], sigString))

        # Get zero-dimensional variables. Note that some of these are not actually zero-d! Anything with jst is actually time-dependent.
        # This needs to be kept in mind when processing this data object.
        zd_signals = [
            'btor', 'cur', 'nel', 'pnb', 'prf', 'tiax', 'teax', 'zeff', 'nimp',
            'nish', 'zim1', 'zim2', 'zim3', 'aim1', 'aim2', 'aim3', 'zfd',
            'afd'
        ]
        zd_ddas = [
            "jst", "jst", "jst", "jst", "jst", "jst", "jst", "jst", "jss",
            "jss", "jss", "jss", "jss", "jss", "jss", "jss", "jss", "jss"
        ]
        zd_tags = [
            'b_tor', 'plasma_cur', 'line_av_dens_e', 'p_nbi', 'p_rf',
            'ax_temp_i', 'ax_temp_e', 'z_eff', 'num_imps', 'num_hyd_spec',
            'z_imp1', 'z_imp2', 'z_imp3', 'mass_imp1', 'mass_imp2',
            'mass_imp3', 'z_fast', 'mass_fast'
        ]
        zd_syms = [
            '$B_T$', '$I_P$', '$<n_e>$', r'$P_{{\rm{{NBI}}}}$',
            r'$P_{{\rm{{RF}}}}$', r'$T_{{\rm{{ax}},i}}$',
            r'$T_{{\rm{{ax}},e}}$', r'$Z_{{\rm{{eff}}}}$',
            r'$N_{{\rm{{imps}}}}$', r'$N_{{\rm{{hyds}}}}$', r'$maxZimp1$',
            '$maxZimp2$', '$maxZimp3$', '$massimp1$', '$massimp2$',
            '$massimp3$', '$maxZfast$', '$massfast$'
        ]

        for i in range(len(zd_signals)):
            sigString = defaultStem + '/{}/{}{}'.format(
                zd_ddas[i], zd_signals[i], seq_post)
            try:
                node = sal.get(sigString)
                print('Found {} at {}.'.format(zd_tags[i], sigString))
                if zd_ddas[i] == 'jst':
                    # If dda is jst, then the data comes as a one-d array against time. Pick the zero-d value at the time-index that most closely matches our time-choice.
                    itime = int(
                        (np.abs(node.dimensions[0].data - t_choice)).argmin())
                    data = tt_structs.oned_item(node, node.data,
                                                zd_tags[i]).reduce_dim(
                                                    t_choice, interpolate).data
                    #data = node.data[itime]
                else:
                    data = node.data
                zerod[zd_tags[i]] = tt_structs.zerod_item(node,
                                                          data,
                                                          zd_tags[i],
                                                          sym=zd_syms[i])
            except:
                print('! --- {} not found at {} --- !'.format(
                    zd_tags[i], sigString))
                continue

        # Make entries for the masses and charges of the hydrogenic ions and electrons.
        for ispec in range(4):
            specLabel = ['h', 'd', 't', 'e'][ispec]
            if 'dens_{}'.format(specLabel) in twod:
                m = 'mass_{}'.format(specLabel)
                z = 'z_{}'.format(specLabel)
                zerod[m] = tt_structs.zerod_item(
                    cp(zerod['b_tor'].signal),
                    np.array([[1.0, 2.0, 3.0, 5.4858e-4][ispec]]),
                    m,
                    description='Mass for species {}'.format(specLabel),
                    units='[amu]',
                    sym='$M$')
                zerod[z] = tt_structs.zerod_item(
                    cp(zerod['b_tor'].signal),
                    np.array([[1.0, 1.0, 1.0, -1.0][ispec]]),
                    z,
                    description='Atomic number for species {}'.format(
                        specLabel),
                    units='',
                    sym='$Z$')

        # Use Wesson's formula for electron-ion collisionality. Coulomb logarithm is loglam.
        loglam = 14.9 - 0.5 * np.log(1.0e-20 * twod['dens_e'].data) + np.log(
            1.0e-3 * twod['temp_e'].data)
        nu_ei = 917.4 * (1.0e-19 * twod['dens_e'].data) * loglam / np.power(
            1.0e-3 * twod['temp_e'].data, 1.5)

        ion_tags = ['h', 'd', 't', 'imp1', 'imp2', 'imp3']
        Tion = twod['temp_i'].data
        Te = twod['temp_e'].data
        ne = twod['dens_e'].data
        me = 5.4858e-4  # In atomic mass units.
        for i in range(6):
            speci = ion_tags[i]
            densi = 'dens_{}'.format(speci)
            if densi in twod:
                mi = zerod['mass_{}'.format(speci)].data
                ni = twod[densi].data
                zi = zerod['z_{}'.format(speci)].data
                col_fac_sum = 0
                for j in range(6):
                    specj = ion_tags[j]
                    densj = 'dens_{}'.format(specj)
                    if densj in twod:
                        mj = zerod['mass_{}'.format(specj)].data
                        nj = twod[densj].data
                        zj = zerod['z_{}'.format(specj)].data
                        col_fac_sum = col_fac_sum + (nj * zj**2 / ne) * 2.0 / (
                            1 + np.sqrt(mi / mj))
                twod['nu_{}'.format(speci)] = tt_structs.twod_item(
                    cp(twod['majrad_out'].signal),
                    nu_ei * np.sqrt(me / mi) * np.power(Te / Tion, 1.5) *
                    zi**2,
                    'nu_{}'.format(speci),
                    zdescription='Collisionality or species {}'.format(speci),
                    zunits='[s-1]',
                    zsym=r'$\nu$')
        twod['nu_e'] = tt_structs.twod_item(
            cp(twod['majrad_out'].signal),
            nu_ei,
            'nu_e',
            zdescription='Collisionality of species e',
            zunits='[s-1]',
            zsym=r'\nu_e')

        # Calculate minor and major radii.
        twod['minrad'] = tt_structs.twod_item(
            cp(twod['majrad_out'].signal),
            (cp(twod['majrad_out'].data) - cp(twod['majrad_in'].data)) / 2.0,
            'minrad',
            zdescription='Minor radius',
            zsym='$r$')
        twod['majrad'] = tt_structs.twod_item(
            cp(twod['majrad_out'].signal),
            (cp(twod['majrad_out'].data) + cp(twod['majrad_in'].data)) / 2.0,
            'majrad',
            zdescription='Major radius',
            zsym='$R_0$')

        # Calculate Rgeo. I have used the definition that I think is correct, which is different to CMR's script that uses majrad[:,-1].
        # I think it should be the poloidal current function on the flux surface of interest (i.e. a function of minrad), divided by the normalizing B field.
        twod['rgeo'] = tt_structs.twod_item(cp(twod['majrad'].signal),
                                            cp(twod['polcurfunc'].data) /
                                            cp(zerod['b_tor'].data),
                                            'rgeo',
                                            zdescription=r'$R_{\rm{geo}}$',
                                            zsym=r'R_{\rm{geo}}')

        # Write down electron mass.

        return (zerod, oned, twod)
Esempio n. 3
0
def sal_jet(pulse, timex=47.0, time_unit="s"):
    """
    Main loading routine, based on simple access layer, loads ppf data, calculates derivatives
    :param pulse: JET pulse number
    :param timex: time of slice
    :param time_unit: (str) "s" or "ms"
    :return: equilibrium
    """

    if time_unit.lower() == "s":
        time_factor = 1.
    elif time_unit.lower() == "ms":
        time_factor = 1000.
    else:
        raise ValueError("Unknown `time_unit`.")

    data_path = '/pulse/{}/ppf/signal/jetppf/efit/{}:{}'

    # default sequence
    sequence = 0

    # obtain psi data (reshape, transpose) and time axis
    packed_psi = sal.get(data_path.format(pulse, 'psi', sequence))
    psi = packed_psi
    psi.data = packed_psi.data[:, :].reshape(len(packed_psi.dimensions[0]), 33,
                                             33)
    psi.data = np.swapaxes(psi.data, 1, 2)

    time = packed_psi.dimensions[0].data

    # psi grid axis
    r = sal.get(data_path.format(pulse, 'psir', sequence)).data
    z = sal.get(data_path.format(pulse, 'psiz', sequence)).data

    # pressure profile
    pressure = sal.get(data_path.format(pulse, 'p', sequence))
    psi_n = pressure.dimensions[1].data

    # f-profile
    f = sal.get(data_path.format(pulse, 'f', sequence))

    # q-profile
    q = sal.get(data_path.format(pulse, 'q', sequence))

    # calculate pprime and FFprime
    deltapsi = deltapsi_calc(pulse)

    pprime = pprime_calc(pressure, deltapsi, len(psi_n))

    FFprime = FFprime_calc(f, deltapsi, len(psi_n))

    #create dataset

    dst = xr.Dataset(
        {
            'psi': (['time', 'R', 'Z'], psi.data),
            'pressure': (['time', 'psi_n'], pressure.data),
            'pprime': (['time', 'psi_n'], pprime),
            'F': (['time', 'psi_n'], f.data),
            'FFprime': (['time', 'psi_n'], FFprime),
            'q': (['time', 'psi_n'], q.data),
            'R': (['R'], r),
            'Z': (['Z'], z),
        },
        coords={
            'time': time,
            'psi_n': psi_n,
        })

    # select desired time
    ds = dst.sel(time=timex / time_factor, method='nearest')

    # try to load limiter from ppfs

    try:
        limiter_r = sal.get(data_path.format(pulse, 'rlim', sequence)).data.T
        limiter_z = sal.get(data_path.format(pulse, 'zlim', sequence)).data.T
    except NodeNotFound:
        limiter_r = sal.get(data_path.format(94508, 'rlim', sequence)).data.T
        limiter_z = sal.get(data_path.format(94508, 'zlim', sequence)).data.T
        print("Limiter points not present in #{}, loaded from #94508".format(
            pulse))

    limiter = np.column_stack([limiter_r, limiter_z])

    # create pleque equilibrium

    eq = Equilibrium(ds, limiter)

    return eq
Esempio n. 4
0
psin_3d = AxisymmetricMapper(equil_time_slice.psi_normalised)
inside_lcfs = equil_time_slice.inside_lcfs

# ########################### PLASMA CONFIGURATION ########################## #
print('Plasma configuration')

plasma = Plasma(parent=world)
plasma.atomic_data = adas
plasma.b_field = VectorAxisymmetricMapper(equil_time_slice.b_field)

DATA_PATH = '/pulse/{}/ppf/signal/{}/{}/{}:{}'
user = '******'
sequence = 0

psi_coord = sal.get(
    DATA_PATH.format(PULSE_PLASMA, user, 'PRFL', 'C6',
                     sequence)).dimensions[0].data
mask = psi_coord <= 1.0
psi_coord = psi_coord[mask]

flow_velocity_tor_data = sal.get(
    DATA_PATH.format(PULSE_PLASMA, user, 'PRFL', 'VT', sequence)).data[mask]
flow_velocity_tor_psi = Interpolate1DCubic(psi_coord, flow_velocity_tor_data)
flow_velocity_tor = AxisymmetricMapper(
    Blend2D(Constant2D(0.0), IsoMapper2D(psin_2d, flow_velocity_tor_psi),
            inside_lcfs))
flow_velocity = lambda x, y, z: Vector3D(y * flow_velocity_tor(x, y, z), - x * flow_velocity_tor(x, y, z), 0.) \
                                / np.sqrt(x*x + y*y)

ion_temperature_data = sal.get(
    DATA_PATH.format(PULSE_PLASMA, user, 'PRFL', 'TI', sequence)).data[mask]
Esempio n. 5
0
    def __init__(self, pulse, user=None, dda=None, sequence=None):

        DDA_PATH = '/pulse/{}/ppf/signal/{}/{}:{}'
        DATA_PATH = '/pulse/{}/ppf/signal/{}/{}/{}:{}'

        # defaults
        user = user or 'jetppf'
        dda = dda or 'efit'
        sequence = sequence or 0

        self.pulse = pulse
        self.user = user
        self.dda = dda

        # identify the current head sequence number if seq = 0 to ensure all data from same sequence
        # this should mitigate the very low probability event of new data being written part way through the read
        if sequence == 0:
            r = sal.list(DDA_PATH.format(pulse, user, dda, sequence))
            sequence = r.revision_latest
        self.sequence = sequence

        # obtain psi data and timebase
        self._packed_psi = sal.get(
            DATA_PATH.format(pulse, user, dda, 'psi', sequence))
        self.time_slices = self._packed_psi.dimensions[0].data

        # psi grid axis
        self._r = sal.get(DATA_PATH.format(pulse, user, dda, 'psir',
                                           sequence)).data
        self._z = sal.get(DATA_PATH.format(pulse, user, dda, 'psiz',
                                           sequence)).data

        # obtain f-profile
        self._f = sal.get(DATA_PATH.format(pulse, user, dda, 'f', sequence))

        # obtain psi at the plasma boundary and magnetic axis
        self._psi_lcfs = sal.get(
            DATA_PATH.format(pulse, user, dda, 'fbnd', sequence))
        self._psi_axis = sal.get(
            DATA_PATH.format(pulse, user, dda, 'faxs', sequence))

        # obtain magnetic axis coordinates
        self._axis_coord_r = sal.get(
            DATA_PATH.format(pulse, user, dda, 'rmag', sequence))
        self._axis_coord_z = sal.get(
            DATA_PATH.format(pulse, user, dda, 'zmag', sequence))

        # obtain vacuum magnetic field sample
        self._b_vacuum_magnitude = sal.get(
            DATA_PATH.format(pulse, user, dda, 'bvac', sequence))

        # obtain lcfs boundary polygon
        self._lcfs_poly_r = sal.get(
            DATA_PATH.format(pulse, user, dda, 'rbnd', sequence))
        self._lcfs_poly_z = sal.get(
            DATA_PATH.format(pulse, user, dda, 'zbnd', sequence))

        self.time_range = self.time_slices.min(), self.time_slices.max()
Esempio n. 6
0
def getsignal_sal(exp_id, source, no_data=False, options={}):
    """ Signal reading function for the JET API SAL
    Does not require using the FLAP storage
    Input: same as get_data()
    Output: DataObject for the given source
    """
    # Function used for getting either ppf or jpf data from the server
    #
    from jet.data import sal

    options_default = {
        "Sequence": 0,
        "UID": "jetppf",
        "Check Time Equidistant": True,
        "Cache Data": True
    }
    options = {**options_default, **options}

    #checking if the data is already in the cache
    curr_path = os.path.dirname(os.path.abspath(__file__))
    location = os.path.sep.join(curr_path.split(os.path.sep))
    split_source = source.split("/")
    filename = os.path.join(os.path.sep.join([location,"cached"]),
                            ("_".join(split_source)+"-"+options["UID"]+\
                            "-"+str(exp_id)+".hdf5").lower())
    if os.path.exists(filename):
        return flap.load(filename)

    # obtaining the data from the server
    # if the UID ends with -team, then it looks in the config file, whether
    # there is a team defined under the name given in UID in the config file
    # if so, it will loop over the name of the team members
    split_uid = options["UID"].split("-")
    if split_uid[-1] == "team":
        uid_list = flap.config.get("Module JET_API",
                                   options["UID"],
                                   evaluate=True)
    else:
        if options["UID"] == "curr_user":
            options["UID"] = pwd.getpwuid(os.getuid()).pw_name
        uid_list = [options["UID"]]

    # has to do some character conversion in the names as SAL uses different
    # naming as the standard ppf names
    character_dict = {
        '>': '_out_',
        '<': '_in_',
        ':': '_sq_',
        '$': '_do_',
        ':': '_sq_',
        '&': '_et_',
        ' ': '_sp_'
    }
    node = split_source[2].lower()
    for char in character_dict.keys():
        node = character_dict[char].join(node.split(char))

    signal_error = None
    error_string = str()
    for uid in uid_list:
        try:
            if split_source[0].lower() == "ppf":
                source_string = "/pulse/"+str(exp_id)+"/"+split_source[0].lower()+"/signal/"+uid.lower()+"/"+split_source[1].lower()\
                            +"/"+node
            elif split_source[0].lower() == "jpf":
                source_string = "/pulse/"+str(exp_id)+"/"+split_source[0].lower()+"/"+split_source[1].lower()\
                            +"/"+node+"/data"
            else:
                raise ValueError("Source should be either jpf or ppf")
            if options["Sequence"] != 0:
                source_string = source_string + ":" + str(options["Sequence"])
            raw_signal = sal.get(source_string)
            data = raw_signal.data
            signal_error = raw_signal.error
        except sal.SALException as e:
            error_string = error_string + source_string + " " + str(e) + "\n"
    if not (signal_error is None):
        raise ValueError("Error reading " + source + " for shot " +
                         str(exp_id) + ", UID " + options["UID"] + ":\nIER " +
                         str(ier) + ": " + desc)
    elif not ("data" in locals()):
        raise ValueError("No data found with errors: \n" + error_string)
    # converting data to flap DataObject
    # possible names for the time coordinate
    time_names = [
        'time', 't', 'jpf time vector', 'ppf time vector', 'time vector',
        'the ppf t-vector.'
    ]
    coordinates = []
    data = data.reshape(raw_signal.shape)
    info = "Obtained at " + str(date.today()) + ", uid " + uid + "\n"
    coord_dimension = 0
    for coord in raw_signal.dimensions:
        name = coord.description
        values = np.array(coord.data)
        unit = coord.units
        equidist = False
        if name.lower() in time_names or coord.temporal is True:
            tunit = ["secs", "s", "seconds", "second"]
            if unit.lower() in tunit:
                unit = "Second"
            name = 'Time'
            equidist = False
            if options['Check Time Equidistant'] is True and (unit == "Second" or coord.temporal is True)\
               and (len(values)==1 and values[0]==-1):
                timesteps = np.array(values[1:]) - np.array(values[0:-1])
                equidistance = np.linalg.norm(
                    timesteps - timesteps[0]) / np.linalg.norm(timesteps)
                if equidistance < 1e-6:
                    info = info + "Time variable is taken as equidistant to an accuracy of "+\
                           str(equidistance)+"\n"
                    coord_object = flap.Coordinate(
                        name=name,
                        unit=values,
                        start=values[0],
                        shape=values.shape,
                        step=np.mean(timesteps),
                        mode=flap.CoordinateMode(equidistant=True),
                        dimension_list=[0])
                    equidist = True
        if equidist is False:
            coord_object = flap.Coordinate(
                name=name,
                unit=unit,
                values=values,
                shape=values.shape,
                mode=flap.CoordinateMode(equidistant=False),
                dimension_list=[coord_dimension])
        coordinates.append(coord_object)
        coord_dimension = coord_dimension + 1

    unit = flap.Unit(name=source, unit=raw_signal.units)
    signal = flap.DataObject(data_array=data,
                             data_unit=unit,
                             coordinates=coordinates,
                             exp_id=str(exp_id),
                             data_title=raw_signal.description,
                             data_shape=data.shape,
                             info=info)
    if "summary" in dir(raw_signal):
        signal.info = signal.info + raw_signal.summary().description + "\n"
    if options["Cache Data"] is True:
        flap.save(signal, filename)
    return signal