GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval"...
Transcript of GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval"...
Managed by Triad National Security, LLC for the U.S. Department of Energy’s NNSA
GNDS API Development at LANL
W. Haeck, J. L. Conlin,
A. P. McCarney
WPEC SG43, June 24-28, 2019
6/25/2019 | 2Los Alamos National Laboratory
June 24-28, 2019
WPEC SG43, Paris, France
• Introduction
• The values node
• The XYs1D node
• The Multiplicity node
• Conclusions and perspectives
A few initial remarks before we begin:
• GNDS is a structure and should not be closely linked to a format type
• Data read from an XML file or from a JSON file should be the same
• This has an impact on some of the rules of the API
Introduction
6/25/2019 | 3Los Alamos National Laboratory
The following examples for a values node are equivalent
<values start="0" size="4" valueType="Float64">
1.00000000e-5 1.0 2.00000000e+7 1.0
</values>
<values>
1.00000000e-5 1.0 2.00000000e+7 1.0
</values>
The start, size and valueType are optional and do not need to be specified
In JSON, this might look like this
{ "start" : 0, "size" : 4, "valueType" : "Float64",
"values" : [ 1.00000000e-5, 1.0, 2.00000000e+7, 1.0 ] }
A first example: the values node
6/25/2019 | 4Los Alamos National Laboratory
Retrieving information from the values node
# values is an instance of the values node
start = values.start() # 0 – either the given value or the default (0)
size = values.size() # 4 – either the given value or the number of values
vtype = values.valueType() # "Float64" – either the given value or the default
values = values.values() # this returns a range of values
Note:
• There is no distinction to what is an attribute, content or child nodes from the XML example.
• This interface is the same for both examples
A first example: the values node
6/25/2019 | 5Los Alamos National Laboratory
Constructing a standalone values node using a fluent builder
# we use a builder object with a fluent interface instead of a specific constructor
values = ValuesBuilder()
# the size is set
.size( 4 )
# the starting position is set
.start( 0 )
# the type is set
.valueType( "Float64" )
# the values are set
.values( [ 1e-5, 1.0, 2e+7, 1.0 ] )
# the values node is constructed and returned
.construct()
Note:
• The code can be read as if it was an actual GNDS instance
• The function names for the data extraction API are used for clarity
A first example: the values node
6/25/2019 | 6Los Alamos National Laboratory
Constructing a values node inside another builder
# a parent builder (e.g. for an XYs1D) calls a builder for a values node that it needs
parent.values()
# the size is set
.size( 4 )
# the starting position is set
.start( 0 )
# the type is set
.valueType( "Float64" )
# the values are set
.values( [ 1e-5, 1.0, 2e+7, 1.0 ] )
# the values node is constructed and added to the parent builder
.add()
# these are equivalent to the examples on the previous slide and the one above
values = ValuesBuilder().values( [ 1e-5, 1.0, 2e+7, 1.0 ] ).construct()
parent.values().values( [ 1e-5, 1.0, 2e+7, 1.0 ] ).add()
A first example: the values node
6/25/2019 | 7Los Alamos National Laboratory
Note:
• Input data is tested during the assembly process
• Consistency is tested once the instance is created (by calling add() or construct())
The data extraction API and the builder API can be reused for higher level nodes
A first example: the values node
6/25/2019 | 8Los Alamos National Laboratory
Let’s take the following XYs1D as an example
<XYs1d label="eval">
<axes>
<axis index="0" label="multiplicity" unit=""/>
<axis index="1" label="energy_in" unit="eV"/>
</axes>
<values>
1.00000000e-05 1.0 2.00000000e+07 1.0
</values>
</XYs1d>
There are already three lower level nodes in here: values, axis and axes
A slightly more complicated example: the XYs1D node
6/25/2019 | 9Los Alamos National Laboratory
Retrieving information from the XSs1D node
# xy is an instance of an XYs1D node
label = xy.label() # "eval"
interpolation = xy.interpolation() # "lin-lin" - the default value
values = xy.values() # this returns a values node
# the API of the values node can then be used through chaining
start = xy.values().start() # or values.start()
size = xy.values().size()
vtype = xy.values().valueType()
data = xy.values().values()
A slightly more complicated example: the XYs1D node
6/25/2019 | 10Los Alamos National Laboratory
Retrieving information from the XYs1D node (continued)
# the API of the values node can also be used on the values instance directly
start = values.start()
size = values.size()
vtype = values.valueType()
data = values.values()
# a similar API can be proposed for the axes node and the underlying axis nodes
axes = xy.axes() # this returns an axes node
axis0 = xy.axes().axis( 0 ) # this returns the axis with the index equal to 0
axis1 = xy.axes().axis( 1 )
label0 = xy.axes().axis( 0 ).label() # the label of axis 0, "multiplicity"
unit0 = xy.axes().axis( 0 ).unit() # the unit of axis 0, ""
index0 = xy.axes().axis( 0 ).index() # the index of axis 0, 0
label1 = xy.axes().axis( 1 ).label() # the label of axis 1, "energy_in"
unit1 = xy.axes().axis( 1 ).unit(). # the unit of axis 1, "eV"
index1 = xy.axes().axis( 1 ).index() # the index of axis 1, 1
A slightly more complicated example: the XYs1D node
6/25/2019 | 11Los Alamos National Laboratory
Constructing an XYs1D node inside another builder
# the parent calls a builder for an XYs1D node
parent.XYs1D() # the parent calls a builder for an XYs1D container
# the label is set on the XYs1D builder
.label( "eval" )
# the XYs1D builder calls an axes builder
.axes()
# call an axis builder, set index, label and unit, add to axes
.axis().index( 0 ).label( "multiplicity" ).add() # multiplicity has no unit
.axis().index( 1 ).label( "energy_in" ).unit( "eV" ).add()
# add the axes node to the XYs1D builder
.add()
# the XYs1D builder calls a values builder and adds the values node
.values().values( [ 1e-5, 1.0, 2e+7, 1.0 ] ).add() # no attributes on the values node
# the XYs1D instance is constructed and added to the parent
.add()
A slightly more complicated example: the XYs1D node
6/25/2019 | 12Los Alamos National Laboratory
A more verbose alternative allowed by the API
# construct the two axis, the axes node and values node
axis0 = AxisBuilder().index( 0 ).label( "crossSection" ).unit( "b" ).construct()
axis1 = AxisBuilder().index( 1 ).label( "energy_in" ).unit( "eV" ).construct()
axes = AxesBuilder().axis( axis0 ).axis( axis1 ).construct()
values = ValuesBuilder().values( [ 1e-5, 2.043608e+1, 2e+7, 4.818408e-1] ).construct()
# construct the XYs1D instance
xy = XYs1DBuilder().label( "eval" ).axis( axes ).values( values ).construct()
# you can also do it this way
xy = XYs1DBuilder().label( "eval" )
.axis(AxesBuilder()
.axis( AxisBuilder().index( 0 ).label( "crossSection" ).unit( "b" ).construct() )
.axis( AxisBuilder().index( 1 ).label( "energy_in" ).unit( "eV" ).construct() )
.construct() )
.values( values ) # we can do the same thing here but I had no more room
.construct()
A slightly more complicated example: the XYs1D node
6/25/2019 | 13Los Alamos National Laboratory
Let’s take the following Multiplicity as an example
<multiplicity>
<XYs1d label="eval">
<axes>
<axis index="0" label="multiplicity" unit=""/>
<axis index="1" label="energy_in" unit="eV"/>
</axes>
<values>
1.00000000e-05 1.0 2.00000000e+07 1.0
</values>
</XYs1d>
</multiplicity>
This node uses an XYs1D node to store the data but there are other possibilities
A node with “context”: the Multiplicity node
6/25/2019 | 14Los Alamos National Laboratory
And than we have Multiplicity as a constant value
<multiplicity>
<constant1d label="eval" constant="1.0" domainMin="1e-5" domainMax="2e+7">
<axes>
<axis index="0" label="multiplicity" unit=""/>
<axis index="1" label="energy_in" unit="eV"/>
</axes>
</constant1d>
</multiplicity>
A node with “context”: the Multiplicity node
6/25/2019 | 15Los Alamos National Laboratory
Constructing a Multiplicity node
# the parent calls a builder for a multiplicity node
parent.multiplicity()
# the multiplicity builder calls an XYs1D builder
.XYs1D().label( "eval" )
.axes().axis().index( 0 ).label( "multiplicity" ).add()
.axis().index( 1 ).label( "energy_in" ).unit( "eV" ).add().add()
.values().values( [ 1e-5, 1.0, ... , 2e+7, 1.0 ] ).add()
.add()
# the multiplicity instance is constructed and added to the parent
.add()
This example uses an XYs1D node explicitly, which requires intimate knowledge of the
GNDS format. We can also provide more “abstract” builders.
A node with “context”: the Multiplicity node
6/25/2019 | 16Los Alamos National Laboratory
Constructing a Multiplicity node (continued)
# the parent calls a builder for an multiplicity node
parent.multiplicity()
# give a tabulated function of multiplicities
.tabulated()
# set the label
.label( "eval" )
# set the energy and multiplicity values
.energies( [ 1e-5, ..., 2e+7,] ).multiplicities( [ 1.0, ..., 1.0 ] )
# set the unit of the energy values
.energyUnit( "eV" )
# add the multiplicity node to the parent
.add() # this will convert the abstracted data into an actual XYs1D
Note:
• The API allows a user to just provide the information he/she is concerned about
• The energy unit can also have a default value associated to it so that it could be omitted.
A node with “context”: the Multiplicity node
6/25/2019 | 17Los Alamos National Laboratory
Constructing a Multiplicity node but with a constant1D node
# the parent calls a builder for an multiplicity node
parent.multiplicity() # the parent calls a builder for an multiplicity node
# the multiplicity builder calls a constant1D builder
.constant1D().label( "eval" ).constant( 1.0 ).domainMin( 1e-5 ).domainMax( 2e+7 )
.axes().axis().index( 0 ).label( "multiplicity" ).add()
.axis().index( 1 ).label( "energy_in" ).unit( "eV" ).add()
.add()
# the multiplicity instance is constructed and added to the parent
.add()
# the more abstract alternative
parent.multiplicity()
.constant()
.label( "eval" )
.lowerEnergy( 1e-5 ).upperEnergy( 2e+7 ).energyUnit( "eV" )
.multiplicity( 1.0 )
.add()
A node with “context”: the Multiplicity node
6/25/2019 | 18Los Alamos National Laboratory
What if I just want to copy existing data you ask?
# retrieve/construct the Multiplicity node
myMultiplicity = myReaction.multiplicity()
# the parent does not call a builder for an multiplicity node but sets it directly
parent.multiplicity( myMultiplicity )
Note:
• Remember the example in the XYs1D node where we do something similar
A node with “context”: the Multiplicity node
6/25/2019 | 19Los Alamos National Laboratory
Error handling when constructing a Multiplicity node
parent.multiplicity() # the parent calls a builder for a multiplicity node
# the multiplicity builder calls an XYs1D builder
.XYs1D().label( "eval" )
.axes().axis().index( 0 ).label( "multiplicity" ).add()
.axis().index( 1 ).label( "energy_in" ).unit( "eV" ).add().add()
.values().values( [ 1e-5, 1.0, ... , 2e+7, 1.0 ] ).add()
.add()
# the multiplicity builder calls a constant1D builder
.constant1D().label( "eval" ).constant( 1.0 ).domainMin( 1e-5 ).domainMax( 2e+7 )
.axes().axis().index( 0 ).label( "multiplicity" ).add()
.axis().index( 1 ).label( "energy_in" ).unit( "eV" ).add()
.add()
# the builder tries to construct the multiplicity instance
.add() # FAIL: received both an XYs1D and a constant1D node with the same label
This code fails when calling the add() function because that’s where we verify
consistency.
A node with “context”: the Multiplicity node
6/25/2019 | 20Los Alamos National Laboratory
This is also the case for the abstract builder
# the parent calls a builder for a multiplicity node
parent.multiplicity()
.tabulated()
.label( "eval" )
.energies( [ 1e-5, ..., 2e+7,] ).multiplicities( [ 1.0, ..., 1.0 ] )
.energyUnit( "eV" )
.add()
.constant()
.label( "eval" )
.lowerEnergy( 1e-5 ).upperEnergy( 2e+7 ).energyUnit( "eV" )
.multiplicity( 1.0 )
.add() # FAIL: received both a table and a constant with the same label
Again, this code fails when calling the add() function because that’s where we verify
consistency.
A node with “context”: the Multiplicity node
6/25/2019 | 21Los Alamos National Laboratory
However, the following is allowed
# the parent calls a builder for an multiplicity node
parent.multiplicity()
.tabulated()
.label( "eval" )
.energies( [ 1e-5, ..., 2e+7,] ).multiplicities( [ 1.0, ..., 1.0 ] )
.energyUnit( "eV" )
.add()
.constant()
.label( "processed" )
.lowerEnergy( 1e-5 ).upperEnergy( 2e+7 ).energyUnit( "eV" )
.multiplicity( 1.0 )
.add() # SUCCESS: received both a table and a constant but with different labels
A node with “context”: the Multiplicity node
6/25/2019 | 22Los Alamos National Laboratory
Developing an API for a new format takes time
• We prefer a format that has stabilised as much as possible ...
• Still a few details to sort out in the interface
We are currently working on an API document
• A first draft is already publicly available
• We continue to modify this document to trace ideas and conceptual changes
We are thinking about doing a proof of concept
• Most likely involving the resonance data
• Hopefully by the next WPEC meeting
Conclusions and perspectives
6/25/2019 | 23Los Alamos National Laboratory