openPMD-api
BaseRecord.hpp
1 /* Copyright 2017-2021 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/Container.hpp"
24 #include "openPMD/RecordComponent.hpp"
25 #include "openPMD/UnitDimension.hpp"
26 
27 #include <array>
28 #include <string>
29 #include <stdexcept>
30 
31 
32 namespace openPMD
33 {
34 
35 template< typename T_elem >
36 class BaseRecord : public Container< T_elem >
37 {
38  friend class Iteration;
39  friend class ParticleSpecies;
40 
41 public:
42  using key_type = typename Container< T_elem >::key_type;
43  using mapped_type = typename Container< T_elem >::mapped_type;
44  using value_type = typename Container< T_elem >::value_type;
45  using size_type = typename Container< T_elem >::size_type;
46  using difference_type = typename Container< T_elem >::difference_type;
47  using allocator_type = typename Container< T_elem >::allocator_type;
48  using reference = typename Container< T_elem >::reference;
49  using const_reference = typename Container< T_elem >::const_reference;
50  using pointer = typename Container< T_elem >::pointer;
51  using const_pointer = typename Container< T_elem >::const_pointer;
52  using iterator = typename Container< T_elem >::iterator;
53  using const_iterator = typename Container< T_elem >::const_iterator;
54 
55  BaseRecord(BaseRecord const& b);
56  BaseRecord& operator=(BaseRecord const& b);
57  virtual ~BaseRecord() = default;
58 
59  mapped_type& operator[](key_type const& key) override;
60  mapped_type& operator[](key_type&& key) override;
61  size_type erase(key_type const& key) override;
62  iterator erase(iterator res) override;
64  // iterator erase(const_iterator first, const_iterator last) override;
65 
82  std::array< double, 7 > unitDimension() const;
83 
88  bool scalar() const;
89 
90 protected:
91  BaseRecord();
92  void readBase();
93 
94  std::shared_ptr< bool > m_containsScalar;
95 
96 private:
97  void flush(std::string const&) final;
98  virtual void flush_impl(std::string const&) = 0;
99  virtual void read() = 0;
100 
109  bool
110  dirtyRecursive() const;
111 }; // BaseRecord
112 
113 
114 template< typename T_elem >
116  : Container< T_elem >(b),
117  m_containsScalar{b.m_containsScalar}
118 { }
119 
120 template< typename T_elem >
121 BaseRecord< T_elem >& BaseRecord< T_elem >::operator=(openPMD::BaseRecord<T_elem> const& b) {
122  Container< T_elem >::operator=( b );
123  m_containsScalar = b.m_containsScalar;
124  return *this;
125 }
126 
127 template< typename T_elem >
128 BaseRecord< T_elem >::BaseRecord()
129  : Container< T_elem >(),
130  m_containsScalar{std::make_shared< bool >(false)}
131 {
132  this->setAttribute("unitDimension",
133  std::array< double, 7 >{{0., 0., 0., 0., 0., 0., 0.}});
134 }
135 
136 
137 template< typename T_elem >
138 inline typename BaseRecord< T_elem >::mapped_type&
139 BaseRecord< T_elem >::operator[](key_type const& key)
140 {
141  auto it = this->find(key);
142  if( it != this->end() )
143  return it->second;
144  else
145  {
146  bool const keyScalar = (key == RecordComponent::SCALAR);
147  if( (keyScalar && !Container< T_elem >::empty() && !scalar()) || (scalar() && !keyScalar) )
148  throw std::runtime_error("A scalar component can not be contained at "
149  "the same time as one or more regular components.");
150 
151  mapped_type& ret = Container< T_elem >::operator[](key);
152  if( keyScalar )
153  {
154  *m_containsScalar = true;
155  ret.parent() = this->parent();
156  }
157  return ret;
158  }
159 }
160 
161 template< typename T_elem >
162 inline typename BaseRecord< T_elem >::mapped_type&
163 BaseRecord< T_elem >::operator[](key_type&& key)
164 {
165  auto it = this->find(key);
166  if( it != this->end() )
167  return it->second;
168  else
169  {
170  bool const keyScalar = (key == RecordComponent::SCALAR);
171  if( (keyScalar && !Container< T_elem >::empty() && !scalar()) || (scalar() && !keyScalar) )
172  throw std::runtime_error("A scalar component can not be contained at "
173  "the same time as one or more regular components.");
174 
175  mapped_type& ret = Container< T_elem >::operator[](std::move(key));
176  if( keyScalar )
177  {
178  *m_containsScalar = true;
179  ret.parent() = this->parent();
180  }
181  return ret;
182  }
183 }
184 
185 template< typename T_elem >
186 inline typename BaseRecord< T_elem >::size_type
187 BaseRecord< T_elem >::erase(key_type const& key)
188 {
189  bool const keyScalar = (key == RecordComponent::SCALAR);
190  size_type res;
191  if( !keyScalar || (keyScalar && this->at(key).constant()) )
192  res = Container< T_elem >::erase(key);
193  else
194  {
195  mapped_type& rc = this->find(RecordComponent::SCALAR)->second;
196  if( rc.written() )
197  {
198  Parameter< Operation::DELETE_DATASET > dDelete;
199  dDelete.name = ".";
200  this->IOHandler()->enqueue(IOTask(&rc, dDelete));
201  this->IOHandler()->flush();
202  }
203  res = Container< T_elem >::erase(key);
204  }
205 
206  if( keyScalar )
207  {
208  this->written() = false;
209  this->writable().abstractFilePosition.reset();
210  *this->m_containsScalar = false;
211  }
212  return res;
213 }
214 
215 template< typename T_elem >
216 inline typename BaseRecord< T_elem >::iterator
217 BaseRecord< T_elem >::erase(iterator res)
218 {
219  bool const keyScalar = (res->first == RecordComponent::SCALAR);
220  iterator ret;
221  if( !keyScalar || (keyScalar && this->at(res->first).constant()) )
222  ret = Container< T_elem >::erase(res);
223  else
224  {
225  mapped_type& rc = this->find(RecordComponent::SCALAR)->second;
226  if( rc.written() )
227  {
228  Parameter< Operation::DELETE_DATASET > dDelete;
229  dDelete.name = ".";
230  this->IOHandler()->enqueue(IOTask(&rc, dDelete));
231  this->IOHandler()->flush();
232  }
233  ret = Container< T_elem >::erase(res);
234  }
235 
236  if( keyScalar )
237  {
238  this->written() = false;
239  this->writable().abstractFilePosition.reset();
240  *this->m_containsScalar = false;
241  }
242  return ret;
243 }
244 
245 template< typename T_elem >
246 inline std::array< double, 7 >
248 {
249  return this->getAttribute("unitDimension").template get< std::array< double, 7 > >();
250 }
251 
252 template< typename T_elem >
253 inline bool
255 {
256  return *m_containsScalar;
257 }
258 
259 template< typename T_elem >
260 inline void
262 {
263  using DT = Datatype;
265 
266  aRead.name = "unitDimension";
267  this->IOHandler()->enqueue(IOTask(this, aRead));
268  this->IOHandler()->flush();
269  if( *aRead.dtype == DT::ARR_DBL_7 )
270  this->setAttribute("unitDimension", Attribute(*aRead.resource).template get< std::array< double, 7 > >());
271  else if( *aRead.dtype == DT::VEC_DOUBLE )
272  {
273  auto vec = Attribute(*aRead.resource).template get< std::vector< double > >();
274  if( vec.size() == 7 )
275  {
276  std::array< double, 7 > arr;
277  std::copy(vec.begin(),
278  vec.end(),
279  arr.begin());
280  this->setAttribute("unitDimension", arr);
281  } else
282  throw std::runtime_error("Unexpected Attribute datatype for 'unitDimension'");
283  }
284  else
285  throw std::runtime_error("Unexpected Attribute datatype for 'unitDimension'");
286 
287  aRead.name = "timeOffset";
288  this->IOHandler()->enqueue(IOTask(this, aRead));
289  this->IOHandler()->flush();
290  if( *aRead.dtype == DT::FLOAT )
291  this->setAttribute("timeOffset", Attribute(*aRead.resource).template get< float >());
292  else if( *aRead.dtype == DT::DOUBLE )
293  this->setAttribute("timeOffset", Attribute(*aRead.resource).template get< double >());
294  else
295  throw std::runtime_error("Unexpected Attribute datatype for 'timeOffset'");
296 }
297 
298 template< typename T_elem >
299 inline void
300 BaseRecord< T_elem >::flush(std::string const& name)
301 {
302  if( !this->written() && this->empty() )
303  throw std::runtime_error("A Record can not be written without any contained RecordComponents: " + name);
304 
305  this->flush_impl(name);
306  // flush_impl must take care to correctly set the dirty() flag so this
307  // method doesn't do it
308 }
309 
310 template< typename T_elem >
311 inline bool
312 BaseRecord< T_elem >::dirtyRecursive() const
313 {
314  if( this->dirty() )
315  {
316  return true;
317  }
318  for( auto const & pair : *this )
319  {
320  if( pair.second.dirtyRecursive() )
321  {
322  return true;
323  }
324  }
325  return false;
326 }
327 } // namespace openPMD
openPMD::ParticleSpecies
Definition: ParticleSpecies.hpp:34
openPMD::Datatype
Datatype
Concrete datatype of an object available at runtime.
Definition: Datatype.hpp:45
openPMD::Container< T_elem >::erase
virtual size_type erase(key_type const &key)
Remove a single element from the container and (if written) from disk.
Definition: Container.hpp:261
openPMD::BaseRecord
Definition: BaseRecord.hpp:36
openPMD::Attribute
Varidic datatype supporting at least all formats for attributes specified in the openPMD standard.
Definition: Attribute.hpp:50
openPMD::BaseRecord::unitDimension
std::array< double, 7 > unitDimension() const
Return the physical dimension (quantity) of a record.
Definition: BaseRecord.hpp:247
openPMD::Iteration
Logical compilation of data from one snapshot (e.g.
Definition: Iteration.hpp:39
openPMD::BaseRecord::scalar
bool scalar() const
Returns true if this record only contains a single component.
Definition: BaseRecord.hpp:254
openPMD
Public definitions of openPMD-api.
Definition: Date.cpp:29
openPMD::Container
Map-like container that enforces openPMD requirements and handles IO.
Definition: Container.hpp:106
openPMD::IOTask
Self-contained description of a single IO operation.
Definition: IOTask.hpp:589
openPMD::Parameter< Operation::READ_ATT >
Definition: IOTask.hpp:486
openPMD::Container::operator[]
virtual mapped_type & operator[](key_type const &key)
Access the value that is mapped to a key equivalent to key, creating it if such key does not exist al...
Definition: Container.hpp:184