adapted to modern netcdf interface
authorAndreas Thurnherr <ant@ldeo.columbia.edu>
Mon, 12 Apr 2021 06:35:27 -0400
changeset 48 534f2a6c7735
parent 44 e77821790bdd
child 49 789eddc6d4b3
adapted to modern netcdf interface
antsnc.pl
antsnc.pl.old
--- a/antsnc.pl
+++ b/antsnc.pl
@@ -1,9 +1,9 @@
 #======================================================================
 #                    A N T S N C . P L 
 #                    doc: Mon Jul 17 11:59:37 2006
-#                    dlm: Fri Jan 15 10:17:51 2016
+#                    dlm: Mon Apr 12 06:32:43 2021
 #                    (c) 2006 A.M. Thurnherr
-#                    uE-Info: 25 56 NIL 0 0 72 2 2 4 NIL ofnI
+#                    uE-Info: 199 0 NIL 0 0 72 2 2 4 NIL ofnI
 #======================================================================
 
 # ANTS netcdf library
@@ -48,12 +48,12 @@
 {
 	my($tp) = @_;
 
-	return 'byte'	if ($tp == NetCDF::BYTE);
-	return 'char'	if ($tp == NetCDF::CHAR);
-	return 'short'	if ($tp == NetCDF::SHORT);
-	return 'long'	if ($tp == NetCDF::LONG);
-	return 'float'	if ($tp == NetCDF::FLOAT);
-	return 'double' if ($tp == NetCDF::DOUBLE);
+	return 'byte'	if ($tp == NetCDF::constant(BYTE,0));
+	return 'char'	if ($tp == NetCDF::constant(CHAR,0));
+	return 'short'	if ($tp == NetCDF::constant(SHORT,0));
+	return 'long'	if ($tp == NetCDF::constant(LONG,0));
+	return 'float'	if ($tp == NetCDF::constant(FLOAT,0));
+	return 'double' if ($tp == NetCDF::constant(DOUBLE,0));
 	croak("$0: unknown NetCDF type #$tp\n");
 }
 
@@ -61,12 +61,12 @@
 {
 	my($tn) = lc($_[0]);
 
-	return  NetCDF::BYTE	if ($tn eq 'byte');
-	return  NetCDF::CHAR	if ($tn eq 'char');
-	return  NetCDF::SHORT	if ($tn eq 'short');
-	return  NetCDF::LONG	if ($tn eq 'long');
-	return  NetCDF::FLOAT	if ($tn eq 'float');
-	return  NetCDF::DOUBLE 	if ($tn eq 'double');
+	return  NetCDF::constant(BYTE,0)	if ($tn eq 'byte');
+	return  NetCDF::constant(CHAR,0)	if ($tn eq 'char');
+	return  NetCDF::constant(SHORT,0)	if ($tn eq 'short');
+	return  NetCDF::constant(LONG,0)	if ($tn eq 'long');
+	return  NetCDF::constant(FLOAT,0)	if ($tn eq 'float');
+	return  NetCDF::constant(DOUBLE,0)	if ($tn eq 'double');
 	croak("$0: unknown NetCDF type <$tn>\n");
 }
 
@@ -78,11 +78,11 @@
 {
 	my($tp) = @_;
 
-	return 1 if ($tp == NetCDF::BYTE);
-	return 1 if ($tp == NetCDF::SHORT);
-	return 1 if ($tp == NetCDF::LONG);
-	return 1 if ($tp == NetCDF::FLOAT);
-	return 1 if ($tp == NetCDF::DOUBLE);
+	return 1 if ($tp == NetCDF::constant(BYTE,0));
+	return 1 if ($tp == NetCDF::constant(SHORT,0));
+	return 1 if ($tp == NetCDF::constant(LONG,0));
+	return 1 if ($tp == NetCDF::constant(FLOAT,0));
+	return 1 if ($tp == NetCDF::constant(DOUBLE,0));
 	return 0;
 }
 
@@ -92,7 +92,7 @@
 
 sub NC_isChar($)
 {
-	return $_[0] == NetCDF::CHAR;
+	return $_[0] == NetCDF::constant(CHAR,0);
 }
 
 #-----------------------------------
@@ -154,15 +154,15 @@
 	my($fn) = @_;
 	my(%NC);
 
-	$NC{id} = NetCDF::open($ARGV[0],NetCDF::NOWRITE);	# open
+	$NC{id} = NetCDF::ncopen($ARGV[0],NetCDF::constant(NOWRITE,0));	# open
 
 	my($nd,$nv,$nga,$udi);								# get nelts
