How to create a clean netcdf file from Matlab ?
Post date: Nov 12, 2009 10:53:19 AM
Update: For those using Matlab version 2010b and higher a netcdf set of routines is now incorporated in the distribution. The syntax is different and the following script is then rather outdated. However it is still valid for older Matlab versions using their locally compiled netcdf package.
IMPORTANT:
This is a Matlab script showing how to create a clean netcdf file.
Create dummy data to put into the netcdf file
% Define axis, for example: X = 0:2:360; % Longitude Y = -90:2:90; % Latitude Z = -5000:100:0; % Depth T = datenum(1958:2006,1,1,0,0,0); % Time % Create a random field to record in the cdf file dimensions are (Z,Y,X): C = rand(length(Z),length(Y),length(X));
File standard set-up
Open file and insert dimensions
I exclude the temporal axis for now.
nc = netcdf('myfirstcdffile.nc','clobber'); % Define dimensions: ncdim('depth',length(Z),nc); ncdim('latitude',length(Y),nc); ncdim('longitude',length(X),nc); % Define axis: ncvar('depth','float',{'depth'},nc); ncatt('units','char','m',nc{'depth'}); ncatt('axis','char','Z',nc{'depth'}); nc{'depth'}(:) = Z(:)'; ncvar('latitude','float',{'latitude'},nc); ncatt('units','char','degrees_north',nc{'latitude'}); ncatt('axis','char','Y',nc{'latitude'}); nc{'latitude'}(:) = Y'; ncvar('longitude','float',{'longitude'},nc); ncatt('units','char','degrees_east',nc{'longitude'}); ncatt('axis','char','X',nc{'longitude'}); nc{'longitude'}(:) = X';
Insert global attributes
These are just my stuff, you need to customize. But note attributes related to the convention. This is very useful for you netcdf file to read by anybody else than you.
nc.title = 'My first netcdf file'; nc.long_title = 'Self explanatory, isnt''it ?'; nc.comments = 'All variables are just noise !'; nc.institution = 'LPO - LMD'; nc.source = 'my_model'; nc.Conventions = 'CF-1.4'; % This way I can always get back to fundamentals: nc.Conventions_help = 'http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4/cf-conventions.html'; nc.CreationDate = datestr(now,'yyyy/mm/dd HH:MM:SS'); nc.CreatedBy = getenv('LOGNAME'); % Next attribute will be very usefull in 1 or 2 years when you'll open the netcdf file: nc.MatlabSource = which('your_matlab_script_generating_this_file');
Check what we’ve just done
We can now close the netcdf file (optional):
>> close(nc)
and go in a terminal to look at it with:
ncdump -h myfirstcdffile.nc
or from Matlab:
>> system('ncdump -h myfirstcdffile.nc | more');
Insert a 3D field
If the file was closed, we need to reopen it:
nc = netcdf('myfirstcdffile.nc','write'); % Rq: here we use 'write' because the file already exists
The variable we want to record is in the Matlab field C but we have to give it a name and define metadata for the netcdf file:
varname = 'my_first_field'; long_name = 'A long description of what this is, well simply noise !'; unit = 'm/s'; % for example of course
And now we can write it:
ncvar(varname,'float',{'depth','latitude','longitude'},nc); % we need to define axis of the field ncatt('long_name','char',long_name,nc{varname}); % Give it the long_name ncatt('units','char',unit,nc{varname}); % The unit ncatt('FillValue_','float',-9999.99,nc{varname}); % Missing var fill value C(isnan(C)) = -9999.99; nc{varname}(:,:,:) = C;
We can now close the netcdf file (optional):
close(nc)
and go to a terminal to look at it with:
ncdump -h myfirstcdffile.nc
or from Matlab:
system('ncdump -h myfirstcdffile.nc | more');
and see the new variable we just added !
Insert a 2D field
If the file was closed, we need to reopen it:
nc = netcdf('myfirstcdffile.nc','write');
Let’s say we want to write only one level iz of C(Z,Y,X):
iz = 1; % Define the level varname2 = sprintf('%s_level%i',varname,iz); long_name = 'Still a long description of what this is, well simply noise !'; unit = 'm/s';
And now we write it:
ncvar(varname2,'float',{'latitude','longitude'},nc); % Note that here we define only horizontal axis ncatt('long_name','char',long_name,nc{varname2}); ncatt('units','char',unit,nc{varname2}); ncatt('FillValue_','float',-9999.99,nc{varname2}); C(isnan(C)) = -9999.99; nc{varname2}(:,:) = squeeze(C(iz,:,:));
Note that we can also add a comment to this variable:
ncatt('comments','char',sprintf('In fact, this is level #%i from variable: %s',iz,varname),nc{varname2});
We can now close the netcdf file (optional):
close(nc)
and go to a terminal to look at it with:
ncdump -h myfirstcdffile.nc
or from Matlab:
system('ncdump -h myfirstcdffile.nc | more');
Handling the time axis
You probably use the native Matlab format for your date/time information: datenum. But this is not how conventional netcdf handles time.
Insert the new dimension
Usually, I define my netcdf time axis as the days (can be fractional) since 1900–01–01. So let’s reopen the file:
nc = netcdf('myfirstcdffile.nc','write');
and add the new time axis. First, define the dimension:
ncdim('time',length(T),nc);
Then add the variable:
ncvar('time','float',{'time'},nc); ncatt('units','char','days since 1900-01-01 00:00:00',nc{'time'}); ncatt('axis','char','T',nc{'time'}); nc{'time'}(:) = T'-datenum(1900,1,1,0,0,0);
NOTE THAT THIS IS ONLY MY CHOICE, YOU NEED TO CUSTOMIZE WITH YOUR REQUIREMENTS
Insert a time varying field
Now we create a dummy variable, supposedly a timeserie of an horizontal 2D fields:
D = rand(length(T),length(Y),length(X));
And record it:
varname3 = 'my_time_serie'; long_name = 'This is a time serie of noise !'; unit = 'degC'; ncvar(varname3,'float',{'time','latitude','longitude'},nc); ncatt('long_name','char',long_name,nc{varname3}); ncatt('units','char',unit,nc{varname3}); ncatt('FillValue_','float',-9999.99,nc{varname3}); D(isnan(D)) = -9999.99; nc{varname3}(:,:,:) = D;
We can now close the netcdf file (optional):
close(nc)
and go to the prompt to look at it with:
ncdump -h myfirstcdffile.nc
or from Matlab:
system('ncdump -h myfirstcdffile.nc | more');