Skip to content

Commit ed26362

Browse files
authored
Merge pull request #683 from UWB-Biocomputing/issue-658-add-compileHistoriesforUpdatedTypeDYNAMICForHdf5Recorder
Implement compileHistories() for UpdatedType:: DYNAMI for Hdf5 Recorder
2 parents f60cd2e + 391cece commit ed26362

File tree

10 files changed

+169
-54
lines changed

10 files changed

+169
-54
lines changed

Simulator/Core/Model.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Model::Model()
6767
void Model::saveResults()
6868
{
6969
if (recorder_ != nullptr) {
70-
recorder_->saveSimData(layout_->getVertices());
70+
recorder_->saveSimData();
7171
}
7272
}
7373

@@ -146,7 +146,7 @@ void Model::updateHistory()
146146
LOG4CPLUS_INFO(fileLogger_, "ERROR: Recorder class is null.");
147147
}
148148
if (recorder_ != nullptr) {
149-
recorder_->compileHistories(layout_->getVertices());
149+
recorder_->compileHistories();
150150
}
151151
}
152152

Simulator/Recorders/Hdf5Recorder.cpp

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,12 @@ void Hdf5Recorder::term()
104104
}
105105

106106
// create the dataset for constant variable and store the data to dataset
107-
void Hdf5Recorder::saveSimData(const AllVertices &neurons)
107+
void Hdf5Recorder::saveSimData()
108108
{
109109
// Initialize datasets for constant variables
110110
for (auto &variableInfo : variableTable_) {
111-
if (variableInfo.variableType_ == UpdatedType::CONSTANT) {
111+
if (variableInfo.variableLocation_.getNumElements() > 0
112+
&& variableInfo.variableType_ == UpdatedType::CONSTANT) {
112113
// Define dimensions for the constant dataset
113114
hsize_t constantDims[1]
114115
= {static_cast<hsize_t>(variableInfo.variableLocation_.getNumElements())};
@@ -122,6 +123,97 @@ void Hdf5Recorder::saveSimData(const AllVertices &neurons)
122123
}
123124
}
124125

126+
// Processes and updates HDF5 datasets for variables marked as DYNAMIC
127+
void Hdf5Recorder::compileHistories()
128+
{
129+
// Define the maximum chunk size for datasets to optimize storage and access
130+
// This defines the maximum number of elements per chunk
131+
// If the dataset size exceeds this value, HDF5 will create multiple chunks to store the data
132+
const hsize_t max_chunk_size = 1024;
133+
134+
// Iterate over each variableInfo object in the variable table
135+
for (auto &variableInfo : variableTable_) {
136+
// Process only variables marked as DYNAMIC, which change over time
137+
if (variableInfo.variableType_ == UpdatedType::DYNAMIC) {
138+
// Check if there are elements to process
139+
if (variableInfo.variableLocation_.getNumElements() > 0) {
140+
// Determine the new dimensions for the dataset based on the number of elements
141+
hsize_t newDims[1]
142+
= {static_cast<hsize_t>(variableInfo.variableLocation_.getNumElements())};
143+
144+
// Create the dataset if it does not already exist
145+
if (!H5Iis_valid(variableInfo.hdf5DataSet_.getId())) {
146+
// Create initial dataspace with unlimited maximum dimensions
147+
hsize_t maxDims[1] = {H5S_UNLIMITED};
148+
DataSpace initialSpace(1, newDims, maxDims);
149+
150+
// Create dataset creation properties and set chunk size for efficiency
151+
DSetCreatPropList cparms;
152+
hsize_t chunk_dims[1] = {std::min(newDims[0], max_chunk_size)};
153+
cparms.setChunk(1, chunk_dims);
154+
155+
// Create the dataset with specified name, datatype, initial space, and properties
156+
variableInfo.hdf5DataSet_ = resultOut_->createDataSet(
157+
variableInfo.variableName_, variableInfo.hdf5Datatype_, initialSpace, cparms);
158+
159+
// Write the initial data to the dataset
160+
variableInfo.captureData();
161+
} else {
162+
// Handle the case where the dataset already exists
163+
164+
// Get the current dimensions of the dataset
165+
DataSpace currentSpace = variableInfo.hdf5DataSet_.getSpace();
166+
hsize_t currentDims[1];
167+
currentSpace.getSimpleExtentDims(currentDims, nullptr);
168+
169+
// Calculate the new dimensions after appending the new data
170+
hsize_t appendDims[1] = {currentDims[0] + newDims[0]};
171+
variableInfo.hdf5DataSet_.extend(appendDims);
172+
173+
// Select the hyperslab in the extended portion of the dataset
174+
DataSpace fileSpace = variableInfo.hdf5DataSet_.getSpace();
175+
hsize_t offset[1] = {currentDims[0]};
176+
fileSpace.selectHyperslab(H5S_SELECT_SET, newDims, offset);
177+
178+
// Create a memory dataspace for the new data
179+
DataSpace memSpace(1, newDims);
180+
181+
// Prepare the data buffer and write the new data to the dataset
182+
if (variableInfo.hdf5Datatype_ == PredType::NATIVE_FLOAT) {
183+
vector<float> dataBuffer(variableInfo.variableLocation_.getNumElements());
184+
for (size_t i = 0; i < variableInfo.variableLocation_.getNumElements(); ++i) {
185+
dataBuffer[i] = get<float>(variableInfo.variableLocation_.getElement(i));
186+
}
187+
variableInfo.hdf5DataSet_.write(dataBuffer.data(), variableInfo.hdf5Datatype_,
188+
memSpace, fileSpace);
189+
} else if (variableInfo.hdf5Datatype_ == PredType::NATIVE_INT) {
190+
vector<int> dataBuffer(variableInfo.variableLocation_.getNumElements());
191+
for (size_t i = 0; i < variableInfo.variableLocation_.getNumElements(); ++i) {
192+
dataBuffer[i] = get<int>(variableInfo.variableLocation_.getElement(i));
193+
}
194+
variableInfo.hdf5DataSet_.write(dataBuffer.data(), variableInfo.hdf5Datatype_,
195+
memSpace, fileSpace);
196+
} else if (variableInfo.hdf5Datatype_ == PredType::NATIVE_UINT64) {
197+
vector<uint64_t> dataBuffer(variableInfo.variableLocation_.getNumElements());
198+
for (size_t i = 0; i < variableInfo.variableLocation_.getNumElements(); ++i) {
199+
dataBuffer[i] = get<uint64_t>(variableInfo.variableLocation_.getElement(i));
200+
}
201+
variableInfo.hdf5DataSet_.write(dataBuffer.data(), variableInfo.hdf5Datatype_,
202+
memSpace, fileSpace);
203+
} else {
204+
// Throw an exception if the data type is unsupported
205+
throw runtime_error("Unsupported data type for variable: "
206+
+ variableInfo.variableName_);
207+
}
208+
}
209+
}
210+
}
211+
212+
// Call startNewEpoch() to prepare for new data input
213+
variableInfo.variableLocation_.startNewEpoch();
214+
}
215+
}
216+
125217
/// Receives a recorded variable entity from the variable owner class
126218
/// used when the return type from recordable variable is supported by Recorder
127219
/**

Simulator/Recorders/Hdf5Recorder.h

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,13 @@ class Hdf5Recorder : public Recorder {
5858
/// Terminate process
5959
virtual void term() override;
6060

61-
// TODO: No parameters needed (AllVertices &vertices)
61+
6262
/// Compile/capture variable history information in every epoch
63-
virtual void compileHistories(AllVertices &neurons) override
64-
{
65-
}
63+
virtual void compileHistories() override;
64+
6665

67-
// TODO: No parameters needed (AllVertices &vertices)
6866
/// Writes simulation results to an output destination.
69-
virtual void saveSimData(const AllVertices &neurons) override;
67+
virtual void saveSimData() override;
7068

7169
/// Prints out all parameters to logging file.
7270
/// Registered to OperationManager as Operation::printParameters
@@ -202,29 +200,6 @@ class Hdf5Recorder : public Recorder {
202200

203201
// Member variables for HDF5 datasets
204202
H5File *resultOut_;
205-
/*DataSet dataSetXloc_;
206-
DataSet dataSetYloc_;
207-
DataSet dataSetNeuronTypes_;
208-
DataSet dataSetNeuronThresh_;
209-
DataSet dataSetStarterNeurons_;
210-
DataSet dataSetTsim_;
211-
DataSet dataSetSimulationEndTime_;
212-
DataSet dataSetProbedNeurons_;
213-
DataSet dataSetSpikesHist_;
214-
DataSet dataSetSpikesProbedNeurons_;
215-
216-
// HDF5 dataset names
217-
const H5std_string nameSpikesHist = "spikesHistory";
218-
const H5std_string nameXloc = "xloc";
219-
const H5std_string nameYloc = "yloc";
220-
const H5std_string nameNeuronTypes = "neuronTypes";
221-
const H5std_string nameNeuronThresh = "neuronThresh";
222-
const H5std_string nameStarterNeurons = "starterNeurons";
223-
const H5std_string nameTsim = "Tsim";
224-
const H5std_string nameSimulationEndTime = "simulationEndTime";
225-
const H5std_string nameSpikesProbedNeurons = "spikesProbedNeurons";
226-
const H5std_string nameAttrPNUnit = "attrPNUint";
227-
const H5std_string nameProbedNeurons = "probedNeurons";*/
228203

229204
// Keep track of where we are in incrementally writing spikes
230205
vector<hsize_t> offsetSpikesProbedNeurons_;

Simulator/Recorders/NG911/Xml911Recorder.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ void Xml911Recorder::getValues()
3333
/// Compile history information in every epoch
3434
///
3535
/// @param[in] vertices The entire list of vertices.
36-
void Xml911Recorder::compileHistories(AllVertices &vertices)
36+
void Xml911Recorder::compileHistories()
3737
{
3838
}
3939

4040
/// Writes simulation results to an output destination.
4141
///
4242
/// @param vertices the Vertex list to search from.
43-
void Xml911Recorder::saveSimData(const AllVertices &vertices)
43+
void Xml911Recorder::saveSimData()
4444
{
4545
auto &conns = Simulator::getInstance().getModel().getConnections();
4646
Connections911 &conns911 = dynamic_cast<Connections911 &>(conns);

Simulator/Recorders/NG911/Xml911Recorder.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ class Xml911Recorder : public XmlRecorder {
4040
/// Compile history information in every epoch
4141
///
4242
/// @param[in] vertices The entire list of vertices.
43-
virtual void compileHistories(AllVertices &vertices) override;
43+
virtual void compileHistories() override;
4444

4545
/// Writes simulation results to an output destination.
4646
///
4747
/// @param vertices the Vertex list to search from.
48-
virtual void saveSimData(const AllVertices &vertices) override;
48+
virtual void saveSimData() override;
4949

5050
/// Prints out all parameters to logging file.
5151
/// Registered to OperationManager as Operation::printParameters

Simulator/Recorders/Recorder.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ class Recorder {
5252
/// Terminate process
5353
virtual void term() = 0;
5454

55-
// TODO: No parameters needed (AllVertices &vertices)
55+
5656
/// Compile/capture variable history information in every epoch
57-
virtual void compileHistories(AllVertices &vertices) = 0;
57+
virtual void compileHistories() = 0;
58+
5859

59-
// TODO: No parameters needed (AllVertices &vertices)
6060
/// Writes simulation results to an output destination.
61-
virtual void saveSimData(const AllVertices &vertices) = 0;
61+
virtual void saveSimData() = 0;
6262

6363
/// Prints loaded parameters to logging file.
6464
virtual void printParameters() = 0;

Simulator/Recorders/XmlRecorder.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ void XmlRecorder::term()
7373

7474
// TODO : @param[in] vertices will be removed eventually after HDF5Recorder implementing
7575
/// Compile history information in every epoch
76-
void XmlRecorder::compileHistories(AllVertices &vertices)
76+
void XmlRecorder::compileHistories()
7777
{
7878
//capture data information in each epoch
7979
for (int rowIndex = 0; rowIndex < variableTable_.size(); rowIndex++) {
@@ -86,7 +86,7 @@ void XmlRecorder::compileHistories(AllVertices &vertices)
8686

8787
// TODO : @param[in] vertices will be removed eventually after HDF5Recorder implementing
8888
/// Writes simulation results to an output destination.
89-
void XmlRecorder::saveSimData(const AllVertices &vertices)
89+
void XmlRecorder::saveSimData()
9090
{
9191
// Write XML header information:
9292
string header = "<?xml version=\"1.0\" standalone=\"no\"?>\n";

Simulator/Recorders/XmlRecorder.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,13 @@ class XmlRecorder : public Recorder {
6060
/// Terminate process
6161
virtual void term() override;
6262

63-
// TODO: No parameters needed (AllVertices &vertices)
63+
6464
/// Compile/capture variable history information in every epoch
65-
virtual void compileHistories(AllVertices &vertices) override;
65+
virtual void compileHistories() override;
66+
6667

67-
// TODO: No parameters needed (AllVertices &vertices)
6868
/// Writes simulation results to an output destination.
69-
virtual void saveSimData(const AllVertices &vertices) override;
69+
virtual void saveSimData() override;
7070

7171
/// Prints out all parameters to logging file.
7272
/// Registered to OperationManager as Operation::printParameters

Testing/UnitTesting/Hdf5RecorderTests.cpp

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,8 @@ TEST(Hdf5RecorderTest, SaveSimDataTest)
8686
// Register the variable with Hdf5Recorder
8787
recorder.registerVariable("test_var1", eventBuffer, Recorder::UpdatedType::CONSTANT);
8888

89-
// Create a unique_ptr to an empty AllVertices object
90-
unique_ptr<AllVertices> vertices = 0;
91-
9289
// Call saveSimData() to write the data to the file
93-
recorder.saveSimData(*vertices);
90+
recorder.saveSimData();
9491

9592
// Open the HDF5 file and read back the data
9693
H5File file(outputFile, H5F_ACC_RDONLY);
@@ -112,4 +109,55 @@ TEST(Hdf5RecorderTest, SaveSimDataTest)
112109
}
113110
}
114111

112+
// Define the test case for compiling histories
113+
TEST(Hdf5RecorderTest, CompileHistoriesTest)
114+
{
115+
// Define a temporary file path for testing
116+
std::string outputFile
117+
= "../Testing/UnitTesting/TestOutput/Hdf5test_output_compile_histories.h5";
118+
119+
// Create an instance of Hdf5Recorder
120+
Hdf5Recorder recorder(outputFile);
121+
recorder.init();
122+
123+
// Create and configure variables for testing
124+
EventBuffer eventBufferInt(5); // Example with int type
125+
126+
// Register the variable with Hdf5Recorder as DYNAMIC
127+
recorder.registerVariable("test_var_int", eventBufferInt, Recorder::UpdatedType::DYNAMIC);
128+
129+
130+
// Call compileHistories() multiple times to simulate multiple epochs
131+
for (int epoch = 0; epoch < 3; ++epoch) {
132+
// Clear and insert new events to simulate new data each epoch
133+
eventBufferInt.clear();
134+
eventBufferInt.insertEvent(1 * (epoch + 1));
135+
eventBufferInt.insertEvent(2 * (epoch + 1));
136+
eventBufferInt.insertEvent(3 * (epoch + 1));
137+
eventBufferInt.insertEvent(4 * (epoch + 1));
138+
eventBufferInt.insertEvent(5 * (epoch + 1));
139+
140+
recorder.compileHistories();
141+
}
142+
143+
// Open the HDF5 file and read back the data
144+
H5File file(outputFile, H5F_ACC_RDONLY);
145+
DataSet dataset = file.openDataSet("test_var_int");
146+
DataSpace dataspace = dataset.getSpace();
147+
148+
hsize_t num_elements;
149+
dataspace.getSimpleExtentDims(&num_elements, nullptr);
150+
151+
std::vector<int> dataBuffer(num_elements);
152+
// Read the data into the buffer
153+
dataset.read(dataBuffer.data(), PredType::NATIVE_INT);
154+
155+
// Verify the data matches the expected values (repeated 3 times)
156+
std::vector<int> expectedData = {1, 2, 3, 4, 5, 2, 4, 6, 8, 10, 3, 6, 9, 12, 15};
157+
ASSERT_EQ(expectedData.size(), dataBuffer.size());
158+
for (size_t i = 0; i < expectedData.size(); ++i) {
159+
EXPECT_EQ(expectedData[i], dataBuffer[i]);
160+
}
161+
}
162+
115163
#endif // HDF5

Testing/UnitTesting/XmlRecorderTests.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ TEST(XmlRecorderTest, CompileHistoriesTest)
146146
buffer0.insertEvent(2);
147147

148148
// Call the compileHistories method
149-
recorderTest_->compileHistories(*vertices.get());
149+
recorderTest_->compileHistories();
150150
vector<std::variant<uint64_t, bool, int, BGFLOAT>> history = recorderTest_->getHistory(0);
151151

152152
// Verify the events compiled hisotry
@@ -214,9 +214,9 @@ TEST(XmlRecorderTest, SaveSimDataTest)
214214
buffer.insertEvent(3);
215215

216216
// Call the compileHistories method
217-
recorderTest_->compileHistories(*vertices.get());
217+
recorderTest_->compileHistories();
218218
// Call the saveSimData() function
219-
recorderTest_->saveSimData(*vertices.get());
219+
recorderTest_->saveSimData();
220220

221221
// Open the test_output.xml file and read its content
222222
std::ifstream inputFile("../Testing/UnitTesting/TestOutput/test_output.xml");

0 commit comments

Comments
 (0)