-	NetCDF::inquire($NC{id},$nd,$nv,$nga,$udi);
+	NetCDF::ncinquire($NC{id},$nd,$nv,$nga,$udi);
 	$NC{unlim_dimId} = $udi;
 
 	for (my($d)=0; $d<$nd; $d++) {						# dimensions
 		my($dnm,$ln);
-		NetCDF::diminq($NC{id},$d,$dnm,$ln);
+		NetCDF::ncdiminq($NC{id},$d,$dnm,$ln);
 		$NC{dimName}[$d] = $dnm;
 		$NC{dimId}{$dnm} = $d;
 		$NC{dimLen}{$dnm} = $ln;
@@ -171,7 +171,7 @@
 	for (my($v)=0; $v<$nv; $v++) {						# vars & var-attribs
 		my($vnm,$vtp,$nvd,$nva);
 		my(@dids) = ();
-		NetCDF::varinq($NC{id},$v,$vnm,$vtp,$nvd,\@dids,$nva);
+		NetCDF::ncvarinq($NC{id},$v,$vnm,$vtp,$nvd,\@dids,$nva);
 		$NC{varName}[$v] = $vnm;
 		$NC{varId}{$vnm} = $v;
 		$NC{varType}{$vnm} = $vtp;
@@ -179,15 +179,15 @@
 		
 		for (my($a)=0; $a<$nva; $a++) {					# var-attribs
 			my($anm,$atp,$aln);
-			NetCDF::attname($NC{id},$v,$a,$anm);
+			NetCDF::ncattname($NC{id},$v,$a,$anm);
 			$NC{varAttrName}{$vnm}[$a] = $anm;
-			NetCDF::attinq($NC{id},$v,$anm,$atp,$aln);
+			NetCDF::ncattinq($NC{id},$v,$anm,$atp,$aln);
 			$NC{varAttrType}{$vnm}{$anm} = $atp;
 			$NC{varAttrLen}{$vnm}{$anm} = $aln;
-			if ($atp == NetCDF::BYTE || $atp == NetCDF::CHAR || $aln == 1) {
+			if ($atp == NetCDF::constant(BYTE,0) || $atp == NetCDF::constant(CHAR,0) || $aln == 1) {
 				my($val) = "";
-				NetCDF::attget($NC{id},$v,$anm,\$val);
-				$val =~ s{\0+$}{} if ($atp == NetCDF::CHAR);	# trailing \0
+				NetCDF::ncattget($NC{id},$v,$anm,\$val);
+				$val =~ s{\0+$}{} if ($atp == NetCDF::constant(CHAR,0));	# trailing \0
 				$NC{varAttr}{$vnm}{$anm} = $val;
 			}		
 		}
@@ -195,15 +195,15 @@
 
 	for (my($a)=0; $a<$nga; $a++) {						#  global attribs
 		my($anm,$atp,$aln);
-		NetCDF::attname($NC{id},NetCDF::GLOBAL,$a,$anm);
+		NetCDF::ncattname($NC{id},NetCDF::constant(GLOBAL,0),$a,$anm);
 		$NC{attrName}[$a] = $anm;
-		NetCDF::attinq($NC{id},NetCDF::GLOBAL,$anm,$atp,$aln);
+		NetCDF::ncattinq($NC{id},NetCDF::constant(GLOBAL,0),$anm,$atp,$aln);
 		$NC{attrType}{$anm} = $atp;
 		$NC{attrLen}{$anm} = $aln;
-		if ($atp == NetCDF::BYTE || $atp == NetCDF::CHAR || $aln == 1) {
+		if ($atp == NetCDF::constant(BYTE,0) || $atp == NetCDF::constant(CHAR,0) || $aln == 1) {
 			my($val) = "";
-			NetCDF::attget($NC{id},NetCDF::GLOBAL,$anm,\$val);
-			$val =~ s{\0+$}{} if ($atp == NetCDF::CHAR);
+			NetCDF::ncattget($NC{id},NetCDF::constant(GLOBAL,0),$anm,\$val);
+			$val =~ s{\0+$}{} if ($atp == NetCDF::constant(CHAR,0));
 			$NC{attr}{$anm} = $val;
 		}	    
     }
@@ -235,22 +235,22 @@
 	my($fn,$abscissa,$suppress_params) = @_;
 	my(%attrDone,@slDim,@NCtype);
 
-	my($ncId) = NetCDF::create($fn,NetCDF::CLOBBER);
-	NetCDF::setfill($ncId,NetCDF::NOFILL);				# NetCDF library bug
+	my($ncId) = NetCDF::nccreate($fn,NetCDF::constant(CLOBBER,0));
+	NetCDF::ncsetfill($ncId,NetCDF::constant(NOFILL,0));				# NetCDF library bug
 
 														# DIMENSIONS
-	my($aid) = NetCDF::dimdef($ncId,$abscissa,NetCDF::UNLIMITED);
+	my($aid) = NetCDF::ncdimdef($ncId,$abscissa,NetCDF::constant(UNLIMITED,0));
 
 	for (my($f)=0; $f<=$#antsLayout; $f++) {			# types
 		my($tpa) = $antsLayout[$f] . ':NC_type';
 		my($sl) = ($P{$tpa} =~ m{^string(\d+)$});
 		if ($sl > 0) {									# string
-			$slDim[$f] = NetCDF::dimdef($ncId,"$antsLayout[$f]:strlen",$sl);
-			$NCtype[$f] = NetCDF::CHAR;
+			$slDim[$f] = NetCDF::ncdimdef($ncId,"$antsLayout[$f]:strlen",$sl);
+			$NCtype[$f] = NetCDF::constant(CHAR,0);
 		} elsif (defined($P{$tpa})) {					# custom
 			$NCtype[$f] = NC_type($P{$tpa});
 		} else {										# default
-			$NCtype[$f] = NetCDF::DOUBLE;
+			$NCtype[$f] = NetCDF::constant(DOUBLE,0);
 		}
 #		printf(STDERR "type %s set to %s\n",$antsLayout[$f],NC_typeName($NCtype[$f]));
 		undef($P{$tpa});								# do not add to ATTRIBs
@@ -259,9 +259,9 @@
 	for (my($f)=0; $f<=$#antsLayout; $f++) {			# VARIABLES
 		my($vid);
 		if (defined($slDim[$f])) {
-			$vid = NetCDF::vardef($ncId,$antsLayout[$f],$NCtype[$f],[$aid,$slDim[$f]]);
+			$vid = NetCDF::ncvardef($ncId,$antsLayout[$f],$NCtype[$f],[$aid,$slDim[$f]]);
 		} else {
-			$vid = NetCDF::vardef($ncId,$antsLayout[$f],$NCtype[$f],[$aid]);
+			$vid = NetCDF::ncvardef($ncId,$antsLayout[$f],$NCtype[$f],[$aid]);
 		}
 		croak("$0: varid != fnr (implementation restriction)")
 			unless ($vid == $f);
@@ -271,9 +271,9 @@
 			next unless ($var eq $antsLayout[$f]);
 			$attrDone{$anm} = 1;						# mark
 			if (numberp($P{$anm}) || lc($P{$anm}) eq nan) {
-				NetCDF::attput($ncId,$f,$attr,NetCDF::DOUBLE,$P{$anm});
+				NetCDF::ncattput($ncId,$f,$attr,NetCDF::constant(DOUBLE,0),$P{$anm});
 			} else {
-				NetCDF::attput($ncId,$f,$attr,NetCDF::CHAR,$P{$anm});
+				NetCDF::ncattput($ncId,$f,$attr,NetCDF::constant(CHAR,0),$P{$anm});
 			}
         }		                  
 	}
@@ -287,14 +287,14 @@
 					 $anm eq 'RECNO'	|| $anm eq 'LINENO');
 			next if $attrDone{$anm};
 			if (numberp($P{$anm}) || lc($P{$anm}) eq nan) {
-				NetCDF::attput($ncId,NetCDF::GLOBAL,$anm,NetCDF::DOUBLE,$P{$anm});
+				NetCDF::ncattput($ncId,NetCDF::constant(GLOBAL,0),$anm,NetCDF::constant(DOUBLE,0),$P{$anm});
 			} else {
-				NetCDF::attput($ncId,NetCDF::GLOBAL,$anm,NetCDF::CHAR,$P{$anm});
+				NetCDF::ncattput($ncId,NetCDF::constant(GLOBAL,0),$anm,NetCDF::constant(CHAR,0),$P{$anm});
 			}
 	    }
 	}
 
-	NetCDF::endef($ncId);
+	NetCDF::ncendef($ncId);
 
 	return $ncId;
 }
