source: trunk/src/struct2nc.m @ 1049

Last change on this file since 1049 was 1033, checked in by sommeria, 7 years ago

miscellaneous updates

File size: 12.3 KB
RevLine 
[106]1% 'struct2nc': create a netcdf file from a Matlab structure
2%---------------------------------------------------------------------
[943]3% errormsg=struct2nc(flname,Data,action)
[106]4%
[811]5% OUTPUT:
[106]6% errormsg=error message, =[]: default, no error
7%
8% INPUT:
9% flname: name of the netcdf file to create (must end with the extension '.nc')
10%  Data: structure containing all the information of the netcdf file (or netcdf object)
11%           with fields:
12%       (optional) .ListGlobalAttribute: list (cell array of character strings) of the names of the global attributes Att_1, Att_2...
13%                  .Att_1,Att_2...: values of the global attributes
14%      (requested) .ListVarName: list of the variable names Var_1, Var_2....(cell array of character strings).
15%      (requested) .VarDimName: list of dimension names for each element of .ListVarName (cell array of string cells)
16%       (optional) .VarAttribute: cell array of structures of the form .VarAttribute{ivar}.key=value, defining an attribute key name and value for the variable #ivar
17%      (requested) .Var1, .Var2....: variables (Matlab arrays) with names listed in .ListVarName
[943]18% action: if ='keep_open', don't close the file after creation, keep it open for further data input
[106]19
[809]20%=======================================================================
[1027]21% Copyright 2008-2018, LEGI UMR 5519 / CNRS UGA G-INP, Grenoble, France
[809]22%   http://www.legi.grenoble-inp.fr
23%   Joel.Sommeria - Joel.Sommeria (A) legi.cnrs.fr
24%
[106]25%     This file is part of the toolbox UVMAT.
[809]26%
[106]27%     UVMAT is free software; you can redistribute it and/or modify
[809]28%     it under the terms of the GNU General Public License as published
29%     by the Free Software Foundation; either version 2 of the license,
30%     or (at your option) any later version.
31%
[106]32%     UVMAT is distributed in the hope that it will be useful,
33%     but WITHOUT ANY WARRANTY; without even the implied warranty of
34%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
[809]35%     GNU General Public License (see LICENSE.txt) for more details.
36%=======================================================================
[106]37
[943]38function [errormsg,nc]=struct2nc(flname,Data,action,ListDimName,DimValue,VarDimIndex)
[930]39nc=[];
[943]40if ~ischar(flname)
41    errormsg='invalid input for the netcf file name';
42    return
43end
[106]44if ~exist('Data','var')
45     errormsg='no data  input for the netcdf file';
46    return
47end
[919]48
[930]49
[919]50%% check the validity of the input field structure
[943]51if ~ (exist('action','var') && strcmp(action,'keep_open'))
[404]52[errormsg,ListDimName,DimValue,VarDimIndex]=check_field_structure(Data);
53if ~isempty(errormsg)
54    errormsg=['error in struct2nc:invalid input structure_' errormsg];
55    return
56end
[943]57end
[404]58ListVarName=Data.ListVarName;
[919]59
60%% create the netcdf file with name flname in format NETCDF4
[943]61% if ischar(flname)
[930]62    FilePath=fileparts(flname);
63    if ~strcmp(FilePath,'') && ~exist(FilePath,'dir')
64        errormsg=['directory ' FilePath ' needs to be created'];
65        return
66    end
67    cmode = netcdf.getConstant('NETCDF4');
68    cmode = bitor(cmode, netcdf.getConstant('CLASSIC_MODEL'));
69    cmode = bitor(cmode, netcdf.getConstant('CLOBBER'));
70    nc = netcdf.create(flname, cmode);
[943]71% else
72%     nc=flname;
73% end
[913]74
[919]75%% write global constants
[404]76if isfield(Data,'ListGlobalAttribute')
77    keys=Data.ListGlobalAttribute;
78    for iattr=1:length(keys)
79        if isfield(Data,keys{iattr})
80             testvar=0;
81            for ivar=1:length(ListVarName)% eliminate possible global attributes with the same name as a variable
82                if isequal(ListVarName{ivar}, keys{iattr})
83                    testvar=1;
84                    break
[106]85                end
[404]86            end
87            if ~testvar               
88                eval(['cte=Data.' keys{iattr} ';'])
89                if (ischar(cte) ||isnumeric(cte)) &&  ~isempty(cte)%&& ~isequal(cte,'')
90                    %write constant only if it is numeric or char string, and not empty
91                    netcdf.putAtt(nc,netcdf.getConstant('NC_GLOBAL'),keys{iattr},cte)
[106]92                end
93            end
94        end
95    end
[404]96end
[919]97
98%% create the dimensions
[404]99dimid=zeros(1,length(ListDimName));
100for idim=1:length(ListDimName)
101     dimid(idim) = netcdf.defDim(nc,ListDimName{idim},DimValue(idim));
102end
103VarAttribute={}; %default
104testattr=0;
105if isfield(Data,'VarAttribute')
106    VarAttribute=Data.VarAttribute;
107    testattr=1;
108end
[919]109
110
111%% create the variables
112varid=nan(1,length(Data.ListVarName));
[404]113for ivar=1:length(ListVarName)
[919]114    if isfield(Data,ListVarName{ivar})
[943]115        VarClass{ivar}=class(Data.(ListVarName{ivar}));
[919]116        VarType='';
[943]117        switch VarClass{ivar}
[919]118            case {'single','double'}
119                VarType='nc_float'; % store all floating reals as single
[944]120            case {'int8','uint8','int16','uint16','int32','uint32','int64','uint64'}
[919]121                VarType='nc_int';
[988]122            case 'logical'
[1033]123                VarType='nc_int';
124                Data.(ListVarName{ivar})=uint8(Data.(ListVarName{ivar}));
[919]125        end
126        if ~isempty(VarType)
127            varid(ivar)=netcdf.defVar(nc,ListVarName{ivar},VarType,dimid(VarDimIndex{ivar}));%define variable
128        end
129    end
[404]130end
[919]131
132%% write variable attributes
[404]133if testattr
134    for ivar=1:min(numel(VarAttribute),numel(ListVarName)) 
[919]135        if isstruct(VarAttribute{ivar}) && ~isnan(varid(ivar))
[404]136            attr_names=fields(VarAttribute{ivar});
137            for iattr=1:length(attr_names)
[526]138                attr_val=VarAttribute{ivar}.(attr_names{iattr});
139                if ~isempty(attr_names{iattr})&& ~isempty(attr_val)&&~iscell(attr_val)
[404]140                    netcdf.putAtt(nc,varid(ivar),attr_names{iattr},attr_val);
[106]141                end
142            end
143        end
144    end
[404]145end
146netcdf.endDef(nc); %put in data mode
[919]147
148%% fill the variables with input data
[943]149if ~(exist('action','var') && strcmp(action,'keep_open'))
150    for ivar=1:length(ListVarName)
151        if ~isnan(varid(ivar))
152            VarVal=Data.(ListVarName{ivar});
153            if strcmp(VarClass{ivar},'single')
154                VarVal=single(VarVal);
155            end
156            %varval=values of the current variable
157            VarDimName=Data.VarDimName{ivar};
158            if ischar(VarDimName)
159                VarDimName={VarDimName};
160            end
161            siz=size(VarVal);
162            testrange=(numel(VarDimName)==1 && strcmp(VarDimName{1},ListVarName{ivar}) && numel(VarVal)==2);% case of a coordinate defined on a regular mesh by the first and last values.
163            testline=isequal(length(siz),2) && isequal(siz(1),1)&& isequal(siz(2), DimValue(VarDimIndex{ivar}));%matlab vector
164            %testcolumn=isequal(length(siz),2) && isequal(siz(1), DimValue(VarDimIndex{ivar}))&& isequal(siz(2),1);%matlab column vector
165            if testline || testrange
166                if testrange
167                    VarVal=linspace(VarVal(1),VarVal(2),DimValue(VarDimIndex{ivar}));% restitute the whole array of coordinate values from the first and last values
168                end
169                netcdf.putVar(nc,varid(ivar), VarVal');
170            else
171                netcdf.putVar(nc,varid(ivar), VarVal);
172            end
[404]173        end
[106]174    end
175end
[943]176 netcdf.close(nc)
[938]177[success,errormsg] = fileattrib(flname ,'+w');% allow writing access for the group of users
[530]178%'check_field_structure': check the validity of the field struture representation consistant with the netcdf format
179%------------------------------------------------------------------------
[674]180% [errormsg,ListDimName,DimValue,VarDimIndex]=check_field_structure(Data)
[530]181%
182% OUTPUT:
183% errormsg: error message which is not empty when the input structure does not have the right form
[674]184% ListDimName: list of dimension names (cell array of cahr strings)
185% DimValue: list of dimension values (numerical array with the same dimension as ListDimName)
186% VarDimIndex: cell array of dimension index (in the list ListDimName) for each element of Data.ListVarName
[530]187%
188% INPUT:
189% Data:   structure containing
190%         (optional) .ListGlobalAttribute: cell listing the names of the global attributes
191%                    .Att_1,Att_2... : values of the global attributes
192%         (requested)  .ListVarName: list of variable names to select (cell array of  char strings {'VarName1', 'VarName2',...} )
193%         (requested)  .VarDimName: list of dimension names for each element of .ListVarName (cell array of string cells)                         
194%         (requested) .Var1, .Var2....: variables (Matlab arrays) with names listed in .ListVarName
195
196
197function [errormsg,ListDimName,DimValue,VarDimIndex]=check_field_structure(Data)
198errormsg='';
199ListDimName={};
200DimValue=[]; %default
201VarDimIndex={};
202if ~isstruct(Data)
203    errormsg='input field is not a structure';
204    return
205end
206if isfield(Data,'ListVarName') && iscell(Data.ListVarName)
207    nbfield=numel(Data.ListVarName);
208else
209    errormsg='input field does not contain the cell array of variable names .ListVarName';
210    return
211end
212%check dimension names
213if (isfield(Data,'VarDimName') && iscell(Data.VarDimName))
214    if  numel(Data.VarDimName)~=nbfield
215       errormsg=' .ListVarName and .VarDimName have different lengths';
216        return
217    end
218else
219    errormsg='input field does not contain the  cell array of dimension names .VarDimName';
220    return
221end
222nbdim=0;
223ListDimName={};
224
225%% main loop on the list of variables
[674]226VarDimIndex=cell(1,nbfield);
[530]227for ivar=1:nbfield
228    VarName=Data.ListVarName{ivar};
229    if ~isfield(Data,VarName)
230        errormsg=['the listed variable ' VarName ' is not found'];
231        return
232    end
233    sizvar=size(Data.(VarName));% sizvar = dimension of variable
234    DimCell=Data.VarDimName{ivar};
235    if ischar(DimCell)
236        DimCell={DimCell};%case of a single dimension name, defined by a string
237    elseif ~iscell(DimCell)
238        errormsg=['wrong format for .VarDimName{' num2str(ivar) ' (must be the cell of dimension names of the variable ' VarName];
[674]239        return       
[530]240    end
241    nbcoord=numel(sizvar);%nbre of coordinates for variable named VarName
242    testrange=0;
243    if numel(DimCell)==0
244        errormsg=['empty declared dimension .VarDimName{' num2str(ivar) '} for ' VarName];
245        return
246    elseif numel(DimCell)==1% one dimension declared
247        if nbcoord==2
248            if sizvar(1)==1
249                sizvar(1)=sizvar(2);
250            elseif sizvar(2)==1
251            else
252                errormsg=['1 dimension declared in .VarDimName{' num2str(ivar) '} inconsistent with the nbre of dimensions =2 of the variable ' VarName];
253                return
254            end
255            if sizvar(1)==2 && isequal(VarName,DimCell{1})
256                testrange=1;% test for a dimension variable representing a range
257            end
258        else
259            errormsg=['1 dimension declared in .VarDimName{' num2str(ivar) '} inconsistent with the nbre of dimensions =' num2str(nbcoord) ' of the variable ' VarName];
260            return
261        end
262    else
263        if numel(DimCell)>nbcoord
264            sizvar(nbcoord+1:numel(DimCell))=1;% case of singleton dimensions (not seen by the function size)
265        elseif nbcoord > numel(DimCell)
266            errormsg=['nbre of declared dimensions in .VarDimName{' num2str(ivar) '} smaller than the nbre of dimensions =' num2str(nbcoord) ' of the variable ' VarName];
267            return
268        end
269    end
270    DimIndex=[];
271    for idim=1:numel(DimCell) %loop on the coordinates of variable #ivar
272        DimName=DimCell{idim};
273        iprev=find(strcmp(DimName,ListDimName),1);%look for dimension name DimName in the current list
274        if isempty(iprev)% append the dimension name to the current list
275            nbdim=nbdim+1;
276            RangeTest(nbdim)=0; %default
277            if sizvar(idim)==2 && strcmp(DimName,VarName)%case of a coordinate defined by the two end values (regular spacing)
278                RangeTest(nbdim)=1; %to be updated for a later variable 
279            end
280            DimValue(nbdim)=sizvar(idim);
281            ListDimName{nbdim}=DimName;
282            DimIndex=[DimIndex nbdim];
283        else % DimName is detected in the current list of dimension names
284            if ~isequal(DimValue(iprev),sizvar(idim))
285                if isequal(DimValue(iprev),2)&& RangeTest(iprev)  % the dimension has been already detected as a range [min max]
286                    DimValue(iprev)=sizvar(idim); %update with actual value
287                elseif ~testrange               
288                    errormsg=['dimension declaration inconsistent with the size =[' num2str(sizvar) '] for ' VarName];
289                    return
290                end
291            end
292            DimIndex=[DimIndex iprev];
293        end
294    end
295    VarDimIndex{ivar}=DimIndex;
[801]296end
Note: See TracBrowser for help on using the repository browser.