netCDF-4 is based on HDF5. So in principle this already works as you can create HDF5 files from scratch in IP. For netCDF-3 only read support is currently there.
The HDF-5 tools in Igor will not allow you to make a CF compliant netCDF-4.
kaikin
This tool from my colleague I know all about and have my own modified version to support the complex file structure and extra primitive data types available in netCDF-4 (which people are welcome to FYI). It was the author himself lamenting the lack of proper netCDF support that prompted me to post - I was twisting his arm about updating a 32-bit XOP to 64-bit - he was asking why he would move from IP6.37 for no change in useability in his circumstances. Especially with a 30 seat license and the inevitable "why aren't you doing it 'in python?", it's free!
Forgive me, I might be reading your user name completely wrong, but in my head it parses to a name I recognise from a certain series of GeneralMacros out of NOAA I've been using versions of since the dark ages. If so I probably owe you!
A native tool would be a great addition to IP I think, even if currently if isn't holding me back personally.
I don't recall the specific issue, and this might have changed since IP6. What I should have written is:
>"The HDF-5 tools in Igor did not seem to allow me to make a CF compliant netCDF-4."
The XOP I was referring to isn't really related to netCDF, other than being used to generate some data that is output as netCDF. It isn't openly available and is unlikely to be of interest to anyone who doesn't also have an atmospheric research aircraft, or perhaps a cloud observatory on a mountain top.
Agreed, a native tool would be very useful and may help get Igor wider recognition and use since netCDF files are more easily dealt with in other code as you mention.
Yes, you are correct about the reference to General Macros code from NOAA! Glad you've found it to be useful.
I (re-)discovered the limitation with the Igor HDF-5 writing tools which do not permit direct creation of netCDF-4 (~ish) files.
There is no way to output the dimensional dependencies that netCDF has. Presumably this is netCDF specific and implemented as an extension to HDF5 in netCDF-4. Everything else seems possible, albeit you must be very explicit in writing internal attributes compared with using ncgen which handles this i.e. _Netcdf4Dimid, _Netcdf4Coordinates, _nc3_strict, _NCProperties.
Maybe the ability to write the dimensional dependencies could be added? As an extra flag to HDF5SaveData perhaps much like global and group attributes are handled? Please?
For reference if you create a HDF-5 file with all the requisite netCDF attributes but without the dimensional dependencies and use nccopy to convert it to a netCDF-4 a whole menagerie of dummy dimensions are created; one for each variables' dimension. With just the ability to deal with netCDF-4 properly you can easily convert between nc3 and nc4 or hdf5 with nccopy if required and avoid having to load whole files into memory and write them back out again.
Playing around with the attached cdl files converted to netcdf4 and loading them into IP reveals that it uses quite complicated HDF5 data types like compounds with object references in them. And I don't think you can write those from within IP. There is also no support for the HDF5 dimension scales in IP which avoids requiring you to manage these low level data types.
But this is just me guessing, I propose to reach out to WM support.
I still think though that netCDF4 files are 100% standard HDF5.
Yes, it's the dimension scales that are the sticking point. I think the datatypes that igor can't produce are not allowed in netCDF anyway, so that solves that problem. Writing netCDFs is possible indirectly via cdl as you have done, and I do this regularly. In that case ncgen handles the niceties of dimensionality. As dimension scales are a part of HDF5 it would be nice to be able to use them in Igor and by extension be able to make netCDF-4 files directly, and also to modify then directly without having to load the whole thing into memory.
Reading netCDFs isn't a problem FYI, I haven't found one that I couldn't nicely parse using some combination of HDF5.xop, netCDF.xop or nc_load.xop.
Much to my chagrin... you could parse them out of the HDF5dump it has just (re-)occurred to me, I had some horrible kluge to get all the metadata this way but retired it - maybe I'll look again.
EDIT: get dimensions with HDF5dump is slooooow.
If the netCDF was made with a netCDF API of some description there will be hidden/packed internal attributes _Netcdf4Dimid and _Netcdf4Coordinates which contain all the information you need to get the dimensions. Spat out of python code though, not so much.
The old nc_load.xop doesn't return enough info to get the dimensions if you have more than 1! That was written in 1999 though and I don't use it any longer but have written a basic parser that will use it if you're using Igor <8 for users of my data.
//___________________________________________________________________________________________________ // return a comma separated list of dimensions assoicated with HDF5 dataset Function/S getDimensions(String dumpStr)
variablei string dimStr, lineStr, outstr=""
for(i=0;i<ItemsInList(dumpStr,"\r");i+=1)
lineStr = StringfromList(i,dumpStr,"\r") if(stringmatch(lineStr,"*DATASET*")) do SplitString/E=".*\"/(.*)\".*" lineStr,dimStr
outstr=ReplaceString(";",RemoveEnding(outstr),", ")[1,inf] return outstr end
Igor can interpret the dimensions from HDF5 in some way as it dumps them in the HDF5 browser, but no tool is provided to load them directly and it isn't clear why - something about the datatype being VLEN, though I stopped reading at the point in the docs where it said it wasn't possible.
hrodstein has added a HDF5DimensionScale operation to the Igor Pro 9 beta which allows you to query and write HDF5 dimension scales. This by extension will allow you to make fully fledged netCDF-4 files directly from Igor 9 now easily.
Many thanks to Howard.
EDIT: below is a simple example that creates a file with most of the netCDF-4 features utilised.
// get a path to make the netCDF file in NewPath/O/Q/Z/M= "Please select a folder to outpt a file to" dataFilePath
// create the netCDF file
HDF5CreateFile/O/P=dataFilePath fileID as outfNameStr+".nc"
// output global attributes Make/O /T/FREE /N=1 StrAttribute = "This is my netCDF"
HDF5SaveData /O /A="title" StrAttribute, fileID, "/"
// save xWave as unlimited
HDF5SaveData /IGOR=0/LAYO={2,32,32}/MAXD={-1} xWave, fileID
HDF5DimensionScale dataset={fileID,"xWave"},dimName="xWave" , setScale Make/O /FREE /N=1 VarAttribute=0
HDF5SaveData /O /A="_Netcdf4Dimid" VarAttribute, fileID, "xWave" Make/O /T/FREE /N=1 StrAttribute = "this is the X dimesnion"
HDF5SaveData /O /A="comment" StrAttribute, fileID, "xWave"
// save zWave as hidden co-ordinate - tell the netCDF API this with the dimName
HDF5SaveData /IGOR=0 zWave, fileID
HDF5DimensionScale dataset={fileID,"zWave"}, dimName="This is a netCDF dimension but not a netCDF variable.\t\t"+num2istr(numpnts(zWave)), setScale Make/O /FREE /N=1 VarAttribute=1
HDF5SaveData /O /A="_Netcdf4Dimid" VarAttribute, fileID, "zWave"
// create a group in the file
HDF5CreateGroup fileID, "/sub-group", groupID
// output group attributes Make/O /T/FREE /N=1 StrAttribute = "This is my group"
HDF5SaveData /O /A="title" StrAttribute, groupID , "/sub-group"
// save bWave as hidden co-ordinate - tell the netCDF API this with the dimName
HDF5SaveData/IGOR=0 bWave, groupID
HDF5DimensionScale dataset={groupID ,"bWave"}, dimName="This is a netCDF dimension but not a netCDF variable.\t\t"+num2istr(numpnts(zWave)), setScale Make/O /FREE /N=1 VarAttribute=3
HDF5SaveData /O /A="_Netcdf4Dimid" VarAttribute, groupID , "bWave"
// save aWave to the group
HDF5SaveData/IGOR=0 aWave, groupID
HDF5DimensionScale scale={fileID,"xWave"}, dataset={groupID,"aWave"}, dimIndex=0, attachScale
HDF5DimensionScale scale={fileID,"zWave"}, dataset={groupID,"aWave"}, dimIndex=1, attachScale
HDF5DimensionScale scale={groupID,"bWave"}, dataset={groupID,"aWave"}, dimIndex=2, attachScale Make/O /T/FREE /N=1 StrAttribute = "this another data variable"
HDF5SaveData /O /A="comment" StrAttribute, groupID, "aWave"
// close the file
HDF5CloseFile fileID
// clean up KillWaves/Z xWave,yWave,zWave,aWave,bWave end
netCDF-4 is based on HDF5. So in principle this already works as you can create HDF5 files from scratch in IP. For netCDF-3 only read support is currently there.
November 19, 2020 at 07:15 am - Permalink
There is an example of how to create netCDF-3 files at:
https://www.wavemetrics.com/project/NCGEN
But it does require the installation of other tools (noted at the top of the procedure window) depending on which platform you are using.
November 20, 2020 at 11:02 am - Permalink
thomas_braun
The HDF-5 tools in Igor will not allow you to make a CF compliant netCDF-4.
kaikin
This tool from my colleague I know all about and have my own modified version to support the complex file structure and extra primitive data types available in netCDF-4 (which people are welcome to FYI). It was the author himself lamenting the lack of proper netCDF support that prompted me to post - I was twisting his arm about updating a 32-bit XOP to 64-bit - he was asking why he would move from IP6.37 for no change in useability in his circumstances. Especially with a 30 seat license and the inevitable "why aren't you doing it 'in python?", it's free!
Forgive me, I might be reading your user name completely wrong, but in my head it parses to a name I recognise from a certain series of GeneralMacros out of NOAA I've been using versions of since the dark ages. If so I probably owe you!
A native tool would be a great addition to IP I think, even if currently if isn't holding me back personally.
November 20, 2020 at 01:51 pm - Permalink
@cpr
> The HDF-5 tools in Igor will not allow you to make a CF compliant netCDF-4.
Do you know what is missing in IP for being able to write netCDF-4 files as plain HDF5 files?
You are also speaking about an XOP. Is that available somewhere?
November 21, 2020 at 03:35 am - Permalink
thomas_braun
I don't recall the specific issue, and this might have changed since IP6. What I should have written is:
>"The HDF-5 tools in Igor did not seem to allow me to make a CF compliant netCDF-4."
The XOP I was referring to isn't really related to netCDF, other than being used to generate some data that is output as netCDF. It isn't openly available and is unlikely to be of interest to anyone who doesn't also have an atmospheric research aircraft, or perhaps a cloud observatory on a mountain top.
November 23, 2020 at 02:04 am - Permalink
In reply to thomas_braun The HDF-5… by cpr
Agreed, a native tool would be very useful and may help get Igor wider recognition and use since netCDF files are more easily dealt with in other code as you mention.
Yes, you are correct about the reference to General Macros code from NOAA! Glad you've found it to be useful.
November 23, 2020 at 08:39 am - Permalink
I (re-)discovered the limitation with the Igor HDF-5 writing tools which do not permit direct creation of netCDF-4 (~ish) files.
There is no way to output the dimensional dependencies that netCDF has. Presumably this is netCDF specific and implemented as an extension to HDF5 in netCDF-4. Everything else seems possible, albeit you must be very explicit in writing internal attributes compared with using ncgen which handles this i.e. _Netcdf4Dimid, _Netcdf4Coordinates, _nc3_strict, _NCProperties.
Maybe the ability to write the dimensional dependencies could be added? As an extra flag to HDF5SaveData perhaps much like global and group attributes are handled? Please?
For reference if you create a HDF-5 file with all the requisite netCDF attributes but without the dimensional dependencies and use nccopy to convert it to a netCDF-4 a whole menagerie of dummy dimensions are created; one for each variables' dimension. With just the ability to deal with netCDF-4 properly you can easily convert between nc3 and nc4 or hdf5 with nccopy if required and avoid having to load whole files into memory and write them back out again.
February 9, 2021 at 02:57 am - Permalink
> Presumably this is netCDF specific and implemented as an extension to HDF5 in netCDF-4.
Do you mean that netCDF-4 files are HDF5 files with some extra stuff? That would really suprise me.
Can you give an example of a netCDF-4 file which you can not create with the HDF5 tools in IP?
February 9, 2021 at 04:13 am - Permalink
>Do you mean that netCDF-4 files are HDF5 files with some extra stuff?
That's precisely what it is, see:
https://www.unidata.ucar.edu/software/netcdf/docs/netcdf_introduction.h…
Edit: This rather comically written note on netCDF-4 / HDF-5 interoperability sums it up nicely:
https://www.unidata.ucar.edu/software/netcdf/docs/interoperability_hdf5…
February 9, 2021 at 04:26 am - Permalink
Thanks for the pointers.
I've also found https://www.unidata.ucar.edu/blogs/developer/en/entry/dimensions_scales which is a 4 part series which explains the details about HDF5 wrt netCDF4 dimension scales.
Playing around with the attached cdl files converted to netcdf4 and loading them into IP reveals that it uses quite complicated HDF5 data types like compounds with object references in them. And I don't think you can write those from within IP. There is also no support for the HDF5 dimension scales in IP which avoids requiring you to manage these low level data types.
But this is just me guessing, I propose to reach out to WM support.
I still think though that netCDF4 files are 100% standard HDF5.
February 9, 2021 at 05:29 am - Permalink
@thomas_braun
Yes, it's the dimension scales that are the sticking point. I think the datatypes that igor can't produce are not allowed in netCDF anyway, so that solves that problem. Writing netCDFs is possible indirectly via cdl as you have done, and I do this regularly. In that case ncgen handles the niceties of dimensionality. As dimension scales are a part of HDF5 it would be nice to be able to use them in Igor and by extension be able to make netCDF-4 files directly, and also to modify then directly without having to load the whole thing into memory.
Reading netCDFs isn't a problem FYI, I haven't found one that I couldn't nicely parse using some combination of HDF5.xop, netCDF.xop or nc_load.xop.
February 9, 2021 at 07:28 am - Permalink
> Reading netCDFs isn't a problem FYI, I haven't found one that I couldn't nicely parse using some combination of HDF5.xop, netCDF.xop or nc_load.xop.
But how do you read the dimension scales? The HDF5 xop AFAIU is not able to read them.
February 12, 2021 at 02:13 pm - Permalink
> The HDF5 xop AFAIU is not able to read them.
Much to my chagrin... you could parse them out of the HDF5dump it has just (re-)occurred to me, I had some horrible kluge to get all the metadata this way but retired it - maybe I'll look again.
EDIT: get dimensions with HDF5dump is slooooow.
If the netCDF was made with a netCDF API of some description there will be hidden/packed internal attributes _Netcdf4Dimid and _Netcdf4Coordinates which contain all the information you need to get the dimensions. Spat out of python code though, not so much.
The old nc_load.xop doesn't return enough info to get the dimensions if you have more than 1! That was written in 1999 though and I don't use it any longer but have written a basic parser that will use it if you're using Igor <8 for users of my data.
February 15, 2021 at 04:26 am - Permalink
Something like this hastily hacked together example:
String dimStr = getDimensions(S_HDF5Dump)
//___________________________________________________________________________________________________
// return a comma separated list of dimensions assoicated with HDF5 dataset
Function/S getDimensions(String dumpStr)
variable i
string dimStr, lineStr, outstr=""
for (i=0;i<ItemsInList(dumpStr,"\r");i+=1)
lineStr = StringfromList(i,dumpStr,"\r")
if (stringmatch(lineStr,"*DATASET*"))
do
SplitString/E=".*\"/(.*)\".*" lineStr,dimStr
outStr = AddListItem(dimStr,outstr)
lineStr=replacestring("\"/"+dimStr+"\"",lineStr,"")
while (strlen(dimStr)>0)
Break
endif
endfor
outstr=ReplaceString(";",RemoveEnding(outstr),", ")[1,inf]
return outstr
end
Igor can interpret the dimensions from HDF5 in some way as it dumps them in the HDF5 browser, but no tool is provided to load them directly and it isn't clear why - something about the datatype being VLEN, though I stopped reading at the point in the docs where it said it wasn't possible.
This is way off track now....
February 15, 2021 at 06:14 am - Permalink
Update:
hrodstein has added a HDF5DimensionScale operation to the Igor Pro 9 beta which allows you to query and write HDF5 dimension scales. This by extension will allow you to make fully fledged netCDF-4 files directly from Igor 9 now easily.
Many thanks to Howard.
EDIT: below is a simple example that creates a file with most of the netCDF-4 features utilised.
int fileID, groupID
Make/O/I xWave=x // unlimited co-ordinate variable
Make/O/N=(128,2) yWave=gnoise(x) // dataset
Make/O/N=2/B/U zWave=x+1 // hidden co-ordinate variable
Make/O/N=(128,2,2) aWave=gnoise(x^2) // dataset
Make/O/N=2/B/U bWave=x+1 // hidden co-ordinate variable
// get a path to make the netCDF file in
NewPath/O/Q/Z/M= "Please select a folder to outpt a file to" dataFilePath
// create the netCDF file
HDF5CreateFile/O/P=dataFilePath fileID as outfNameStr+".nc"
// output global attributes
Make/O /T /FREE /N=1 StrAttribute = "This is my netCDF"
HDF5SaveData /O /A="title" StrAttribute, fileID, "/"
// save xWave as unlimited
HDF5SaveData /IGOR=0 /LAYO={2,32,32} /MAXD={-1} xWave, fileID
HDF5DimensionScale dataset={fileID,"xWave"},dimName="xWave" , setScale
Make /O /FREE /N=1 VarAttribute=0
HDF5SaveData /O /A="_Netcdf4Dimid" VarAttribute, fileID, "xWave"
Make/O /T /FREE /N=1 StrAttribute = "this is the X dimesnion"
HDF5SaveData /O /A="comment" StrAttribute, fileID, "xWave"
// save zWave as hidden co-ordinate - tell the netCDF API this with the dimName
HDF5SaveData /IGOR=0 zWave, fileID
HDF5DimensionScale dataset={fileID,"zWave"}, dimName="This is a netCDF dimension but not a netCDF variable.\t\t"+num2istr(numpnts(zWave)), setScale
Make /O /FREE /N=1 VarAttribute=1
HDF5SaveData /O /A="_Netcdf4Dimid" VarAttribute, fileID, "zWave"
// save yWave variable
HDF5SaveData /IGOR=0 yWave, fileID
HDF5DimensionScale scale={fileID,"xWave"}, dataset={fileID,"yWave"}, dimIndex=0, attachScale
HDF5DimensionScale scale={fileID,"zWave"}, dataset={fileID,"yWave"}, dimIndex=1, attachScale
Make/O /T /FREE /N=1 StrAttribute = "this a data variable"
HDF5SaveData /O /A="comment" StrAttribute, fileID, "yWave"
// create a group in the file
HDF5CreateGroup fileID, "/sub-group", groupID
// output group attributes
Make/O /T /FREE /N=1 StrAttribute = "This is my group"
HDF5SaveData /O /A="title" StrAttribute, groupID , "/sub-group"
// save bWave as hidden co-ordinate - tell the netCDF API this with the dimName
HDF5SaveData/IGOR=0 bWave, groupID
HDF5DimensionScale dataset={groupID ,"bWave"}, dimName="This is a netCDF dimension but not a netCDF variable.\t\t"+num2istr(numpnts(zWave)), setScale
Make /O /FREE /N=1 VarAttribute=3
HDF5SaveData /O /A="_Netcdf4Dimid" VarAttribute, groupID , "bWave"
// save aWave to the group
HDF5SaveData/IGOR=0 aWave, groupID
HDF5DimensionScale scale={fileID,"xWave"}, dataset={groupID,"aWave"}, dimIndex=0, attachScale
HDF5DimensionScale scale={fileID,"zWave"}, dataset={groupID,"aWave"}, dimIndex=1, attachScale
HDF5DimensionScale scale={groupID,"bWave"}, dataset={groupID,"aWave"}, dimIndex=2, attachScale
Make/O /T /FREE /N=1 StrAttribute = "this another data variable"
HDF5SaveData /O /A="comment" StrAttribute, groupID, "aWave"
// close the file
HDF5CloseFile fileID
// clean up
KillWaves/Z xWave,yWave,zWave,aWave,bWave
end
March 9, 2021 at 03:23 am - Permalink