Skip to content
Merged
Show file tree
Hide file tree
Changes from 69 commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
9ba9323
Update file_read.rst
ehennestad Jun 30, 2025
211f737
Update nwbfile.rst
ehennestad Jun 30, 2025
299ee8e
Create schemas_and_generation.rst
ehennestad Jun 30, 2025
aba4874
Update schemas_and_generation.rst
ehennestad Jun 30, 2025
3e26eb0
Fix typos
ehennestad Jun 30, 2025
59b8808
Merge branch 'main' into update-docs-read-section
ehennestad Jun 30, 2025
4d999f8
Update conf.py
ehennestad Sep 3, 2025
3e1d843
Reorganize docs based on diataxis framework
ehennestad Sep 4, 2025
78f5882
Fix links
ehennestad Sep 4, 2025
0aa79ce
smaller rewordings
ehennestad Sep 4, 2025
2923c40
Update overview.rst
ehennestad Sep 4, 2025
b7a4609
Removed troubleshooting section
ehennestad Sep 4, 2025
80b0a28
Minor rewording
ehennestad Sep 4, 2025
9a4a24b
Update installation.rst
ehennestad Sep 4, 2025
843e9d5
Update quickstart.rst
ehennestad Sep 4, 2025
b1c18bf
Update file_create.rst
ehennestad Sep 4, 2025
3c5c3ed
Update file_create.rst
ehennestad Sep 4, 2025
a817423
Update nwbfile.rst
ehennestad Sep 4, 2025
6879b2a
Add neurodata types page
ehennestad Sep 4, 2025
419af7b
Merge branch 'main' into update-docs-read-section
ehennestad Sep 24, 2025
eafbddc
Minor reformulations
ehennestad Sep 25, 2025
343c303
Update hdf5_considerations.rst
ehennestad Sep 25, 2025
9fb18e9
Update performance_optimization.rst
ehennestad Sep 25, 2025
354455b
Update overview.rst
ehennestad Sep 25, 2025
96382e8
Update performance_optimization.rst
ehennestad Sep 25, 2025
097f3f4
Update docs/source/pages/getting_started/quickstart.rst
ehennestad Sep 25, 2025
f5c1ae5
Merge branch 'main' into update-docs-read-section
ehennestad Sep 25, 2025
926c832
Merge branch 'main' into update-docs-read-section
ehennestad Sep 25, 2025
806a5ae
Update nwbfile.rst
ehennestad Sep 26, 2025
7452da5
Update docs/source/pages/concepts/file_create.rst
bendichter Sep 26, 2025
65c0d1e
Update docs/source/pages/concepts/file_create/hdf5_considerations.rst
bendichter Sep 26, 2025
99d27bd
Update docs/source/pages/concepts/file_create/hdf5_considerations.rst
bendichter Sep 26, 2025
69f67aa
Update docs/source/pages/concepts/file_create/nwbfile.rst
bendichter Sep 26, 2025
f40db55
Update docs/source/pages/concepts/file_create/performance_optimizatio…
bendichter Sep 26, 2025
994e6d2
Update docs/source/pages/getting_started/overview.rst
bendichter Sep 26, 2025
6ad5451
Update docs/source/pages/getting_started/overview.rst
bendichter Sep 26, 2025
9af98a6
Rename considerations.rst to dimension_ordering.rst
ehennestad Sep 27, 2025
0523f6a
Rename hdf5_considerations.rst to about_hdf5.rst
ehennestad Sep 29, 2025
b152e3b
Update file_create.rst
ehennestad Sep 29, 2025
fc47c8d
Update overview.rst
ehennestad Sep 29, 2025
f038ff5
Updating the file_create concept pages
ehennestad Sep 29, 2025
f4290e2
Update editing_nwb_files.rst
ehennestad Sep 29, 2025
e54b1ad
Change performance page and add how-to for using config profiles
ehennestad Sep 29, 2025
4c2ff13
Update performance_optimization.rst
ehennestad Sep 29, 2025
b73e8d4
Update compression_profiles.rst
ehennestad Sep 29, 2025
8d15987
Simplify config-profile how-to guide, add to main index
ehennestad Sep 29, 2025
d19e137
Update compression_profiles.rst
ehennestad Sep 29, 2025
67b566e
Update neurodata_types.rst
ehennestad Sep 29, 2025
c0a30fe
Update compression_profiles.rst
ehennestad Sep 29, 2025
af42d1c
Update neurodata_types.rst
ehennestad Sep 29, 2025
0e71485
Update index.rst
ehennestad Sep 29, 2025
a1c738a
Rename performance_optimization to storage_optimization
ehennestad Sep 29, 2025
b57e049
Update compression_profiles.rst
ehennestad Sep 29, 2025
06f2bad
Merge branch 'main' into update-docs-read-section
ehennestad Oct 2, 2025
b0e0cb6
Update storage_optimization.rst
ehennestad Oct 7, 2025
0045e35
Update storage_optimization.rst
ehennestad Oct 7, 2025
b5588d9
Improve api for applying dataset configuration profiles to file befor…
ehennestad Oct 21, 2025
25143ca
Document limitations on editing NWB datasets in MatNWB
ehennestad Oct 23, 2025
f218a96
Merge branch 'main' into update-docs-read-section
ehennestad Oct 23, 2025
516b57e
Update editing_nwb_files.rst
ehennestad Oct 23, 2025
c3fc4e9
Merge branch 'update-docs-read-section' of https://github.com/Neuroda…
ehennestad Oct 23, 2025
df6488b
Fix links and formatting issues
ehennestad Oct 23, 2025
6cd3415
Update documentation with citation info and improved links
ehennestad Oct 23, 2025
44342b8
Remove concepts page on neurodata types
ehennestad Oct 23, 2025
449e474
Clarify NWB schema usage and class regeneration docs
ehennestad Oct 23, 2025
ed17a14
Add note on lazy loading with DataStub in MatNWB
ehennestad Oct 23, 2025
a6826b2
Update storage_backends.rst
ehennestad Oct 23, 2025
e9ec92f
Apply suggestion from @bendichter
ehennestad Oct 23, 2025
50c618d
Update overview.rst
ehennestad Oct 23, 2025
e3f3977
Revise documentation on editing NWB files in MatNWB
ehennestad Oct 23, 2025
df23311
Merge branch 'main' into update-docs-read-section
ehennestad Oct 24, 2025
68d082e
Adjust introductions for index and overview pages
ehennestad Oct 24, 2025
adf1064
Remove page on editing NWB files
ehennestad Oct 24, 2025
ff95a84
Update nwbfile.rst
ehennestad Oct 24, 2025
3eee884
Update storage_optimization.rst
ehennestad Oct 24, 2025
9ea1b52
Merge branch 'main' into update-docs-read-section
ehennestad Oct 27, 2025
33881e6
Merge branch 'main' into update-docs-read-section
ehennestad Oct 31, 2025
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
19 changes: 19 additions & 0 deletions +io/+config/+enum/ConfigurationProfile.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
classdef ConfigurationProfile < handle
%CONFIGURATIONPROFILE Dataset configuration profiles recognised by MatNWB.
%
% Use these enumeration members when selecting chunking/compression presets
% via NwbFile.applyDatasetSettingsProfile, or nwbExport. Profiles map to
% JSON files in the ``configuration`` folder:
%
% * ``default`` – general-purpose balance of size and performance.
% * ``cloud`` – tuned for object storage and remote streaming access.
% * ``archive`` – favors compact, long-term storage.
% * ``none`` – opt out of applying a profile entirely.

