NetCDF(3pm)

✨ NAME ✨

Hey bestie!! You just opened the NetCDF(3pm) page from User Contributed Perl Documentation. It's the PDL::NetCDF manual β€” the object-oriented interface between NetCDF files and PDL objects. πŸ¦‹

It's also a Perl extension that lets you interface to NetCDF portable binary gridded files via PDL objects. πŸ’…


🌸 SYNOPSIS 🌸

Okay so here's the whole vibe in code, no cap. πŸ‘‘

use PDL;
use PDL::NetCDF;
use PDL::Char;

my $ncobj = PDL::NetCDF->new ("test.nc", {REVERSE_DIMS => 1, PDL_BAD => 1});  # New file
my $pdl = pdl [[1, 2, 3], [4, 5, 6]];

# Specify variable name to put PDL in, plus names of the dimensions.  Dimension
# lengths are taken from the PDL, in this case, dim1 = 2 and dim2 = 3.
$ncobj->put ('var1', ['dim1', 'dim2'], $pdl);
# or for netcdf4 files
# $ncobj->put ('var1', ['dim1', 'dim2'], $pdl, {DEFLATE => 9, _FillValue => -999});

# get the deflate level (for any fileformat)
my ($deflate, $shuffle) = $ncobj->getDeflateShuffle('var1');

# $pdlout = [[1, 2, 3], [4, 5, 6]]
my $pdlout = $ncobj->get ('var1');

# Store textual NetCDF arrays using perl strings:  (This is a bit primitive, but works)
my $str = "Station1  Station2  Station3  ";
$obj->puttext('textvar', ['n_station', 'n_string'], [3,10], $str);
my $outstr = $obj->gettext('textvar');
# $outstr = "Station1  Station2  Station3  "

# Now textual NetCDF arrays can be stored with PDL::Char style PDLs.  This is much
# more natural and flexible than the above method.
$str = PDL::Char->new (['Station1', 'Station2', 'Station3']);
$obj->put ('stations', ['dim_station', 'dim_charlen'], $str);
$outstr = $obj->get('stations');
print $outstr;
# Prints: ['Station1', 'Station2', 'Station3']
# For more info on PDL::Char variables see PDL::Char(3), or perldoc PDL::Char

# $dim1size = 2
my $dim1size = $ncobj->dimsize('dim1');

# A slice of the netCDF variable.
# [0,0] is the starting point, [1,2] is the count.
# $slice = [1,2]
my $slice  = $ncobj->get ('var1', [0,0], [1,2]);

# Attach a double attribute of size 3 to var1
$ncobj->putatt (double([1,2,3]), 'double_attribute', 'var1');

# $attr1 = [1,2,3]
my $attr1 = $ncobj->getatt ('double_attribute', 'var1');

# $type = PDL::double
my $type = $ncobj->getvariabletype('var1');

# Write a textual, global attribute.  'attr_name' is the attribute name.
$ncobj->putatt ('The text of the global attribute', 'attr_name');

# $attr2 = 'The text of the global attribute'
my $attr2 = $ncobj->getatt ('attr_name');

# Close the netCDF file.  The file is also automatically closed in a DESTROY block
# when it passes out of scope.  This just makes is explicit.
$ncobj->close;

For (much) more information on NetCDF, see http://www.unidata.ucar.edu/packages/netcdf/index.html

Also see the test file, test.pl in this distribution for some working examples. 🫢


πŸ’– DESCRIPTION πŸ’–

This is the PDL interface to the Unidata NetCDF library. It uses the netCDF version 3 library to make a subset of netCDF functionality available to PDL users in a clean, object-oriented interface. ✨

Another NetCDF perl interface, which allows access to the entire range of netCDF functionality (but in a non-object-oriented style which uses perl arrays instead of PDLs) is available through Unidata at http://www.unidata.ucar.edu/packages/netcdf/index.html). πŸ™ˆ

The NetCDF standard allows N-dimensional binary data to be efficiently stored, annotated and exchanged between many platforms. 🌈

When one creates a new netCDF object, this object is associated with one netCDF file. πŸ’…


πŸ‘‘ FUNCTIONS πŸ‘‘

