GNDStk: GNDS Toolkit¶
INTRODUCTION & PRIMER¶Introduction¶Description¶Los Alamos National Laboratory’s GNDS Toolkit, or GNDStk, has been designed first and foremost to provide a powerful, intuitive, and flexible C++ language API for interacting with Generalized Nuclear Database Structure data. We begin by providing basic and cleanly-designed classes in which GNDS data are stored. Next, we support a robust and flexible I/O system for reading from, and writing to, both the XML and JSON file formats. Support for more file formats is anticipated in the future, as GNDS becomes more widely used. While GNDStk is one library, from which you can use any functionality you wish to at any time, we consider it conceptually to consist of roughly three major parts: basic constructs and I/O; a “core” interface, and a higher-level interface that will also be equipped with Python bindings for users who wish to take advantage of them. Let’s say a bit more about all of these elements. BASICS Here we have the basic requisite data structures and functions, as well as flexible and easy-to-use GNDS file I/O capabilities. Along with these also come, of course, the numerous and sundry utilities needed for their implementation. Some of the utilities, e.g. those for generating diagnostic messages such as warnings and errors, may be of value in their own right to our users. We’ll therefore provide some documentation of how selected utility constructs work, without distracting us from our focus on GNDStk’s major, most interesting capabilities. CORE INTERFACE The heart of GNDStk lies in its Core Interface. Consider this interface to include the basics as described above, while adding to them a powerful, flexible, and highly user-programmable suite of data query and creation capabilities that can be used to great effect by themselves if you wish – given some knowledge of the GNDS hierarchy’s internal structure – and also for creating higher-level interfaces like our own. Our Core Interface allows for version-independent access to all data in any GNDS file, including functionality for reading, writing, and modification. We support both a more-traditional C++ API design, in which users can interact with classes and functions in the usual fashion (largely through the Basics as described above), as well as a powerful and easily extensible “query system” for retrieving or creating GNDS content. The query system is, in particular, quite intentionally designed to enable you to integrate GNDStk’s capabilities easily into virtually any other code in which you might wish to use it – code that utilizes entirely your own data structures, perhaps, or those of any other library or libraries with which you may be working. HIGH-LEVEL INTERFACE While still a work-in-progress at the time of this writing, GNDStk’s High-Level Interface will be comprised of several elements. First, we’ll provide one or more C++ base classes that are designed to provide value to high-level derived classes that one might wish to create, individually or en masse, to represent GNDS data structures. Note that, here, our use of the word derived refers to derived classes in C++ – not, say, to nuclear data that were derived in some sense from other nuclear data. Notably, and with support from the Core Interface, proper handling will be made available for capturing the concepts of a required field, an optional field, and an optional, with default field in a GNDS data structure. Here, and thinking in the language of XML, field may mean something from an XML element’s attributes (we’ll prefer the non-XML-specific term metadata in GNDStk), or from its nested XML elements – child nodes, in our preferred terminology. An additional note regarding terminology: We may occasionally write element to mean GNDS data as it would appear in an XML element, if and when the term works well in the narrative, but do so with the understanding that GNDS data need not, of course, originate from an XML source, or be intended for an XML destination. We’ll also design GNDS version-specific collections of high-level classes that represent important data structures for the GNDS version in question. Assuming that the GNDS specifications don’t change a great deal between releases, we’ll expect to see substantial overlap, across our version-specific collections, of these classes. GNDStk will, naturally, handle such issues efficiently, and will do so in a manner that’s entirely transparent to users. We’ll focus on making our capabilities work well, so that you can focus similarly on yours. Finally, but no less significantly for many of GNDStk’s intended beneficiaries, we’ll provide a suite of Python bindings to much of our C++ functionality. That will include, certainly, the major classes in our version-specific interfaces, and perhaps also – where reasonable and possible, given differences between Python and C++ as well as their respective limitations - to selected fabulous and action-packed lower-level Core Interface constructs as well. Background¶The Generalized Nuclear Database Structure, or GNDS, started at Lawrence Livermore National Laboratory as an effort to update their ENDL format. Realizing that they could also modernize the ENDF format for nuclear data, and make this modernization useful and available to everyone, they evolved GNDS to contain evaluated data, processed data, and application data. GNDS has become an international standard as part of the OECD/NEA. Acknowledgements¶The author wishes to thank several individuals for the support and ideas that they provided throughout this endeavor. An introduction to GNDS, to the broader NJOY21 project, and to the need for quality software for working with the GNDS format, was provided by Nuclear Data team leader Jeremy Conlin. Materials and Physical Data (XCP-5) group leader Patrick Talou tirelessly ensured financial support for the project, and positive encouragement for its personnel. Nathan Gibson constructed the build system currently in use not only for GNDStk but for many other elements of NJOY21. Finally, Wim Haeck was a source of copious and invaluable comments, ideas, and discussions, without which GNDStk wouldn’t be what it is today. Martin Staley, Los Alamos National Laboratory, February 2021 Building GNDStk¶We designed GNDStk with the hope that it will prove to be straightforward to download, to install, and to use. GNDStk resides on Github, and uses CMake as its primary build system. The following description is based on commands that work for us, on a Linux system. Adapt our instructions as necessary for your own platform. Download¶Enter the directory in which you’d like GNDStk to reside. This might be your
root user directory, e.g. Download: git clone https://github.com/njoy/GNDStk.git
Enter directory: cd GNDStk
At this point you can, if you wish, check out a specific branch of GNDStk.
As is typical in Check out a branch: git checkout master
With Build & Test¶Some people prefer, as you may, to create a Make and enter a build directory: mkdir build
cd build
Now run Run cmake: cmake ..
The good news is that the above command should download GNDStk’s
dependencies – the modest number of outside C++ libraries on which
it depends – automatically. The bad news is that the download may, for the
same reason, take quite some time. Resist the temptation to terminate the
command, perhaps believing that your computer has hung, and consider starting
Finally, the Build GNDStk’s test suite: make
Or, if you have for instance six processor cores available, then Multicore build: make -j 6
will no doubt run far faster. GNDStk was carefully designed to not be one of those infamous C++ libraries
that triggers hours-long, even many-minutes-long, compilations, leaving
beleaguered users wondering if they could more quickly find the data they’re
looking for by loading a file into an editor, finding the data of interest,
and cutting-and-pasting in their own code.
The above You can then invoke: Run GNDStk’s tests: make test
to run all of the tests. We hope that you will, at this point, have the same
pleasant experience that we do when we invoke Summary¶Here’s a summary of the commands described above, from downloading GNDS from our repository, through building and running its full suite of tests: # Get GNDStk
git clone https://github.com/njoy/GNDStk.git
cd GNDStk
git checkout master
# Cmake; may take some time
mkdir build
cd build
cmake ..
# Make and run test suite
make
# ...or make -j 6
make test
Your Own Application¶Let’s outline how you can interface your own application code with GNDStk, using CMake. First, you should have downloaded the GNDStk repository as described above. Building and running its test suite isn’t a prerequisite for our present purposes, but certainly wouldn’t hurt. Any problems you might encounter in that process would no doubt show themselves again, in some form, here. Now assume you have some directory, call it MyApp, for your application, with the following file structure: MyApp/
CMakeLists.txt
dependencies/
GNDStk/
src/
app.cpp
A working
cmake_minimum_required( VERSION 3.14 )
project( app LANGUAGES CXX )
set( CMAKE_CXX_STANDARD 17 )
set( CMAKE_CXX_STANDARD_REQUIRED YES )
add_subdirectory(dependencies/GNDStk)
add_executable(app src/app.cpp)
target_link_libraries(app PUBLIC GNDStk)
And, the simplest possible GNDStk-aware
#include "GNDStk.hpp"
int main()
{
}
Finally, building cd MyApp # <== If you're not there already
mkdir build
cd build
cmake ..
make
If all went well, Alternative: Bash Script¶An important goal for us is that GNDStk be accessible, with as minimally intrusive a build process as possible, to a wide variety of researchers. If you’re using libraries other than GNDStk, they may impose their own build systems – possibly ones you like, possibly ones you don’t, but ones you’re stuck with, regardless, for better or for worse. You may, on the other hand, be using your preferred and well-liked build system. Independent of what you may or may not be working with in that respect, we want GNDStk to impose as little additional complexity as it can. In the above spirit, and if you’re using a Linux or Linux-based machine, you may find that the contents of the following simple shell script can be adapted easily to your needs: #!/bin/bash
# Specify your base GNDStk directory (as cloned from github) here.
GNDSTKDIR=/path/to/your/downloaded/GNDStk
# Example compilation command. -std=c++17, and the -Is, are needed.
COMPILE="
g++
-std=c++17
-I$GNDSTKDIR/src
-I$GNDSTKDIR/build/_deps/pugixml-adapter-src/src/src
-I$GNDSTKDIR/build/_deps/json-src/include
-I$GNDSTKDIR/build/_deps/json-src/include/nlohmann
-I$GNDSTKDIR/build/_deps/log-src/src
-I$GNDSTKDIR/build/_deps/spdlog-src/include
-Wall -Wextra -Wpedantic"
# pugixml.cpp is the only C++ source file, other than your own,
# that needs to be compiled. We'll arrange to build it *once*.
if [ ! -f "pugixml.o" ]; then
$COMPILE \
$GNDSTKDIR/build/_deps/pugixml-adapter-src/src/src/pugixml.cpp \
-c -o pugixml.o
fi
# Compile your own C++ application, linking with the .o from above.
$COMPILE app.cpp pugixml.o -o app
Begin, as you can see, by specifying the base GNDStk directory you cloned. The
script immediately uses this value to create a simple compilation command, in
this case one that uses You’re welcome to adapt our script, or its contents, as may be necessary or helpful within your own build regime. Some caveats. Use of the sample bash script assumes that you’ve downloaded
GNDStk, and run Be aware also that the script reflects dependencies, and their locations in
directories, that are correct at the time of this writing. While we intend to
update these instructions if and when we make relevant changes to GNDStk, it’s
possible that some detective work may prove to be necessary if we drop the
documentation ball after dependencies do, for whatever reason, change. If, for
instance, we decide to explore someday one of those deep mysteries of the
universe that regularly visits our world through computers, such as why
Header-Only Library¶GNDStk, proper, is a C++ header-only library. You can find plenty of information online if you’re unfamiliar with the concept. In our opinion, header-only libraries provide a multitude of advantages, such as making builds far less complex than they’d otherwise be; and their disadvantages, generally distilling down to some variation of “builds can take longer,” are straightforward to mitigate with careful design. We designed GNDStk carefully. Our library does, however, have one dependency, We mention our library’s header-only nature not so as to conclude this chapter with any particular profound point, but largely for informational purposes. If you’re unfamiliar with the header-only concept, or with how to write or to use such libraries in C++, then you might find it helpful – or more importantly, fun – to learn more. With respect to GNDStk, knowing that it’s formulated in this fashion may allow you, in one way or another, to make the best use of GNDStk in your own build system, and in your own application. Tutorial¶Basics + Core Interface¶Let’s begin with some very minimal GNDStk-based example codes, explain our arrangement of major C++ namespaces, and then move on to more-interesting and useful examples. Minimal GNDStk-Aware Code¶Here’s the most minimal GNDStk “application”, albeit one that doesn’t do anything: #include "GNDStk.hpp"
int main()
{
}
The takeaway: to use GNDStk, you should Put another way, GNDStk is not designed, as some libraries are, so that you
selectively choose what headers to The entire C++ Standard library is very large, of course, and such selectivity
is important so that compile times are kept under control. GNDStk is much
smaller, and we’ve judged that compilation times aren’t significantly impacted
by putting forth our simple, easily followed rule: just
Recommended Starting Point¶For most users, most of the time, we suggest this starting point: #include "GNDStk.hpp"
using namespace njoy::GNDStk::core;
int main( /* argc,argv as necessary */ )
{
}
This merely adds a specific Namespace Hierarchy¶GNDStk, like many C++ libraries, places its various constructs into a moderate number of C++ namespaces. For simplicity’s sake, omitting details that most users won’t care about, consider that GNDStk’s namespace hierarchy looks something like this: // Outer namespaces that surround everything else.
namespace njoy {
namespace GNDStk {
// Namespace for basic, generic "GNDS query objects". There are
// other sets of query objects; don't worry about those for now.
namespace basic {
// Query objects specific for GNDS metadata.
namespace meta {
}
// Query objects specific for GNDS child nodes.
namespace child {
}
// Bring in meta:: and child:: above.
using namespace meta;
using namespace child;
// Bring in common:: below.
using namespace common;
}
// Some query objects that we wish to be shared across basic::
// as well as other query-object namespaces that we don't list here.
namespace common {
}
// Logging capabilities, including, for example, support for errors,
// warnings, and informational messages.
namespace log {
}
// Our "core" namespace; see discussion.
namespace core {
// Bring in GNDStk:: itself, and basic:: above
using namespace GNDStk;
using namespace basic;
}
}
}
A few things are happening here, so bear with us. For starters, everything is in Fundamentally important in GNDStk are its “query objects”. Think of these as
small modules of information that facilitate a concise notation for getting
(or setting) GNDS data. (Much more on all of this later.) For now, suffice it
to say that the query objects of most interest to users are our basic ones
in – drum roll, please – Strangely – at first glance –
A namespace Core Interface¶That brings us, finally, to the using namespace njoy::GNDStk::core;
namespace core {
using namespace GNDStk;
using namespace basic;
}
So, All things considered, then, the single directive
njoy::GNDStk::
njoy::GNDStk::basic::
njoy::GNDStk::basic::meta::
njoy::GNDStk::basic::child::
njoy::GNDStk::common::
with the single caveat we spoke of already in regards to The combined content of the above-listed namespaces constitute what we consider
to be a good set of core GNDStk capabilities. Hence, our motivation for
creating a You can consider the phrases core namespace and core interface to be essentially interchangeable. Which term we use, and where, depends on whether we’re referring to the namespace in particular, or to the functionality it exposes. We’ll note, finally, that having (and recommending) our Read and Write GNDS¶Read XML¶Here’s a simple code that reads the XML format GNDS file named
#include "GNDStk.hpp"
using namespace njoy::GNDStk::core;
int main()
{
Tree pu239("n-094_Pu_239.xml");
}
Obviously, but worth a reminder, this assumes that the file resides right there, in the directory from which this code is run. If it doesn’t, include a suitable absolute or relative path in the file name string. We, and no doubt everyone reading this, have probably made this mistake often enough over the years.
A large collection of XML-format GNDS files can be downloaded from here: That’s where we got our example’s While we didn’t say so directly, a C++ programmer will have realized what the
above code tells us: that #include "GNDStk.hpp"
using namespace njoy::GNDStk::core;
int main()
{
Tree pu239;
pu239.read("n-094_Pu_239.xml");
}
which is more explicit in its purpose, but slightly less concise. (And the
earlier, direct-constructed Files, Streams, Types¶In terms of what we saw above, The first argument is either a file name, or a C++ enum class FileType {
null, // Default, automagick, etc.
tree, // <== DON't use this for reading; just writing
// Generally use one of these:
xml, XML = xml,
json, JSON = json,
hdf5, HDF5 = hdf5
};
or can be a direct string: HDF5 is not supported at this time! Just XML and JSON. You should seldom, if ever, need to provide the second argument. Absent the second argument, GNDStk determines the file type automatically, and we doubt that you’ll have any objections to that. If you do choose provide the second argument, then it, not GNDStk’s automatic file type determination, will be used, but you’ll see a warning if your directly-given value contradicts GNDStk’s automatic determination, which it still performs for diagnostic purposes. Of course, if you try to force reading in one format, and the file’s actual format is something else, you’ll soon be seeing a flood of errors, not mere warnings, as we attempt to read the file pursuant to the (incorrect) forced format. GNDStk uses the “file magic number,” not the file name, to determine file type
automatically. The file magic number really means the first byte, or bytes,
of the file. XML files always begin with a A nice thing about using the file magic number, not the file name, is that
it works for Read & Write XML¶Here’s a simple example in which we read our trusty example GNDS XML file, then write it back out to another XML file: #include "GNDStk.hpp"
using namespace njoy::GNDStk::core;
int main()
{
Tree pu239;
pu239.read("n-094_Pu_239.xml");
indent = 2; // <== not necessary; just for illustration
pu239.write("pu239");
}
You probably already guessed, correctly, that if We wrote earlier that GNDStk uses the file magic number, not the file name, to determine the file type when reading. When writing, there is no file magic number – the file to be written doesn’t exist, yet, or if it does, then its present contents are meaningless because the file is about to be replaced. When you call pu239.write("pu239.xml", "json");
then GNDStk will write file What if the file name extension isn’t given, or isn’t recognized, and a format
isn’t forced with a second argument? That is, what if we wrote, for example,
In the above code, what’s Finally, we note that More Reads & Writes¶We hope that GNDStk’s basic facilities for reading and writing GNDS files are clear enough at this point, but we’ll provide a few more examples nonetheless. A simple XML to JSON conversion can be done like this: #include "GNDStk.hpp"
using namespace njoy::GNDStk::core;
int main()
{
Tree pu239;
pu239.read("n-094_Pu_239.xml");
pu239.write("n-094_Pu_239.json");
}
Here’s a more compact version of the same thing: #include "GNDStk.hpp"
using namespace njoy::GNDStk::core;
int main()
{
Tree("n-094_Pu_239.xml").write("n-094_Pu_239.json");
}
Just as we can write JSON, we can read it, too. If we’ve produced the output
#include "GNDStk.hpp"
using namespace njoy::GNDStk::core;
int main()
{
Tree pu239("n-094_Pu_239.json");
}
Here, as you can see , we’ve returned to using a “read by constructor,” as in
our original XML example, instead of employing a Read, Write, Compare¶We’ll wrap up our set of read/write examples with a code that reads our favorite
GNDS XML file, writes it to a JSON file, independently reads the JSON back into
another #include "GNDStk.hpp"
using namespace njoy::GNDStk::core;
int main()
{
// Read from XML
Tree FromXML;
FromXML.read("n-094_Pu_239.xml");
// Write to JSON
FromXML.write("test.json");
// Read back from JSON
Tree FromJSON;
FromJSON.read("test.json");
// Compare
assert(FromXML == FromJSON);
}
Several remarks are in order here. The comparison operator for Interestingly enough, the above example’s It turns out that pugixml preserves the existing ordering of XML elements and
attributes when it reads a file, while nlohmann/json lexicographically sorts
the JSON name/value pairs by name. The latter library’s behavior could be
considered unfortunate if we’d rather see ordering preserved; while the
former library’s behavior could be considered unfortunate if we wish to fully
respect GNDS’ “no-ordering” rule and discourage the creation of code that might
inadvertently depend on data ordering. In any event, our What’s assert about? If you’re not familiar with This documentation uses GNDStk’s test suite, by the way, makes use of the CATCH library, described
online as a “test framework for unit-tests.” CATCH, on the one hand, has far
more macros and other testing machinery than we get with just Therefore, for your benefit throughout this manual, we’ll keep things
simple. We’ll provide complete, working, compile-able codes except where
it’s obvious that something is just a code fragment; and employ the simple
but clear Regarding JSON Files¶The specifications for Version 1.9 of the GNDS standard can be found here: which, at the time of this writing, is the latest available standard. Section 2.4 of the downloadable PDF document discusses limitations of some “meta-languages” (roughly speaking, file formats) such as JSON, in comparison with what XML is able to represent. Here are three points quoted directly from the document:
For (1), GNDStk does the first suggested action: it groups all of a node’s
attributes under a child node called For (2), GNDStk does exactly as illustrated: multiple elements of the same name
are suffixed with For (3), GNDStk does nothing in particular right now. Our understanding of GNDS
is that it’s designed so that elements – nodes – can appear in any order.
Here’s a small XML fragment taken directly from the <axes>
<axis index="1" label="energy_in" unit="eV"/>
<axis index="0" label="radius" unit="fm"/>
</axes>
Those The GNDS document then puts forth the following example XML fragment – slightly reformatted here for clarity, and with a proper XML declaration node (the first line) added for completeness: <?xml version="1.0" encoding="UTF-8"?>
<employees>
<employee>
<name first="Doc" last="Jones"/>
</employee>
<employee>
<name first="Grumpy" last="Smith"/>
</employee>
<employee>
<name first="Happy" last="Earp"/>
</employee>
</employees>
A viable JSON equivalent is then suggested. Calling the above XML file #include "GNDStk.hpp"
using namespace njoy::GNDStk::core;
int main()
{
Tree("employees.xml").write("employees.json");
}
and see what happens. Here’s exactly the output JSON file that the above code, applied to the sample XML, creates: {
"employees": {
"employee0": {
"name": {
"attributes": {
"first": "Doc",
"last": "Jones"
}
},
"nodeName": "employee"
},
"employee1": {
"name": {
"attributes": {
"first": "Grumpy",
"last": "Smith"
}
},
"nodeName": "employee"
},
"employee2": {
"name": {
"attributes": {
"first": "Happy",
"last": "Earp"
}
},
"nodeName": "employee"
}
}
}
This illustrates how GNDStk creates JSON files, consistent with the suggestions in the GNDS specification. If you try the above code, on the given input, you’ll see that GNDStk prints
two warnings. The same warning twice, actually: once during input, and once
during output. The warning tells us that Naturally, GNDStk reverses the modifications when we read from a JSON file
into our internal format. Specifically: values in an At this time, GNDStk provides no other options, such as the Data Structure “Direct”¶In this section, we’ll talk about some of the basic internal constructs of some
of GNDStk’s classes: more importantly, About Direct Access¶Right away, we strongly suggest that most users avoid direct access of member
data in these classes! An exception is the In most cases, we hope you’ll prefer to use GNDStk’s rich variety of powerful higher-level capabilities, in our “smart query system,” for pulling data from, or pushing data to, the metadata and child-node containers. (We’re talking right now about capabilities that are still in our core interface – not in our “high-level” interface that provides classes tailored to specific versions of the GNDS standard.) The basics of our “smart query system” are described in an upcoming section of this document. Our query system was designed precisely so that you’ll have something much more concise and powerful than you will by directly accessing the containers in question. And, most likely, also safer to use, insofar as working directly with the internals of data structures, especially those that were designed by other people, invariably runs some risks. C++ containers aren’t rocket science (and, besides, some GNDStk users may well be rocket scientists), so we do in fact provide public access to these structures, in the interest of supporting users who are comfortable and capable with the C++ language. Finally, we believe that if you understand the basic internal data format, then you may find the behavior of the higher-level capabilities, and our motivation for creating them, to be more clear. Tree vs. Node¶We’ve already seen Here’s a short sketch of our arrangement: class Node
{
using metaPair = std::pair<std::string,std::string>;
using childPtr = std::unique_ptr<Node>;
public:
std::string name;
std::vector<metaPair> metadata;
std::vector<childPtr> children;
// constructors, member functions, ...
};
class Tree : public Node
{
// a few additional and/or different member functions
};
The GNDS standard is essentially a tree structure, and this is reflected in
our classes, with Some readers may realize, correctly, that a typical tree structure’s top-level node could be treated in exactly the same way as all of its other nodes. One doesn’t generally need different data types for a tree’s root node and its other nodes, including leaves. Roughly speaking, tree nodes all “look the same,” with similar contents as well as relationships to their child nodes. That’s all true, and it could be described as the theoretical/mathematical view
of tree structures. From a practical/engineering standpoint, some utility can
sometimes be had in treating a top-level node differently from the others.
That’s the reasoning for As a derived class, One could also imagine extra functionality that a Content Preservation¶An important initial design decision that we made for our Consider, as a simple example, this small fragment of content from near the
beginning of our favorite <mass>
<double label="eval" value="1.00866491574" unit="amu"/>
</mass>
We could probably all agree that the label Instead of making wild assumptions, we’ll opt instead to preserve original content – that is, to respect precisely what exists in a GNDS file to begin with. To this end, all individual data, regardless of what they may appear to be
(string, floating-point, integer, single character, etc.), are stored as
strings. More precisely, as C++ <values>
2500 8.9172 2550 8.9155 2650 8.9139 ...
... 28500 8.4901 29500 8.4741 3e4 8.4659
</values>
are stored, in a No worries, though: our core interface, and especially the smart query system that we’ve spoken of, has plenty of functionality for serving its internal strings to you as floating-points, for instance; or for re-forming long strings, like the ones just described, into vectors of strings, or vectors of floating-points, or vectors of just about anything you may wish to create. When we speak of content preservation, then, we’re saying that an input text file – XML or JSON, for now – is factored into its underlying tree structure, but with its individual meaningful parts (neglecting, as usual, whitespace) still stored as text, with no modifications. A given user’s application code will almost certainly have its own internal
classes that contain GNDS data, or data computed from GNDS data, in ways that
work well for the user’s application. Someone may also have classes specifically
intended to mirror the content in various GNDS nodes, just in a different way.
(GNDStk’s own “high-level interface” will provide precisely such classes.) Such
classes can certainly make assumptions we didn’t want GNDStk to make – like,
for example, that we do want We’ll write more about the above considerations elsewhere. For now, let’s return to the main point of this chapter, and describe GNDStk’s two major classes that store GNDS data. Node¶We’ll write first about class Node
{
using metaPair = std::pair<std::string,std::string>;
using childPtr = std::unique_ptr<Node>;
public:
std::string name;
std::vector<metaPair> metadata;
std::vector<childPtr> children;
// constructors, member functions, ...
};
In short, inlining the // Node's data members
string name;
vector< pair<string,string> > metadata;
vector< unique_ptr<Node> > children;
The above evinces a simple tree structure that’s entirely sufficient for representing the contents of any GNDS node. Let’s provide a short but concrete example. Here’s some XML content from near
the top of the <evaluated label="eval" date="2017-12-01" library="ENDF/B" version="8.0.5">
<temperature value="0.0" unit="K"/>
<projectileEnergyDomain min="1e-05" max="20000000.0" unit="eV"/>
</evaluated>
Here, an outer At the risk of continuing a narrative of statements that are no doubt obvious,
here’s precisely how the above name: "evaluated"
metadata[0]: {"label", "eval"}
metadata[1]: {"date", "2017-12-01"}
metadata[2]: {"library", "ENDF/B"}
metadata[3]: {"version", "8.0.5"}
children[0]: pointer to another Node, with:
name: "temperature"
metadata[0]: {"value", "0.0"}
metadata[1]: {"unit", "K"}
children[1]: pointer to another Node, with:
name: "projectileEnergyDomain"
metadata[0]: {"min", "1e-05"}
metadata[1]: {"max", "20000000.0"}
metadata[2]: {"unit", "eV"}
Here, We use C++ Pointers about Pointers¶A couple of early users asked us about the motivation for using pointers, so we’ll briefly address, here, the concerns that they raised, in case other users wonder the same things. One person wondered why Without delving into a discussion that’s well beyond the scope of this
document, we’ll say only that implementing a Node’s Another user wanted to write code that copied some of A code shouldn’t attempt to take any actions that would break the ownership
rules Tree¶
Direct-Access Examples¶XML and JSON¶Smart Query System¶GNDS Creation¶node ctors tree ctors add()s Advanced Examples¶Largely continue query system discussion. Not sure about “advanced examples” characterization. |
|||||
CORE INTERFACE¶Motivation¶Query System, Part 1¶Operators¶Query Metadata¶Node.meta(string)¶1const string &meta ( const string &key ) const;
2 string &meta ( const string &key );
Node.meta(Meta)¶1const string &meta ( const Meta< void > &kwd ) const;
2 string &meta ( const Meta< void > &kwd );
3 TYPE meta ( const Meta< TYPE, CONVERTER> &kwd ) const;
4optional <TYPE> meta ( const Meta<optional <TYPE>, CONVERTER> &kwd ) const;
5Defaulted<TYPE> meta ( const Meta<Defaulted<TYPE>, CONVERTER> &kwd ) const;
6 TYPE meta ( const Meta<variant <Ts...>,CONVERTER> &kwd ) const;
Node(Meta)¶1decltype(auto) operator()( const Meta<TYPE,CONVERTER> &kwd ) const;
2decltype(auto) operator()( const Meta<TYPE,CONVERTER> &kwd );
Query Child Nodes¶Node.one(string)¶1const Node &one( const string &key, const FILTER &filter ) const;
2 Node &one( const string &key, const FILTER &filter );
3const Node &one( const string &key ) const;
4 Node &one( const string &key );
Node.many(string)¶1CONTAINER<Node> many( const string &key, const FILTER &filter ) const;
2CONTAINER<Node> many( const string &key ) const;
Node.child(Child)¶ 1 const Node &child( const Child<void, one, void, FILTER> &kwd ) const;
2 Node &child( const Child<void, one, void, FILTER> &kwd );
3CONTAINER<Node> child( const Child<void, many, void, FILTER> &kwd ) const;
4 TYPE child( const Child<TYPE, one, CONVERTER, FILTER> &kwd ) const;
5 optional<TYPE> child( const Child<optional <TYPE>, one, CONVERTER, FILTER> &kwd ) const;
6Defaulted<TYPE> child( const Child<Defaulted<TYPE>, one, CONVERTER, FILTER> &kwd ) const;
7 TYPE child( const Child<variant <Ts...>, one, CONVERTER, FILTER> &kwd ) const;
8CONTAINER<TYPE> child( const Child<TYPE, many, CONVERTER, FILTER> &kwd ) const;
9CONTAINER<TYPE> child( const Child<optional <TYPE>, many, CONVERTER, FILTER> &kwd ) const;
10CONTAINER<TYPE> child( const Child<Defaulted<TYPE>, many, CONVERTER, FILTER> &kwd ) const;
11CONTAINER<TYPE> child( const Child<variant <Ts...>, many, CONVERTER, FILTER> &kwd ) const;
Node(Child)¶1decltype(auto) operator()( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd ) const;
2decltype(auto) operator()( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd );
3decltype(auto) operator()( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd, const string label ) const;
4decltype(auto) operator()( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd, const string label );
5decltype(auto) operator()( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd, const regex labelRegex ) const;
6decltype(auto) operator()( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd, const regex labelRegex );
Query System, Part 2¶Sequence Queries¶Node.operator(…)¶ 1decltype(auto) operator()( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd, KEYWORDS &&...kwds ) const;
2decltype(auto) operator()( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd, KEYWORDS &&...kwds );
3decltype(auto) operator()( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd, const string label, KEYWORDS &&...kwds ) const;
4decltype(auto) operator()( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd, const string label, KEYWORDS &&...kwds );
5decltype(auto) operator()( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd, const char *const label, KEYWORDS &&...kwds ) const;
6decltype(auto) operator()( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd, const char *const label, KEYWORDS &&...kwds );
7decltype(auto) operator()( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd, const regex labelRegex, KEYWORDS &&...kwds ) const;
8decltype(auto) operator()( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd, const regex labelRegex, KEYWORDS &&...kwds );
9decltype(auto) operator()( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd, const pair<Child,label>, KEYWORDS &&...kwds ) const;
10decltype(auto) operator()( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd, const pair<Child,label>, KEYWORDS &&...kwds );
Multi-Queries¶Node.operator( | )¶1auto operator()( const KeywordTup<Ks...> &kwds ) const;
2auto operator()( const KeywordTup<Ks...> &kwds );
Conversion & Filters¶Creating Data¶Direct¶Using “Query” Objects¶Metadata¶Need a legend for things like this…. 1// string, value
2metaPair &add( const string &key, const T &val, const CONVERTER &converter = CONVERTER{} );
3metaPair &add( const string &key, const Defaulted<T> &def, const CONVERTER &converter = CONVERTER{} );
4
5// Meta<void>, value
6metaPair &add( const Meta<void> &kwd, const T &val = T{}, const CONVERTER &converter = CONVERTER{} );
7
8// Meta<TYPE>, value
9metaPair &add( const Meta<TYPE,CONVERTER> &kwd, const T &val = T{} );
10metaPair &add( const Meta<TYPE,CONVERTER> &kwd, const Defaulted<T> &def );
11
12// Meta<optional>, value
13metaPair &add( const Meta<optional<TYPE>,CONVERTER> &kwd, const T &val = T{} );
14bool add( const Meta<optional<TYPE>,CONVERTER> &kwd, const optional <T> &opt );
15bool add( const Meta<optional<TYPE>,CONVERTER> &kwd, const Defaulted<T> &def );
16
17// Meta<Defaulted>, value
18metaPair &add( const Meta<Defaulted<TYPE>,CONVERTER> &kwd, const T &val = T{} );
19bool add( const Meta<Defaulted<TYPE>,CONVERTER> &kwd, const optional <T> &opt );
20bool add( const Meta<Defaulted<TYPE>,CONVERTER> &kwd, const Defaulted<T> &def );
Children¶ 1// string
2Node &add( const string &name = "" );
3
4// value
5Node &add( const T &val );
6Node &add( const Defaulted<T> &def );
7
8// Child<void>, value
9Node &add( const Child<void,ALLOW,void,FILTER> &kwd, const T &val = T{} );
10
11// Child<TYPE>, value
12Node &add( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd, const T &val = T{} );
13Node &add( const Child<TYPE,ALLOW,CONVERTER,FILTER> &kwd, const Defaulted<T> &def );
14
15// Child<optional>, value
16Node &add( const Child<optional<TYPE>,ALLOW,CONVERTER,FILTER> &kwd, const T &val = T{} );
17bool add( const Child<optional<TYPE>,ALLOW,CONVERTER,FILTER> &kwd, const optional <T> &opt );
18bool add( const Child<optional<TYPE>,ALLOW,CONVERTER,FILTER> &kwd, const Defaulted<T> &def );
19
20// Child<Defaulted>, value
21Node &add( const Child<Defaulted<TYPE>,ALLOW,CONVERTER,FILTER> &kwd, const T &val = T{} );
22bool add( const Child<Defaulted<TYPE>,ALLOW,CONVERTER,FILTER> &kwd, const optional <T> &opt );
23bool add( const Child<Defaulted<TYPE>,ALLOW,CONVERTER,FILTER> &kwd, const Defaulted<T> &def );
24
25// Child<*> w/allow::many, container
26void add( const Child<TYPE,allow::many,CONVERTER,FILTER> &kwd, const CONTAINER<T,Args...> &container );
Conversion Scheme¶Advanced Topics¶ |
|||||
SEARCH¶ |
INDEX¶ |