GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval"...

23
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

Transcript of GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval"...

Page 1: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 2: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 3: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 4: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 5: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 6: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 7: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 8: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 9: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 10: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 11: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 12: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 13: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 14: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 15: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 16: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 17: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 18: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 19: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 20: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 21: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 22: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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

Page 23: GNDS API Development at LANL · # xy is an instance of an XYs1D node label = xy.label() # "eval" interpolation = xy.interpolation() # "lin-lin" - the default value values = xy.values()

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