πŸ¦‹ isNetcdf4 πŸ¦‹

Check if compiled against netcdf4. 🧚

Arguments: none.

if (PDL::NetCDF::isNetcdf4) {
      # open netcdf4 file
}

🌈 defaultFormat 🌈

Get or change the default format when creating a netcdf-file. This can be overwritten by the NC_FORMAT option for new. Possible values are:

  • PDL::NetCDF::NC_FORMAT_CLASSIC
  • PDL::NetCDF::NC_FORMAT_64BIT
  • PDL::NetCDF::NC_FORMAT_NETCDF4
  • PDL::NetCDF::NC_FORMAT_NETCDF4_CLASSIC

Arguments:

  1. new format (constant)

Return:

old format as one of the NC_FORMAT_* constants

πŸ’… new πŸ’…

Create an object representing a netCDF file. πŸ‘‘

Arguments:

  1. The name of the file.
  2. optional: A hashref containing options. Currently defined are:
  • TEMPLATE β€” An existing netCDF object for a file with identical layout. This allows one to read in many similar netCDF files without incurring the overhead of reading in all variable and dimension names and IDs each time.
😭 Caution: Undefined weirdness may occur if you pass the netCDF object from a dissimilar file! 😭
  • MODE β€” use sysopen file-opening arguments, O_RDONLY, O_RDWR, O_CREAT, O_EXCL. When used, this will overwrite the '>file.nc' type of opening. See L<perlopentut> for usage of O_RDONLY...
  • REVERSE_DIMS β€” this will turn the order of the dimension-names of netcdf-files. Even with this option the 'put' function will write variables in FORTRAN order (as before) and will reverse the dimension names so they fit this order. With this option, the 'putslice' function will write variables in the same way as 'put'. You should use this option if your planning to work with other netcdf-programs (ncview, NCL) or if you are planning to combine putslice and slice. You should _not_ use this option, if you need compatibility to older versions of PDL::NetCDF.
  • NC_FORMAT β€” set the file format for a new netcdf file, see defaultFormat()
  • SLOW_CHAR_FETCH β€” If this option is set, then a 'get' into a PDL::Char will be done one string at a time instead of all text data at once. This is necessary if there are NULLs (hex 0) values embedded in the string arrays. This takes longer, but gives the correct results. If the fetch of a string array yields only the first element, try setting this option.
  • PDL_BAD β€” _FillValue's or missing_values are translated to bad-pdls.

Example:

my $nc = PDL::NetCDF->new ("file1.nc", {REVERSE_DIMS => 1, PDL_BAD => 1});
...
foreach my $ncfile (@a_bunch_of_similar_format_netcdf_files) {
  $nc = PDL::NetCDF->new("file2.nc", {TEMPLATE => $nc});  # These calls to 'new' are *much* faster
  ...
}

# opening using MODE
use Fcntl; # define O_CREAT...
# opening a completely new file (deleting if it exists!)
my $newnc = PDL::NetCDF->new ("file2.nc", {MODE => O_CREAT|O_RDWR,
                                           REVERSE_DIMS => 1, NC_FORMAT => PDL::NetCDF::NC_FORMAT_NETCDF4});
# opening existing file for reading and writing
$nc = PDL::NetCDF->new ("file2.nc", {MODE => O_RDWR}
                      REVERSE_DIMS => 1});
# opening existing file for reading only
$nc = PDL::NetCDF->new ("file2.nc", {MODE => O_RDONLY,
                                     REVERSE_DIMS => 1});

If this file exists and you want to write to it, prepend the name with the '>' character: ">name.nc"

Returns: The netCDF object. Barfs if there is an error. πŸ’€

🌟 getFormat 🌟

Get the format of a netcdf file. πŸ¦‹

Arguments: none

Returns: @ integer equal to one of the PDL::NetCDF::NC_FORMAT_* constants.

πŸ”₯ put πŸ”₯

Put a PDL matrix to a netCDF variable. πŸ’…

Arguments:

  1. The name of the variable to create
  2. A reference to a list of dimension names for this variable
  3. The PDL to put. It must have the same number of dimensions as specified in the dimension name list.
  4. Optional options hashref: {SHUFFLE => 1, DEFLATE => 7, COMPRESS => 0, _FillValue => -32767}

