Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature support for NWBv2 #179

Merged
merged 34 commits into from
Jun 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e2cfdeb
Neurodata Without Borders 2.0.1.
Jun 4, 2019
e4dc9fb
Allow local executions
Jan 7, 2020
c54d482
Neurodata Without Borders 2.1.0
Jan 10, 2020
337471b
Neurodata Without Borders 2.2.0
Jan 10, 2020
3608cec
Enforce check for valid headstages
Jan 21, 2020
698fe9c
Update H5_OpenGroup usage
Jan 22, 2020
d8103d9
Support IZeroClampSeries
Jan 24, 2020
7bee5cf
Only write gain for PatchClampSeries ancestry
Jan 24, 2020
8f0c4a6
Update Reference To IPNWB Documentation
Jan 24, 2020
7322607
DAEphys: Regenerate panel and raise version
t-b Jan 31, 2020
7ed1875
Udpate IPNWB
t-b Feb 8, 2020
6b81463
NWB_GetTimeSeriesProperties: Only output scale for NWBv1
t-b Feb 9, 2020
6735415
Add validation tests for NWBV2
t-b Feb 9, 2020
1d0c5da
TestTimeSeries: Pass in the NWB file name instead of guessing it
t-b Feb 9, 2020
989fa8f
UTF_TestNWBExportV1.ipf: Use -V1 suffix for NWB files
t-b Feb 9, 2020
ecafe30
Hardware Tests: Handle I=0 timeseries correctly for NWBv2 export tests
t-b Feb 9, 2020
a18c5db
Add MIES_PXP_NWB_CONVERSION_SKIP_SAVING define
t-b Feb 12, 2020
6dcb717
Add script for mass conversion of PXPs to NWBv2
t-b Feb 12, 2020
b5caec6
Update IPNWB
t-b Mar 4, 2020
7b49310
MIES_MassExperimentProcessing.ipf: Assert on lingering RTEs
t-b Mar 17, 2020
1a6e671
MIES_MassExperimentProcessing.ipf: Add workaround in instructions for…
t-b Mar 17, 2020
40eeb70
UTF_TestNWBExportV2.ipf: Check the NWB versions
t-b Apr 15, 2020
6d70c92
ProcessCurrentExperiment: Delete NWB file on error
t-b Apr 17, 2020
370ce8a
Update IPNWB
t-b May 10, 2020
b8824f6
Update IPNWB
t-b May 11, 2020
e18539b
NWBv2 Export: Write neurodata types for ndx-mies extension
t-b May 10, 2020
9216a5e
Testing/NWBv2: Adapt test to translate hardcoded units back for
t-b May 10, 2020
533ba5c
MIES_IVSCC: Export files into NWBv2 by default
t-b May 19, 2020
0799224
Update IPNWB
t-b May 20, 2020
b3f679e
NWB_AddDeviceSpecificData: Don't add a neurodata type to /general
t-b May 21, 2020
f9a764e
Update IPNWB
t-b May 21, 2020
a60f90d
SaveExperimentSpecial: Reset session start time in "Save and clear" mode
t-b May 28, 2020
6943cb0
NWB_GetFileForExport: Work around stale session start times
t-b May 28, 2020
33b63e3
DQ_StopOngoingDAQAllLocked: Make it more robust
t-b May 29, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# Documentation files
Packages/doc/html
Packages/doc/IPNWB
Packages/doc/doctrees
Packages/doc/sphinx-output.log

Packages/MIES/backup
*.orig
*.rej
Expand Down
361 changes: 361 additions & 0 deletions Packages/Conversion/MIES_MassExperimentProcessing.ipf
Original file line number Diff line number Diff line change
@@ -0,0 +1,361 @@
#pragma rtGlobals=3 // Use modern global access method.

