openPMD-api
RecordComponent.hpp
1 /* Copyright 2017-2020 Fabian Koller
2  *
3  * This file is part of openPMD-api.
4  *
5  * openPMD-api is free software: you can redistribute it and/or modify
6  * it under the terms of of either the GNU General Public License or
7  * the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * openPMD-api is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License and the GNU Lesser General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * and the GNU Lesser General Public License along with openPMD-api.
19  * If not, see <http://www.gnu.org/licenses/>.
20  */
21 #pragma once
22 
23 #include "openPMD/backend/BaseRecordComponent.hpp"
24 #include "openPMD/auxiliary/ShareRaw.hpp"
25 #include "openPMD/Dataset.hpp"
26 
27 #include <cmath>
28 #include <memory>
29 #include <limits>
30 #include <queue>
31 #include <string>
32 #include <sstream>
33 #include <stdexcept>
34 #include <type_traits>
35 #include <vector>
36 #include <array>
37 
38 // expose private and protected members for invasive testing
39 #ifndef OPENPMD_protected
40 # define OPENPMD_protected protected
41 #endif
42 
43 
44 namespace openPMD
45 {
46 namespace traits
47 {
56 template< typename T >
58 {
59  static constexpr bool value = false;
60 };
61 
62 template< typename T_Value >
63 struct IsContiguousContainer< std::vector< T_Value > >
64 {
65  static constexpr bool value = true;
66 };
67 
68 template<
69  typename T_Value,
70  std::size_t N
71 >
72 struct IsContiguousContainer< std::array< T_Value, N > >
73 {
74  static constexpr bool value = true;
75 };
76 } // namespace traits
77 
79 {
80  template<
81  typename T,
82  typename T_key,
83  typename T_container
84  >
85  friend class Container;
86  friend class Iteration;
87  friend class ParticleSpecies;
88  template< typename T_elem >
89  friend class BaseRecord;
90  friend class Record;
91  friend class Mesh;
92 
93 public:
94  enum class Allocation
95  {
96  USER,
97  API,
98  AUTO
99  }; // Allocation
100 
101  RecordComponent& setUnitSI(double);
102 
103  RecordComponent& resetDataset(Dataset);
104 
105  uint8_t getDimensionality() const;
106  Extent getExtent() const;
107 
108  template< typename T >
109  RecordComponent& makeConstant(T);
110 
119  template< typename T >
120  RecordComponent& makeEmpty( uint8_t dimensions );
121 
129  template< typename T >
130  std::shared_ptr< T > loadChunk(
131  Offset = {0u},
132  Extent = {-1u},
133  double targetUnitSI = std::numeric_limits< double >::quiet_NaN() );
134 
144  template< typename T >
145  void loadChunk(
146  std::shared_ptr< T >,
147  Offset,
148  Extent,
149  double targetUnitSI = std::numeric_limits< double >::quiet_NaN() );
150 
151  template< typename T >
152  void storeChunk(std::shared_ptr< T >, Offset, Extent);
153 
154  template< typename T_ContiguousContainer >
155  typename std::enable_if<
157  >::type
158  storeChunk(T_ContiguousContainer &, Offset = {0u}, Extent = {-1u});
159 
160  static constexpr char const * const SCALAR = "\vScalar";
161 
162  virtual ~RecordComponent() = default;
163 
164 OPENPMD_protected:
165  RecordComponent();
166 
167  void readBase();
168 
169  std::shared_ptr< std::queue< IOTask > > m_chunks;
170  std::shared_ptr< Attribute > m_constantValue;
171  std::shared_ptr< bool > m_isEmpty = std::make_shared< bool >( false );
172 
173 private:
174  void flush(std::string const&);
175  virtual void read();
176 
183  RecordComponent& makeEmpty( Dataset d );
184 }; // RecordComponent
185 
186 
187 template< typename T >
188 inline RecordComponent&
189 RecordComponent::makeConstant(T value)
190 {
191  if( written )
192  throw std::runtime_error("A recordComponent can not (yet) be made constant after it has been written.");
193 
194  *m_constantValue = Attribute(value);
195  *m_isConstant = true;
196  return *this;
197 }
198 
199 template< typename T >
200 inline RecordComponent&
201 RecordComponent::makeEmpty( uint8_t dimensions )
202 {
203  return makeEmpty( Dataset(
204  determineDatatype< T >(),
205  Extent( dimensions, 0 ) ) );
206 }
207 
208 template< typename T >
209 inline std::shared_ptr< T >
210 RecordComponent::loadChunk(Offset o, Extent e, double targetUnitSI)
211 {
212  uint8_t dim = getDimensionality();
213 
214  // default arguments
215  // offset = {0u}: expand to right dim {0u, 0u, ...}
216  Offset offset = o;
217  if( o.size() == 1u && o.at(0) == 0u && dim > 1u )
218  offset = Offset(dim, 0u);
219 
220  // extent = {-1u}: take full size
221  Extent extent(dim, 1u);
222  if( e.size() == 1u && e.at(0) == -1u )
223  {
224  extent = getExtent();
225  for( uint8_t i = 0u; i < dim; ++i )
226  extent[i] -= offset[i];
227  }
228  else
229  extent = e;
230 
231  uint64_t numPoints = 1u;
232  for( auto const& dimensionSize : extent )
233  numPoints *= dimensionSize;
234 
235  auto newData = std::shared_ptr<T>(new T[numPoints], []( T *p ){ delete [] p; });
236  loadChunk(newData, offset, extent, targetUnitSI);
237  return newData;
238 }
239 
240 template< typename T >
241 inline void
242 RecordComponent::loadChunk(std::shared_ptr< T > data, Offset o, Extent e, double targetUnitSI)
243 {
244  if( !std::isnan(targetUnitSI) )
245  throw std::runtime_error("unitSI scaling during chunk loading not yet implemented");
246  Datatype dtype = determineDatatype(data);
247  if( dtype != getDatatype() )
248  if( !isSameInteger< T >( dtype ) && !isSameFloatingPoint< T >( dtype ) )
249  throw std::runtime_error("Type conversion during chunk loading not yet implemented");
250 
251  uint8_t dim = getDimensionality();
252 
253  // default arguments
254  // offset = {0u}: expand to right dim {0u, 0u, ...}
255  Offset offset = o;
256  if( o.size() == 1u && o.at(0) == 0u && dim > 1u )
257  offset = Offset(dim, 0u);
258 
259  // extent = {-1u}: take full size
260  Extent extent(dim, 1u);
261  if( e.size() == 1u && e.at(0) == -1u )
262  {
263  extent = getExtent();
264  for( uint8_t i = 0u; i < dim; ++i )
265  extent[i] -= offset[i];
266  }
267  else
268  extent = e;
269 
270  if( extent.size() != dim || offset.size() != dim )
271  {
272  std::ostringstream oss;
273  oss << "Dimensionality of chunk ("
274  << "offset=" << offset.size() << "D, "
275  << "extent=" << extent.size() << "D) "
276  << "and record component ("
277  << int(dim) << "D) "
278  << "do not match.";
279  throw std::runtime_error(oss.str());
280  }
281  Extent dse = getExtent();
282  for( uint8_t i = 0; i < dim; ++i )
283  if( dse[i] < offset[i] + extent[i] )
284  throw std::runtime_error("Chunk does not reside inside dataset (Dimension on index " + std::to_string(i)
285  + " - DS: " + std::to_string(dse[i])
286  + " - Chunk: " + std::to_string(offset[i] + extent[i])
287  + ")");
288  if( !data )
289  throw std::runtime_error("Unallocated pointer passed during chunk loading.");
290 
291  if( *m_isConstant )
292  {
293  uint64_t numPoints = 1u;
294  for( auto const& dimensionSize : extent )
295  numPoints *= dimensionSize;
296 
297  T value = m_constantValue->get< T >();
298 
299  T* raw_ptr = data.get();
300  std::fill(raw_ptr, raw_ptr + numPoints, value);
301  } else
302  {
304  dRead.offset = offset;
305  dRead.extent = extent;
306  dRead.dtype = getDatatype();
307  dRead.data = std::static_pointer_cast< void >(data);
308  m_chunks->push(IOTask(this, dRead));
309  }
310 }
311 
312 template< typename T >
313 inline void
314 RecordComponent::storeChunk(std::shared_ptr<T> data, Offset o, Extent e)
315 {
316  if( *m_isConstant )
317  throw std::runtime_error("Chunks cannot be written for a constant RecordComponent.");
318  if( *m_isEmpty )
319  throw std::runtime_error("Chunks cannot be written for an empty RecordComponent.");
320  if( !data )
321 throw std::runtime_error("Unallocated pointer passed during chunk store.");
322  Datatype dtype = determineDatatype(data);
323  if( dtype != getDatatype() )
324  {
325  std::ostringstream oss;
326  oss << "Datatypes of chunk data ("
327  << dtype
328  << ") and record component ("
329  << getDatatype()
330  << ") do not match.";
331  throw std::runtime_error(oss.str());
332  }
333  uint8_t dim = getDimensionality();
334  if( e.size() != dim || o.size() != dim )
335  {
336  std::ostringstream oss;
337  oss << "Dimensionality of chunk ("
338  << "offset=" << o.size() << "D, "
339  << "extent=" << e.size() << "D) "
340  << "and record component ("
341  << int(dim) << "D) "
342  << "do not match.";
343  throw std::runtime_error(oss.str());
344  }
345  Extent dse = getExtent();
346  for( uint8_t i = 0; i < dim; ++i )
347  if( dse[i] < o[i] + e[i] )
348  throw std::runtime_error("Chunk does not reside inside dataset (Dimension on index " + std::to_string(i)
349  + " - DS: " + std::to_string(dse[i])
350  + " - Chunk: " + std::to_string(o[i] + e[i])
351  + ")");
352 
354  dWrite.offset = o;
355  dWrite.extent = e;
356  dWrite.dtype = dtype;
357  /* std::static_pointer_cast correctly reference-counts the pointer */
358  dWrite.data = std::static_pointer_cast< void const >(data);
359  m_chunks->push(IOTask(this, dWrite));
360 }
361 
362 template< typename T_ContiguousContainer >
363 inline typename std::enable_if<
365 >::type
366 RecordComponent::storeChunk(T_ContiguousContainer & data, Offset o, Extent e)
367 {
368  uint8_t dim = getDimensionality();
369 
370  // default arguments
371  // offset = {0u}: expand to right dim {0u, 0u, ...}
372  Offset offset = o;
373  if( o.size() == 1u && o.at(0) == 0u && dim > 1u )
374  offset = Offset(dim, 0u);
375 
376  // extent = {-1u}: take full size
377  Extent extent(dim, 1u);
378  // avoid outsmarting the user:
379  // - stdlib data container implement 1D -> 1D chunk to write
380  if( e.size() == 1u && e.at(0) == -1u && dim == 1u )
381  extent.at(0) = data.size();
382  else
383  extent = e;
384 
385  storeChunk(shareRaw(data), offset, extent);
386 }
387 } // namespace openPMD
Definition: Dataset.hpp:36
Self-contained description of a single IO operation.
Definition: IOTask.hpp:468
RecordComponent & makeEmpty(uint8_t dimensions)
Create a dataset with zero extent in each dimension.
Definition: RecordComponent.hpp:201
Logical compilation of data from one snapshot (e.g.
Definition: Iteration.hpp:35
Emulate in the C++17 concept ContiguousContainer.
Definition: RecordComponent.hpp:57
STL namespace.
std::shared_ptr< T > shareRaw(T *x)
Share ownership with a raw pointer.
Definition: ShareRaw.hpp:47
Datatype
Concrete datatype of an object available at runtime.
Definition: Datatype.hpp:38
amount of substance
Varidic datatype supporting at least all formats for attributes specified in the openPMD standard...
Definition: Attribute.hpp:49
Definition: RecordComponent.hpp:78
Public definitions of openPMD-api.
Definition: Date.cpp:28
Definition: Record.hpp:33
Definition: BaseRecord.hpp:36
Definition: ParticleSpecies.hpp:34
Container for N-dimensional, homogeneous Records.
Definition: Mesh.hpp:39
Map-like container that enforces openPMD requirements and handles IO.
Definition: Container.hpp:70
std::shared_ptr< T > loadChunk(Offset={0u}, Extent={-1u}, double targetUnitSI=std::numeric_limits< double >::quiet_NaN())
Load and allocate a chunk of data.
Definition: RecordComponent.hpp:210
Definition: BaseRecordComponent.hpp:34