Returns: None.

my $pdl = pdl [[1, 2, 3], [4, 5, 6]];

# Specify variable name to put PDL in, plus names of the dimensions.  Dimension
# lengths are taken from the PDL, in this case, dim1 = 2 and dim2 = 3.
$ncobj->put ('var1', ['dim1', 'dim2'], $pdl);

# Now textual NetCDF arrays can be stored with PDL::Char style PDLs.
$str = PDL::Char->new (['Station1', 'Station2', 'Station3']);
$obj->put ('stations', ['dim_station', 'dim_charlen'], $str);
$outstr = $obj->get('stations');
print $outstr;
# Prints: ['Station1', 'Station2', 'Station3']
# For more info on PDL::Char variables see PDL::Char(3), or perldoc PDL::Char

πŸ’Ž putslice πŸ’Ž

Put a PDL matrix to a slice of a NetCDF variable. ✨

Arguments:

  1. The name of the variable to create
  2. A reference to a list of dimension names for this variable
  3. A reference to a list of dimensions for this variable
  4. A reference to a list which specifies the N dimensional starting point of the slice.
  5. A reference to a list which specifies the N dimensional count of the slice.
  6. The PDL to put. It must conform to the size specified by the 4th and 5th arguments. The 2nd and 3rd argument are optional if the variable is already defined in the netcdf object.
  7. Optional options: {DEFLATE => 7, SHUFFLE => 0/1, _FillValue => -32767} will use gzip compression (level 7) on that variable and shuffle will not/will use the shuffle filter. These options are only valid for netcdf4 files. If you are unsure, test with ($nc->getFormat >= PDL::NetCDF::NC_FORMAT::NC_FORMAT_NETCDF4)

In addition, netcdf4 does not allow changing the _FillValue attribute after the variable has been put/putslice'd. Therefore, the _FillValue can be set with an option to put/putslice.

Returns: None.

my $pdl = pdl [[1, 2, 3], [4, 5, 6]];

# Specify variable name to put PDL in, plus names of the dimensions.  Dimension
# lengths are taken from the PDL, in this case, dim1 = 2 and dim2 = 3.
$ncobj->putslice ('var1', ['dim1', 'dim2', 'dim3'], [2,3,3], [0,0,0], [2,3,1], $pdl);
$ncobj->putslice ('var1', [], [], [0,0,2], [2,3,1], $pdl);

my $pdl2 = $ncobj->get('var1');

print $pdl2;

         [
        [
         [          1 9.96921e+36           1]
         [          2 9.96921e+36           2]
         [          3 9.96921e+36           3]
        ]
        [
         [          4 9.96921e+36           4]
         [          5 9.96921e+36           5]
         [          6 9.96921e+36           6]
        ]
       ]

note that the netcdf missing value (not 0) is filled in. 😭

🫢 sync 🫢

Synchronize the data to the disk. Use this if you want to read the file from another process without closing the file. This makes only sense after put, puttext, putslice, putatt operations. πŸ’Ύ

Returns: nothing. Barfs on error. πŸ’€

$ncobj->sync

🌸 get 🌸

Get a PDL matrix from a netCDF variable. πŸŽ€

Arguments:

  1. The name of the netCDF variable to fetch. If this is the only argument, then the entire variable will be returned.

To fetch a slice of the netCDF variable, optional 2nd and 3rd arguments must be specified:

  1. A pdl which specifies the N dimensional starting point of the slice.
  2. A pdl that says how many elements you want in each dimension of the slice β€” the N-dimensional count, bestie.

okay and you can also slide an options hashref in there too, because of course you can πŸ’…

  • NOCOMPRESS β€” tells PDL::NetCDF to not try to uncompress a compressed variable. Peep the COMPRESS option over on put and putslice for the tea on that one.
  • PDL_BAD β€” tells PDL::NetCDF to translate the _FillValue or missing_value attributes into bad-values, like NaN's. iconic behavior honestly.

Returns: the PDL that represents the netCDF variable. It barfs on error β€” so like, heads up. 😭

