source: sans/Dev/trunk/NCNR_User_Procedures/Reduction/HDF5gateway_NCNR.ipf

Last change on this file was 1058, checked in by srkline, 5 years ago

changes to HDFLoadGroup to use the new R=2 flag to allow reading of duplicated groups in the data file (the temperature log). Requires Igor 7.05+ and HDF5XOP 1.24+

File size: 44.1 KB
Line 
1#pragma rtGlobals=3             // Use modern global access method.
2
3#include <HDF5 Browser>
4
5//
6// !!! search for "SRK" to see what I have modified to work with NCNR files
7// -- these changes may only be for testing, and may need to be removed in the final version!!!
8//
9//
10//
11
12
13// requires the Wavemetrics "HDF5.xop" to be installed for IgorPro
14
15// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
16// $Id: HDF5gateway.ipf 768 2012-11-26 04:48:16Z svnsmang $
17// This file is part of a project hosted at the Advanced Photon Source.
18// Access all the source files and data files by checking out the project here:
19//   svn co https://subversion.xray.aps.anl.gov/small_angle/hdf5gateway/trunk hdf5gateway
20// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
21// The documentation is written in restructured text for use by Sphinx.
22// A python program will read this file and grab all the text between lines
23// beginning with "//@+" and "//@-" that begin with "//\t" and use them for the Sphinx documentation.
24// The tables here have been adjusted for a fixed width font.  Don't "fix" them in Igor!
25
26//@+
27//      ============================================================
28//      HDF5gateway: HDF5 File I/O Support
29//      ============================================================
30//     
31//      Version: 1.0
32//     
33//      HDF5gateway makes it easy to read
34//      a HDF5 file into an IgorPro folder,
35//      including group and dataset attributes,
36//      such as a NeXus data file,
37//      modify it, and then write it back out.
38//     
39//      .. index:: goal
40//     
41//      The goal was to make it easy to read a HDF5 file into an IgorPro folder,
42//      including group and dataset attributes,
43//      such as a NeXus data file,
44//      modify it, and then write it back out.
45//      This file provides functions to do just that.
46//     
47//      .. index:: functions; public
48//     
49//      Starting with utilities provided in the *HDF5 Browser* package, this file provides
50//      these public functions:
51//     
52//              * :ref:`H5GW_ReadHDF5`
53//              * :ref:`H5GW_WriteHDF5`
54//              * :ref:`H5GW_ValidateFolder`
55//     
56//      and this function which is useful only for testing and development:
57//     
58//              * :ref:`H5GW_TestSuite`
59//     
60//      Help is provided with each of these functions to indicate their usage.
61//     
62//      .. index:: read
63//     
64//      Reading
65//      ===========
66//     
67//      An HDF5 file is read into an IgorPro data folder in these steps:
68//     
69//      #. The groups and datasets are read and stored into an IgorPro folder.
70//      #. Any attributes of these groups and datasets are read and assigned to IgorPro objects.
71//     
72//      .. index:: home
73//     
74//      The data file is expected to be in the *home* folder (the folder specified by IgorPro's *home* path),
75//      or relative to that folder, or given by an absolute path name.
76//     
77//      .. index:: write, HDF5___xref
78//     
79//      Writing
80//      =================
81//     
82//      An IgorPro data folder is written to an HDF5 file in these steps:
83//     
84//      #. The IgorPro folder is validated for correct structure.
85//      #. The objects in the *HDF5___xref* text wave are written to the HDF5 file.
86//      #. Any folder attributes or wave notes are written to the corresponding HDF5 data path.
87//     
88//      The data file is expected to be in the *home* folder (the folder specified by IgorPro's *home* path),
89//      or relative to that folder, or given by an absolute path name.
90//     
91//      .. index:: validate
92//     
93//      Validating
94//      =================
95//     
96//      Call :ref:`H5GW_ValidateFolder` to test if the
97//      *parentFolder* in the IgorPro Data Browser has the proper structure to
98//      successfully write out to an HDF5 file by :ref:`H5GW_WriteHDF5`.
99//     
100//      .. index:: ! HDF5___xref
101//     
102//      Structure of the *HDF5___xref* text wave
103//      =====================================================
104//     
105//      It is necessary to devise a method to correlate the name
106//      of the same object in the HDF5 file with its representation in
107//      the IgorPro data structure.   In IgorPro, certain names are
108//      reserved such that objects cannot be named.  Routines exist
109//      to substitute such names on data import to comply with
110//      these restrictions.  The routine *HDF5LoadGroup* performs
111//      this substitution automatically, yet no routine is provided to
112//      describe any name substitutions performed.
113//     
114//      The text wave, *HDF5___xref*, is created in the base folder of
115//      the IgorPro folder structure to describe the mapping between
116//      relative IgorPro and HDF5 path names, as shown in the next table.
117//      This name was chosen in hopes that it might remain unique
118//      and unused by others at the root level HDF5 files.
119//     
120//              HDF5___xref wave column plan
121//             
122//              =======   ==================
123//              column    description
124//              =======   ==================
125//              0         HDF5 path
126//              1         Igor relative path
127//              =======   ==================
128//     
129//              **Example**
130//             
131//              Consider the HDF5 file with datasets stored in this structure:
132//     
133//              .. code-block:: guess
134//                      :linenos:
135//                     
136//                      /
137//                        /sasentry01
138//                          /sasdata01
139//                            I
140//                            Q
141//     
142//              The next table shows the contents of *HDF5___xref* once this
143//              HDF5 is read by *H5GW_WriteHDF5()*:
144//             
145//              ===  =======================  ==========================
146//              row  ``HDF5___xref[row][0]``  ``HDF5___xref[row][1]``
147//              ===  =======================  ==========================
148//              0    /                        :
149//              1    /sasentry01              :sasentry01
150//              2    /sasentry01/sasdata01    :sasentry01:sasdata01
151//              3    /sasentry01/sasdata01/I  :sasentry01:sasdata01:I0
152//              4    /sasentry01/sasdata01/Q  :sasentry01:sasdata01:Q0
153//              ===  =======================  ==========================
154//     
155//              Remember, column 0 is for HDF5 paths, column 1 is for IgorPro paths.
156//     
157//      On reading an HDF5 file, the *file_name* and *file_path* are written to the
158//      wave note of *HDF5___xref*.  These notations are strictly informative and
159//      are not used further by this interface.  When writing back to HDF5, any
160//      wave notes of the *HDF5___xref* wave are ignored.
161//     
162//              .. rubric::  About *HDF5___xref*:
163//     
164//              * Only the folders and waves listed in the *HDF5___xref* text
165//                wave will be written to the HDF5 file.
166//              * The *HDF5___xref* text wave is **not written** to the HDF5 file.
167//     
168//      When writing an HDF5 file with these functions,
169//      based on the structure expected in an IgorPro data folder structure,
170//      the *HDF5___xref* text wave is required.  Each IgorPro object described
171//      must exist as either an IgorPro folder or wave.  A wave note is optional.
172//      For each such IgorPro object, a corresponding HDF5 file object will be created.
173//     
174//      .. note:: Important!  Any IgorPro data storage objects (folders or waves)
175//         not listed in *HDF5___xref* **will not be written** to the HDF5 file.
176//     
177//      .. index:: group
178//      .. index:: folder
179//     
180//      Groups and Folders
181//      =====================
182//     
183//      An HDF5 *group* corresponds to the IgorPro *folder*.  Both are containers
184//      for either data or containers. 
185//     
186//      .. index:: Igor___folder_attributes
187//     
188//      In HDF5, a group may have attached metadata
189//      known as *attributes*.  In IgorPro, folders have no provision to store 
190//      attributes, thus an optional *Igor___folder_attributes* wave is created.  The
191//      folder attributes are stored in the wave note of this wave.  For more information
192//      about attributes, see the discussion of :ref:`attributes` below.
193//     
194//      .. index:: datasets
195//      .. index:: waves
196//     
197//      Datasets and Waves
198//      ======================
199//     
200//      Data is stored in HDF5 datasets and IgorPro waves. 
201//      Both objects are capable of storing a variety of data types
202//      with different shapes (rank and length).  Of the two systems,
203//      IgorPro is the more restrictive, limiting the rank of stored data
204//      to four dimensions.
205//     
206//      Keep in mind that all components of a single dataset (or wave) are
207//      of the same data type (such as 64-bit float or 8-bit int).
208//     
209//      In HDF5, a dataset may have attached metadata known as
210//      *attributes*.  HDF5 attributes are data structures in their own
211//      right and may contain data structures.  In IgorPro, waves have
212//      a provision to store  attributes in a text construct called the *wave note*. 
213//      Of these two, IgorPro is the more restrictive, unless one creates
214//      a new wave to hold the data structure of the attributes.
215//      For more information
216//      about attributes, see the discussion of :ref:`attributes` below.
217//     
218//      The HDF5 library used by this package will take care of converting
219//      between HDF5 datasets and IgorPro waves and the user need
220//      not be too concerned about this.
221//     
222//     
223//      .. index:: attributes
224//      .. index:: ! Igor___folder_attributes
225//     
226//      .. _attributes:
227//     
228//      Attributes and Wave Notes
229//      ============================================
230//     
231//      Metadata about each of the objects in HDF5 files and IgorPro folders
232//      is provided by *attributes*.  In HDF5, these are attributes directly attached
233//      to the object (group or dataset).  In IgorPro, these attributes are **stored as text** in
234//      different places depending on the type of the object, as shown in this table:
235//     
236//              ========   =======================================================
237//              object     description
238//              ========   =======================================================
239//              folder     attributes are stored in the wave note of a special
240//                         wave in the folder named *Igor___folder_attributes*
241//              wave       attributes are stored in the wave note of the wave
242//              ========   =======================================================
243//     
244//      .. note:: IgorPro folders do not have a *wave note*
245//     
246//      HDF5 allows an attribute to be a data structure with the same rules for
247//      complexity as a dataset except that attributes must be attached to a dataset
248//      and cannot themselves have attributes.
249//     
250//      .. note:: In IgorPro, attributes will be stored as text.
251//     
252//      An IgorPro wave note is a text string that is used here to store a list of
253//      *key,value* pairs.  IgorPro provides helpful routines to manipulate such
254//      lists, especially when used as wave notes.  The IgorPro wave note is the most
255//      natural representation of an *attribute* except that it does not preserve
256//      the data structure of an HDF5 attribute without additional coding.  This
257//      limitation is deemed acceptable for this work. 
258//     
259//      It is most obvious to see
260//      the conversion of attributes into text by reading and HDF5 file and then
261//      writing it back out to a new file.  The data type of the HDF5 attributes will
262//      likely be changed from its original type into "string, variable length".  If this
263//      is not acceptable, more work must be done in the routines below.
264//     
265//      IgorPro key,value list for the attributes
266//      ----------------------------------------------------------------------------------------
267//     
268//      Attributes are represented in IgorPro wave notes using a
269//      list of *key,value* pairs.  For example:
270//     
271//              .. code-block:: guess
272//                      :linenos:
273//     
274//                      NX_class=SASdata
275//                      Q_indices=0,1
276//                      I_axes=Q,Q
277//                      Mask_indices=0,1
278//     
279//      It is important to know the delimiters used by this string to
280//      differentiate various attributes, some of which may have a
281//      list of values.  Please refer to this table:
282//     
283//              ===========  ====  ==========================================
284//              separator    char  description
285//              ===========  ====  ==========================================
286//              keySep       =     between *key* and *value*
287//              itemSep      ,     between multiple items in *value*
288//              listSep      \\r   between multiple *key,value* pairs
289//              ===========  ====  ==========================================
290//     
291//      .. note::  A proposition is to store these values in a text wave
292//         at the base of the folder structure and then use these value
293//         throughout the folder.  This can allow some flexibility with other
294//         code and to make obvious which terms are used.
295//     
296//      .. index:: example
297//     
298//      Examples
299//      ====================
300//     
301//      Export data from IgorPro
302//      -------------------------------------------------------
303//     
304//      To write a simple dataset *I(Q)*, one might write this IgorPro code:
305//     
306//              .. code-block:: guess
307//                      :linenos:
308//                     
309//                      // create the folder structure
310//                      NewDataFolder/O/S root:mydata
311//                      NewDataFolder/O sasentry
312//                      NewDataFolder/O :sasentry:sasdata
313//     
314//                      // create the waves
315//                      Make :sasentry:sasdata:I0
316//                      Make :sasentry:sasdata:Q0
317//     
318//                      Make/N=0 Igor___folder_attributes
319//                      Make/N=0 :sasentry:Igor___folder_attributes
320//                      Make/N=0 :sasentry:sasdata:Igor___folder_attributes
321//     
322//                      // create the attributes
323//                      Note/K Igor___folder_attributes, "producer=IgorPro\rNX_class=NXroot"
324//                      Note/K :sasentry:Igor___folder_attributes, "NX_class=NXentry"
325//                      Note/K :sasentry:sasdata:Igor___folder_attributes, "NX_class=NXdata"
326//                      Note/K :sasentry:sasdata:I0, "units=1/cm\rsignal=1\rtitle=reduced intensity"
327//                      Note/K :sasentry:sasdata:Q0, "units=1/A\rtitle=|scattering vector|"
328//     
329//                      // create the cross-reference mapping
330//                      Make/T/N=(5,2) HDF5___xref
331//                      Edit/K=0 'HDF5___xref';DelayUpdate
332//                      HDF5___xref[0][1] = ":"
333//                      HDF5___xref[1][1] = ":sasentry"
334//                      HDF5___xref[2][1] = ":sasentry:sasdata"
335//                      HDF5___xref[3][1] = ":sasentry:sasdata:I0"
336//                      HDF5___xref[4][1] = ":sasentry:sasdata:Q0"
337//                      HDF5___xref[0][0] = "/"
338//                      HDF5___xref[1][0] = "/sasentry"
339//                      HDF5___xref[2][0] = "/sasentry/sasdata"
340//                      HDF5___xref[3][0] = "/sasentry/sasdata/I"
341//                      HDF5___xref[4][0] = "/sasentry/sasdata/Q"
342//     
343//                      // Check our work so far.
344//                      // If something prints, there was an error above.
345//                      print H5GW_ValidateFolder("root:mydata")
346//     
347//                      // set I0 and Q0 to your data
348//             
349//                      print H5GW_WriteHDF5("root:mydata", "mydata.h5")
350//     
351//      .. index:: read
352//     
353//      Read data into IgorPro
354//      -------------------------------------------------------
355//     
356//      .. index:: example
357//     
358//      This is a simple operation, reading the file from the previous example into a new folder:
359//     
360//              .. code-block:: guess
361//                      :linenos:
362//                     
363//                      NewDataFolder/O/S root:newdata
364//                      H5GW_ReadHDF5("", "mydata.h5")  // reads into current folder
365//@-
366
367//  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //
368
369
370//@+
371//      .. index:: read
372//     
373//      Public Functions
374//      ======================================
375//     
376//      .. index:: ! H5GW_ReadHDF5()
377//     
378//      .. _H5GW_ReadHDF5:
379//     
380//      H5GW_ReadHDF5(parentFolder, fileName, [hdf5Path])
381//      -------------------------------------------------------------------------------------------------------------
382//     
383//      Read the HDF5 data file *fileName* (located in directory *data*,
384//      an IgorPro path variable) and store it in a subdirectory of
385//      IgorPro folder *parentFolder*.
386//     
387//      At present, the *hdf5Path* parameter is not used.  It is planned
388//      (for the future) to use this to indicate reading only part of the
389//      HDF5 file to be read.
390//     
391//      :String parentFolder: Igor folder path (default is current folder)
392//      :String fileName: name of file (with extension),
393//                      either relative to current file system directory,
394//                      or include absolute file system path
395//      :String hdf5Path: path of HDF file to load (default is "/")
396//              :return String: Status: ""=no error, otherwise, error is described in text
397//@-
398
399Function/S H5GW_ReadHDF5(parentFolder, fileName, [hdf5Path])
400        String parentFolder, fileName, hdf5Path
401        if ( ParamIsDefault(hdf5Path) )
402                hdf5Path = "/"
403        endif
404
405        String status = ""
406        String oldFolder
407        oldFolder = GetDataFolder(1)
408        parentFolder = H5GW__SetStringDefault(parentFolder, oldFolder)
409
410        // First, check that parentFolder exists
411        if ( DataFolderExists(parentFolder) )
412                SetDataFolder $parentFolder
413        else
414                return parentFolder + " (Igor folder) not found"
415        endif
416
417
418        // do the work here:
419        Variable/G fileID = H5GW__OpenHDF5_RO(fileName)
420        if ( fileID == 0 )
421                return fileName + ": could not open as HDF5 file"
422        endif
423       
424//v_tic()               //fast
425       
426        SVAR tmpStr=root:file_name
427        fileName=tmpStr         //SRK - in case the file was chosen from a dialog
428       
429        //   read the data (too bad that HDF5LoadGroup does not read the attributes)
430        String base_name = StringFromList(0,FileName,".")
431        // SRK - added /R=2 flag to allow duplicate groups to be loaded (Jul 2017)
432        HDF5LoadGroup/Z/L=7/O/R=2/T=$base_name  :, fileID, hdf5Path             //      recursive
433        if ( V_Flag != 0 )
434                SetDataFolder $oldFolder
435                return fileName + ": problem while opening HDF5 file"
436        endif
437
438//v_toc()
439       
440//v_tic()               // this is the slow part, 0.7s for Igor-generated. > 9s for NICE (which has DAS_log)
441
442        String/G objectPaths = S_objectPaths  // this gives a clue to renamed datasets (see below for attributes)
443        //   read the attributes
444        H5GW__HDF5ReadAttributes(fileID, hdf5Path, base_name)
445        HDF5CloseFile fileID
446
447        String/G file_path
448        String/G group_name_list
449        String/G dataset_name_list
450        String file_info
451        sprintf file_info, ":%s:HDF5___xref", base_name
452        Note/K $file_info, "file_name="+fileName
453        Note $file_info, "file_path="+file_path
454
455        KillStrings/Z file_path, objectPaths, group_name_list, dataset_name_list // ,file_name
456        KillVariables/Z fileID
457
458//v_toc()
459       
460        SetDataFolder $oldFolder
461        return status
462End
463
464
465// ======================================
466//@+
467//      .. index:: write
468//      .. index:: ! H5GW_WriteHDF5()
469//     
470//      .. _H5GW_WriteHDF5:
471//     
472//      H5GW_WriteHDF5(parentFolder, newFileName)
473//      -------------------------------------------------------------------------------------------------------------
474//     
475//      Starting with an IgorPro folder constructed such that it passes the :ref:`H5GW_ValidateFolder` test,
476//      write the components described in *HDF5___xref* to *newFileName*.
477//     
478//      :String parentFolder: Igor folder path (default is current folder)
479//      :String fileName: name of file (with extension),
480//                      either relative to current file system directory,
481//                      or include absolute file system path
482//@-
483Function/T H5GW_WriteHDF5(parentFolder, newFileName, [replace])
484        String parentFolder, newFileName
485        Variable replace
486        if ( ParamIsDefault(replace) )
487                replace = 1
488        endif
489
490        String status = ""
491        String oldFolder = GetDataFolder(1)
492
493        // First, check that parentFolder exists
494        status = H5GW_ValidateFolder(parentFolder)
495        if ( strlen(status) > 0 )
496                return status
497        endif
498        SetDataFolder $parentFolder
499       
500        // Build HDF5 group structure
501        Variable fileID = H5GW__OpenHDF5_RW(newFileName, replace)
502        if (fileID == 0)
503                SetDataFolder $oldFolder
504                return "Could not create HDF5 file " + newFileName + " for writing"
505        endif
506
507        // write datasets and attributes based on HDF5___xref table
508        status = H5GW__WriteHDF5_Data(fileID)
509        if ( strlen(status) > 0 )
510                HDF5CloseFile fileID
511                SetDataFolder $oldFolder
512                return status
513        endif
514       
515        HDF5CloseFile fileID
516
517        SetDataFolder $oldFolder
518        return status                   // report success
519End
520
521
522
523// ======================================
524//@+
525//      .. index:: validate
526//      .. index:: ! H5GW_ValidateFolder()
527//     
528//      .. _H5GW_ValidateFolder:
529//     
530//      H5GW_ValidateFolder(parentFolder)
531//      -------------------------------------------------------------------------------------------------------------
532//     
533//      Check (validate) that a given IgorPro folder has the necessary
534//      structure for the function H5GW__WriteHDF5_Data(fileID) to be
535//      successful when writing that folder to an HDF5 file.
536//     
537//              :String parentFolder: Igor folder path (default is current folder)
538//              :return String: Status: ""=no error, otherwise, error is described in text
539//@-
540
541Function/T H5GW_ValidateFolder(parentFolder)
542        String parentFolder
543        String oldFolder = GetDataFolder(1)
544        // First, check that parentFolder exists
545        if ( DataFolderExists(parentFolder) )
546                SetDataFolder $parentFolder
547        else
548                return parentFolder + " (Igor folder) not found"
549        endif
550
551        if (1 != Exists("HDF5___xref"))
552                SetDataFolder $oldFolder
553                return "required wave (HDF5___xref) is missing in folder: " + parentFolder
554        endif
555        Wave/T HDF5___xref
556        if ( DimSize(HDF5___xref, 1) != 2 )
557                SetDataFolder $oldFolder
558                return "text wave HDF5___xref must be of shape (N,2)"
559        endif
560
561        Variable length = DimSize(HDF5___xref, 0), ii
562        String item, msg
563        for (ii=0; ii < length; ii=ii+1)
564                item = HDF5___xref[ii][1]
565                if ( (1 != DataFolderExists(item)) && (1 != Exists(item)) )
566                        SetDataFolder $oldFolder
567                        return "specified IgorPro object " + item + " was not found in folder " + parentFolder
568                endif
569                // TODO: Check that each corresponding HDF5___xref[ii][0] is a valid HDF5 path name
570                if ( itemsInList(item, ":") != itemsInList(HDF5___xref[ii][0], "/") )
571                        SetDataFolder $oldFolder
572                        msg = "different lengths between HDF5 and IgorPro paths on row" + num2str(ii) + "of HDF5___xref"
573                        return msg
574                endif
575        endfor
576       
577        // TODO: more validation steps
578
579        SetDataFolder $oldFolder
580        return ""
581End
582
583
584// ======================================
585//@+
586//      .. index:: test
587//      .. index:: ! H5GW_TestSuite()
588//     
589//      .. _H5GW_TestSuite:
590//     
591//      H5GW_TestSuite()
592//      -------------------------------------------------------------------------------------------------------------
593//     
594//      Test the routines in this file using the supplied test data files.
595//      HDF5 data files are obtained from the canSAS 2012 repository of
596//      HDF5 examples
597//      (http://www.cansas.org/formats/canSAS2012/1.0/doc/_downloads/simpleexamplefile.h5).
598//@-
599
600Function H5GW_TestSuite()
601        String listSep = ";"
602        String fileExt = ".h5"
603        String parentDir = "root:worker"
604
605        String name_list = "simpleexamplefile"
606        name_list = name_list +listSep + "simple2dcase"
607        name_list = name_list +listSep + "simple2dmaskedcase"
608        name_list = name_list +listSep + "generic2dqtimeseries"
609        name_list = name_list +listSep + "generic2dtimetpseries"
610
611        Variable length = itemsInList(name_list, listSep), ii
612        String name, newName, newerName
613        for (ii = 0; ii < length; ii = ii + 1)
614                name = StringFromList(ii, name_list, listSep) + fileExt
615                // Test reading the HDF5 file and then writing the data to a new HDF5 file
616                newName = H5GW__TestFile(parentDir, name)
617                // Apply the test again on the new HDF5 file
618                newerName = H5GW__TestFile(parentDir, newName)
619        endfor
620End
621
622
623//  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //
624
625
626// ======================================
627//@+
628//      .. index:: functions; private (static)
629//     
630//      Private (static) Functions
631//      ======================================
632//     
633//      Documentation of some, but not all, private functions is provided.
634//     
635//@-
636
637
638//@+
639//      .. index:: ! H5GW__OpenHDF5_RW()
640//     
641//      H5GW__OpenHDF5_RW(newFileName, replace)
642//      -------------------------------------------------------------------------------------------------------------
643//@-
644static Function H5GW__OpenHDF5_RW(newFileName, replace)
645        String newFileName
646        Variable replace
647        Variable fileID
648        if (replace)
649                HDF5CreateFile/P=home/Z/O fileID as newFileName
650        else
651                // make sure file does not exist now, or handle better
652                HDF5CreateFile/P=home/Z fileID as newFileName
653        endif
654        if (V_Flag != 0)
655                return 0
656        endif
657        return fileID
658End
659
660
661//@+
662//      .. index:: ! H5GW__WriteHDF5_Data()
663//     
664//      H5GW__WriteHDF5_Data(fileID)
665//      -------------------------------------------------------------------------------------------------------------
666//@-
667static Function/T H5GW__WriteHDF5_Data(fileID)
668        Variable fileID
669        String status = ""
670       
671        Wave/t xref = HDF5___xref
672        Variable HDF5_col = 0
673        Variable Igor_col = 1
674        Variable rows = DimSize(xref,0), ii, groupID, length, jj
675        String igorPath, hdf5Path, dataType, folder_attr_info, notes, item, key, value
676        for (ii = 0; ii < rows; ii=ii+1)
677                igorPath = xref[ii][Igor_col]
678                hdf5Path = xref[ii][HDF5_col]
679                // print DataFolderExists(igorPath), igorPath, " --> ", hdf5Path
680                if ( DataFolderExists(igorPath) )
681                        // group
682                        if ( cmpstr("/", hdf5Path) != 0 )
683                                HDF5CreateGroup /Z fileID , hdf5Path, groupID
684                                if (V_Flag != 0)
685                                        status = H5GW__AppendString(status, "\r",  "problem creating HDF5 group: " + hdf5Path)
686                                endif
687                        else
688                                groupID = fileID
689                        endif
690                        // attributes
691                        notes = ""
692                        folder_attr_info = H5GW__appendPathDelimiter(igorPath, ":") + "Igor___folder_attributes"
693                        Wave/Z folder_attr = $folder_attr_info
694                        // SRK - folder_attr wave may not exist if HDF file not written out by HDF5Gateway. Handle the error
695                        if(WaveExists(folder_attr))
696                                notes = note(folder_attr)
697                                length = itemsInList(notes, "\r")
698                                String response
699                                if ( length > 0 )
700                                        for (jj = 0; jj < length; jj=jj+1 )
701                                                item = StringFromList(jj, notes,"\r")
702                                                key = StringFromList(0, item,"=")
703                                                value = StringFromList(1, item,"=")
704                                                response = H5GW__SetTextAttributeHDF5(fileID, key, value, hdf5Path)
705                                                status = H5GW__AppendString(status, "\r",  response)
706                                        endfor
707                                endif
708                        endif   // SRK check if folder_attr exists
709                else
710                        // dataset
711                        Wave theWave = $igorPath
712                        HDF5SaveData/IGOR=0/Z theWave, fileID, hdf5Path
713                        if (V_Flag != 0)
714                                status = H5GW__AppendString(status, "\r",  "problem saving HDF5 dataset: " + hdf5Path)
715                        endif
716
717                        // look at the wave note for any attributes
718                        notes = note(theWave)
719                        length = itemsInList(notes, "\r")
720                        if ( length > 0 )
721                                for (jj = 0; jj < length; jj=jj+1 )
722                                        item = StringFromList(jj, notes,"\r")
723                                        key = StringFromList(0, item,"=")
724                                        value = StringFromList(1, item,"=")
725                                        H5GW__SetTextAttributeHDF5(fileID, key, value, hdf5Path)
726                                endfor
727                        endif
728
729                endif
730        endfor
731       
732        return status
733End
734
735
736//@+
737//      .. index:: ! H5GW__SetHDF5ObjectAttributes()
738//     
739//      H5GW__SetHDF5ObjectAttributes(itemID, igorPath, hdf5Path)
740//      -------------------------------------------------------------------------------------------------------------
741//@-
742static Function H5GW__SetHDF5ObjectAttributes(itemID, igorPath, hdf5Path)
743        Variable itemID
744        String igorPath, hdf5Path
745        Wave theWave = $igorPath
746        String notes = note(theWave), item, key, value
747        Variable jj, length = itemsInList(notes, "\r")
748        notes = note(theWave)
749        length = itemsInList(notes, "\r")
750        if ( length > 0 )
751                for (jj = 0; jj < length; jj=jj+1 )
752                        item = StringFromList(jj, notes,"\r")
753                        key = StringFromList(0, item,"=")
754                        value = StringFromList(1, item,"=")
755                        H5GW__SetTextAttributeHDF5(itemID, key, value, hdf5Path)
756                endfor
757        endif
758End
759
760
761//@+
762//      .. index:: ! H5GW__SetTextAttributeHDF5()
763//     
764//      H5GW__SetTextAttributeHDF5(itemID, name, value, hdf5Path)
765//      -------------------------------------------------------------------------------------------------------------
766//@-
767static Function/T H5GW__SetTextAttributeHDF5(itemID, name, value, hdf5Path)
768        Variable itemID
769        String name, value, hdf5Path
770        String status = ""
771        Make/T/N=(1) H5GW____temp
772        H5GW____temp[0] = value
773        HDF5SaveData/Z/A=name   H5GW____temp, itemID, hdf5Path
774        if ( V_Flag != 0)
775                status = "problem saving HDF5 text attribute: " + hdf5Path
776        endif
777        KillWaves  H5GW____temp
778        return status
779End
780
781
782// ======================================
783//@+
784//      .. index:: ! H5GW__make_xref()
785//     
786//      H5GW__make_xref(parentFolder, objectPaths, group_name_list, dataset_name_list, base_name)
787//      ---------------------------------------------------------------------------------------------------------------------------------------------------------
788//     
789//      Analyze the mapping between HDF5 objects and Igor paths
790//      Store the discoveries of this analysis in the HDF5___xref text wave
791//     
792//              :String parentFolder: Igor folder path (default is current folder)
793//              :String objectPaths: Igor paths to data objects
794//              :String group_name_list:
795//              :String dataset_name_list:
796//              :String base_name:
797//     
798//      HDF5___xref wave column plan
799//     
800//              ======   ===============================
801//              column   description
802//              ======   ===============================
803//              0        HDF5 path
804//              1        Igor relative path
805//              ======   ===============================
806//@-
807
808static Function H5GW__make_xref(parentFolder, objectPaths, group_name_list, ds_list, base_name)
809        String parentFolder, objectPaths, group_name_list, ds_list, base_name
810       
811        String xref = ""                // key.value pair list as a string
812        String keySep = "="     // between key and value
813        String listSep = "\r"   // between key,value pairs
814       
815        String matchStr = parentFolder + base_name
816        String igorPaths = ReplaceString(matchStr, objectPaths, "")
817       
818        Variable ii, length
819        String dataset, igorPath
820        // compare items in ds_list and igorPaths
821        length = itemsInList(ds_list, ";")
822        if ( length == itemsInList(igorPaths, ";") )
823                for (ii = 0; ii < length; ii = ii + 1)
824                        // ASSUME this is the cross-reference list we need
825                        dataset = StringFromList(ii, ds_list, ";")
826                        igorPath = StringFromList(ii, igorPaths, ";")
827                        xref = H5GW__addPathXref(parentFolder, base_name, dataset, igorPath, xref, keySep, listSep)
828                endfor
829        else
830                // TODO: report an error here and return
831        endif
832       
833        // finally, write the xref contents to a wave
834        length = itemsInList(xref, listSep)
835        String file_info
836        sprintf file_info, ":%s:HDF5___xref", base_name
837        Make/O/N=(length,2)/T $file_info
838        Wave/T file_infoT = $file_info
839        String item
840        Variable HDF5_col = 0
841        Variable Igor_col = 1
842        for (ii = 0; ii < length; ii=ii+1)
843                item = StringFromList(ii, xref, listSep)
844                file_infoT[ii][HDF5_col] = StringFromList(0, item, keySep)
845                file_infoT[ii][Igor_col] = StringFromList(1, item, keySep)
846        endfor
847
848End
849
850
851//@+
852//      .. index:: ! H5GW__addPathXref()
853//     
854//      H5GW__addPathXref(parentFolder, base_name, hdf5Path, igorPath, xref, keySep, listSep)
855//      ----------------------------------------------------------------------------------------------------------------------------------------------------------
856//@-
857static Function/T H5GW__addPathXref(parentFolder, base_name, hdf5Path, igorPath, xref, keySep, listSep)
858        String parentFolder, base_name, hdf5Path, igorPath, xref, keySep, listSep
859        String result = xref
860        // look through xref to find each component of full hdf5Path, including the dataset
861        Variable ii, length = itemsInList(hdf5Path, "/")
862        String hdf5 = "", path = ""
863        for (ii = 0; ii < length; ii = ii + 1)
864                hdf5 = H5GW__appendPathDelimiter(hdf5, "/") + StringFromList(ii, hdf5Path, "/")
865                path = H5GW__appendPathDelimiter(path, ":") + StringFromList(ii, igorPath, ":")
866                if ( strlen(StringByKey(hdf5, result, keySep, listSep)) == 0 )
867                        result = H5GW__addXref(hdf5, path, result, keySep, listSep)
868                endif
869        endfor
870        return result
871End
872
873
874//@+
875//      .. index:: ! H5GW__addXref()
876//     
877//      H5GW__addXref(key, value, xref, keySep, listSep)
878//      -------------------------------------------------------------------------------------------------------------
879//     
880//      append a new key,value pair to the cross-reference list
881//@-
882static Function/T H5GW__addXref(key, value, xref, keySep, listSep)
883        String key, value, xref, keySep, listSep
884        return H5GW__AppendString(xref, listSep,  key + keySep + value)
885End
886
887
888//@+
889//      .. index:: ! H5GW__appendPathDelimiter()
890//     
891//      H5GW__appendPathDelimiter(str, sep)
892//      -------------------------------------------------------------------------------------------------------------
893//@-
894static Function/T H5GW__appendPathDelimiter(str, sep)
895        String str, sep
896        if ( (strlen(str) == 0) || ( cmpstr(sep, str[strlen(str)-1]) != 0) )
897                return str + sep
898        endif
899        return str
900End
901
902
903
904// ======================================
905//@+
906//      .. index:: ! H5GW__findTextWaveIndex()
907//     
908//      H5GW__findTextWaveIndex(twave, str, col)
909//      -------------------------------------------------------------------------------------------------------------
910//     
911//              :Wave/T twave: correlation between HDF5 and Igor paths
912//              :String str: text to be located in column *col*
913//              :int col: column number to search for *str*
914//              :returns int: index of found text or -1 if not found
915//@-
916
917static Function H5GW__findTextWaveIndex(twave, str, col)
918        Wave/T twave
919        String str
920        Variable col
921        Variable result = -1, ii, rows=DimSize(twave,0)
922        for (ii=0; ii < rows; ii=ii+1)
923                if (0 == cmpstr(str, twave[ii][col]) )
924                        result = ii
925                        break
926                endif
927        endfor
928        return result
929End
930
931
932// ======================================
933//@+
934//      .. index:: ! H5GW__OpenHDF5_RO()
935//     
936//      H5GW__OpenHDF5_RO(fileName)
937//      -------------------------------------------------------------------------------------------------------------
938//     
939//              :String fileName: name of file (with extension),
940//                      either relative to current file system directory
941//                      or includes absolute file system path
942//              :returns int: Status: 0 if error, non-zero (fileID) if successful
943//     
944//         Assumed Parameter:
945//     
946//              * *home* (path): Igor path name (defines a file system
947//                        directory in which to find the data files)
948//                        Note: data is not changed by this function
949//@-
950
951Static Function H5GW__OpenHDF5_RO(fileName)
952        String fileName
953       
954// SRK - now will present a dialog if the file can't be found
955//      if ( H5GW__FileExists(fileName) == 0 )
956                // avoid the open file dialog if the file is not found here
957//              return 0
958//      endif
959
960        Variable fileID = 0
961        HDF5OpenFile/R/P=home/Z fileID as fileName
962//      HDF5OpenFile/R/P=catPathName/Z fileID as fileName
963        if (V_Flag != 0)
964                return 0
965        endif
966
967        if ( 0 )
968                STRUCT HDF5DataInfo di  // Defined in HDF5 Browser.ipf.
969                InitHDF5DataInfo(di)    // Initialize structure.
970                HDF5AttributeInfo(fileID, "/", 1, "file_name", 0, di)
971                Print di
972        endif
973
974
975        String/G root:file_path = S_path
976        String/G root:file_name = S_FileName
977        return fileID
978End
979
980
981// ======================================
982//@+
983//      .. index:: ! H5GW__HDF5ReadAttributes()
984//     
985//      H5GW__HDF5ReadAttributes(fileID, hdf5Path, baseName)
986//      -------------------------------------------------------------------------------------------------------------
987//     
988//      Reads and assigns the group and dataset attributes.
989//      For groups, it creates a dummy wave *Igor___folder_attributes*
990//      to hold the group attributes.
991//     
992//      All attributes are stored in the wave note
993//     
994//      Too bad that HDF5LoadGroup does not read the attributes.
995//     
996//              :int fileID: IgorPro reference number for this HDF5 file
997//              :String hdf5Path: read the HDF5 file starting
998//                              from this level (default is the root level, "/")
999//                              Note: not implemented yet.
1000//              :String baseName: IgorPro subfolder name to
1001//                              store attributes.  Maps directly from HDF5 path.
1002//@-
1003
1004Static Function H5GW__HDF5ReadAttributes(fileID, hdf5Path, baseName)
1005        Variable fileID
1006        String hdf5Path
1007        String baseName
1008       
1009        Variable group_attributes_type = 1
1010        Variable dataset_attributes_type = 2
1011       
1012//v_tic()       // 0.11 s
1013        // read and assign group attributes
1014        String S_HDF5ListGroup
1015        HDF5ListGroup/F/R/TYPE=(group_attributes_type)  fileID, hdf5Path                //      TYPE=1 reads groups
1016        String/G group_name_list = hdf5Path + ";" + S_HDF5ListGroup
1017       
1018        Variable length = ItemsInList(group_name_list)
1019        Variable index, i_attr
1020        String group_name
1021        String attr_name_list, attr_name, attribute_str
1022       
1023        String old_dir = GetDataFolder(1), subdir, group_attr_name
1024        for (index = 0; index < length; index = index+1)
1025                group_name = StringFromList(index, group_name_list)
1026                attribute_str = H5GW__HDF5AttributesToString(fileID, group_name, group_attributes_type)
1027                if ( strlen(attribute_str) > 0 )
1028                        // store these attributes in the wavenote of a unique wave in the group
1029                        subdir = ":" + baseName + ReplaceString("/", group_name, ":")
1030                        SetDataFolder $subdir
1031                        group_attr_name = StringFromList((itemsInList(group_name, "/")-1), group_name, "/")
1032                        group_attr_name = H5GW__SetStringDefault(group_attr_name, "root") + "_attributes"
1033                        group_attr_name = "Igor___folder_attributes"
1034                        Make/O/N=0 $group_attr_name
1035                        Note/K $group_attr_name, attribute_str
1036                endif
1037                SetDataFolder $old_dir
1038        endfor
1039
1040//v_toc()
1041//v_tic()       // 3.2 s !!
1042       
1043        // read and assign dataset attributes
1044        HDF5ListGroup/F/R/TYPE=(dataset_attributes_type)  fileID, hdf5Path              //      TYPE=2 reads groups
1045        String/G dataset_name_list = S_HDF5ListGroup
1046
1047        // build a table connecting objectPaths with group_name_list and dataset_name_list
1048        // using parentFolder and baseName
1049        String parentFolder = GetDataFolder(1)
1050        String/G objectPaths
1051        H5GW__make_xref(parentFolder, objectPaths, group_name_list, dataset_name_list, baseName)
1052
1053        String file_info
1054        sprintf file_info, ":%s:HDF5___xref", baseName
1055        Wave/T xref = $file_info
1056
1057//v_toc()
1058//v_tic()       // worst @ 6.3 s
1059
1060        Variable row
1061        String hdf5_path, igor_path
1062        length = ItemsInList(dataset_name_list)
1063        for (index = 0; index < length; index = index+1)
1064                hdf5_path = StringFromList(index, dataset_name_list)
1065                attribute_str = H5GW__HDF5AttributesToString(fileID, hdf5_path, dataset_attributes_type)
1066                if ( strlen(attribute_str) > 0 )
1067                        // store these attributes in the wavenote of the dataset
1068                        row = H5GW__findTextWaveIndex(xref, hdf5_path, 0)
1069                        if (row > -1)
1070                                igor_path = ":" + baseName + xref[row][1]
1071                                wave targetWave=$igor_path
1072                                Note/K targetWave, attribute_str
1073                        endif
1074                endif
1075        endfor
1076//v_toc()
1077
1078End
1079
1080// ======================================
1081//@+
1082//      .. index:: ! H5GW__HDF5AttributesToString()
1083//     
1084//      H5GW__HDF5AttributesToString(fileID, hdf5_Object, hdf5_Type, [keyDelimiter, keyValueSep, itemDelimiter])
1085//      ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
1086//     
1087//      Reads the attributes assigned to this object and returns
1088//      a string of key=value pairs, delimited by ;
1089//      Multiple values for a key are delimited by ,
1090//     
1091//      All attributes are stored in the wave note
1092//     
1093//      Too bad that HDF5LoadGroup does not read the attributes.
1094//     
1095//              :int fileID: IgorPro reference number for this HDF5 file
1096//              :String hdf5_Object: full HDF5 path of object
1097//              :int hdf5_Type: 1=group, 2=dataset
1098//              :String keyDelimiter: between key=value pairs, default = "\r"
1099//              :String keyValueSep: key and value, default = "="
1100//              :String itemDelimiter: between multiple values, default = ","
1101//              :returns str: key1=value;key2=value,value;key3=value, empty string if no attributes
1102//@-
1103
1104Static Function/T H5GW__HDF5AttributesToString(fileID, hdf5_Object, hdf5_Type, [keyDelimiter, keyValueSep, itemDelimiter])
1105        Variable fileID
1106        String hdf5_Object
1107        Variable hdf5_Type
1108        String keyDelimiter
1109        String keyValueSep
1110        String itemDelimiter
1111       
1112        if ( ParamIsDefault(keyDelimiter) )
1113                keyDelimiter = "\r"
1114        endif
1115        if ( ParamIsDefault(keyValueSep) )
1116                keyValueSep = "="
1117        endif
1118        if ( ParamIsDefault(itemDelimiter) )
1119                itemDelimiter = ","
1120        endif
1121       
1122        String result = ""
1123        String attr_name_list, attr_name, attr_str, temp_str
1124        Variable num_attr, i_attr
1125        HDF5ListAttributes/TYPE=(hdf5_Type)/Z  fileID, hdf5_Object
1126        if ( V_Flag != 0 )
1127                return result
1128        endif
1129        attr_name_list = S_HDF5ListAttributes
1130        num_attr = ItemsInList(attr_name_list)
1131        for (i_attr = 0; i_attr < num_attr; i_attr = i_attr+1)
1132                attr_name = StringFromList(i_attr, attr_name_list)
1133                attr_str = H5GW__HDF5AttributeDataToString(fileID, hdf5_Object, hdf5_Type, attr_name, itemDelimiter)
1134                if (strlen(result) > 0)
1135                        result = result + keyDelimiter
1136                endif
1137                result = result + attr_name + keyValueSep + attr_str
1138        endfor
1139        KillWaves/Z attr_wave
1140
1141        return result
1142End
1143
1144
1145// ======================================
1146//@+
1147//      .. index:: ! H5GW__HDF5AttributeDataToString()
1148//     
1149//      H5GW__HDF5AttributeDataToString(fileID, hdf5_Object, hdf5_Type, attr_name, itemDelimiter)
1150//      ---------------------------------------------------------------------------------------------------------------------------------------------------
1151//     
1152//      Reads the value of a specific attribute assigned to this object
1153//      and returns its value.
1154//     
1155//              :int fileID: IgorPro reference number for this HDF5 file
1156//              :String hdf5_Object: full HDF5 path of object
1157//              :int hdf5_Type: 1=group, 2=dataset
1158//              :String attr_name: name of the attribute
1159//              :String itemDelimiter: if the attribute data is an array,
1160//                              this will delimit the representation of its members in a string
1161//              :returns String: value, empty string if no value
1162//@-
1163
1164Static Function/T H5GW__HDF5AttributeDataToString(fileID, hdf5_Object, hdf5_Type, attr_name, itemDelimiter)
1165        Variable fileID
1166        String hdf5_Object
1167        Variable hdf5_Type
1168        String attr_name
1169        String itemDelimiter
1170
1171        String attr_str = "", temp_str
1172        Variable index
1173        HDF5LoadData/A=attr_name/TYPE=(hdf5_Type)/N=attr_wave/Z/O/Q   fileID, hdf5_Object
1174        if ( V_Flag == 0 )
1175                if ( 0 == cmpstr( "attr_wave,", WaveList("attr_wave", ",", "TEXT:1")) )
1176                        Wave/T attr_waveT=attr_wave
1177                        attr_str = attr_waveT[0]
1178                else
1179                        Wave attr_waveN=attr_wave
1180                        attr_str = ""
1181                        sprintf attr_str, "%g", attr_waveN[0]           // assume at least one point
1182                        for ( index=1; index < numpnts(attr_waveN); index=index+1)
1183                                sprintf temp_str, "%g", attr_waveN[index]
1184                                attr_str = attr_str + itemDelimiter + temp_str
1185                        endfor
1186                endif
1187        endif
1188        KillWaves/Z attr_wave
1189        return attr_str
1190End
1191
1192
1193// ======================================
1194//@+
1195//      .. index:: ! H5GW__SetStringDefault()
1196//     
1197//      H5GW__SetStringDefault(str, string_default)
1198//      -------------------------------------------------------------------------------------------------------------
1199//     
1200//         :String str: supplied value
1201//         :String string_default: default value
1202//         :returns String: default if supplied value is empty
1203//@-
1204
1205Static Function/T H5GW__SetStringDefault(str, string_default)
1206        String str
1207        String string_default
1208        String result = string_default
1209        if ( strlen(str)>0 )
1210                result = str
1211        endif
1212        return result
1213End
1214
1215
1216// ======================================
1217//@+
1218//      .. index:: ! H5GW__AppendString()
1219//     
1220//      H5GW__AppendString(str, sep, newtext)
1221//      -------------------------------------------------------------------------------------------------------------
1222//     
1223//         :String str: starting string
1224//         :String sep: separator
1225//         :String newtext: text to be appended
1226//         :returns String: result
1227//@-
1228
1229Static Function/T H5GW__AppendString(str, sep, newtext)
1230        String str, sep, newtext
1231        if ( strlen(newtext) == 0 )
1232                return str
1233        endif
1234        if ( strlen(str) > 0 )
1235                str = str + sep
1236        endif
1237        return str +newtext
1238End
1239
1240
1241
1242
1243// ======================================
1244//@+
1245//      .. index:: ! H5GW__FileExists()
1246//     
1247//      H5GW__FileExists(file_name)
1248//      -------------------------------------------------------------------------------------------------------------
1249//     
1250//              :String file_name: name of file to be found
1251//              :returns int: 1 if exists, 0 if does not exist
1252//@-
1253
1254Static Function H5GW__FileExists(file_name)
1255        String file_name
1256        Variable fileID
1257//      Open/R/P=home/Z fileID as file_name     // test if it will open as a regular file
1258        Open/R/P=catPathName/Z fileID as file_name      // test if it will open as a regular file
1259        if ( fileID > 0 )
1260                Close fileID
1261                return 1
1262        endif
1263        return 0
1264End
1265
1266
1267//  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //  //
1268
1269
1270//@+
1271//      Testing and Development
1272//      ======================================
1273//     
1274//      Examples to test read and write:
1275//     
1276//              .. code-block:: guess
1277//                      :linenos:
1278//                     
1279//                      print H5GW_ReadHDF5("root:worker", "simpleexamplefile.h5")
1280//                      print H5GW_ReadHDF5("root:worker", "simple2dcase.h5")
1281//                      print H5GW_ReadHDF5("root:worker", "simple2dmaskedcase.h5")
1282//                      print H5GW_ReadHDF5("root:worker", "generic2dqtimeseries.h5")
1283//                      print H5GW_ReadHDF5("root:worker", "generic2dtimetpseries.h5")
1284//                      print H5GW_WriteHDF5("root:worker:simpleexamplefile", "test_output.h5")
1285//     
1286//      .. index:: test
1287//      .. index:: ! H5GW__TestFile()
1288//     
1289//      H5GW__TestFile(parentDir, sourceFile)
1290//      -------------------------------------------------------------------------------------------------------------
1291//     
1292//      Reads HDF5 file sourceFile into a subfolder of IgorPro folder parentDir.
1293//      Then writes the structure from that subfolder to a new HDF5 file: ``"test_"+sourceFile``
1294//      Assumes that sourceFile is only a file name, with no path components, in the present working directory.
1295//      Returns the name (string) of the new HDF5 file written.
1296//     
1297//              :String parentDir: folder within IgorPro memory to contain the HDF5 test data
1298//              :String sourceFile: HDF5 test data file (assumes no file path information prepends the file name)
1299//@-
1300
1301static Function/T H5GW__TestFile(parentDir, sourceFile)
1302        String parentDir, sourceFile
1303        String prefix = "test_"
1304        String newFile = prefix + sourceFile
1305        String name = StringFromList(0, sourceFile, ".")
1306        print H5GW_ReadHDF5(parentDir, sourceFile)
1307        print H5GW_WriteHDF5(parentDir+":"+name, newFile)
1308        return newFile
1309End
Note: See TracBrowser for help on using the repository browser.