openPMD-api
BaseRecord.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/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 >
123  m_containsScalar = b.m_containsScalar;
124  return *this;
125 }
126 
127 template< typename 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.m_writable->parent = this->m_writable->parent;
156  ret.parent = this->parent;
157  }
158  return ret;
159  }
160 }
161 
162 template< typename T_elem >
163 inline typename BaseRecord< T_elem >::mapped_type&
164 BaseRecord< T_elem >::operator[](key_type&& key)
165 {
166  auto it = this->find(key);
167  if( it != this->end() )
168  return it->second;
169  else
170  {
171  bool const keyScalar = (key == RecordComponent::SCALAR);
172  if( (keyScalar && !Container< T_elem >::empty() && !scalar()) || (scalar() && !keyScalar) )
173  throw std::runtime_error("A scalar component can not be contained at "
174  "the same time as one or more regular components.");
175 
176  mapped_type& ret = Container< T_elem >::operator[](std::move(key));
177  if( keyScalar )
178  {
179  *m_containsScalar = true;
180  ret.m_writable->parent = this->m_writable->parent;
181  ret.parent = this->parent;
182  }
183  return ret;
184  }
185 }
186 
187 template< typename T_elem >
188 inline typename BaseRecord< T_elem >::size_type
189 BaseRecord< T_elem >::erase(key_type const& key)
190 {
191  bool const keyScalar = (key == RecordComponent::SCALAR);
192  size_type res;
193  if( !keyScalar || (keyScalar && this->at(key).constant()) )
194  res = Container< T_elem >::erase(key);
195  else
196  {
197  mapped_type& rc = this->find(RecordComponent::SCALAR)->second;
198  if( rc.written() )
199  {
201  dDelete.name = ".";
202  this->IOHandler->enqueue(IOTask(&rc, dDelete));
203  this->IOHandler->flush();
204  }
205  res = Container< T_elem >::erase(key);
206  }
207 
208  if( keyScalar )
209  {
210  this->written() = false;
211  this->m_writable->abstractFilePosition.reset();
212  *this->m_containsScalar = false;
213  }
214  return res;
215 }
216 
217 template< typename T_elem >
218 inline typename BaseRecord< T_elem >::iterator
219 BaseRecord< T_elem >::erase(iterator res)
220 {
221  bool const keyScalar = (res->first == RecordComponent::SCALAR);
222  iterator ret;
223  if( !keyScalar || (keyScalar && this->at(res->first).constant()) )
224  ret = Container< T_elem >::erase(res);
225  else
226  {
227  mapped_type& rc = this->find(RecordComponent::SCALAR)->second;
228  if( rc.written() )
229  {
231  dDelete.name = ".";
232  this->IOHandler->enqueue(IOTask(&rc, dDelete));
233  this->IOHandler->flush();
234  }
235  ret = Container< T_elem >::erase(res);
236  }
237 
238  if( keyScalar )
239  {
240  this->written() = false;
241  this->m_writable->abstractFilePosition.reset();
242  *this->m_containsScalar = false;
243  }
244  return ret;
245 }
246 
247 template< typename T_elem >
248 inline std::array< double, 7 >
250 {
251  return Attributable::getAttribute("unitDimension").template get< std::array< double, 7 > >();
252 }
253 
254 template< typename T_elem >
255 inline bool
257 {
258  return *m_containsScalar;
259 }
260 
261 template< typename T_elem >
262 inline void
264 {
265  using DT = Datatype;
267 
268  aRead.name = "unitDimension";
269  this->IOHandler->enqueue(IOTask(this, aRead));
270  this->IOHandler->flush();
271  if( *aRead.dtype == DT::ARR_DBL_7 )
272  this->setAttribute("unitDimension", Attribute(*aRead.resource).template get< std::array< double, 7 > >());
273  else if( *aRead.dtype == DT::VEC_DOUBLE )
274  {
275  auto vec = Attribute(*aRead.resource).template get< std::vector< double > >();
276  if( vec.size() == 7 )
277  {
278  std::array< double, 7 > arr;
279  std::copy(vec.begin(),
280  vec.end(),
281  arr.begin());
282  this->setAttribute("unitDimension", arr);
283  } else
284  throw std::runtime_error("Unexpected Attribute datatype for 'unitDimension'");
285  }
286  else
287  throw std::runtime_error("Unexpected Attribute datatype for 'unitDimension'");
288 
289  aRead.name = "timeOffset";
290  this->IOHandler->enqueue(IOTask(this, aRead));
291  this->IOHandler->flush();
292  if( *aRead.dtype == DT::FLOAT )
293  this->setAttribute("timeOffset", Attribute(*aRead.resource).template get< float >());
294  else if( *aRead.dtype == DT::DOUBLE )
295  this->setAttribute("timeOffset", Attribute(*aRead.resource).template get< double >());
296  else
297  throw std::runtime_error("Unexpected Attribute datatype for 'timeOffset'");
298 }
299 
300 template< typename T_elem >
301 inline void
302 BaseRecord< T_elem >::flush(std::string const& name)
303 {
304  if( !this->written() && this->empty() )
305  throw std::runtime_error("A Record can not be written without any contained RecordComponents: " + name);
306 
307  this->flush_impl(name);
308  this->dirty() = false;
309 }
310 
311 template< typename T_elem >
312 inline bool
314 {
315  if( Attributable::dirty() )
316  {
317  return true;
318  }
319  for( auto const & pair : *this )
320  {
321  if( pair.second.dirtyRecursive() )
322  {
323  return true;
324  }
325  }
326  return false;
327 }
328 } // namespace openPMD
Attribute getAttribute(std::string const &key) const
Retrieve value of Attribute stored with provided key.
Definition: Attributable.cpp:62
Self-contained description of a single IO operation.
Definition: IOTask.hpp:550
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:142
Logical compilation of data from one snapshot (e.g.
Definition: Iteration.hpp:38
virtual void enqueue(IOTask const &iotask)
Add provided task to queue according to FIFO.
Definition: AbstractIOHandler.hpp:89
virtual std::future< void > flush()=0
Process operations in queue according to FIFO.
virtual size_type erase(key_type const &key)
Remove a single element from the container and (if written) from disk.
Definition: Container.hpp:215
bool setAttribute(std::string const &key, T const &value)
Populate Attribute of provided name with provided value.
Definition: Attributable.hpp:209
Datatype
Concrete datatype of an object available at runtime.
Definition: Datatype.hpp:42
Varidic datatype supporting at least all formats for attributes specified in the openPMD standard...
Definition: Attribute.hpp:50
Public definitions of openPMD-api.
Definition: Date.cpp:29
Definition: BaseRecord.hpp:36
bool scalar() const
Returns true if this record only contains a single component.
Definition: BaseRecord.hpp:256
Definition: ParticleSpecies.hpp:34
Map-like container that enforces openPMD requirements and handles IO.
Definition: Container.hpp:70
std::array< double, 7 > unitDimension() const
Return the physical dimension (quantity) of a record.
Definition: BaseRecord.hpp:249