source: trunk/src/struct2nc.m @ 1162

Last change on this file since 1162 was 1157, checked in by sommeria, 4 months ago

filter_time added

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