enumeration
none
default
cloud
archive
end
end
55 changes: 47 additions & 8 deletions +io/+config/readDatasetConfiguration.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function datasetConfig = readDatasetConfiguration(profile)
function datasetConfig = readDatasetConfiguration(profile, options)
% READDATASETCONFIGURATION Reads the default dataset configuration from a JSON file.
%
% Syntax:
Expand All @@ -20,22 +20,61 @@
%
% % Load the default dataset configuration
% datasetConfig = io.config.readDatasetConfiguration();
% disp(datasetConfig);
%
% Example 2 - Load dataset configurations from a specific file ::
%
% datasetConfig = io.config.readDatasetConfiguration("FilePath", "configuration_file.json");
% disp(datasetConfig);

arguments
profile (1,1) string {mustBeMember(profile, [ ...
"default", ...
"cloud", ...
"archive"
])} = "default"
profile (1,1) io.config.enum.ConfigurationProfile = "default"
options.FilePath string {mustBeJsonFileOrEmpty} = string.empty
end

filename = sprintf('%s_dataset_configuration.json', profile);
if profile == io.config.enum.ConfigurationProfile.none && isempty(options.FilePath)
datasetConfig = [];
return
end

configFilePath = fullfile(misc.getMatnwbDir, 'configuration', filename);
% If FilePath is specified, we use that file
if ~isempty(options.FilePath)
configFilePath = options.FilePath;
else
filename = sprintf('%s_dataset_configuration.json', profile);
configFilePath = fullfile(misc.getMatnwbDir, 'configuration', filename);
end

