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
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)
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
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]
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()
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