1 // -*- C++ -*-
2 /**
3 * @brief XMLH5StorageManager implements xml/h5 storage
4 *
5 * Copyright 2005-2021 Airbus-EDF-IMACS-ONERA-Phimeca
6 *
7 * This library is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include "openturns/XMLH5StorageManager.hxx"
23 #include "openturns/PersistentObject.hxx"
24 #include "openturns/Os.hxx"
25
26 #include <H5Cpp.h>
27 #include <libxml/tree.h>
28
29 BEGIN_NAMESPACE_OPENTURNS
30
31
32 class XMLH5StorageManagerImplementation
33 {
34 public:
35
XMLH5StorageManagerImplementation(const FileName & h5FileName)36 explicit XMLH5StorageManagerImplementation(const FileName & h5FileName)
37 : h5FileName_(h5FileName)
38 {}
39
40 template <class CPP_Type>
41 void addIndexedValue(Pointer<StorageManager::InternalObject> & p_obj, UnsignedInteger index, CPP_Type value);
42
43 template <class CPP_Type>
44 void readIndexedValue(Pointer<StorageManager::InternalObject> & p_obj, UnsignedInteger index, CPP_Type & value);
45
46 private:
47 template <class CPP_Type>
48 void writeToH5(const String & dataSetName);
49
50 template <class CPP_Type>
51 void readFromH5(const String & dataSetName);
52
53 // Buffer size for writing hdf5 slabs
54 const UnsignedInteger BufferSize = 1048576;
55
56 // 30kB (max recommended dataset header size)
57 // https://support.hdfgroup.org/HDF5/doc/UG/HDF5_Users_Guide.pdf
58 const UnsignedInteger MaxHeaderSize = 30720;
59
60 template <class CPP_Type> inline std::vector<CPP_Type> & getBuffer();
61
62 FileName h5FileName_;
63 std::vector<Scalar> valBuf_Scalar_;
64 std::vector<UnsignedInteger> valBuf_UnsignedInteger_;
65 OT::Bool isFirstDS_ = true;
66 };
67
68
getBuffer()69 template <> inline std::vector<Scalar> & XMLH5StorageManagerImplementation::getBuffer()
70 {
71 return valBuf_Scalar_;
72 }
getBuffer()73 template <> inline std::vector<UnsignedInteger> & XMLH5StorageManagerImplementation::getBuffer()
74 {
75 return valBuf_UnsignedInteger_;
76 }
77
78 template <class CPP_Type> inline H5::DataType getDataType();
getDataType()79 template <> inline H5::DataType getDataType<Scalar>()
80 {
81 return H5::PredType::IEEE_F64LE;
82 }
getDataType()83 template <> inline H5::DataType getDataType<UnsignedInteger>()
84 {
85 return H5::PredType::NATIVE_ULONG;
86 }
87
88
89 template <class CPP_Type>
addIndexedValue(Pointer<StorageManager::InternalObject> & p_obj,UnsignedInteger index,CPP_Type value)90 void XMLH5StorageManagerImplementation::addIndexedValue(Pointer<StorageManager::InternalObject> & p_obj,
91 UnsignedInteger index,
92 CPP_Type value)
93 {
94 // Get XML node associated to the Collection
95 assert(p_obj);
96 XMLInternalObject & obj = dynamic_cast<XMLInternalObject&>(*p_obj);
97 XML::Node node = obj.node_;
98 assert(node);
99 const hsize_t dsetSize = std::stoi(XML::GetAttributeByName(node, "size"));
100
101 // append value in buffer
102 getBuffer<CPP_Type>().push_back(value);
103
104 // if last value or buffer full then write to dataset
105 if ((index == dsetSize - 1) || ((index % BufferSize == 0) && (index > 0)))
106 {
107 String dataSetName = XML::GetAttributeByName(node, "id");
108 writeToH5<CPP_Type>(dataSetName);
109 }
110
111 // if last value then add XML node
112 if (index == dsetSize - 1)
113 {
114 String dataSetName = XML::GetAttributeByName(node, "id");
115 const size_t idx = h5FileName_.find_last_of(Os::GetDirectorySeparator());
116 const FileName h5FileNameRel = h5FileName_.substr(idx + 1);
117 XML::Node child = XML::NewNode(XML_STMGR::string_tag::Get(), h5FileNameRel + ":/" + dataSetName);
118 assert(child);
119 XML::AddChild( node, child );
120 }
121 }
122
123
124 template <class CPP_Type>
writeToH5(const String & dataSetName)125 void XMLH5StorageManagerImplementation::writeToH5(const String & dataSetName)
126 {
127 H5::Exception::dontPrint();
128 H5::H5File h5File;
129 if (isFirstDS_)
130 {
131 //Create new or overwrite existing
132 h5File = H5::H5File(h5FileName_.c_str(), H5F_ACC_TRUNC);
133 isFirstDS_ = false;
134 }
135 else
136 {
137 //R+W access
138 h5File = H5::H5File(h5FileName_.c_str(), H5F_ACC_RDWR);
139 }
140
141 const hsize_t dims[1] = { getBuffer<CPP_Type>().size() };
142
143 if (!H5Lexists(h5File.getId(), dataSetName.c_str(), H5P_DEFAULT))
144 {
145 //Dataset does not exist, need to create it and initialize it with first chunk
146 const hsize_t maxdims[1] = { H5S_UNLIMITED };
147 H5::DataSpace dsp;
148 H5::DSetCreatPropList prop;
149 if (getBuffer<CPP_Type>().size() < BufferSize)
150 {
151 dsp = H5::DataSpace(1, dims, dims);
152 // Set dataspace compact if dataset can fit into dataset header
153 // Set it contiguous otherwise
154 if (getBuffer<CPP_Type>().size()*sizeof(CPP_Type) < MaxHeaderSize)
155 prop.setLayout(H5D_COMPACT);
156 else
157 prop.setLayout(H5D_CONTIGUOUS);
158 }
159 //Set dataspace to unlimited if full buffer
160 else
161 dsp = H5::DataSpace(1, dims, maxdims);
162 //Propagate dataspace properties to dataset
163 prop.setChunk(1, dims);
164 //Create new dataset and write it
165 H5::DataSet dset(h5File.createDataSet(dataSetName, getDataType<CPP_Type>(), dsp, prop));
166 dset.write(getBuffer<CPP_Type>().data(), getDataType<CPP_Type>());
167 prop.close();
168 }
169 else
170 {
171 //Dataset exists, and will be appended with buffer values
172 H5::DataSet dset(h5File.openDataSet(dataSetName));
173 //Get actual dset size
174 const hsize_t offset[1] = { (hsize_t)dset.getSpace().getSimpleExtentNpoints() };
175 const hsize_t extent[1] = { dims[0] + offset[0] };
176 //Extend dset size by the buffer size
177 dset.extend(extent);
178 //Get updated dataspace
179 H5::DataSpace filespace(dset.getSpace());
180 filespace.selectHyperslab(H5S_SELECT_SET, dims, offset);
181 //Create space for new data
182 H5::DataSpace memspace(1, dims, NULL);
183 //Write new data
184 dset.write(getBuffer<CPP_Type>().data(), getDataType<CPP_Type>(), memspace, filespace);
185 }
186 getBuffer<CPP_Type>().clear();
187 h5File.close();
188 }
189
190
191 template <class CPP_Type>
readIndexedValue(Pointer<StorageManager::InternalObject> & p_obj,UnsignedInteger index,CPP_Type & value)192 void XMLH5StorageManagerImplementation::readIndexedValue(Pointer<StorageManager::InternalObject> & p_obj,
193 UnsignedInteger index,
194 CPP_Type & value)
195 {
196 assert(p_obj);
197 //Read values only once
198 XMLH5StorageManagerState & state = dynamic_cast<XMLH5StorageManagerState &>(*p_obj);
199
200 if (index == 0)
201 {
202 XML::Node node = state.current_->parent;
203 String dataSetName = XML::GetAttributeByName(node, "id");
204 readFromH5<CPP_Type>(dataSetName);
205 state.reachedEnd_ = false;
206 }
207 if (index == getBuffer<CPP_Type>().size() - 1)
208 {
209 state.reachedEnd_ = true;
210 }
211 state.next();
212 //Get value from index
213 value = getBuffer<CPP_Type>()[index];
214 }
215
216
217 template <class CPP_Type>
readFromH5(const String & dataSetName)218 void XMLH5StorageManagerImplementation::readFromH5(const String & dataSetName)
219 {
220 H5::Exception::dontPrint();
221 H5::H5File file(h5FileName_.c_str(), H5F_ACC_RDONLY);
222 H5::DataSet dataset = file.openDataSet(dataSetName.c_str());
223 H5::DataSpace dataspace = dataset.getSpace();
224 const int size = dataspace.getSimpleExtentNpoints();
225
226 getBuffer<CPP_Type>().resize(size);
227 dataset.read(getBuffer<CPP_Type>().data(), getDataType<CPP_Type>());
228
229 dataspace.close();
230 dataset.close();
231 file.close();
232 }
233
CLASSNAMEINIT(XMLH5StorageManager)234 CLASSNAMEINIT(XMLH5StorageManager)
235
236
237 /* Default constructor */
238 XMLH5StorageManager::XMLH5StorageManager(const FileName & filename,
239 const UnsignedInteger compressionLevel)
240 : XMLStorageManager(filename, compressionLevel)
241 {
242 p_state_ = new XMLH5StorageManagerState;
243 p_implementation_ = new XMLH5StorageManagerImplementation(filename.substr(0, filename.find_last_of('.')) + ".h5");
244 }
245
246 /*
247 * Virtual constructor
248 */
clone() const249 XMLH5StorageManager * XMLH5StorageManager::clone() const
250 {
251 return new XMLH5StorageManager(*this);
252 }
253
254
checkStorageManager()255 void XMLH5StorageManager::checkStorageManager()
256 {
257 if (XML::GetAttributeByName( p_state_->root_, XML_STMGR::manager_attribute::Get()) !=
258 "XMLH5StorageManager")
259 throw StudyFileParsingException(HERE) << XML::GetAttributeByName( p_state_->root_, XML_STMGR::manager_attribute::Get())
260 << " is used in study file. XMLH5StorageManager is expected";
261 }
262
263
setStorageManager()264 void XMLH5StorageManager::setStorageManager()
265 {
266 XML::SetAttribute(p_state_->root_, XML_STMGR::manager_attribute::Get(), "XMLH5StorageManager");
267 }
268
269
addIndexedValue(Pointer<InternalObject> & p_obj,UnsignedInteger index,Scalar value)270 void XMLH5StorageManager::addIndexedValue(Pointer<InternalObject> & p_obj, UnsignedInteger index, Scalar value)
271 {
272 p_implementation_->addIndexedValue(p_obj, index, value);
273 }
274
addIndexedValue(Pointer<InternalObject> & p_obj,UnsignedInteger index,UnsignedInteger value)275 void XMLH5StorageManager::addIndexedValue(Pointer<InternalObject> & p_obj, UnsignedInteger index, UnsignedInteger value)
276 {
277 p_implementation_->addIndexedValue(p_obj, index, value);
278 }
279
readIndexedValue(Pointer<StorageManager::InternalObject> & p_obj,UnsignedInteger index,UnsignedInteger & value)280 void XMLH5StorageManager::readIndexedValue(Pointer<StorageManager::InternalObject> & p_obj, UnsignedInteger index, UnsignedInteger & value)
281 {
282 // we started to store integers into h5 in 1.17
283 if (getStudyVersion() >= 101700)
284 p_implementation_->readIndexedValue(p_obj, index, value);
285 else
286 XMLStorageManager::readIndexedValue(p_obj, index, value);
287 }
288
readIndexedValue(Pointer<StorageManager::InternalObject> & p_obj,UnsignedInteger index,Scalar & value)289 void XMLH5StorageManager::readIndexedValue(Pointer<StorageManager::InternalObject> & p_obj, UnsignedInteger index, Scalar & value)
290 {
291 p_implementation_->readIndexedValue(p_obj, index, value);
292 }
293
294 END_NAMESPACE_OPENTURNS
295