new file mode 100644
--- /dev/null
+++ b/antsnc.pl.old
@@ -0,0 +1,302 @@
+#======================================================================
+#                    A N T S N C . P L 
+#                    doc: Mon Jul 17 11:59:37 2006
+#                    dlm: Fri Jan 15 10:17:51 2016
+#                    (c) 2006 A.M. Thurnherr
+#                    uE-Info: 25 56 NIL 0 0 72 2 2 4 NIL ofnI
+#======================================================================
+
+# ANTS netcdf library
+
+# HISTORY:
+#	Jul 17, 2006: - created
+#	Jul 21, 2006: - documented
+#				  - added NC-encoding routines
+#	Jul 22: 2006: - BUG: pseudo %PARAMs were written as well
+#				  -	BUG: var ATTRs were not enconded correctly
+#				  - added type support
+#	Jul 23, 2006: - improved type magic
+#	Sep  1, 2006: - BUG: removing trainling 0s had not worked
+#	Sep 23, 2006: - fiddled
+#	Jul 11, 2008: - adapted to new pseudo %PARAMs
+#	Jul 16, 2008: - remove \0s from strings in NC_stringify
+#	Mar 20, 2008: - added progress output to NC_stringify
+#	Jul 21, 2009: - allowed for suppression of %PARAMs
+#	Jan 15, 2016: - BUG: %DEPS pseudo-%PARAM was encoded
+
+# NOTES:
+#	- multi-valued attribs are not loaded by getInfo()
+#	- spaces in NC strings are replaced by underscores
+#	- data filling is disabled, because of a bug in the NetCDF library
+
+# NetCDF Library Bug:
+#	The library appears to have incorrect default _FillValue types for
+#	integer data types. The error appears if the "setfill" line is commented
+#	out and the following command is run:
+#		listNC -ct dbk100.nc | NCode -o TEMP.nc time
+#	NB: The error occurs when the 1st variable value is written, NOT when
+#	    the first Q_time value is written. However, when all the Q_ fields
+#		are ommitted, the error disappears.
+
+use NetCDF;
+
+#----------------------------------
+# string representation of NC types
+#----------------------------------
+
+sub NC_typeName($)
+{
+	my($tp) = @_;
+
+	return 'byte'	if ($tp == NetCDF::BYTE);
+	return 'char'	if ($tp == NetCDF::CHAR);
+	return 'short'	if ($tp == NetCDF::SHORT);
+	return 'long'	if ($tp == NetCDF::LONG);
+	return 'float'	if ($tp == NetCDF::FLOAT);
+	return 'double' if ($tp == NetCDF::DOUBLE);
+	croak("$0: unknown NetCDF type #$tp\n");
+}
+
+sub NC_type($)
+{
+	my($tn) = lc($_[0]);
+
+	return  NetCDF::BYTE	if ($tn eq 'byte');
+	return  NetCDF::CHAR	if ($tn eq 'char');
+	return  NetCDF::SHORT	if ($tn eq 'short');
+	return  NetCDF::LONG	if ($tn eq 'long');
+	return  NetCDF::FLOAT	if ($tn eq 'float');
+	return  NetCDF::DOUBLE 	if ($tn eq 'double');
+	croak("$0: unknown NetCDF type <$tn>\n");
+}
+
+#--------------------------------------
+# test whether given NC type is numeric
+#--------------------------------------
+
+sub NC_isNumeric($)
+{
+	my($tp) = @_;
+
+	return 1 if ($tp == NetCDF::BYTE);
+	return 1 if ($tp == NetCDF::SHORT);
+	return 1 if ($tp == NetCDF::LONG);
+	return 1 if ($tp == NetCDF::FLOAT);
+	return 1 if ($tp == NetCDF::DOUBLE);
+	return 0;
+}
+
+#----------------------------------------
+# test whether given NC type is character
+#----------------------------------------
+
+sub NC_isChar($)
+{
+	return $_[0] == NetCDF::CHAR;
+}
+
+#-----------------------------------
+# convert character- to string array
+#-----------------------------------
+
+sub NC_stringify($@)
+{
+	my($len,@chars) = @_;
+	my(@strings);
+	my($nStrings) = @chars/$len;
+
+	print(STDERR "$0: extracting $nStrings strings")
+		if ($nStrings > 1000);
+
+	while (@chars) {
+		print(STDERR ".") if ($nStrings>1000 && $n++%1000 == 0);
+		push(@strings,pack("c$len",@chars));
+		$strings[$#strings] =~ s/ /_/g;
+		$strings[$#strings] =~ s/\0//g;
+		splice(@chars,0,$len);
+	}
+	print(STDERR "\n") if ($nStrings > 1000);
+	return @strings;
+}
+
+#----------------------------------------------------------------------
+# open netcdf file and read (most) metadata into hash
+#
+#	INPUT:
+#		<filename>
+#
+#	OUTPUT:
+#		$NC{id}								netcdf id
+#
+#		@NC{attrName}[]						names of global attrs
+#		%NC{AttrType}{$aName}				types of global attrs
+#		%NC{AttrLen}{$aName}				# of elts in global attrs
+#		%NC{Attr}{$aName}					vals of scalar global attrs
+#
+#		$NC{unlim_dimId}					dim id of unlimited dim
+#		@NC{dimName}[$dimId]				dim names
+#		%NC{dimID}{$dName}					dim ids
+#		%NC{dimLen}{$dName}					# elts in dim
+#
+#		@NC{varName}[$varId]				var names
+#		%NC{varType}{$vName}				var types
+#		%NC{varId}{$vName}					var ids
+#		@%NC{varDimIDs}{$vName}[]			dims of vars, e.g. u(lon,lat)
+#		@%NC{varAttrName}{$vName}[]			names of var attrs
+#		%%NC{varAttrType}{$vName}{$aName}	types of var attrs
+#		%%NC{varAttrLen}{$vName}{$aName}	# of elts in var attrs
+#		%%NC{varAttr}{$vName}{$aName}		vals of scalar var attrs
+#
+#----------------------------------------------------------------------
+
+sub NC_readMData($)
+{
+	my($fn) = @_;
+	my(%NC);
+
+	$NC{id} = NetCDF::open($ARGV[0],NetCDF::NOWRITE);	# open
+
+	my($nd,$nv,$nga,$udi);								# get nelts
+	NetCDF::inquire($NC{id},$nd,$nv,$nga,$udi);
+	$NC{unlim_dimId} = $udi;
+
+	for (my($d)=0; $d<$nd; $d++) {						# dimensions
+		my($dnm,$ln);
+		NetCDF::diminq($NC{id},$d,$dnm,$ln);
+		$NC{dimName}[$d] = $dnm;
+		$NC{dimId}{$dnm} = $d;
+		$NC{dimLen}{$dnm} = $ln;
+	}
+
+	for (my($v)=0; $v<$nv; $v++) {						# vars & var-attribs
+		my($vnm,$vtp,$nvd,$nva);
+		my(@dids) = ();
+		NetCDF::varinq($NC{id},$v,$vnm,$vtp,$nvd,\@dids,$nva);
+		$NC{varName}[$v] = $vnm;
+		$NC{varId}{$vnm} = $v;
+		$NC{varType}{$vnm} = $vtp;
+		@{$NC{varDimIds}{$vnm}} = @dids[0..$nvd-1];
+		
+		for (my($a)=0; $a<$nva; $a++) {					# var-attribs
+			my($anm,$atp,$aln);
+			NetCDF::attname($NC{id},$v,$a,$anm);
+			$NC{varAttrName}{$vnm}[$a] = $anm;
+			NetCDF::attinq($NC{id},$v,$anm,$atp,$aln);
+			$NC{varAttrType}{$vnm}{$anm} = $atp;
+			$NC{varAttrLen}{$vnm}{$anm} = $aln;
+			if ($atp == NetCDF::BYTE || $atp == NetCDF::CHAR || $aln == 1) {
+				my($val) = "";
+				NetCDF::attget($NC{id},$v,$anm,\$val);
+				$val =~ s{\0+$}{} if ($atp == NetCDF::CHAR);	# trailing \0
+				$NC{varAttr}{$vnm}{$anm} = $val;
+			}		
+		}
+	}
+
+	for (my($a)=0; $a<$nga; $a++) {						#  global attribs
+		my($anm,$atp,$aln);
+		NetCDF::attname($NC{id},NetCDF::GLOBAL,$a,$anm);
+		$NC{attrName}[$a] = $anm;
+		NetCDF::attinq($NC{id},NetCDF::GLOBAL,$anm,$atp,$aln);
+		$NC{attrType}{$anm} = $atp;
+		$NC{attrLen}{$anm} = $aln;
+		if ($atp == NetCDF::BYTE || $atp == NetCDF::CHAR || $aln == 1) {
+			my($val) = "";
+			NetCDF::attget($NC{id},NetCDF::GLOBAL,$anm,\$val);
+			$val =~ s{\0+$}{} if ($atp == NetCDF::CHAR);
+			$NC{attr}{$anm} = $val;
+		}	    
+    }
+	
+	return %NC;
+}
+
+#----------------------------------------------------------------------
+# create new nc file and write metadata
+#
+#	INPUT:
+#		<filename>
+#		<abscissa>			name of unlimited dimension
+#		<suppress-params>	if true, don't write %PARAMs
+#
+#	OUTPUT:
+#		<netcdf id>
+#
+#	NOTES:
+#		- netcdf types can be set with %<var>:NC_type to
+#			byte, long, short, double
+#		- string types are as in old PASCAL convention (e.g. string80)
+#		- default type is NetCDF::DOUBLE
+#		- %<var>:NC_type are not added to ATTRIBs
+#----------------------------------------------------------------------
+
+sub NC_writeMData($$$)
+{
+	my($fn,$abscissa,$suppress_params) = @_;
+	my(%attrDone,@slDim,@NCtype);
+
+	my($ncId) = NetCDF::create($fn,NetCDF::CLOBBER);
+	NetCDF::setfill($ncId,NetCDF::NOFILL);				# NetCDF library bug
+
+														# DIMENSIONS
+	my($aid) = NetCDF::dimdef($ncId,$abscissa,NetCDF::UNLIMITED);
+
+	for (my($f)=0; $f<=$#antsLayout; $f++) {			# types
+		my($tpa) = $antsLayout[$f] . ':NC_type';
+		my($sl) = ($P{$tpa} =~ m{^string(\d+)$});
+		if ($sl > 0) {									# string
+			$slDim[$f] = NetCDF::dimdef($ncId,"$antsLayout[$f]:strlen",$sl);
+			$NCtype[$f] = NetCDF::CHAR;
+		} elsif (defined($P{$tpa})) {					# custom
+			$NCtype[$f] = NC_type($P{$tpa});
+		} else {										# default
+			$NCtype[$f] = NetCDF::DOUBLE;
+		}
+#		printf(STDERR "type %s set to %s\n",$antsLayout[$f],NC_typeName($NCtype[$f]));
+		undef($P{$tpa});								# do not add to ATTRIBs
+    }
+
+	for (my($f)=0; $f<=$#antsLayout; $f++) {			# VARIABLES
+		my($vid);
+		if (defined($slDim[$f])) {
+			$vid = NetCDF::vardef($ncId,$antsLayout[$f],$NCtype[$f],[$aid,$slDim[$f]]);
+		} else {
+			$vid = NetCDF::vardef($ncId,$antsLayout[$f],$NCtype[$f],[$aid]);
+		}
+		croak("$0: varid != fnr (implementation restriction)")
+			unless ($vid == $f);
+		foreach my $anm (keys(%P)) {					# variable attributes
+			next unless defined($P{$anm});
+			my($var,$attr) = ($anm =~ m{([^:]+):(.*)});
+			next unless ($var eq $antsLayout[$f]);
+			$attrDone{$anm} = 1;						# mark
+			if (numberp($P{$anm}) || lc($P{$anm}) eq nan) {
+				NetCDF::attput($ncId,$f,$attr,NetCDF::DOUBLE,$P{$anm});
+			} else {
+				NetCDF::attput($ncId,$f,$attr,NetCDF::CHAR,$P{$anm});
+			}
+        }		                  
+	}
+
+	unless ($suppress_params) {
+		foreach my $anm (keys(%P)) {					# GLOBAL ATTRIBUTES
+			next unless defined($P{$anm});
+			next if ($anm eq 'FILENAME' || $anm eq 'DIRNAME' || # skip pseudo 
+					 $anm eq 'BASENAME' || $anm eq 'EXTN' ||
+					 $anm eq 'PATHNAME' || $anm eq 'DEPS' ||
+					 $anm eq 'RECNO'	|| $anm eq 'LINENO');
+			next if $attrDone{$anm};
+			if (numberp($P{$anm}) || lc($P{$anm}) eq nan) {
+				NetCDF::attput($ncId,NetCDF::GLOBAL,$anm,NetCDF::DOUBLE,$P{$anm});
+			} else {
+				NetCDF::attput($ncId,NetCDF::GLOBAL,$anm,NetCDF::CHAR,$P{$anm});
+			}
+	    }
+	}
+
+	NetCDF::endef($ncId);
+
+	return $ncId;
+}
+
+1;