icedb  version 0.5.1
Snow particle scattering database API
fs-test.cpp
Go to the documentation of this file.
1 
17 #include <icedb/defs.h>
18 
19 #include <boost/program_options.hpp>
20 #include <iostream>
21 #include <string>
22 #include <vector>
23 
24 #include <icedb/fs.hpp>
25 #include <icedb/Database.hpp>
26 #include <icedb/shape.hpp>
27 
28 int main(int argc, char** argv) {
29  using namespace std;
30  // These statements create and parse command-line options. See
31  // the boost::program_options documentation for other cool
32  // things that you can do, like reading environment variables or XML
33  // files.
34  namespace po = boost::program_options;
35  po::options_description desc("Allowed options");
36 
37  desc.add_options()
38  // Users can get a help message by just typing --help as an option. --help takes no parameters.
39  ("help,h", "produce help message")
40  // Users can change the name of the sample database to be read or written.
41  // The default database folder is "testdataset". By passing the --dbpath {path} or -p {path}
42  // option, this can be overridden.
43  ("dbpath,p", po::value<string>()->default_value("testdataset"), "Database path")
44  // These tell the program whether you want to create or open a database.
45  ("create", "Create a sample database at this location")
46  ("rw", "Specifies that the database should be opened in read-write mode")
47  ("ro", "Specifies that the database should be opened in read-only mode")
48  ;
49 
50  // The user-passed options are read from argv and argv, and are placed into a
51  // variables_map structure, called "vm".
52  po::variables_map vm;
53  po::store(po::command_line_parser(argc, argv).
54  options(desc).run(), vm);
55  po::notify(vm);
56 
57  // C++11 introduced lambda functions. This doHelp function is used when parsing the program options.
58  // If the user-specified options are inconsistent, then this funciton prints a descriptive message,
59  // re-lists the program options and terminates the program.
60  auto doHelp = [&](const std::string& s)->void
61  {
62  std::cout << s << endl;
63  std::cout << desc << endl;
64  exit(1);
65  };
66 
67  // For example, if the user does not specify any options, or if the user types --help or -h,
68  // then the doHelp function is called.
69 
70  // To check if an option is specified, use vm.count("long-option-name").
71  if (vm.count("help")) doHelp("");
72  if (!vm.count("create") && !vm.count("rw") && !vm.count("ro"))
73  doHelp("Must specify if the database is being created, or "
74  "opened in read-write or read-only mode.");
75 
76  // If an option is specified (or has a default value), then it can be read.
77  // Here, we read the "dbpath" option as a string.
78  string dbpath = vm["dbpath"].as<string>();
79 
80  // BASIC NAVIGATION (DATABASES AND GROUPS)
81  // ------------------
82 
83  // Here, the variable 'db' is a smart pointer to a loaded database.
84  // A smart pointer is a special type of C++ class that automatically
85  // frees (i.e. closes and deallocates) the database when it is no longer
86  // used.
87  // The Database class definition is located in Database.hpp.
88  std::unique_ptr<icedb::Databases::Database> db;
89 
90  // Are we creating a new database, or opening an existing database?
91  if (vm.count("create")) {
92  std::cout << "Trying to create a sample database at " << dbpath << std::endl;
93  // Let's call Database::createSampleDatabase.
94  // The createSampleDatabase function creates several folders and files, under a
95  // base directory located at 'dbpath'. These files, combined, form a database.
96  //
97  // After creating the sample database files, createSampleDatabase calls
98  // openDatabase. This function loops through all directories, HDF5 and netCDF
99  // files and unifies them into a single virtual database. See index.hdf5 to get
100  // a view of how these files all come together.
102  }
103  else {
104  // Instead of creating a new database, this branch of code deals with
105  // opening an existing database.
106  std::cout << "Trying to open the database at " << dbpath << std::endl;
107  std::cout << "Note: This will fail if the database does not exist. "
108  "If this is the case, either run with --create as an option, "
109  "or check that the database path (--dbpath) is correct." << std::endl;
111  if (vm.count("ro")) {
113  std::cerr << "You have stated that you want to open the database in "
114  "read-only mode. While this is a perfectly valid option for most "
115  "scenarios, this example program will fail with various errors. "
116  "Viewing these error messages is rather instructive, as it can help "
117  "you to become accustomed to debugging icedb."
118  << std::endl;
119  }
120  if (vm.count("rw")) flags = icedb::fs::IOopenFlags::READ_WRITE;
121  db = icedb::Databases::Database::openDatabase(dbpath, flags);
122  // Once the database is opened, you can open groups, read attributes, tables and shapes.
123  // If the database is opened in READ_WRITE mode, then you can also write objects.
124 
125  // The indexDatabase command will generate an index file that points the
126  // location to all of the shapes and scattering objects. TO BE IMPLEMENTED.
128  }
129 
130 
131  // Once the database is opened, we can start to manipulate the database structure.
132  // Here is how to open a netCDF / HDF5 group.
133  std::unique_ptr<icedb::Groups::Group> grpMain = db->openGroup("Testing");
134  // For ease of typing, you could also just type:
135  //auto grpMain = db->openGroup("Testing");
136  // The createSampleDatabase function created a folder, "Testing", which
137  // contains the "scratch.hdf5" file.
138  // The openDatabase command represents the "Testing" folder as a Group
139  // (see icedb::Groups::Group in Group.hpp).
140  // The "scratch.hdf5" file is mounted in the virtual database under
141  // "Testing/scratch.hdf5". All objects placed this group are written to that
142  // hdf5 file.
143  auto grpScratch = grpMain->openGroup("scratch.hdf5");
144 
145  // Let's create another group, this time within scratch.hdf5.
146  auto grpTest1 = grpScratch->createGroup("Obj_1");
147 
148  // ATTRIBUTES
149  // --------------------------------
150  {
151  // Groups can have Attributes, which are small blobs of data.
152  // Attributes have a name, dimensions, and a datatype.
153  // Here is how you can define an attribute:
155  "TestDouble1", { 1 }, { 2.0 });
156  // This creates an attribute, called "TestDouble1", with a double type,
157  // expressed as a one-dimensional array with a single value (1) in this dimension,
158  // with a value of 2.0.
159  // We can write this attribute to the Obj_1 group using:
160  grpTest1->writeAttribute(attrDouble1);
161 
162  // In HDF5, attributes can be multi-dimensional, but in netCDF, they may
163  // have only a single dimension. Here is how to define a single-dimensional
164  // vector of four floating-point values.
166  "TestFloat2",
167  { 4 },
168  { 1.0f, 2.0f, 2.5f, 3.0f });
169  grpTest1->writeAttribute(attrFloat2);
170 
171  // A multi-dimensional attribute may be defined this way:
173  "TestFloat3",
174  { 2, 3 },
175  { 1.0f, 2.0f, 3.1f, 4.2f, 5.3f, 6.0f });
176  // Note: since we are sticking to the netCDF-4 standard, we wil not write this
177  // variable to the file. If we did, then the file would not be intelligible
178  // to the netCDF libraries and the nc_dump program. HDF5 and HDFview would
179  // still be able to read it, though.
180  //grpTest1->writeAttribute(attrFloat3);
181 
182  // Also note, there is a difference between a vector (a one-dimensional array)
183  // and a two-dimensional array that has one of the dimension lengths set to one.
184  // For example, here is how to define a 1x3 matrix. It will not work with netCDF.
186  "TestFloat4",
187  { 1, 3 },
188  { 4.2f, 5.3f, 6.0f });
189  //grpTest1->writeAttribute(attrFloat4);
190 
191  // For ease of typing, we can write attributes more easily using lines like these:
192  //grpTest1->writeAttribute(icedb::Attributes::Attribute<uint64_t>("TestInt5", { 1 }, { 65536 }));
193  // or
194  //grpTest1->writeAttribute<uint64_t>("TestInt5", {1}, {65536});
195 
196  // Attributes can be floats, doubles, signed 64-bit integers (int64_t), unsigned 64-bit integers (uint64_t),
197  // and strings of characters (std::string).
198  grpTest1->writeAttribute(icedb::Attributes::Attribute<std::string>("TestString6", "Test string 6"));
199  // Any other types are invalid. I might extend the interface to support these later on, but there isn't
200  // much need to do so.
201 
202  // Currently, there is one restriction for writing strings: only a single string can be written to
203  // an attribute. So, this statement will not work:
204  //grpTest1->writeAttribute(icedb::Attributes::Attribute<std::string>("TestStringSet7", { 2 }, { "Test string 7", "TS 8" }));
205  // This will eventually be fixed.
206 
207  // Of course, you will also want to be able to read attributes. The syntax is similar.
208  icedb::Attributes::Attribute<std::string> readTS6 = grpTest1->readAttribute<std::string>("TestString6");
209  std::cout << "We just read TestString6 = " << readTS6.data[0] << std::endl;
210  }
211 
212  // TABLES
213  // --------------------------------
214  {
215  // Groups can also have Tables (a.k.a. DataSets in HDF5 / Variables in NetCDF).
216  // Unlike attributes, table objects are much larger and are not automatically
217  // loaded into memory.
218 
219  // Creating a table is easy:
220  auto tblTest = grpTest1->createTable<int64_t>("Test_table_1", { 2, 3 }, { -2, -1, 0, 1, 2, 3 });
221  // The above command just made a table with two rows and three columns.
222  // The data are inserted into the table in row-major form.
223  // See Table.hpp to list the methods for adding larger data into tables.
224  // For these other methods, the sizing of the table may be de-coupled
225  // from writing data to the tables.
226 
227  // Under their current implementation, table dimensions are entirely fixed and are
228  // set at creation. In the future, support for extendible dimensions will be added.
229 
230  // Tables support the same data types as attributes (uint64_t, int64_t, float, double).
231  // Strings are on the to-do list.
232  // Unlike with attributes, tables can be entirely multi-dimensional.
233 
234  // Tables can have attributes.
235  tblTest->writeAttribute<int64_t>(icedb::Attributes::Attribute<int64_t>("TestInt7", 5));
236  tblTest->writeAttribute<int64_t>(icedb::Attributes::Attribute<int64_t>("TestInt8", { 4 }, { 1, -1, 2, -2 }));
237 
238  // For compatability with netCDF, each data table must be associated with one
239  // or more "dimension scales". To create and associate these scales with a table,
240  // commands like these are needed:
241  auto tblDims1 = grpTest1->createTable<int64_t>("X_axis", { 3 }, { 9, 10, 11 });
242  auto tblDims2 = grpTest1->createTable<int64_t>("Y_axis", { 2 }, { 4, 6 });
243  tblDims1->setDimensionScale("X_axis");
244  tblDims2->setDimensionScale("Y_axis");
245  tblTest->attachDimensionScale(0, tblDims2.get());
246  tblTest->attachDimensionScale(1, tblDims1.get());
247 
248  // I recommend that you open scratch.hdf5 in nc_dump or HDFview to see the resulting attributes
249  // and tables that we just created.
250 
251  }
252 
253 
254  // SHAPE FILES AND DATA STRUCTURES
255  // --------------------------------
256  {
257  // Shapes are special collections of tables and attributes. Let's make a naive shape,
258  // with only a few dipoles and a single substance.
259  auto grpTest2 = grpScratch->createGroup("Obj_Shape_2");
260  // Shapes have required and optional objects
263 
264  constexpr uint64_t numPoints = 8;
265  constexpr uint64_t numSubstances = 1;
266  constexpr uint64_t numAxes = 3;
267  //constexpr std::array<uint64_t,numPoints> point_ids = { 0, 1, 2, 3, 4, 5, 6, 7 }; // eight points
268  //constexpr std::array<uint64_t,numSubstances> composition_ids = { 0 };
269  constexpr std::array<float, numPoints * numAxes> points = {
270  //const std::vector<float> points = {
271  1, 1, 1,
272  1, 1, 2,
273  1, 1, 3,
274  2, 2, 2,
275  2, 2, 3,
276  3, 2, 2,
277  3, 2, 3,
278  3, 3, 2
279  };
280  //constexpr std::array<float, numPoints * numSubstances> point_compositions = { 1, 1, 1, 1, 1, 1, 1, 1 };
281 
282  // Required attributes
283  shpRequired.particle_id = "000001";
284  // Required dimensions
285  shpRequired.number_of_particle_scattering_elements = numPoints;
286  shpRequired.number_of_particle_constituents = numSubstances;
287  // Required variables
288  shpRequired.particle_scattering_element_coordinates = points;
289 
290  shpCommonOptional.particle_scattering_element_spacing = static_cast<float>(40. * 1e-6);
291 
292  // Validate and write out any errors.
293  shpRequired.isValid(&cerr);
294  shpCommonOptional.isValid(&shpRequired, &cerr);
295 
297  *(grpTest2.get()), "Test_shape", shpRequired.particle_id,
298  &shpRequired, &shpCommonOptional);
299  //icedb::Shapes::Shape::createShape(grpTest2,)
300  }
301 
302  // End of example.
303  return 0;
304 }
static void indexDatabase(const std::string &location)
Definition: databases.cpp:161
IOopenFlags
Definition: fs.hpp:16
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
Strucure containing a list of all of the required data needed to create a new shape in the database...
Definition: shape.hpp:12
STL namespace.
std::string particle_id
ATTRIBUTE: Unique Particle Identifier.
Definition: shape.hpp:48
static Database_ptr createSampleDatabase(const std::string &location)
Definition: databases.cpp:68
int main(int argc, char **argv)
This is an example application that illustrates how to manipulate the most basic features of an icedb...
Definition: fs-test.cpp:28
float particle_scattering_element_spacing
OPTIONAL ATTRIBUTE: Physical spacing between adjacent grid points (in meters). Used in DDA...
Definition: shape.hpp:96
std::vector< DataType > data
Definition: Attribute.hpp:30
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
bool isValid(gsl::not_null< const NewShapeRequiredProperties *> required, std::ostream *errout=nullptr) const
Definition: Shapes.cpp:56
This class defines an attribute.
Definition: Attribute.hpp:23
static Database_ptr openDatabase(const std::string &location, fs::IOopenFlags flags=fs::IOopenFlags::READ_ONLY)
Definition: databases.cpp:118
bool isValid(std::ostream *errout=nullptr) const
Definition: Shapes.cpp:10