1 | function nccmp(ncfile1,ncfile2,varargin) |
---|
2 | %NCCMP compares two NetCDF files and prints the differences. |
---|
3 | % This function is useful for testing mathematical forecasting or prediction models. |
---|
4 | % |
---|
5 | % USAGE: |
---|
6 | % NCCMP(ncfile1,ncfile2) |
---|
7 | % NCCMP(ncfile1,ncfile2,tolerance,forceCompare) |
---|
8 | % |
---|
9 | % INPUT: |
---|
10 | % ncfile1 - name of the NetCDF file to compare |
---|
11 | % ncfile2 - name of the NetCDF file to compare |
---|
12 | % tolerance - compare numeric data using a tolerance threshold |
---|
13 | % forceCompare - if false, exit when first difference is found |
---|
14 | % true: coninues to process all variables |
---|
15 | % OUTPUT: |
---|
16 | % |
---|
17 | % EXAMPLES: |
---|
18 | % nccmp('old.nc','new.nc',0.000001) |
---|
19 | % nccmp('old.nc','new.nc',[],true) |
---|
20 | % ... |
---|
21 | % FEATURES: |
---|
22 | % 1. Print the differences and their locations |
---|
23 | % 2. Exits when first difference is found or optionally continues to process all variables |
---|
24 | % 3. User defined tolerance threshold to compare the variables |
---|
25 | % TO DO: |
---|
26 | % 1. Specific variable inclusion or exclusion |
---|
27 | % 2. Specific attribute inclusion or exclusion |
---|
28 | % 3. Option to ignore the history attribute |
---|
29 | % 4. Ignore difference between values that have different missing values etc |
---|
30 | % See also: |
---|
31 | % |
---|
32 | % HISTORY: |
---|
33 | % 1.0.0 (21-07-2014): Initial release |
---|
34 | % 1.1.0 (21-07-2014): Option to forceCompare true or false |
---|
35 | % 1.2.0 (16-09-2014): Input checking and clean up |
---|
36 | % 1.3.0 (16-09-2014): Adding internal function IND2SUB1 to return single |
---|
37 | % variable IJ rather than returning I,J (length(siz)) |
---|
38 | % Author: Durga Lal Shrestha |
---|
39 | % CSIRO Land & Water, Highett, Australia |
---|
40 | % eMail: durgalal.shrestha@gmail.com |
---|
41 | % Website: www.durgalal.co.cc |
---|
42 | % Copyright 2014 Durga Lal Shrestha |
---|
43 | % $First created: 21-Jul-2014 |
---|
44 | % $Revision: 1.3.0 $ $Date: 16-Sep-2014 10:06:03 $ |
---|
45 | % *********************************************************************** |
---|
46 | %% INPUT ARGUMENTS CHECK |
---|
47 | narginchk(2, 4) |
---|
48 | tolerance = 0; |
---|
49 | filesAreIdentical = true; |
---|
50 | forceCompare = false; |
---|
51 | if nargin>2 && ~isempty(varargin{1}) |
---|
52 | tolerance = varargin{1}; |
---|
53 | if ~isscalar(tolerance) |
---|
54 | error('NCCMP:WrongDataType','Tolerance input should be scalar') |
---|
55 | end |
---|
56 | end |
---|
57 | if nargin>3 |
---|
58 | forceCompare = varargin{2}; |
---|
59 | if ~isscalar(forceCompare) |
---|
60 | error('NCCMP:WrongDataType','Fourth argumnet should be boolean') |
---|
61 | end |
---|
62 | end |
---|
63 | % Check if files exist |
---|
64 | if exist(ncfile1,'file')~=2 && exist(ncfile2,'file')~=2 |
---|
65 | error('NCCMP:FileNotFound','Input files %s and %s do not exist...',ncfile1,ncfile2) |
---|
66 | end |
---|
67 | if exist(ncfile1,'file')~=2 |
---|
68 | error('NCCMP:FileNotFound','Input file %s does not exist...',ncfile1) |
---|
69 | end |
---|
70 | if exist(ncfile2,'file')~=2 |
---|
71 | error('NCCMP:FileNotFound','Input file %s does not exist...',ncfile2) |
---|
72 | end |
---|
73 | fprintf('COMPARING netcdf files: %s <> %s\n',ncfile1,ncfile2) |
---|
74 | fprintf('...With tolerance: %1.1E\n',tolerance) |
---|
75 | fprintf('\n') |
---|
76 | ncid1 = netcdf.open(ncfile1,'NC_NOWRITE'); |
---|
77 | ncid2 = netcdf.open(ncfile2,'NC_NOWRITE'); |
---|
78 | % Check if output files exit, if yes delete it. Otherwise we can end up |
---|
79 | % with huge files, in particular the savefile_all |
---|
80 | if forceCompare |
---|
81 | if exist('all_diff.txt') |
---|
82 | fprintf('The all_diff.txt exist, we delete it before we run the script \n') |
---|
83 | delete('all_diff.txt') |
---|
84 | end |
---|
85 | else |
---|
86 | if exist('first_diff.txt') |
---|
87 | fprintf('The first_diff.txt exist, we delete it before we run the script \n') |
---|
88 | delete('first_diff.txt') |
---|
89 | end |
---|
90 | end |
---|
91 | %% % Get information about the contents of the file. |
---|
92 | [numdims1, numvars1, numglobalatts1, unlimdimID1] = netcdf.inq(ncid1); |
---|
93 | [numdims2, numvars2, numglobalatts2, unlimdimID2] = netcdf.inq(ncid2); |
---|
94 | %ncfile2(ncid1) |
---|
95 | %ncfile2(ncid2) |
---|
96 | %% Get Global attributes Information |
---|
97 | if numglobalatts1~=numglobalatts2 |
---|
98 | fprintf('DIFFER: NUMBER OF GLOBAL ATTRIBUTES : %d <> %d\n',numglobalatts1,numglobalatts2) |
---|
99 | filesAreIdentical = false; |
---|
100 | end |
---|
101 | finfo1 = ncinfo(ncfile1); |
---|
102 | finfo2 = ncinfo(ncfile2); |
---|
103 | attrName1 = {finfo1.Attributes.Name}; |
---|
104 | attrName2 = {finfo2.Attributes.Name}; |
---|
105 | % Check common attribute names |
---|
106 | comName = intersect(attrName1,attrName2); |
---|
107 | % Check value of common attributes |
---|
108 | for i=1:length(comName) |
---|
109 | attvalue1 = ncreadatt(ncfile1,'/',comName{i}); |
---|
110 | attvalue2 = ncreadatt(ncfile2,'/',comName{i}); |
---|
111 | if ischar(attvalue1) && ischar(attvalue1) |
---|
112 | if ~strcmp(attvalue1,attvalue2) |
---|
113 | fprintf('DIFFER: LENGTH OF GLOBAL ATTRIBUTE: %s : %d <> %d : VALUES :%s <> %s\n' ,comName{i},length(attvalue1),length(attvalue2),attvalue1,attvalue2) |
---|
114 | filesAreIdentical = false; |
---|
115 | end |
---|
116 | else |
---|
117 | if ~isequal(attvalue1,attvalue2) |
---|
118 | fprintf('DIFFER: LENGTH OF GLOBAL ATTRIBUTE: %s : %d <> %d : VALUES :%12.5f <> %12.5f\n' ,comName{i},length(attvalue1),length(attvalue2),attvalue1,attvalue2) |
---|
119 | filesAreIdentical = false; |
---|
120 | end |
---|
121 | end |
---|
122 | end |
---|
123 | % Check name of attributes which exist in ncfile1 but not in ncfile2 |
---|
124 | attrName1Only = setdiff(attrName1,attrName2); |
---|
125 | for i=1:length(attrName1Only) |
---|
126 | fprintf('DIFFER: NAME OF GLOBAL ATTRIBUTE: %s : GLOBAL ATTRIBUTE DOES NOT EXIST in %s\n' ,attrName1Only{i},ncfile2) |
---|
127 | end |
---|
128 | % Check name of attributes which exist in ncfile2 but not in ncfile1 |
---|
129 | attrName2Only = setdiff(attrName2,attrName1); |
---|
130 | for i=1:length(attrName2Only) |
---|
131 | fprintf('DIFFER: NAME OF GLOBAL ATTRIBUTE: %s : GLOBAL ATTRIBUTE DOES NOT EXIST in %s\n' ,attrName2Only{i},ncfile1) |
---|
132 | end |
---|
133 | fprintf('\n') |
---|
134 | %% Get Dimension Information |
---|
135 | if numdims1~=numdims2 |
---|
136 | fprintf('DIFFER: NUMBER OF DIMENSION IN FILES: %d <> %d\n',numdims1,numdims2) |
---|
137 | filesAreIdentical = false; |
---|
138 | end |
---|
139 | % if ~isequal(finfo1.Dimensions,finfo2.Dimensions) |
---|
140 | % fprintf('Dimesnions are not same\n') |
---|
141 | % end |
---|
142 | dimName1 = {finfo1.Dimensions.Name}; |
---|
143 | dimName2 = {finfo2.Dimensions.Name}; |
---|
144 | % Check common attribute names |
---|
145 | comName = intersect(dimName1,dimName2); |
---|
146 | % Check value of common attributes |
---|
147 | for i=1:length(comName) |
---|
148 | dimid1 = netcdf.inqDimID(ncid1,comName{i}); |
---|
149 | dimid2 = netcdf.inqDimID(ncid2,comName{i}); |
---|
150 | [dimname1, dimlen1] = netcdf.inqDim(ncid1,dimid1); |
---|
151 | [dimname2, dimlen2] = netcdf.inqDim(ncid2,dimid2); |
---|
152 | if dimlen1 ~= dimlen2 |
---|
153 | fprintf('DIFFER: LENGTH OF DIMENSION: %s : %d <> %d\n' ,comName{i},dimlen1,dimlen2) |
---|
154 | end |
---|
155 | end |
---|
156 | netcdf.close(ncid1); |
---|
157 | netcdf.close(ncid2); |
---|
158 | % Check name of attributes which exist in ncfile1 but not in ncfile2 |
---|
159 | dimName1Only = setdiff(dimName1,dimName2); |
---|
160 | for i=1:length(dimName1Only) |
---|
161 | fprintf('DIFFER: NAME OF DIMENSION: %s : DIMENSION DOES NOT EXIST in %s\n' ,dimName1Only{i},ncfile2) |
---|
162 | end |
---|
163 | % Check name of attributes which exist in ncfile2 but not in ncfile1 |
---|
164 | dimName2Only = setdiff(dimName2,dimName1); |
---|
165 | for i=1:length(dimName2Only) |
---|
166 | fprintf('DIFFER: NAME OF DIMENSION: %s : DIMESNION DOES NOT EXIST in %s\n' ,dimName2Only{i},ncfile1) |
---|
167 | end |
---|
168 | fprintf('\n') |
---|
169 | %% Get Variable Information |
---|
170 | if numvars1~=numvars2 |
---|
171 | fprintf('DIFFER: NUMBER OF VARIABLE IN FILES: %d <> %d\n',numvars1,numvars2) |
---|
172 | filesAreIdentical = false; |
---|
173 | end |
---|
174 | % if ~isequal(finfo1.Variables,finfo2.Variables) |
---|
175 | % fprintf('Variables are not same\n') |
---|
176 | % end |
---|
177 | varName1 = {finfo1.Variables.Name}; |
---|
178 | varName2 = {finfo2.Variables.Name}; |
---|
179 | % Check common attribute names |
---|
180 | [comName,ia,ib] = intersect(varName1,varName2); |
---|
181 | % Check value of common attributes |
---|
182 | for i=1:length(comName) |
---|
183 | % dimensions1 = finfo1.Variables(ia(i)).Dimensions; |
---|
184 | % dimensions2 = finfo2.Variables(ib(i)).Dimensions; |
---|
185 | % if ~isequal(dimensions1,dimensions2) |
---|
186 | % %fprintf('DIFFER: DIMENSIONS: %s : %d <> %d\n' ,comName{i},dimensions1,dimensions1) |
---|
187 | % fprintf('TO DO: CHECK dimension Name and Length\n') |
---|
188 | % end |
---|
189 | size1 = finfo1.Variables(ia(i)).Size; |
---|
190 | size2 = finfo2.Variables(ib(i)).Size; |
---|
191 | if ~isequal(size1,size2) |
---|
192 | fprintf('DIFFER: SIZE, VARIABLE: %s : %s <> %s\n' ,comName{i},num2str(size1),num2str(size2)) |
---|
193 | else |
---|
194 | DataType1 = finfo1.Variables(ia(i)).Datatype; |
---|
195 | DataType2 = finfo2.Variables(ib(i)).Datatype; |
---|
196 | if ~strcmp(DataType1,DataType2) |
---|
197 | fprintf('DIFFER: TYPES, VARIABLE: %s : %s <> %s\n' ,comName{i},DataType1,DataType2) |
---|
198 | end |
---|
199 | data1=ncread(ncfile1,comName{i}); |
---|
200 | data2=ncread(ncfile2,comName{i}); |
---|
201 | if ~ischar(data1) && ~ischar(data2) |
---|
202 | dff = abs(double(data1)-double(data2)); |
---|
203 | % [row,col] = find(dff > tolerance); |
---|
204 | ind = find(dff > tolerance); |
---|
205 | if ~isempty(ind) |
---|
206 | filesAreIdentical = false; |
---|
207 | if forceCompare |
---|
208 | for j=1:length(ind) |
---|
209 | %[I1,I2,I3] = ind2sub(size1,ind(j)); |
---|
210 | X = ind2sub1(size1,ind(j)); |
---|
211 | all_diff{j} = sprintf('DIFFER: VARIABLE, %s : POSITION %s: VALUES: %12.20G <> %12.20G \n' ,comName{i},num2str(X),data1(ind(j)),data2(ind(j))); |
---|
212 | dlmwrite('all_diff.txt',all_diff{j},'-append','delimiter','') |
---|
213 | end |
---|
214 | else |
---|
215 | % Just report the first occurrence |
---|
216 | first_diff = sprintf('DIFFER: VARIABLE, %s : NUMBER OF LOCATION %d, Reporting the first location only...\n' ,comName{i},length(ind)); |
---|
217 | dlmwrite('first_diff.txt',first_diff,'-append','delimiter','') |
---|
218 | X = ind2sub1(size1,ind(1)); |
---|
219 | first_diff = sprintf('DIFFER: VARIABLE, %s : POSITION %s: VALUES: %12.20G <> %12.20G \n' ,comName{i},num2str(X),data1(ind(1)),data2(ind(1))); |
---|
220 | dlmwrite('first_diff.txt',first_diff,'-append','delimiter','') |
---|
221 | end |
---|
222 | end |
---|
223 | else |
---|
224 | % character data |
---|
225 | ind = find(data1~=data2); |
---|
226 | if ~isempty(ind) |
---|
227 | filesAreIdentical = false; |
---|
228 | if forceCompare |
---|
229 | for j=1:length(ind) |
---|
230 | %[I1,I2,I3] = ind2sub(size1,ind(j)); |
---|
231 | X = ind2sub1(size1,ind(j)); |
---|
232 | savefile = fprintf('DIFFER: VARIABLE, %s : POSITION %s: VALUES: %12.20G <> %12.20G \n' ,comName{i},num2str(X),data1(ind(j)),data2(ind(j))); |
---|
233 | dlmwrite('Savefile.txt',savefile) |
---|
234 | end |
---|
235 | else |
---|
236 | % Just report the first occurance |
---|
237 | fprintf('DIFFER: VARIABLE, %s : NUMBER OF LOCATION %d, Reporting the first location only...\n' ,comName{i},length(ind)) |
---|
238 | X = ind2sub1(size1,ind(1)); |
---|
239 | fprintf('DIFFER: VARIABLE, %s : POSITION %s: VALUES: %12.20G <> %12.20G \n' ,comName{i},num2str(X),data1(ind(1)),data2(ind(1))) |
---|
240 | end |
---|
241 | end |
---|
242 | end |
---|
243 | end |
---|
244 | att = ''; |
---|
245 | att = ''; |
---|
246 | if isempty(finfo1.Variables(ia(i)).Attributes) |
---|
247 | fname = ncfile1; |
---|
248 | elseif isempty(finfo2.Variables(ib(i)).Attributes) |
---|
249 | fname = ncfile2; |
---|
250 | end |
---|
251 | |
---|
252 | if isempty(finfo1.Variables(ia(i)).Attributes) || isempty(finfo2.Variables(ib(i)).Attributes) |
---|
253 | if ~(isempty(finfo1.Variables(ia(i)).Attributes) && isempty(finfo2.Variables(ib(i)).Attributes)) |
---|
254 | fprintf('DIFFER: ATTRIBUTE: VARIABLE: %s : No attributes in %s\n' ,finfo1.Variables(ia(i)).Name,fname) |
---|
255 | end |
---|
256 | else |
---|
257 | att1 = {finfo1.Variables(ia(i)).Attributes.Name}; |
---|
258 | att2 = {finfo2.Variables(ib(i)).Attributes.Name}; |
---|
259 | if length(att1)~=length(att2) |
---|
260 | fprintf('DIFFER: NUMBER OF ATTRIBUTE: VARIABLE: %s : %d <> %d\n' ,finfo1.Variables(ia(i)).Name,length(att1),length(att2)) |
---|
261 | end |
---|
262 | |
---|
263 | [comAtt,ind1,ind2] = intersect(att1,att2); |
---|
264 | % Check if values are same in common attributes |
---|
265 | for j=1:length(comAtt) |
---|
266 | attvalue1 = ''; |
---|
267 | attvalue2 = ''; |
---|
268 | if isfield(finfo1.Variables(ia(i)),'Attributes') |
---|
269 | Att1= finfo1.Variables(ia(i)).Attributes; |
---|
270 | attvalue1 = Att1(ind1(j)).Value; |
---|
271 | end |
---|
272 | if isfield(finfo2.Variables(ib(i)),'Attributes') |
---|
273 | Att2= finfo2.Variables(ib(i)).Attributes; |
---|
274 | attvalue2 = Att2(ind2(j)).Value; |
---|
275 | end |
---|
276 | if ischar(attvalue1) && ischar(attvalue2) |
---|
277 | if ~strcmp(attvalue1,attvalue2) |
---|
278 | fprintf('DIFFER: LENGTH, ATTRIBUTE: %s, VARIABLE: %s :%d <> %d , VALUES: %s <> %s\n' ,comAtt{j},comName{i},length(attvalue1),length(attvalue2),attvalue1,attvalue2) |
---|
279 | filesAreIdentical = false; |
---|
280 | end |
---|
281 | else |
---|
282 | if ~isequal(attvalue1,attvalue2) |
---|
283 | fprintf('DIFFER: LENGTH, ATTRIBUTE: %s, VARIABLE : %s :%d <> %d : VALUES: %12.5f <> %12.5f\n' ,comAtt{j},comName{i},length(attvalue1),length(attvalue2),attvalue1,attvalue2) |
---|
284 | filesAreIdentical = false; |
---|
285 | end |
---|
286 | end |
---|
287 | end |
---|
288 | |
---|
289 | % Check name of attributes which exist in ncfile1 but not in ncfile2 |
---|
290 | attrName1Only = setdiff(att1,att2); |
---|
291 | for j=1:length(attrName1Only) |
---|
292 | fprintf('DIFFER: NAME OF ATTRIBUTE: %s : ATTRIBUTE DOES NOT EXIST in VARIABLE: %s in %s\n' ,attrName1Only{j},comName{i},ncfile2) |
---|
293 | end |
---|
294 | |
---|
295 | % Check name of attributes which exist in ncfile2 but not in ncfile1 |
---|
296 | attrName2Only = setdiff(att1,att2); |
---|
297 | for j=1:length(attrName2Only) |
---|
298 | fprintf('DIFFER: NAME OF ATTRIBUTE: %s : ATTRIBUTE DOES NOT EXIST in VARIABLE: %s in %s\n' ,attrName2Only{j},comName{i},ncfile1) |
---|
299 | end |
---|
300 | end |
---|
301 | end |
---|
302 | % Check name of attributes which exist in ncfile1 but not in ncfile2 |
---|
303 | varName1Only = setdiff(varName1,varName2); |
---|
304 | for i=1:length(varName1Only) |
---|
305 | fprintf('DIFFER: NAME OF VARIABLE: %s : VARIABLE DOES NOT EXIST in %s\n' ,varName1Only{i},ncfile2) |
---|
306 | end |
---|
307 | % Check name of attributes which exist in ncfile2 but not in ncfile1 |
---|
308 | varName2Only = setdiff(varName2,varName1); |
---|
309 | for i=1:length(varName2Only) |
---|
310 | fprintf('DIFFER: NAME OF VARIABLE: %s : VARIABLE DOES NOT EXIST in %s\n' ,varName2Only{i},ncfile1) |
---|
311 | end |
---|
312 | fprintf('\n') |
---|
313 | %% |
---|
314 | if filesAreIdentical |
---|
315 | % fprintf('FILES: %s and %s are identical\n' ,ncfile1,ncfile2) |
---|
316 | fprintf('FILES are identical\n') |
---|
317 | end |
---|
318 | %% Internal function ind2sub1 |
---|
319 | function IJ = ind2sub1(siz,IND) |
---|
320 | % IND2SUB1 returns single variable IJ rather than returning I,J (length(siz)) |
---|
321 | % variables |
---|
322 | if ~isvector(IND) |
---|
323 | error('IND2SUB1:WrongInputFormat','ind2sub1 only works with scalar and vector') |
---|
324 | end |
---|
325 | IND = IND(:); |
---|
326 | [out{1:length(siz)}] = ind2sub(siz,IND); |
---|
327 | IJ = cell2mat(out); |
---|