icedb  version 0.5.1
Snow particle scattering database API
Shapes.cpp
Go to the documentation of this file.
1 #include <string>
2 #include <sstream>
3 #include <iostream>
4 #include "../icedb/shape.hpp"
5 #include "../private/hdf5_supplemental.hpp"
6 #include "../private/Shape_impl.hpp"
7 
8 namespace icedb {
9  namespace Shapes {
10  bool NewShapeRequiredProperties::isValid(std::ostream *out) const {
11  bool good = true;
12 
14  good = false;
15  if (out) (*out) << "The number of scattering elements is not set." << std::endl;
16  }
18  good = false;
19  if (out) (*out) << "The number of particle constituents is not set." << std::endl;
20  }
21  if (!particle_id.size()) {
22  good = false;
23  if (out) (*out) << "Particle ID is not set." << std::endl;
24  }
25 
26  if (this->particle_scattering_element_coordinates.empty()) {
27  good = false;
28  if (out) (*out) << "particle_scattering_element_coordinates is not set. "
29  "Particles need to have scattering elements." << std::endl;
30  }
32  good = false;
33  if (out) (*out) << "particle_scattering_element_coordinates has "
34  "the wrong dimensions. It should have dimensions of "
35  "[number_of_particle_scattering_elements][3], yielding a total of "
36  << 3 * number_of_particle_scattering_elements << " elements. "
37  "Instead, it currently has " << particle_scattering_element_coordinates.size()
38  << " elements." << std::endl;
39  }
40  if (this->number_of_particle_constituents == 0) {
41  good = false;
42  if (out) (*out) << "number_of_particle_constituents is not set. "
43  "Particles need to have a composition." << std::endl;
44  }
45 
46 
47  return good;
48  }
49 
51  bool req = false;
52  if (this->number_of_particle_constituents > 1) req = true;
53  return req;
54  }
55 
57  gsl::not_null<const NewShapeRequiredProperties*> required, std::ostream *out) const
58  {
59  bool good = true;
60 
61  if (!this->particle_scattering_element_number.empty()) {
62  if (required->number_of_particle_scattering_elements != this->particle_scattering_element_number.size()) {
63  good = false;
64  if (out) (*out) << "number_of_particle_scattering_elements is not equal to particle_scattering_element_number." << std::endl;
65  }
66  }
67  if (required->number_of_particle_constituents > 1) {
68  if (this->particle_constituent_number.empty() || this->particle_constituent_number.size() != required->number_of_particle_constituents) {
69  good = false;
70  if (out) (*out) << "particle_constituent_number is not set. "
71  "This is an essential dimension scale that the rest of the particle "
72  "data depends on." << std::endl;
73  }
74 
75  if (this->particle_scattering_element_composition_whole.empty()
76  && this->particle_scattering_element_composition_fractional.empty())
77  {
78  good = false;
79  if (out) (*out) << "particle_scattering_element_composition_whole and "
80  "particle_scattering_element_composition_fractional are not set, but one is required. "
81  << std::endl;
82  }
83 
84  if (!this->particle_scattering_element_composition_whole.empty()
85  && !this->particle_scattering_element_composition_fractional.empty())
86  {
87  good = false;
88  if (out) (*out) << "particle_scattering_element_composition_whole and "
89  "particle_scattering_element_composition_fractional are both set, but only one is allowed. "
90  << std::endl;
91  }
92 
93  if (!this->particle_scattering_element_composition_whole.empty()) {
94  if (this->particle_scattering_element_composition_whole.size()
95  != required->number_of_particle_scattering_elements)
96  {
97  good = false;
98  if (out) (*out) << "particle_scattering_element_composition_whole "
99  "has the wrong size. It should have a size of number_of_particle_scattering_elements."
100  << std::endl;
101  }
102  }
103  if (!this->particle_scattering_element_composition_fractional.empty()) {
104  if (this->particle_scattering_element_composition_fractional.size() !=
105  (required->number_of_particle_scattering_elements * required->number_of_particle_constituents)) {
106  good = false;
107  if (out) (*out) << "particle_scattering_element_composition_fractional has "
108  "the wrong dimensions. It should have dimensions of "
109  "[number_of_particle_scattering_elements][number_of_particle_constituents], yielding a total of "
110  << required->number_of_particle_scattering_elements * required->number_of_particle_constituents << " elements. "
111  "Instead, it currently has " << this->particle_scattering_element_composition_fractional.size()
112  << " elements." << std::endl;
113  }
114  }
115  }
116 
117 
118  if (this->particle_scattering_element_radius.size()) {
119  if (this->particle_scattering_element_radius.size() != required->number_of_particle_scattering_elements) {
120  good = false;
121  if (out) (*out) << "particle_scattering_element_radius has the wrong size. "
122  "It should have dimensions of [number_of_particle_scattering_elements]. "
123  "particle_scattering_element_radius has a current size of " << particle_scattering_element_radius.size()
124  << ", and this should be " << required->number_of_particle_scattering_elements
125  << "." << std::endl;
126  }
127  }
128 
129  if (particle_constituent_single_name.size() && required->number_of_particle_constituents != 1) {
130  good = false;
131  if (out) (*out) << "particle_constituent_single_name is a valid attribute only when a single non-ice constituent exists."
132  << std::endl;
133  }
134  if (particle_constituent_single_name.size() && this->particle_constituent_name.size()) {
135  good = false;
136  if (out) (*out) << "particle_constituent_single_name and particle_constituent_name are mutually exclusive."
137  << std::endl;
138  }
139  if (this->particle_constituent_name.size()) {
140  if (this->particle_constituent_name.size() != required->number_of_particle_constituents) {
141  good = false;
142  if (out) (*out) << "particle_constituent_name has the wrong size. "
143  "It should have dimensions of [number_of_particle_constituents]. "
144  << std::endl;
145  }
146  }
147  if (required->number_of_particle_constituents > 1 && this->particle_constituent_name.empty()) {
148  good = false;
149  if (out) (*out) << "number_of_particle_constituents > 1, so particle_constituent_name "
150  "is required." << std::endl;
151  }
152 
153  return good;
154  }
155 
157  : Shape{ id }, Groups::Group_impl( grp ) {}
160  Shape::Shape(const std::string &uid) : particle_unique_id{ uid } {}
161  const std::string Shape::_icedb_obj_type_shape_identifier = "shape";
162 
163  bool Shape::isShape(Groups::Group &owner, const std::string &name) {
164  if (!owner.doesGroupExist(name)) return false;
165  auto grp = owner.openGroup(name);
166  return isShape(grp->getHDF5Group().get());
167  }
168  bool Shape::isShape(gsl::not_null<H5::Group*> group) {
169  if (!Attributes::CanHaveAttributes::doesAttributeExist(group, Group::_icedb_obj_type_identifier)) return false;
170  if (Attributes::CanHaveAttributes::getAttributeTypeId(group, Group::_icedb_obj_type_identifier) != typeid(std::string)) return false;
171 
173  = Attributes::CanHaveAttributes::readAttribute<std::string>(group, Group::_icedb_obj_type_identifier);
174  if (obj_type.data.size() != 1) return false;
175  if (obj_type.data[0] != Shape::_icedb_obj_type_shape_identifier) return false;
176  return true;
177  }
178  bool Shape::isShape() const {
179  return isShape(getHDF5Group().get());
180  }
181 
182  bool Shape::isValid(std::ostream *out) const { return isValid(this->getHDF5Group().get(), out); }
183  bool Shape::isValid(gsl::not_null<H5::Group*> group, std::ostream *out) {
186 
188 
189  bool good = true;
190  if (!isShape(group)) {
191  good = false;
192  if (out) (*out) << "This is not a valid shape. Missing the appropriate "
193  << Groups::Group::_icedb_obj_type_identifier << " attribute." << std::endl;
194  return good;
195  }
196 
197  if (!Attributes::CanHaveAttributes::doesAttributeExist(group, "particle_id")) {
198  good = false;
199  if (out) (*out) << "Missing the particle_id attribute." << std::endl;
200  }
201  else if (Attributes::CanHaveAttributes::getAttributeTypeId(group, "particle_id") != typeid(std::string)) {
202  good = false;
203  if (out) (*out) << "The particle_id attribute has the wrong type." << std::endl;
204  }
205  else {
206  auto attr = Attributes::CanHaveAttributes::readAttribute<std::string>(group, "particle_id");
207  if (attr.data.size() != 1) {
208  good = false;
209  if (out) (*out) << "The particle_id attribute has the wrong size. It should be a scalar." << std::endl;
210  }
211  else {
212  if (!attr.data[0].size()) {
213  good = false;
214  if (out) (*out) << "The particle_id attribute is empty." << std::endl;
215  }
216  }
217  }
218  if (out) (*out) << "TODO: Finish these checks!" << std::endl;
219  return good;
220  }
221 
222  Shape::Shape_Type Shape::openShape(Groups::Group &owner, const std::string &name) {
223  assert(owner.doesGroupExist(name));
224  return openShape(owner.openGroup(name)->getHDF5Group());
225  }
226 
228  return openShape(grpshp.getHDF5Group());
229  }
230 
232  const std::string &uid,
233  gsl::not_null<const NewShapeRequiredProperties*> required,
234  const NewShapeCommonOptionalProperties* optional)
235  {
236  return createShape(grpshp.getHDF5Group(), uid, required, optional);
237  }
238 
240  const std::string &uid,
241  gsl::not_null<const NewShapeRequiredProperties*> required,
242  const NewShapeCommonOptionalProperties* optional)
243  {
244  if (owner.doesGroupExist(name)) {
245  const auto grp = owner.openGroup(name);
246  return createShape(grp->getHDF5Group(), uid, required, optional);
247  }
248  else {
249  const auto grp = owner.createGroup(name);
250  return createShape(grp->getHDF5Group(), uid, required, optional);
251  }
252  }
253 
255  const std::string &uid,
256  gsl::not_null<const NewShapeRequiredProperties*> required,
257  const NewShapeCommonOptionalProperties* optional)
258  {
259  Expects(required->isValid(&(std::cerr)));
260  if (required->requiresOptionalPropertiesStruct()) Expects(optional);
261  if (optional) Expects(optional->isValid(required));
262 
263  Shape::Shape_Type res = std::make_unique<Shape_impl>(uid, newShapeLocation);
264 
265  // Write required attributes
266  res->writeAttribute<std::string>(Group::_icedb_obj_type_identifier, { 1 }, { Shape::_icedb_obj_type_shape_identifier });
267  res->writeAttribute<std::string>("particle_id", { 1 }, { required->particle_id });
268 
269  // Write required dimensions
270 
271  // This table is created, but if it is trivial, then it is unset (i.e. internally set only to the fill value)
272  bool createTblPSEN = false;
273  if (optional) {
274  if (optional->particle_scattering_element_number.size()) createTblPSEN = true;
275  }
276  if (required->NC4_compat) createTblPSEN = true;
277 
278  std::unique_ptr<icedb::Tables::Table> tblPSEN;
279  if (createTblPSEN) {
280  tblPSEN = res->createTable<uint64_t>("particle_scattering_element_number",
281  { static_cast<size_t>(required->number_of_particle_scattering_elements) });
282  if (optional) {
283  if (!optional->particle_scattering_element_number.empty()) {
284  tblPSEN->writeAll<uint64_t>(optional->particle_scattering_element_number);
285  }
286  }
287  // NOTE: The HDF5 dimension scale specification explicitly allows for dimensions to not have assigned values.
288  tblPSEN->setDimensionScale("particle_scattering_element_number");
289  }
290 
291  bool createTblPCN = false;
292  if (optional) {
293  if (optional->particle_constituent_number.size()) createTblPCN = true;
294  }
295  if (required->NC4_compat) createTblPCN = true;
296 
297  std::unique_ptr<icedb::Tables::Table> tblPCN;
298  if (createTblPCN) {
299  tblPCN = res->createTable<uint8_t>("particle_constituent_number",
300  { static_cast<size_t>(required->number_of_particle_constituents) });
301  if (optional) {
302  if (!optional->particle_constituent_number.empty()) {
303  tblPCN->writeAll<uint8_t>(optional->particle_constituent_number);
304  }
305  }
306  tblPCN->setDimensionScale("particle_constituent_number");
307  }
308 
309  std::unique_ptr<icedb::Tables::Table> tblXYZ;
310  if (required->NC4_compat) {
311  tblXYZ = res->createTable<uint8_t>("particle_axis", { 3 }, { 0, 1, 2 });
312  tblXYZ->setDimensionScale("particle_axis");
313  }
314 
315  constexpr size_t max_x = 20000;
316  const std::vector<size_t> chunks{
317  (max_x < required->number_of_particle_scattering_elements) ?
318  max_x : required->number_of_particle_scattering_elements, 3 };
319 
320  // Determine if we can store the data as integers.
321  // If non-integral coordinates, no.
322  bool considerInts = (required->particle_scattering_element_coordinates_are_integral) ? true : false;
323  bool useUint16s = false, useUint8s = false;
324  if (considerInts) {
325  // This minmax check is very slow.....
326  //const auto bounds = std::minmax_element(required->particle_scattering_element_coordinates.cbegin(), required->particle_scattering_element_coordinates.cend());
327  //if (*(bounds.first) > 0) {
328  // if (*(bounds.second) < UINT8_MAX - 2) useUint8s = true;
329  // else if (*(bounds.second) < UINT16_MAX - 2) useUint16s = true;
330  //}
331  float mx = -1;
332  if (optional) {
333  if (optional->hint_max_scattering_element_dimension > 0) {
334  mx = optional->hint_max_scattering_element_dimension > 0;
335  }
336  }
337  if (mx < 0) {
338  auto me = std::max_element(required->particle_scattering_element_coordinates.cbegin(), required->particle_scattering_element_coordinates.cend());
339  mx = *me;
340  }
341  if (mx < UINT8_MAX - 2) useUint8s = true;
342  else if (mx < UINT16_MAX - 2) useUint16s = true;
343  }
344 
345  if (useUint8s) {
346  std::vector<uint8_t> crds_ints(required->number_of_particle_scattering_elements * 3);
347  for (size_t i = 0; i < crds_ints.size(); ++i) {
348  crds_ints[i] = static_cast<uint8_t>(required->particle_scattering_element_coordinates[i]);
349  }
350  auto tblPSEC = res->createTable<uint8_t>(
351  "particle_scattering_element_coordinates",
352  { static_cast<size_t>(required->number_of_particle_scattering_elements), 3 },
353  crds_ints, &chunks);
354  if (tblPSEN) tblPSEC->attachDimensionScale(0, tblPSEN.get());
355  if (tblXYZ) tblPSEC->attachDimensionScale(1, tblXYZ.get());
356  }
357  else if (useUint16s) {
358  std::vector<uint16_t> crds_ints(required->number_of_particle_scattering_elements*3);
359  for (size_t i = 0; i < crds_ints.size(); ++i) {
360  crds_ints[i] = static_cast<uint16_t>(required->particle_scattering_element_coordinates[i]);
361  }
362  auto tblPSEC = res->createTable<uint16_t>(
363  "particle_scattering_element_coordinates",
364  { static_cast<size_t>(required->number_of_particle_scattering_elements), 3 },
365  crds_ints, &chunks);
366  if (tblPSEN) tblPSEC->attachDimensionScale(0, tblPSEN.get());
367  if (tblXYZ) tblPSEC->attachDimensionScale(1, tblXYZ.get());
368  } else {
369  auto tblPSEC = res->createTable<float>("particle_scattering_element_coordinates",
370  { static_cast<size_t>(required->number_of_particle_scattering_elements), 3 },
371  required->particle_scattering_element_coordinates, &chunks);
372  if (tblPSEN) tblPSEC->attachDimensionScale(0, tblPSEN.get());
373  if (tblXYZ) tblPSEC->attachDimensionScale(1, tblXYZ.get());
374  }
375 
376 
377  if (optional) {
378 
379  // TODO: if (optional->particle_constituent_name.size()) {}
380 
381 
382  if (optional->particle_constituent_single_name.size()) {
383  res->writeAttribute<std::string>("particle_single_constituent_name",
384  { 1 }, { optional->particle_constituent_single_name });
385  }
386 
388  const std::vector<size_t> cs{
389  (max_x < required->number_of_particle_scattering_elements) ?
390  max_x : required->number_of_particle_scattering_elements,
391  static_cast<size_t>(required->number_of_particle_constituents)
392  };
393  auto tblPSEC2a = res->createTable<float>("particle_scattering_element_composition_fractional",
394  { static_cast<size_t>(required->number_of_particle_scattering_elements),
395  static_cast<size_t>(required->number_of_particle_constituents) },
397  if (tblPSEN) tblPSEC2a->attachDimensionScale(0, tblPSEN.get());
398  if (tblPCN) tblPSEC2a->attachDimensionScale(1, tblPCN.get());
399  }
400 
401  if (optional->particle_scattering_element_composition_whole.size()) {
402  const std::vector<size_t> cs{
403  (max_x < required->number_of_particle_scattering_elements) ?
404  max_x : required->number_of_particle_scattering_elements
405  };
406  auto tblPSEC2b = res->createTable<uint8_t>(
407  "particle_scattering_element_composition_whole",
408  { static_cast<size_t>(required->number_of_particle_scattering_elements) },
410  if (tblPSEN) tblPSEC2b->attachDimensionScale(0, tblPSEN.get());
411  }
412 
413 
414 
415  // Write common optional attributes
416  if (optional->particle_scattering_element_spacing > 0)
417  res->writeAttribute<float>("particle_scattering_element_spacing", { 1 }, { optional->particle_scattering_element_spacing });
418 
419  // Write common optional variables
420  if (optional->particle_scattering_element_radius.size()) {
421  auto tblPSER = res->createTable<float>("particle_scattering_element_radius",
422  { static_cast<size_t>(required->number_of_particle_scattering_elements) },
424  if (tblPSEN) tblPSER->attachDimensionScale(0, tblPSEN.get());
425  }
426 
427  }
428 
429  return res;
430  }
431 
433  // Get UUID
434  //res->writeAttribute<std::string>("particle_id", { 1 }, { required->particle_id });
435  Expects(isShape(shape.get()));
436  //Expects(shape->attrExists("particle_id")); // Expects(isShape(shape.get())) subsumes this.
437  auto id = Attributes::CanHaveAttributes::readAttribute<std::string>(shape.get(),"particle_id");
438 
439  Shape::Shape_Type res = std::make_unique<Shape_impl>(id.data[0], shape);
440  return res;
441  }
442  }
443 }
gsl::span< const uint8_t > particle_scattering_element_composition_whole
Definition: shape.hpp:93
static bool isValid(gsl::not_null< H5::Group *> group, std::ostream *out=nullptr)
Is "group" a valid shape, according to the spec.?
Definition: Shapes.cpp:183
static Shape_Type createShape(Groups::Group &grpshp, const std::string &uid, gsl::not_null< const NewShapeRequiredProperties *> required, const NewShapeCommonOptionalProperties *optional=nullptr)
Create a new shape.
Definition: Shapes.cpp:231
virtual ~Shape()
Definition: Shapes.cpp:159
void setDimensionScale(const std::string &dimensionScaleName)
Designate this table as a dimension scale.
Definition: Tables.cpp:106
virtual Group_HDF_shared_ptr getHDF5Group() const =0
Get the fundamental HDF5 object that the group is built on.
std::shared_ptr< H5::Group > Group_HDF_shared_ptr
Definition: Group.hpp:32
A group is similar to a folder / directory. It can have Attributes and Tables.
Definition: Group.hpp:22
static const std::string _icedb_obj_type_shape_identifier
Each shape &#39;group&#39; has an attribute with this identifier. Used for shape collection and searching...
Definition: shape.hpp:118
std::string particle_id
ATTRIBUTE: Unique Particle Identifier.
Definition: shape.hpp:48
A high-level class to manipulate particle shapes.
Definition: shape.hpp:112
virtual Group_ptr createGroup(const std::string &groupName)=0
Create a group.
gsl::span< const float > particle_scattering_element_radius
Definition: shape.hpp:69
virtual bool doesGroupExist(const std::string &groupName) const =0
Does a group with this name exist?
float particle_scattering_element_spacing
OPTIONAL ATTRIBUTE: Physical spacing between adjacent grid points (in meters). Used in DDA...
Definition: shape.hpp:96
float hint_max_scattering_element_dimension
EXPERIMENTAL HINT: Specify the maximum scattering element dimension.
Definition: shape.hpp:100
std::vector< DataType > data
Definition: Attribute.hpp:30
Shape_impl(const std::string &id, Groups::Group::Group_HDF_shared_ptr grp)
Definition: Shapes.cpp:156
Group_HDF_shared_ptr grp
Definition: Group_impl.hpp:17
virtual Group_ptr openGroup(const std::string &groupName) const =0
Opens a group.
Structure containing a list of all of the common optional data for creating a new shape in the databa...
Definition: shape.hpp:60
gsl::span< const float > particle_scattering_element_coordinates
Definition: shape.hpp:37
const std::string particle_unique_id
This is the unique identifier for this shape.
Definition: shape.hpp:194
gsl::span< const uint64_t > particle_scattering_element_number
DIMENSION: The id number for each scattering element. Single dimension.
Definition: shape.hpp:62
Shape(const std::string &uid)
Definition: Shapes.cpp:160
void writeAll(const gsl::span< const DataType > &outData) const
Write the passed data to the table. Writes whole table.
Definition: Table.hpp:100
bool isValid(gsl::not_null< const NewShapeRequiredProperties *> required, std::ostream *errout=nullptr) const
Definition: Shapes.cpp:56
bool isShape() const
Is this object actually a shape?
Definition: Shapes.cpp:178
static Shape_Type openShape(Groups::Group &grpshp)
Definition: Shapes.cpp:227
gsl::span< const float > particle_scattering_element_composition_fractional
Definition: shape.hpp:88
This class defines an attribute.
Definition: Attribute.hpp:23
std::unique_ptr< Shape > Shape_Type
The preferred C++ type for referencing a shape.
Definition: shape.hpp:145
gsl::span< const uint8_t > particle_constituent_number
DIMENSION: The id number of each particle&#39;s constituent. Single dimension.
Definition: shape.hpp:64
std::type_index getAttributeTypeId(const std::string &attributeName) const
Returns the type of an attribute.
Definition: Attributes.cpp:50
static const std::string _icedb_obj_type_identifier
The tag used in icedb to identify a group.
Definition: Group.hpp:29
const std::string name
The name of the group.
Definition: Group.hpp:34
bool doesAttributeExist(const std::string &attributeName) const
Does the object have an attribute with the given name?
Definition: Attributes.cpp:61
bool isValid(std::ostream *errout=nullptr) const
Definition: Shapes.cpp:10