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