datasetConfig = jsondecode(fileread(configFilePath));
datasetConfig = datasetConfig.datasetSpecifications;

datasetConfig = io.config.internal.applyCustomMatNWBPropertyNames(datasetConfig);
datasetConfig = io.config.internal.flipChunkDimensions(datasetConfig);
end

function mustBeJsonFileOrEmpty(value)
%MUSTBEJSONFILEOREMPTY Validate that input is a JSON file path or empty
%
% mustBeJsonFileOrEmpty(VALUE) throws an error if VALUE is not empty and
% not a character vector or string scalar ending with '.json' (case-insensitive).

arguments
value string
end

if isempty(value)
return
end

assert(isscalar(value), ...
"NWB:validator:mustBeJsonFileOrEmpty:InvalidInput", ...
"Value must be a string scalar, character vector, or empty.");

assert(endsWith(value, ".json", "IgnoreCase", true), ...
"NWB:validator:mustBeJsonFileOrEmpty:InvalidFileType", ...
"Value must end with '.json'.");

assert(exist(value, "file") == 2, ...
"NWB:validator:mustBeJsonFileOrEmpty:FileMustExist", ...
"Value must be the name of an existing json file.")
end
48 changes: 48 additions & 0 deletions +io/+config/resolveDatasetConfiguration.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
function datasetConfig = resolveDatasetConfiguration(input)
% resolveDatasetConfiguration - Resolves the dataset configuration based on the input.
%
% Syntax:
% datasetConfig = io.config.resolveDatasetConfiguration(input)
% This function resolves NWB dataset configurations from the specified input,
% which can be a file path or a structure. If no input is provided, it
% uses the default NWB dataset configuration profile.
%
% Input Arguments:
% input {mustBeStringOrStruct} - A value to resolve configurations for,
% which can either be a string representing the file path to the
% configurations or a struct containing the configurations directly.
%
% Output Arguments:
% datasetConfig - The NWB dataset configurations, returned as a structure.

arguments
input {mustBeStringOrStruct} = struct.empty
end

if isempty(input)
disp('No dataset settings provided, using default dataset settings profile.')
datasetConfig = io.config.readDatasetConfiguration();

elseif ischar(input) || (isstring(input) && isscalar(input))
input = string(input);
if isfile(input)
datasetConfig = io.config.readDatasetConfiguration("FilePath", input);
else
datasetConfig = io.config.readDatasetConfiguration(input);
end

elseif isstruct(input)
datasetConfig = input;
end
end

function mustBeStringOrStruct(value)
isValid = isempty(value) || ...
ischar(value) || (isstring(value) && isscalar(value)) || ...
isstruct(value);

