Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
133 commits
Select commit Hold shift + click to select a range
689d747
Initial plan
Copilot Aug 13, 2026
54fd124
Apply remaining changes
Copilot Aug 13, 2026
b5575c7
Make Subject required by default in NWBFile::initialize()
Copilot Aug 13, 2026
b4bd01e
Added Subject type
oruebel Aug 13, 2026
ac90401
Merge branch 'copilot/make-subject-required-by-default' of https://gi…
oruebel Aug 13, 2026
d5a741e
Update unit tests
oruebel Aug 13, 2026
a526662
Update Changelog and fix docstring
oruebel Aug 13, 2026
b732aea
Add missing testSubject.cpp file
oruebel Aug 13, 2026
dabddc7
Fix nwb-inspector validation errors
oruebel Aug 13, 2026
cbd9a1f
Fix validation tests
oruebel Aug 13, 2026
10de7dd
Fix nwb-inspector validation errors
oruebel Aug 13, 2026
bcb223e
Fix nwb-inspector validation errors
oruebel Aug 13, 2026
d3f1893
Fix subject metadata nwb-inspector test
oruebel Aug 13, 2026
49f9ea6
Potential fix for pull request finding
oruebel Aug 13, 2026
b016125
Potential fix for pull request finding
oruebel Aug 13, 2026
7d16947
Potential fix for pull request finding
oruebel Aug 13, 2026
7bb903e
Address review comment
oruebel Aug 13, 2026
e6427bd
Avoid overwriting exiting subject
oruebel Aug 13, 2026
5e39cd2
Prevent invalid Subject data write
oruebel Aug 13, 2026
91d9a2f
Add draft outline for the TimeIntervals type
oruebel Aug 14, 2026
795eec5
Make TimeIntervals constructor protected
oruebel Aug 14, 2026
80aed1d
Add basic outline for VectorIndex type
oruebel Aug 14, 2026
0c94cc2
Added tages VectorIndex to TimeIntervals and fixed configuration
oruebel Aug 14, 2026
76be639
Merge branch 'main' into copilot/make-subject-required-by-default
oruebel Aug 16, 2026
71a7150
Fix DynamicTable::configureDataObject to exclude VectorIndex from col…
oruebel Aug 16, 2026
9d02b86
Added unit tests for VectorIndex
oruebel Aug 16, 2026
1591e3e
Clean up VectorData.hpp by moving implementations to the cpp file
oruebel Aug 16, 2026
8d4791c
Add support for ragged column write via VectorIndex and DynamicTable
oruebel Aug 16, 2026
e6419c0
Remove TODO items
oruebel Aug 16, 2026
356b537
Merge branch 'schema_2_10' into add_time_intervals
oruebel Aug 16, 2026
292f476
Added unit tests for TimeIntervals
oruebel Aug 16, 2026
82335ea
Updated NWBFile to support standard intervals TimeIintervals tables
oruebel Aug 16, 2026
39c5f7a
Fix cppcheck error in TimeIntervals
oruebel Aug 16, 2026
5402bb0
Suppress false positive cppcheck explicit constructor warnings in Cel…
oruebel Aug 16, 2026
0612740
Fix spell check errors
oruebel Aug 16, 2026
a1b3ade
Fix CI build issue
oruebel Aug 16, 2026
ef2ce8c
Fix linter
oruebel Aug 16, 2026
082a2e0
Fix float comparison in unit test
oruebel Aug 16, 2026
65f3948
Fix ubuntu compiler warning
oruebel Aug 16, 2026
ed4a3d6
Fix NWB Insepctor warning due to empty tables
oruebel Aug 16, 2026
c462a82
Support row-based read for DynamicTable
oruebel Aug 17, 2026
a76691f
Updated CHANGELOG
oruebel Aug 17, 2026
ac268d4
Fix cppcheck suggestion
oruebel Aug 17, 2026
aeb028d
Fix linter
oruebel Aug 17, 2026
8f3f5e7
Add DynamicTable::toString support for printing a DynamicTable
oruebel Aug 17, 2026
cffeb4e
Fix linter
oruebel Aug 17, 2026
154c7f6
Added tutorial for acquisiton of TimeIntervals
oruebel Aug 17, 2026
d953dc0
Fix cppcheck warning and add time_intervals.dox to the page index
oruebel Aug 17, 2026
3007b72
Move note in docs to clean up layout
oruebel Aug 17, 2026
5cf5e1e
Fix cppcheck warning in DynamicTable::toString
oruebel Aug 17, 2026
bc8a095
Update clang-format version in lint.yml
oruebel Aug 17, 2026
6350323
Change Types from a class to a namespace
oruebel Aug 18, 2026
7d80fd8
Enhance type-safety of Types::CellValue
oruebel Aug 18, 2026
62d03bb
Fix formatting of lists in DynamicTabel::toString CSV formatting
oruebel Aug 18, 2026
b259db2
Make virtual in so polymorphism can be used naturally in
oruebel Aug 18, 2026
ee21f21
Add a validation check before subtraction in VectorIndex::readIndexed…
oruebel Aug 18, 2026
1753e37
Fix performance issue in VectorIndex::readIndexedCellValues. Read the…
oruebel Aug 18, 2026
62f256f
Add missing DEFINE_UNNAMED_REGISTERED_FIELD( for time intervals
oruebel Aug 18, 2026
2e7a3fc
Improved the performance of DynamicTable::addRows by caching target c…
oruebel Aug 18, 2026
646b311
Fix performance issue in VectorIndex::readIndexedCellValues. Read the…
oruebel Aug 18, 2026
d0b07a8
Fix cppcheck warning. Replace loop with std::transform
oruebel Aug 18, 2026
fb7d7f5
Enahnce unit tests for VectorIndex and add error checking in VectorIn…
oruebel Aug 19, 2026
1f3eae2
Enhance unit tests for DynamicTable
oruebel Aug 19, 2026
bf2f8f1
Added unit test for VectorData::appendBuffer
oruebel Aug 19, 2026
5db39e1
Review and update VectorIndex unit tests and add error checks
oruebel Aug 19, 2026
f3eaec6
Add unit tests for CellValue
oruebel Aug 19, 2026
aa6f9a8
Fix spelling error in CHANGELOG
oruebel Aug 19, 2026
0e0e324
Clarify comment in VectorData addData
oruebel Aug 19, 2026
b521de2
Fix #327. Update caching of objects to centralize caching in the I/O …
oruebel Aug 20, 2026
9dba136
Fix ubuntu build
oruebel Aug 20, 2026
db4b4bb
Merge branch 'schema_2_10' into add_time_intervals
oruebel Aug 20, 2026
7a2934f
Merge branch 'add_time_intervals' into add_global_object_lookup
oruebel Aug 20, 2026
49739bf
Fix linter
oruebel Aug 20, 2026
aefaddd
Merged BaseIO::getDataSet and getCachedRecordingData to make sure all…
oruebel Aug 20, 2026
a0290e7
Fix minor build warnings on Ubuntu and Windows
oruebel Aug 20, 2026
707bd63
Merge pull request #328 from NeurodataWithoutBorders/add_global_objec…
oruebel Aug 20, 2026
f43adfd
Fix link in CHANGELOG
oruebel Aug 20, 2026
597be22
Make Subject constructor protected
oruebel Aug 20, 2026
8cd3833
Merge branch 'copilot/make-subject-required-by-default' of https://gi…
oruebel Aug 20, 2026
88c3ce9
Merge branch 'schema_2_10' into copilot/make-subject-required-by-default
oruebel Aug 20, 2026
0a430ce
Fix missing subject in unit test from parent branch
oruebel Aug 20, 2026
5dab595
Added missing convenience Subject::create to fix the path
oruebel Aug 20, 2026
425ff13
Fix codespell
oruebel Aug 20, 2026
b164a7a
Added missing Subject to unit test to fix nwb-inspector error
oruebel Aug 20, 2026
87af098
Fix missing Subject in Event workflow
oruebel Aug 20, 2026
2efab35
Allow function changing in SubjectSpec to ease initalization with opt…
oruebel Aug 20, 2026
bef3132
Fix codespell
oruebel Aug 20, 2026
19a366a
Merge branch 'add_time_intervals' into copilot/make-subject-required-…
oruebel Aug 20, 2026
b28ea09
Add missing Subject to time intervals tests
oruebel Aug 20, 2026
699e4de
Updated CHANGELOG to group related changes for the upcoming release
oruebel Aug 20, 2026
a707472
Merge branch 'schema_2_10' into add_time_intervals
oruebel Aug 20, 2026
5903883
Fix linter error
oruebel Aug 20, 2026
0ad29bb
Merge branch 'add_time_intervals' into copilot/make-subject-required-…
oruebel Aug 20, 2026
3b0005e
Added missing Subject in new unit test
oruebel Aug 20, 2026
5ad93fe
Make events/ and intervals/ groups optional as defined in the schema
oruebel Aug 21, 2026
6f3a3b6
Support optional timestamp.resolution and duration.resolution and opt…
oruebel Aug 21, 2026
6d38f28
Merge branch 'schema_2_10' into add_time_intervals
oruebel Aug 21, 2026
86625d8
Fix cppcheck warning in unit tests
oruebel Aug 22, 2026
d262d8b
Merge branch 'add_time_intervals' into copilot/make-subject-required-…
oruebel Aug 22, 2026
526e6f5
Fix failing unit test
oruebel Aug 22, 2026
6d04453
Fix NWB scherma version in change log
oruebel Aug 22, 2026
1f64903
Fix column docstrings in EventsTable::createDefaultDataSpecs to match…
oruebel Aug 22, 2026
2d43100
Update MeaningsTable.target doc
oruebel Aug 22, 2026
64cbf76
Merge branch 'schema_2_10' into add_time_intervals
oruebel Aug 23, 2026
35d2eaf
Merge branch 'add_time_intervals' into copilot/make-subject-required-…
oruebel Aug 23, 2026
907d4f9
Update changelog to fix formatting
oruebel Aug 23, 2026
dd449bb
Fix bad link in CHANGLEOG
oruebel Aug 30, 2026
4fb365e
Merge branch 'schema_2_10' into add_time_intervals
oruebel Aug 31, 2026
b125ff5
Fix linter
oruebel Aug 31, 2026
07deef1
Merge branch 'add_time_intervals' into copilot/make-subject-required-…
oruebel Aug 31, 2026
a19c381
Test creating EventTable with default spec
oruebel Aug 31, 2026
d89055e
Update docs/pages/userdocs/events.dox
oruebel Sep 2, 2026
af0af17
add examples/testTimeIntervalsWorkflowExamples.cpp to test build
oruebel Sep 2, 2026
cdd6f44
Renamed NWBFile::createTimeIntervals to NWBFile::createTimeIntervalsI…
oruebel Sep 2, 2026
7b84c44
Revise CHANGELOG with migration and breaking changes
oruebel Sep 2, 2026
8d9010e
Fix spelling errors in time_intervals documentation
oruebel Sep 2, 2026
5b7943a
Clarify readRows usage in comments
oruebel Sep 2, 2026
ef3846f
Update readRows method parameters in documentation
oruebel Sep 2, 2026
87e1574
Fix spelling in docs and code formatting
oruebel Sep 2, 2026
3c78f84
Update CHANGELOG with new features and tutorials
oruebel Sep 2, 2026
a4b1e96
Change property status check to use Status::Success
oruebel Sep 2, 2026
62d9bf9
Refactor target attribute initialization
oruebel Sep 2, 2026
3af3042
Added isPathOrDescendant utility to simplify path initalization check…
oruebel Sep 2, 2026
7c3630c
Updated changelog
oruebel Sep 2, 2026
62762d2
Added BaseDataType::isCompatibleCellValue() and isCompatibleVector() …
oruebel Sep 2, 2026
b37aba0
Added missing unit tests for BaseDataType isCompatibleCellValue()  an…
oruebel Sep 2, 2026
64ee4a6
Update changelog
oruebel Sep 2, 2026
bae5dda
Updated VectorIndex::readIndexedCellValues to return the correct dtyp…
oruebel Sep 2, 2026
59a9718
Merge branch 'add_time_intervals' into copilot/make-subject-required-…
oruebel Sep 2, 2026
616b39e
Ensure cached registered objects match their concrete on-disk type, keep
oruebel Sep 2, 2026
5131191
Fix status checks in the Subject class
oruebel Sep 2, 2026
8fcb541
Fix  NWBFile::initialize()  docstring to clarify subject creation
oruebel Sep 2, 2026
71c97c3
Merge pull request #320 from NeurodataWithoutBorders/copilot/make-sub…
oruebel Sep 3, 2026
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
2 changes: 2 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install clang-format
run: pipx install clang-format
- name: Lint
run: cmake -D FORMAT_COMMAND=clang-format -P cmake/lint.cmake
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ jobs:
run: |
python -m pip install --upgrade pip
python -m pip install nwbinspector
nwbinspector nwb_files --threshold BEST_PRACTICE_VIOLATION --ignore=check_subject_exists --json-file-path out.json
nwbinspector nwb_files --threshold BEST_PRACTICE_VIOLATION --ignore=check_electrodes_location_allen_ccf --json-file-path out.json
if ! grep -q '"messages": \[\]' out.json; then
echo "NWBInspector found issues in the NWB files"
exit 1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/upgrade_schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
run: |
mkdir -p nwb_files
cp build/tests/data/*.nwb nwb_files/
nwbinspector nwb_files --threshold BEST_PRACTICE_VIOLATION --ignore=check_subject_exists --json-file-path out.json
nwbinspector nwb_files --threshold BEST_PRACTICE_VIOLATION --ignore=check_electrodes_location_allen_ccf --json-file-path out.json
if ! grep -q '"messages": \[\]' out.json; then
echo "NWBInspector found issues in the NWB files"
exit 1
Expand Down
83 changes: 57 additions & 26 deletions CHANGELOG.md

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,21 @@ add_library(
src/nwb/device/Device.cpp
src/nwb/ecephys/ElectricalSeries.cpp
src/nwb/ecephys/SpikeEventSeries.cpp
src/nwb/epoch/TimeIntervals.cpp
src/nwb/event/EventsTable.cpp
src/nwb/event/TimestampVectorData.cpp
src/nwb/event/DurationVectorData.cpp
src/nwb/file/ElectrodeGroup.cpp
src/nwb/file/ElectrodesTable.cpp
src/nwb/file/Subject.cpp
src/nwb/misc/AnnotationSeries.cpp
src/nwb/hdmf/base/Container.cpp
src/nwb/hdmf/base/Data.cpp
src/nwb/hdmf/table/DynamicTable.cpp
src/nwb/hdmf/table/ElementIdentifiers.cpp
src/nwb/hdmf/table/MeaningsTable.cpp
src/nwb/hdmf/table/VectorData.cpp
src/nwb/hdmf/table/ElementIdentifiers.cpp
src/nwb/hdmf/table/VectorIndex.cpp
src/spec/NamespaceRegistry.cpp
)

Expand Down
1 change: 1 addition & 0 deletions docs/Doxyfile.in
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,4 @@ PROJECT_LOGO = "@PROJECT_SOURCE_DIR@/resources/logo/aqnwb-logo.png"

# Fail on warnings
WARN_AS_ERROR = FAIL_ON_WARNINGS_PRINT
DOT_GRAPH_MAX_NODES = 100
1 change: 1 addition & 0 deletions docs/pages/1_userdocs.dox
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* - \subpage user_install_page
* - \subpage workflow
* - \subpage events
* - \subpage time_intervals
* - \subpage read_page
* - \subpage reads3_page
* - \subpage hdf5io
Expand Down
13 changes: 13 additions & 0 deletions docs/pages/devdocs/read_design.dox
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,12 @@
* - The \ref AQNWB::NWB::RegisteredType::create "RegisteredType::create" method is used to create
* an instance of a registered subclass by name. This method looks up the subclass name in the
* registry and calls the corresponding factory function to create an instance.
* - Each I/O object caches one canonical registered object per file path.
* The path-only \ref AQNWB::NWB::RegisteredType::create "create" overload
* resolves the object's on-disk ``namespace`` and ``neurodata_type`` before
* reusing a cache entry. A request whose registered type conflicts with an
* existing cache entry fails rather than returning a different type or
* creating a second object for the same path.
*
* 5. **Class Name and Namespace Retrieval**:
* - The \ref AQNWB::NWB::RegisteredType::getTypeName "getTypeName" and
Expand Down Expand Up @@ -339,6 +345,13 @@
* with the type already set. The same approach is also applied in the case of \ref AQNWB::NWB::Data "Data" and its derived
* class \ref AQNWB::NWB::DataTyped "DataTyped".
*
* ``DataTyped<DTYPE>``, ``VectorDataTyped<DTYPE>``, and
* ``NWBDataTyped<DTYPE>`` are typed read facades, not canonical registered
* objects. They are intentionally not stored in
* \ref AQNWB::IO::RecordingObjects "RecordingObjects"; use their specialized
* ``create`` or ``from...`` methods to obtain a typed view of a dataset without
* changing the canonical object cached for its path.
*
* For further details and alternative approaches for implementing templated \ref AQNWB::NWB::RegisteredType "RegisteredType"
* classes, see \ref implement_templated_registered_type.
*
Expand Down
36 changes: 23 additions & 13 deletions docs/pages/devdocs/record_design.dox
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
* AqNWB records datasets efficiently via \ref AQNWB::IO::BaseRecordingData "BaseRecordingData" objects. The main components involved in
* writing data to an NWB file via AqNWB are:
*
* 1. \ref DEFINE_DATASET_FIELD Macro
* 1. \ref DEFINE_DATASET_FIELD Macro and \ref AQNWB::NWB::RegisteredType "RegisteredType"
* - The \ref DEFINE_DATASET_FIELD macro not only defines methods for reading datasets for a particular neurodata_type (as described in \ref read_design_page),
* but also defines methods for retrieving \ref AQNWB::IO::BaseRecordingData "BaseRecordingData" objects that are used for recording to individual datasets.
* For each dataset field defined with this macro, a corresponding method is generated that returns a \ref AQNWB::IO::BaseRecordingData "BaseRecordingData"
Expand All @@ -31,13 +31,14 @@
* - It provides methods for writing data blocks to the dataset, such as \ref AQNWB::IO::BaseRecordingData::writeDataBlock "writeDataBlock",
* which can handle different data types and dimensions.
*
* 3. \ref AQNWB::NWB::RegisteredType "RegisteredType"
* - \ref AQNWB::NWB::RegisteredType "RegisteredType" maintains a cache of \ref AQNWB::IO::BaseRecordingData "BaseRecordingData" objects via the `m_recordingDataCache` member.
* 3. \ref AQNWB::IO::BaseIO::m_recordingDataCache "BaseIO::m_recordingDataCache"
* - \ref AQNWB::IO::BaseIO "BaseIO" maintains a cache of \ref AQNWB::IO::BaseRecordingData "BaseRecordingData" objects that belong to the I/O object/file.
* This cache allows reusing the same \ref AQNWB::IO::BaseRecordingData "BaseRecordingData" object when it is requested multiple times,
* improving performance and retaining the recording position. The cache is essential for writing data to the dataset in a streaming fashion,
* as it ensures that each write continues from where the previous write left off. The cache also avoids the need for manually maintaining
* the objects and allows caching of an arbitrary number of \ref AQNWB::IO::BaseRecordingData "BaseRecordingData" object such that the
* individual neurodata_type classes do not need to worry about maintaining their recording state.
* individual RegisteredType classes (i.e., neurodata_types) do not need to worry about maintaining their recording state, but the state is maintained
* centrally in the I/O object they belong to.
*
* 4. \ref AQNWB::IO::BaseIO "BaseIO"
* - \ref AQNWB::IO::BaseIO "BaseIO" and its implementations (e.g., \ref AQNWB::IO::HDF5::HDF5IO "HDF5IO") are responsible for
Expand All @@ -64,6 +65,7 @@
* <tr><td align="left">+ getDataSet(): BaseRecordingData</td></tr>
* <tr><td colspan="2" bgcolor="lightgray"><b>Attributes</b></td></tr>
* <tr><td align="left">- m_recording_objects: std::shared_ptr&lt;RecordingObjects&gt;</td></tr>
* <tr><td align="left">- m_recordingDataCache: std::unordered_map&lt;std::string, std::shared_ptr&lt;BaseRecordingData&gt;&gt;</td></tr>
* </table>
* >
* ];
Expand Down Expand Up @@ -91,11 +93,8 @@
* label=<
* <table border="0" cellborder="1" cellspacing="0">
* <tr><td colspan="2" bgcolor="lightgray"><b>RegisteredType</b></td></tr>
* <tr><td colspan="2" bgcolor="lightgray"><b>Functions</b></td></tr>
* <tr><td align="left">+ clearRecordingDataCache(): void</td></tr>
* <tr><td colspan="2" bgcolor="lightgray"><b>Attributes</b></td></tr>
* <tr><td align="left">+ m_recordingDataCache: std::unordered_map</td></tr>
* <tr><td align="left">+ m_io: std::shared_ptr&lt;BaseIO&gt;</td></tr>
* <tr><td align="left">+ m_io: std::weak_ptr&lt;BaseIO&gt;</td></tr>
* <tr><td align="left">+ m_path: std::string</td></tr>
* </table>
* >
Expand All @@ -119,7 +118,8 @@
*
* RegisteredType -> Container [arrowhead=empty, style=dashed];
* Container -> BaseRecordingData [label="created by functions defined via \nDEFINE_DATASET_FIELD"];
* RegisteredType -> BaseRecordingData [label="caches"];
* HDF5IO -> BaseRecordingData [label="caches"];
* HDF5IO -> RegisteredType [label="caches"];
* BaseRecordingData -> HDF5IO [label="uses for\nwriting data"];
* HDF5IO -> NWBFile [label="write data"];
* }
Expand Down Expand Up @@ -197,7 +197,6 @@
* \ref AQNWB::IO::RecordingObjects::addRecordingObject "addRecordingObject" and
* \ref AQNWB::IO::RecordingObjects::addRecordingObject "getRecordingObject" methods.
* - **Finalization of recording objects:** via \ref AQNWB::IO::RecordingObjects::finalize "finalize"
* - **Clearing of cached BaseRecordingData objects** via \ref AQNWB::IO::RecordingObjects::clearRecordingDataCache "clearRecordingDataCache";
*
* \section record_design_nwbio_utils NWB I/O convenience utilities
*
Expand Down Expand Up @@ -233,9 +232,20 @@
* facilitate reliable memory management.
*
* \note
* Registration with the \ref AQNWB::IO::RecordingObjects "RecordingObjects" of the I/O occurs when a
* \ref AQNWB::NWB::RegisteredType "RegisteredType" is being created via the static
* \ref AQNWB::NWB::RegisteredType::create "RegisteredType::create" factory methods.
* Canonical \ref AQNWB::NWB::RegisteredType "RegisteredType" objects created
* through \ref AQNWB::NWB::RegisteredType::create "RegisteredType::create"
* are registered with the I/O's \ref AQNWB::IO::RecordingObjects
* "RecordingObjects". The cache has one canonical registered object per I/O
* and path; repeated compatible factory calls return that same instance. A
* request for a conflicting registered type fails rather than creating a
* second object for the path.
*
* Typed dataset facades such as \ref AQNWB::NWB::DataTyped "DataTyped",
* \ref AQNWB::NWB::VectorDataTyped "VectorDataTyped", and
* \ref AQNWB::NWB::NWBDataTyped "NWBDataTyped" are intentionally transient
* and are not registered with RecordingObjects. They share the I/O's
* BaseRecordingData cache with the canonical object when accessing the same
* dataset.
*
* \section recording_design_further_reading Further Reading
*
Expand Down
36 changes: 23 additions & 13 deletions docs/pages/devdocs/registered_types.dox
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@
* DEFINE_ATTRIBUTE_FIELD(getDescription, std::string, "description", Description of the data)
* @endcode
* and in for datasets also a method for retrieving a \ref AQNWB::IO::BaseRecordingData "BaseRecordingData"
* wrapper used for recording data to a dataset.
* wrapper used for recording data to a dataset. The \ref AQNWB::IO::BaseRecordingData "BaseRecordingData"
* objects are cached by the \ref AQNWB::IO::BaseIO "BaseIO" object to ensure that the current
* state of the recording process (e.g., the \ref AQNWB::IO::BaseRecordingData::getPosition "position")
* are stored.
* @code
* DEFINE_DATASET_FIELD(getData, recordData, float, "data", The main data)
* @endcode
Expand Down Expand Up @@ -389,19 +392,25 @@
* the \ref DEFINE_REGISTERED_FIELD macro, we can set the data type for read at compile time, simplifying read.
*
* \note
* Since the templacted \ref AQNWB::NWB::VectorDataTyped "VectorDataTyped<DTYPE>" does not call
* \ref REGISTER_SUBCLASS we must manually declare: 1) `friend class AQNWB::NWB::RegisteredType;`
* and 2) `using VectorData::VectorData;` (as protected) to make sure we have access to the
* constructors and define `static std::shared_ptr<VectorDataTyped> create( const std::string& path, std::shared_ptr<AQNWB::IO::BaseIO> io) { return RegisteredType::create<VectorDataTyped>(path, io); }`
* to make sure we have the factory method available.
*
* Since the templated \ref AQNWB::NWB::VectorDataTyped
* "VectorDataTyped<DTYPE>" does not call \ref REGISTER_SUBCLASS, it must
* declare `friend class AQNWB::NWB::RegisteredType;` and inherit the
* protected `VectorData` constructors with `using VectorData::VectorData;`
* so its specialized factory can construct it. That factory must create a
* transient typed view rather than delegate to
* \ref AQNWB::NWB::RegisteredType::create
* "RegisteredType::create<T>", because that factory registers canonical
* objects in \ref AQNWB::IO::RecordingObjects "RecordingObjects".
* `DataTyped<DTYPE>` and `NWBDataTyped<DTYPE>` follow the same transient
* typed-view pattern; only their non-templated base classes are canonical
* registered types.
*
* \par
* \note
* The `std::unique_ptr<..>` template type is not covariant, i.e., `std::unique_ptr<DerivedClass>` does
* not automatically convert to `std::unique_ptr<BaseClass>`. I.e., while \ref AQNWB::NWB::VectorDataTyped "VectorDataTyped<DTYPE>"
* can be used anywhere \ref AQNWB::NWB::VectorData "VectorData" is being used, when using `std::unique_ptr<..>`
* we cannot rely on the compiler to automatically upcast for us, but we will need to explicitly release
* and upcast `std::unique_ptr<VectorDataTyped<DTYPE>` if `std::unique_ptr<VectorData>` is required. Since
* defining the `DTYPE` is primarily useful for read, we therefore typically use
* AqNWB uses `std::shared_ptr` for registered and typed-view objects, so a
* `std::shared_ptr<VectorDataTyped<DTYPE>>` converts to
* `std::shared_ptr<VectorData>` when needed. Since defining `DTYPE` is
* primarily useful for read, we therefore typically use
* \ref AQNWB::NWB::VectorDataTyped "VectorDataTyped<DTYPE>" on read while
* using \ref AQNWB::NWB::VectorData "VectorData" otherwise.
*
Expand Down Expand Up @@ -529,6 +538,7 @@
* than the requested version. In practice, this is mainly relevant if you are generating classes for the
* NWB `core` and `hdmf-common` namespaces.
*
* \par
* \note
* When generating the test app via the `--generate-test-app` option we also
* need to generate the schema headers files and save them in the `/spec` folder.
Expand Down
1 change: 1 addition & 0 deletions docs/pages/userdocs/events.dox
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
*
* After `startRecording`, retrieve each column object via the corresponding
* `read*Column()` accessor on the table, then call
* \ref AQNWB::NWB::Data::recordData "recordData" on the column and
Comment thread
oruebel marked this conversation as resolved.
* \ref AQNWB::IO::BaseRecordingData::writeDataBlock "writeDataBlock" to write the
* full column vector in one call. Finally, set the row IDs with
* \ref AQNWB::NWB::DynamicTable::setRowIDs "setRowIDs".
Expand Down
61 changes: 61 additions & 0 deletions docs/pages/userdocs/read.dox
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,67 @@
*
* \snippet tests/examples/test_ecephys_data_read.cpp example_use_std_variant_to_compute_on_data
*
* \subsection read_example_dynamic_table Reading data from DynamicTable objects
*
* First, let's get the table we want to read from. In this example, we'll use the `InvalidTimes` table.
* We can also see the standard approach of reading a single column's data, which is efficient when you
* only need data from one or a few columns.
*
* \snippet tests/examples/test_ecephys_data_read.cpp example_read_dynamic_table_rows_setup_snippet
*
* In addition to reading individual columns, \ref AQNWB::NWB::DynamicTable::readRows "DynamicTable::readRows(start, count, colNames)"
* provides a convenient method for reading a consecutive range of rows from the table. Note, `start`
* as the starting row index is *inclusive*, while `count` is the number of rows to read.
* `colNames` is optional and allows reading of a subset of columns.
*
* This method returns a `std::vector<RowData>`, where each \ref AQNWB::Types::RowData "RowData" is a map
* of column names to their corresponding \ref AQNWB::Types::CellValue "CellValue" values for that row.
*
* Now, let's use the `readRows` approach to read a slice of rows (e.g., rows 0 to 2). This is convenient
* when you need to process data row-by-row across multiple columns.
*
* \snippet tests/examples/test_ecephys_data_read.cpp example_read_dynamic_table_rows_all_cols_snippet
*
* \note
* The \ref AQNWB::Types::CellValue "CellValue" struct holds an `std::variant`. \ref AQNWB::Types::CellValue "CellValue"
* provides an implicit conversion operator for convenient shorthand syntax when extracting
* values (e.g., `float val = row.at("col");`). However, because it relies on implicit conversion, it
* could lead to ambiguous function calls or unexpected behavior if the compiler infers the wrong type.
* For a safer and more explicit approach, it is recommended to use the `get<T>()` method
* (e.g., `float val = row.at("col").get<float>();`). However, in either approach, a mismatch
* between the underlying type and the target type will result in a `std::bad_variant_access`
* runtime error. For additional safety:
* - <b>Check before you get via \ref AQNWB::Types::CellValue.holds_alternative "CellValue.holds_alternative":</b>
* You can check if the variant holds a specific type before trying to extract it. If it returns true,
* `std::get` is guaranteed to succeed.
* - **Get a pointer via `std::get_if`:** Instead of `std::get` (which throws on failure), you can use
* `std::get_if`. It returns a pointer to the value if the type matches, or a `nullptr` if it doesn't
* so you then check if the pointer is null.
* - **Handle all types via `std::visit`:** Using `std::visit` you provide a function
* (or a generic lambda with `auto`) that can accept any of the possible types, and `std::visit`
* will automatically call your function with the correct type that is currently stored. This
* eliminates the need to guess the type and prevents the exception entirely.
*
* You can also specify a subset of columns to read by passing a vector of column names as the third
* argument to `readRows`. This can improve performance if you only need data from specific columns.
*
* \snippet tests/examples/test_ecephys_data_read.cpp example_read_dynamic_table_rows_specific_cols_snippet
*
* Finally, you can easily print the contents of a `DynamicTable` using the
* \ref AQNWB::NWB::DynamicTable::toString "DynamicTable::toString" method. This is useful for debugging
* and inspecting the table's contents.
*
* \snippet tests/examples/test_ecephys_data_read.cpp example_read_dynamic_table_to_string_snippet
*
* This will output the table contents in a readable CSV format:
*
* \code
* InvalidTimes table contents:
* id,start_time,stop_time,tags
* 0,1.000000,2.000000,"[device_error, user_error]"
* 1,3.000000,4.000000,"[external_interference]"
* 2,5.000000,6.000000,"[lost_connection, external_interference, user_error]"
* \endcode
*
* \section read_further_reading Further reading
* - \ref read_design_page discusses the underlying software design of AqNWB for data read
Expand Down
Loading
Loading