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 namespace internal
34 {
35  template <typename T_elem>
36  class BaseRecordData : public ContainerData<T_elem>
37  {
38  public:
45  bool m_containsScalar = false;
46 
48 
49  BaseRecordData(BaseRecordData const &) = delete;
50  BaseRecordData(BaseRecordData &&) = delete;
51 
52  BaseRecordData &operator=(BaseRecordData const &) = delete;
53  BaseRecordData &operator=(BaseRecordData &&) = delete;
54  };
55 } // namespace internal
56 
57 template <typename T_elem>
58 class BaseRecord : public Container<T_elem>
59 {
60  friend class Iteration;
61  friend class ParticleSpecies;
62  friend class PatchRecord;
63  friend class Record;
64  friend class Mesh;
65 
66  std::shared_ptr<internal::BaseRecordData<T_elem> > m_baseRecordData{
68 
70  {
71  return *m_baseRecordData;
72  }
73 
74  inline internal::BaseRecordData<T_elem> const &get() const
75  {
76  return *m_baseRecordData;
77  }
78 
79  BaseRecord();
80 
81 protected:
83 
84  inline void setData(internal::BaseRecordData<T_elem> *data)
85  {
86  m_baseRecordData = std::move(data);
87  Container<T_elem>::setData(m_baseRecordData);
88  }
89 
90 public:
91  using key_type = typename Container<T_elem>::key_type;
92  using mapped_type = typename Container<T_elem>::mapped_type;
93  using value_type = typename Container<T_elem>::value_type;
94  using size_type = typename Container<T_elem>::size_type;
95  using difference_type = typename Container<T_elem>::difference_type;
96  using allocator_type = typename Container<T_elem>::allocator_type;
97  using reference = typename Container<T_elem>::reference;
98  using const_reference = typename Container<T_elem>::const_reference;
99  using pointer = typename Container<T_elem>::pointer;
100  using const_pointer = typename Container<T_elem>::const_pointer;
101  using iterator = typename Container<T_elem>::iterator;
102  using const_iterator = typename Container<T_elem>::const_iterator;
103 
104  virtual ~BaseRecord() = default;
105 
106  mapped_type &operator[](key_type const &key) override;
107  mapped_type &operator[](key_type &&key) override;
108  size_type erase(key_type const &key) override;
109  iterator erase(iterator res) override;
111  // iterator erase(const_iterator first, const_iterator last) override;
112 
131  std::array<double, 7> unitDimension() const;
132 
137  bool scalar() const;
138 
139 protected:
141  void readBase();
142 
143 private:
144  void flush(std::string const &, internal::FlushParams const &) final;
145  virtual void
146  flush_impl(std::string const &, internal::FlushParams const &) = 0;
147  virtual void read() = 0;
148 
157  bool dirtyRecursive() const;
158 }; // BaseRecord
159 
160 // implementation
161 
162 namespace internal
163 {
164  template <typename T_elem>
165  BaseRecordData<T_elem>::BaseRecordData()
166  {
167  Attributable impl{{this, [](auto const *) {}}};
168  impl.setAttribute(
169  "unitDimension",
170  std::array<double, 7>{{0., 0., 0., 0., 0., 0., 0.}});
171  }
172 } // namespace internal
173 
174 template <typename T_elem>
176 {
177  Container<T_elem>::setData(m_baseRecordData);
178 }
179 
180 template <typename T_elem>
182  std::shared_ptr<internal::BaseRecordData<T_elem> > data)
183  : Container<T_elem>{data}, m_baseRecordData{std::move(data)}
184 {}
185 
186 template <typename T_elem>
187 inline typename BaseRecord<T_elem>::mapped_type &
188 BaseRecord<T_elem>::operator[](key_type const &key)
189 {
190  auto it = this->find(key);
191  if (it != this->end())
192  return it->second;
193  else
194  {
195  bool const keyScalar = (key == RecordComponent::SCALAR);
196  if ((keyScalar && !Container<T_elem>::empty() && !scalar()) ||
197  (scalar() && !keyScalar))
198  throw std::runtime_error(
199  "A scalar component can not be contained at "
200  "the same time as one or more regular components.");
201 
202  mapped_type &ret = Container<T_elem>::operator[](key);
203  if (keyScalar)
204  {
205  get().m_containsScalar = true;
206  ret.parent() = this->parent();
207  }
208  return ret;
209  }
210 }
211 
212 template <typename T_elem>
213 inline typename BaseRecord<T_elem>::mapped_type &
214 BaseRecord<T_elem>::operator[](key_type &&key)
215 {
216  auto it = this->find(key);
217  if (it != this->end())
218  return it->second;
219  else
220  {
221  bool const keyScalar = (key == RecordComponent::SCALAR);
222  if ((keyScalar && !Container<T_elem>::empty() && !scalar()) ||
223  (scalar() && !keyScalar))
224  throw std::runtime_error(
225  "A scalar component can not be contained at "
226  "the same time as one or more regular components.");
227 
228  mapped_type &ret = Container<T_elem>::operator[](std::move(key));
229  if (keyScalar)
230  {
231  get().m_containsScalar = true;
232  ret.parent() = this->parent();
233  }
234  return ret;
235  }
236 }
237 
238 template <typename T_elem>
239 inline typename BaseRecord<T_elem>::size_type
240 BaseRecord<T_elem>::erase(key_type const &key)
241 {
242  bool const keyScalar = (key == RecordComponent::SCALAR);
243  size_type res;
244  if (!keyScalar || (keyScalar && this->at(key).constant()))
245  res = Container<T_elem>::erase(key);
246  else
247  {
248  mapped_type &rc = this->find(RecordComponent::SCALAR)->second;
249  if (rc.written())
250  {
252  dDelete.name = ".";
253  this->IOHandler()->enqueue(IOTask(&rc, dDelete));
254  this->IOHandler()->flush(internal::defaultFlushParams);
255  }
256  res = Container<T_elem>::erase(key);
257  }
258 
259  if (keyScalar)
260  {
261  this->written() = false;
262  this->writable().abstractFilePosition.reset();
263  this->get().m_containsScalar = false;
264  }
265  return res;
266 }
267 
268 template <typename T_elem>
269 inline typename BaseRecord<T_elem>::iterator
270 BaseRecord<T_elem>::erase(iterator res)
271 {
272  bool const keyScalar = (res->first == RecordComponent::SCALAR);
273  iterator ret;
274  if (!keyScalar || (keyScalar && this->at(res->first).constant()))
275  ret = Container<T_elem>::erase(res);
276  else
277  {
278  mapped_type &rc = this->find(RecordComponent::SCALAR)->second;
279  if (rc.written())
280  {
282  dDelete.name = ".";
283  this->IOHandler()->enqueue(IOTask(&rc, dDelete));
284  this->IOHandler()->flush(internal::defaultFlushParams);
285  }
286  ret = Container<T_elem>::erase(res);
287  }
288 
289  if (keyScalar)
290  {
291  this->written() = false;
292  this->writable().abstractFilePosition.reset();
293  this->get().m_containsScalar = false;
294  }
295  return ret;
296 }
297 
298 template <typename T_elem>
299 inline std::array<double, 7> BaseRecord<T_elem>::unitDimension() const
300 {
301  return this->getAttribute("unitDimension")
302  .template get<std::array<double, 7> >();
303 }
304 
305 template <typename T_elem>
306 inline bool BaseRecord<T_elem>::scalar() const
307 {
308  return get().m_containsScalar;
309 }
310 
311 template <typename T_elem>
312 inline void BaseRecord<T_elem>::readBase()
313 {
314  using DT = Datatype;
316 
317  aRead.name = "unitDimension";
318  this->IOHandler()->enqueue(IOTask(this, aRead));
319  this->IOHandler()->flush(internal::defaultFlushParams);
320  if (auto val =
321  Attribute(*aRead.resource).getOptional<std::array<double, 7> >();
322  val.has_value())
323  this->setAttribute("unitDimension", val.value());
324  else
325  throw std::runtime_error(
326  "Unexpected Attribute datatype for 'unitDimension'");
327 
328  aRead.name = "timeOffset";
329  this->IOHandler()->enqueue(IOTask(this, aRead));
330  this->IOHandler()->flush(internal::defaultFlushParams);
331  if (*aRead.dtype == DT::FLOAT)
332  this->setAttribute(
333  "timeOffset", Attribute(*aRead.resource).get<float>());
334  else if (*aRead.dtype == DT::DOUBLE)
335  this->setAttribute(
336  "timeOffset", Attribute(*aRead.resource).get<double>());
337  // conversion cast if a backend reports an integer type
338  else if (auto val = Attribute(*aRead.resource).getOptional<double>();
339  val.has_value())
340  this->setAttribute("timeOffset", val.value());
341  else
342  throw std::runtime_error(
343  "Unexpected Attribute datatype for 'timeOffset'");
344 }
345 
346 template <typename T_elem>
347 inline void BaseRecord<T_elem>::flush(
348  std::string const &name, internal::FlushParams const &flushParams)
349 {
350  if (!this->written() && this->empty())
351  throw std::runtime_error(
352  "A Record can not be written without any contained "
353  "RecordComponents: " +
354  name);
355 
356  this->flush_impl(name, flushParams);
357  // flush_impl must take care to correctly set the dirty() flag so this
358  // method doesn't do it
359 }
360 
361 template <typename T_elem>
362 inline bool BaseRecord<T_elem>::dirtyRecursive() const
363 {
364  if (this->dirty())
365  {
366  return true;
367  }
368  for (auto const &pair : *this)
369  {
370  if (pair.second.dirtyRecursive())
371  {
372  return true;
373  }
374  }
375  return false;
376 }
377 } // namespace openPMD
Self-contained description of a single IO operation.
Definition: IOTask.hpp:695
U get() const
Retrieve a stored specific Attribute and cast if convertible.
Definition: Attribute.hpp:253
Definition: Attributable.hpp:85
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:285
Logical compilation of data from one snapshot (e.g.
Definition: Iteration.hpp:126
Definition: PatchRecord.hpp:31
virtual size_type erase(key_type const &key)
Remove a single element from the container and (if written) from disk.
Definition: Container.hpp:384
std::optional< U > getOptional() const
Retrieve a stored specific Attribute and cast if convertible.
Definition: Attribute.hpp:277
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:54
bool setAttribute(std::string const &key, T value)
Populate Attribute of provided name with provided value.
Definition: Attributable.hpp:402
Public definitions of openPMD-api.
Definition: Record.hpp:32
std::array< double, 7 > unitDimension() const
Return the physical dimension (quantity) of a record.
Definition: BaseRecord.hpp:58
Parameters recursively passed through the openPMD hierarchy when flushing.
Definition: AbstractIOHandler.hpp:84
bool m_containsScalar
True if this Record contains a scalar record component.
Definition: BaseRecord.hpp:45
bool scalar() const
Returns true if this record only contains a single component.
Definition: ParticleSpecies.hpp:33
Container for N-dimensional, homogeneous Records.
Definition: Mesh.hpp:40
Map-like container that enforces openPMD requirements and handles IO.
Definition: Container.hpp:131
Layer to manage storage of attributes associated with file objects.
Definition: Attributable.hpp:93