# A slice of the netCDF variable.
# [0,0] is the starting point, [1,2] is the count.
my $slice  = $ncobj->get ('var1', [0,0], [1,2], {NOCOMPRESS => 1, PDL_BAD => 1});

# If var1 contains this:  [[1, 2, 3], [4, 5, 6]]
# Then $slice contains: [1,2] (Size '1' dimensions are eliminated).

πŸŽ€ putatt πŸŽ€

putatt β€” attach a numerical or textual attribute to a NetCDF variable, or to the whole entire file. slay.

Arguments:

  1. The attribute itself. Either a one dimensional PDL (which, yes, can totally just hold one single number) or a string.
  2. The name you want that attribute to have inside the netCDF file. lowkey important: lots of attribute names already have pre-defined meanings, so go read the netCDF documentation for the full lore.
  3. Optional β€” you can name the pre-defined netCDF variable you want this attribute glued onto. Leave it off and the attribute becomes a global one, pertaining to the entire netCDF file. main character global energy 🌍

Returns: nothing. Barfs on error.

# Attach a double attribute of size 3 to var1
$ncobj->putatt (double([1,2,3]), 'double_attribute', 'var1');

# Write a textual, global attribute.  'attr_name' is the attribute name.
$ncobj->putatt ('The text of the global attribute', 'attr_name');

πŸ¦‹ getatt πŸ¦‹

Grab an attribute off a netCDF object. yes bestie, we're retrieving.

Arguments:

  1. The name of the attribute (a text string).
  2. The name of the variable this attribute is attached to. Don't pass this one and the function hands you back the global attribute of that name instead.
# Get a global attribute
my $attr2 = $ncobj->getatt ('attr_name');

# Get an attribute associated with the variable 'var1'
my $attr1 = $ncobj->getatt ('double_attribute', 'var1');

✨ getDeflateShuffle ✨

Get the deflate level and the shuffle flag for a variable. yes, both of them, in one go, we love efficiency.

You can call this on all files, but ngl only netcdf4 files actually support shuffle and deflate, so, yk. πŸ’€

Arguments:

  1. The name of the variable.

Returns:

($deflate, $shuffle)

my ($deflate, $shuffle) = $nc->getDeflateShuffle('varName');

πŸ’œ getvariabletype πŸ’œ

Get the type of a variable from a netCDF object.

Arguments:

  1. The name of the variable.

Returns: PDL::type, or undef when the variable isn't defined. (undef era, we've all been there πŸ™ˆ)

# Get a type
my $type = $ncobj->getvariabletype ('var1');

🌸 puttext 🌸

Put a perl text string into a multi-dimensional NetCDF array. iconic.

Arguments:

  1. The name of the variable to be created (a text string).
  2. A reference to a perl list of dimension names to use when creating this NetCDF array.
  3. A reference to a perl list of dimension lengths.
  4. A perl string to put into the netCDF array. Heads up: if the NetCDF array is 3 x 10, then the string has to be 30 characters long. fr fr, do the math.
  5. Optional nc4 options: {DEFLATE => 7, SHUFFLE => 0}
my $str = "Station1  Station2  Station3  ";
$obj->puttext('textvar', ['n_station', 'n_string'], [3,10], $str);

πŸ“ gettext πŸ“

Get a multi-dimensional NetCDF array and dump it into a perl string.

Arguments:

  1. The name of the NetCDF variable.
my $outstr = $obj->gettext('textvar');

🌈 dimsize 🌈

Get the size of a dimension from a netCDF object.

Arguments:

  1. The name of the dimension.

Returns: the size of the dimension, bestie. that's it, that's the whole vibe.

my $dim1size = $ncobj->dimsize('dim1');

πŸ‘‘ close πŸ‘‘

Close a NetCDF object, writing out the file. the finale, periodt.

Arguments: none

Returns: nothing

You can totally close the netCDF file explicitly through this close method. But lowkey you don't have to β€” a DESTROY block does an automatic close whenever the netCDF object passes out of scope. self-care πŸ’…

$ncobj->close();

🧚 getdimensionnames ([$varname]) 🧚

Get all the dimension names from an open NetCDF object. If you specify a variable name, it'll just return the dimension names for that variable. so specific, love that.