/// @file MIES_MassExperimentProcessing.ipf
/// @brief __MEP__ Process multiple MIES pxps to convert data into NWBv2
///
/// Installation:
/// - Stop Igor Pro
/// - Create a shortcut to this file and place it in the `Igor Procedures` folder
/// - Ensure that only MIES is installed and no other Igor Pro packages
/// - In the MIES installation folder (All Users: `C:\Program Files\MIES`, User: `C:\Users\$User\Documents\MIES`)
/// create an empty file named `UserConfig.txt`.
///
/// Running:
/// - Start Igor Pro
/// - Select "Macros" -> "Mass convert PXPs to NWBv2"
/// - Enter an input and output folder for the conversion
/// - Wait until it's done
///
/// In the output folder there will be a `conversion.json` file with results of
/// the conversion process. Search for the `error` key for failed conversions.

// #define MEP_DEBUGGING

#ifdef MEP_DEBUGGING

static StrConstant INPUT_FOLDER = "C:tim-data:pxp_examples_for_nwb_2:"
static StrConstant OUTPUT_FOLDER = "C:tim-data:output:"

#else

static StrConstant INPUT_FOLDER = ""
static StrConstant OUTPUT_FOLDER = ""

#endif

Menu "Macros"
"Mass convert PXPs to NWBv2", /Q, StartMultiExperimentProcess()
End

// NOTE: If you use these procedures for your own purposes, change the package name
// to a distinctive name so that you don't clash with other people's preferences.
static StrConstant kPackageName = "MIES PXP to NWBv2"
static StrConstant kPreferencesFileName = "ProcessPrefsMIESNWBv2.bin"
static Constant kPrefsRecordID = 0 // The recordID is a unique number identifying a record within the preference file.
// In this example we store only one record in the preference file.

// The structure stored in preferences to keep track of what experiment to load next.
// If you add, remove or change fields you must delete your old prefs file. See the help
// topic "Saving Package Preferences" for details.
static Structure MultiExperimentProcessPrefs
uint32 version // Prefs version
uint32 processRunning // Truth that we are running the mult-experiment process
char settingsFile[256]
EndStructure

// In version 101 of the prefs structure we increased folderPath from 100 to 256 bytes
static Constant kPrefsVersionNumber = 102

// Loads preferences into our structure.
static Function LoadPackagePrefs(prefs)
STRUCT MultiExperimentProcessPrefs &prefs

Variable currentPrefsVersion = kPrefsVersionNumber

// This loads preferences from disk if they exist on disk.
LoadPackagePreferences /MIS=1 kPackageName, kPreferencesFileName, kPrefsRecordID, prefs
// Printf "%d byte loaded\r", V_bytesRead

// If error or prefs not found or not valid, initialize them.
if (V_flag!=0 || V_bytesRead==0 || prefs.version!=currentPrefsVersion)
prefs.version = currentPrefsVersion

prefs.processRunning = 0
prefs.settingsFile = ""

SavePackagePrefs(prefs) // Create default prefs file.
endif
End

// Saves our structure to preferences.
static Function SavePackagePrefs(prefs)
STRUCT MultiExperimentProcessPrefs &prefs

SavePackagePreferences kPackageName, kPreferencesFileName, kPrefsRecordID, prefs
End

// This is the routine that you would need to change to use this procedure file for your own purposes.
// See comments about labeled "TO USE FOR YOUR OWN PURPOSES".
static Function ProcessCurrentExperiment(prefs)
STRUCT MultiExperimentProcessPrefs &prefs

variable jsonID, index
string outputFilePath, inputFile, outputFolder

jsonID = GetJSON(prefs)

if(IsAppropriateExperiment())

outputFolder = JSON_GetString(jsonID, "/outputFolder")

PathInfo home
inputFile = S_path + GetExperimentName() + ".pxp"

outputFilePath = outputFolder + S_path + GetExperimentName() + ".nwb"

index = JSON_GetVariable(jsonID, "/index")
JSON_AddString(jsonID, "/log/" + num2str(index) + "/from", inputFile)
JSON_AddString(jsonID, "/log/" + num2str(index) + "/to", outputFilePath)

DoWindow/K HistoryCarbonCopy
NewNotebook/V=0/F=0 /N=HistoryCarbonCopy

