.. _reading: Reading in the Data =================== `webnucleo `__ data files are in either in `XML `_ or `HDF5 `_ format. `wnutils` routines can read either format. In the following tutorials, you will enter Python commands. In your terminal, type python (or python3, or, perhaps, python2.x or python3.x, depending on your version). You will see something like:: Python 3.6.5 (default, Mar 29 2018, 15:38:28) [GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> The >>> is the interactive python prompt (it will typically show up in the tutorials in red). You type your commands at this prompt. To exit python, type:: >>> exit() and hit enter. XML --- The format of `webnucleo` XML files is described in the libnucnet technical report `XML Input to libnucnet`, available at the `libnucnet Home page `_. The class :obj:`wnutils.xml.Xml` has methods that read these XML files. To begin, import the namespace by typing:: >>> import wnutils.xml as wx To illustrate the use of `wnutils.xml` routines, use the files `my_output1.xml` and `my_output2.xml`, which you should have downloaded according to the :ref:`data ` tutorial. For each file, create an Xml object. For example, type:: >>> my_xml = wx.Xml('my_output1.xml') Read the nuclide data. ...................... You can retrieve the nuclide data in the webnucleo XML file by typing:: >>> nuclides = my_xml.get_nuclide_data() This returns a dictionary of data with the key being the nuclide name. You may print out all the data for a specific nuclide, say, o16, by typing:: >>> print(nuclides['o16']) Or, to get specific data, try typing:: >>> print('The mass excess in MeV of o16 is', nuclides['o16']['mass excess']) It is possible to use an XPath expression to select out only certain nuclides. For example, to get the data for nitrogen isotopes only, type:: >>> n = my_xml.get_nuclide_data(nuc_xpath='[z = 7]') To confirm that you only retrieved nitrogen data, type:: >>> for isotope in n: ... print(isotope, ':', 'Z =', n[isotope]['z'], 'A =', n[isotope]['a']) ... Partition function data for the nuclei are stored in two :obj:`numpy.array` objects. The first array, with key `t9`, gives the temperature points (in billions of k) at which the partition function is evaluated. The second array, with key `partf`, gives the partition function `G` evaluated at each of the temperature points. To see how this works, try printing out the partition function for one of the iron isotopes, say, fe56. Begin by extracting the data for the iron isotopes by typing:: >>> fe = my_xml.get_nuclide_data(nuc_xpath='[z = 26]') Then print out the partition function `G` as a function of `t9` by typing:: >>> sp = 'fe56' >>> for i in range(len(fe[sp]['t9'])): ... print('t9 = ', fe[sp]['t9'][i], 'G(t9) = ', fe[sp]['partf'][i]) ... Read the network limits. ........................ It is often useful to know the limits of the network that comprises the nuclei in the nuclear data collection. To get this information, type:: >>> lim = my_xml.get_network_limits() This returns a :obj:`dict` of :obj:`numpy.array` objects. The array retrieved with key `z` gives the atomic numbers. The array retrieved with key `n_min` gives the smallest neutron number present for the corresponding atomic number, while the array retrieved with key `n_max` gives the largest neutron number present for the corresponding atomic number. You can print out the retrieved data by typing:: >>> for z in range(len(lim['z'])): ... print('Z =', z, ': N_min =', lim['n_min'][z], ', N_max =', lim['n_max'][z]) ... You can retrieve a subnetwork with an XPath expression. For example, you can type:: >>> lim = my_xml.get_network_limits(nuc_xpath = '[z <= 5 or z >= 25]') Now print out the data:: >>> for i in range(len(lim['z'])): ... print('Z =', lim['z'][i], ': N_min =', lim['n_min'][i], ', N_max =', lim['n_max'][i]) ... Read the reaction data. ....................... You can retrieve the reaction data in the webnucleo XML file by typing:: >>> reactions = my_xml.get_reaction_data() This returns a dictionary with the key being the reaction string and each value being a :class:`.Reaction`. To see a list of the reactions, type:: >>> for r in reactions: ... print(r) ... You can use an XPath expression to select the reactions. For example, you can type:: >>> reactions = my_xml.get_reaction_data('[count(non_smoker_fit) = 1]') Since the reaction data include the reaction type, you can confirm your request by typing:: >>> for r in reactions: ... data = reactions[r].get_data() ... print(r, ': type is', data['type']) ... You may choose a particular reaction from the dictionary by typing, for example:: >>> reac = reactions['n + he4 + he4 -> be9 + gamma'] It is then possible to retrieve the `reactants`, `nuclide_reactants`, `products`, `nuclide_products`, the reaction string, and code giving the source by typing:: >>> print(reac.reactants) >>> print(reac.nuclide_reactants) >>> print(reac.products) >>> print(reac.nuclide_products) >>> print(reac.get_string()) >>> print(reac.source) You can also compute the rate for the reaction (among interacting multiplets and assuming one of the standard rate forms `single_rate`, `rate_table`, or `non_smoker_fit`) at a variety of temperatures by typing:: >>> import numpy as np >>> t9s = np.power(10., np.linspace(-2,1)) >>> for t9 in t9s: ... print(t9, reac.compute_rate(t9)) ... To compute the rate for user-defined rate functions, each defined with a `user_rate` `key`, first write a python routine for each rate function, then bind any data to that function (which must still take `t9` as an argument), and then create a dictionary of the functions associated with each `key`. Pass that dictionary into the `compute_rate` method with the keyword `user_funcs`. Read all properties in a zone. .............................. In a `webnucleo` XML file, a `zone` is a collection of the `mutable` quantities during a network calculation. For a single-zone network calculation, a zone is often a time step in the calculation. The zone will contain mass fractions of the network species at the time step to which the zone corresponds and properties, which can be any quantity, such as time, temperature, or density. The properties themselves have a `name` and up to two `tags`, called `tag1` and `tag2`. If the property only has a name, it can be retrieved by a :obj:`str`. If the property has tags, the identifier for the property is a :obj:`tuple` of up to three strings, namely, the `name`, `tag1`, and `tag2`. To retrieve all the properties of a given zone, say, the 10th zone, type:: >>> props = my_xml.get_all_properties_for_zone('[position() = 10]') Now you can print out the properties and their values in this zone by typing:: >>> for prop in props: ... print(str(prop).rjust(25), ':', props[prop]) ... Notice the conversion to :obj:`str` to print out the `('exposure', 'n')` tuple correctly. Read properties in all zones. ............................. You can retrieve selected properties in all zones. For the present example, you retrieve the `time`, `t9` (temperature in billions of Kelvins), and `rho` (mass density in g/cc) by typing:: >>> props = my_xml.get_properties( ['time','t9','rho'] ) The properties are returned in the dictionary `props`. Each dictionary element is a list of strings giving the properties in the zones. To see this, type:: >>> print(props['time']) This prints all the times. Print the first time entry by typing:: >>> print(props['time'][0]) To see the types, print:: >>> type(props) which shows that it is a hash (:obj:`dict`). Next, type:: >>> type(props['time']) which shows that each dictionary entry is a :obj:`list`. Next, type:: >>> type(props['time'][0]) which shows each list entry is a :obj:`str`. To retrieve properties with tags, you need to enter the appropriate tuple. For example, type:: >>> props = my_xml.get_properties(['time', ('exposure', 'n')]) To print out the exposures, type:: >>> for i in range(len(props[('exposure', 'n')])): ... print('time:', props['time'][i], 'exposure:', props[('exposure', 'n')][i]) ... Read properties of selected zones. .................................. You can select out the zones whose properties you wish to read by using an `XPath `_ expression. For example, you can retrieve the `time`, `t9`, and `rho` properties, as in the above example, but only for the last 10 zones. Type:: >>> props = my_xml.get_properties( ... ['time','t9','rho'], zone_xpath='[position() > last() - 10]' ... ) You can print the zone properties, for example, by typing:: >>> print(props['t9']) Confirm that there are only the properties for 10 zones by typing:: >>> print(len(props['t9'])) Read zone properties as floats. ............................... Properties are by default strings. When you wish to manipulate them (for example, to plot them), you want them to be :obj:`float` objects. You can retrieve them as floats by typing:: >>> props = my_xml.get_properties_as_floats( ['time','t9','rho'] ) The returned hash has entries that are :obj:`numpy.array`, which you confirm with:: >>> type(props['rho']) You can confirm that the array entries are floats by typing:: >>> type(props['rho'][0]) You can print out the entries by typing:: >>> for i in range(len(props['time'])): ... print( ... 'Zone = {0:d} time(s) = {1:.2e} t9 = {2:.2f} rho(g/cc) = {3:.2e}'.format( ... i, props['time'][i], props['t9'][i], props['rho'][i] ... ) ... ) ... This will output the time, temperature (in billions of K), and mass density (in g/cc) in all zones (time steps). Read mass fractions in zones. ............................. You can retrieve the mass fractions in zones. For example, to get the mass fractions of o16, si28, and s36, type:: >>> x = my_xml.get_mass_fractions(['o16','si28','s36']) The method returns a :obj:`dict` of :obj:`numpy.array`. Each array element is a :obj:`float`. You can print the mass fraction of silicon-28 in all zones by typing:: >>> print(x['si28']) The method also accepts the `zone_xpath` keyword to select specific zones. For example, to retrieve the mass fraction in the first 10 zones, type:: >>> x = my_xml.get_mass_fractions( ... ['o16','si28','s36'], zone_xpath='[position() <= 10]' ... ) Read all abundances in zones. ............................. You can retrieve abundances in the zones as a function of atomic and neutron number. The retrieved data are stored in a three-dimensional :obj:`numpy.array`. The first index gives the zone, the second gives the atomic number, and the third gives the neutron number. The array value is the abundance (per nucleon). Zones can be selected by XPath. To see how this works, retrieve the abundances in all zones by typing:: >>> abunds = my_xml.get_all_abundances_in_zones() Now print out the abundances in the 50th zone (remember the zero-indexing) by typing:: >>> for z in range(abunds.shape[1]): ... for n in range(abunds.shape[2]): ... print('Z =', z, ', N =', n, ', Y(Z,N) =', abunds[49,z,n]) ... You could do the same by typing:: >>> abunds = my_xml.get_all_abundances_in_zones(zone_xpath='[position() = 50]') >>> for z in range(abunds.shape[1]): ... for n in range(abunds.shape[2]): ... print('Z =', z, ', N =', n, ', Y(Z,N) =', abunds[0,z,n]) ... This is because the XPath selects only one zone, which will have index 0 in the retrieved data. Retrieve abundances summed over nucleon number in zones. ........................................................ It is often convenient to retrieve the abundances of the nuclei in a network file summed over proton number (`z`), neutron number (`n`), or mass number (`a`). To do so, type:: >>> y = my_xml.get_abundances_vs_nucleon_number() This returns a two-dimensional :obj:`numpy.array` in which the first index gives the zone and the second the mass number `a`. To print out the abundances versus mass number in the eighth zone, type:: >>> for i in range(y.shape[1]): ... print('A:', i, 'Y(A):', y[7,i]) ... To retrieve the abundances summed over atomic (proton) number (`z`), use the keyword `nucleon`:: >>> y = my_xml.get_abundances_vs_nucleon_number(nucleon='z') To retrieve the abundances in particular zones, for example, in the last 10 zones, use an XPath expression:: >>> y = my_xml.get_abundances_vs_nucleon_number(nucleon='n', zone_xpath='[position() > last() - 10]') Retrieve abundances for a chain of species. ........................................... To retrieve the abundances for a set of isotopes or isotones, use the method to get chain abundances. For example, to retrieve the isotopic abundances for `Z = 30` for all timesteps, type:: >>> n, y = my_xml.get_chain_abundances(('z', 30)) The method returns a :obj:`tuple` with the first element being an array of neutron numbers for the isotopes and the second element being a two dimensional :obj:`numpy.array` with the abundances for each step. To print the isotopic abundances in the final step, type:: >>> step = y.shape[0] - 1 >>> for i in range(y.shape[1]): ... print('N =', n[i], ', Y[N] =', y[step, i]) ... To return the isotonic abundances for `N = 25` in the first thirty timesteps, type:: >>> z, y = my_xml.get_chain_abundances(('n', 25), zone_xpath="[position() <= 30]") To return the same isotonic abundances, but as a function of the mass number, set the keyword variable `vs_A` to True:: >>> a, y = my_xml.get_chain_abundances(('n', 25), zone_xpath="[position() <= 30]", vs_A=True) To print these abundances in the thirtieth step, type:: >>> step = y.shape[0] - 1 >>> for i in range(y.shape[1]): ... print('A =', a[i], ', Y[A] =', y[step, i]) ... Multi_XML --------- The :obj:`wnutils.multi_xml.Multi_Xml` class allows you to access and plot data from multiple webnucleo XML files. First import the namespace by typing:: >>> import wnutils.multi_xml as mx Then create a class instance from a :obj:`list` of XML files. For this tutorial, type >>> my_multi_xml = mx.Multi_Xml(['my_output1.xml','my_output2.xml']) Methods allow you to access or plot data from the files. Read data from the individual XML instances. ............................................ To retrieve the individual XML instances from a Multi_Xml instance, type:: >>> xmls = my_multi_xml.get_xml() To retrieve the original file names, type:: >>> files = my_multi_xml.get_files() Of course the number of XML instances must equal the number of files. To confirm, type:: >>> print(len(xmls) == len(files)) Use the methods on the individual instances. For example, type:: >>> for i in range(len(xmls)): ... props = xmls[i].get_properties(['time']) ... print(files[i],'has',len(props['time']),'zones.') ... H5 ---- Methods that read webnucleo HDF5 files are in the namespace `wnutils.h5`. The class that contains these methods is :obj:`wnutils.h5.H5`. Begin by importing the namespace by typing:: >>> import wnutils.h5 as w5 Then create an object for your file `my_output1.h5` (which you already downloaded according to the instructions in the :ref:`data ` tutorial) by typing:: >>> my_h5 = w5.H5('my_output1.h5') Read the nuclide data. ...................... The nuclide data are in a group of their own in the file. To retrieve the data (as a :obj:`dict` of :obj:`dict` with the nuclide names as the top-level dictionary keys), type:: >>> nuclides = my_h5.get_nuclide_data() Print out the data for, say, o16, by typing:: >>> print(nuclides['o16']) Print out the mass excess and spin for all species by typing:: >>> for nuclide in nuclides: ... print(nuclide, nuclides[nuclide]['mass excess'], nuclides[nuclide]['spin']) ... Read the names of the iterable groups. ....................................... Iterable groups are the groups in the HDF5 file that typically represent timesteps (that is, the groups that are not the nuclide data group). To retrieve their names (as a :obj:`list` of :obj:`str`), type:: >>> groups = my_h5.get_iterable_groups() Print them out by typing:: >>> for group in groups: ... print(group) ... Read the zone labels for a group. ................................. In a webnucleo HDF5 file, a zone is contained in a group and typically represents a spatial region. Zones are specified by three labels, which we denote by a :obj:`tuple`. To retrieve and print out the labels for a given group, say, `Step 00010`, type:: >>> labels = my_h5.get_zone_labels_for_group('Step 00010') >>> for i in range(len(labels)): ... print('Zone',i,'has label',labels[i]) ... Read all properties in a zone for a group. .......................................... To retrieve all the properties from a zone in a group, type, for example:: >>> zone = ('2','0','0') >>> props = my_h5.get_group_zone_properties('Step 00010', zone) You can print those properties out by typing:: >>> for prop in props: ... print(str(prop).rjust(25), ':', props[prop]) ... Read properties in all zones for a group. ......................................... It is possible to retrieve the properties in all zones for a group as as :obj:`dict` of :obj:`list`. Each list entry is a :obj:`str`. For example, to retrieve and print the properties `time`, `t9`, and `rho` in all zones for a given group, say, `Step 00024`, type:: >>> p = ['time','t9','rho'] >>> props = my_h5.get_group_properties_in_zones('Step 00024',p) >>> labels = my_h5.get_zone_labels_for_group('Step 00024') >>> for i in range(len(labels)): ... print('In',labels[i],'time=',props['time'][i],'t9=',props['t9'][i],'rho=',props['rho'][i]) ... Read properties in all zones for a group as floats. ................................................... It is often desirable to retrieve the properties in zones for a group as floats. For example, one may again retrieve `time`, `t9`, and `rho` from `Step 00024` but, this time, as floats by typing:: >>> p = ['time','t9','rho'] >>> props = my_h5.get_group_properties_in_zones_as_floats('Step 00024',p) >>> type(props['time']) >>> type(props['time'][0]) Read mass fractions in all zones for a group. ............................................. You can read all the mass fractions in all the zones for a given group. For a group `Step 00021`, type:: >>> x = my_h5.get_group_mass_fractions('Step 00021') The array x is a 2d HDF5 :obj:`h5py:Dataset`. The first index gives the zone and the second the species. To print out the mass fraction of ne20 in all the zones, type:: >>> i_ne20 = (my_h5.get_nuclide_data())['ne20']['index'] >>> labels = my_h5.get_zone_labels_for_group('Step 00021') >>> for i in range(x.shape[0]): ... print('Zone',labels[i],'has X(ne20) =',x[i,i_ne20]) ... Read properties of a zone in the groups. ........................................ It is possible to retrieve properties from a given zone in all groups. To retrieve the properties `time`, `t9`, and `rho` in all groups for the zone with labels `1`, `0`, `0`, type:: >>> zone = ('1','0','0') >>> props = my_h5.get_zone_properties_in_groups(zone, ['time','t9','rho']) This returns a :obj:`dict` of :obj:`list` of :obj:`str`. To print the properties out in the groups, type:: >>> groups = my_h5.get_iterable_groups() >>> for i in range(len(groups)): ... print( ... groups[i], ': ', props['time'][i], props['t9'][i], props['rho'][i] ... ) ... Read properties of a zone in the groups as floats. .................................................. One often wants the properties of a zone in the groups as floats. To retrieve the properties `time`, `t9`, and `rho` in all group for the zone with labels `1`, `0`, `0`, type:: >>> zone = ('1','0','0') >>> props = my_h5.get_zone_properties_in_groups_as_floats(zone, ['time','t9','rho']) This returns a :obj:`dict` of :obj:`numpy.array`. Each array entry is a :obj:`float`. To print the properties out in the groups, type:: >>> groups = my_h5.get_iterable_groups() >>> for i in range(len(groups)): ... print( ... '{0:s}: time(s) = {1:.2e} t9 = {2:.2f} rho(g/cc) = {3:.2e}'.format( ... groups[i], props['time'][i], props['t9'][i], props['rho'][i] ... ) ... ) ... Read mass fractions in a zone in the groups. ............................................ You can retrieve the mass fractions of specific species for a given zone in all the iterable groups. For example, to retrieve `o16`, `o17`, and `o18` in the zone with labels `1`, `0`, `0`, type:: >>> species = ['o16','o17','o18'] >>> zone = ('1','0','0') >>> x = my_h5.get_zone_mass_fractions_in_groups( zone, species ) To print them out, you can now type:: >>> groups = my_h5.get_iterable_groups() >>> for i in range(len(groups)): ... print(groups[i],':','X(o16)=',x['o16'][i],'X(o17)=',x['o17'][i],'X(o18)=',x['o18'][i]) ... Multi_H5 -------- The :obj:`wnutils.multi_h5.Multi_H5` class allows you to access and plot data from multiple webnucleo HDF5 files. First import the namespace by typing:: >>> import wnutils.multi_h5 as m5 Then create a class instance from a :obj:`list` of HDF5 files. For this tutorial, type >>> my_multi_h5 = m5.Multi_H5(['my_output1.h5','my_output2.h5']) Methods allow you to access or plot data from the files. Read data from the individual HDF5 instances. ............................................. To retrieve the individual HDF5 instances from a Multi_H5 instance, type:: >>> h5s = my_multi_h5.get_h5() To retrieve the original file names, type:: >>> files = my_multi_h5.get_files() Of course the number of HDF5 instances must equal the number of files. To confirm, type:: >>> print(len(h5s) == len(files)) Use the methods on the individual instances. For example, type:: >>> for i in range(len(h5s)): ... props = h5s[i].get_zone_properties_in_groups(('0','0','0'), ['time']) ... print(files[i],'has',len(props['time']),'groups.') ...