source: trunk/src/struct2nc.m @ 919

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

netcdf files stored in 32 bits (not double)

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