assert(isValid, ...
'NWB:ResolveDatasetSettings:InvalidInput', ...
['Expected datasetSettings to be a string (profile name or filename) ' ...
'or a struct (already loaded settings).'])
end
19 changes: 19 additions & 0 deletions +tests/+unit/+io/+config/ApplyDatasetConfigurationTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -274,5 +274,24 @@ function testApplyCustomMatNWBPropertyNames(testCase)
testCase.verifyTrue( isfield(updatedConfig, 'VectorData_data') );
testCase.verifyTrue( isfield(updatedConfig, 'GrayscaleImage_data') );
end

function testNwbFileApplyDatasetSettingsProfile(testCase)
nwbFile = tests.factory.NWBFile();

largeSeries = types.core.TimeSeries( ...
'data', rand(64, 100000), ...
'data_unit', 'n/a', ...
'timestamps', 1:100000);

nwbFile.acquisition.set('data', largeSeries);

datasetConfig = nwbFile.applyDatasetSettingsProfile('cloud');

resultPipe = nwbFile.acquisition.get('data').data;
testCase.verifyTrue(isa(resultPipe, 'types.untyped.DataPipe'), ...
'applyDatasetSettings should configure datasets using named profile');
testCase.verifyTrue(isstruct(datasetConfig), ...
'applyDatasetSettings should return the dataset configuration that was applied');
end
end
end
46 changes: 46 additions & 0 deletions +tests/+unit/+io/+config/FunctionsTest.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
classdef FunctionsTest < matlab.unittest.TestCase
% FunctionsTest - Test inputs and outputs of functions in io.config namespace

methods (Test)
function testReadDatasetConfiguration(testCase)
% Test with no inputs:
defaultDatasetConfig = io.config.readDatasetConfiguration();
testCase.verifyClass(defaultDatasetConfig, 'struct')

% Test with configuration profile name
cloudDatasetConfig = io.config.readDatasetConfiguration('cloud');
testCase.verifyClass(cloudDatasetConfig, 'struct')
testCase.verifyNotEqual(cloudDatasetConfig, defaultDatasetConfig)

% Test with configuration profile name "none"
noConfig = io.config.readDatasetConfiguration('none');
testCase.verifyEmpty(noConfig')

% Test with filepath input
filename = 'default_dataset_configuration.json';
configFilePath = fullfile(misc.getMatnwbDir, 'configuration', filename);
defaultDatasetConfigFromFile = io.config.readDatasetConfiguration('FilePath', configFilePath);
testCase.verifyEqual(defaultDatasetConfigFromFile, defaultDatasetConfig)
end

function testResolveDatasetConfiguration(testCase)
% Test with no inputs (capture command window output):
C = evalc("defaultDatasetConfigA = io.config.resolveDatasetConfiguration()"); %#ok<NASGU>
testCase.verifyClass(defaultDatasetConfigA, 'struct')

% Test with structure input, i.e already loaded configuration
defaultDatasetConfigB = io.config.resolveDatasetConfiguration(defaultDatasetConfigA);
testCase.verifyEqual(defaultDatasetConfigB, defaultDatasetConfigA)

% Test with profile name
defaultDatasetConfigC = io.config.resolveDatasetConfiguration("default");
testCase.verifyEqual(defaultDatasetConfigC, defaultDatasetConfigA)

% Test with filepath input
filename = 'default_dataset_configuration.json';
configFilePath = fullfile(misc.getMatnwbDir, 'configuration', filename);
defaultDatasetConfigD = io.config.resolveDatasetConfiguration(configFilePath);
testCase.verifyEqual(defaultDatasetConfigD, defaultDatasetConfigA)
end
end
end
19 changes: 19 additions & 0 deletions +tests/+unit/nwbExportTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,25 @@ function testExportTimeseriesWithoutStartingTimeRate(testCase)
'NWB:CustomConstraintUnfulfilled')
end