try
PerformMiesTasks(outputFilePath); AbortOnRTE
catch
print "Caught an RTE"
JSON_AddBoolean(jsonID, "/log/" + num2str(index) + "/error", 1)
JSON_SetVariable(jsonID, "/errors", JSON_GetVariable(jsonID, "/errors") + 1)
HDF5CloseFile/A/Z 0
DeleteFile/Z outputFilePath
endtry

Notebook HistoryCarbonCopy getData=1
JSON_AddString(jsonID, "/log/" + num2str(index) + "/output", trimstring(S_Value))

JSON_SetVariable(jsonID, "/processed", JSON_GetVariable(jsonID, "/processed") + 1)
else
JSON_SetVariable(jsonID, "/skipped", JSON_GetVariable(jsonID, "/skipped") + 1)
endif

JSON_SetVariable(jsonID, "/index", JSON_GetVariable(jsonID, "/index") + 1)

StoreJSON(prefs, jsonID)
End

static Function PerformMiesTasks(outputFilePath)
string outputFilePath

string folder, message
variable nwbVersion, error

printf "Free Memory: %g GB\r", GetFreeMemory()

if(FileExists(outputFilePath))
print "Output file already exists, skipping!"
return 0
endif

folder = GetFolder(outputFilePath)

if(!FolderExists(folder))
CreateFolderOnDisk(folder)
endif

ClearRTError()

nwbVersion = 2
NWB_ExportAllData(nwbVersion, overrideFilePath=outputFilePath)
HDF5CloseFile/A/Z 0

message = GetRTErrMessage()
error = GetRTError(1)
ASSERT(error == 0, "Encountered lingering RTE of " + num2str(error) + "(message: " + message + ") after executing NWB_ExportAllData.")
End

static Function IsAppropriateExperiment()

return ItemsInList(GetAllDevicesWithContent()) > 0
End

// Returns full path to the next experiment file to be loaded or "" if we are finished.
static Function/S FindNextExperiment(prefs)
STRUCT MultiExperimentProcessPrefs &prefs

variable jsonID, index

jsonID = GetJSON(prefs)

WAVE/T inputFiles = JSON_GetTextWave(jsonID, "inputFiles")
index = JSON_GetVariable(jsonID, "/index")
JSON_Release(jsonID)

if(!(index >= DimSize(inputFiles, ROWS)))
return inputFiles[index]
endif

return ""
End

// Caller needs to release json
static Function GetJSON(prefs)
STRUCT MultiExperimentProcessPrefs &prefs

string data, fname

[data, fname] = LoadTextFile(prefs.settingsFile)

return JSON_Parse(data)
End

// json will be released
static Function StoreJSON(prefs, jsonID)
STRUCT MultiExperimentProcessPrefs &prefs
variable jsonID

string data = JSON_Dump(jsonID, indent=2)

SaveTextFile(data, prefs.settingsFile)

ASSERT(!JSON_Release(jsonID), "Could not release json")
End

// Posts commands to Igor's operation queue to close the current experiment and open the next one.
// Igor executes operation queue commands when it is idling - that is, when it is not running a
// function or operation.
static Function PostLoadNextExperiment(nextExperimentFullPath)
String nextExperimentFullPath

ASSERT(FileExists(nextExperimentFullPath), "Experiment must exist")

Execute/P/Q "NEWEXPERIMENT " // Post command to close this experiment.

Execute/P/Q "SetIgorOption poundDefine=MIES_PXP_NWB_CONVERSION_SKIP_SAVING"

// Post command to open next experiment.
String cmd
sprintf cmd "Execute/P/Q \"LOADFILE %s\"", nextExperimentFullPath
Execute/Q cmd
End

// This is the hook function that Igor calls whenever a file is opened. We use it to
// detect the opening of an experiment and to call our ProcessCurrentExperiment function.
static Function AfterFileOpenHook(refNum,file,pathName,type,creator,kind)
Variable refNum,kind
String file,pathName,type,creator

