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 
116  template< typename T >
117  RecordComponent& makeConstant(T);
118 
127  template< typename T >
128  RecordComponent& makeEmpty( uint8_t dimensions );
129 
138  RecordComponent& makeEmpty( Datatype dt, uint8_t dimensions );
139 
147  bool empty() const;
148 
156  template< typename T >
157  std::shared_ptr< T > loadChunk(
158  Offset = {0u},
159  Extent = {-1u},
160  double targetUnitSI = std::numeric_limits< double >::quiet_NaN() );
161 
171  template< typename T >
172  void loadChunk(
173  std::shared_ptr< T >,
174  Offset,
175  Extent,
176  double targetUnitSI = std::numeric_limits< double >::quiet_NaN() );
177 
178  template< typename T >
179  void storeChunk(std::shared_ptr< T >, Offset, Extent);
180 
181  template< typename T_ContiguousContainer >
182  typename std::enable_if<
184  >::type
185  storeChunk(T_ContiguousContainer &, Offset = {0u}, Extent = {-1u});
186 
187  static constexpr char const * const SCALAR = "\vScalar";
188 
189  virtual ~RecordComponent() = default;
190 
191 OPENPMD_protected:
192  RecordComponent();
193 
194  void readBase();
195 
196  std::shared_ptr< std::queue< IOTask > > m_chunks;
197  std::shared_ptr< Attribute > m_constantValue;
198  std::shared_ptr< bool > m_isEmpty = std::make_shared< bool >( false );
199 
200 private:
201  void flush(std::string const&);
202  virtual void read();
203 
210  RecordComponent& makeEmpty( Dataset d );
211 
220  bool
221  dirtyRecursive() const;
222 
223 protected:
227  std::shared_ptr< bool > hasBeenRead = std::make_shared< bool >( false );
228 }; // RecordComponent
229 
230 
231 template< typename T >
232 inline RecordComponent&
234 {
235  if( written() )
236  throw std::runtime_error("A recordComponent can not (yet) be made constant after it has been written.");
237 
238  *m_constantValue = Attribute(value);
239  *m_isConstant = true;
240  return *this;
241 }
242 
243 template< typename T >
244 inline RecordComponent&
245 RecordComponent::makeEmpty( uint8_t dimensions )
246 {
247  return makeEmpty( Dataset(
248  determineDatatype< T >(),
249  Extent( dimensions, 0 ) ) );
250 }
251 
252 template< typename T >
253 inline std::shared_ptr< T >
254 RecordComponent::loadChunk(Offset o, Extent e, double targetUnitSI)
255 {
256  uint8_t dim = getDimensionality();
257 
258  // default arguments
259  // offset = {0u}: expand to right dim {0u, 0u, ...}
260  Offset offset = o;
261  if( o.size() == 1u && o.at(0) == 0u && dim > 1u )
262  offset = Offset(dim, 0u);
263 
264  // extent = {-1u}: take full size
265  Extent extent(dim, 1u);
266  if( e.size() == 1u && e.at(0) == -1u )
267  {
268  extent = getExtent();
269  for( uint8_t i = 0u; i < dim; ++i )
270  extent[i] -= offset[i];
271  }
272  else
273  extent = e;
274 
275  uint64_t numPoints = 1u;
276  for( auto const& dimensionSize : extent )
277  numPoints *= dimensionSize;
278 
279  auto newData = std::shared_ptr<T>(new T[numPoints], []( T *p ){ delete [] p; });
280  loadChunk(newData, offset, extent, targetUnitSI);
281  return newData;
282 }
283 
284 template< typename T >
285 inline void
286 RecordComponent::loadChunk(std::shared_ptr< T > data, Offset o, Extent e, double targetUnitSI)
287 {
288  if( !std::isnan(targetUnitSI) )
289  throw std::runtime_error("unitSI scaling during chunk loading not yet implemented");
290  Datatype dtype = determineDatatype(data);
291  if( dtype != getDatatype() )
292  if( !isSameInteger< T >( dtype ) &&
293  !isSameFloatingPoint< T >( dtype ) &&
294  !isSameComplexFloatingPoint< T >( dtype ) )
295  throw std::runtime_error("Type conversion during chunk loading not yet implemented");
296 
297  uint8_t dim = getDimensionality();
298 
299  // default arguments
300  // offset = {0u}: expand to right dim {0u, 0u, ...}
301  Offset offset = o;
302  if( o.size() == 1u && o.at(0) == 0u && dim > 1u )
303  offset = Offset(dim, 0u);
304 
305  // extent = {-1u}: take full size
306  Extent extent(dim, 1u);
307  if( e.size() == 1u && e.at(0) == -1u )
308  {
309  extent = getExtent();
310  for( uint8_t i = 0u; i < dim; ++i )
311  extent[i] -= offset[i];
312  }
313  else
314  extent = e;
315 
316  if( extent.size() != dim || offset.size() != dim )
317  {
318  std::ostringstream oss;
319  oss << "Dimensionality of chunk ("
320  << "offset=" << offset.size() << "D, "
321  << "extent=" << extent.size() << "D) "
322  << "and record component ("
323  << int(dim) << "D) "
324  << "do not match.";
325  throw std::runtime_error(oss.str());
326  }
327  Extent dse = getExtent();
328  for( uint8_t i = 0; i < dim; ++i )
329  if( dse[i] < offset[i] + extent[i] )
330  throw std::runtime_error("Chunk does not reside inside dataset (Dimension on index " + std::to_string(i)
331  + ". DS: " + std::to_string(dse[i])
332  + " - Chunk: " + std::to_string(offset[i] + extent[i])
333  + ")");
334  if( !data )
335  throw std::runtime_error("Unallocated pointer passed during chunk loading.");
336 
337  if( constant() )
338  {
339  uint64_t numPoints = 1u;
340  for( auto const& dimensionSize : extent )
341  numPoints *= dimensionSize;
342 
343  T value = m_constantValue->get< T >();
344 
345  T* raw_ptr = data.get();
346  std::fill(raw_ptr, raw_ptr + numPoints, value);
347  } else
348  {
350  dRead.offset = offset;
351  dRead.extent = extent;
352  dRead.dtype = getDatatype();
353  dRead.data = std::static_pointer_cast< void >(data);
354  m_chunks->push(IOTask(this, dRead));
355  }
356 }
357 
358 template< typename T >
359 inline void
360 RecordComponent::storeChunk(std::shared_ptr<T> data, Offset o, Extent e)
361 {
362  if( constant() )
363  throw std::runtime_error("Chunks cannot be written for a constant RecordComponent.");
364  if( empty() )
365  throw std::runtime_error("Chunks cannot be written for an empty RecordComponent.");
366  if( !data )
367  throw std::runtime_error("Unallocated pointer passed during chunk store.");
368  Datatype dtype = determineDatatype(data);
369  if( dtype != getDatatype() )
370  {
371  std::ostringstream oss;
372  oss << "Datatypes of chunk data ("
373  << dtype
374  << ") and record component ("
375  << getDatatype()
376  << ") do not match.";
377  throw std::runtime_error(oss.str());
378  }
379  uint8_t dim = getDimensionality();
380  if( e.size() != dim || o.size() != dim )
381  {
382  std::ostringstream oss;
383  oss << "Dimensionality of chunk ("
384  << "offset=" << o.size() << "D, "
385  << "extent=" << e.size() << "D) "
386  << "and record component ("
387  << int(dim) << "D) "
388  << "do not match.";
389  throw std::runtime_error(oss.str());
390  }
391  Extent dse = getExtent();
392  for( uint8_t i = 0; i < dim; ++i )
393  if( dse[i] < o[i] + e[i] )
394  throw std::runtime_error("Chunk does not reside inside dataset (Dimension on index " + std::to_string(i)
395  + ". DS: " + std::to_string(dse[i])
396  + " - Chunk: " + std::to_string(o[i] + e[i])
397  + ")");
398 
400  dWrite.offset = o;
401  dWrite.extent = e;
402  dWrite.dtype = dtype;
403  /* std::static_pointer_cast correctly reference-counts the pointer */
404  dWrite.data = std::static_pointer_cast< void const >(data);
405  m_chunks->push(IOTask(this, dWrite));
406 }
407 
408 template< typename T_ContiguousContainer >
409 inline typename std::enable_if<
411 >::type
412 RecordComponent::storeChunk(T_ContiguousContainer & data, Offset o, Extent e)
413 {
414  uint8_t dim = getDimensionality();
415 
416  // default arguments
417  // offset = {0u}: expand to right dim {0u, 0u, ...}
418  Offset offset = o;
419  if( o.size() == 1u && o.at(0) == 0u && dim > 1u )
420  offset = Offset(dim, 0u);
421 
422  // extent = {-1u}: take full size
423  Extent extent(dim, 1u);
424  // avoid outsmarting the user:
425  // - stdlib data container implement 1D -> 1D chunk to write
426  if( e.size() == 1u && e.at(0) == -1u && dim == 1u )
427  extent.at(0) = data.size();
428  else
429  extent = e;
430 
431  storeChunk(shareRaw(data), offset, extent);
432 }
433 } // namespace openPMD
Definition: Dataset.hpp:36
Self-contained description of a single IO operation.
Definition: IOTask.hpp:550
RecordComponent & makeEmpty(uint8_t dimensions)
Create a dataset with zero extent in each dimension.
Definition: RecordComponent.hpp:245
Logical compilation of data from one snapshot (e.g.
Definition: Iteration.hpp:38
Emulate in the C++17 concept ContiguousContainer.
Definition: RecordComponent.hpp:57
RecordComponent & makeConstant(T)
Create a dataset with regular extent and constant value.
Definition: RecordComponent.hpp:233
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:42
amount of substance
Varidic datatype supporting at least all formats for attributes specified in the openPMD standard...
Definition: Attribute.hpp:50
Definition: RecordComponent.hpp:78
Public definitions of openPMD-api.
Definition: Date.cpp:29
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:254
Definition: BaseRecordComponent.hpp:34