function testExportAppliesDatasetSettingsOption(testCase)
nwb = tests.factory.NWBFile();
largeSeries = types.core.TimeSeries( ...
'data', rand(64, 100000), ...
'data_unit', 'n/a', ...
'timestamps', 1:100000);

nwb.acquisition.set('export_data', largeSeries);

nwbFilePath = testCase.getRandomFilename();
nwbExport(nwb, nwbFilePath, 'DatasetSettingsProfile', 'cloud');

configuredData = nwb.acquisition.get('export_data').data;
testCase.verifyTrue(isa(configuredData, 'types.untyped.DataPipe'), ...
'nwbExport should configure datasets when DatasetSettings option is provided');
testCase.verifyTrue(isfile(nwbFilePath), ...
'nwbExport should still write the requested file');
end

function testEmbeddedSpecs(testCase)

% Install extensions, one will be used, the other will not.
Expand Down
93 changes: 93 additions & 0 deletions NwbFile.m
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,99 @@ function export(obj, filename, mode)
end
end

function datasetConfig = applyDatasetSettingsProfile(obj, profile, options)
% APPLYDATASETSETTINGSPROFILE - Configure datasets using predefined settings profile
%
% Syntax:
% nwb.applyDatasetSettingsProfile(profile) applies a dataset
% configuration profile to the nwb-file ``nwb``. Available profiles:
% "default", "cloud", "archive". This will configure datasets in
% the NwbFile object for chunking and compression.
%
% Input Arguments:
% - obj (NwbFile) -
% An instance of the NwbFile class.
%
% - profile (ConfigurationProfile) -
% Specifies the settings profile to use. Default is "none".
%
% Name-Value Arguments:
% - OverrideExisting (logical) -
% This boolean determines if existing DataPipe objects in the
% file will be reconfigured with the provided options. Default is
% false. **Important**: This does not work for DataPipes that has
% previously been exported to file.
%
% Output Arguments:
% - datasetConfig -
% (Optional) The configuration settings applied to the dataset.
%
% See also:
% io.config.enum.ConfigurationProfile
% NwbFile.applyDatasetSettings

arguments
obj (1,1) NwbFile
profile (1,1) io.config.enum.ConfigurationProfile
options.OverrideExisting (1,1) logical = false
end

datasetConfig = io.config.readDatasetConfiguration(profile);
nvPairs = namedargs2cell(options);
obj.applyDatasetSettings(datasetConfig, nvPairs{:});
if ~nargout
clear datasetConfig
end
end


function datasetConfig = applyDatasetSettings(obj, settingsReference, options)
% APPLYDATASETSETTINGS - Configure datasets using NWB dataset settings
%
% Syntax:
% nwb.applyDatasetSettings(settingsReference) applies a dataset
% configuration profile to the nwb-file ``nwb``. This method
% accepts the filename of a custom configuration profile or a
% structure representing a configuration profile.
%
% Input Arguments:
% - obj (NwbFile) -
% An instance of the NwbFile class.
%
% - settingsReference (string | struct) -
% The filename of a custom configuration profile or an in-memory
% structure representing a configuration profile.
%
% Name-Value Arguments:
% - OverrideExisting (logical) -
% This boolean determines if existing DataPipe objects in the
% file will be reconfigured with the provided options. Default is
% false. **Important**: This does not work for DataPipes that has
% previously been exported to file.
%
% Output Arguments:
% - datasetConfig -
% (Optional) The configuration settings applied to the dataset.
%
% See also:
% io.config.enum.ConfigurationProfile
% NwbFile.applyDatasetSettingsProfile

arguments
obj (1,1) NwbFile
settingsReference
options.OverrideExisting (1,1) logical = false
end

datasetConfig = io.config.resolveDatasetConfiguration(settingsReference);

nvPairs = namedargs2cell(options);
io.config.applyDatasetConfiguration(obj, datasetConfig, nvPairs{:});
if ~nargout
clear datasetConfig
end
end

function o = resolve(obj, path)
if ischar(path)
path = {path};
Expand Down
Loading