STRUCT MultiExperimentProcessPrefs prefs

LoadPackagePrefs(prefs) // Load our prefs into our structure
if (prefs.processRunning == 0)
return 0 // Process not yet started.
endif

// Check file type
if (CmpStr(type,"IGsU") != 0)
return 0 // This is not a packed experiment file
endif

ProcessCurrentExperiment(prefs)

// See if there are more experiments to process.
String nextExperimentFullPath = FindNextExperiment(prefs)
if (strlen(nextExperimentFullPath) == 0)
// Process is finished
prefs.processRunning = 0 // Flag process is finished.
Execute/P "NEWEXPERIMENT " // Post command to close this experiment.
print "Multi-experiment process is finished."
else
// Load the next experiment in the designated folder, if any.
PostLoadNextExperiment(nextExperimentFullPath) // Post operation queue commands to load next experiment
endif

SavePackagePrefs(prefs)

return 0 // Tell Igor to handle file in default fashion.
End

// This function enables our special Igor hooks which skip saving the experiment
Function StartMultiExperimentProcess()

Execute/P/Q "SetIgorOption poundDefine=MIES_PXP_NWB_CONVERSION_SKIP_SAVING"
Execute/P/Q "COMPILEPROCEDURES "
Execute/P/Q "StartMultiExperimentProcessWrapper()"
End

// Allow user to choose the folder containing the experiment files and start the process.
Function StartMultiExperimentProcessWrapper()

string message, settingsFile, inputFolder, outputFolder, files
variable jsonID

STRUCT MultiExperimentProcessPrefs prefs
LoadPackagePrefs(prefs)

message = "Choose input folder with MIES pxps"
if(!cmpstr(INPUT_FOLDER, ""))
NewPath/O/Q/M=message MultiExperimentInputFolder
else
NewPath/O/Q/M=message MultiExperimentInputFolder, INPUT_FOLDER
endif

if (V_flag != 0)
return -1 // User canceled from New Path dialog
endif

PathInfo MultiExperimentInputFolder
inputFolder = S_Path
ASSERT(V_flag, "Invalid path")

message = "Choose output folder for NWBv2 files"
if(!cmpstr(OUTPUT_FOLDER, ""))
NewPath/O/Q/M=message MultiExperimentOutputFolder
else
NewPath/O/Q/M=message MultiExperimentOutputFolder, OUTPUT_FOLDER
endif

if (V_flag != 0)
return -1 // User canceled from New Path dialog
endif

PathInfo MultiExperimentOutputFolder
outputFolder = S_Path
ASSERT(V_flag, "Invalid path")

files = GetAllFilesRecursivelyFromPath("MultiExperimentInputFolder", extension=".pxp")

// 16: Case-insensitive alphanumeric sort that sorts wave0 and wave9 before wave10.
// ...
// 64: Ignore + and - in the alphanumeric sort so that "Text-09" sorts before "Text-10". Set options to 80 or 81.
files = SortList(files, "|", 80)

WAVE/T/Z inputPXPs = ListToTextWave(files, "|")

jsonID = JSON_New()
JSON_AddWave(jsonID, "/inputFiles", inputPXPs)
JSON_AddString(jsonID, "/inputFolder", inputFolder)
JSON_AddString(jsonID, "/outputFolder", outputFolder)
JSON_AddVariable(jsonID, "/index", 0)
JSON_AddVariable(jsonID, "/processed", 0)
JSON_AddVariable(jsonID, "/errors", 0)
JSON_AddVariable(jsonID, "/skipped", 0)
JSON_AddVariable(jsonID, "/total", DimSize(inputPXPs, ROWS))

JSON_AddTreeArray(jsonID, "/log")
JSON_AddObjects(jsonID, "/log", objCount = DimSize(inputPXPs, ROWS))

prefs.settingsFile = outputFolder + "conversion.json"
StoreJSON(prefs, jsonID)

prefs.processRunning = 1 // Flag process is started.

