From a5ea0a157c6c3b1eb5264323ce4a6d601288ba53 Mon Sep 17 00:00:00 2001 From: Olivier Sauter <olivier.sauter@epfl.ch> Date: Wed, 15 Jan 2020 16:49:56 +0100 Subject: [PATCH] add machine='imas' and getting 'ids','source','ids_name' from IMAS database --- matlab/IMAS/gdat_imas.m | 716 ++++++++++++++++++++++++++++ matlab/IMAS/imas_help_parameters.m | 60 +++ matlab/IMAS/imas_requests_mapping.m | 43 ++ 3 files changed, 819 insertions(+) create mode 100644 matlab/IMAS/gdat_imas.m create mode 100644 matlab/IMAS/imas_help_parameters.m create mode 100644 matlab/IMAS/imas_requests_mapping.m diff --git a/matlab/IMAS/gdat_imas.m b/matlab/IMAS/gdat_imas.m new file mode 100644 index 00000000..1547090f --- /dev/null +++ b/matlab/IMAS/gdat_imas.m @@ -0,0 +1,716 @@ +function [gdat_data,gdat_params,error_status,varargout] = gdat_imas(shot,data_request,varargin) +% +% function [gdat_data,gdat_params,error_status,varargout] = gdat(shot,data_request,varargin) +% +% Aim: get data from a given machine using full path or keywords. +% data_request are and should be case independent (transformed in lower case in the function and outputs) +% +% If no inputs are provided, return the list of available pre-defined data_request in gdat_data and default parameters gdat_params +% +% IMAS means assume access data from IDS database ala IMAS with tokamakname and dataversion and run_number using imas_open_env and ids_get calls +% Should be the same on ITER hpc and gateway, to be checked +% +% At first only "ids" request implemented, but keywords like ip etc should relate to specific IDS as for any machine +% +% Inputs: +% +% no inputs: return default parameters in a structure form in gdat_params +% shot: shot number (if empty uses 'idx_imas_open_env' in params from previous call or input param) +% data_request: keyword (like 'ip') or trace name or structure containing all parameters but shot number +% varargin{i},varargin{i+1},i=1:nargin-2: additional optional parameters given in pairs: param_name, param_value +% The optional parameters list might depend on the data_request +% examples of optional parameters: +% 'plot',1 (plot is set by default to 0) +% 'machine','IMAS' (the default machine is the local machine) +% +% +% Outputs: +% +% gdat_data: structure containing the data, the time trace if known and other useful information +% gdat_data.t : time trace +% gdat_data.data: requested data values +% gdat_data.dim : values of the various coordinates related to the dimensions of .data(:,:,...) +% note that one of the dim is the time, replicated in .t for clarity +% gdat_data.dimunits : units of the various dimensions, 'dimensionless' if dimensionless +% gdat_data.error_bar : if provided +% gdat_data.gdat_call : list of parameters provided in the gdat call (so can be reproduced) +% gdat_data.shot: shot number +% gdat_data.machine: machine providing the data +% gdat_data.gdat_request: keyword for gdat if relevant +% gdat_data.data_fullpath: full path to the data node if known and relevant, or relevant expression called if relevant +% gdat_data.gdat_params: copy gdat_params for completeness (gdat_params contains a .help structure detailing each parameter) +% gdat_data.xxx: any other relevant information +% +% gdat_params contains the options relevant for the called data_request. It also contains a help structure for each option +% eg.: param1 in gdat_params.param1 and help in gdat_params.help.param1 +% +% Note for liuqe parameter: At this time we have liuqe_fortran (which used to be the default) and liuqe_matlab (which is new and now becomes/is the default) +% We still have liuqe1, 2 and 3 nodes for either of them, so you can choose: +% 'liuqe',1 (2 or 3): to get the default liuqe 1, 2 or 3 (which is now the matlab version) +% 'liuqe',11 (12 or 13): to get liuqe_fortran nodes 1, 2 or 3 +% 'liuqe',21 (22 or 23): to get liuqe_matlab nodes 1, 2 or 3 (may be needed if we set the default to liuqe_fortran for old shots) +% 'liuqe',-1 : to get FBTE result +% +% Examples: +% (should add working examples for various machines (provides working shot numbers for each machine...)) +% +% to get the list of ids: aa=gdat([],'ids','machine','imas'); aa.gdat_params.sources_available +% to get an empty ids: aa=gdat([],'ids','machine','imas','source','core_profiles'); aa.core_profiles +% +% [a1,a2]=gdat; +% a2.data_request = 'ids'; a2.source='equilibrium'; a2.machine = 'imas'; +% a3=gdat(48836,a2); % gives input parameters as a structure, allows to call the same for many shots +% a4=gdat('opt1',123,'opt2',[1 2 3],'shot',48832,'data_request','Ip','opt3','aAdB'); % all in pairs +% a5=gdat(48836,'ids','source','core_profiles'); % standard call for a given IDS core_profiles, use {'',''} for a list of IDSs +% a6=gdat(48836,'ip'); % standard call for a specific keyword +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% +% Comments for local developer: +% gdat is just a "header routine" calling the gdat for the specific machine gdat_`machine`.m which can be called +% directly, thus which should be able to treat the same type of input arguments +% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Prepare some variables, etc + +varargout{1}=cell(1,1); +error_status=1; + +% construct main default parameters structure +gdat_params.data_request = ''; +default_machine = 'imas'; + +gdat_params.machine=default_machine; +gdat_params.doplot = 0; +gdat_params.nverbose = 1; +gdat_params.run = []; % should be defined by user to make sure +gdat_params.occurence = 0; % default/usual value + +% special case need to treat here if params given as structure in data_request: +if isstruct(data_request) + gdat_params = data_request; +end + +% construct list of keywords from global set of keywords and specific IMAS set +% get data_request names from centralized table +%%% data_request_names = get_data_request_names; % do not use xlsx anymore but scan respective machine_requests_mapping.m files +data_request_names = get_data_request_names_from_gdat_xxx(default_machine); +% add IMAS specific to all: +if ~isempty(data_request_names.imas) + imas_names = fieldnames(data_request_names.imas); + for i=1:length(imas_names) + data_request_names.all.(imas_names{i}) = data_request_names.imas.(imas_names{i}); + end +end +data_request_names_all = sort(fieldnames(data_request_names.all)); + +% construct default output structure +gdat_data.data = []; +gdat_data.units = []; +gdat_data.dim = []; +gdat_data.dimunits = []; +gdat_data.t = []; +gdat_data.x = []; +gdat_data.shot = []; +gdat_data.gdat_request = []; +gdat_data.gdat_params = gdat_params; +gdat_data.data_fullpath = []; + + +% Treat inputs: +ivarargin_first_char = 3; +data_request_eff = ''; +if nargin>=2 + if ischar(data_request); + data_request_eff = data_request; + data_request = data_request; % should not lower until see if keyword + elseif isstruct(data_request) + data_request_eff = data_request.data_request; + end +end +gdat_data.gdat_request = data_request_names_all; % so if return early gives list of possible request names +gdat_data.gdat_params.help = imas_help_parameters(fieldnames(gdat_data.gdat_params)); +gdat_params = gdat_data.gdat_params; + +% no inputs +if nargin==0 + % return defaults and list of keywords + return +end + +do_mdsopen_mdsclose = 1; +% treat 1st arg +if nargin>=1 + if isempty(shot) + % if shot already open, will use idx from gdat_params, otherwise returns + do_mdsopen_mdsclose = 0; + if ~isfield(gdat_data.gdat_params,'idx_imas_open_env') + if ~strcmp(data_request_eff,'ids'); return; end % empty shot return empty ids so valid command + end + elseif isnumeric(shot) + gdat_data.shot = shot; + elseif ischar(shot) + ivarargin_first_char = 1; + else + if gdat_params.nverbose>=1; warning('type of 1st argument unexpected, should be numeric or char'); end + error_status=2; + return + end + if nargin==1 + % Only shot number given. If there is a default data_request set it and continue, otherwise return + return + end +end +% 2nd input argument if not part of pairs +if nargin>=2 && ivarargin_first_char~=1 + if isempty(data_request) + return + end + % 2nd arg can be a structure with all options except shot_number, or a string for the pathname or keyword, or the start of pairs string/value for the parameters + if isstruct(data_request) + if ~isfield(data_request,'data_request') + if gdat_params.nverbose>=1; warning('expects field data_request in input parameters structure'); end + error_status=3; + return + end + data_request_eff = data_request.data_request; + % some request are case sensitive because mds names can be like \magnetics::ipol[*,"OH_001"] + data_request.data_request = data_request.data_request; + gdat_params = data_request; + else + % since data_request is char check from nb of args if it is data_request or a start of pairs + if mod(nargin-1,2)==0 + ivarargin_first_char = 2; + else + ivarargin_first_char = 3; + data_request_eff = data_request; + end + end +end + +if ~isstruct(data_request) + gdat_params.data_request = data_request_eff; +end + +% if start pairs from shot or data_request, shift varargin +if ivarargin_first_char==1 + varargin_eff{1} = shot; + varargin_eff{2} = data_request; + varargin_eff(3:nargin) = varargin(:); +elseif ivarargin_first_char==2 + varargin_eff{1} = data_request; + varargin_eff(2:nargin-1) = varargin(:); +else + varargin_eff(1:nargin-2) = varargin(:); +end + +% extract parameters from pairs of varargin: +if (nargin>=ivarargin_first_char) + if mod(nargin-ivarargin_first_char+1,2)==0 + for i=1:2:nargin-ivarargin_first_char+1 + if ischar(varargin_eff{i}) + gdat_params.(lower(varargin_eff{i})) = varargin_eff{i+1}; % cannot enforce lower(params value) if filename or else are case sensitive + else + if gdat_params.nverbose>=1; warning(['input argument nb: ' num2str(i) ' is incorrect, expects a character string']); end + error_status=401; + return + end + end + else + if gdat_params.nverbose>=1; warning('number of input arguments incorrect, cannot make pairs of parameters'); end + error_status=402; + return + end +end + +% if it is a request_keyword copy it: +if ischar(data_request_eff) || length(data_request_eff)==1 + ij=strmatch(lower(data_request_eff),data_request_names_all,'exact'); +else + ij=[]; +end +if ~isempty(ij); + gdat_data.gdat_request = data_request_names_all{ij}; + gdat_params.data_request = gdat_data.gdat_request; + if isfield(data_request_names.all.(data_request_names_all{ij}),'description') && ~isempty(data_request_names.all.(data_request_names_all{ij}).description) + % copy description of keyword + gdat_data.request_description = data_request_names.all.(data_request_names_all{ij}).description; + end +end + +% special treatment if shot and data_request given within pairs +if isfield(gdat_params,'shot') + shot = gdat_params.shot; % should use only gdat_params.shot but change shot to make sure + gdat_data.shot = gdat_params.shot; + gdat_params=rmfield(gdat_params,'shot'); +end + +if ~isfield(gdat_params,'data_request') || isempty(gdat_params.data_request) + % warning('input for ''data_request'' missing from input arguments') % might be correct, asking for list of requests + error_status=5; + return +end +gdat_data.gdat_params = gdat_params; % This means that before here it is gdat_params which should be updated +clear gdat_params; % to make sure one uses gdat_data.gdat_params now + +% re-assign main variables to make sure use the one in gdat_data structure +shot = gdat_data.shot; +data_request_eff = gdat_data.gdat_params.data_request; +if ~isfield(gdat_data.gdat_params,'database_user') + gdat_data.gdat_params.database_user = getenv('USER'); + if gdat_data.gdat_params.nverbose >= 3; disp('set database_user to username as default since not provided'); end +end +if ~isfield(gdat_data.gdat_params,'tokamak_name') + gdat_data.gdat_params.tokamak_name = gdat_data.gdat_params.machine; + if gdat_data.gdat_params.nverbose >= 3; disp('set tokamak_name to machine as default since not provided'); end +end +if ~isfield(gdat_data.gdat_params,'data_major_version') + gdat_data.gdat_params.data_major_version = '3'; + if gdat_data.gdat_params.nverbose >= 3; disp('set data_major_version to ''3'' as default since not provided'); end +end +error_status = 6; % at least reached this level +gdat_params = gdat_data.gdat_params; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Specifications on how to get the data provided in imas_requests_mapping +mapping_for_imas = imas_requests_mapping(data_request_eff,shot); +gdat_data.label = mapping_for_imas.label; +ishot=NaN; +if do_mdsopen_mdsclose + if ~isfield(gdat_data.gdat_params,'run') || isempty(gdat_data.gdat_params.run) + warning('run number not provided, cannot continue') + return + end + try + gdat_data.gdat_params.idx_imas_open_env = imas_open_env('ids', shot, gdat_data.gdat_params.run,gdat_data.gdat_params.database_user, ... + gdat_data.gdat_params.tokamak_name,gdat_data.gdat_params.data_major_version); + disp('') + catch ME + warning('could not imas_open_env with following gdat_data.gdat_params:'); + gdat_data.gdat_params + rethrow(ME) + end + if gdat_data.gdat_params.idx_imas_open_env < 0 + if gdat_data.gdat_params.nverbose>=1 + warning(['idx < 0, shot/run not opened properly with gdat_data.gdat_params:']); + gdat_data.gdat_params + end + return + end +else + if isfield(gdat_data.gdat_params,'idx_imas_open_env') + if gdat_data.gdat_params.idx_imas_open_env < 0 + if gdat_data.gdat_params.nverbose>=1 + warning(['idx < 0, shot/run not opened properly with gdat_data.gdat_params:']); + gdat_data.gdat_params + end + return + end + else + if ~isempty(shot) + if gdat_data.gdat_params.nverbose>=1 + warning(['no idx in gdat_params, cannot open shot with gdat_data.gdat_params:']); + gdat_data.gdat_params + end + return + end + end +end +gdat_params = gdat_data.gdat_params; + +% fill again at end to have full case, but here to have present status in case of early return +gdat_data.gdat_params.help = imas_help_parameters(fieldnames(gdat_data.gdat_params)); +gdat_data.mapping_for.imas = mapping_for_imas; +gdat_params = gdat_data.gdat_params; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +if strcmp(mapping_for_imas.method,'expression') + % method="expression" + % assume expression contains an expression to evaluate and which creates a local structure into variable gdat_tmp + % we copy the structure, to make sure default nodes are defined and to avoid if return is an closed object like tdi + % eval_expr = [mapping_for_imas.expression ';']; + eval([mapping_for_imas.expression ';']); + if isempty(gdat_tmp) || (~isstruct(gdat_tmp) & ~isobject(gdat_tmp)) + if (gdat_params.nverbose>=1); warning(['expression does not create a gdat_tmp structure: ' mapping_for_imas.expression]); end + error_status=801; + return + end + tmp_fieldnames = setdiff(fieldnames(gdat_tmp),{'gdat_request','label'}); % could/should also remove label in any case + if sum(strcmp(tmp_fieldnames,'data'))==0 % note: cannot do isfield since gdat_tmp might be an object + if (gdat_params.nverbose>=1); warning(['expression does not return a child name ''data'' for ' data_request_eff]); end + end + for i=1:length(tmp_fieldnames) + gdat_data.(tmp_fieldnames{i}) = gdat_tmp.(tmp_fieldnames{i}); + end + % add .t and .x in case only dim is provided + % do not allow shifting of timedim since should be treated in the relevant expression + ijdim=find(strcmp(tmp_fieldnames,'dim')==1); + if ~isempty(ijdim) + nbdims = length(gdat_data.dim); + if mapping_for_imas.timedim==-1; + mapping_for_imas.timedim = nbdims; + if (size(gdat_data.data,nbdims)==1 && nbdims>1); mapping_for_imas.timedim = nbdims-1; end + end + dim_nontim = setdiff([1:nbdims],mapping_for_imas.timedim); + ijt=find(strcmp(tmp_fieldnames,'t')==1); + if isempty(ijt) + gdat_data.t = gdat_data.dim{mapping_for_imas.timedim}; + end + ijx=find(strcmp(tmp_fieldnames,'x')==1); + if isempty(ijx) + if ~isempty(dim_nontim) + % since most cases have at most 2d, copy as array if data is 2D and as cell if 3D or more + if length(dim_nontim)==1 + gdat_data.x = gdat_data.dim{dim_nontim(1)}; + else + gdat_data.x = gdat_data.dim(dim_nontim); + end + end + end + gdat_data.data_fullpath=mapping_for_imas.expression; + if isfield(gdat_tmp,'help') + gdat_data.help = gdat_tmp.help; + else + gdat_data.help = []; + end + if isfield(gdat_data.gdat_params,'time_out') && ~isempty(gdat_data.gdat_params.time_out) + [gdat_data] = gdat2time_out(gdat_data,1); + end + end + % end of method "expression" + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +elseif strcmp(mapping_for_imas.method,'switchcase') + switch data_request_eff % not lower(...) since data_request_eff should be lower case already at this stage + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % First the request names valid for "all" machines: + % + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + case {'ids'} + ids_empty_path = fullfile(fileparts(mfilename('fullpath')),'..','IMAS_IMAS','ids_empty'); + + params_eff = gdat_data.gdat_params; + if isfield(params_eff,'source') && ~isempty(params_eff.source) + ids_top_name = params_eff.source; + else + ids_top_name = []; + warning('gdat:EmptyIDSName','Need an ids name in ''source'' parameter\n check substructure gdat_params.sources_available for an ids list'); + addpath(ids_empty_path); + assert(~~exist('ids_list','file'),'could not find ids_list.m in %s',ids_empty_path); + gdat_data.gdat_params.sources_available = ids_list; + rmpath(ids_empty_path); + return + end + ids_gen_ok = ~~exist('ids_gen','file'); + + if ids_gen_ok + ids_empty = ids_gen(ids_top_name); % generate ids from gateway function ids_gen + else + % load empty ids structure from template file + fname = sprintf('ids_empty_%s',ids_top_name); + + try + assert(~~exist(ids_empty_path,'dir'),'folder %s not found',ids_empty_path); + addpath(ids_empty_path); + assert(~~exist(fname,'file'),'file %s does not exist in %s',fname,ids_empty_path); + + ids_empty = eval(fname); + disp(['use structure in .mat file: ' ids_empty_path '/' fname]); + rmpath(ids_empty_path); + catch ME + fprintf('Could not load empty template for %s\n',ids_top_name); + fprintf('Available templates:\n'); + disp(dir(ids_empty_path)); + rmpath(ids_empty_path); + rethrow(ME); + end + end + if ~isfield(gdat_data.gdat_params,'error_bar') || isempty(gdat_data.gdat_params.error_bar) + gdat_data.gdat_params.error_bar = 'delta'; + end + if ~isfield(gdat_data.gdat_params,'cocos_in') || isempty(gdat_data.gdat_params.cocos_in) + gdat_data.gdat_params.cocos_in = 11; + end + if ~isfield(gdat_data.gdat_params,'cocos_out') || isempty(gdat_data.gdat_params.cocos_out) + gdat_data.gdat_params.cocos_out = 11; + end + if ~isfield(gdat_data.gdat_params,'ipsign_out') || isempty(gdat_data.gdat_params.ipsign_out) + gdat_data.gdat_params.ipsign_out = 0; + end + if ~isfield(gdat_data.gdat_params,'b0sign_out') || isempty(gdat_data.gdat_params.b0sign_out) + gdat_data.gdat_params.b0sign_out = 0; + end + if ~isfield(gdat_data.gdat_params,'ipsign_in') || isempty(gdat_data.gdat_params.ipsign_in) + if gdat_data.gdat_params.ipsign_out~=0 + gdat_data.gdat_params.ipsign_in = mdsvalue('\pcs::data:iohfb'); + else + gdat_data.gdat_params.ipsign_in = 0; + end + end + if ~isfield(gdat_data.gdat_params,'b0sign_in') || isempty(gdat_data.gdat_params.b0sign_in) + if gdat_data.gdat_params.b0sign_out~=0 + gdat_data.gdat_params.b0sign_in = mdsvalue('\pcs::data:if36fb'); + else + gdat_data.gdat_params.b0sign_in = 0; + end + end + try + if isfield(gdat_data.gdat_params,'idx_imas_open_env') && gdat_data.gdat_params.idx_imas_open_env >= 0 + if gdat_data.gdat_params.occurence == 0 + ids_top = ids_get(gdat_data.gdat_params.idx_imas_open_env,ids_top_name); + else + ids_top = ids_get(gdat_data.gdat_params.idx_imas_open_env,[ids_top_name '/' num2str(gdat_data.gdat_params.occurence)]); + end + gdat_data.(ids_top_name) = ids_top; + else + gdat_data.(ids_top_name) = ids_empty; + end + catch ME_imas_ids_get + disp(['there is a problem with: imas_get_ids_' ids_top_name ... + ' , may be check if it exists in your path or test it by itself']) + gdat_data.(ids_top_name) = ids_empty; + gdat_data.([ids_top_name '_help']) = getReport(ME_imas_ids_get); + rethrow(ME_imas_ids_get) + end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + otherwise + if (gdat_params.nverbose>=1); warning(['switchcase= ' data_request_eff ' not known in gdat_imas']); end + error_status=901; + return + end + +else + if (gdat_params.nverbose>=1); warning(['IMAS method=' mapping_for_imas.method ' not known yet, contact Olivier.Sauter@epfl.ch']); end + error_status=602; + return +end + +gdat_data.gdat_params.help = imas_help_parameters(fieldnames(gdat_data.gdat_params)); +gdat_data.mapping_for.imas = mapping_for_imas; +gdat_params = gdat_data.gdat_params; +error_status=0; + +return + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [firthomratio] = get_fir_thom_rat_data(shot,maintracename,timebase,nverbose); +% +% since depends on shot number for using auto fit and thomson or thomson edge, use tracename and function here +% +% maintracename = 'thomson' or 'thomson_edge +% +% return fir_to_thomson ratio on time=timebase +% +% normally should use "main" thomson one built from auto fit if possible +% take edge one if need be +% +% default: maintracename = "thomson" +% +maintracename_eff = 'thomson'; +if exist('maintracename') && ~isempty(maintracename) + maintracename_eff = maintracename; +end + +firthomratio = NaN; +if ~exist('timebase') || isempty(timebase) + if (nverbose>=1) + disp('need a timebase in get_fir_thom_rat_data') + end + return +end +firthomratio = NaN*ones(size(timebase)); + +if strcmp(maintracename_eff,'thomson') + if shot>=23801 + tracefirrat=tdi('\results::thomson.profiles.auto:fir_thom_rat'); %time base not same!! + if isempty(tracefirrat.data) || ischar(tracefirrat.data) + if (nverbose>=1); disp('problem with \results::thomson.profiles.auto:fir_thom_rat: empty, use thomson:fir_thom_rat'); end + tracefirrat=tdi('\results::thomson:fir_thom_rat'); + end + else + tracefirrat=tdi('\results::thomson:fir_thom_rat'); + if isempty(tracefirrat.data) || ischar(tracefirrat.data) + if (nverbose>=1); disp('problem with \results::thomson:fir_thom_rat: empty'); end + end + end +elseif strcmp(maintracename_eff,'thomson_edge') + if shot>=23801 + tracefirrat=tdi('\results::thomson.profiles.auto:fir_thom_rat'); %time base not same!! + if isempty(tracefirrat.data) || ischar(tracefirrat.data) + if (nverbose>=1); disp('problem with \results::thomson.profiles.auto:fir_thom_rat: empty, use thomson:fir_thom_rat'); end + tracefirrat=tdi('\results::thomson:fir_thom_rat'); + end + else + tracefirrat=tdi('\results::thomson_edge:fir_thom_rat'); + if isempty(tracefirrat.data) || ischar(tracefirrat.data) + if (nverbose>=1); disp('problem with \results::thomson_edge:fir_thom_rat: empty'); end + end + end +else + if (nverbose>=1); disp('bad input in get_fir_thom_rat_data'); end + return +end + +if ~isempty(tracefirrat.data) && ~ischar(tracefirrat.data) && any(isfinite(tracefirrat.data)) ... + && ~isempty(tracefirrat.dim) && ~isempty(tracefirrat.dim{1}) + firthomratio = interp1(tracefirrat.dim{1},tracefirrat.data,timebase); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [gdat_data] = get_thomson_raw_data(shot,data_request_eff,gdat_data,doedge,nverbose); +% +try + time=mdsdata('\results::thomson:times'); +catch + gdat_data.error_bar = []; + if strcmp(data_request_eff(1:2),'ne') + tracefirrat_data = []; + gdat_data.firrat=tracefirrat_data; + gdat_data.data_raw = gdat_data.data; + gdat_data.error_bar_raw = gdat_data.error_bar; + end + + if (nverbose>=1) && shot<100000 + warning('Problems with \results::thomson:times') + warning(['!!!!!!!!!!!!!!!!!!!!!!!!! cannot continue with ' data_request_eff]) + end + return +end +if isempty(time) || ischar(time) + thomsontimes=time; + if (nverbose>=1) && shot<100000 + warning('!!!!!!!!!!!!!!!!!!!!!!!!!\results::thomson:times is empty? Check') + disp(['!!!!!!!!!!!!!!!!!!!!!!!!! cannot continue with ' data_request_eff]) + end + return +end +edge_str_ = ''; +edge_str_dot = ''; +if doedge + edge_str_ = '_edge'; + edge_str_dot = '.edge'; +end + +nodenameeff = ['\results::thomson' edge_str_dot ':' data_request_eff(1:2)]; +tracetdi=tdi(nodenameeff); +if isempty(tracetdi.data) || ischar(tracetdi.data) || isempty(tracetdi.dim) + gdat_data.error_bar = []; + gdat_data.firrat = []; + gdat_data.data_raw = []; + gdat_data.error_bar_raw = []; + return +end + +gdat_data.data=tracetdi.data'; % Thomson data as (t,z) +tracestd=tdi(['\results::thomson' edge_str_dot ':' data_request_eff(1:2) ':error_bar']); +gdat_data.error_bar=tracestd.data'; +gdat_data.data_fullpath=[nodenameeff '; error_bar']; +% add fir if ne requested +if strcmp(data_request_eff(1:2),'ne') + tracefirrat_data = get_fir_thom_rat_data(shot,['thomson' edge_str_],time,nverbose); + gdat_data.firrat=tracefirrat_data; + gdat_data.data_raw = gdat_data.data; + gdat_data.data = gdat_data.data_raw * diag(tracefirrat_data); + gdat_data.error_bar_raw = gdat_data.error_bar; + gdat_data.error_bar = gdat_data.error_bar_raw * diag(tracefirrat_data); + gdat_data.data_fullpath=[gdat_data.data_fullpath '; fir_thom_rat ; _raw without firrat']; + ij=find(isfinite(tracefirrat_data)); + if isempty(ij) + gdat_data.data_fullpath=[gdat_data.data_fullpath ' WARNING use ne_raw since firrat has NaNs thus ne=NaNs; fir_thom_rat ; _raw without firrat']; + disp('***********************************************************************') + disp('WARNING: ne from Thomson has fir_thom_rat with Nans so only ne_raw can be used'); + disp('***********************************************************************') + end +end +z=mdsdata(['\diagz::thomson_set_up' edge_str_dot ':vertical_pos']); +gdat_data.dim=[{z};{time}]; +gdat_data.dimunits=[{'Z [m]'} ; {'time [s]'}]; +gdat_data.x=z; +gdat_data.t=time; +% isfield does not work since tracetdi is not a 'struct' but a tdi object, thus use fieldnames +if any(strcmp(fieldnames(tracetdi),'units')) + gdat_data.units=tracetdi.units; +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [psiscatvol,psi_max] = psi_scatvol_zshift(gdat_data, zshift, system_str_dot, psitbx_str, gdat_params ) +% Input arguments: +% gdat_data: current structure containing TS data +% zshift: vertical shift of the equilibrium, can be a vector for time-dependent data +% system_str_dot: '' or '.edge' indicating thomson syste, +% psitbx_str: source for the equilibrium map, psitbx-style +% gdat_params: parameters for the gdat call % TODO: Why is it different from gdat_data.gdat_params? + +% Use psitbx +t_th = gdat_data.t; +th_z = mdsdata(['dim_of(\thomson' system_str_dot ':te,1)']); % TODO: Isn't this gdat_data.dim{1}? +th_r = 0.9*ones(size(th_z)); +ntt = numel(t_th); +nch = numel(th_z); % number of chords + +[t_psi,status] = mdsvalue('dim_of(imas_eq("i_p",$))',psitbx_str); % TODO: replace with time_psi when imas_eq supports it +if ~rem(status,2) + warning('problem with getting equilibrium time base in gdat_imas') + display(t_psi); + return +end + +zshifteff=zshift; +% set zshifteff time array same as t_psi +switch numel(zshifteff) + case 1 + zshifteff=zshifteff * ones(size(t_th)); + case numel(t_psi) + zshifteff=interp1(t_psi,zshifteff,t_th); + case numel(t_th) + % ok + otherwise + if (gdat_params.nverbose>=1) + disp(' bad time dimension for zshift') + disp(['it should be 1 or ' num2str(numel(t_th)) ' or ' num2str(numel(t_psi))]) + return + end +end + +% Get LIUQE times corresponding to TS times +t_eq = t_psi(iround(t_psi,t_th)); + +% Get PSI map +psi = psitbximas(gdat_data.shot,t_eq,'*0',psitbx_str); +% PSITBXIMAS will have removed duplicate times +i_psi = iround(psi.t,t_eq); % Mapping from psi times to TS times + +% Define grid points where to evaluate PSI +if ~any(diff(zshifteff)) + % Constant vector + pgrid = psitbxgrid('cylindrical','points',{th_r,th_z - zshifteff(1),0}); +else + % We need as many time points in psi as TS times + psi = subs_time(psi,i_psi); % Now has ntt times + i_psi = 1:ntt; % Trivial mapping from psi times to TS times + % Time-varying vector + th_z_eff = repmat(th_z(:),1,ntt) - repmat(reshape(zshifteff,1,ntt),nch,1); + th_r_eff = repmat(th_r(:),1,ntt); + pgrid = psitbxgrid('cylindrical','time-points',{th_r_eff,th_z_eff,0}); +end + +% Compute psi at TS positions +psi_ts = psitbxf2f(psi,pgrid); +psiscatvol.data = squeeze(psi_ts.x(:,i_psi)); +psiscatvol.dim{1} = gdat_data.x; +psiscatvol.dim{2} = t_th; +% NOTE: we should probably not include time points where equilibrium time is far from TS time. + +% Compute psi_axis at TS times +% Do not use psi_axis node because: +% - For legacy LIUQE, psi_axis is not computed by psitbx, so we could get a +% different answer and cause complex numbers computing rho_psi +% - For MATLAB LIUQE, psi_axis is only the result of asxymex whereas the +% psitbx adds some Newton iterations so again complex numbers are possible +psi_norm = psitbxp2p(psi,'01'); +psi_max.data = psi_norm.psimag(i_psi); +psi_max.dim = {t_th}; diff --git a/matlab/IMAS/imas_help_parameters.m b/matlab/IMAS/imas_help_parameters.m new file mode 100644 index 00000000..bbf4bbd7 --- /dev/null +++ b/matlab/IMAS/imas_help_parameters.m @@ -0,0 +1,60 @@ +function help_struct = imas_help_parameters(parameter_list) +% +% retrieve from present table the relevant help lines for the parameter_list{:} +% should come from sqlite database at some point... +% +% return the whole help structure if parameter_list empty or not provided +% +% do: +% help_struct = imas_help_parameters(fieldnames(gdat_data.gdat_params)); +% +% to get relevant help description +% + +% Defaults +help_struct_all = struct(... + 'data_request', ['automatically filled in by gdat, name of request used in gdat call.' char(10) ... + 'contains current list of keywords if gdat called with no arguments: aa=gdat;' char(10) ... + 'Note shot value should not be in params so params can be used to load same data from another shot'] ... + ,'machine', 'machine name like ''TCV'', ''AUG'', ''IMAS'' case insensitive' ... + ,'doplot', '0 (default), if 1 calls gdat_plot for a new figure, -1 plot over current figure with hold all, see gdat_plot for details' ... + ,'nverbose','1 (default) displays warnings, 0: only errors, >=3: displays all extra information' ... + ); + +% IMAS related +help_struct_all.time = 'eqdsk: time(s) value(s) requested, by default time=1.0s (see time_out for other requests)'; +help_struct_all.time_out = ['requested time for output: data points within interval if time_out=[t1 t2], otherwise assumes series of points, uses linear interpolation in that case (default [-Inf Inf])'... + char(10) 'for sxr, mpx: only time interval provided in time_out is relevant']; +help_struct_all.cocos_in_out = ['cocos_in or cocos_out value input or desired in output, uses eqdsk_cocos_transform. Note should use latter if a specific Ip and/or B0 sign' ... + 'is wanted. See O. Sauter et al Comput. Phys. Commun. 184 (2013) 293']; +help_struct_all.source = sprintf('%s\n',... + 'ids_names for request ''ids'' like magnetics, equilibrium, etc'); +help_struct_all.error_bar = sprintf('%s\n','for ids: choice of nodes fill in and how:', ... + '''delta'' (default): only upper fill in with the abs(value) to add or subtract to data to get upper and lower values (symmetric)', ... + '''delta_with_lower'': same as delta but fill in lower node as well (with delta as well, same as upper)', ... + '''added'': add the delta values (old cpo style), so upper=data+error_bar and lower=data+error_bar'); +help_struct_all.write = 'eqdsk: write eqdsk while loading data (1, default) or not (0)'; +%help_struct_all. = ''; + +if ~exist('parameter_list') || isempty(parameter_list) + help_struct = help_struct_all; + return +end + +if iscell(parameter_list) + parameter_list_eff = parameter_list; +elseif ischar(parameter_list) + % assume only one parameter provided as char string + parameter_list_eff{1} = parameter_list; +else + disp(['unknown type of parameter_list in imas_help_parameters.m']) + parameter_list + return +end + +fieldnames_all = fieldnames(help_struct_all); +for i=1:length(parameter_list_eff) + if any(strcmp(fieldnames_all,parameter_list_eff{i})) + help_struct.(parameter_list_eff{i}) = help_struct_all.(parameter_list_eff{i}); + end +end diff --git a/matlab/IMAS/imas_requests_mapping.m b/matlab/IMAS/imas_requests_mapping.m new file mode 100644 index 00000000..575e4649 --- /dev/null +++ b/matlab/IMAS/imas_requests_mapping.m @@ -0,0 +1,43 @@ +function mapping = imas_requests_mapping(data_request,shot) +% +% Information pre-defined for gdat_imas, you can add cases here to match official cases in gdat_imas, allowing backward compatibility +% +% give the shot number in case data origin depends on the shot number, allows to adapt easily +% + +% Defaults +mapping = struct(... + 'label', '', ... + 'method', '', ... + 'expression','', ... + 'timedim', -1, ... % dim which is the time is the database, to copy in .t, the other dims are in .x (-1 means last dimension) + 'gdat_timedim',[], ... % if need to reshape data and dim orders to have timedim as gdat_timedim (shifting time to gdat_timedim) + 'min', -inf, ... + 'max', inf); +% Note that gdat_timedim is set to timedim at end of this function if empty +% gdat_timedim should have effective index of the time dimension in gdat + +if ~exist('data_request') || isempty(data_request) + return +end + +% default label: data_request keyword itself +mapping.label = data_request; + +% +% label is used for plotting +switch lower(data_request) + case 'ids' + mapping.timedim = 1; + mapping.label = 'ids ala imas'; + mapping.method = 'switchcase'; + otherwise + mapping.label = data_request; + mapping.method = 'switchcase'; + mapping.expression = data_request; + +end + +if isempty(mapping.gdat_timedim) + mapping.gdat_timedim = mapping.timedim; +end -- GitLab