Arguments: none

Returns: an array reference of dimension names

my $varlist = $ncobj->getdimensionnames();
foreach(@$varlist){
  print "Found dim $_\n";
}

🫢 getattributenames 🫢

Get the attribute names for a given variable from an open NetCDF object.

Arguments: optional variable name β€” pass nothing and it'll hand you back the object's global netcdf attributes.

Returns: an array reference of attribute names

my $attlist = $ncobj->getattributenames('var1');

⭐ getvariablenames ⭐

Get all the variable names for an open NetCDF object. all of them. every last one.

Arguments: none.

Returns: an array reference of variable names

my $varlist = $ncobj->getvariablenames();

πŸ’– setrec πŸ’–

Set up a 'record' of several 1D netCDF variables that all share the same dimension. Once this is set up, you get quick reading/writing of one element from all the variables at once, into/out of a perl list. group chat for your data honestly.

Arguments:

  1. The names of all the netCDF variables to group into a record

Returns: a record name to use in future putrec/getrec calls

my $rec = $ncobj->setrec('var1', 'var2', 'var3');

πŸ’… getrec πŸ’…

Gets a 'record' β€” that's one value from each of several 1D netCDF variables β€” that you previously set up using 'setrec'. These values come back to you in a perl list.

Arguments:

  1. The name of the record set up in 'setrec'.
  2. The index to fetch.

Returns: a perl list of all values. Note that these variables can be of different types: float, double, integer, string. mix and match, we stan flexibility ✨

my @rec = $ncobj->getrec($rec, 5);

πŸŽ€ putrec πŸŽ€

Puts a 'record' β€” one value from each of several 1D netCDF variables β€” that you previously set up using 'setrec'. This time the values get supplied as a perl list reference.

Arguments:

  1. The name of the record set up in 'setrec'.
  2. The index to set.
  3. A perl list ref containing the values.

Returns: none.

$ncobj->putrec($rec, 5, \@values);

πŸ“ WRITING NetCDF-FILES EFFICIENTLY πŸ“

okay bestie, this part is important so listen up. Writing several variables to NetCDF-files can take a long time. When a new variable gets attached by put to a file, the attribute header has to be written. That might force the internal netcdf-library to restructure the complete file, and that can eat up a truly unhinged amount of IO-resources. 😭

The glow-up: pre-defining the dimensions, attributes, and variables saves you soooo much time. The rule of thumb is basically β€” define and write the data in the order it'll be laid out in the file. In PDL::NetCDF terms, that means the following:

Open the netcdf file

my $nc = new PDL::NetCDF('test.nc', {MODE => O_CREAT|O_RDWR,
                                     REVERSE_DIMS => 1});

Write the global attributes

$nc->putatt (double([1,2,3]), 'double_attribute');

Define all variables, make use of the NC_UNLIMITED dimension

# here it is possible to choose float/double/short/long
$pdl_init = long ([]);
for (my $i=0; $i<$it; $i++) {
    my $out2 = $nc->putslice("VAR$i",
                          ['x','y','z','t'],
                          [150,100,20,PDL::NetCDF::NC_UNLIMITED()],
                          [0,0,0,0],[1,0,0,0],$pdl_init);
}

Write the variable-attributes

$nc->putatt ("var-attr", 'attribute', 'VAR0');

Write data with putslice

$nc->putslice("VAR5",[],[],[0,0,0,0],[$datapdl->dims],$datapdl);

🌟 AUTHOR 🌟

Doug Hunt, dhunt@ucar.edu.

πŸ’œ CONTRIBUTORS πŸ’œ

Heiko Klein, heiko.klein@met.no Edward Baudrez, Royal Meteorological Institute of Belgium, edward.baudrez@meteo.be Ed J (mohawk2), etj@cpan.org

πŸ”₯ SEE ALSO πŸ”₯

perl(1), PDL(1), netcdf(3).


and that's a wrap, bestie 🫢 this whole thing is perl v5.42.3, dated 2026-08-21, and it ships as NetCDF(3pm) β€” go forth and read your netCDF files, no cap πŸ’–βœ¨

πŸ“¦ where this page comes from