// Start the process off by loading the first experiment.
String nextExperimentFullPath = FindNextExperiment(prefs)
PostLoadNextExperiment(nextExperimentFullPath) // Start the process off

SavePackagePrefs(prefs)

return 0
End

#ifdef MEP_DEBUGGING

Function TestMe()

STRUCT MultiExperimentProcessPrefs prefs

LoadPackagePrefs(prefs)
ProcessCurrentExperiment(prefs)
End

#endif
2 changes: 1 addition & 1 deletion Packages/IPNWB
Submodule IPNWB updated 61 files
+2 −0 .gitattributes
+1 −0 .gitignore
+6 −0 .gitmodules
+98 −1 IPNWB_Constants.ipf
+22 −0 IPNWB_Debugging.ipf
+138 −73 IPNWB_HDF5Helpers.ipf
+197 −145 IPNWB_Reader.ipf
+79 −13 IPNWB_Structures.ipf
+702 −63 IPNWB_Utils.ipf
+401 −113 IPNWB_Writer.ipf
+20 −0 Makefile
+0 −334 Readme.rst
+52 −0 conf.py
+14 −0 doc/nwb1.rst
+37 −0 doc/nwb2.rst
+98 −0 doc/schema.diff
+113 −0 doc/specifications_core_1_nwb.yaml
+ doc/sweep_table.png
+77 −0 examples/IPNWB_Examples_Reader.ipf
+79 −0 examples/IPNWB_Examples_Writer.ipf
+91 −0 index.rst
+35 −0 make.bat
+1 −0 namespace/core/json/nwb.base.json
+1 −0 namespace/core/json/nwb.behavior.json
+1 −0 namespace/core/json/nwb.device.json
+1 −0 namespace/core/json/nwb.ecephys.json
+1 −0 namespace/core/json/nwb.epoch.json
+1 −0 namespace/core/json/nwb.file.json
+1 −0 namespace/core/json/nwb.icephys.json
+1 −0 namespace/core/json/nwb.image.json
+1 −0 namespace/core/json/nwb.misc.json
+1 −0 namespace/core/json/nwb.namespace.json
+1 −0 namespace/core/json/nwb.ogen.json
+1 −0 namespace/core/json/nwb.ophys.json
+1 −0 namespace/core/json/nwb.retinotopy.json
+206 −0 namespace/core/yaml/nwb.base.yaml
+116 −0 namespace/core/yaml/nwb.behavior.yaml
+14 −0 namespace/core/yaml/nwb.device.yaml
+324 −0 namespace/core/yaml/nwb.ecephys.yaml
+44 −0 namespace/core/yaml/nwb.epoch.yaml
+439 −0 namespace/core/yaml/nwb.file.yaml
+247 −0 namespace/core/yaml/nwb.icephys.yaml
+192 −0 namespace/core/yaml/nwb.image.yaml
+279 −0 namespace/core/yaml/nwb.misc.yaml
+60 −0 namespace/core/yaml/nwb.namespace.yaml
+43 −0 namespace/core/yaml/nwb.ogen.yaml
+326 −0 namespace/core/yaml/nwb.ophys.yaml
+234 −0 namespace/core/yaml/nwb.retinotopy.yaml
+1 −0 namespace/hdmf-common/json/namespace.json
+1 −0 namespace/hdmf-common/json/sparse.json
+1 −0 namespace/hdmf-common/json/table.json
+21 −0 namespace/hdmf-common/yaml/namespace.yaml
+24 −0 namespace/hdmf-common/yaml/sparse.yaml
+155 −0 namespace/hdmf-common/yaml/table.yaml
+1 −0 namespace/ndx-mies/json/namespace.json
+1 −0 namespace/ndx-mies/json/ndx-mies.extensions.json
+12 −0 namespace/ndx-mies/yaml/namespace.yaml
+208 −0 namespace/ndx-mies/yaml/ndx-mies.extensions.yaml
+1 −0 ndx-MIES
+1 −0 specifications
+84 −0 update_specifications.sh
Loading