diff --git a/docs/source/io_formats/mgxs_library.rst b/docs/source/io_formats/mgxs_library.rst index ccd68c184..4f6233aaf 100644 --- a/docs/source/io_formats/mgxs_library.rst +++ b/docs/source/io_formats/mgxs_library.rst @@ -56,17 +56,18 @@ data for the nuclide or material that it represents. that the azimuthal angular domain is subdivided if the `representation` attribute is "angle". This parameter is ignored otherwise. - - **num-polar** (*int*) -- Number of equal width angular bins + - **num-polar** (*int*) -- Number of equal width angular bins that the polar angular domain is subdivided if the `representation` attribute is "angle". This parameter is ignored otherwise. - - **scatter-type** (*char[]*) -- The representation of the - scattering angular distribution. The options are either - "legendre", "histogram", or "tabular". If not provided, the - default of "legendre" will be assumed. - - **order** (*int*) -- Either the Legendre order, number of bins, - or number of points (depending on the value of `scatter-type`) - used to describe the angular distribution associated with each group-to-group transfer probability. + - **scatter-type** (*char[]*) -- The representation of the + scattering angular distribution. The options are either + "legendre", "histogram", or "tabular". If not provided, the + default of "legendre" will be assumed. + - **order** (*int*) -- Either the Legendre order, number of bins, + or number of points (depending on the value of `scatter-type`) + used to describe the angular distribution associated with each + group-to-group transfer probability. **//kTs/** @@ -86,30 +87,6 @@ Temperature-dependent data, provided for temperature K. This is a 1-D vector if `representation` is "isotropic", or a 3-D vector if `representation` is "angle" with dimensions of [groups, azimuthal, polar]. - - **scatter matrix** (*double[][][]* or *double[][][][][]*) -- - Scattering moment matrices. This dataset requires the scattering - moment matrices presented with the columns representing incoming - group and rows representing the outgoing group. That is, - down-scatter will be above the diagonal of the resultant matrix. - This matrix is repeated for every Legendre order (in order of - increasing orders) if `scatter-type` is "legendre"; otherwise, this - matrix is repeated for every bin of the histogram or tabular - representation. Finally, if ``representation`` is "angle", the - above is repeated for every azimuthal angle and every polar angle, - in that order. - - **multiplicity matrix** (*double[][]* or *double[][][][]*) -- - Scattering multiplicity matrices. - This dataset provides the ratio of neutrons produced in scattering - collisions to the neutrons which undergo scattering collisions; - that is, the multiplicity provides the code with a scaling factor - to account for neutrons being produced in (n,xn) reactions. This - information is assumed isotropic and therefore is not repeated for - every Legendre moment or histogram/tabular bin. This matrix - follows the same arrangement as described for the `scatter` - dataset, with the exception of the data needed to provide the - scattering type information. - This dataset is optional, if it is not provided no multiplication - (i.e., values of 1.0) will be assumed. - **fission** (*double[]* or *double[][][]*) -- Fission cross section. This is a 1-D vector if `representation` is "isotropic", or a 3-D @@ -135,4 +112,46 @@ Temperature-dependent data, provided for temperature K. the `nu-fission` data must represent the fission neutron energy spectra as well and thus will have one additional dimension for the outgoing energy group. In this case, `nu-fission` has the - same dimensionality as `multiplicity matrix`. \ No newline at end of file + same dimensionality as `multiplicity matrix`. + +**//K/scatter data/** + +Data specific to neutron scattering for the temperature K + +:Datasets: - **g_out bounds** (*int[2]* or *int[][][2]) -- + Minimum (most energetic) and maximum (most thermal) outgoing groups + with non-zero values of the scattering matrix. These group numbers + use the standard ordering where the fastest neutron energy group + is group 1 while the most thermal neutron energy group is group G. + The dimensionality of `g_out bounds` is: + `g_out bounds][g_in][g_out]`, or + `g_out bounds[num-polar][num-azimuthal][g_in][g_out]`. + The former is used when `representation` is "isotropic", and the + latter when `representation` is "angle". + - **scatter matrix** (*double[][]* or *double[][][][]*) -- + Flattened representation of the scattering moment matrices where + the [g_in] and [g_out] indices are flattened. The pre-flattened + array is shaped as follows (in row-major format): + `scatter matrix[order(+1)][g_in][g_out]`, or + `scatter matrix[num-polar][num-azimuthal][order(+1)][g_in][g_out]` + The former is used when `representation` is "isotropic", and the + latter when `representation` is "angle". Note that if the value of + `scatter-type` is "legendre", the order dimension will be one + larger than the value of `order`, otherwise it will match `order`. + Finally, the g_out dimension has a dimensionality of + `g_out bounds`[0] to `g_out bounds`[1]. + - **multiplicity matrix** (*double[]*) -- Flattened representation of + the scattering moment matrices where the [g_in] and [g_out] indices + are flattened. This dataset provides the code with a scaling factor + to account for neutrons being produced in (n,xn) reactions. This + is assumed isotropic and therefore is not repeated for every + Legendre moment or histogram/tabular bin. This dataset is optional, + if it is not provided no multiplication (i.e., values of 1.0) will + be assumed. + The pre-flattened array is shaped as follows (in row-major format): + `multiplicity matrix[g_in][g_out]`, or + `multiplicity matrix[num-polar][num-azimuthal][g_in][g_out]` + The former is used when `representation` is "isotropic", and the + latter when `representation` is "angle". Finally, the g_out + dimension has a dimensionality of `g_out bounds`[0] to + `g_out bounds`[1]. \ No newline at end of file diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index ad60a5779..687e2d693 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -1137,14 +1137,15 @@ class XSdata(object): if self.fissionable is not None: grp.attrs['fissionable'] = self.fissionable if self.representation is not None: - grp.attrs['representation'] = np.string_(self.representation) + grp.attrs['representation'] = np.array(self.representation, + dtype='S') if self.representation == 'angle': if self.num_azimuthal is not None: grp.attrs['num-azimuthal'] = self.num_azimuthal if self.num_polar is not None: grp.attrs['num-polar'] = self.num_polar if self.scatter_type is not None: - grp.attrs['scatter-type'] = np.string_(self.scatter_type) + grp.attrs['scatter-type'] = np.array(self.scatter_type, dtype='S') if self.order is not None: grp.attrs['order'] = self.order @@ -1157,20 +1158,16 @@ class XSdata(object): # Create the temperature datasets for i, temperature in enumerate(self.temperatures): xsgrp = grp.create_group(str(int(np.round(temperature))) + "K") - if self._total[i] is not None: - xsgrp.create_dataset("total", data=self._total[i], - compression=compression) - if self._absorption[i] is not None: - xsgrp.create_dataset("absorption", data=self._absorption[i], - compression=compression) - if self._scatter_matrix[i] is not None: - xsgrp.create_dataset("scatter matrix", - data=self._scatter_matrix[i], - compression=compression) - if self._multiplicity_matrix[i] is not None: - xsgrp.create_dataset("multiplicity matrix", - data=self._multiplicity_matrix[i], - compression=compression) + if self._total[i] is None: + raise ValueError('total data must be provided when writing ' + 'the HDF5 library') + xsgrp.create_dataset("total", data=self._total[i], + compression=compression) + if self._absorption[i] is None: + raise ValueError('absorption data must be provided when ' + 'writing the HDF5 library') + xsgrp.create_dataset("absorption", data=self._absorption[i], + compression=compression) if self.fissionable: if self._fission[i] is not None: xsgrp.create_dataset("fission", data=self._fission[i], @@ -1182,9 +1179,103 @@ class XSdata(object): if self._chi[i] is not None: xsgrp.create_dataset("chi", data=self._chi[i], compression=compression) - if self._nu_fission[i] is not None: - xsgrp.create_dataset("nu-fission", - data=self._nu_fission[i], + if self._nu_fission[i] is None: + raise ValueError('nu-fission data must be provided when ' + 'writing the HDF5 library') + xsgrp.create_dataset("nu-fission", + data=self._nu_fission[i], + compression=compression) + + if self._scatter_matrix[i] is None: + raise ValueError('Scatter matrix must be provided when ' + 'writing the HDF5 library') + # Get the sparse scattering data to print to the library + G = self.energy_groups.num_groups + if self.representation is 'isotropic': + g_out_bounds = np.zeros((G, 2), dtype=np.int) + for g_in in range(G): + nz = np.nonzero(self._scatter_matrix[i][0, g_in, :]) + g_out_bounds[g_in, 0] = nz[0] + g_out_bounds[g_in, 1] = nz[-1] + # Now create the flattened scatter matrix array + flat_scatt = [] + for l in range(self._scatter_matrix[i].shape[0]): + for g_in in range(G): + matrix = self._scatter_matrix[i][l, g_in, :] + for g_out in range(g_out_bounds[g_in, 0], + g_out_bounds[g_in, 1] + 1): + flat_scatt.append(matrix[g_out]) + # And write it. + scatt_grp = xsgrp.create_group('scatter data') + scatt_grp.create_dataset("scatter matrix", + data=np.array(flat_scatt), + compression=compression) + # Repeat for multiplicity + if self._multiplicity_matrix[i] is not None: + # Now create the flattened scatter matrix array + flat_mult = [] + for g_in in range(G): + matrix = self._multiplicity_matrix[i][g_in, :] + for g_out in range(g_out_bounds[g_in, 0], + g_out_bounds[g_in, 1] + 1): + flat_mult.append(matrix[g_out]) + scatt_grp.create_dataset("multiplicity matrix", + data=np.array(flat_mult), + compression=compression) + # And finally, adjust g_out_bounds for 1-based group counting + # and write it. + g_out_bounds[:, :] += 1 + scatt_grp.create_dataset("g_out bounds", data=g_out_bounds, + compression=compression) + + else: + Np = self.num_polar + Na = self.num_azimuthal + g_out_bounds = np.zeros((Np, Na, G, 2), dtype=np.int) + for p in range(Np): + for a in range(Na): + for g_in in range(G): + matrix = self._scatter_matrix[i][p, a, 0, g_in, :] + nz = np.nonzero(matrix) + g_out_bounds[p, a, g_in, 0] = nz[0] + g_out_bounds[p, a, g_in, 1] = nz[-1] + # Now create the flattened scatter matrix array + flat_scatt = [[[] for a in range(Na)] for p in range(Np)] + for p in range(Np): + for a in range(Na): + for l in range(self._scatter_matrix[i].shape[2]): + for g_in in range(G): + matrix = \ + self._scatter_matrix[i][p, a, 0, g_in, :] + for g_out in range(g_out_bounds[p, a, g_in, 0], + g_out_bounds[p, a, g_in, 1] + + 1): + flat_scatt[p][a].append(matrix[g_out]) + # And write it. + scatt_grp = xsgrp.create_group('scatter data') + scatt_grp.create_dataset("scatter matrix", + data=np.array(flat_scatt).flatten(), + compression=compression) + # Repeat for multiplicity + if self._multiplicity_matrix[i] is not None: + # Now create the flattened scatter matrix array + flat_mult = [[[] for a in range(Na)] for p in range(Np)] + for p in range(Np): + for a in range(Na): + for l in range(self._scatter_matrix[i].shape[2]): + for g_in in range(G): + matrix = \ + self._multiplicity_matrix[i][p, a, g_in, :] + for g_out in range(g_out_bounds[p, a, g_in, 0], + g_out_bounds[p, a, g_in, 1] + 1): + flat_mult[p][a].append(matrix[g_out]) + scatt_grp.create_dataset("multiplicity matrix", + data=np.array(flat_mult).flatten(), + compression=compression) + # And finally, adjust g_out_bounds for 1-based group counting + # and write it. + g_out_bounds[:, :, :, :] += 1 + scatt_grp.create_dataset("g_out bounds", data=g_out_bounds, compression=compression)