source: trunk/src/struct2nc.m @ 644

Last change on this file since 644 was 530, checked in by sommeria, 9 years ago

new conventions for find_field_cells .

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