diff --git a/docs/source/_images/loss.png b/docs/source/_images/loss.png new file mode 100644 index 0000000000..37d4169a18 Binary files /dev/null and b/docs/source/_images/loss.png differ diff --git a/docs/source/_images/prod.png b/docs/source/_images/prod.png new file mode 100644 index 0000000000..a4c2887d8f Binary files /dev/null and b/docs/source/_images/prod.png differ diff --git a/docs/source/conf.py b/docs/source/conf.py index 1943432165..12558139e1 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -23,7 +23,7 @@ sys.path.insert(0, os.path.abspath('../sphinxext')) # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.pngmath'] +extensions = ['sphinx.ext.pngmath', 'sphinxcontrib.tikz'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -48,7 +48,7 @@ copyright = u'2011-2014, Massachusetts Institute of Technology' # The short X.Y version. version = "0.6" # The full version, including alpha/beta/rc tags. -release = "0.6.0" +release = "0.6.1" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -188,7 +188,14 @@ latex_documents = [ u'Massachusetts Institute of Technology', 'manual'), ] -latex_elements = {'preamble': '\\usepackage{enumitem}\\setlistdepth{9}'} +latex_elements = { +'preamble': ''' +\usepackage{enumitem} +\setlistdepth{9} +\usepackage{tikz} +\usetikzlibrary{shapes,snakes,shadows,arrows,calc,decorations.markings,patterns,fit,matrix,spy} +''' +} # The name of an image file (relative to this directory) to place at the top of # the title page. diff --git a/docs/source/devguide/statepoint.rst b/docs/source/devguide/statepoint.rst index fee3fab591..0fc02171c6 100644 --- a/docs/source/devguide/statepoint.rst +++ b/docs/source/devguide/statepoint.rst @@ -290,6 +290,13 @@ if (run_mode == MODE_EIGENVALUE and source_present) Energy of the i-th source particle. +----------- +Revision 12 +----------- + +Same as revision 11, except **tallies(i) % scatt_order(j)** is now **tallies(i) +% moment_order(j)**. + ----------- Revision 11 ----------- @@ -504,13 +511,13 @@ if (run_mode == MODE_EIGENVALUE) **integer(4) tallies(i) % score_bins(j)** Values of specified scoring bins (e.g. SCORE_FLUX). - + *do j = 1, tallies(i) % n_score_bins* **integer(4) tallies(i) % scatt_order(j)** Scattering Order specified scoring bins. - + **integer(4) tallies(i) % n_score_bins** Number of scoring bins without accounting for those added by @@ -551,7 +558,7 @@ if (tallies_on > 0) *do j = 1, size(tallies(i) % scores, 1)* **real(8) tallies(i) % scores(j,k) % sum** - + Accumulated sum for the j-th score and k-th filter of the i-th tally @@ -581,7 +588,7 @@ if (run_mode == MODE_EIGENVALUE and source_present) Energy of the i-th source particle. ----------- -Revision 10 +Revision 10 ----------- **integer(4) FILETYPE_STATEPOINT** @@ -794,13 +801,13 @@ if (run_mode == MODE_EIGENVALUE) **integer(4) tallies(i) % score_bins(j)** Values of specified scoring bins (e.g. SCORE_FLUX). - + *do j = 1, tallies(i) % n_score_bins* **integer(4) tallies(i) % scatt_order(j)** Scattering Order specified scoring bins. - + **integer(4) tallies(i) % n_score_bins** Number of scoring bins without accounting for those added by @@ -837,7 +844,7 @@ if (tallies_on > 0) *do j = 1, size(tallies(i) % scores, 1)* **real(8) tallies(i) % scores(j,k) % sum** - + Accumulated sum for the j-th score and k-th filter of the i-th tally @@ -1046,13 +1053,13 @@ if (run_mode == MODE_EIGENVALUE) **integer(4) tallies(i) % score_bins(j)** Values of specified scoring bins (e.g. SCORE_FLUX). - + *do j = 1, tallies(i) % n_score_bins* **integer(4) tallies(i) % scatt_order(j)** Scattering Order specified scoring bins. - + **integer(4) tallies(i) % n_score_bins** Number of scoring bins without accounting for those added by @@ -1089,7 +1096,7 @@ if (tallies_on > 0) *do j = 1, size(tallies(i) % scores, 1)* **real(8) tallies(i) % scores(j,k) % sum** - + Accumulated sum for the j-th score and k-th filter of the i-th tally @@ -1294,13 +1301,13 @@ if (run_mode == MODE_EIGENVALUE) **integer(4) tallies(i) % score_bins(j)** Values of specified scoring bins (e.g. SCORE_FLUX). - + *do j = 1, tallies(i) % n_score_bins* **integer(4) tallies(i) % scatt_order(j)** Scattering Order specified scoring bins. - + **integer(4) tallies(i) % n_score_bins** Number of scoring bins without accounting for those added by @@ -1337,7 +1344,7 @@ if (tallies_on > 0) *do j = 1, size(tallies(i) % scores, 1)* **real(8) tallies(i) % scores(j,k) % sum** - + Accumulated sum for the j-th score and k-th filter of the i-th tally @@ -1526,13 +1533,13 @@ if (run_mode == MODE_EIGENVALUE) **integer(4) tallies(i) % score_bins(j)** Values of specified scoring bins (e.g. SCORE_FLUX). - + *do j = 1, tallies(i) % n_score_bins* **integer(4) tallies(i) % scatt_order(j)** Scattering Order specified scoring bins. - + **integer(4) tallies(i) % n_score_bins** Number of scoring bins without accounting for those added by @@ -1569,7 +1576,7 @@ if (tallies_on > 0) *do j = 1, size(tallies(i) % scores, 1)* **real(8) tallies(i) % scores(j,k) % sum** - + Accumulated sum for the j-th score and k-th filter of the i-th tally @@ -1790,7 +1797,7 @@ if (tallies_on > 0) *do j = 1, size(tallies(i) % scores, 1)* **real(8) tallies(i) % scores(j,k) % sum** - + Accumulated sum for the j-th score and k-th filter of the i-th tally @@ -1999,7 +2006,7 @@ if (tallies_on > 0) *do j = 1, size(tallies(i) % scores, 1)* **real(8) tallies(i) % scores(j,k) % sum** - + Accumulated sum for the j-th score and k-th filter of the i-th tally @@ -2204,7 +2211,7 @@ if (tallies_on > 0) *do j = 1, size(tallies(i) % scores, 1)* **real(8) tallies(i) % scores(j,k) % sum** - + Accumulated sum for the j-th score and k-th filter of the i-th tally @@ -2405,7 +2412,7 @@ if (tallies_on > 0) *do j = 1, size(tallies(i) % scores, 1)* **real(8) tallies(i) % scores(j,k) % sum** - + Accumulated sum for the j-th score and k-th filter of the i-th tally @@ -2526,7 +2533,7 @@ Revision 2 *do j = 1, size(tallies(i) % scores, 1)* **real(8) tallies(i) % scores(j,k) % sum** - + Accumulated sum for the j-th score and k-th filter of the i-th tally @@ -2625,7 +2632,7 @@ Revision 1 *do j = 1, size(tallies(i) % scores, 1)* **real(8) tallies(i) % scores(j,k) % sum** - + Accumulated sum for the j-th score and k-th filter of the i-th tally diff --git a/docs/source/methods/cmfd.rst b/docs/source/methods/cmfd.rst new file mode 100644 index 0000000000..3681df3b75 --- /dev/null +++ b/docs/source/methods/cmfd.rst @@ -0,0 +1,561 @@ +.. _methods_cmfd: + +================================================================ +Nonlinear Diffusion Acceleration - Coarse Mesh Finite Difference +================================================================ + +This page section discusses how nonlinear diffusion acceleration (NDA) using +coarse mesh finite difference (CMFD) is implemented into OpenMC. Before we get +into the theory, general notation for this section is discussed. + +-------- +Notation +-------- + +Before deriving NDA relationships, notation is explained. If a parameter has a +:math:`\overline{\cdot}`, it is surface area-averaged and if it has a +:math:`\overline{\overline\cdot}`, it is volume-averaged. When describing a +specific cell in the geometry, indices :math:`(i,j,k)` are used which correspond +to directions :math:`(x,y,z)`. In most cases, the same operation is performed in +all three directions. To compactly write this, an arbitrary direction set +:math:`(u,v,w)` that corresponds to cell indices :math:`(l,m,n)` is used. Note +that :math:`u` and :math:`l` do not have to correspond to :math:`x` and +:math:`i`. However, if :math:`u` and :math:`l` correspond to :math:`y` and +:math:`j`, :math:`v` and :math:`w` correspond to :math:`x` and :math:`z` +directions. An example of this is shown in the following expression: + +.. math:: + :label: not1 + + \sum\limits_{u\in(x,y,z)}\left\langle\overline{J}^{u,g}_{l+1/2,m,n} + \Delta_m^v\Delta_n^w\right\rangle + +Here, :math:`u` takes on each direction one at a time. The parameter :math:`J` +is surface area-averaged over the transverse indices :math:`m` and :math:`n` +located at :math:`l+1/2`. Usually, spatial indices are listed as subscripts and +the direction as a superscript. Energy group indices represented by :math:`g` +and :math:`h` are also listed as superscripts here. The group :math:`g` is the +group of interest and, if present, :math:`h` is all groups. Finally, any +parameter surrounded by :math:`\left\langle\cdot\right\rangle` represents a +tally quantity that can be edited from a Monte Carlo (MC) solution. + +------ +Theory +------ + +NDA is a diffusion model that has equivalent physics to a transport model. There +are many different methods that can be classified as NDA. The CMFD method is a +type of NDA that represents second order multigroup diffusion equations on a +coarse spatial mesh. Whether a transport model or diffusion model is used to +represent the distribution of neutrons, these models must satisfy the *neutron +balance equation*. This balance is represented by the following formula for a +specific energy group :math:`g` in cell :math:`(l,m,n)`: + +.. math:: + :label: eq_neut_bal + + \sum\limits_{u\in(x,y,z)}\left(\left\langle\overline{J}^{u,g}_{l+1/2,m,n} + \Delta_m^v\Delta_n^w\right\rangle - + \left\langle\overline{J}^{u,g}_{l-1/2,m,n} + \Delta_m^v\Delta_n^w\right\rangle\right) + + + \left\langle\overline{\overline\Sigma}_{t_{l,m,n}}^g + \overline{\overline\phi}_{l,m,n}^g\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle + = \\ + \sum\limits_{h=1}^G\left\langle + \overline{\overline{\nu_s\Sigma}}_{s_{l,m,n}}^{h\rightarrow + g}\overline{\overline\phi}_{l,m,n}^h\Delta_l^u\Delta_m^v\Delta_n^w + \right\rangle + + + \frac{1}{k_{eff}}\sum\limits_{h=1}^G + \left\langle\overline{\overline{\nu_f\Sigma}}_{f_{l,m,n}}^{h\rightarrow + g}\overline{\overline\phi}_{l,m,n}^h + \Delta_l^u\Delta_m^v\Delta_n^w\right\rangle. + +In eq. :eq:`eq_neut_bal` the parameters are defined as: + +* :math:`\left\langle\overline{J}^{u,g}_{l\pm + 1/2,m,n}\Delta_m^v\Delta_n^w\right\rangle` --- surface area-integrated net + current over surface :math:`(l\pm 1/2,m,n)` with surface normal in direction + :math:`u` in energy group :math:`g`. By dividing this quantity by the transverse + area, :math:`\Delta_m^v\Delta_n^w`, the surface area-averaged net current can + be computed. +* :math:`\left\langle\overline{\overline\Sigma}_{t_{l,m,n}}^g + \overline{\overline\phi}_{l,m,n}^g\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle` + --- volume-integrated total reaction rate over energy group :math:`g`. +* :math:`\left\langle\overline{\overline{\nu_s\Sigma}}_{s_{l,m,n}}^{h\rightarrow + g} + \overline{\overline\phi}_{l,m,n}^h\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle` + --- volume-integrated scattering production rate of neutrons that begin with + energy in group :math:`h` and exit reaction in group :math:`g`. This reaction + rate also includes the energy transfer of reactions (except fission) that + produce multiple neutrons such as (n, 2n); hence, the need for :math:`\nu_s` + to represent neutron multiplicity. +* :math:`k_{eff}` --- core multiplication factor. +* :math:`\left\langle\overline{\overline{\nu_f\Sigma}}_{f_{l,m,n}}^{h\rightarrow + g}\overline{\overline\phi}_{l,m,n}^h\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle` + --- volume-integrated fission production rate of neutrons from fissions in + group :math:`h` that exit in group :math:`g`. + +Each quantity in :math:`\left\langle\cdot\right\rangle` represents a scalar value that +is obtained from an MC tally. A good verification step when using an MC code is +to make sure that tallies satisfy this balance equation within statistics. No +NDA acceleration can be performed if the balance equation is not satisfied. + +There are three major steps to consider when performing NDA: (1) calculation of +macroscopic cross sections and nonlinear parameters, (2) solving an eigenvalue +problem with a system of linear equations, and (3) modifying MC source +distribution to align with the NDA solution on a chosen mesh. This process is +illustrated as a flow chart below. After a batch of neutrons +is simulated, NDA can take place. Each of the steps described above is described +in detail in the following sections. + +.. tikz:: Flow chart of NDA process. Note "XS" is used for cross section and + "DC" is used for diffusion coefficient. + :libs: shapes, snakes, shadows, arrows, calc, decorations.markings, patterns, fit, matrix, spy + :include: cmfd_tikz/cmfd_flow.tikz + +Calculation of Macroscopic Cross Sections +----------------------------------------- + +A diffusion model needs macroscopic cross sections and diffusion coefficients to +solve for multigroup fluxes. Cross sections are derived by conserving reaction +rates predicted by MC tallies. From Eq. :eq:`eq_neut_bal`, total, scattering +production and fission production macroscopic cross sections are needed. They are +defined from MC tallies as follows: + +.. math:: + :label: xs1 + + \overline{\overline\Sigma}_{t_{l,m,n}}^g \equiv + \frac{\left\langle\overline{\overline\Sigma}_{t_{l,m,n}}^g + \overline{\overline\phi}_{l,m,n}^g\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle} + {\left\langle\overline{\overline\phi}_{l,m,n}^g + \Delta_l^u\Delta_m^v\Delta_n^w\right\rangle}, + +.. math:: + :label: xs2 + + \overline{\overline{\nu_s\Sigma}}_{s_{l,m,n}}^{h\rightarrow g} \equiv + \frac{\left\langle\overline{\overline{\nu_s\Sigma}}_{s_{l,m,n}}^{h\rightarrow + g}\overline{\overline\phi}_{l,m,n}^h\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle} + {\left\langle\overline{\overline\phi}_{l,m,n}^h + \Delta_l^u\Delta_m^v\Delta_n^w\right\rangle} + +and + +.. math:: + :label: xs3 + + \overline{\overline{\nu_f\Sigma}}_{f_{l,m,n}}^{h\rightarrow g} \equiv + \frac{\left\langle\overline{\overline{\nu_f\Sigma}}_{f_{l,m,n}}^{h\rightarrow + g}\overline{\overline\phi}_{l,m,n}^h\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle} + {\left\langle\overline{\overline\phi}_{l,m,n}^h\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle}. + +In order to fully conserve neutron balance, leakage rates also need to be +preserved. In standard diffusion theory, leakage rates are represented by +diffusion coefficients. Unfortunately, it is not easy in MC to calculate a +single diffusion coefficient for a cell that describes leakage out of each +surface. Luckily, it does not matter what definition of diffusion coefficient is +used because nonlinear equivalence parameters will correct for this +inconsistency. However, depending on the diffusion coefficient definition +chosen, different convergence properties of NDA equations are observed. +Here, we introduce a diffusion coefficient that is derived for a coarse energy +transport reaction rate. This definition can easily be constructed from +MC tallies provided that angular moments of scattering reaction rates can +be obtained. The diffusion coefficient is defined as follows: + +.. math:: + :label: eq_transD + + \overline{\overline D}_{l,m,n}^g = + \frac{\left\langle\overline{\overline\phi}_{l,m,n}^g + \Delta_l^u\Delta_m^v\Delta_n^w\right\rangle}{3 + \left\langle\overline{\overline\Sigma}_{tr_{l,m,n}}^g + \overline{\overline\phi}_{l,m,n}^g + \Delta_l^u\Delta_m^v\Delta_n^w\right\rangle}, + +where + +.. math:: + :label: xs4 + + \left\langle\overline{\overline\Sigma}_{tr_{l,m,n}}^g + \overline{\overline\phi}_{l,m,n}^g\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle + = + \left\langle\overline{\overline\Sigma}_{t_{l,m,n}}^g + \overline{\overline\phi}_{l,m,n}^g\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle + \\ - + \left\langle\overline{\overline{\nu_s\Sigma}}_{s1_{l,m,n}}^g + \overline{\overline\phi}_{l,m,n}^g\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle. + +Note that the transport reaction rate is calculated from the total reaction rate +reduced by the :math:`P_1` scattering production reaction rate. Equation :eq:`eq_transD` +does not represent the best definition of diffusion coefficients from MC; +however, it is very simple and usually fits into MC tally frameworks +easily. Different methods to calculate more accurate diffusion coefficients can +found in [Herman]_. + +CMFD Equations +-------------- + +The first part of this section is devoted to discussing second-order finite +volume discretization of multigroup diffusion equations. This will be followed +up by the formulation of CMFD equations that are used in this NDA +scheme. When performing second-order finite volume discretization of the +diffusion equation, we need information that relates current to flux. In this +numerical scheme, each cell is coupled only to its direct neighbors. Therefore, +only two types of coupling exist: (1) cell-to-cell coupling and (2) +cell-to-boundary coupling. The derivation of this procedure is referred to as +finite difference diffusion equations and can be found in literature such +as [Hebert]_. These current/flux relationships are as follows: + +* cell-to-cell coupling + +.. math:: + :label: eq_cell_cell + + \overline{J}^{u,g}_{l\pm1/2,m,n} = -\frac{2\overline{\overline + D}_{l\pm1,m,n}^g\overline{\overline + D}_{l,m,n}^g}{\overline{\overline D}_{l\pm1,m,n}^g\Delta_l^u + + \overline{\overline + D}_{l,m,n}^g\Delta_{l\pm1}^u} + \left(\pm\overline{\overline{\phi}}_{l\pm1,m,n}^g\mp + \overline{\overline{\phi}}_{l,m,n}^g\right), + +* cell-to-boundary coupling + +.. math:: + :label: eq_cell_bound + + \overline{J}^{u,g}_{l\pm1/2,m,n} = \pm\frac{2\overline{\overline + D}_{l,m,n}^g\left(1 - + \beta_{l\pm1/2,m,n}^{u,g}\right)}{4\overline{\overline + D}_{l,m,n}^g\left(1 + \beta_{l\pm1/2,m,n}^{u,g}\right) + \left(1 - + \beta_{l\pm1/2,m,n}^{u,g}\right)\Delta_l^u}\overline{\overline{\phi}}_{l,m,n}^{g}. + +In Eqs. :eq:`eq_cell_cell` and :eq:`eq_cell_bound`, the :math:`\pm` refers to +left (:math:`-x`) or right (:math:`+x`) surface in the :math:`x` direction, +back (:math:`-y`) or front (:math:`+y`) surface in the :math:`y` direction and +bottom (:math:`-z`) or top (:math:`+z`) surface in the :math:`z` direction. For +cell-to-boundary coupling, a general albedo, :math:`\beta_{l\pm1/2,m,n}^{u,g}`, +is used. The albedo is defined as the ratio of incoming (:math:`-` superscript) +to outgoing (:math:`+` superscript) partial current on any surface represented +as + +.. math:: + :label: eq_albedo + + \beta_{l\pm1/2,m,n}^{u,g} = + \frac{\overline{J}^{u,g-}_{l\pm1/2,m,n}}{\overline{J}^{u,g+}_{l\pm1/2,m,n}}. + +Common boundary conditions are: vacuum (:math:`\beta=0`), reflective +(:math:`\beta=1`) and zero flux (:math:`\beta=-1`). Both eq. :eq:`eq_cell_cell` +and eq. :eq:`eq_cell_bound` can be written in this generic form, + +.. math:: + :label: eq_dtilde + + \overline{J}^{u,g}_{l\pm1/2,m,n} = \widetilde{D}_{l,m,n}^{u,g} \left(\dots\right). + +The parameter :math:`\widetilde{D}_{l,m,n}^{u,g}` represents the linear +coupling term between current and flux. These current relationships can be +sustituted into eq. :eq:`eq_neut_bal` to produce a linear system of multigroup +diffusion equations for each spatial cell and energy group. However, a solution +to these equations is not consistent with a higher order transport solution +unless equivalence factors are present. This is because both the diffusion +approximation, governed by Fick's Law, and spatial trunction error will produce +differences. Therefore, a nonlinear parameter, +:math:`\widehat{D}_{l,m,n}^{u,g}`, is added to eqs. :eq:`eq_cell_cell` and +:eq:`eq_cell_bound`. These equations are, respectively, + +.. math:: + :label: eq_dhat_cell + + \overline{J}^{u,g}_{l\pm1/2,m,n} = -\widetilde{D}_{l,m,n}^{u,g} + \left(\pm\overline{\overline{\phi}}_{l\pm1,m,n}^g\mp + \overline{\overline{\phi}}_{l,m,n}^g\right) + \widehat{D}_{l,m,n}^{u,g} + \left(\overline{\overline{\phi}}_{l\pm1,m,n}^g + + \overline{\overline{\phi}}_{l,m,n}^g\right) + +and + +.. math:: + :label: eq_dhat_bound + + \overline{J}^{u,g}_{l\pm1/2,m,n} = \pm\widetilde{D}_{l,m,n}^{u,g} + \overline{\overline{\phi}}_{l,m,n}^{g} + \widehat{D}_{l,m,n}^{u,g} + \overline{\overline{\phi}}_{l,m,n}^{g}. + +The only unknown in each of these equations is the equivalence parameter. The +current, linear coupling term and flux can either be obtained or derived from +MC tallies. Thus, it is called nonlinear because it is dependent on the flux +which is updated on the next iteration. + +Equations :eq:`eq_dhat_cell` and :eq:`eq_dhat_bound` can be substituted into +eq. :eq:`eq_neut_bal` to create a linear system of equations that is consistent +with transport physics. One example of this equation is written for an +interior cell, + +.. math:: + :label: eq_cmfd_sys + + \sum_{u\in + x,y,x}\frac{1}{\Delta_l^u}\left[\left(-\tilde{D}_{l-1/2,m,n}^{u,g} - + \hat{D}_{l-1/2,m,n}^{u,g}\right)\overline{\overline{\phi}}_{l-1,m,n}^g\right. + + \left(\tilde{D}_{l-1/2,m,n}^{u,g} + + \tilde{D}_{l+1/2,m,n}^{u,g} - \hat{D}_{l-1/2,m,n}^{u,g} + + \hat{D}_{l+1/2,m,n}^{u,g}\right)\overline{\overline{\phi}}_{l,m,n}^g + \\ + + \left. \left(-\tilde{D}_{l+1/2,m,n}^{u,g} + + \hat{D}_{l+1/2,m,n}^{u,g}\right)\overline{\overline{\phi}}_{l+1,m,n}^g + \right] + + \overline{\overline\Sigma}_{t_{l,m,n}}^g\overline{\overline{\phi}}_{l,m,n}^g + - \sum\limits_{h=1}^G\overline{\overline{\nu_s\Sigma}}^{h\rightarrow + g}_{s_{l,m,n}}\overline{\overline{\phi}}_{l,m,n}^h = + \frac{1}{k}\sum\limits_{h=1}^G\overline{\overline{\nu_f\Sigma}}^{h\rightarrow + g}_{f_{l,m,n}}\overline{\overline{\phi}}_{l,m,n}^h. + +It should be noted that before substitution, eq. :eq:`eq_neut_bal` was divided +by the volume of the cell, :math:`\Delta_l^u\Delta_m^v\Delta_n^w`. Equation +:eq:`eq_cmfd_sys` can be represented in operator form as + +.. math:: + :label: eq_CMFDopers + + \mathbb{M}\mathbf{\Phi} = \frac{1}{k}\mathbb{F}\mathbf{\Phi}, + +where :math:`\mathbb{M}` is the neutron loss matrix operator, +:math:`\mathbb{F}` is the neutron production matrix operator, +:math:`\mathbf{\Phi}` is the multigroup flux vector and :math:`k` is the +eigenvalue. This generalized eigenvalue problem is solved to obtain fundamental +mode multigroup fluxes and eigenvalue. In order to produce consistent results +with transport theory from these equations, the neutron balance equation must +have been satisfied by MC tallies. The desire is that CMFD equations will +produce a more accurate source than MC after each fission source generation. + +CMFD Feedback +------------- + +Now that a more accurate representation of the expected source distribution is +estimated from CMFD, it needs to be communicated back to MC. The first step +in this process is to generate a probability mass function that provides +information about how probable it is for a neutron to be born in a given cell +and energy group. This is represented as + +.. math:: + :label: eq_cmfd_psrc + + p_{l,m,n}^g = + \frac{\sum_{h=1}^{G}\overline{\overline{\nu_f\Sigma}}^{h\rightarrow + g}_{f_{l,m,n}}\overline{\overline{\phi}}_{l,m,n}^h\Delta_l^u\Delta_m^v + \Delta_n^w}{\sum_n\sum_m\sum_l\sum_{h=1}^{G}\overline{ + \overline{\nu_f\Sigma}}^{h\rightarrow + g}_{f_{l,m,n}}\overline{\overline{\phi}}_{l,m,n}^h\Delta_l^u\Delta_m^v + \Delta_n^w}. + +This equation can be multiplied by the number of source neutrons to obtain an +estimate of the expected number of neutrons to be born in a given cell and +energy group. This distribution can be compared to the MC source distribution +to generate weight adjusted factors defined as + +.. math:: + :label: eq_waf + + f_{l,m,n}^g = \frac{Np_{l,m,n}^g}{\sum\limits_s w_s};\quad s\in + \left(g,l,m,n\right). + +The MC source distribution is represented on the same coarse mesh as +CMFD by summing all neutrons' weights, :math:`w_s`, in a given cell and +energy group. MC source weights can then be modified by this weight +adjustment factor so that it matches the CMFD solution on the coarse +mesh, + +.. math:: + :label: src_mod + + w^\prime_s = w_s\times f_{l,m,n}^g;\quad s\in \left(g,l,m,n\right). + +It should be noted that heterogeneous information about local coordinates and +energy remain constant throughout this modification process. + +------------------------ +Implementation in OpenMC +------------------------ + +The section describes how CMFD was implemented in OpenMC. Before the simulation +begins, a user sets up a CMFD input file that contains the following basic +information: + +* CMFD mesh (space and energy), +* boundary conditions at edge of mesh (albedos), +* acceleration region (subset of mesh, optional), +* fission source generation (FSG)/batch that CMFD should begin, and +* whether CMFD feedback should be applied. + +It should be noted that for more difficult simulations (e.g., light water +reactors), there are other options available to users such as tally resetting +parameters, effective down-scatter usage, tally estimator, etc. For more +information please see :ref:`usersguide_cmfd`. + +Of the options described above, the optional acceleration subset region is an +uncommon feature. Because OpenMC only has a structured Cartesian mesh, mesh +cells may overlay regions that don't contain fissionable material and may be so +far from the core that the neutron flux is very low. If these regions were +included in the CMFD solution, bad estimates of diffusion parameters may result +and affect CMFD feedback. To deal with this, a user can carve out an active +acceleration region from their structured Cartesian mesh. This is illustrated +in diagram below. When placing a CMFD mesh over a geometry, the boundary +conditions must be known at the global edges of the mesh. If the geometry is +complex like the one below, one may have to cover the whole geometry including +the reactor pressure vessel because we know that there is a zero incoming +current boundary condition at the outer edge of the pressure vessel. This is +not viable in practice because neutrons in simulations may not reach mesh cells +that are near the pressure vessel. To circumvent this, one can shrink the mesh +to cover just the core region as shown in the diagram. However, one must still +estimate the boundary conditions at the global boundaries, but at these +locations, they are not readily known. In OpenMC, one can carve out the active +core region from the entire structured Cartesian mesh. This is shown in the +diagram below by the darkened region over the core. The albedo boundary +conditions at the active core/reflector boundary can be tallied indirectly +during the MC simulation with incoming and outgoing partial currents. This +allows the user to not have to worry about neutrons producing adequate tallies +in mesh cells far away from the core. + +.. tikz:: Diagram of CMFD acceleration mesh + :libs: shapes, snakes, shadows, arrows, calc, decorations.markings, patterns, fit, matrix, spy + :include: cmfd_tikz/meshfig.tikz + +During an MC simulation, CMFD tallies are accumulated. The basic tallies needed +are listed in Table :ref:`tab_tally`. Each tally is performed on a spatial and +energy mesh basis. The surface area-integrated net current is tallied on every +surface of the mesh. OpenMC tally objects are created by the CMFD code +internally, and cross sections are calculated at each CMFD feedback iteration. +The first CMFD iteration, controlled by the user, occurs just after tallies are +communicated to the master processor. Once tallies are collapsed, cross +sections, diffusion coefficients and equivalence parameters are calculated. This +is performed only on the acceleration region if that option has been activated +by the user. Once all diffusion parameters are calculated, CMFD matrices are +formed where energy groups are the inner most iteration index. In OpenMC, +compressed row storage sparse matrices are used due to the sparsity of CMFD +operators. An example of this sparsity is shown for the 3-D BEAVRS model in +figures :ref:`fig_loss` and :ref:`fig_prod` [BEAVRS]_. These matrices represent +an assembly radial mesh, 24 cell mesh in the axial direction and two energy +groups. The loss matrix is 99.92% sparse and the production matrix is 99.99% +sparse. Although the loss matrix looks like it is tridiagonal, it is really a +seven banded matrix with a block diagonal matrix for scattering. The production +matrix is a :math:`2\times 2` block diagonal; however, zeros are present because +no fission neutrons appear with energies in the thermal group. + +.. _tab_tally: + +.. table:: OpenMC CMFD tally list + + +--------------------------------------------------------------------------------------------+----------------+---------------------------+ + +--------------------------------------------------------------------------------------------+----------------+---------------------------+ + | tally | score | filter | + +============================================================================================+================+===========================+ + | \ :math:`\left\langle\overline{\overline\phi}_{l,m,n}^g | flux | mesh, energy | + | \Delta_l^u\Delta_m^v\Delta_n^w\right\rangle` | | | + +--------------------------------------------------------------------------------------------+----------------+---------------------------+ + | \ :math:`\left\langle\overline{\overline\Sigma}_{t_{l,m,n}}^g | total | mesh, energy | + | \overline{\overline\phi}_{l,m,n}^g\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle` | | | + +--------------------------------------------------------------------------------------------+----------------+---------------------------+ + | \ :math:`\left\langle\overline{\overline{\nu_s\Sigma}}_{s1_{l,m,n}}^g | nu-scatter-1 | mesh, energy | + | \overline{\overline\phi}_{l,m,n}^g\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle` | | | + +--------------------------------------------------------------------------------------------+----------------+---------------------------+ + | \ :math:`\left\langle\overline{\overline{\nu_s\Sigma}}_{s_{l,m,n}}^{h\rightarrow g} | nu-scatter | mesh, energy, energyout | + | \overline{\overline\phi}_{l,m,n}^h\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle` | | | + +--------------------------------------------------------------------------------------------+----------------+---------------------------+ + | \ :math:`\left\langle\overline{\overline{\nu_f\Sigma}}_{f_{l,m,n}}^{h\rightarrow g} | nu-fission | mesh, energy, energyout | + | \overline{\overline\phi}_{l,m,n}^h\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle` | | | + +--------------------------------------------------------------------------------------------+----------------+---------------------------+ + | \ :math:`\left\langle\overline{J}^{u,g}_{l\pm 1/2,m,n}\Delta_m^v\Delta_n^w\right\rangle` | current | mesh, energy | + +--------------------------------------------------------------------------------------------+----------------+---------------------------+ + +.. _fig_loss: + +.. figure:: ../_images/loss.png + :scale: 50 + + Sparsity of Neutron Loss Operator + +.. _fig_prod: + +.. figure:: ../_images/prod.png + :scale: 50 + + Sparsity of Neutron Production Operator + +To solve the eigenvalue problem with these matrices, different source iteration +and linear solvers can be used. The most common source iteration solver used is +standard power iteration as described in [Gill]_. To accelerate these source +iterations, a Wielandt shift scheme can be used as discussed in [Park]_. PETSc +solvers were first implemented to perform the linear solution in parallel that +occurs once per source iteration. When using PETSc, different types of parallel +linear solvers and preconditioners can be used. By default, OpenMC uses an +incomplete LU preconditioner and a GMRES Krylov solver. After some initial +studies of parallelization with PETSc, it was observed that because CMFD +matrices are very sparse, solution times do not scale well. An additional +Gauss-Seidel linear solver with Chebyshev acceleration was added that is +similar to the one used for CMFD in CASMO [Rhodes]_ and [Smith]_. This solver +was implemented with a custom section for two energy groups. Because energy +group is the inner most index, a block diagonal is formed when using more than +one group. For two groups, it is easy to invert this diagonal analytically +inside the Gauss-Seidel iterative solver. For more than two groups, this +analytic inversion can still be performed, but with more computational effort. +A standard Gauss-Seidel solver is used for more than two groups. + +Besides a power iteration, a Jacobian-free Newton-Krylov method was also +implemented to obtain eigenvalue and multigroup fluxes as described in [Gill]_ +and [Knoll]_. This method is not the primary one used, but has gotten recent +attention due to its coupling advantages to other physics such as thermal +hydraulics. Once multigroup fluxes are obtained, a normalized fission source is +calculated in the code using eq. :eq:`eq_cmfd_psrc` directly. + +The next step in the process is to compute weight adjustment factors. These are +calculated by taking the ratio of the expected number of neutrons from the CMFD +source distribution to the current number of neutrons in each mesh. It is +straightforward to compute the CMFD number of neutrons because it is the +product between the total starting initial weight of neutrons and the CMFD +normalized fission source distribution. To compute the number of neutrons from +the current MC source, OpenMC sums the statistical +weights of neutrons from the source bank on a given spatial and energy mesh. +Once weight adjustment factors were calculated, each neutron's statistical +weight in the source bank was modified according to its location and energy. +Examples of CMFD simulations using OpenMC can be found in [Herman_Thesis]_. + +---------- +References +---------- + +.. [BEAVRS] Nick Horelik, Bryan Herman. *Benchmark for Evaluation And Verification of Reactor + Simulations*. Massachusetts Institute of Technology, http://crpg.mit.edu/pub/beavrs + , 2013. + +.. [Gill] Daniel F. Gill. *Newton-Krylov methods for the solution of the k-eigenvalue problem in + multigroup neutronics calculations*. Ph.D. thesis, Pennsylvania State University, 2010. + +.. [Hebert] Alain Hebert. *Applied reactor physics*. Presses Internationales Polytechnique, + Montreal, 2009. + +.. [Herman] Bryan R. Herman, Benoit Forget, Kord Smith, and Brian N. Aviles. Improved + diffusion coefficients generated from Monte Carlo codes. In *Proceedings of M&C + 2013*, Sun Valley, ID, USA, May 5 - 9, 2013. + +.. [Herman_Thesis] Bryan R. Herman. *Monte Carlo and Thermal Hydraulic Coupling using + Low-Order Nonlinear Diffusion Acceleration*. Sc.D. thesis, + Massachusetts Institute of Technology, 2014. + +.. [Knoll] D.A. Knoll, H. Park, and C. Newman. *Acceleration of k-eigenvalue/criticality + calculations using the Jacobian-free Newton-Krylov method*. Nuclear Science and + Engineering, 167:133–140, 2011. + +.. [Park] H. Park, D.A. Knoll, and C.K. Newman. *Nonlinear acceleration of transport + criticality problems*. Nuclear Science and Engineering, 172:52–65, 2012. + +.. [Rhodes] Joel Rhodes and Malte Edenius. *CASMO-4 --- A Fuel Assembly Burnup Program. + User’s Manual*. Studsvik of America, ssp-09/443-u rev 0, proprietary edition, 2001. + +.. [Smith] Kord S Smith and Joel D Rhodes III. *Full-core, 2-D, LWR core calculations with + CASMO-4E*. In Proceedings of PHYSOR 2002, Seoul, Korea, October 7 - 10, 2002. diff --git a/docs/source/methods/cmfd_tikz/cmfd_flow.tikz b/docs/source/methods/cmfd_tikz/cmfd_flow.tikz new file mode 100644 index 0000000000..0611918052 --- /dev/null +++ b/docs/source/methods/cmfd_tikz/cmfd_flow.tikz @@ -0,0 +1,19 @@ +\begin{tikzpicture} + \matrix[every node/.style={draw, thick, minimum width=3cm, minimum height=1cm, align=center}, column sep=2cm, row sep=1cm] (m) { +\node[draw, fill=red!40] (start) {Batch $i$ \\ tally NDA}; & \\ + \node[draw, diamond, aspect=2, fill=green!40] (cmfd) {Run NDA?}; & \node[draw, fill=red!40] (end) {Batch $i + 1$ \\ tally NDA}; \\ +\node[draw, fill=blue!40] (xs) {Calculate XS \& DC}; & \node[draw, fill=blue!40] (modify) {Modify MC Source}; \\ +\node[draw, fill=blue!40] (nonlinear) {Calculate Equivalence}; & \node[draw, fill=blue!40] (eqs) {Solve NDA eqs.};\\ +}; + +\begin{scope}[every path/.style={->,very thick,draw}] + \draw (start.south) -- (cmfd.north); + \draw (cmfd.east) -- node[above] {no} (end.west); + \draw (cmfd.south) -- node[right] {yes} (xs.north); + \draw (xs.south) -- (nonlinear.north); + \draw (nonlinear.east) -- (eqs.west); + \draw (eqs.north) -- (modify.south); + \draw (modify.north) -- (end.south); + \end{scope} + +\end{tikzpicture} \ No newline at end of file diff --git a/docs/source/methods/cmfd_tikz/meshfig.tikz b/docs/source/methods/cmfd_tikz/meshfig.tikz new file mode 100644 index 0000000000..1e5bbfa476 --- /dev/null +++ b/docs/source/methods/cmfd_tikz/meshfig.tikz @@ -0,0 +1,628 @@ + + % these dimensions are determined in arrow_dimms.ods + + \def\scale{1.0} + + \def\latWidth{0.2808363589*\scale} + + \def\RPVOR{3*\scale} + \def\rectW{0.75*\scale} + \def\RPVIR{2.8694005485*\scale} + \def\BarrelIR{2.4547472901*\scale} + \def\BarrelOR{2.5293848766*\scale} + \def\ShieldOR{2.6040224631*\scale} + + \def\bafCIRx{0.9829272561*\scale} + \def\bafCIRy{2.1062726917*\scale} + \def\bafCORx{1.0119529842*\scale} + \def\bafCORy{2.1352984197*\scale} + \def\bafMIRx{1.8254363328*\scale} + \def\bafMIRy{1.5445999739*\scale} + \def\bafMORx{1.8544620609*\scale} + \def\bafMORy{1.573625702*\scale} + + \tikzset{Assembly/.style={ + inner sep=0pt, + text width=\latWidth in, + minimum size=\latWidth in, + draw=black, + align=center + } + } + + \def\tkzRPV{(0,0) circle (\RPVIR) (0,0) circle (\RPVOR)} + \def\tkzBarrel{(0,0) circle (\BarrelIR) (0,0) circle (\BarrelOR)} + \def\tkzShields{(0,0) circle (\BarrelOR) (0,0) circle (\ShieldOR)} + + \def\tkzBaffCOR{(-\bafCORx, -\bafCORy) rectangle (\bafCORx, \bafCORy)} + \def\tkzBaffCIR{(-\bafCIRx, -\bafCIRy) rectangle (\bafCIRx, \bafCIRy)} + \def\tkzBaffMOR{(-\bafMORx, -\bafMORy) rectangle (\bafMORx, \bafMORy)} + \def\tkzBaffMIR{(-\bafMIRx, -\bafMIRy) rectangle (\bafMIRx, \bafMIRy) } + \def\tkzBaffleC{ \tkzBaffCIR \tkzBaffCOR } + \def\tkzBaffleM{ \tkzBaffMIR \tkzBaffMOR } + + \def\tkzBaffCClip{\tkzBaffCIR (-\RPVOR, -\RPVOR) rectangle (\RPVOR, \RPVOR)} + \def\tkzBaffMClip{\tkzBaffMIR (-\RPVOR, -\RPVOR) rectangle (\RPVOR, \RPVOR)} + + \def\highenr{blue!50} + \def\midenr{yellow!50} + \def\lowenr{red!50} + \def\lightgray{black!25} + \def\darkgray{black!80} + + \begin{tikzpicture}[x=1in,y=1in, xshift=3in] +\scalebox{0.6}{ + % draw RPV, barrel, and shield panels + + \path[fill=black,even odd rule] \tkzRPV; + \path[fill=black,even odd rule] \tkzBarrel; + \begin{scope} + \clip[rotate around={45:(0,0)}] (-\RPVOR, -\rectW) rectangle (\RPVOR, \rectW) (-\rectW, \RPVOR) rectangle (\rectW, -\RPVOR); + \path[fill=black,even odd rule] \tkzShields; + \end{scope} + + + % draw assembly row/column headers + + \draw[red, thick] ($(-7*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[above, anchor=south] {R} -- ($(-7*\latWidth,4*\latWidth)$); + \draw[red, thick] ($(-6*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[above, anchor=south] {P} -- ($(-6*\latWidth,6*\latWidth)$); + \draw[red, thick] ($(-5*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[above, anchor=south] {N} -- ($(-5*\latWidth,7*\latWidth)$); + \draw[red, thick] ($(-4*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[above, anchor=south] {M} -- ($(-4*\latWidth,7*\latWidth)$); + \draw[red, thick] ($(-3*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[above, anchor=south] {L} -- ($(-3*\latWidth,8*\latWidth)$); + \draw[red, thick] ($(-2*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[above, anchor=south] {K} -- ($(-2*\latWidth,8*\latWidth)$); + \draw[red, thick] ($(-1*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[above, anchor=south] {J} -- ($(-1*\latWidth,8*\latWidth)$); + \draw[red, thick] ($(-0*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[above, anchor=south] {H} -- ($(-0*\latWidth,8*\latWidth)$); + \draw[red, thick] ($(1*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[above, anchor=south] {G} -- ($(1*\latWidth,8*\latWidth)$); + \draw[red, thick] ($(2*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[above, anchor=south] {F} -- ($(2*\latWidth,8*\latWidth)$); + \draw[red, thick] ($(3*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[above, anchor=south] {E} -- ($(3*\latWidth,8*\latWidth)$); + \draw[red, thick] ($(4*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[above, anchor=south] {D} -- ($(4*\latWidth,7*\latWidth)$); + \draw[red, thick] ($(5*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[above, anchor=south] {C} -- ($(5*\latWidth,7*\latWidth)$); + \draw[red, thick] ($(6*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[above, anchor=south] {B} -- ($(6*\latWidth,6*\latWidth)$); + \draw[red, thick] ($(7*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[above, anchor=south] {A} -- ($(7*\latWidth,4*\latWidth)$); + + \begin{scope}[rotate=90] + \draw[red, thick] ($(-7*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[left, anchor=east] {15} -- ($(-7*\latWidth,4*\latWidth)$); + \draw[red, thick] ($(-6*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[left, anchor=east] {14} -- ($(-6*\latWidth,6*\latWidth)$); + \draw[red, thick] ($(-5*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[left, anchor=east] {13} -- ($(-5*\latWidth,7*\latWidth)$); + \draw[red, thick] ($(-4*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[left, anchor=east] {12} -- ($(-4*\latWidth,7*\latWidth)$); + \draw[red, thick] ($(-3*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[left, anchor=east] {11} -- ($(-3*\latWidth,8*\latWidth)$); + \draw[red, thick] ($(-2*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[left, anchor=east] {10} -- ($(-2*\latWidth,8*\latWidth)$); + \draw[red, thick] ($(-1*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[left, anchor=east] {9} -- ($(-1*\latWidth,8*\latWidth)$); + \draw[red, thick] ($(-0*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[left, anchor=east] {8} -- ($(-0*\latWidth,8*\latWidth)$); + \draw[red, thick] ($(1*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[left, anchor=east] {7} -- ($(1*\latWidth,8*\latWidth)$); + \draw[red, thick] ($(2*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[left, anchor=east] {6} -- ($(2*\latWidth,8*\latWidth)$); + \draw[red, thick] ($(3*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[left, anchor=east] {5} -- ($(3*\latWidth,8*\latWidth)$); + \draw[red, thick] ($(4*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[left, anchor=east] {4} -- ($(4*\latWidth,7*\latWidth)$); + \draw[red, thick] ($(5*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[left, anchor=east] {3} -- ($(5*\latWidth,7*\latWidth)$); + \draw[red, thick] ($(6*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[left, anchor=east] {2} -- ($(6*\latWidth,6*\latWidth)$); + \draw[red, thick] ($(7*\latWidth,\RPVOR/\latWidth*\latWidth)$) node[left, anchor=east] {1} -- ($(7*\latWidth,4*\latWidth)$); + \end{scope} + + % draw fuel assembly nodes + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-7*\latWidth,8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-6*\latWidth,8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-5*\latWidth,8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-4*\latWidth,8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-3*\latWidth,8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-2*\latWidth,8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-1*\latWidth,8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-0*\latWidth,8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 1*\latWidth,8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 2*\latWidth,8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 3*\latWidth,8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 4*\latWidth,8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 5*\latWidth,8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 6*\latWidth,8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 7*\latWidth,8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,8*\latWidth)$) {}; + + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-7*\latWidth,7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-6*\latWidth,7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-5*\latWidth,7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-4*\latWidth,7*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-3*\latWidth,7*\latWidth)$) {}; % L1 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-3*\latWidth,7*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-2*\latWidth,7*\latWidth)$) {6}; % K1 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-2*\latWidth,7*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-1*\latWidth,7*\latWidth)$) {}; % J1 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-1*\latWidth,7*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-0*\latWidth,7*\latWidth)$) {6}; % H1 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-0*\latWidth,7*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 1*\latWidth,7*\latWidth)$) {}; % G1 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 1*\latWidth,7*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 2*\latWidth,7*\latWidth)$) {6}; % F1 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 2*\latWidth,7*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 3*\latWidth,7*\latWidth)$) {}; % E1 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 3*\latWidth,7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 4*\latWidth,7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 5*\latWidth,7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 6*\latWidth,7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 7*\latWidth,7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,7*\latWidth)$) {}; + + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,6*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-7*\latWidth,6*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-6*\latWidth,6*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-5*\latWidth,6*\latWidth)$) {}; % N2 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-5*\latWidth,6*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-4*\latWidth,6*\latWidth)$) {}; % M2 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-4*\latWidth,6*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-3*\latWidth,6*\latWidth)$) {16}; % L2 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-3*\latWidth,6*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-2*\latWidth,6*\latWidth)$) {}; % K2 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-2*\latWidth,6*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-1*\latWidth,6*\latWidth)$) {20}; % J2 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-1*\latWidth,6*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-0*\latWidth,6*\latWidth)$) {}; % H2 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-0*\latWidth,6*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 1*\latWidth,6*\latWidth)$) {20}; % G2 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 1*\latWidth,6*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 2*\latWidth,6*\latWidth)$) {}; % F2 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 2*\latWidth,6*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 3*\latWidth,6*\latWidth)$) {16}; % E2 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 3*\latWidth,6*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 4*\latWidth,6*\latWidth)$) {}; % D2 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 4*\latWidth,6*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 5*\latWidth,6*\latWidth)$) {}; % C2 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 5*\latWidth,6*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 6*\latWidth,6*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 7*\latWidth,6*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,6*\latWidth)$) {}; + + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,5*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-7*\latWidth,5*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-6*\latWidth,5*\latWidth)$) {}; % P3 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-6*\latWidth,5*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-5*\latWidth,5*\latWidth)$) {15}; % N3 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-5*\latWidth,5*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-4*\latWidth,5*\latWidth)$) {16}; % M3 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-4*\latWidth,5*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-3*\latWidth,5*\latWidth)$) {}; % L3 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-3*\latWidth,5*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-2*\latWidth,5*\latWidth)$) {16}; % K3 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-2*\latWidth,5*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-1*\latWidth,5*\latWidth)$) {}; % J3 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-1*\latWidth,5*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-0*\latWidth,5*\latWidth)$) {16}; % H3 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-0*\latWidth,5*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 1*\latWidth,5*\latWidth)$) {}; % G3 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 1*\latWidth,5*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 2*\latWidth,5*\latWidth)$) {16}; % F3 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 2*\latWidth,5*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 3*\latWidth,5*\latWidth)$) {}; % E3 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 3*\latWidth,5*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 4*\latWidth,5*\latWidth)$) {16}; % D3 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 4*\latWidth,5*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 5*\latWidth,5*\latWidth)$) {15}; % C3 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 5*\latWidth,5*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 6*\latWidth,5*\latWidth)$) {}; % B3 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 6*\latWidth,5*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 7*\latWidth,5*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,5*\latWidth)$) {}; + + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,4*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-7*\latWidth,4*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-6*\latWidth,4*\latWidth)$) {}; % P4 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-6*\latWidth,4*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-5*\latWidth,4*\latWidth)$) {16}; % N4 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-5*\latWidth,4*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-4*\latWidth,4*\latWidth)$) {}; % M4 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-4*\latWidth,4*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-3*\latWidth,4*\latWidth)$) {16}; % L4 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-3*\latWidth,4*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-2*\latWidth,4*\latWidth)$) {}; % K4 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-2*\latWidth,4*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-1*\latWidth,4*\latWidth)$) {12}; % J4 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-1*\latWidth,4*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-0*\latWidth,4*\latWidth)$) {}; % H4 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-0*\latWidth,4*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 1*\latWidth,4*\latWidth)$) {12}; % G4 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 1*\latWidth,4*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 2*\latWidth,4*\latWidth)$) {}; % F4 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 2*\latWidth,4*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 3*\latWidth,4*\latWidth)$) {16}; % E4 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 3*\latWidth,4*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 4*\latWidth,4*\latWidth)$) {}; % D4 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 4*\latWidth,4*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 5*\latWidth,4*\latWidth)$) {16}; % C4 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 5*\latWidth,4*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 6*\latWidth,4*\latWidth)$) {}; % B4 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 6*\latWidth,4*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 7*\latWidth,4*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,4*\latWidth)$) {}; + + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,3*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-7*\latWidth,3*\latWidth)$) {}; % R5 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-7*\latWidth,3*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-6*\latWidth,3*\latWidth)$) {16}; % P5 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-6*\latWidth,3*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-5*\latWidth,3*\latWidth)$) {}; % N5 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-5*\latWidth,3*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-4*\latWidth,3*\latWidth)$) {16}; % M5 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-4*\latWidth,3*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-3*\latWidth,3*\latWidth)$) {}; % L5 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-3*\latWidth,3*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-2*\latWidth,3*\latWidth)$) {12}; % K5 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-2*\latWidth,3*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-1*\latWidth,3*\latWidth)$) {}; % J5 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-1*\latWidth,3*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-0*\latWidth,3*\latWidth)$) {12}; % H5 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-0*\latWidth,3*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 1*\latWidth,3*\latWidth)$) {}; % G5 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 1*\latWidth,3*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 2*\latWidth,3*\latWidth)$) {12}; % F5 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 2*\latWidth,3*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 3*\latWidth,3*\latWidth)$) {}; % E5 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 3*\latWidth,3*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 4*\latWidth,3*\latWidth)$) {16}; % D5 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 4*\latWidth,3*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 5*\latWidth,3*\latWidth)$) {}; % C5 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 5*\latWidth,3*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 6*\latWidth,3*\latWidth)$) {16}; % B5 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 6*\latWidth,3*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 7*\latWidth,3*\latWidth)$) {}; % A5 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 7*\latWidth,3*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,3*\latWidth)$) {}; + + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,2*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-7*\latWidth,2*\latWidth)$) {6}; % R6 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-7*\latWidth,2*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-6*\latWidth,2*\latWidth)$) {}; % P6 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-6*\latWidth,2*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-5*\latWidth,2*\latWidth)$) {16}; % N6 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-5*\latWidth,2*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-4*\latWidth,2*\latWidth)$) {}; % M6 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-4*\latWidth,2*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-3*\latWidth,2*\latWidth)$) {12}; % L6 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-3*\latWidth,2*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-2*\latWidth,2*\latWidth)$) {}; % K6 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-2*\latWidth,2*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-1*\latWidth,2*\latWidth)$) {12}; % J6 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-1*\latWidth,2*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-0*\latWidth,2*\latWidth)$) {}; % H6 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-0*\latWidth,2*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 1*\latWidth,2*\latWidth)$) {12}; % G6 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 1*\latWidth,2*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 2*\latWidth,2*\latWidth)$) {}; % F6 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 2*\latWidth,2*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 3*\latWidth,2*\latWidth)$) {12}; % E6 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 3*\latWidth,2*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 4*\latWidth,2*\latWidth)$) {}; % D6 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 4*\latWidth,2*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 5*\latWidth,2*\latWidth)$) {16}; % C6 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 5*\latWidth,2*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 6*\latWidth,2*\latWidth)$) {}; % B6 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 6*\latWidth,2*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 7*\latWidth,2*\latWidth)$) {6}; % A6 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 7*\latWidth,2*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,2*\latWidth)$) {}; + + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,1*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-7*\latWidth,1*\latWidth)$) {}; % R7 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-7*\latWidth,1*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-6*\latWidth,1*\latWidth)$) {20}; % P7 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-6*\latWidth,1*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-5*\latWidth,1*\latWidth)$) {}; % N7 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-5*\latWidth,1*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-4*\latWidth,1*\latWidth)$) {12}; % M7 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-4*\latWidth,1*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-3*\latWidth,1*\latWidth)$) {}; % L7 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-3*\latWidth,1*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-2*\latWidth,1*\latWidth)$) {12}; % K7 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-2*\latWidth,1*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-1*\latWidth,1*\latWidth)$) {}; % J7 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-1*\latWidth,1*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-0*\latWidth,1*\latWidth)$) {16}; % H7 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-0*\latWidth,1*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 1*\latWidth,1*\latWidth)$) {}; % G7 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 1*\latWidth,1*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 2*\latWidth,1*\latWidth)$) {12}; % F7 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 2*\latWidth,1*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 3*\latWidth,1*\latWidth)$) {}; % E7 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 3*\latWidth,1*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 4*\latWidth,1*\latWidth)$) {12}; % D7 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 4*\latWidth,1*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 5*\latWidth,1*\latWidth)$) {}; % C7 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 5*\latWidth,1*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 6*\latWidth,1*\latWidth)$) {20}; % B7 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 6*\latWidth,1*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 7*\latWidth,1*\latWidth)$) {}; % A7 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 7*\latWidth,1*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,1*\latWidth)$) {}; + + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,0*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-7*\latWidth,0*\latWidth)$) {6}; % R8 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-7*\latWidth,0*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-6*\latWidth,0*\latWidth)$) {}; % P8 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-6*\latWidth,0*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-5*\latWidth,0*\latWidth)$) {16}; % N8 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-5*\latWidth,0*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-4*\latWidth,0*\latWidth)$) {}; % M8 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-4*\latWidth,0*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-3*\latWidth,0*\latWidth)$) {12}; % L8 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-3*\latWidth,0*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-2*\latWidth,0*\latWidth)$) {}; % K8 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-2*\latWidth,0*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-1*\latWidth,0*\latWidth)$) {16}; % J8 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-1*\latWidth,0*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-0*\latWidth,0*\latWidth)$) {}; % H8 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-0*\latWidth,0*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 1*\latWidth,0*\latWidth)$) {16}; % G8 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 1*\latWidth,0*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 2*\latWidth,0*\latWidth)$) {}; % F8 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 2*\latWidth,0*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 3*\latWidth,0*\latWidth)$) {12}; % E8 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 3*\latWidth,0*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 4*\latWidth,0*\latWidth)$) {}; % D8 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 4*\latWidth,0*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 5*\latWidth,0*\latWidth)$) {16}; % C8 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 5*\latWidth,0*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 6*\latWidth,0*\latWidth)$) {}; % B8 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 6*\latWidth,0*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 7*\latWidth,0*\latWidth)$) {6}; % A8 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 7*\latWidth,0*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,0*\latWidth)$) {}; + + + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,-1*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-7*\latWidth,-1*\latWidth)$) {}; % R9 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-7*\latWidth,-1*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-6*\latWidth,-1*\latWidth)$) {20}; % P9 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-6*\latWidth,-1*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-5*\latWidth,-1*\latWidth)$) {}; % N9 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-5*\latWidth,-1*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-4*\latWidth,-1*\latWidth)$) {12}; % M9 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-4*\latWidth,-1*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-3*\latWidth,-1*\latWidth)$) {}; % L9 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-3*\latWidth,-1*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-2*\latWidth,-1*\latWidth)$) {12}; % K9 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-2*\latWidth,-1*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-1*\latWidth,-1*\latWidth)$) {}; % J9 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-1*\latWidth,-1*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-0*\latWidth,-1*\latWidth)$) {16}; % H9 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-0*\latWidth,-1*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 1*\latWidth,-1*\latWidth)$) {}; % G9 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 1*\latWidth,-1*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 2*\latWidth,-1*\latWidth)$) {12}; % F9 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 2*\latWidth,-1*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 3*\latWidth,-1*\latWidth)$) {}; % E9 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 3*\latWidth,-1*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 4*\latWidth,-1*\latWidth)$) {12}; % D9 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 4*\latWidth,-1*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 5*\latWidth,-1*\latWidth)$) {}; % C9 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 5*\latWidth,-1*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 6*\latWidth,-1*\latWidth)$) {20}; % B9 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 6*\latWidth,-1*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 7*\latWidth,-1*\latWidth)$) {}; % A9 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 7*\latWidth,-1*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,-1*\latWidth)$) {}; + + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,-2*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-7*\latWidth,-2*\latWidth)$) {6}; % R10 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-7*\latWidth,-2*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-6*\latWidth,-2*\latWidth)$) {}; % P10 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-6*\latWidth,-2*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-5*\latWidth,-2*\latWidth)$) {16}; % N10 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-5*\latWidth,-2*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-4*\latWidth,-2*\latWidth)$) {}; % M10 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-4*\latWidth,-2*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-3*\latWidth,-2*\latWidth)$) {12}; % L10 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-3*\latWidth,-2*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-2*\latWidth,-2*\latWidth)$) {}; % K10 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-2*\latWidth,-2*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-1*\latWidth,-2*\latWidth)$) {12}; % J10 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-1*\latWidth,-2*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-0*\latWidth,-2*\latWidth)$) {}; % H10 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-0*\latWidth,-2*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 1*\latWidth,-2*\latWidth)$) {12}; % G10 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 1*\latWidth,-2*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 2*\latWidth,-2*\latWidth)$) {}; % F10 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 2*\latWidth,-2*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 3*\latWidth,-2*\latWidth)$) {12}; % E10 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 3*\latWidth,-2*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 4*\latWidth,-2*\latWidth)$) {}; % D10 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 4*\latWidth,-2*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 5*\latWidth,-2*\latWidth)$) {16}; % C10 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 5*\latWidth,-2*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 6*\latWidth,-2*\latWidth)$) {}; % B10 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 6*\latWidth,-2*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 7*\latWidth,-2*\latWidth)$) {6}; % A10 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 7*\latWidth,-2*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,-2*\latWidth)$) {}; + + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,-3*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-7*\latWidth,-3*\latWidth)$) {}; % R11 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-7*\latWidth,-3*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-6*\latWidth,-3*\latWidth)$) {16}; % P11 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-6*\latWidth,-3*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-5*\latWidth,-3*\latWidth)$) {}; % N11 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-5*\latWidth,-3*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-4*\latWidth,-3*\latWidth)$) {16}; % M11 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-4*\latWidth,-3*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-3*\latWidth,-3*\latWidth)$) {}; % L11 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-3*\latWidth,-3*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-2*\latWidth,-3*\latWidth)$) {12}; % K11 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-2*\latWidth,-3*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-1*\latWidth,-3*\latWidth)$) {}; % J11 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-1*\latWidth,-3*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-0*\latWidth,-3*\latWidth)$) {12}; % H11 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-0*\latWidth,-3*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 1*\latWidth,-3*\latWidth)$) {}; % G11 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 1*\latWidth,-3*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 2*\latWidth,-3*\latWidth)$) {12}; % F11 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 2*\latWidth,-3*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 3*\latWidth,-3*\latWidth)$) {}; % E11 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 3*\latWidth,-3*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 4*\latWidth,-3*\latWidth)$) {16}; % D11 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 4*\latWidth,-3*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 5*\latWidth,-3*\latWidth)$) {}; % C11 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 5*\latWidth,-3*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 6*\latWidth,-3*\latWidth)$) {16}; % B11 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 6*\latWidth,-3*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 7*\latWidth,-3*\latWidth)$) {}; % A11 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 7*\latWidth,-3*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,-3*\latWidth)$) {}; + + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,-4*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-7*\latWidth,-4*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-6*\latWidth,-4*\latWidth)$) {}; % P12 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-6*\latWidth,-4*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-5*\latWidth,-4*\latWidth)$) {16}; % N12 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-5*\latWidth,-4*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-4*\latWidth,-4*\latWidth)$) {}; % M12 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-4*\latWidth,-4*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-3*\latWidth,-4*\latWidth)$) {16}; % L12 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-3*\latWidth,-4*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-2*\latWidth,-4*\latWidth)$) {}; % K12 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-2*\latWidth,-4*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-1*\latWidth,-4*\latWidth)$) {12}; % J12 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-1*\latWidth,-4*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-0*\latWidth,-4*\latWidth)$) {}; % H12 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-0*\latWidth,-4*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 1*\latWidth,-4*\latWidth)$) {12}; % G12 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 1*\latWidth,-4*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 2*\latWidth,-4*\latWidth)$) {}; % F12 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 2*\latWidth,-4*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 3*\latWidth,-4*\latWidth)$) {16}; % E12 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 3*\latWidth,-4*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 4*\latWidth,-4*\latWidth)$) {}; % D12 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 4*\latWidth,-4*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 5*\latWidth,-4*\latWidth)$) {16}; % C12 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 5*\latWidth,-4*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 6*\latWidth,-4*\latWidth)$) {}; % B12 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 6*\latWidth,-4*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 7*\latWidth,-4*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,-4*\latWidth)$) {}; + + + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,-5*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-7*\latWidth,-5*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-6*\latWidth,-5*\latWidth)$) {}; % P13 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-6*\latWidth,-5*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-5*\latWidth,-5*\latWidth)$) {15}; % N13 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-5*\latWidth,-5*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-4*\latWidth,-5*\latWidth)$) {16}; % M13 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-4*\latWidth,-5*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-3*\latWidth,-5*\latWidth)$) {}; % L13 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-3*\latWidth,-5*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-2*\latWidth,-5*\latWidth)$) {16}; % K13 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-2*\latWidth,-5*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-1*\latWidth,-5*\latWidth)$) {}; % J13 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-1*\latWidth,-5*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($(-0*\latWidth,-5*\latWidth)$) {16}; % H13 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-0*\latWidth,-5*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 1*\latWidth,-5*\latWidth)$) {}; % G13 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 1*\latWidth,-5*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 2*\latWidth,-5*\latWidth)$) {16}; % F13 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 2*\latWidth,-5*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 3*\latWidth,-5*\latWidth)$) {}; % E13 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 3*\latWidth,-5*\latWidth)$) {}; + \node [Assembly, fill=\midenr] at ($( 4*\latWidth,-5*\latWidth)$) {16}; % D13 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 4*\latWidth,-5*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 5*\latWidth,-5*\latWidth)$) {15}; % C13 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 5*\latWidth,-5*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 6*\latWidth,-5*\latWidth)$) {}; % B13 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 6*\latWidth,-5*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 7*\latWidth,-5*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,-5*\latWidth)$) {}; + + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,-6*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-7*\latWidth,-6*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-6*\latWidth,-6*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-5*\latWidth,-6*\latWidth)$) {}; % N14 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-5*\latWidth,-6*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-4*\latWidth,-6*\latWidth)$) {}; % M14 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-4*\latWidth,-6*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-3*\latWidth,-6*\latWidth)$) {16}; % L14 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-3*\latWidth,-6*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-2*\latWidth,-6*\latWidth)$) {}; % K14 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-2*\latWidth,-6*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-1*\latWidth,-6*\latWidth)$) {20}; % J14 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-1*\latWidth,-6*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($(-0*\latWidth,-6*\latWidth)$) {}; % H14 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-0*\latWidth,-6*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 1*\latWidth,-6*\latWidth)$) {20}; % G14 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 1*\latWidth,-6*\latWidth)$) {}; + \node [Assembly, fill=\lowenr] at ($( 2*\latWidth,-6*\latWidth)$) {}; % F14 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 2*\latWidth,-6*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 3*\latWidth,-6*\latWidth)$) {16}; % E14 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 3*\latWidth,-6*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 4*\latWidth,-6*\latWidth)$) {}; % D14 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 4*\latWidth,-6*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 5*\latWidth,-6*\latWidth)$) {}; % C14 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 5*\latWidth,-6*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 6*\latWidth,-6*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 7*\latWidth,-6*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,-6*\latWidth)$) {}; + + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,-7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-7*\latWidth,-7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-6*\latWidth,-7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-5*\latWidth,-7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-4*\latWidth,-7*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-3*\latWidth,-7*\latWidth)$) {}; % L15 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-3*\latWidth,-7*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-2*\latWidth,-7*\latWidth)$) {6}; % K15 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-2*\latWidth,-7*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-1*\latWidth,-7*\latWidth)$) {}; % J15 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-1*\latWidth,-7*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($(-0*\latWidth,-7*\latWidth)$) {6}; % H15 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($(-0*\latWidth,-7*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 1*\latWidth,-7*\latWidth)$) {}; % G15 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 1*\latWidth,-7*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 2*\latWidth,-7*\latWidth)$) {6}; % F15 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 2*\latWidth,-7*\latWidth)$) {}; + \node [Assembly, fill=\highenr] at ($( 3*\latWidth,-7*\latWidth)$) {}; % E15 + \node [Assembly, fill=\darkgray, opacity=0.7] at ($( 3*\latWidth,-7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 4*\latWidth,-7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 5*\latWidth,-7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 6*\latWidth,-7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 7*\latWidth,-7*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,-7*\latWidth)$) {}; + + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-8*\latWidth,-8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-7*\latWidth,-8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-6*\latWidth,-8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-5*\latWidth,-8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-4*\latWidth,-8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-3*\latWidth,-8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-2*\latWidth,-8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-1*\latWidth,-8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($(-0*\latWidth,-8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 1*\latWidth,-8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 2*\latWidth,-8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 3*\latWidth,-8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 4*\latWidth,-8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 5*\latWidth,-8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 6*\latWidth,-8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 7*\latWidth,-8*\latWidth)$) {}; + \node [Assembly, fill=\lightgray, opacity=0.3] at ($( 8*\latWidth,-8*\latWidth)$) {}; + + % draw baffle north/south + + \begin{scope}[even odd rule] + \clip[rotate=90] \tkzBaffMClip; + \path[fill=black] \tkzBaffleC; + \end{scope} + \begin{scope}[even odd rule] + \clip \tkzBaffCClip; + \clip \tkzBaffMClip; + \path[fill=black, rotate=90] \tkzBaffleM; + \end{scope} + + % draw baffle east/west + + \begin{scope}[rotate=90] + \begin{scope}[even odd rule] + \clip[rotate=90] \tkzBaffMClip; + \path[fill=black] \tkzBaffleC; + \end{scope} + \begin{scope}[even odd rule] + \clip \tkzBaffCClip; + \clip \tkzBaffMClip; + \path[fill=black, rotate=90] \tkzBaffleM; + \end{scope} + \end{scope}} + \end{tikzpicture} diff --git a/docs/source/methods/index.rst b/docs/source/methods/index.rst index 0ff4fb1988..1df4f324a3 100644 --- a/docs/source/methods/index.rst +++ b/docs/source/methods/index.rst @@ -16,3 +16,4 @@ Theory and Methodology tallies eigenvalue parallelization + cmfd diff --git a/docs/source/releasenotes/index.rst b/docs/source/releasenotes/index.rst index b40126b243..9799a5cfc7 100644 --- a/docs/source/releasenotes/index.rst +++ b/docs/source/releasenotes/index.rst @@ -10,6 +10,7 @@ bugs fixed, and known issues for each successive release. .. toctree:: :maxdepth: 1 + notes_0.6.1 notes_0.6.0 notes_0.5.4 notes_0.5.3 diff --git a/docs/source/releasenotes/notes_0.6.1.rst b/docs/source/releasenotes/notes_0.6.1.rst new file mode 100644 index 0000000000..71852bd174 --- /dev/null +++ b/docs/source/releasenotes/notes_0.6.1.rst @@ -0,0 +1,65 @@ +.. _notes_0.6.1: + +============================== +Release Notes for OpenMC 0.6.1 +============================== + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions, Mac OS X, +and Microsoft Windows 7. Memory requirements will vary depending on the size of +the problem at hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- Coarse mesh finite difference (CMFD) acceleration no longer requires PETSc +- Statepoint file numbering is now zero-padded +- Python scripts now compatible with Python 2 or 3 +- Ability to run particle restarts in fixed source calculations +- Capability to filter box source by fissionable materials +- Nuclide/element names are now case insensitive in input files +- Improved treatment of resonance scattering for heavy nuclides + +--------- +Bug Fixes +--------- + +- 03e890_: Check for energy-dependent multiplicities in ACE files +- 4439de_: Fix distance-to-surface calculation for general plane surface +- 5808ed_: Account for differences in URR band probabilities at different energies +- 2e60c0_: Allow zero atom/weight percents in materials +- 3e0870_: Don't use PWD environment variable when setting path to input files +- dc4776_: Handle probability table resampling correctly +- 01178b_: Fix metastables nuclides in NNDC cross_sections.xml file +- 62ec43_: Don't read tallies.xml when OpenMC is run in plotting mode +- 2a95ef_: Prevent segmentation fault on "current" score without mesh filter +- 93e482_: Check for negative values in probability tables + +.. _03e890: https://github.com/mit-crpg/openmc/commit/03e890 +.. _4439de: https://github.com/mit-crpg/openmc/commit/4439de +.. _5808ed: https://github.com/mit-crpg/openmc/commit/5808ed +.. _2e60c0: https://github.com/mit-crpg/openmc/commit/2e60c0 +.. _3e0870: https://github.com/mit-crpg/openmc/commit/3e0870 +.. _dc4776: https://github.com/mit-crpg/openmc/commit/dc4776 +.. _01178b: https://github.com/mit-crpg/openmc/commit/01178b +.. _62ec43: https://github.com/mit-crpg/openmc/commit/62ec43 +.. _2a95ef: https://github.com/mit-crpg/openmc/commit/2a95ef +.. _93e482: https://github.com/mit-crpg/openmc/commit/93e482 + +------------ +Contributors +------------ + +This release contains new contributions from the following people: + +- `Sterling Harper `_ +- `Bryan Herman `_ +- `Adam Nelson `_ +- `Paul Romano `_ +- `Jon Walsh `_ +- `Will Boyd `_ diff --git a/docs/source/usersguide/input.rst b/docs/source/usersguide/input.rst index de3cbc7f23..f7e70ef209 100644 --- a/docs/source/usersguide/input.rst +++ b/docs/source/usersguide/input.rst @@ -261,7 +261,7 @@ or sub-elements and can be set to either "false" or "true". *Default*: true ```` Element ----------------------- +---------------------------------- The ``resonance_scattering`` element can contain one or more of the following attributes or sub-elements: @@ -269,7 +269,7 @@ attributes or sub-elements: :scatterer: An element with attributes/sub-elements called ``nuclide``, ``method``, ``xs_label``, ``xs_label_0K``, ``E_min``, and ``E_max``. The ``nuclide`` - attribute is the name, as given by the ``name`` attribute within the + attribute is the name, as given by the ``name`` attribute within the ``nuclide`` sub-element of the ``material`` element in ``materials.xml``, of the nuclide to which a resonance scattering treatment is to be applied. The ``method`` attribute gives the type of resonance scattering treatment @@ -433,6 +433,13 @@ attributes/sub-elements: *Default*: 0.988 2.249 + :write_initial: + An element specifying whether to write out the initial source bank used at + the beginning of the first batch. The output file is named + "initial_source.binary(h5)" + + *Default*: false + ```` Element ------------------------- @@ -1085,10 +1092,10 @@ The ```` element accepts the following sub-elements: all of the harmonic moments of order 0 to N. N must be between 0 and 10. :total-YN: - Spherical harmonic expansion of the incoming particle's direction of - motion :math:`\left(\Omega\right)` of the total flux. This score will - tally all of the harmonic moments of order 0 to N. N must be between 0 - and 10. + The total reaction rate expanded via spherical harmonics about the + direction of motion of the neutron, :math:`\Omega`. + This score will tally all of the harmonic moments of order 0 to N. N must + be between 0 and 10. :current: Partial currents on the boundaries of each cell in a mesh. @@ -1222,7 +1229,7 @@ sub-elements: attribute or sub-element: :pixels: - Specifies the number of pixes or voxels to be used along each of the basis + Specifies the number of pixels or voxels to be used along each of the basis directions for "slice" and "voxel" plots, respectively. Should be two or three integers separated by spaces. @@ -1254,7 +1261,7 @@ attributes or sub-elements. These are not used in "voxel" plots: Specifies the RGB color of the regions where no OpenMC cell can be found. Should be three integers separated by spaces. - *Default*: 0 0 0 (white) + *Default*: 0 0 0 (black) :col_spec: Any number of this optional tag may be included in each ```` element, @@ -1294,6 +1301,37 @@ attributes or sub-elements. These are not used in "voxel" plots: *Default*: None + :meshlines: + The ``meshlines`` sub-element allows for plotting the boundaries of + a tally mesh on top of a plot. Only one ``meshlines`` element is allowed per + ``plot`` element, and it must contain as attributes or sub-elements a mesh + type and a linewidth. Optionally, a color may be specified for the overlay: + + :meshtype: + The type of the mesh to be plotted. Valid options are "tally", "entropy", + "ufs", and "cmfd". If plotting "tally" meshes, the id of the mesh to plot + must be specified with the ``id`` sub-element. + + :id: + A single integer id number for the mesh specified on ``tallies.xml`` that + should be plotted. This element is only required for ``meshtype="tally"``. + + :linewidth: + A single integer number of pixels of linewidth to specify for the mesh + boundaries. Specifying this as 0 indicates that lines will be 1 pixel + thick, specifying 1 indicates 3 pixels thick, specifying 2 indicates + 5 pixels thick, etc. + + :color: + Specifies the custom color for the meshlines boundaries. Should be 3 + integers separated by whitespace. This element is optional. + + *Default*: 0 0 0 (black) + + *Default*: None + +.. _usersguide_cmfd: + ------------------------------ CMFD Specification -- cmfd.xml ------------------------------ @@ -1303,15 +1341,6 @@ Currently, it allows users to accelerate fission source convergence during inactive neutron batches. To run CMFD, the ```` element in ``settings.xml`` should be set to "true". -```` Element --------------------------- - -The ```` element controls the batch where CMFD tallies should be -reset. CMFD tallies should be reset before active batches so they are accumulated -without bias. - - *Default*: 0 - ```` Element ------------------- @@ -1333,7 +1362,25 @@ The ```` element sets one additional CMFD output column. Options are: * "source" - prints the RMS [%] between the OpenMC fission source and CMFD fission source. - *Default*: None + *Default*: balance + +```` Element +------------------------ + +The ```` element controls whether :math:`\widehat{D}` nonlinear +CMFD parameters should be reset to zero before solving CMFD eigenproblem. +It can be turned on with "true" and off with "false". + + *Default*: false + +```` Element +------------------------- + +The ```` element controls whether an effective downscatter cross +section should be used when using 2-group CMFD. It can be turned on with "true" +and off with "false". + + *Default*: false ```` Element ---------------------- @@ -1344,24 +1391,16 @@ It can be turned on with "true" and off with "false". *Default*: false -```` Element ----------------------- +```` Element +------------------------------------ -The ```` element controls if cmfd tallies should be accumulated -during inactive batches. For some applications, CMFD tallies may not be -needed until the start of active batches. This option can be turned on -with "true" and off with "false" +The ```` element specifies two parameters. The first is +the absolute inner tolerance for Gauss-Seidel iterations when performing CMFD +and the second is the relative inner tolerance for Gauss-Seidel iterations +for CMFD calculations. It is only used in the standalone CMFD power iteration +solver and not when PETSc is active. - *Default*: true - -```` Element ----------------------------- - -The ```` element controls when CMFD tallies are reset during -inactive batches. The integer set here is the interval at which this reset -occurs. The amout of resets is controlled with the ```` element. - - *Defualt*: 9999 + *Default*: 1.e-10 1.e-5 ```` Element ------------------------- @@ -1370,9 +1409,16 @@ The ```` element is used to view the convergence of linear GMRES iterations in PETSc. This option can be turned on with "true" and turned off with "false". - *Default*: false +```` Element +-------------------- + +The ```` element specifies the tolerance on the eigenvalue when performing +CMFD power iteration. + + *Default*: 1.e-8 + ```` Element ------------------ @@ -1441,14 +1487,6 @@ not impact the calculation. *Default*: 1.0 -```` Element -------------------------- - -The ```` element controls the number of CMFD tally resets that -occur during inactive CMFD batches. - - *Default*: 9999 - ```` Element --------------------------- @@ -1461,16 +1499,8 @@ This option can be turned on with "true" and turned off with "false". ------------------------- The ```` element can be turned on with "true" to have an adjoint -calculation be performed on the last batch when CMFD is active. - - *Default*: false - -```` Element --------------------------- - -The ```` element is used to view the convergence of the nonlinear SNES -function in PETSc. This option can be turned on with "true" and turned off with "false". - +calculation be performed on the last batch when CMFD is active. OpenMC should be +compiled with PETSc when using this option. *Default*: false @@ -1483,6 +1513,41 @@ By setting "power", power iteration is used and by setting "jfnk", JFNK is used. *Default*: power +```` Element +-------------------- + +The ```` element specifies an optional Wielandt shift parameter for +accelerating power iterations. It can only be used when PETSc is not active. +It is by default very large so the impact of the shift is effectively zero. + + *Default*: 1e6 + +```` Element +---------------------- + +The ```` element specifies an optional spectral radius that can be set to +accelerate the convergence of Gauss-Seidel iterations during CMFD power iteration +solve. Note this is only used in the standalone CMFD solver and does not affect +the calculation when PETSc is active. + + *Default*: power + +```` Element +------------------ + +The ```` element specifies the tolerance on the fission source when performing +CMFD power iteration. + + *Default*: 1.e-8 + +```` Element +------------------------- + +The ```` element contains a list of batch numbers in which CMFD tallies +should be reset. + + *Default*: None + ```` Element ---------------------------- diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 358937e303..33e5936586 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -239,20 +239,22 @@ the root directory of the source code: .. code-block:: sh - cd src + mkdir src/build + cd src/build + cmake .. make - sudo make install + make install This will build an executable named ``openmc`` and install it (by default in /usr/local/bin). If you do not have administrative privileges, you can install -OpenMC locally by replacing the last command with: +OpenMC locally by specifying an install prefix when running cmake: .. code-block:: sh - make install -e prefix=$HOME/.local + cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local .. -The ``prefix`` variable can be changed to any path for which you have -write-access. +The ``CMAKE_INSTALL_PREFIX`` variable can be changed to any path for which you +have write-access. Compiling on Windows -------------------- @@ -326,7 +328,7 @@ Testing Build ------------- If you have ENDF/B-VII.1 cross sections from NNDC_ you can test your build. -Make sure the **CROSS_SECTIONS** environmental variable is set to the +Make sure the **CROSS_SECTIONS** environmental variable is set to the *cross_sections.xml* file in the *data/nndc* directory. There are two ways to run tests. The first is to use the Makefile present in the source directory and run the following: diff --git a/examples/lattice/simple/plots.xml b/examples/lattice/simple/plots.xml index a8c6fbb7c5..a25a5a8280 100644 --- a/examples/lattice/simple/plots.xml +++ b/examples/lattice/simple/plots.xml @@ -5,6 +5,7 @@ 0. 0. 0. 4.0 4.0 400 400 + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fc6c6630cc..bf657c03de 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -261,7 +261,8 @@ install(FILES ../LICENSE DESTINATION "share/doc/${program}/copyright") find_package(PythonInterp) if(PYTHONINTERP_FOUND) install(CODE "execute_process( - COMMAND ${PYTHON_EXECUTABLE} setup.py install --user + COMMAND ${PYTHON_EXECUTABLE} setup.py install + --prefix=${CMAKE_INSTALL_PREFIX} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/utils)") endif() @@ -277,7 +278,7 @@ file(GLOB_RECURSE TESTS ${CMAKE_CURRENT_SOURCE_DIR}/../tests/test_*.py) # Check to see if PETSC is compiled for CMFD tests if (NOT ${PETSC_ENABLED}) - file(GLOB_RECURSE CMFD_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/../tests/test_cmfd*.py) + file(GLOB_RECURSE CMFD_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/../tests/test_cmfd_jfnk.py) foreach(cmfd_test in ${CMFD_TESTS}) list(REMOVE_ITEM TESTS ${cmfd_test}) endforeach(cmfd_test) diff --git a/src/ace.F90 b/src/ace.F90 index fb42180da0..584254149d 100644 --- a/src/ace.F90 +++ b/src/ace.F90 @@ -7,21 +7,21 @@ module ace use error, only: fatal_error, warning use fission, only: nu_total use global - use list_header, only: ListElemInt, ListInt + use list_header, only: ListInt use material_header, only: Material use output, only: write_message use set_header, only: SetChar - use string, only: to_str + use string, only: to_str, to_lower implicit none - integer :: NXS(16) ! Descriptors for ACE XSS tables - integer :: JXS(32) ! Pointers into ACE XSS tables - real(8), allocatable :: XSS(:) ! Cross section data - integer :: XSS_index ! current index in XSS data + integer :: JXS(32) ! Pointers into ACE XSS tables + integer :: NXS(16) ! Descriptors for ACE XSS tables + real(8), allocatable :: XSS(:) ! Cross section data + integer :: XSS_index ! Current index in XSS data - private :: NXS private :: JXS + private :: NXS private :: XSS contains @@ -68,8 +68,8 @@ contains name = mat % names(j) if (.not. already_read % contains(name)) then - i_listing = xs_listing_dict % get_key(name) - i_nuclide = nuclide_dict % get_key(name) + i_listing = xs_listing_dict % get_key(to_lower(name)) + i_nuclide = nuclide_dict % get_key(to_lower(name)) name = xs_listings(i_listing) % name alias = xs_listings(i_listing) % alias @@ -116,8 +116,8 @@ contains name = mat % sab_names(k) if (.not. already_read % contains(name)) then - i_listing = xs_listing_dict % get_key(name) - i_sab = sab_dict % get_key(name) + i_listing = xs_listing_dict % get_key(to_lower(name)) + i_sab = sab_dict % get_key(to_lower(name)) ! Read the ACE table into the appropriate entry on the sab_tables ! array @@ -151,9 +151,9 @@ contains ! Check to make sure S(a,b) table matched a nuclide if (mat % i_sab_nuclides(k) == NONE) then - message = "S(a,b) table " // trim(mat % sab_names(k)) // " did not & - &match any nuclide on material " // trim(to_str(mat % id)) - call fatal_error() + call fatal_error("S(a,b) table " // trim(mat % sab_names(k)) & + &// " did not match any nuclide on material " & + &// trim(to_str(mat % id))) end if end do ASSIGN_SAB @@ -264,17 +264,14 @@ contains ! Check if ACE library exists and is readable inquire(FILE=filename, EXIST=file_exists, READ=readable) if (.not. file_exists) then - message = "ACE library '" // trim(filename) // "' does not exist!" - call fatal_error() + call fatal_error("ACE library '" // trim(filename) // "' does not exist!") elseif (readable(1:3) == 'NO') then - message = "ACE library '" // trim(filename) // "' is not readable! & - &Change file permissions with chmod command." - call fatal_error() + call fatal_error("ACE library '" // trim(filename) // "' is not readable!& + & Change file permissions with chmod command.") end if ! display message - message = "Loading ACE cross section table: " // listing % name - call write_message(6) + call write_message("Loading ACE cross section table: " // listing % name, 6) if (filetype == ASCII) then ! ======================================================================= @@ -293,9 +290,8 @@ contains ! Check that correct xs was found -- if cross_sections.xml is broken, the ! location of the table may be wrong if(adjustl(name) /= adjustl(listing % name)) then - message = "XS listing entry " // trim(listing % name) // " did not & - &match ACE data, " // trim(name) // " found instead." - call fatal_error() + call fatal_error("XS listing entry " // trim(listing % name) // " did & + ¬ match ACE data, " // trim(name) // " found instead.") end if ! Read more header and NXS and JXS @@ -377,8 +373,8 @@ contains ! if any fissionable material is found in a fixed source calculation, ! abort the run. if (run_mode == MODE_FIXEDSOURCE .and. nuc % fissionable) then - message = "Cannot have fissionable material in a fixed source run." - call fatal_error() + call fatal_error("Cannot have fissionable material in a fixed source & + &run.") end if ! for fissionable nuclides, precalculate microscopic nu-fission cross @@ -1315,9 +1311,8 @@ contains ! Abort if no corresponding inelastic reaction was found if (nuc % urr_inelastic == NONE) then - message = "Could not find inelastic reaction specified on " & - // "unresolved resonance probability table." - call fatal_error() + call fatal_error("Could not find inelastic reaction specified on & + &unresolved resonance probability table.") end if end if @@ -1343,9 +1338,8 @@ contains ! Check for negative values if (any(nuc % urr_data % prob < ZERO)) then - message = "Negative value(s) found on probability table for nuclide " & - // nuc % name - call warning() + if (master) call warning("Negative value(s) found on probability table & + &for nuclide " // nuc % name) end if end subroutine read_unr_res @@ -1563,16 +1557,11 @@ contains integer :: i ! index in nuclides array integer :: j ! index in nuclides array - type(ListElemInt), pointer :: nuc_list => null() ! pointer to nuclide list do i = 1, n_nuclides_total - allocate(nuclides(i) % nuc_list) - nuc_list => nuclides(i) % nuc_list do j = 1, n_nuclides_total if (nuclides(i) % zaid == nuclides(j) % zaid) then - nuc_list % data = j - allocate(nuc_list % next) - nuc_list => nuc_list % next + call nuclides(i) % nuc_list % append(j) end if end do end do diff --git a/src/ace_header.F90 b/src/ace_header.F90 index 6d339891c9..76492dfe80 100644 --- a/src/ace_header.F90 +++ b/src/ace_header.F90 @@ -2,7 +2,7 @@ module ace_header use constants, only: MAX_FILE_LEN use endf_header, only: Tab1 - use list_header, only: ListElemInt + use list_header, only: ListInt implicit none @@ -97,7 +97,7 @@ module ace_header real(8) :: kT ! temperature in MeV (k*T) ! Linked list of indices in nuclides array of instances of this same nuclide - type(ListElemInt), pointer :: nuc_list => null() + type(ListInt) :: nuc_list ! Energy grid information integer :: n_grid ! # of nuclide grid points @@ -114,7 +114,7 @@ module ace_header ! Resonance scattering info logical :: resonant = .false. ! resonant scatterer? - character(10) :: name_0K ! name of 0K nuclide, e.g. 92235.00c + character(10) :: name_0K = '' ! name of 0K nuclide, e.g. 92235.00c character(16) :: scheme ! target velocity sampling scheme integer :: n_grid_0K ! number of 0K energy grid points real(8), allocatable :: energy_0K(:) ! energy grid for 0K xs @@ -416,6 +416,8 @@ module ace_header deallocate(this % reactions) end if + call this % nuc_list % clear() + end subroutine nuclide_clear end module ace_header diff --git a/src/cmfd_data.F90 b/src/cmfd_data.F90 index b5612dcd05..b392e86a28 100644 --- a/src/cmfd_data.F90 +++ b/src/cmfd_data.F90 @@ -5,13 +5,12 @@ module cmfd_data ! parameters for CMFD calculation. !============================================================================== + use constants implicit none private public :: set_up_cmfd, neutron_balance - logical :: dhat_reset = .false. - contains !============================================================================== @@ -55,7 +54,7 @@ contains OUT_FRONT, IN_TOP, OUT_TOP, CMFD_NOACCEL, ZERO, & ONE, TINY_BIT use error, only: fatal_error - use global, only: cmfd, message, n_cmfd_tallies, cmfd_tallies, meshes,& + use global, only: cmfd, n_cmfd_tallies, cmfd_tallies, meshes,& matching_bins use mesh, only: mesh_indices_to_bin use mesh_header, only: StructuredMesh @@ -103,6 +102,8 @@ contains cmfd % hxyz(2,:,:,:) = m % width(2) ! set y width cmfd % hxyz(3,:,:,:) = m % width(3) ! set z width + cmfd % keff_bal = ZERO + ! Begin loop around tallies TAL: do ital = 1, n_cmfd_tallies @@ -159,10 +160,9 @@ contains ! Detect zero flux, abort if located if ((flux - ZERO) < TINY_BIT) then - message = 'Detected zero flux without coremap overlay at: (' & - // to_str(i) // ',' // to_str(j) // ',' // to_str(k) & - // ') in group ' // to_str(h) - call fatal_error() + call fatal_error('Detected zero flux without coremap overlay & + &at: (' // to_str(i) // ',' // to_str(j) // ',' // & + &to_str(k) // ') in group ' // to_str(h)) end if ! Get total rr and convert to total xs @@ -211,6 +211,9 @@ contains ! Bank source cmfd % openmc_src(g,i,j,k) = cmfd % openmc_src(g,i,j,k) + & t % results(2,score_index) % sum + cmfd % keff_bal = cmfd % keff_bal + & + t % results(2,score_index) % sum / & + dble(t % n_realizations) end do INGROUP @@ -623,7 +626,9 @@ contains subroutine compute_dhat() use constants, only: CMFD_NOACCEL, ZERO - use global, only: cmfd, cmfd_coremap + use global, only: cmfd, cmfd_coremap, dhat_reset + use output, only: write_message + use string, only: to_str integer :: nx ! maximum number of cells in x direction integer :: ny ! maximum number of cells in y direction @@ -743,7 +748,9 @@ contains cmfd%dhat(l,g,i,j,k) = dhat ! check for dhat reset - if (dhat_reset) cmfd%dhat(l,g,i,j,k) = ZERO + if (dhat_reset) then + cmfd%dhat(l,g,i,j,k) = ZERO + end if end do LEAK @@ -755,6 +762,11 @@ contains end do ZLOOP + ! write that dhats are zero + if (dhat_reset) then + call write_message('Dhats reset to zero.', 1) + end if + end subroutine compute_dhat !=============================================================================== @@ -763,8 +775,8 @@ contains function get_reflector_albedo(l, g, i, j, k) - use constants, only: ALBEDO_REJECT - use global, only: cmfd, cmfd_hold_weights + use constants, only: ONE + use global, only: cmfd real(8) :: get_reflector_albedo ! reflector albedo integer, intent(in) :: i ! iteration counter for x @@ -786,8 +798,7 @@ contains ! Calculate albedo if ((shift_idx == 1 .and. current(2*l ) < 1.0e-10_8) .or. & (shift_idx == -1 .and. current(2*l-1) < 1.0e-10_8)) then - albedo = ALBEDO_REJECT - cmfd_hold_weights = .true. + albedo = ONE else albedo = (current(2*l-1)/current(2*l))**(shift_idx) end if @@ -797,137 +808,6 @@ contains end function get_reflector_albedo -!=============================================================================== -! FIX_NEUTRON_BALANCE is a method to adjust parameters to have perfect balance -!=============================================================================== -#ifdef DEVELOPMENTAL - subroutine fix_neutron_balance() - - use constants, only: ONE, ZERO, CMFD_NOACCEL - use global, only: cmfd, keff - use, intrinsic :: ISO_FORTRAN_ENV - - integer :: nx ! number of mesh cells in x direction - integer :: ny ! number of mesh cells in y direction - integer :: nz ! number of mesh cells in z direction - integer :: ng ! number of energy groups - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: l ! iteration counter for surface - real(8) :: leak1 ! leakage rate in group 1 - real(8) :: leak2 ! leakage rate in group 2 - real(8) :: flux1 ! group 1 volume int flux - real(8) :: flux2 ! group 2 volume int flux - real(8) :: sigt1 ! group 1 total xs - real(8) :: sigt2 ! group 2 total xs - real(8) :: sigs11 ! scattering transfer 1 --> 1 - real(8) :: sigs21 ! scattering transfer 2 --> 1 - real(8) :: sigs12 ! scattering transfer 1 --> 2 - real(8) :: sigs22 ! scattering transfer 2 --> 2 - real(8) :: nsigf11 ! fission transfer 1 --> 1 - real(8) :: nsigf21 ! fission transfer 2 --> 1 - real(8) :: nsigf12 ! fission transfer 1 --> 2 - real(8) :: nsigf22 ! fission transfer 2 --> 2 - real(8) :: siga1 ! group 1 abs xs - real(8) :: siga2 ! group 2 abs xs - real(8) :: sigs12_eff ! effective downscatter xs - - ! Extract spatial and energy indices from object - nx = cmfd % indices(1) - ny = cmfd % indices(2) - nz = cmfd % indices(3) - ng = cmfd % indices(4) - - ! Return if not two groups - if (ng /= 2) return - - ! Begin loop around space and energy groups - ZLOOP: do k = 1, nz - - YLOOP: do j = 1, ny - - XLOOP: do i = 1, nx - - ! Check for active mesh - if (allocated(cmfd%coremap)) then - if (cmfd%coremap(i,j,k) == CMFD_NOACCEL) cycle - end if - - ! Compute leakage in groups 1 and 2 - leak1 = ZERO - leak2 = ZERO - LEAK: do l = 1, 3 - - leak1 = leak1 + ((cmfd % current(4*l,1,i,j,k) - & - cmfd % current(4*l-1,1,i,j,k))) - & - ((cmfd % current(4*l-2,1,i,j,k) - & - cmfd % current(4*l-3,1,i,j,k))) - - leak2 = leak2 + ((cmfd % current(4*l,2,i,j,k) - & - cmfd % current(4*l-1,2,i,j,k))) - & - ((cmfd % current(4*l-2,2,i,j,k) - & - cmfd % current(4*l-3,2,i,j,k))) - - - end do LEAK - - ! Extract cross sections and flux from object - flux1 = cmfd % flux(1,i,j,k) - flux2 = cmfd % flux(2,i,j,k) - sigt1 = cmfd % totalxs(1,i,j,k) - sigt2 = cmfd % totalxs(2,i,j,k) - sigs11 = cmfd % scattxs(1,1,i,j,k) - sigs21 = cmfd % scattxs(2,1,i,j,k) - sigs12 = cmfd % scattxs(1,2,i,j,k) - sigs22 = cmfd % scattxs(2,2,i,j,k) - nsigf11 = cmfd % nfissxs(1,1,i,j,k) - nsigf21 = cmfd % nfissxs(2,1,i,j,k) - nsigf12 = cmfd % nfissxs(1,2,i,j,k) - nsigf22 = cmfd % nfissxs(2,2,i,j,k) - - ! Check for no fission into group 2 - if (.not.(nsigf12 < 1e-6_8 .and. nsigf22 < 1e-6_8)) then - write(OUTPUT_UNIT,'(A,1PE11.4,1X,1PE11.4)') 'Fission in G=2', & - nsigf12,nsigf22 - end if - - ! Compute absorption xs - siga1 = sigt1 - sigs11 - sigs12 - siga2 = sigt2 - sigs22 - sigs21 - - ! Compute effective downscatter xs - sigs12_eff = (ONE/keff*nsigf11*flux1 - leak1 - siga1*flux1 & - - ONE/keff*nsigf21/siga2*leak2 ) / ( flux1*(ONE & - - ONE/keff*nsigf21/siga2)) - - ! Redefine flux 2 - flux2 = (sigs12_eff*flux1 - leak2)/siga2 - cmfd % flux(2,i,j,k) = flux2 - - ! Recompute total cross sections (use effective and no upscattering) - sigt1 = siga1 + sigs11 + sigs12_eff - sigt2 = siga2 + sigs22 - - ! Record total xs - cmfd % totalxs(1,i,j,k) = sigt1 - cmfd % totalxs(2,i,j,k) = sigt2 - - ! Record effective downscatter xs - cmfd % scattxs(1,2,i,j,k) = sigs12_eff - - ! Zero out upscatter cross section - cmfd % scattxs(2,1,i,j,k) = ZERO - - end do XLOOP - - end do YLOOP - - end do ZLOOP - - end subroutine fix_neutron_balance -#endif - !=============================================================================== ! COMPUTE_EFFECTIVE_DOWNSCATTER changes downscatter rate for zero upscatter !=============================================================================== diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 index 6234d9fcf9..8e0eb3fda0 100644 --- a/src/cmfd_execute.F90 +++ b/src/cmfd_execute.F90 @@ -22,6 +22,7 @@ contains use cmfd_data, only: set_up_cmfd use cmfd_power_solver, only: cmfd_power_execute use cmfd_jfnk_solver, only: cmfd_jfnk_execute + use cmfd_solver, only: cmfd_solver_execute use error, only: warning, fatal_error ! CMFD single processor on master @@ -37,14 +38,17 @@ contains call process_cmfd_options() ! Call solver +#ifdef PETSC if (trim(cmfd_solver_type) == 'power') then call cmfd_power_execute() elseif (trim(cmfd_solver_type) == 'jfnk') then call cmfd_jfnk_execute() else - message = 'solver type became invalid after input processing' - call fatal_error() + call fatal_error('solver type became invalid after input processing') end if +#else + call cmfd_solver_execute() +#endif ! Save k-effective cmfd % k_cmfd(current_batch) = cmfd % keff @@ -64,7 +68,7 @@ contains call calc_fission_source() ! calculate weight factors - if (cmfd_feedback) call cmfd_reweight(.true.) + call cmfd_reweight(.true.) ! stop cmfd timer if (master) call time_cmfd % stop() @@ -77,36 +81,23 @@ contains subroutine cmfd_init_batch() - use global, only: cmfd_begin, cmfd_on, cmfd_tally_on, & - cmfd_inact_flush, cmfd_act_flush, cmfd_run, & - current_batch, cmfd_hold_weights + use global, only: cmfd_begin, cmfd_on, & + cmfd_reset, cmfd_run, & + current_batch ! Check to activate CMFD diffusion and possible feedback ! this guarantees that when cmfd begins at least one batch of tallies are ! accumulated if (cmfd_run .and. cmfd_begin == current_batch) then cmfd_on = .true. - cmfd_tally_on = .true. end if ! If this is a restart run and we are just replaying batches leave if (restart_run .and. current_batch <= restart_batch) return - ! Check to flush cmfd tallies for active batches, no more inactive flush - if (cmfd_run .and. cmfd_act_flush == current_batch) then + ! Check to reset tallies + if (cmfd_run .and. cmfd_reset % contains(current_batch)) then call cmfd_tally_reset() - cmfd_tally_on = .true. - cmfd_inact_flush(2) = -1 - end if - - ! Check to flush cmfd tallies during inactive batches (>= on number of - ! flushes important as the code will flush on the first batch which we - ! dont want to count) - if (cmfd_run .and. mod(current_batch,cmfd_inact_flush(1)) & - == 0 .and. cmfd_inact_flush(2) > 0 .and. cmfd_begin < current_batch) then - cmfd_hold_weights = .true. - call cmfd_tally_reset() - cmfd_inact_flush(2) = cmfd_inact_flush(2) - 1 end if end subroutine cmfd_init_batch @@ -139,6 +130,7 @@ contains use constants, only: CMFD_NOACCEL, ZERO, TWO use global, only: cmfd, cmfd_coremap, master, entropy_on, current_batch + use string, only: to_str #ifdef MPI use global, only: mpi_err @@ -262,11 +254,12 @@ contains use constants, only: ZERO, ONE use error, only: warning, fatal_error - use global, only: meshes, source_bank, work, n_user_meshes, message, & - cmfd, master + use global, only: meshes, source_bank, work, n_user_meshes, cmfd, & + master use mesh_header, only: StructuredMesh use mesh, only: count_bank_sites, get_mesh_indices use search, only: binary_search + use string, only: to_str #ifdef MPI use global, only: mpi_err @@ -287,7 +280,6 @@ contains logical :: in_mesh ! source site is inside mesh type(StructuredMesh), pointer :: m ! point to mesh - real(8), allocatable :: egrid(:) ! energy grid ! Associate pointer m => meshes(n_user_meshes + 1) @@ -308,24 +300,20 @@ contains cmfd % weightfactors = ONE end if - ! Allocate energy grid and reverse cmfd energy grid - if (.not. allocated(egrid)) allocate(egrid(ng + 1)) - egrid = (/(cmfd % egrid(ng - i + 2), i = 1, ng + 1)/) - ! Compute new weight factors if (new_weights) then - ! Zero out weights - cmfd%weightfactors = ZERO + ! Set weight factors to a default 1.0 + cmfd%weightfactors = ONE - ! Count bank sites in mesh - call count_bank_sites(m, source_bank, cmfd%sourcecounts, egrid, & + ! Count bank sites in mesh and reverse due to egrid structure + call count_bank_sites(m, source_bank, cmfd%sourcecounts, cmfd % egrid, & sites_outside=outside, size_bank=work) + cmfd % sourcecounts = cmfd%sourcecounts(ng:1:-1,:,:,:) ! Check for sites outside of the mesh if (master .and. outside) then - message = "Source sites outside of the CMFD mesh!" - call fatal_error() + call fatal_error("Source sites outside of the CMFD mesh!") end if ! Have master compute weight factors (watch for 0s) @@ -336,12 +324,14 @@ contains end where end if + if (.not. cmfd_feedback) return + ! Broadcast weight factors to all procs #ifdef MPI call MPI_BCAST(cmfd % weightfactors, ng*nx*ny*nz, MPI_REAL8, 0, & MPI_COMM_WORLD, mpi_err) #endif - end if + end if ! begin loop over source bank do i = 1, int(work,4) @@ -353,12 +343,10 @@ contains n_groups = size(cmfd % egrid) - 1 if (source_bank(i) % E < cmfd % egrid(1)) then e_bin = 1 - message = 'Source pt below energy grid' - call warning() + if (master) call warning('Source pt below energy grid') elseif (source_bank(i) % E > cmfd % egrid(n_groups + 1)) then e_bin = n_groups - message = 'Source pt above energy grid' - call warning() + if (master) call warning('Source pt above energy grid') else e_bin = binary_search(cmfd % egrid, n_groups + 1, source_bank(i) % E) end if @@ -368,8 +356,7 @@ contains ! Check for outside of mesh if (.not. in_mesh) then - message = 'Source site found outside of CMFD mesh' - call fatal_error() + call fatal_error('Source site found outside of CMFD mesh') end if ! Reweight particle @@ -378,9 +365,6 @@ contains end do - ! Deallocate all - if (allocated(egrid)) deallocate(egrid) - end subroutine cmfd_reweight !=============================================================================== @@ -421,15 +405,14 @@ contains subroutine cmfd_tally_reset() - use global, only: n_cmfd_tallies, cmfd_tallies, message + use global, only: n_cmfd_tallies, cmfd_tallies use output, only: write_message use tally, only: reset_result integer :: i ! loop counter ! Print message - message = "CMFD tallies reset" - call write_message(7) + call write_message("CMFD tallies reset", 7) ! Begin loop around CMFD tallies do i = 1, n_cmfd_tallies diff --git a/src/cmfd_header.F90 b/src/cmfd_header.F90 index 73bc0e0163..c9bc0f32af 100644 --- a/src/cmfd_header.F90 +++ b/src/cmfd_header.F90 @@ -83,6 +83,9 @@ module cmfd_header ! List of CMFD k real(8), allocatable :: k_cmfd(:) + ! Balance keff + real(8) :: keff_bal + end type cmfd_type contains diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index 7e6ca18e53..578ecb9678 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -62,16 +62,20 @@ contains use error, only: fatal_error, warning use global use output, only: write_message - use string, only: lower_case + use string, only: to_lower use xml_interface use, intrinsic :: ISO_FORTRAN_ENV + integer :: i integer :: ng + integer :: n_params integer, allocatable :: iarray(:) + integer, allocatable :: int_array(:) logical :: file_exists ! does cmfd.xml exist? logical :: found character(MAX_LINE_LEN) :: filename character(MAX_LINE_LEN) :: temp_str + real(8) :: gs_tol(2) type(Node), pointer :: doc => null() type(Node), pointer :: node_mesh => null() @@ -81,15 +85,14 @@ contains if (.not. file_exists) then ! CMFD is optional unless it is in on from settings if (cmfd_on) then - message = "No CMFD XML file, '" // trim(filename) // "' does not exist!" - call fatal_error() + call fatal_error("No CMFD XML file, '" // trim(filename) // "' does not& + & exist!") end if return else ! Tell user - message = "Reading CMFD XML file..." - call write_message(5) + call write_message("Reading CMFD XML file...", 5) end if @@ -101,8 +104,7 @@ contains ! Check if mesh is there if (.not.found) then - message = "No CMFD mesh specified in CMFD XML file." - call fatal_error() + call fatal_error("No CMFD mesh specified in CMFD XML file.") end if ! Set spatial dimensions in cmfd object @@ -133,8 +135,7 @@ contains cmfd % indices(3))) if (get_arraysize_integer(node_mesh, "map") /= & product(cmfd % indices(1:3))) then - message = 'FATAL==>CMFD coremap not to correct dimensions' - call fatal_error() + call fatal_error('CMFD coremap not to correct dimensions') end if allocate(iarray(get_arraysize_integer(node_mesh, "map"))) call get_node_array(node_mesh, "map", iarray) @@ -151,91 +152,119 @@ contains ! Set feedback logical if (check_for_node(doc, "feedback")) then call get_node_value(doc, "feedback", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - cmfd_feedback = .true. + cmfd_feedback = .true. end if ! Set downscatter logical if (check_for_node(doc, "downscatter")) then call get_node_value(doc, "downscatter", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - cmfd_downscatter = .true. + cmfd_downscatter = .true. + end if + + ! Reset dhat parameters + if (check_for_node(doc, "dhat_reset")) then + call get_node_value(doc, "dhat_reset", temp_str) + temp_str = to_lower(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + dhat_reset = .true. end if ! Set the solver type if (check_for_node(doc, "solver")) & - call get_node_value(doc, "solver", cmfd_solver_type) + call get_node_value(doc, "solver", cmfd_solver_type) ! Set monitoring if (check_for_node(doc, "snes_monitor")) then call get_node_value(doc, "snes_monitor", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - cmfd_snes_monitor = .true. + cmfd_snes_monitor = .true. end if if (check_for_node(doc, "ksp_monitor")) then call get_node_value(doc, "ksp_monitor", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - cmfd_ksp_monitor = .true. + cmfd_ksp_monitor = .true. end if if (check_for_node(doc, "power_monitor")) then call get_node_value(doc, "power_monitor", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - cmfd_power_monitor = .true. + cmfd_power_monitor = .true. end if ! Output logicals if (check_for_node(doc, "write_matrices")) then - call get_node_value(doc, "write_matices", temp_str) - call lower_case(temp_str) + call get_node_value(doc, "write_matrices", temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - cmfd_write_matrices = .true. + cmfd_write_matrices = .true. end if ! Run an adjoint calc if (check_for_node(doc, "run_adjoint")) then call get_node_value(doc, "run_adjoint", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & +#ifndef PETSC + call fatal_error('Must use PETSc when running adjoint option.') +#endif cmfd_run_adjoint = .true. end if ! Batch to begin cmfd if (check_for_node(doc, "begin")) & - call get_node_value(doc, "begin", cmfd_begin) + call get_node_value(doc, "begin", cmfd_begin) - ! Tally during inactive batches - if (check_for_node(doc, "inactive")) then - call get_node_value(doc, "inactive", temp_str) - call lower_case(temp_str) - if (trim(temp_str) == 'false' .or. trim(temp_str) == '0') & - cmfd_tally_on = .false. + ! Check for cmfd tally resets + if (check_for_node(doc, "tally_reset")) then + n_cmfd_resets = get_arraysize_integer(doc, "tally_reset") + else + n_cmfd_resets = 0 + end if + if (n_cmfd_resets > 0) then + allocate(int_array(n_cmfd_resets)) + call get_node_array(doc, "tally_reset", int_array) + do i = 1, n_cmfd_resets + call cmfd_reset % add(int_array(i)) + end do + deallocate(int_array) end if - - ! Inactive batch flush window - if (check_for_node(doc, "inactive_flush")) & - call get_node_value(doc, "inactive_flush", cmfd_inact_flush(1)) - if (check_for_node(doc, "num_flushes")) & - call get_node_value(doc, "num_flushes", cmfd_inact_flush(2)) - - ! Last flush before active batches - if (check_for_node(doc, "active_flush")) & - call get_node_value(doc, "active_flush", cmfd_act_flush) ! Get display if (check_for_node(doc, "display")) & - call get_node_value(doc, "display", cmfd_display) + call get_node_value(doc, "display", cmfd_display) if (trim(cmfd_display) == 'dominance' .and. & - trim(cmfd_solver_type) /= 'power') then - message = 'Dominance Ratio only aviable with power iteration solver' - call warning() + trim(cmfd_solver_type) /= 'power') then + if (master) call warning('Dominance Ratio only aviable with power & + &iteration solver') cmfd_display = '' end if + ! Read in spectral radius estimate and tolerances + if (check_for_node(doc, "spectral")) & + call get_node_value(doc, "spectral", cmfd_spectral) + if (check_for_node(doc, "shift")) & + call get_node_value(doc, "shift", cmfd_shift) + if (check_for_node(doc, "ktol")) & + call get_node_value(doc, "ktol", cmfd_ktol) + if (check_for_node(doc, "stol")) & + call get_node_value(doc, "stol", cmfd_stol) + if (check_for_node(doc, "gauss_seidel_tolerance")) then + n_params = get_arraysize_double(doc, "gauss_seidel_tolerance") + if (n_params /= 2) then + call fatal_error('Gauss Seidel tolerance is not 2 parameters & + &(absolute, relative).') + end if + call get_node_array(doc, "gauss_seidel_tolerance", gs_tol) + cmfd_atoli = gs_tol(1) + cmfd_rtoli = gs_tol(2) + end if + ! Create tally objects call create_cmfd_tally(doc) @@ -299,8 +328,7 @@ contains ! Determine number of dimensions for mesh n = get_arraysize_integer(node_mesh, "dimension") if (n /= 2 .and. n /= 3) then - message = "Mesh must be two or three dimensions." - call fatal_error() + call fatal_error("Mesh must be two or three dimensions.") end if m % n_dimension = n @@ -313,9 +341,8 @@ contains ! Check that dimensions are all greater than zero call get_node_array(node_mesh, "dimension", iarray3(1:n)) if (any(iarray3(1:n) <= 0)) then - message = "All entries on the element for a tally mesh & - &must be positive." - call fatal_error() + call fatal_error("All entries on the element for a tally mesh& + & must be positive.") end if ! Read dimensions in each direction @@ -323,42 +350,37 @@ contains ! Read mesh lower-left corner location if (m % n_dimension /= get_arraysize_double(node_mesh, "lower_left")) then - message = "Number of entries on must be the same as & - &the number of entries on ." - call fatal_error() + call fatal_error("Number of entries on must be the same as & + &the number of entries on .") end if call get_node_array(node_mesh, "lower_left", m % lower_left) ! Make sure both upper-right or width were specified if (check_for_node(node_mesh, "upper_right") .and. & check_for_node(node_mesh, "width")) then - message = "Cannot specify both and on a & - &tally mesh." - call fatal_error() + call fatal_error("Cannot specify both and on a & + &tally mesh.") end if ! Make sure either upper-right or width was specified if (.not.check_for_node(node_mesh, "upper_right") .and. & .not.check_for_node(node_mesh, "width")) then - message = "Must specify either and on a & - &tally mesh." - call fatal_error() + call fatal_error("Must specify either and on a & + &tally mesh.") end if if (check_for_node(node_mesh, "width")) then ! Check to ensure width has same dimensions if (get_arraysize_double(node_mesh, "width") /= & get_arraysize_double(node_mesh, "lower_left")) then - message = "Number of entries on must be the same as the & - &number of entries on ." - call fatal_error() + call fatal_error("Number of entries on must be the same as the & + &number of entries on .") end if ! Check for negative widths call get_node_array(node_mesh, "width", rarray3(1:n)) if (any(rarray3(1:n) < ZERO)) then - message = "Cannot have a negative on a tally mesh." - call fatal_error() + call fatal_error("Cannot have a negative on a tally mesh.") end if ! Set width and upper right coordinate @@ -369,17 +391,15 @@ contains ! Check to ensure width has same dimensions if (get_arraysize_double(node_mesh, "upper_right") /= & get_arraysize_double(node_mesh, "lower_left")) then - message = "Number of entries on must be the same as & - &the number of entries on ." - call fatal_error() + call fatal_error("Number of entries on must be the same & + &as the number of entries on .") end if ! Check that upper-right is above lower-left call get_node_array(node_mesh, "upper_right", rarray3(1:n)) if (any(rarray3(1:n) < m % lower_left)) then - message = "The coordinates must be greater than the & - & coordinates on a tally mesh." - call fatal_error() + call fatal_error("The coordinates must be greater than & + &the coordinates on a tally mesh.") end if ! Set upper right coordinate and width @@ -405,9 +425,9 @@ contains ! Set reset property if (check_for_node(doc, "reset")) then call get_node_value(doc, "reset", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - t % reset = .true. + t % reset = .true. end if ! Set up mesh filter diff --git a/src/cmfd_power_solver.F90 b/src/cmfd_power_solver.F90 index 65b60bc891..f80c122b71 100644 --- a/src/cmfd_power_solver.F90 +++ b/src/cmfd_power_solver.F90 @@ -242,7 +242,7 @@ contains subroutine convergence(iter) - use constants, only: ONE, TINY_BIT + use constants, only: ONE, ZERO use global, only: cmfd_power_monitor, master use, intrinsic :: ISO_FORTRAN_ENV @@ -255,7 +255,7 @@ contains kerr = abs(k_o - k_n)/k_n ! Calculate max error in source - where (s_n % val > TINY_BIT) + where (s_n % val > ZERO) serr_v % val = ((s_n % val - s_o % val)/s_n % val)**2 end where serr = sqrt(ONE/dble(s_n % n) * sum(serr_v % val)) diff --git a/src/cmfd_solver.F90 b/src/cmfd_solver.F90 new file mode 100644 index 0000000000..e660c92470 --- /dev/null +++ b/src/cmfd_solver.F90 @@ -0,0 +1,813 @@ +module cmfd_solver + +! This module contains routines to execute the power iteration solver + + use constants, only: MAX_LINE_LEN + use cmfd_loss_operator, only: init_loss_matrix, build_loss_matrix + use cmfd_prod_operator, only: init_prod_matrix, build_prod_matrix + use matrix_header, only: Matrix + use vector_header, only: Vector + + implicit none + private + public :: cmfd_solver_execute + + real(8) :: k_n ! New k-eigenvalue + real(8) :: k_o ! Old k-eigenvalue + real(8) :: k_s ! Shift of eigenvalue + real(8) :: k_ln ! New shifted eigenvalue + real(8) :: k_lo ! Old shifted eigenvalue + real(8) :: norm_n ! Current norm of source vector + real(8) :: norm_o ! Old norm of source vector + real(8) :: kerr ! Error in keff + real(8) :: serr ! Error in source + real(8) :: ktol ! Tolerance on keff + real(8) :: stol ! Tolerance on source + logical :: adjoint_calc ! Run an adjoint calculation + type(Matrix) :: loss ! Cmfd loss matrix + type(Matrix) :: prod ! Cmfd prod matrix + type(Vector) :: phi_n ! New flux vector + type(Vector) :: phi_o ! Old flux vector + type(Vector) :: s_n ! New source vector + type(Vector) :: s_o ! Old flux vector + type(Vector) :: serr_v ! Error in source + + ! CMFD linear solver interface + procedure(linsolve), pointer :: cmfd_linsolver => null() + abstract interface + subroutine linsolve(A, b, x, tol, i) + import :: Matrix + import :: Vector + type(Matrix), intent(inout) :: A + type(Vector), intent(inout) :: b + type(Vector), intent(inout) :: x + real(8), intent(in) :: tol + integer, intent(out) :: i + end subroutine linsolve + end interface + +contains + +!=============================================================================== +! CMFD_SOLVER_EXECUTE sets up and runs power iteration solver for CMFD +!=============================================================================== + + subroutine cmfd_solver_execute(adjoint) + + use global, only: cmfd_adjoint_type, time_cmfdbuild, time_cmfdsolve + + logical, optional, intent(in) :: adjoint ! adjoint calc + + logical :: physical_adjoint = .false. + + ! Check for adjoint execution + adjoint_calc = .false. + if (present(adjoint)) adjoint_calc = adjoint + + ! Check for physical adjoint + if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'physical') & + physical_adjoint = .true. + + ! Start timer for build + call time_cmfdbuild % start() + + ! Initialize matrices and vectors + call init_data(physical_adjoint) + + ! Check for mathematical adjoint calculation + if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'math') & + call compute_adjoint() + + ! Stop timer for build + call time_cmfdbuild % stop() + + ! Begin power iteration + call time_cmfdsolve % start() + call execute_power_iter() + call time_cmfdsolve % stop() + + ! Extract results + call extract_results() + + ! Deallocate data + call finalize() + + end subroutine cmfd_solver_execute + +!=============================================================================== +! INIT_DATA allocates matrices and vectors for CMFD solution +!=============================================================================== + + subroutine init_data(adjoint) + + use constants, only: ONE, ZERO + use global, only: cmfd, cmfd_shift, keff, cmfd_ktol, cmfd_stol, & + cmfd_write_matrices + + logical, intent(in) :: adjoint + + integer :: n ! problem size + real(8) :: guess ! initial guess + real(8) :: dw ! eigenvalue shift + + ! Set up matrices + call init_loss_matrix(loss) + call init_prod_matrix(prod) + + ! Get problem size + n = loss % n + + ! Set up flux vectors + call phi_n % create(n) + call phi_o % create(n) + + ! Set up source vectors + call s_n % create(n) + call s_o % create(n) + call serr_v % create(n) + + ! Set initial guess + guess = ONE + phi_n % val = guess + phi_o % val = guess + k_n = keff + k_o = k_n + dw = cmfd_shift + k_s = k_o + dw + k_ln = ONE/(ONE/k_n - ONE/k_s) + k_lo = k_ln + + ! Fill in loss matrix + call build_loss_matrix(loss, adjoint=adjoint) + + ! Fill in production matrix + call build_prod_matrix(prod, adjoint=adjoint) + + ! Finalize setup of CSR matrices + call loss % assemble() + call prod % assemble() + if (cmfd_write_matrices) then + call loss % write('loss.dat') + call prod % write('prod.dat') + end if + + ! Set norms to 0 + norm_n = ZERO + norm_o = ZERO + + ! Set up solver + select case(cmfd % indices(4)) + case(1) + cmfd_linsolver => cmfd_linsolver_1g + case(2) + cmfd_linsolver => cmfd_linsolver_2g + case default + cmfd_linsolver => cmfd_linsolver_ng + end select + + ! Set tolerances + ktol = cmfd_ktol + stol = cmfd_stol + + end subroutine init_data + +!=============================================================================== +! COMPUTE_ADJOINT computes a mathematical adjoint of CMFD problem +!=============================================================================== + + subroutine compute_adjoint() + + use error, only: fatal_error +#ifdef PETSC + use global, only: cmfd_write_matrices +#endif + +#ifdef PETSC + ! Transpose matrices + call loss % transpose() + call prod % transpose() + + ! Write out matrix in binary file (debugging) + if (cmfd_write_matrices) then + call loss % write_petsc_binary('adj_lossmat.bin') + call prod % write_petsc_binary('adj_prodmat.bin') + end if +#else + call fatal_error('Adjoint calculations only allowed with PETSc') +#endif + + end subroutine compute_adjoint + +!=============================================================================== +! EXECUTE_POWER_ITER is the main power iteration routine +! for the cmfd calculation +!=============================================================================== + + subroutine execute_power_iter() + + use constants, only: ONE + use error, only: fatal_error + use global, only: cmfd_atoli, cmfd_rtoli + + integer :: i ! iteration counter + integer :: innerits ! # of inner iterations + integer :: totalits ! total number of inners + logical :: iconv ! did the problem converged + real(8) :: atoli ! absolute minimum tolerance + real(8) :: rtoli ! relative tolerance based on source conv + real(8) :: toli ! the current tolerance of inners + + ! Reset convergence flag + iconv = .false. + + ! Set up tolerances + atoli = cmfd_atoli + rtoli = cmfd_rtoli + toli = rtoli*100._8 + + ! Perform shift + call wielandt_shift() + totalits = 0 + + ! Begin power iteration + do i = 1, 10000 + + ! Check if reached iteration 10000 + if (i == 10000) then + call fatal_error('Reached maximum iterations in CMFD power iteration & + &solver.') + end if + + ! Compute source vector + call prod % vector_multiply(phi_o, s_o) + + ! Normalize source vector + s_o % val = s_o % val / k_lo + + ! Compute new flux vector + call cmfd_linsolver(loss, s_o, phi_n, toli, innerits) + + ! Compute new source vector + call prod % vector_multiply(phi_n, s_n) + + ! Compute new shifted eigenvalue + k_ln = sum(s_n % val) / sum(s_o % val) + + ! Compute new eigenvalue + k_n = ONE/(ONE/k_ln + ONE/k_s) + + ! Renormalize the old source + s_o % val = s_o % val * k_lo + + ! Check convergence + call convergence(i, innerits, iconv) + totalits = totalits + innerits + + ! Break loop if converged + if (iconv) exit + + ! Record old values + phi_o % val = phi_n % val + k_o = k_n + k_lo = k_ln + norm_o = norm_n + + ! Get new tolerance for inners + toli = max(atoli, rtoli*serr) + + end do + + end subroutine execute_power_iter + +!=============================================================================== +! WIELANDT SHIFT +!=============================================================================== + + subroutine wielandt_shift() + + use constants, only: ONE + + integer :: irow ! row counter + integer :: icol ! col counter + integer :: jcol ! current col index in prod matrix + + ! perform subtraction + jcol = 1 + ROWS: do irow = 1, loss % n + COLS: do icol = loss % get_row(irow), loss % get_row(irow + 1) - 1 + if (loss % get_col(icol) == prod % get_col(jcol) .and. & + jcol < prod % get_row(irow + 1)) then + loss % val(icol) = loss % val(icol) - ONE/k_s*prod % val(jcol) + jcol = jcol + 1 + end if + end do COLS + end do ROWS + + end subroutine wielandt_shift + +!=============================================================================== +! CONVERGENCE checks the convergence of the CMFD problem +!=============================================================================== + + subroutine convergence(iter, innerits, iconv) + + use constants, only: ONE, ZERO + use global, only: cmfd_power_monitor, master + use, intrinsic :: ISO_FORTRAN_ENV + + integer, intent(in) :: iter ! outer iteration number + integer, intent(in) :: innerits ! inner iteration nubmer + logical, intent(out) :: iconv ! convergence logical + + ! Reset convergence flag + iconv = .false. + + ! Calculate error in keff + kerr = abs(k_o - k_n)/k_n + + ! Calculate max error in source + where (s_n % val > ZERO) + serr_v % val = ((s_n % val - s_o % val)/s_n % val)**2 + end where + serr = sqrt(ONE/dble(s_n % n) * sum(serr_v % val)) + + ! Check for convergence + if(kerr < ktol .and. serr < stol) iconv = .true. + + ! Save the L2 norm of the source + norm_n = serr + + ! Print out to user + if (cmfd_power_monitor .and. master) then + write(OUTPUT_UNIT,FMT='(I0,":",T10,"k-eff: ",F0.8,T30,"k-error: ", & + &1PE12.5,T55, "src-error: ",1PE12.5,T80,I0)') iter, k_n, kerr, & + serr, innerits + end if + + end subroutine convergence + +!=============================================================================== +! CMFD_LINSOLVER_1g solves the CMFD linear system +!=============================================================================== + + subroutine cmfd_linsolver_1g(A, b, x, tol, its) + + use constants, only: ONE, ZERO + use error, only: fatal_error + use global, only: cmfd, cmfd_spectral + + type(Matrix), intent(inout) :: A ! coefficient matrix + type(Vector), intent(inout) :: b ! right hand side vector + type(Vector), intent(inout) :: x ! unknown vector + real(8), intent(in) :: tol ! tolerance on final error + integer, intent(out) :: its ! number of inner iterations + + integer :: g ! group index + integer :: i ! loop counter for x + integer :: j ! loop counter for y + integer :: k ! loop counter for z + integer :: n ! total size of vector + integer :: nx ! maximum dimension in x direction + integer :: ny ! maximum dimension in y direction + integer :: nz ! maximum dimension in z direction + integer :: ng ! number of energy groups + integer :: igs ! Gauss-Seidel iteration counter + integer :: irb ! Red/Black iteration switch + integer :: irow ! row iteration + integer :: icol ! iteration counter over columns + integer :: didx ! index for diagonal component + logical :: found ! did we find col + real(8) :: tmp1 ! temporary sum g1 + real(8) :: x1 ! new g1 value of x + real(8) :: err ! error in convergence of solution + real(8) :: w ! overrelaxation parameter + type(Vector) :: tmpx ! temporary solution vector + + ! Set overrelaxation parameter + w = ONE + + ! Dimensions + ng = 1 + nx = cmfd % indices(1) + ny = cmfd % indices(2) + nz = cmfd % indices(3) + n = A % n + + ! Perform Gauss Seidel iterations + GS: do igs = 1, 10000 + + ! Check for max iterations met + if (igs == 10000) then + call fatal_error('Maximum Gauss-Seidel iterations encountered.') + endif + + ! Copy over x vector + call tmpx % copy(x) + + ! Perform red/black gs iterations + REDBLACK: do irb = 0,1 + + ! Begin loop around matrix rows + ROWS: do irow = 1, n + + ! Get spatial location + call matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) + + ! Filter out black cells (even) + if (mod(i+j+k,2) == irb) cycle + + ! Get the index of the diagonals for both rows + call A % search_indices(irow, irow, didx, found) + + ! Perform temporary sums, first do left of diag block, then right of diag block + tmp1 = ZERO + do icol = A % get_row(irow), didx - 1 + tmp1 = tmp1 + A % val(icol)*x % val(A % get_col(icol)) + end do + do icol = didx + 1, A % get_row(irow + 1) - 1 + tmp1 = tmp1 + A % val(icol)*x % val(A % get_col(icol)) + end do + + ! Solve for new x + x1 = (b % val(irow) - tmp1)/A % val(didx) + + ! Perform overrelaxation + x % val(irow) = (ONE - w)*x % val(irow) + w*x1 + + end do ROWS + + end do REDBLACK + + ! Check convergence + err = sqrt(sum(((tmpx % val - x % val)/tmpx % val)**2)/n) + its = igs + if (err < tol) exit + + ! Calculation new overrelaxation parameter + w = ONE/(ONE - 0.25_8*cmfd_spectral*w) + + end do GS + + call tmpx % destroy() + + end subroutine cmfd_linsolver_1g + +!=============================================================================== +! CMFD_LINSOLVER_2G solves the CMFD linear system +!=============================================================================== + + subroutine cmfd_linsolver_2g(A, b, x, tol, its) + + use constants, only: ONE, ZERO + use error, only: fatal_error + use global, only: cmfd, cmfd_spectral + + type(Matrix), intent(inout) :: A ! coefficient matrix + type(Vector), intent(inout) :: b ! right hand side vector + type(Vector), intent(inout) :: x ! unknown vector + real(8), intent(in) :: tol ! tolerance on final error + integer, intent(out) :: its ! number of inner iterations + + integer :: g ! group index + integer :: i ! loop counter for x + integer :: j ! loop counter for y + integer :: k ! loop counter for z + integer :: n ! total size of vector + integer :: nx ! maximum dimension in x direction + integer :: ny ! maximum dimension in y direction + integer :: nz ! maximum dimension in z direction + integer :: ng ! number of energy groups + integer :: d1idx ! index of row "1" diagonal + integer :: d2idx ! index of row "2" diagonal + integer :: igs ! Gauss-Seidel iteration counter + integer :: irb ! Red/Black iteration switch + integer :: irow ! row iteration + integer :: icol ! iteration counter over columns + logical :: found ! did we find col + real(8) :: m11 ! block diagonal component 1,1 + real(8) :: m12 ! block diagonal component 1,2 + real(8) :: m21 ! block diagonal component 2,1 + real(8) :: m22 ! block diagonal component 2,2 + real(8) :: dm ! determinant of block diagonal + real(8) :: d11 ! inverse component 1,1 + real(8) :: d12 ! inverse component 1,2 + real(8) :: d21 ! inverse component 2,1 + real(8) :: d22 ! inverse component 2,2 + real(8) :: tmp1 ! temporary sum g1 + real(8) :: tmp2 ! temporary sum g2 + real(8) :: x1 ! new g1 value of x + real(8) :: x2 ! new g2 value of x + real(8) :: err ! error in convergence of solution + real(8) :: w ! overrelaxation parameter + type(Vector) :: tmpx ! temporary solution vector + + ! Set tolerance and overrelaxation parameter + w = ONE + + ! Dimensions + ng = 2 + nx = cmfd % indices(1) + ny = cmfd % indices(2) + nz = cmfd % indices(3) + n = A % n + + ! Perform Gauss Seidel iterations + GS: do igs = 1, 10000 + + ! Check for max iterations met + if (igs == 10000) then + call fatal_error('Maximum Gauss-Seidel iterations encountered.') + endif + + ! Copy over x vector + call tmpx % copy(x) + + ! Perform red/black gs iterations + REDBLACK: do irb = 0,1 + + ! Begin loop around matrix rows + ROWS: do irow = 1, n, 2 + + ! Get spatial location + call matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) + + ! Filter out black cells (even) + if (mod(i+j+k,2) == irb) cycle + + ! Get the index of the diagonals for both rows + call A % search_indices(irow, irow, d1idx, found) + call A % search_indices(irow + 1, irow + 1, d2idx, found) + + ! Get block diagonal + m11 = A % val(d1idx) ! group 1 diagonal + m12 = A % val(d1idx + 1) ! group 1 right of diagonal (sorted by col) + m21 = A % val(d2idx - 1) ! group 2 left of diagonal (sorted by col) + m22 = A % val(d2idx) ! group 2 diagonal + + ! Analytically invert the diagonal + dm = m11*m22 - m12*m21 + d11 = m22/dm + d12 = -m12/dm + d21 = -m21/dm + d22 = m11/dm + + ! Perform temporary sums, first do left of diag block, then right of diag block + tmp1 = ZERO + tmp2 = ZERO + do icol = A % get_row(irow), d1idx - 1 + tmp1 = tmp1 + A % val(icol)*x % val(A % get_col(icol)) + end do + do icol = A % get_row(irow + 1), d2idx - 2 + tmp2 = tmp2 + A % val(icol)*x % val(A % get_col(icol)) + end do + do icol = d1idx + 2, A % get_row(irow + 1) - 1 + tmp1 = tmp1 + A % val(icol)*x % val(A % get_col(icol)) + end do + do icol = d2idx + 1, A % get_row(irow + 2) - 1 + tmp2 = tmp2 + A % val(icol)*x % val(A % get_col(icol)) + end do + + ! Adjust with RHS vector + tmp1 = b % val(irow) - tmp1 + tmp2 = b % val(irow + 1) - tmp2 + + ! Solve for new x + x1 = d11*tmp1 + d12*tmp2 + x2 = d21*tmp1 + d22*tmp2 + + ! Perform overrelaxation + x % val(irow) = (ONE - w)*x % val(irow) + w*x1 + x % val(irow + 1) = (ONE - w)*x % val(irow + 1) + w*x2 + + end do ROWS + + end do REDBLACK + + ! Check convergence + err = sqrt(sum(((tmpx % val - x % val)/tmpx % val)**2)/n) + its = igs + if (err < tol) exit + + ! Calculation new overrelaxation parameter + w = ONE/(ONE - 0.25_8*cmfd_spectral*w) + + end do GS + + call tmpx % destroy() + + end subroutine cmfd_linsolver_2g + +!=============================================================================== +! CMFD_LINSOLVER_ng solves the CMFD linear system +!=============================================================================== + + subroutine cmfd_linsolver_ng(A, b, x, tol, its) + + use constants, only: ONE, ZERO + use error, only: fatal_error + use global, only: cmfd, cmfd_spectral + + type(Matrix), intent(inout) :: A ! coefficient matrix + type(Vector), intent(inout) :: b ! right hand side vector + type(Vector), intent(inout) :: x ! unknown vector + real(8), intent(in) :: tol ! tolerance on final error + integer, intent(out) :: its ! number of inner iterations + + integer :: g ! group index + integer :: i ! loop counter for x + integer :: j ! loop counter for y + integer :: k ! loop counter for z + integer :: n ! total size of vector + integer :: nx ! maximum dimension in x direction + integer :: ny ! maximum dimension in y direction + integer :: nz ! maximum dimension in z direction + integer :: ng ! number of energy groups + integer :: igs ! Gauss-Seidel iteration counter + integer :: irow ! row iteration + integer :: icol ! iteration counter over columns + integer :: didx ! index for diagonal component + logical :: found ! did we find col + real(8) :: tmp1 ! temporary sum g1 + real(8) :: x1 ! new g1 value of x + real(8) :: err ! error in convergence of solution + real(8) :: w ! overrelaxation parameter + type(Vector) :: tmpx ! temporary solution vector + + ! Set overrelaxation parameter + w = ONE + + ! Dimensions + ng = 1 + nx = cmfd % indices(1) + ny = cmfd % indices(2) + nz = cmfd % indices(3) + n = A % n + + ! Perform Gauss Seidel iterations + GS: do igs = 1, 10000 + + ! Check for max iterations met + if (igs == 10000) then + call fatal_error('Maximum Gauss-Seidel iterations encountered.') + endif + + ! Copy over x vector + call tmpx % copy(x) + + ! Begin loop around matrix rows + ROWS: do irow = 1, n + + ! Get spatial location + call matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) + + ! Get the index of the diagonals for both rows + call A % search_indices(irow, irow, didx, found) + + ! Perform temporary sums, first do left of diag block, then right of diag block + tmp1 = ZERO + do icol = A % get_row(irow), didx - 1 + tmp1 = tmp1 + A % val(icol)*x % val(A % get_col(icol)) + end do + do icol = didx + 1, A % get_row(irow + 1) - 1 + tmp1 = tmp1 + A % val(icol)*x % val(A % get_col(icol)) + end do + + ! Solve for new x + x1 = (b % val(irow) - tmp1)/A % val(didx) + + ! Perform overrelaxation + x % val(irow) = (ONE - w)*x % val(irow) + w*x1 + + end do ROWS + + ! Check convergence + err = sqrt(sum(((tmpx % val - x % val)/tmpx % val)**2)/n) + its = igs + + if (err < tol) exit + + ! Calculation new overrelaxation parameter + w = ONE/(ONE - 0.25_8*cmfd_spectral*w) + + end do GS + + call tmpx % destroy() + + end subroutine cmfd_linsolver_ng + +!=============================================================================== +! EXTRACT_RESULTS takes results and puts them in CMFD global data object +!=============================================================================== + + subroutine extract_results() + + use global, only: cmfd, cmfd_write_matrices, current_batch + + character(len=25) :: filename ! name of file to write data + integer :: n ! problem size + + ! Get problem size + n = loss % n + + ! Allocate in cmfd object if not already allocated + if (adjoint_calc) then + if (.not. allocated(cmfd%adj_phi)) allocate(cmfd%adj_phi(n)) + else + if (.not. allocated(cmfd%phi)) allocate(cmfd%phi(n)) + end if + + ! Save values + if (adjoint_calc) then + cmfd % adj_phi = phi_n % val + else + cmfd % phi = phi_n % val + end if + + ! Save eigenvalue + if(adjoint_calc) then + cmfd%adj_keff = k_n + else + cmfd%keff = k_n + end if + + ! Normalize phi to 1 + if (adjoint_calc) then + cmfd%adj_phi = cmfd%adj_phi/sqrt(sum(cmfd%adj_phi*cmfd%adj_phi)) + else + cmfd%phi = cmfd%phi/sqrt(sum(cmfd%phi*cmfd%phi)) + end if + + ! Save dominance ratio + cmfd % dom(current_batch) = norm_n/norm_o + + ! Write out results + if (cmfd_write_matrices) then + if (adjoint_calc) then + filename = 'adj_fluxvec.bin' + else + filename = 'fluxvec.bin' + end if +#ifdef PETSC + call phi_n % write_petsc_binary(filename) +#endif + end if + + end subroutine extract_results + +!=============================================================================== +! MATRIX_TO_INDICES converts a matrix index to spatial and group indicies +!=============================================================================== + + subroutine matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) + + use global, only: cmfd, cmfd_coremap + + integer, intent(out) :: i ! iteration counter for x + integer, intent(out) :: j ! iteration counter for y + integer, intent(out) :: k ! iteration counter for z + integer, intent(out) :: g ! iteration counter for groups + integer, intent(in) :: irow ! iteration counter over row (0 reference) + integer, intent(in) :: nx ! maximum number of x cells + integer, intent(in) :: ny ! maximum number of y cells + integer, intent(in) :: nz ! maximum number of z cells + integer, intent(in) :: ng ! maximum number of groups + + ! Check for core map + if (cmfd_coremap) then + + ! Get indices from indexmap + g = mod(irow-1, ng) + 1 + i = cmfd % indexmap((irow-1)/ng+1,1) + j = cmfd % indexmap((irow-1)/ng+1,2) + k = cmfd % indexmap((irow-1)/ng+1,3) + + else + + ! Compute indices + g = mod(irow-1, ng) + 1 + i = mod(irow-1, ng*nx)/ng + 1 + j = mod(irow-1, ng*nx*ny)/(ng*nx)+ 1 + k = mod(irow-1, ng*nx*ny*nz)/(ng*nx*ny) + 1 + + end if + + end subroutine matrix_to_indices + +!=============================================================================== +! FINALIZE frees all memory associated with power iteration +!=============================================================================== + + subroutine finalize() + + ! Destroy all objects + call loss % destroy() + call prod % destroy() + call phi_n % destroy() + call phi_o % destroy() + call s_n % destroy() + call s_o % destroy() + call serr_v % destroy + + end subroutine finalize + +end module cmfd_solver diff --git a/src/constants.F90 b/src/constants.F90 index a890f261c6..af12b97be2 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -8,7 +8,7 @@ module constants ! OpenMC major, minor, and release numbers integer, parameter :: VERSION_MAJOR = 0 integer, parameter :: VERSION_MINOR = 6 - integer, parameter :: VERSION_RELEASE = 0 + integer, parameter :: VERSION_RELEASE = 1 ! Revision numbers for binary files integer, parameter :: REVISION_STATEPOINT = 13 @@ -393,9 +393,6 @@ module constants ! constant to represent a zero flux "albedo" real(8), parameter :: ZERO_FLUX = 999.0_8 - ! constant to represent albedo rejection - real(8), parameter :: ALBEDO_REJECT = 999.0_8 - ! constant for writing out no residual real(8), parameter :: CMFD_NORES = 99999.0_8 diff --git a/src/cross_section.F90 b/src/cross_section.F90 index 0620cf11f8..8e4c19cccd 100644 --- a/src/cross_section.F90 +++ b/src/cross_section.F90 @@ -113,7 +113,7 @@ contains atom_density * micro_xs(i_nuclide) % elastic ! Add contributions to material macroscopic absorption cross section - material_xs % absorption = material_xs % absorption + & + material_xs % absorption = material_xs % absorption + & atom_density * micro_xs(i_nuclide) % absorption ! Add contributions to material macroscopic fission cross section @@ -123,7 +123,7 @@ contains ! Add contributions to material macroscopic nu-fission cross section material_xs % nu_fission = material_xs % nu_fission + & atom_density * micro_xs(i_nuclide) % nu_fission - + ! Add contributions to material macroscopic energy release from fission material_xs % kappa_fission = material_xs % kappa_fission + & atom_density * micro_xs(i_nuclide) % kappa_fission @@ -214,7 +214,7 @@ contains ! Calculate microscopic nuclide nu-fission cross section micro_xs(i_nuclide) % nu_fission = (ONE - f) * nuc % nu_fission( & i_grid) + f * nuc % nu_fission(i_grid+1) - + ! Calculate microscopic nuclide kappa-fission cross section ! The ENDF standard (ENDF-102) states that MT 18 stores ! the fission energy as the Q_value (fission(1)) @@ -276,7 +276,7 @@ contains f = ZERO else i_grid = binary_search(sab % inelastic_e_in, sab % n_inelastic_e_in, E) - f = (E - sab%inelastic_e_in(i_grid)) / & + f = (E - sab%inelastic_e_in(i_grid)) / & (sab%inelastic_e_in(i_grid+1) - sab%inelastic_e_in(i_grid)) end if @@ -342,21 +342,22 @@ contains integer, intent(in) :: i_nuclide ! index into nuclides array real(8), intent(in) :: E ! energy - integer :: i_energy ! index for energy - integer :: i_low ! band index at lower bounding energy - integer :: i_up ! band index at upper bounding energy - real(8) :: f ! interpolation factor - real(8) :: r ! pseudo-random number - real(8) :: elastic ! elastic cross section - real(8) :: capture ! (n,gamma) cross section - real(8) :: fission ! fission cross section - real(8) :: inelastic ! inelastic cross section - logical :: same_nuc ! do we know the xs for this nuclide at this energy? + integer :: i ! loop index + integer :: i_energy ! index for energy + integer :: i_low ! band index at lower bounding energy + integer :: i_up ! band index at upper bounding energy + integer :: same_nuc_idx ! index of same nuclide + real(8) :: f ! interpolation factor + real(8) :: r ! pseudo-random number + real(8) :: elastic ! elastic cross section + real(8) :: capture ! (n,gamma) cross section + real(8) :: fission ! fission cross section + real(8) :: inelastic ! inelastic cross section + logical :: same_nuc ! do we know the xs for this nuclide at this energy? type(UrrData), pointer, save :: urr => null() type(Nuclide), pointer, save :: nuc => null() type(Reaction), pointer, save :: rxn => null() - type(ListElemInt), pointer :: nuc_list => null() -!$omp threadprivate(urr, nuc, rxn, nuc_list) +!$omp threadprivate(urr, nuc, rxn) micro_xs(i_nuclide) % use_ptable = .true. @@ -381,18 +382,16 @@ contains ! this energy but a different temperature, use the original random number to ! preserve correlation of temperature in probability tables same_nuc = .false. - nuc_list => nuc % nuc_list - do - if (E /= ZERO .and. E == micro_xs(nuc_list % data) % last_E) then + do i = 1, nuc % nuc_list % size() + if (E /= ZERO .and. E == micro_xs(nuc % nuc_list % get_item(i)) % last_E) then same_nuc = .true. + same_nuc_idx = i exit end if - nuc_list => nuc_list % next - if (.not. associated(nuc_list % next)) exit end do if (same_nuc) then - r = micro_xs(nuc_list % data) % last_prn + r = micro_xs(nuc % nuc_list % get_item(same_nuc_idx)) % last_prn else r = prn() micro_xs(i_nuclide) % last_prn = r @@ -476,6 +475,11 @@ contains fission = fission * micro_xs(i_nuclide) % fission end if + ! Check for negative values + if (elastic < ZERO) elastic = ZERO + if (fission < ZERO) fission = ZERO + if (capture < ZERO) capture = ZERO + ! Set elastic, absorption, fission, and total cross sections. Note that the ! total cross section is calculated as sum of partials rather than using the ! table-provided value @@ -539,11 +543,11 @@ contains if (nuc % energy_0K(i_grid) == nuc % energy_0K(i_grid+1)) then i_grid = i_grid + 1 end if - + ! calculate interpolation factor f = (E - nuc % energy_0K(i_grid)) & & / (nuc % energy_0K(i_grid + 1) - nuc % energy_0K(i_grid)) - + ! Calculate microscopic nuclide elastic cross section xs_out = (ONE - f) * nuc % elastic_0K(i_grid) & & + f * nuc % elastic_0K(i_grid + 1) diff --git a/src/eigenvalue.F90 b/src/eigenvalue.F90 index 37edb922c4..c46f90ab17 100644 --- a/src/eigenvalue.F90 +++ b/src/eigenvalue.F90 @@ -26,8 +26,9 @@ module eigenvalue private public :: run_eigenvalue - real(8) :: keff_generation ! Single-generation k on each processor - real(8) :: k_sum(2) = ZERO ! used to reduce sum and sum_sq + real(8) :: keff_generation ! Single-generation k on each + ! processor + real(8) :: k_sum(2) = ZERO ! Used to reduce sum and sum_sq contains @@ -114,8 +115,8 @@ contains subroutine initialize_batch() - message = "Simulating batch " // trim(to_str(current_batch)) // "..." - call write_message(8) + call write_message("Simulating batch " // trim(to_str(current_batch)) & + &// "...", 8) ! Reset total starting particle weight used for normalizing tallies total_weight = ZERO @@ -301,8 +302,7 @@ contains ! runs enough particles to avoid this in the first place. if (n_bank == 0) then - message = "No fission sites banked on processor " // to_str(rank) - call fatal_error() + call fatal_error("No fission sites banked on processor " // to_str(rank)) end if ! Make sure all processors start at the same point for random sampling. Then @@ -536,12 +536,12 @@ contains m % n_dimension = 3 allocate(m % dimension(3)) m % dimension = n + + ! determine width + m % width = (m % upper_right - m % lower_left) / m % dimension + end if - ! allocate and determine width - allocate(m % width(3)) - m % width = (m % upper_right - m % lower_left) / m % dimension - ! allocate p allocate(entropy_p(1, m % dimension(1), m % dimension(2), & m % dimension(3))) @@ -553,8 +553,7 @@ contains ! display warning message if there were sites outside entropy box if (sites_outside) then - message = "Fission source site(s) outside of entropy box." - call warning() + if (master) call warning("Fission source site(s) outside of entropy box.") end if ! sum values to obtain shannon entropy @@ -772,8 +771,7 @@ contains ! Check for sites outside of the mesh if (master .and. sites_outside) then - message = "Source sites outside of the UFS mesh!" - call fatal_error() + call fatal_error("Source sites outside of the UFS mesh!") end if #ifdef MPI @@ -803,8 +801,7 @@ contains ! Write message at beginning if (current_batch == 1) then - message = "Replaying history from state point..." - call write_message(1) + call write_message("Replaying history from state point...", 1) end if do current_gen = 1, gen_per_batch @@ -821,8 +818,7 @@ contains ! Write message at end if (current_batch == restart_batch) then - message = "Resuming simulation..." - call write_message(1) + call write_message("Resuming simulation...", 1) end if end subroutine replay_batch_history diff --git a/src/energy_grid.F90 b/src/energy_grid.F90 index 66b95be0de..a006959d92 100644 --- a/src/energy_grid.F90 +++ b/src/energy_grid.F90 @@ -21,8 +21,7 @@ contains type(ListReal), pointer :: list => null() type(Nuclide), pointer :: nuc => null() - message = "Creating unionized energy grid..." - call write_message(5) + call write_message("Creating unionized energy grid...", 5) ! Add grid points for each nuclide in the problem do i = 1, n_nuclides_total diff --git a/src/error.F90 b/src/error.F90 index 78fd64c25e..77f663108f 100644 --- a/src/error.F90 +++ b/src/error.F90 @@ -1,6 +1,7 @@ module error use, intrinsic :: ISO_FORTRAN_ENV + use constants use global @@ -17,9 +18,9 @@ contains ! stream. !=============================================================================== - subroutine warning(force) + subroutine warning(message) - logical, optional :: force ! force write from proc other than master + character(*) :: message integer :: i_start ! starting position integer :: i_end ! ending position @@ -27,9 +28,6 @@ contains integer :: length ! length of message integer :: indent ! length of indentation - ! Only allow master to print to screen - if (.not. master .and. .not. present(force)) return - ! Write warning at beginning write(ERROR_UNIT, fmt='(1X,A)', advance='no') 'WARNING: ' @@ -78,8 +76,9 @@ contains ! the program is aborted. !=============================================================================== - subroutine fatal_error(error_code) + subroutine fatal_error(message, error_code) + character(*) :: message integer, optional :: error_code ! error code integer :: code ! error code diff --git a/src/fission.F90 b/src/fission.F90 index 8934b79cf5..27143bf386 100644 --- a/src/fission.F90 +++ b/src/fission.F90 @@ -3,7 +3,6 @@ module fission use ace_header, only: Nuclide use constants use error, only: fatal_error - use global, only: message use interpolation, only: interpolate_tab1 use search, only: binary_search @@ -27,8 +26,7 @@ contains real(8) :: c ! polynomial coefficient if (nuc % nu_t_type == NU_NONE) then - message = "No neutron emission data for table: " // nuc % name - call fatal_error() + call fatal_error("No neutron emission data for table: " // nuc % name) elseif (nuc % nu_t_type == NU_POLYNOMIAL) then ! determine number of coefficients NC = int(nuc % nu_t_data(1)) diff --git a/src/fixed_source.F90 b/src/fixed_source.F90 index 11290db538..3cbf8673a6 100644 --- a/src/fixed_source.F90 +++ b/src/fixed_source.F90 @@ -1,6 +1,6 @@ module fixed_source - use constants, only: ZERO + use constants, only: ZERO, MAX_LINE_LEN use global use output, only: write_message, header use particle_header, only: Particle @@ -94,8 +94,8 @@ contains subroutine initialize_batch() - message = "Simulating batch " // trim(to_str(current_batch)) // "..." - call write_message(1) + call write_message("Simulating batch " // trim(to_str(current_batch)) & + &// "...", 1) ! Reset total starting particle weight used for normalizing tallies total_weight = ZERO diff --git a/src/geometry.F90 b/src/geometry.F90 index 187a207fd9..043ded05b5 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -11,7 +11,7 @@ module geometry use tally, only: score_surface_current implicit none - + contains !=============================================================================== @@ -100,11 +100,10 @@ contains if (simple_cell_contains(c, p)) then ! the particle should only be contained in one cell per level if (index_cell /= coord % cell) then - message = "Overlapping cells detected: " // & - trim(to_str(cells(index_cell) % id)) // ", " // & - trim(to_str(cells(coord % cell) % id)) // & - " on universe " // trim(to_str(univ % id)) - call fatal_error() + call fatal_error("Overlapping cells detected: " & + &// trim(to_str(cells(index_cell) % id)) // ", " & + &// trim(to_str(cells(coord % cell) % id)) & + &// " on universe " // trim(to_str(univ % id))) end if overlap_check_cnt(index_cell) = overlap_check_cnt(index_cell) + 1 @@ -179,8 +178,7 @@ contains ! Show cell information on trace if (verbosity >= 10 .or. trace) then - message = " Entering cell " // trim(to_str(c % id)) - call write_message() + call write_message(" Entering cell " // trim(to_str(c % id))) end if if (c % type == CELL_NORMAL) then @@ -384,8 +382,7 @@ contains i_surface = abs(p % surface) surf => surfaces(i_surface) if (verbosity >= 10 .or. trace) then - message = " Crossing surface " // trim(to_str(surf % id)) - call write_message() + call write_message(" Crossing surface " // trim(to_str(surf % id))) end if if (surf % bc == BC_VACUUM .and. (run_mode /= MODE_PLOTTING)) then @@ -417,8 +414,8 @@ contains ! Display message if (verbosity >= 10 .or. trace) then - message = " Leaked out of surface " // trim(to_str(surf % id)) - call write_message() + call write_message(" Leaked out of surface " & + &// trim(to_str(surf % id))) end if return @@ -428,9 +425,8 @@ contains ! Do not handle reflective boundary conditions on lower universes if (.not. associated(p % coord, p % coord0)) then - message = "Cannot reflect particle " // trim(to_str(p % id)) // & - " off surface in a lower universe." - call handle_lost_particle(p) + call handle_lost_particle(p, "Cannot reflect particle " & + &// trim(to_str(p % id)) // " off surface in a lower universe.") return end if @@ -562,9 +558,8 @@ contains w = w + 2*dot_prod*R*z case default - message = "Reflection not supported for surface " // & - trim(to_str(surf % id)) - call fatal_error() + call fatal_error("Reflection not supported for surface " & + &// trim(to_str(surf % id))) end select ! Set new particle direction @@ -583,8 +578,8 @@ contains call deallocate_coord(p % coord0 % next) call find_cell(p, found) if (.not. found) then - message = "Couldn't find particle after reflecting from surface." - call handle_lost_particle(p) + call handle_lost_particle(p, "Couldn't find particle after reflecting& + & from surface.") return end if end if @@ -594,8 +589,8 @@ contains ! Diagnostic message if (verbosity >= 10 .or. trace) then - message = " Reflected from surface " // trim(to_str(surf%id)) - call write_message() + call write_message(" Reflected from surface " & + &// trim(to_str(surf%id))) end if return end if @@ -643,10 +638,9 @@ contains ! undefined region in the geometry. if (.not. found) then - message = "After particle " // trim(to_str(p % id)) // " crossed surface " & - // trim(to_str(surfaces(i_surface) % id)) // " it could not be & - &located in any cell and it did not leak." - call handle_lost_particle(p) + call handle_lost_particle(p, "After particle " // trim(to_str(p % id)) & + &// " crossed surface " // trim(to_str(surfaces(i_surface) % id)) & + &// " it could not be located in any cell and it did not leak.") return end if end if @@ -672,11 +666,10 @@ contains lat => lattices(p % coord % lattice) if (verbosity >= 10 .or. trace) then - message = " Crossing lattice " // trim(to_str(lat % id)) // & - ". Current position (" // trim(to_str(p % coord % lattice_x)) & - // "," // trim(to_str(p % coord % lattice_y)) // "," // & - trim(to_str(p % coord % lattice_z)) // ")" - call write_message() + call write_message(" Crossing lattice " // trim(to_str(lat % id)) & + &// ". Current position (" // trim(to_str(p % coord % lattice_x)) & + &// "," // trim(to_str(p % coord % lattice_y)) // "," & + &// trim(to_str(p % coord % lattice_z)) // ")") end if if (lat % type == LATTICE_RECT) then @@ -739,9 +732,8 @@ contains ! Search for particle call find_cell(p, found) if (.not. found) then - message = "Could not locate particle " // trim(to_str(p % id)) // & - " after crossing a lattice boundary." - call handle_lost_particle(p) + call handle_lost_particle(p, "Could not locate particle " & + &// trim(to_str(p % id)) // " after crossing a lattice boundary.") return end if else @@ -762,9 +754,9 @@ contains ! Search for particle call find_cell(p, found) if (.not. found) then - message = "Could not locate particle " // trim(to_str(p % id)) // & - " after crossing a lattice boundary." - call handle_lost_particle(p) + call handle_lost_particle(p, "Could not locate particle " & + &// trim(to_str(p % id)) & + &// " after crossing a lattice boundary.") return end if end if @@ -1494,8 +1486,8 @@ contains type(Cell), pointer :: c type(Surface), pointer :: surf - message = "Building neighboring cells lists for each surface..." - call write_message(4) + call write_message("Building neighboring cells lists for each surface...", & + &4) allocate(count_positive(n_surfaces)) allocate(count_negative(n_surfaces)) @@ -1562,12 +1554,13 @@ contains ! HANDLE_LOST_PARTICLE !=============================================================================== - subroutine handle_lost_particle(p) + subroutine handle_lost_particle(p, message) type(Particle), intent(inout) :: p + character(*) :: message ! Print warning and write lost particle file - call warning(force = .true.) + call warning(message) call write_particle_restart(p) ! Increment number of lost particles @@ -1579,8 +1572,7 @@ contains ! Abort the simulation if the maximum number of lost particles has been ! reached if (n_lost_particles == MAX_LOST_PARTICLES) then - message = "Maximum number of lost particles has been reached." - call fatal_error() + call fatal_error("Maximum number of lost particles has been reached.") end if end subroutine handle_lost_particle diff --git a/src/global.F90 b/src/global.F90 index b136c2c166..f4c50b8330 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -272,9 +272,6 @@ module global character(MAX_FILE_LEN) :: path_particle_restart ! Path to particle restart character(MAX_FILE_LEN) :: path_output = '' ! Path to output directory - ! Message used in message/warning/fatal_error - character(2*MAX_LINE_LEN) :: message - ! Random number seed integer(8) :: seed = 1_8 @@ -299,6 +296,9 @@ module global ! Particle restart run logical :: particle_restart_run = .false. + ! Write out initial source + logical :: write_initial_source = .false. + ! ============================================================================ ! CMFD VARIABLES @@ -329,9 +329,6 @@ module global integer :: n_cmfd_meshes = 1 ! # of structured meshes integer :: n_cmfd_tallies = 3 ! # of user-defined tallies - ! Flag to hold cmfd weight adjustment factors - logical :: cmfd_hold_weights = .false. - ! Eigenvalue solver type character(len=10) :: cmfd_solver_type = 'power' @@ -344,11 +341,9 @@ module global ! Batch to begin cmfd integer :: cmfd_begin = 1 - ! When and how long to flush cmfd tallies during inactive batches - integer :: cmfd_inact_flush(2) = (/9999,1/) - - ! Batch to last flush before active batches - integer :: cmfd_act_flush = 0 + ! Tally reset list + integer :: n_cmfd_resets + type(SetInt) :: cmfd_reset ! Compute effective downscatter cross section logical :: cmfd_downscatter = .false. @@ -366,11 +361,18 @@ module global ! CMFD run logicals logical :: cmfd_on = .false. - logical :: cmfd_tally_on = .true. ! CMFD display info character(len=25) :: cmfd_display = 'balance' + ! Estimate of spectral radius of CMFD matrices and tolerances + real(8) :: cmfd_spectral = ZERO + real(8) :: cmfd_shift = 1.e6 + real(8) :: cmfd_ktol = 1.e-8_8 + real(8) :: cmfd_stol = 1.e-8_8 + real(8) :: cmfd_atoli = 1.e-10_8 + real(8) :: cmfd_rtoli = 1.e-5_8 + ! Information about state points to be written integer :: n_state_points = 0 type(SetInt) :: statepoint_batch @@ -391,7 +393,7 @@ module global integer :: n_res_scatterers_total = 0 ! total number of resonant scatterers type(Nuclide0K), allocatable, target :: nuclides_0K(:) ! 0K nuclides info -!$omp threadprivate(micro_xs, material_xs, fission_bank, n_bank, message, & +!$omp threadprivate(micro_xs, material_xs, fission_bank, n_bank, & !$omp& trace, thread_id, current_work, matching_bins) contains diff --git a/src/initialize.F90 b/src/initialize.F90 index 7d4adad493..e0a5bf8b0c 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -156,10 +156,8 @@ contains ! Warn if overlap checking is on if (master .and. check_overlaps) then - message = "" - call write_message() - message = "Cell overlap checking is ON" - call warning() + call write_message("") + call warning("Cell overlap checking is ON") end if ! Stop initialization timer @@ -342,9 +340,8 @@ contains ! Check that number specified was valid if (n_particles == ERROR_INT) then - message = "Must specify integer after " // trim(argv(i-1)) // & - " command-line flag." - call fatal_error() + call fatal_error("Must specify integer after " // trim(argv(i-1)) & + &// " command-line flag.") end if case ('-r', '-restart', '--restart') ! Read path for state point/particle restart @@ -364,8 +361,7 @@ contains path_particle_restart = argv(i) particle_restart_run = .true. case default - message = "Unrecognized file after restart flag." - call fatal_error() + call fatal_error("Unrecognized file after restart flag.") end select ! If its a restart run check for additional source file @@ -383,8 +379,8 @@ contains call sp % read_data(filetype, 'filetype') call sp % file_close() if (filetype /= FILETYPE_SOURCE) then - message = "Second file after restart flag must be a source file" - call fatal_error() + call fatal_error("Second file after restart flag must be a & + &source file") end if ! It is a source file @@ -418,13 +414,13 @@ contains ! Read and set number of OpenMP threads n_threads = int(str_to_int(argv(i)), 4) if (n_threads < 1) then - message = "Invalid number of threads specified on command line." - call fatal_error() + call fatal_error("Invalid number of threads specified on command & + &line.") end if call omp_set_num_threads(n_threads) #else - message = "Ignoring number of threads specified on command line." - call warning() + if (master) call warning("Ignoring number of threads specified on & + &command line.") #endif case ('-?', '-h', '-help', '--help') @@ -440,8 +436,7 @@ contains write_all_tracks = .true. i = i + 1 case default - message = "Unknown command line option: " // argv(i) - call fatal_error() + call fatal_error("Unknown command line option: " // argv(i)) end select last_flag = i @@ -577,9 +572,8 @@ contains i_array = surface_dict % get_key(abs(id)) c % surfaces(j) = sign(i_array, id) else - message = "Could not find surface " // trim(to_str(abs(id))) // & - " specified on cell " // trim(to_str(c % id)) - call fatal_error() + call fatal_error("Could not find surface " // trim(to_str(abs(id)))& + &// " specified on cell " // trim(to_str(c % id))) end if end if end do @@ -591,9 +585,8 @@ contains if (universe_dict % has_key(id)) then c % universe = universe_dict % get_key(id) else - message = "Could not find universe " // trim(to_str(id)) // & - " specified on cell " // trim(to_str(c % id)) - call fatal_error() + call fatal_error("Could not find universe " // trim(to_str(id)) & + &// " specified on cell " // trim(to_str(c % id))) end if ! ======================================================================= @@ -607,9 +600,8 @@ contains c % type = CELL_NORMAL c % material = material_dict % get_key(id) else - message = "Could not find material " // trim(to_str(id)) // & - " specified on cell " // trim(to_str(c % id)) - call fatal_error() + call fatal_error("Could not find material " // trim(to_str(id)) & + &// " specified on cell " // trim(to_str(c % id))) end if else id = c % fill @@ -626,15 +618,14 @@ contains else if (material_dict % has_key(mid)) then c % material = material_dict % get_key(mid) else - message = "Could not find material " // trim(to_str(mid)) // & - " specified on lattice " // trim(to_str(lid)) - call fatal_error() + call fatal_error("Could not find material " // trim(to_str(mid)) & + &// " specified on lattice " // trim(to_str(lid))) end if else - message = "Specified fill " // trim(to_str(id)) // " on cell " // & - trim(to_str(c % id)) // " is neither a universe nor a lattice." - call fatal_error() + call fatal_error("Specified fill " // trim(to_str(id)) // " on cell "& + &// trim(to_str(c % id)) // " is neither a universe nor a & + &lattice.") end if end if end do @@ -659,9 +650,8 @@ contains if (universe_dict % has_key(id)) then lat % universes(j,k,m) = universe_dict % get_key(id) else - message = "Invalid universe number " // trim(to_str(id)) & - // " specified on lattice " // trim(to_str(lat % id)) - call fatal_error() + call fatal_error("Invalid universe number " // trim(to_str(id)) & + &// " specified on lattice " // trim(to_str(lat % id))) end if end do end do @@ -685,9 +675,8 @@ contains if (cell_dict % has_key(id)) then t % filters(j) % int_bins(k) = cell_dict % get_key(id) else - message = "Could not find cell " // trim(to_str(id)) // & - " specified on tally " // trim(to_str(t % id)) - call fatal_error() + call fatal_error("Could not find cell " // trim(to_str(id)) & + &// " specified on tally " // trim(to_str(t % id))) end if end do @@ -701,9 +690,8 @@ contains if (surface_dict % has_key(id)) then t % filters(j) % int_bins(k) = surface_dict % get_key(id) else - message = "Could not find surface " // trim(to_str(id)) // & - " specified on tally " // trim(to_str(t % id)) - call fatal_error() + call fatal_error("Could not find surface " // trim(to_str(id)) & + &// " specified on tally " // trim(to_str(t % id))) end if end do @@ -714,9 +702,8 @@ contains if (universe_dict % has_key(id)) then t % filters(j) % int_bins(k) = universe_dict % get_key(id) else - message = "Could not find universe " // trim(to_str(id)) // & - " specified on tally " // trim(to_str(t % id)) - call fatal_error() + call fatal_error("Could not find universe " // trim(to_str(id)) & + &// " specified on tally " // trim(to_str(t % id))) end if end do @@ -727,9 +714,8 @@ contains if (material_dict % has_key(id)) then t % filters(j) % int_bins(k) = material_dict % get_key(id) else - message = "Could not find material " // trim(to_str(id)) // & - " specified on tally " // trim(to_str(t % id)) - call fatal_error() + call fatal_error("Could not find material " // trim(to_str(id)) & + &// " specified on tally " // trim(to_str(t % id))) end if end do @@ -864,8 +850,7 @@ contains ! Check for allocation errors if (alloc_err /= 0) then - message = "Failed to allocate source bank." - call fatal_error() + call fatal_error("Failed to allocate source bank.") end if #ifdef _OPENMP @@ -892,8 +877,7 @@ contains ! Check for allocation errors if (alloc_err /= 0) then - message = "Failed to allocate fission bank." - call fatal_error() + call fatal_error("Failed to allocate fission bank.") end if end subroutine allocate_banks diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 12bd01b5e3..e992a0eae0 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -11,7 +11,7 @@ module input_xml use output, only: write_message use plot_header use random_lcg, only: prn - use string, only: lower_case, to_str, str_to_int, str_to_real, & + use string, only: to_lower, to_str, str_to_int, str_to_real, & starts_with, ends_with use tally_header, only: TallyObject, TallyFilter use tally_initialize, only: add_tallies @@ -20,7 +20,7 @@ module input_xml implicit none save - type(DictIntInt) :: cells_in_univ_dict ! used to count how many cells each + type(DictIntInt) :: cells_in_univ_dict ! Used to count how many cells each ! universe contains contains @@ -36,7 +36,7 @@ contains if (run_mode /= MODE_PLOTTING) call read_cross_sections_xml() call read_geometry_xml() call read_materials_xml() - if (run_mode /= MODE_PLOTTING) call read_tallies_xml() + call read_tallies_xml() if (cmfd_run) call configure_cmfd() end subroutine read_input_xml @@ -76,19 +76,17 @@ contains type(NodeList), pointer :: node_scat_list => null() ! Display output message - message = "Reading settings XML file..." - call write_message(5) + call write_message("Reading settings XML file...", 5) ! Check if settings.xml exists filename = trim(path_input) // "settings.xml" inquire(FILE=filename, EXIST=file_exists) if (.not. file_exists) then - message = "Settings XML file '" // trim(filename) // "' does not exist! & - &In order to run OpenMC, you first need a set of input files; at a & - &minimum, this includes settings.xml, geometry.xml, and & + call fatal_error("Settings XML file '" // trim(filename) // "' does not & + &exist! In order to run OpenMC, you first need a set of input files;& + & at a minimum, this includes settings.xml, geometry.xml, and & &materials.xml. Please consult the user's guide at & - &http://mit-crpg.github.io/openmc for further information." - call fatal_error() + &http://mit-crpg.github.io/openmc for further information.") end if ! Parse settings.xml file @@ -104,13 +102,12 @@ contains ! environment variable call get_environment_variable("CROSS_SECTIONS", env_variable) if (len_trim(env_variable) == 0) then - message = "No cross_sections.xml file was specified in settings.xml & - &or in the CROSS_SECTIONS environment variable. OpenMC needs a & - &cross_sections.xml file to identify where to find ACE cross & - §ion libraries. Please consult the user's guide at & - &http://mit-crpg.github.io/openmc for information on how to set & - &up ACE cross section libraries." - call fatal_error() + call fatal_error("No cross_sections.xml file was specified in & + &settings.xml or in the CROSS_SECTIONS environment variable. & + &OpenMC needs a cross_sections.xml file to identify where to & + &find ACE cross section libraries. Please consult the user's & + &guide at http://mit-crpg.github.io/openmc for information on & + &how to set up ACE cross section libraries.") else path_cross_sections = trim(env_variable) end if @@ -130,8 +127,7 @@ contains ! Make sure that either eigenvalue or fixed source was specified if (.not.check_for_node(doc, "eigenvalue") .and. & .not.check_for_node(doc, "fixed_source")) then - message = " or not specified." - call fatal_error() + call fatal_error(" or not specified.") end if ! Eigenvalue information @@ -144,8 +140,7 @@ contains ! Check number of particles if (.not.check_for_node(node_mode, "particles")) then - message = "Need to specify number of particles per generation." - call fatal_error() + call fatal_error("Need to specify number of particles per generation.") end if ! Get number of particles @@ -179,8 +174,7 @@ contains ! Check number of particles if (.not.check_for_node(node_mode, "particles")) then - message = "Need to specify number of particles per batch." - call fatal_error() + call fatal_error("Need to specify number of particles per batch.") end if ! Get number of particles @@ -199,14 +193,11 @@ contains ! Check number of active batches, inactive batches, and particles if (n_active <= 0) then - message = "Number of active batches must be greater than zero." - call fatal_error() + call fatal_error("Number of active batches must be greater than zero.") elseif (n_inactive < 0) then - message = "Number of inactive batches must be non-negative." - call fatal_error() + call fatal_error("Number of inactive batches must be non-negative.") elseif (n_particles <= 0) then - message = "Number of particles must be greater than zero." - call fatal_error() + call fatal_error("Number of particles must be greater than zero.") end if ! Copy random number seed if specified @@ -224,11 +215,9 @@ contains case ('union') grid_method = GRID_UNION case ('lethargy') - message = "Lethargy mapped energy grid not yet supported." - call fatal_error() + call fatal_error("Lethargy mapped energy grid not yet supported.") case default - message = "Unknown energy grid method: " // trim(temp_str) - call fatal_error() + call fatal_error("Unknown energy grid method: " // trim(temp_str)) end select ! Verbosity @@ -243,14 +232,12 @@ contains if (n_threads == NONE) then call get_node_value(doc, "threads", n_threads) if (n_threads < 1) then - message = "Invalid number of threads: " // to_str(n_threads) - call fatal_error() + call fatal_error("Invalid number of threads: " // to_str(n_threads)) end if call omp_set_num_threads(n_threads) end if #else - message = "Ignoring number of threads." - call warning() + if (master) call warning("Ignoring number of threads.") #endif end if @@ -261,8 +248,15 @@ contains if (check_for_node(doc, "source")) then call get_node_ptr(doc, "source", node_source) else - message = "No source specified in settings XML file." - call fatal_error() + call fatal_error("No source specified in settings XML file.") + end if + + ! Check if we want to write out source + if (check_for_node(node_source, "write_initial")) then + call get_node_value(node_source, "write_initial", temp_str) + temp_str = to_lower(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + write_initial_source = .true. end if ! Check for external source file @@ -273,9 +267,8 @@ contains ! Check if source file exists inquire(FILE=path_source, EXIST=file_exists) if (.not. file_exists) then - message = "Binary source file '" // trim(path_source) // & - "' does not exist!" - call fatal_error() + call fatal_error("Binary source file '" // trim(path_source) & + &// "' does not exist!") end if else @@ -290,8 +283,7 @@ contains type = '' if (check_for_node(node_dist, "type")) & call get_node_value(node_dist, "type", type) - call lower_case(type) - select case (trim(type)) + select case (to_lower(type)) case ('box') external_source % type_space = SRC_SPACE_BOX coeffs_reqd = 6 @@ -302,9 +294,8 @@ contains external_source % type_space = SRC_SPACE_POINT coeffs_reqd = 3 case default - message = "Invalid spatial distribution for external source: " & - // trim(type) - call fatal_error() + call fatal_error("Invalid spatial distribution for external source: "& + &// trim(type)) end select ! Determine number of parameters specified @@ -316,21 +307,19 @@ contains ! Read parameters for spatial distribution if (n < coeffs_reqd) then - message = "Not enough parameters specified for spatial & - &distribution of external source." - call fatal_error() + call fatal_error("Not enough parameters specified for spatial & + &distribution of external source.") elseif (n > coeffs_reqd) then - message = "Too many parameters specified for spatial & - &distribution of external source." - call fatal_error() + call fatal_error("Too many parameters specified for spatial & + &distribution of external source.") elseif (n > 0) then allocate(external_source % params_space(n)) call get_node_array(node_dist, "parameters", & external_source % params_space) end if else - message = "No spatial distribution specified for external source." - call fatal_error() + call fatal_error("No spatial distribution specified for external & + &source.") end if ! Determine external source angular distribution @@ -343,8 +332,7 @@ contains type = '' if (check_for_node(node_dist, "type")) & call get_node_value(node_dist, "type", type) - call lower_case(type) - select case (trim(type)) + select case (to_lower(type)) case ('isotropic') external_source % type_angle = SRC_ANGLE_ISOTROPIC coeffs_reqd = 0 @@ -354,9 +342,8 @@ contains case ('tabular') external_source % type_angle = SRC_ANGLE_TABULAR case default - message = "Invalid angular distribution for external source: " & - // trim(type) - call fatal_error() + call fatal_error("Invalid angular distribution for external source: "& + &// trim(type)) end select ! Determine number of parameters specified @@ -368,13 +355,11 @@ contains ! Read parameters for angle distribution if (n < coeffs_reqd) then - message = "Not enough parameters specified for angle & - &distribution of external source." - call fatal_error() + call fatal_error("Not enough parameters specified for angle & + &distribution of external source.") elseif (n > coeffs_reqd) then - message = "Too many parameters specified for angle & - &distribution of external source." - call fatal_error() + call fatal_error("Too many parameters specified for angle & + &distribution of external source.") elseif (n > 0) then allocate(external_source % params_angle(n)) call get_node_array(node_dist, "parameters", & @@ -395,8 +380,7 @@ contains type = '' if (check_for_node(node_dist, "type")) & call get_node_value(node_dist, "type", type) - call lower_case(type) - select case (trim(type)) + select case (to_lower(type)) case ('monoenergetic') external_source % type_energy = SRC_ENERGY_MONO coeffs_reqd = 1 @@ -409,9 +393,8 @@ contains case ('tabular') external_source % type_energy = SRC_ENERGY_TABULAR case default - message = "Invalid energy distribution for external source: " & - // trim(type) - call fatal_error() + call fatal_error("Invalid energy distribution for external source: " & + &// trim(type)) end select ! Determine number of parameters specified @@ -423,13 +406,11 @@ contains ! Read parameters for energy distribution if (n < coeffs_reqd) then - message = "Not enough parameters specified for energy & - &distribution of external source." - call fatal_error() + call fatal_error("Not enough parameters specified for energy & + &distribution of external source.") elseif (n > coeffs_reqd) then - message = "Too many parameters specified for energy & - &distribution of external source." - call fatal_error() + call fatal_error("Too many parameters specified for energy & + &distribution of external source.") elseif (n > 0) then allocate(external_source % params_energy(n)) call get_node_array(node_dist, "parameters", & @@ -446,7 +427,7 @@ contains ! Survival biasing if (check_for_node(doc, "survival_biasing")) then call get_node_value(doc, "survival_biasing", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & survival_biasing = .true. end if @@ -454,7 +435,7 @@ contains ! Probability tables if (check_for_node(doc, "ptables")) then call get_node_value(doc, "ptables", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'false' .or. trim(temp_str) == '0') & urr_ptables_on = .false. end if @@ -479,9 +460,9 @@ contains ! Make sure that there are three values per particle n_tracks = get_arraysize_integer(doc, "track") if (mod(n_tracks, 3) /= 0) then - message = "Number of integers specified in 'track' is not divisible & - &by 3. Please provide 3 integers per particle to be tracked." - call fatal_error() + call fatal_error("Number of integers specified in 'track' is not & + &divisible by 3. Please provide 3 integers per particle to be & + &tracked.") end if ! Allocate space and get list of tracks @@ -501,17 +482,18 @@ contains ! Check to make sure enough values were supplied if (get_arraysize_double(node_entropy, "lower_left") /= 3) then - message = "Need to specify (x,y,z) coordinates of lower-left corner & - &of Shannon entropy mesh." + call fatal_error("Need to specify (x,y,z) coordinates of lower-left & + &corner of Shannon entropy mesh.") elseif (get_arraysize_double(node_entropy, "upper_right") /= 3) then - message = "Need to specify (x,y,z) coordinates of upper-right corner & - &of Shannon entropy mesh." + call fatal_error("Need to specify (x,y,z) coordinates of upper-right & + &corner of Shannon entropy mesh.") end if ! Allocate mesh object and coordinates on mesh allocate(entropy_mesh) allocate(entropy_mesh % lower_left(3)) allocate(entropy_mesh % upper_right(3)) + allocate(entropy_mesh % width(3)) ! Copy values call get_node_array(node_entropy, "lower_left", & @@ -520,10 +502,10 @@ contains entropy_mesh % upper_right) ! Check on values provided - if (.not. all(entropy_mesh % upper_right > entropy_mesh % lower_left)) then - message = "Upper-right coordinate must be greater than lower-left & - &coordinate for Shannon entropy mesh." - call fatal_error() + if (.not. all(entropy_mesh % upper_right > entropy_mesh % lower_left)) & + &then + call fatal_error("Upper-right coordinate must be greater than & + &lower-left coordinate for Shannon entropy mesh.") end if ! Check if dimensions were specified -- if not, they will be calculated @@ -532,9 +514,8 @@ contains ! If so, make sure proper number of values were given if (get_arraysize_integer(node_entropy, "dimension") /= 3) then - message = "Dimension of entropy mesh must be given as three & - &integers." - call fatal_error() + call fatal_error("Dimension of entropy mesh must be given as three & + &integers.") end if ! Allocate dimensions @@ -543,6 +524,11 @@ contains ! Copy dimensions call get_node_array(node_entropy, "dimension", entropy_mesh % dimension) + + ! Calculate width + entropy_mesh % width = (entropy_mesh % upper_right - & + entropy_mesh % lower_left) / entropy_mesh % dimension + end if ! Turn on Shannon entropy calculation @@ -557,14 +543,14 @@ contains ! Check to make sure enough values were supplied if (get_arraysize_double(node_ufs, "lower_left") /= 3) then - message = "Need to specify (x,y,z) coordinates of lower-left corner & - &of UFS mesh." + call fatal_error("Need to specify (x,y,z) coordinates of lower-left & + &corner of UFS mesh.") elseif (get_arraysize_double(node_ufs, "upper_right") /= 3) then - message = "Need to specify (x,y,z) coordinates of upper-right corner & - &of UFS mesh." + call fatal_error("Need to specify (x,y,z) coordinates of upper-right & + &corner of UFS mesh.") elseif (get_arraysize_integer(node_ufs, "dimension") /= 3) then - message = "Dimension of UFS mesh must be given as three integers." - call fatal_error() + call fatal_error("Dimension of UFS mesh must be given as three & + &integers.") end if ! Allocate mesh object and coordinates on mesh @@ -586,9 +572,8 @@ contains ! Check on values provided if (.not. all(ufs_mesh % upper_right > ufs_mesh % lower_left)) then - message = "Upper-right coordinate must be greater than lower-left & - &coordinate for UFS mesh." - call fatal_error() + call fatal_error("Upper-right coordinate must be greater than & + &lower-left coordinate for UFS mesh.") end if ! Calculate width @@ -685,19 +670,19 @@ contains ! Check if the user has specified to write binary source file if (check_for_node(node_sp, "separate")) then call get_node_value(node_sp, "separate", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. & trim(temp_str) == '1') source_separate = .true. end if if (check_for_node(node_sp, "write")) then call get_node_value(node_sp, "write", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'false' .or. & trim(temp_str) == '0') source_write = .false. end if if (check_for_node(node_sp, "overwrite_latest")) then call get_node_value(node_sp, "overwrite_latest", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. & trim(temp_str) == '1') then source_latest = .true. @@ -721,9 +706,8 @@ contains do i = 1, n_source_points if (.not. statepoint_batch % contains(sourcepoint_batch % & get_item(i))) then - message = 'Sourcepoint batches are not a subset& - & of statepoint batches.' - call fatal_error() + call fatal_error('Sourcepoint batches are not a subset& + & of statepoint batches.') end if end do end if @@ -732,7 +716,7 @@ contains ! batch if (check_for_node(doc, "no_reduce")) then call get_node_value(doc, "no_reduce", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & reduce_tallies = .false. end if @@ -741,7 +725,7 @@ contains ! uncertainties rather than standard deviations if (check_for_node(doc, "confidence_intervals")) then call get_node_value(doc, "confidence_intervals", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. & trim(temp_str) == '1') confidence_intervals = .true. end if @@ -755,7 +739,7 @@ contains ! Check for summary option if (check_for_node(node_output, "summary")) then call get_node_value(node_output, "summary", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. & trim(temp_str) == '1') output_summary = .true. end if @@ -763,7 +747,7 @@ contains ! Check for cross sections option if (check_for_node(node_output, "cross_sections")) then call get_node_value(node_output, "cross_sections", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. & trim(temp_str) == '1') output_xs = .true. end if @@ -771,7 +755,7 @@ contains ! Check for ASCII tallies output option if (check_for_node(node_output, "tallies")) then call get_node_value(node_output, "tallies", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'false' .or. & trim(temp_str) == '0') output_tallies = .false. end if @@ -780,15 +764,9 @@ contains ! Check for cmfd run if (check_for_node(doc, "run_cmfd")) then call get_node_value(doc, "run_cmfd", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') then cmfd_run = .true. -#ifndef PETSC - if (master) then - message = 'CMFD is not available, compile OpenMC with PETSc' - call fatal_error() - end if -#endif end if end if @@ -796,7 +774,7 @@ contains if (check_for_node(doc, "resonance_scattering")) then call get_node_ptr(doc, "resonance_scattering", node_res_scat) call get_node_list(node_res_scat, "scatterer", node_scat_list) - + ! check that a nuclide is specified if (get_list_size(node_scat_list) >= 1) then treat_res_scat = .true. @@ -806,39 +784,37 @@ contains allocate(nuclides_0K(n_res_scatterers_total)) do i = 1, n_res_scatterers_total call get_list_item(node_scat_list, i, node_scatterer) - + ! check to make sure a nuclide is specified if (.not. check_for_node(node_scatterer, "nuclide")) then - message = "No nuclide specified for scatterer " // trim(to_str(i)) & - // " in settings.xml file!" - call fatal_error() + call fatal_error("No nuclide specified for scatterer " & + &// trim(to_str(i)) // " in settings.xml file!") end if call get_node_value(node_scatterer, "nuclide", & nuclides_0K(i) % nuclide) - + if (check_for_node(node_scatterer, "method")) then call get_node_value(node_scatterer, "method", & nuclides_0K(i) % scheme) end if - + ! check to make sure xs name for which method is applied is given if (.not. check_for_node(node_scatterer, "xs_label")) then - message = "Must specify the temperature dependent name of " // '' & - //"scatterer " // trim(to_str(i)) // " given in cross_sections.xml" - call fatal_error() + call fatal_error("Must specify the temperature dependent name of & + &scatterer " // trim(to_str(i)) & + &// " given in cross_sections.xml") end if call get_node_value(node_scatterer, "xs_label", & nuclides_0K(i) % name) - + ! check to make sure 0K xs name for which method is applied is given if (.not. check_for_node(node_scatterer, "xs_label_0K")) then - message = "Must specify the 0K name of " // '' & - //"scatterer "// trim(to_str(i)) // " given in cross_sections.xml" - call fatal_error() + call fatal_error("Must specify the 0K name of scatterer " & + &// trim(to_str(i)) // " given in cross_sections.xml") end if call get_node_value(node_scatterer, "xs_label_0K", & nuclides_0K(i) % name_0K) - + if (check_for_node(node_scatterer, "E_min")) then call get_node_value(node_scatterer, "E_min", & nuclides_0K(i) % E_min) @@ -846,39 +822,36 @@ contains ! check that E_min is non-negative if (nuclides_0K(i) % E_min < ZERO) then - message = "Lower resonance scattering energy bound is negative" - call fatal_error() + call fatal_error("Lower resonance scattering energy bound is & + &negative") end if if (check_for_node(node_scatterer, "E_max")) then call get_node_value(node_scatterer, "E_max", & nuclides_0K(i) % E_max) end if - + ! check that E_max is not less than E_min if (nuclides_0K(i) % E_max < nuclides_0K(i) % E_min) then - message = "Lower resonance scattering energy bound exceeds upper" - call fatal_error() + call fatal_error("Lower resonance scattering energy bound exceeds & + &upper") end if nuclides_0K(i) % nuclide = trim(nuclides_0K(i) % nuclide) - nuclides_0K(i) % scheme = trim(nuclides_0K(i) % scheme) - call lower_case(nuclides_0K(i) % scheme) + nuclides_0K(i) % scheme = to_lower(trim(nuclides_0K(i) % scheme)) nuclides_0K(i) % name = trim(nuclides_0K(i) % name) nuclides_0K(i) % name_0K = trim(nuclides_0K(i) % name_0K) end do else - message = "No resonant scatterers are specified within the " // "" & - // "resonance_scattering element in settings.xml" - call fatal_error() + call fatal_error("No resonant scatterers are specified within the & + &resonance_scattering element in settings.xml") end if end if ! Natural element expansion option if (check_for_node(doc, "natural_elements")) then call get_node_value(doc, "natural_elements", temp_str) - call lower_case(temp_str) - select case (temp_str) + select case (to_lower(temp_str)) case ('endf/b-vii.0') default_expand = ENDF_BVII0 case ('endf/b-vii.1') @@ -896,8 +869,8 @@ contains case ('jendl-4.0') default_expand = JENDL_40 case default - message = "Unknown natural element expansion option: " // trim(temp_str) - call fatal_error() + call fatal_error("Unknown natural element expansion option: " & + &// trim(temp_str)) end select end if @@ -939,8 +912,7 @@ contains type(NodeList), pointer :: node_lat_list => null() ! Display output message - message = "Reading geometry XML file..." - call write_message(5) + call write_message("Reading geometry XML file...", 5) ! ========================================================================== ! READ CELLS FROM GEOMETRY.XML @@ -949,8 +921,8 @@ contains filename = trim(path_input) // "geometry.xml" inquire(FILE=filename, EXIST=file_exists) if (.not. file_exists) then - message = "Geometry XML file '" // trim(filename) // "' does not exist!" - call fatal_error() + call fatal_error("Geometry XML file '" // trim(filename) // "' does not & + &exist!") end if ! Parse geometry.xml file @@ -964,8 +936,7 @@ contains ! Check for no cells if (n_cells == 0) then - message = "No cells found in geometry.xml!" - call fatal_error() + call fatal_error("No cells found in geometry.xml!") end if ! Allocate cells array @@ -987,8 +958,7 @@ contains if (check_for_node(node_cell, "id")) then call get_node_value(node_cell, "id", c % id) else - message = "Must specify id of cell in geometry XML file." - call fatal_error() + call fatal_error("Must specify id of cell in geometry XML file.") end if if (check_for_node(node_cell, "universe")) then call get_node_value(node_cell, "universe", c % universe) @@ -1003,16 +973,15 @@ contains ! Check to make sure 'id' hasn't been used if (cell_dict % has_key(c % id)) then - message = "Two or more cells use the same unique ID: " // to_str(c % id) - call fatal_error() + call fatal_error("Two or more cells use the same unique ID: " & + &// to_str(c % id)) end if ! Read material word = '' if (check_for_node(node_cell, "material")) & call get_node_value(node_cell, "material", word) - call lower_case(word) - select case(word) + select case(to_lower(word)) case ('void') c % material = MATERIAL_VOID @@ -1025,30 +994,27 @@ contains ! Check for error if (c % material == ERROR_INT) then - message = "Invalid material specified on cell " // to_str(c % id) - call fatal_error() + call fatal_error("Invalid material specified on cell " & + &// to_str(c % id)) end if end select ! Check to make sure that either material or fill was specified if (c % material == NONE .and. c % fill == NONE) then - message = "Neither material nor fill was specified for cell " // & - trim(to_str(c % id)) - call fatal_error() + call fatal_error("Neither material nor fill was specified for cell " & + &// trim(to_str(c % id))) end if ! Check to make sure that both material and fill haven't been ! specified simultaneously if (c % material /= NONE .and. c % fill /= NONE) then - message = "Cannot specify material and fill simultaneously" - call fatal_error() + call fatal_error("Cannot specify material and fill simultaneously") end if ! Check to make sure that surfaces were specified if (.not. check_for_node(node_cell, "surfaces")) then - message = "No surfaces specified for cell " // & - trim(to_str(c % id)) - call fatal_error() + call fatal_error("No surfaces specified for cell " & + &// trim(to_str(c % id))) end if ! Allocate array for surfaces and copy @@ -1062,17 +1028,15 @@ contains ! Rotations can only be applied to cells that are being filled with ! another universe if (c % fill == NONE) then - message = "Cannot apply a rotation to cell " // trim(to_str(& - c % id)) // " because it is not filled with another universe" - call fatal_error() + call fatal_error("Cannot apply a rotation to cell " // trim(to_str(& + &c % id)) // " because it is not filled with another universe") end if ! Read number of rotation parameters n = get_arraysize_double(node_cell, "rotation") if (n /= 3) then - message = "Incorrect number of rotation parameters on cell " // & - to_str(c % id) - call fatal_error() + call fatal_error("Incorrect number of rotation parameters on cell " & + &// to_str(c % id)) end if ! Copy rotation angles in x,y,z directions @@ -1098,17 +1062,16 @@ contains ! Translations can only be applied to cells that are being filled with ! another universe if (c % fill == NONE) then - message = "Cannot apply a translation to cell " // trim(to_str(& - c % id)) // " because it is not filled with another universe" - call fatal_error() + call fatal_error("Cannot apply a translation to cell " & + &// trim(to_str(c % id)) // " because it is not filled with & + &another universe") end if ! Read number of translation parameters n = get_arraysize_double(node_cell, "translation") if (n /= 3) then - message = "Incorrect number of translation parameters on cell " & - // to_str(c % id) - call fatal_error() + call fatal_error("Incorrect number of translation parameters on & + &cell " // to_str(c % id)) end if ! Copy translation vector @@ -1149,8 +1112,7 @@ contains ! Check for no surfaces if (n_surfaces == 0) then - message = "No surfaces found in geometry.xml!" - call fatal_error() + call fatal_error("No surfaces found in geometry.xml!") end if ! Allocate cells array @@ -1166,23 +1128,20 @@ contains if (check_for_node(node_surf, "id")) then call get_node_value(node_surf, "id", s % id) else - message = "Must specify id of surface in geometry XML file." - call fatal_error() + call fatal_error("Must specify id of surface in geometry XML file.") end if ! Check to make sure 'id' hasn't been used if (surface_dict % has_key(s % id)) then - message = "Two or more surfaces use the same unique ID: " // & - to_str(s % id) - call fatal_error() + call fatal_error("Two or more surfaces use the same unique ID: " & + &// to_str(s % id)) end if ! Copy and interpret surface type word = '' if (check_for_node(node_surf, "type")) & call get_node_value(node_surf, "type", word) - call lower_case(word) - select case(trim(word)) + select case(to_lower(word)) case ('x-plane') s % type = SURF_PX coeffs_reqd = 1 @@ -1217,8 +1176,7 @@ contains s % type = SURF_CONE_Z coeffs_reqd = 4 case default - message = "Invalid surface type: " // trim(word) - call fatal_error() + call fatal_error("Invalid surface type: " // trim(word)) end select ! Check to make sure that the proper number of coefficients @@ -1227,13 +1185,11 @@ contains n = get_arraysize_double(node_surf, "coeffs") if (n < coeffs_reqd) then - message = "Not enough coefficients specified for surface: " // & - trim(to_str(s % id)) - call fatal_error() + call fatal_error("Not enough coefficients specified for surface: " & + &// trim(to_str(s % id))) elseif (n > coeffs_reqd) then - message = "Too many coefficients specified for surface: " // & - trim(to_str(s % id)) - call fatal_error() + call fatal_error("Too many coefficients specified for surface: " & + &// trim(to_str(s % id))) else allocate(s % coeffs(n)) call get_node_array(node_surf, "coeffs", s % coeffs) @@ -1243,8 +1199,7 @@ contains word = '' if (check_for_node(node_surf, "boundary")) & call get_node_value(node_surf, "boundary", word) - call lower_case(word) - select case (trim(word)) + select case (to_lower(word)) case ('transmission', 'transmit', '') s % bc = BC_TRANSMIT case ('vacuum') @@ -1254,9 +1209,8 @@ contains s % bc = BC_REFLECT boundary_exists = .true. case default - message = "Unknown boundary condition '" // trim(word) // & - "' specified on surface " // trim(to_str(s % id)) - call fatal_error() + call fatal_error("Unknown boundary condition '" // trim(word) // & + &"' specified on surface " // trim(to_str(s % id))) end select ! Add surface to dictionary @@ -1267,8 +1221,7 @@ contains ! Check to make sure a boundary condition was applied to at least one ! surface if (.not. boundary_exists) then - message = "No boundary conditions were applied to any surfaces!" - call fatal_error() + call fatal_error("No boundary conditions were applied to any surfaces!") end if ! ========================================================================== @@ -1291,37 +1244,32 @@ contains if (check_for_node(node_lat, "id")) then call get_node_value(node_lat, "id", lat % id) else - message = "Must specify id of lattice in geometry XML file." - call fatal_error() + call fatal_error("Must specify id of lattice in geometry XML file.") end if ! Check to make sure 'id' hasn't been used if (lattice_dict % has_key(lat % id)) then - message = "Two or more lattices use the same unique ID: " // & - to_str(lat % id) - call fatal_error() + call fatal_error("Two or more lattices use the same unique ID: " & + &// to_str(lat % id)) end if ! Read lattice type word = '' if (check_for_node(node_lat, "type")) & call get_node_value(node_lat, "type", word) - call lower_case(word) - select case (trim(word)) + select case (to_lower(word)) case ('rect', 'rectangle', 'rectangular') lat % type = LATTICE_RECT case ('hex', 'hexagon', 'hexagonal') lat % type = LATTICE_HEX case default - message = "Invalid lattice type: " // trim(word) - call fatal_error() + call fatal_error("Invalid lattice type: " // trim(word)) end select ! Read number of lattice cells in each dimension n = get_arraysize_integer(node_lat, "dimension") if (n /= 2 .and. n /= 3) then - message = "Lattice must be two or three dimensions." - call fatal_error() + call fatal_error("Lattice must be two or three dimensions.") end if lat % n_dimension = n @@ -1331,9 +1279,8 @@ contains ! Read lattice lower-left location if (size(lat % dimension) /= & get_arraysize_double(node_lat, "lower_left")) then - message = "Number of entries on must be the same as & - &the number of entries on ." - call fatal_error() + call fatal_error("Number of entries on must be the same & + &as the number of entries on .") end if allocate(lat % lower_left(n)) @@ -1342,9 +1289,8 @@ contains ! Read lattice widths if (size(lat % dimension) /= & get_arraysize_double(node_lat, "width")) then - message = "Number of entries on must be the same as & - &the number of entries on ." - call fatal_error() + call fatal_error("Number of entries on must be the same as & + &the number of entries on .") end if allocate(lat % width(n)) @@ -1363,9 +1309,8 @@ contains ! Check that number of universes matches size n = get_arraysize_integer(node_lat, "universes") if (n /= n_x*n_y*n_z) then - message = "Number of universes on does not match size of & - &lattice " // trim(to_str(lat % id)) // "." - call fatal_error() + call fatal_error("Number of universes on does not match & + &size of lattice " // trim(to_str(lat % id)) // ".") end if allocate(temp_int_array(n)) @@ -1441,15 +1386,14 @@ contains type(NodeList), pointer :: node_sab_list => null() ! Display output message - message = "Reading materials XML file..." - call write_message(5) + call write_message("Reading materials XML file...", 5) ! Check is materials.xml exists filename = trim(path_input) // "materials.xml" inquire(FILE=filename, EXIST=file_exists) if (.not. file_exists) then - message = "Material XML file '" // trim(filename) // "' does not exist!" - call fatal_error() + call fatal_error("Material XML file '" // trim(filename) // "' does not & + &exist!") end if ! Initialize default cross section variable @@ -1483,15 +1427,13 @@ contains if (check_for_node(node_mat, "id")) then call get_node_value(node_mat, "id", mat % id) else - message = "Must specify id of material in materials XML file" - call fatal_error() + call fatal_error("Must specify id of material in materials XML file") end if ! Check to make sure 'id' hasn't been used if (material_dict % has_key(mat % id)) then - message = "Two or more materials use the same unique ID: " // & - to_str(mat % id) - call fatal_error() + call fatal_error("Two or more materials use the same unique ID: " & + &// to_str(mat % id)) end if if (run_mode == MODE_PLOTTING) then @@ -1507,9 +1449,8 @@ contains if (check_for_node(node_mat, "density")) then call get_node_ptr(node_mat, "density", node_dens) else - message = "Must specify density element in material " // & - trim(to_str(mat % id)) - call fatal_error() + call fatal_error("Must specify density element in material " & + &// trim(to_str(mat % id))) end if ! Initialize value to zero @@ -1532,14 +1473,12 @@ contains ! Check for erroneous density sum_density = .false. if (val <= ZERO) then - message = "Need to specify a positive density on material " // & - trim(to_str(mat % id)) // "." - call fatal_error() + call fatal_error("Need to specify a positive density on material " & + &// trim(to_str(mat % id)) // ".") end if ! Adjust material density based on specified units - call lower_case(units) - select case(trim(units)) + select case(to_lower(units)) case ('g/cc', 'g/cm3') mat % density = -val case ('kg/m3') @@ -1549,9 +1488,8 @@ contains case ('atom/cm3', 'atom/cc') mat % density = 1.0e-24 * val case default - message = "Unkwown units '" // trim(units) & - // "' specified on material " // trim(to_str(mat % id)) - call fatal_error() + call fatal_error("Unkwown units '" // trim(units) & + &// "' specified on material " // trim(to_str(mat % id))) end select end if @@ -1561,9 +1499,8 @@ contains ! Check to ensure material has at least one nuclide if (.not. check_for_node(node_mat, "nuclide") .and. & .not. check_for_node(node_mat, "element")) then - message = "No nuclides or natural elements specified on material " // & - trim(to_str(mat % id)) - call fatal_error() + call fatal_error("No nuclides or natural elements specified on & + &material " // trim(to_str(mat % id))) end if ! Get pointer list of XML @@ -1576,17 +1513,15 @@ contains ! Check for empty name on nuclide if (.not.check_for_node(node_nuc, "name")) then - message = "No name specified on nuclide in material " // & - trim(to_str(mat % id)) - call fatal_error() + call fatal_error("No name specified on nuclide in material " & + &// trim(to_str(mat % id))) end if ! Check for cross section if (.not.check_for_node(node_nuc, "xs")) then if (default_xs == '') then - message = "No cross section specified for nuclide in material " & - // trim(to_str(mat % id)) - call fatal_error() + call fatal_error("No cross section specified for nuclide in & + &material " // trim(to_str(mat % id))) else name = trim(default_xs) end if @@ -1605,14 +1540,12 @@ contains ! weight percents were specified if (.not.check_for_node(node_nuc, "ao") .and. & .not.check_for_node(node_nuc, "wo")) then - message = "No atom or weight percent specified for nuclide " // & - trim(name) - call fatal_error() + call fatal_error("No atom or weight percent specified for nuclide " & + &// trim(name)) elseif (check_for_node(node_nuc, "ao") .and. & check_for_node(node_nuc, "wo")) then - message = "Cannot specify both atom and weight percents for a & - &nuclide: " // trim(name) - call fatal_error() + call fatal_error("Cannot specify both atom and weight percents for a & + &nuclide: " // trim(name)) end if ! Copy atom/weight percents @@ -1636,9 +1569,8 @@ contains ! Check for empty name on natural element if (.not.check_for_node(node_ele, "name")) then - message = "No name specified on nuclide in material " // & - trim(to_str(mat % id)) - call fatal_error() + call fatal_error("No name specified on nuclide in material " & + &// trim(to_str(mat % id))) end if call get_node_value(node_ele, "name", name) @@ -1647,9 +1579,8 @@ contains call get_node_value(node_ele, "xs", temp_str) else if (default_xs == '') then - message = "No cross section specified for nuclide in material " & - // trim(to_str(mat % id)) - call fatal_error() + call fatal_error("No cross section specified for nuclide in & + &material " // trim(to_str(mat % id))) else temp_str = trim(default_xs) end if @@ -1659,14 +1590,12 @@ contains ! weight percents were specified if (.not.check_for_node(node_ele, "ao") .and. & .not.check_for_node(node_ele, "wo")) then - message = "No atom or weight percent specified for element " // & - trim(name) - call fatal_error() + call fatal_error("No atom or weight percent specified for element " & + &// trim(name)) elseif (check_for_node(node_ele, "ao") .and. & check_for_node(node_ele, "wo")) then - message = "Cannot specify both atom and weight percents for a & - &element: " // trim(name) - call fatal_error() + call fatal_error("Cannot specify both atom and weight percents for & + &element: " // trim(name)) end if ! Expand element into naturally-occurring isotopes @@ -1675,9 +1604,8 @@ contains call expand_natural_element(name, temp_str, temp_dble, & list_names, list_density) else - message = "The ability to expand a natural element based on weight & - &percentage is not yet supported." - call fatal_error() + call fatal_error("The ability to expand a natural element based on & + &weight percentage is not yet supported.") end if end do NATURAL_ELEMENTS @@ -1694,35 +1622,33 @@ contains ALL_NUCLIDES: do j = 1, mat % n_nuclides ! Check that this nuclide is listed in the cross_sections.xml file name = trim(list_names % get_item(j)) - if (.not. xs_listing_dict % has_key(name)) then - message = "Could not find nuclide " // trim(name) // & - " in cross_sections.xml file!" - call fatal_error() + if (.not. xs_listing_dict % has_key(to_lower(name))) then + call fatal_error("Could not find nuclide " // trim(name) & + &// " in cross_sections.xml file!") end if ! Check to make sure cross-section is continuous energy neutron table n = len_trim(name) if (name(n:n) /= 'c') then - message = "Cross-section table " // trim(name) // & - " is not a continuous-energy neutron table." - call fatal_error() + call fatal_error("Cross-section table " // trim(name) & + &// " is not a continuous-energy neutron table.") end if ! Find xs_listing and set the name/alias according to the listing - index_list = xs_listing_dict % get_key(name) + index_list = xs_listing_dict % get_key(to_lower(name)) name = xs_listings(index_list) % name alias = xs_listings(index_list) % alias ! If this nuclide hasn't been encountered yet, we need to add its name ! and alias to the nuclide_dict - if (.not. nuclide_dict % has_key(name)) then + if (.not. nuclide_dict % has_key(to_lower(name))) then index_nuclide = index_nuclide + 1 mat % nuclide(j) = index_nuclide - call nuclide_dict % add_key(name, index_nuclide) - call nuclide_dict % add_key(alias, index_nuclide) + call nuclide_dict % add_key(to_lower(name), index_nuclide) + call nuclide_dict % add_key(to_lower(alias), index_nuclide) else - mat % nuclide(j) = nuclide_dict % get_key(name) + mat % nuclide(j) = nuclide_dict % get_key(to_lower(name)) end if ! Copy name and atom/weight percent @@ -1734,9 +1660,8 @@ contains ! given if (.not. (all(mat % atom_density >= ZERO) .or. & all(mat % atom_density <= ZERO))) then - message = "Cannot mix atom and weight percents in material " // & - to_str(mat % id) - call fatal_error() + call fatal_error("Cannot mix atom and weight percents in material " & + &// to_str(mat % id)) end if ! Determine density if it is a sum value @@ -1772,8 +1697,8 @@ contains ! Determine name of S(a,b) table if (.not.check_for_node(node_sab, "name") .or. & .not.check_for_node(node_sab, "xs")) then - message = "Need to specify and for S(a,b) table." - call fatal_error() + call fatal_error("Need to specify and for S(a,b) & + &table.") end if call get_node_value(node_sab, "name", name) call get_node_value(node_sab, "xs", temp_str) @@ -1781,25 +1706,24 @@ contains mat % sab_names(j) = name ! Check that this nuclide is listed in the cross_sections.xml file - if (.not. xs_listing_dict % has_key(name)) then - message = "Could not find S(a,b) table " // trim(name) // & - " in cross_sections.xml file!" - call fatal_error() + if (.not. xs_listing_dict % has_key(to_lower(name))) then + call fatal_error("Could not find S(a,b) table " // trim(name) & + &// " in cross_sections.xml file!") end if ! Find index in xs_listing and set the name and alias according to the ! listing - index_list = xs_listing_dict % get_key(name) + index_list = xs_listing_dict % get_key(to_lower(name)) name = xs_listings(index_list) % name ! If this S(a,b) table hasn't been encountered yet, we need to add its ! name and alias to the sab_dict - if (.not. sab_dict % has_key(name)) then + if (.not. sab_dict % has_key(to_lower(name))) then index_sab = index_sab + 1 mat % i_sab_tables(j) = index_sab - call sab_dict % add_key(name, index_sab) + call sab_dict % add_key(to_lower(name), index_sab) else - mat % i_sab_tables(j) = sab_dict % get_key(name) + mat % i_sab_tables(j) = sab_dict % get_key(to_lower(name)) end if end do end if @@ -1869,8 +1793,7 @@ contains end if ! Display output message - message = "Reading tallies XML file..." - call write_message(5) + call write_message("Reading tallies XML file...", 5) ! Parse tallies.xml file call open_xmldoc(doc, filename) @@ -1898,19 +1821,18 @@ contains ! Check for user tallies n_user_tallies = get_list_size(node_tal_list) if (n_user_tallies == 0) then - message = "No tallies present in tallies.xml file!" - call warning() + if (master) call warning("No tallies present in tallies.xml file!") end if ! Allocate tally array - if (n_user_tallies > 0) then + if (n_user_tallies > 0 .and. run_mode /= MODE_PLOTTING) then call add_tallies("user", n_user_tallies) end if ! Check for setting if (check_for_node(doc, "assume_separate")) then call get_node_value(doc, "assume_separate", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & assume_separate = .true. end if @@ -1928,37 +1850,32 @@ contains if (check_for_node(node_mesh, "id")) then call get_node_value(node_mesh, "id", m % id) else - message = "Must specify id for mesh in tally XML file." - call fatal_error() + call fatal_error("Must specify id for mesh in tally XML file.") end if ! Check to make sure 'id' hasn't been used if (mesh_dict % has_key(m % id)) then - message = "Two or more meshes use the same unique ID: " // & - to_str(m % id) - call fatal_error() + call fatal_error("Two or more meshes use the same unique ID: " & + &// to_str(m % id)) end if ! Read mesh type temp_str = '' if (check_for_node(node_mesh, "type")) & call get_node_value(node_mesh, "type", temp_str) - call lower_case(temp_str) - select case (trim(temp_str)) + select case (to_lower(temp_str)) case ('rect', 'rectangle', 'rectangular') m % type = LATTICE_RECT case ('hex', 'hexagon', 'hexagonal') m % type = LATTICE_HEX case default - message = "Invalid mesh type: " // trim(temp_str) - call fatal_error() + call fatal_error("Invalid mesh type: " // trim(temp_str)) end select ! Determine number of dimensions for mesh n = get_arraysize_integer(node_mesh, "dimension") if (n /= 2 .and. n /= 3) then - message = "Mesh must be two or three dimensions." - call fatal_error() + call fatal_error("Mesh must be two or three dimensions.") end if m % n_dimension = n @@ -1971,9 +1888,8 @@ contains ! Check that dimensions are all greater than zero call get_node_array(node_mesh, "dimension", iarray3(1:n)) if (any(iarray3(1:n) <= 0)) then - message = "All entries on the element for a tally mesh & - &must be positive." - call fatal_error() + call fatal_error("All entries on the element for a tally & + &mesh must be positive.") end if ! Read dimensions in each direction @@ -1981,42 +1897,37 @@ contains ! Read mesh lower-left corner location if (m % n_dimension /= get_arraysize_double(node_mesh, "lower_left")) then - message = "Number of entries on must be the same as & - &the number of entries on ." - call fatal_error() + call fatal_error("Number of entries on must be the same & + &as the number of entries on .") end if call get_node_array(node_mesh, "lower_left", m % lower_left) ! Make sure both upper-right or width were specified if (check_for_node(node_mesh, "upper_right") .and. & check_for_node(node_mesh, "width")) then - message = "Cannot specify both and on a & - &tally mesh." - call fatal_error() + call fatal_error("Cannot specify both and on a & + &tally mesh.") end if ! Make sure either upper-right or width was specified if (.not.check_for_node(node_mesh, "upper_right") .and. & .not.check_for_node(node_mesh, "width")) then - message = "Must specify either and on a & - &tally mesh." - call fatal_error() + call fatal_error("Must specify either and on a & + &tally mesh.") end if if (check_for_node(node_mesh, "width")) then ! Check to ensure width has same dimensions if (get_arraysize_double(node_mesh, "width") /= & get_arraysize_double(node_mesh, "lower_left")) then - message = "Number of entries on must be the same as the & - &number of entries on ." - call fatal_error() + call fatal_error("Number of entries on must be the same as & + &the number of entries on .") end if ! Check for negative widths call get_node_array(node_mesh, "width", rarray3(1:n)) if (any(rarray3(1:n) < ZERO)) then - message = "Cannot have a negative on a tally mesh." - call fatal_error() + call fatal_error("Cannot have a negative on a tally mesh.") end if ! Set width and upper right coordinate @@ -2027,17 +1938,15 @@ contains ! Check to ensure width has same dimensions if (get_arraysize_double(node_mesh, "upper_right") /= & get_arraysize_double(node_mesh, "lower_left")) then - message = "Number of entries on must be the same as & - &the number of entries on ." - call fatal_error() + call fatal_error("Number of entries on must be the & + &same as the number of entries on .") end if ! Check that upper-right is above lower-left call get_node_array(node_mesh, "upper_right", rarray3(1:n)) if (any(rarray3(1:n) < m % lower_left)) then - message = "The coordinates must be greater than the & - & coordinates on a tally mesh." - call fatal_error() + call fatal_error("The coordinates must be greater than & + &the coordinates on a tally mesh.") end if ! Set width and upper right coordinate @@ -2052,6 +1961,9 @@ contains call mesh_dict % add_key(m % id, i) end do + ! We only need the mesh info for plotting + if (run_mode == MODE_PLOTTING) return + ! ========================================================================== ! READ TALLY DATA @@ -2077,15 +1989,13 @@ contains if (check_for_node(node_tal, "id")) then call get_node_value(node_tal, "id", t % id) else - message = "Must specify id for tally in tally XML file." - call fatal_error() + call fatal_error("Must specify id for tally in tally XML file.") end if ! Check to make sure 'id' hasn't been used if (tally_dict % has_key(t % id)) then - message = "Two or more tallies use the same unique ID: " // & - to_str(t % id) - call fatal_error() + call fatal_error("Two or more tallies use the same unique ID: " & + &// to_str(t % id)) end if ! Copy tally label @@ -2096,16 +2006,6 @@ contains ! ======================================================================= ! READ DATA FOR FILTERS - ! In older versions, tally filters were specified with a - ! element followed by sub-elements , , etc. This checks for - ! the old format and if it is present, raises an error - -! if (get_number_nodes(node_tal, "filters") > 0) then -! message = "Tally filters should be specified with multiple & -! &elements. Did you forget to change your element?" -! call fatal_error() -! end if - ! Get pointer list to XML and get number of filters call get_node_list(node_tal, "filter", node_filt_list) n_filters = get_list_size(node_filt_list) @@ -2124,7 +2024,7 @@ contains temp_str = '' if (check_for_node(node_filt, "type")) & call get_node_value(node_filt, "type", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) ! Determine number of bins if (check_for_node(node_filt, "bins")) then @@ -2135,8 +2035,8 @@ contains n_words = get_arraysize_integer(node_filt, "bins") end if else - message = "Bins not set in filter on tally " // trim(to_str(t % id)) - call fatal_error() + call fatal_error("Bins not set in filter on tally " & + &// trim(to_str(t % id))) end if ! Determine type of filter @@ -2186,8 +2086,7 @@ contains call get_node_array(node_filt, "bins", t % filters(j) % int_bins) case ('surface') - message = "Surface filter is not yet supported!" - call fatal_error() + call fatal_error("Surface filter is not yet supported!") ! Set type of filter t % filters(j) % type = FILTER_SURFACE @@ -2205,8 +2104,7 @@ contains ! Check to make sure multiple meshes weren't given if (n_words /= 1) then - message = "Can only have one mesh filter specified." - call fatal_error() + call fatal_error("Can only have one mesh filter specified.") end if ! Determine id of mesh @@ -2217,9 +2115,8 @@ contains i_mesh = mesh_dict % get_key(id) m => meshes(i_mesh) else - message = "Could not find mesh " // trim(to_str(id)) // & - " specified on tally " // trim(to_str(t % id)) - call fatal_error() + call fatal_error("Could not find mesh " // trim(to_str(id)) & + &// " specified on tally " // trim(to_str(t % id))) end if ! Determine number of bins -- this is assuming that the tally is @@ -2258,10 +2155,9 @@ contains case default ! Specified tally filter is invalid, raise error - message = "Unknown filter type '" // & - trim(temp_str) // "' on tally " // & - trim(to_str(t % id)) // "." - call fatal_error() + call fatal_error("Unknown filter type '" & + &// trim(temp_str) // "' on tally " & + &// trim(to_str(t % id)) // ".") end select @@ -2275,9 +2171,8 @@ contains ! Check that both cell and surface weren't specified if (t % find_filter(FILTER_CELL) > 0 .and. & t % find_filter(FILTER_SURFACE) > 0) then - message = "Cannot specify both cell and surface filters for tally " & - // trim(to_str(t % id)) - call fatal_error() + call fatal_error("Cannot specify both cell and surface filters for & + &tally " // trim(to_str(t % id))) end if else @@ -2339,10 +2234,9 @@ contains ! Check if no nuclide was found if (.not. associated(pair_list)) then - message = "Could not find the nuclide " // trim(& - sarray(j)) // " specified in tally " & - // trim(to_str(t % id)) // " in any material." - call fatal_error() + call fatal_error("Could not find the nuclide " & + &// trim(sarray(j)) // " specified in tally " & + &// trim(to_str(t % id)) // " in any material.") end if deallocate(pair_list) else @@ -2352,14 +2246,14 @@ contains end if ! Check to make sure nuclide specified is in problem - if (.not. nuclide_dict % has_key(word)) then - message = "The nuclide " // trim(word) // " from tally " // & - trim(to_str(t % id)) // " is not present in any material." - call fatal_error() + if (.not. nuclide_dict % has_key(to_lower(word))) then + call fatal_error("The nuclide " // trim(word) // " from tally " & + &// trim(to_str(t % id)) & + &// " is not present in any material.") end if ! Set bin to index in nuclides array - t % nuclide_bins(j) = nuclide_dict % get_key(word) + t % nuclide_bins(j) = nuclide_dict % get_key(to_lower(word)) end do ! Set number of nuclide bins @@ -2390,7 +2284,7 @@ contains ! (i.e., scatter-p#, flux-y#) n_new = 0 do j = 1, n_words - call lower_case(sarray(j)) + sarray(j) = to_lower(sarray(j)) ! Find if scores(j) is of the form 'moment-p' or 'moment-y' present in ! MOMENT_STRS(:) ! If so, check the order, store if OK, then reset the number to 'n' @@ -2404,13 +2298,13 @@ contains ! User requested too many orders; throw a warning and set to the ! maximum order. ! The above scheme will essentially take the absolute value - message = "Invalid scattering order of " // trim(to_str(n_order)) // & - " requested. Setting to the maximum permissible value, " // & - trim(to_str(MAX_ANG_ORDER)) - call warning() + if (master) call warning("Invalid scattering order of " & + &// trim(to_str(n_order)) // " requested. Setting to the & + &maximum permissible value, " & + &// trim(to_str(MAX_ANG_ORDER))) n_order = MAX_ANG_ORDER - sarray(j) = trim(MOMENT_STRS(imomstr)) // & - trim(to_str(MAX_ANG_ORDER)) + sarray(j) = trim(MOMENT_STRS(imomstr)) & + &// trim(to_str(MAX_ANG_ORDER)) end if ! Find total number of bins for this case if (imomstr >= YN_LOC) then @@ -2435,7 +2329,8 @@ contains j = j + 1 ! Get the input string in scores(l) but if score is one of the moment ! scores then strip off the n and store it as an integer to be used - ! later. Then perform the select case on this modified (number removed) string + ! later. Then perform the select case on this modified (number + ! removed) string score_name = sarray(l) do imomstr = 1, size(MOMENT_STRS) if (starts_with(score_name,trim(MOMENT_STRS(imomstr)))) then @@ -2446,10 +2341,6 @@ contains ! User requested too many orders; throw a warning and set to the ! maximum order. ! The above scheme will essentially take the absolute value - message = "Invalid scattering order of " // trim(to_str(n_order)) // & - " requested. Setting to the maximum permissible value, " // & - trim(to_str(MAX_ANG_ORDER)) - call warning() n_order = MAX_ANG_ORDER end if score_name = trim(MOMENT_STRS(imomstr)) // "n" @@ -2473,10 +2364,10 @@ contains ! User requested too many orders; throw a warning and set to the ! maximum order. ! The above scheme will essentially take the absolute value - message = "Invalid scattering order of " // trim(to_str(n_order)) // & - " requested. Setting to the maximum permissible value, " // & - trim(to_str(MAX_ANG_ORDER)) - call warning() + if (master) call warning("Invalid scattering order of " & + &// trim(to_str(n_order)) // " requested. Setting to & + &the maximum permissible value, " & + &// trim(to_str(MAX_ANG_ORDER))) n_order = MAX_ANG_ORDER end if score_name = trim(MOMENT_N_STRS(imomstr)) // "n" @@ -2490,26 +2381,24 @@ contains ! Prohibit user from tallying flux for an individual nuclide if (.not. (t % n_nuclide_bins == 1 .and. & t % nuclide_bins(1) == -1)) then - message = "Cannot tally flux for an individual nuclide." - call fatal_error() + call fatal_error("Cannot tally flux for an individual nuclide.") end if t % score_bins(j) = SCORE_FLUX if (t % find_filter(FILTER_ENERGYOUT) > 0) then - message = "Cannot tally flux with an outgoing energy filter." - call fatal_error() + call fatal_error("Cannot tally flux with an outgoing energy & + &filter.") end if case ('flux-yn') ! Prohibit user from tallying flux for an individual nuclide if (.not. (t % n_nuclide_bins == 1 .and. & t % nuclide_bins(1) == -1)) then - message = "Cannot tally flux for an individual nuclide." - call fatal_error() + call fatal_error("Cannot tally flux for an individual nuclide.") end if if (t % find_filter(FILTER_ENERGYOUT) > 0) then - message = "Cannot tally flux with an outgoing energy filter." - call fatal_error() + call fatal_error("Cannot tally flux with an outgoing energy & + &filter.") end if t % score_bins(j : j + n_bins - 1) = SCORE_FLUX_YN @@ -2519,16 +2408,14 @@ contains case ('total') t % score_bins(j) = SCORE_TOTAL if (t % find_filter(FILTER_ENERGYOUT) > 0) then - message = "Cannot tally total reaction rate with an & - &outgoing energy filter." - call fatal_error() + call fatal_error("Cannot tally total reaction rate with an & + &outgoing energy filter.") end if case ('total-yn') if (t % find_filter(FILTER_ENERGYOUT) > 0) then - message = "Cannot tally total reaction rate with an & - &outgoing energy filter." - call fatal_error() + call fatal_error("Cannot tally total reaction rate with an & + &outgoing energy filter.") end if t % score_bins(j : j + n_bins - 1) = SCORE_TOTAL_YN @@ -2597,9 +2484,8 @@ contains ! Set tally estimator to analog t % estimator = ESTIMATOR_ANALOG case ('diffusion') - message = "Diffusion score no longer supported for tallies, & - &please remove" - call fatal_error() + call fatal_error("Diffusion score no longer supported for tallies, & + &please remove") case ('n1n') t % score_bins(j) = SCORE_N_1N @@ -2617,16 +2503,14 @@ contains case ('absorption') t % score_bins(j) = SCORE_ABSORPTION if (t % find_filter(FILTER_ENERGYOUT) > 0) then - message = "Cannot tally absorption rate with an outgoing & - &energy filter." - call fatal_error() + call fatal_error("Cannot tally absorption rate with an outgoing & + &energy filter.") end if case ('fission') t % score_bins(j) = SCORE_FISSION if (t % find_filter(FILTER_ENERGYOUT) > 0) then - message = "Cannot tally fission rate with an outgoing & - &energy filter." - call fatal_error() + call fatal_error("Cannot tally fission rate with an outgoing & + &energy filter.") end if case ('nu-fission') t % score_bins(j) = SCORE_NU_FISSION @@ -2643,10 +2527,9 @@ contains ! Check to make sure that current is the only desired response ! for this tally if (n_words > 1) then - message = "Cannot tally other scoring functions in the same & - &tally as surface currents. Separate other scoring & - &functions into a distinct tally." - call fatal_error() + call fatal_error("Cannot tally other scoring functions in the & + &same tally as surface currents. Separate other scoring & + &functions into a distinct tally.") end if ! Since the number of bins for the mesh filter was already set @@ -2656,6 +2539,12 @@ contains ! Get index of mesh filter k = t % find_filter(FILTER_MESH) + ! Check to make sure mesh filter was specified + if (k == 0) then + call fatal_error("Cannot tally surface current without a mesh & + &filter.") + end if + ! Get pointer to mesh i_mesh = t % filters(k) % int_bins(1) m => meshes(i_mesh) @@ -2699,16 +2588,14 @@ contains if (MT > 1) then t % score_bins(j) = MT else - message = "Invalid MT on : " // & - trim(sarray(l)) - call fatal_error() + call fatal_error("Invalid MT on : " & + &// trim(sarray(l))) end if else ! Specified score was not an integer - message = "Unknown scoring function: " // & - trim(sarray(l)) - call fatal_error() + call fatal_error("Unknown scoring function: " & + &// trim(sarray(l))) end if end select @@ -2719,9 +2606,8 @@ contains ! Deallocate temporary string array of scores deallocate(sarray) else - message = "No specified on tally " // trim(to_str(t % id)) & - // "." - call fatal_error() + call fatal_error("No specified on tally " & + &// trim(to_str(t % id)) // ".") end if ! ======================================================================= @@ -2739,18 +2625,16 @@ contains ! If the estimator was set to an analog estimator, this means the ! tally needs post-collision information if (t % estimator == ESTIMATOR_ANALOG) then - message = "Cannot use track-length estimator for tally " & - // to_str(t % id) - call fatal_error() + call fatal_error("Cannot use track-length estimator for tally " & + &// to_str(t % id)) end if ! Set estimator to track-length estimator t % estimator = ESTIMATOR_TRACKLENGTH case default - message = "Invalid estimator '" // trim(temp_str) & - // "' on tally " // to_str(t % id) - call fatal_error() + call fatal_error("Invalid estimator '" // trim(temp_str) & + &// "' on tally " // to_str(t % id)) end select end if @@ -2770,32 +2654,36 @@ contains subroutine read_plots_xml() - integer i, j - integer n_cols, col_id, n_comp, n_masks + integer :: i, j + integer :: n_cols, col_id, n_comp, n_masks, n_meshlines + integer :: meshid + integer :: i_mesh integer, allocatable :: iarray(:) logical :: file_exists ! does plots.xml file exist? character(MAX_LINE_LEN) :: filename ! absolute path to plots.xml character(MAX_LINE_LEN) :: temp_str + character(MAX_WORD_LEN) :: meshtype type(ObjectPlot), pointer :: pl => null() type(Node), pointer :: doc => null() type(Node), pointer :: node_plot => null() type(Node), pointer :: node_col => null() type(Node), pointer :: node_mask => null() + type(Node), pointer :: node_meshlines => null() type(NodeList), pointer :: node_plot_list => null() type(NodeList), pointer :: node_col_list => null() type(NodeList), pointer :: node_mask_list => null() + type(NodeList), pointer :: node_meshline_list => null() ! Check if plots.xml exists filename = trim(path_input) // "plots.xml" inquire(FILE=filename, EXIST=file_exists) if (.not. file_exists) then - message = "Plots XML file '" // trim(filename) // "' does not exist!" - call fatal_error() + call fatal_error("Plots XML file '" // trim(filename) & + &// "' does not exist!") end if ! Display output message - message = "Reading plot XML file..." - call write_message(5) + call write_message("Reading plot XML file...", 5) ! Parse plots.xml file call open_xmldoc(doc, filename) @@ -2817,31 +2705,28 @@ contains if (check_for_node(node_plot, "id")) then call get_node_value(node_plot, "id", pl % id) else - message = "Must specify plot id in plots XML file." - call fatal_error() + call fatal_error("Must specify plot id in plots XML file.") end if ! Check to make sure 'id' hasn't been used if (plot_dict % has_key(pl % id)) then - message = "Two or more plots use the same unique ID: " // & - to_str(pl % id) - call fatal_error() + call fatal_error("Two or more plots use the same unique ID: " & + &// to_str(pl % id)) end if ! Copy plot type temp_str = 'slice' if (check_for_node(node_plot, "type")) & call get_node_value(node_plot, "type", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) select case (trim(temp_str)) case ("slice") pl % type = PLOT_TYPE_SLICE case ("voxel") pl % type = PLOT_TYPE_VOXEL case default - message = "Unsupported plot type '" // trim(temp_str) & - // "' in plot " // trim(to_str(pl % id)) - call fatal_error() + call fatal_error("Unsupported plot type '" // trim(temp_str) & + &// "' in plot " // trim(to_str(pl % id))) end select ! Set output file path @@ -2862,33 +2747,29 @@ contains if (get_arraysize_integer(node_plot, "pixels") == 2) then call get_node_array(node_plot, "pixels", pl % pixels(1:2)) else - message = " must be length 2 in slice plot " // & - trim(to_str(pl % id)) - call fatal_error() + call fatal_error(" must be length 2 in slice plot " & + &// trim(to_str(pl % id))) end if else if (pl % type == PLOT_TYPE_VOXEL) then if (get_arraysize_integer(node_plot, "pixels") == 3) then call get_node_array(node_plot, "pixels", pl % pixels(1:3)) else - message = " must be length 3 in voxel plot " // & - trim(to_str(pl % id)) - call fatal_error() + call fatal_error(" must be length 3 in voxel plot " & + &// trim(to_str(pl % id))) end if end if ! Copy plot background color if (check_for_node(node_plot, "background")) then if (pl % type == PLOT_TYPE_VOXEL) then - message = "Background color ignored in voxel plot " // & - trim(to_str(pl % id)) - call warning() + if (master) call warning("Background color ignored in voxel plot " & + &// trim(to_str(pl % id))) end if if (get_arraysize_integer(node_plot, "background") == 3) then call get_node_array(node_plot, "background", pl % not_found % rgb) else - message = "Bad background RGB " & - // "in plot " // trim(to_str(pl % id)) - call fatal_error() + call fatal_error("Bad background RGB in plot " & + &// trim(to_str(pl % id))) end if else pl % not_found % rgb = (/ 255, 255, 255 /) @@ -2899,7 +2780,7 @@ contains temp_str = 'xy' if (check_for_node(node_plot, "basis")) & call get_node_value(node_plot, "basis", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) select case (trim(temp_str)) case ("xy") pl % basis = PLOT_BASIS_XY @@ -2908,9 +2789,8 @@ contains case ("yz") pl % basis = PLOT_BASIS_YZ case default - message = "Unsupported plot basis '" // trim(temp_str) & - // "' in plot " // trim(to_str(pl % id)) - call fatal_error() + call fatal_error("Unsupported plot basis '" // trim(temp_str) & + &// "' in plot " // trim(to_str(pl % id))) end select end if @@ -2918,9 +2798,8 @@ contains if (get_arraysize_double(node_plot, "origin") == 3) then call get_node_array(node_plot, "origin", pl % origin) else - message = "Origin must be length 3 " & - // "in plot " // trim(to_str(pl % id)) - call fatal_error() + call fatal_error("Origin must be length 3 in plot " & + &// trim(to_str(pl % id))) end if ! Copy plotting width @@ -2928,17 +2807,15 @@ contains if (get_arraysize_double(node_plot, "width") == 2) then call get_node_array(node_plot, "width", pl % width(1:2)) else - message = " must be length 2 in slice plot " // & - trim(to_str(pl % id)) - call fatal_error() + call fatal_error(" must be length 2 in slice plot " & + &// trim(to_str(pl % id))) end if else if (pl % type == PLOT_TYPE_VOXEL) then if (get_arraysize_double(node_plot, "width") == 3) then call get_node_array(node_plot, "width", pl % width(1:3)) else - message = " must be length 3 in voxel plot " // & - trim(to_str(pl % id)) - call fatal_error() + call fatal_error(" must be length 3 in voxel plot " & + &// trim(to_str(pl % id))) end if end if @@ -2946,7 +2823,7 @@ contains temp_str = "cell" if (check_for_node(node_plot, "color")) & call get_node_value(node_plot, "color", temp_str) - call lower_case(temp_str) + temp_str = to_lower(temp_str) select case (trim(temp_str)) case ("cell") @@ -2969,9 +2846,8 @@ contains end do case default - message = "Unsupported plot color type '" // trim(temp_str) & - // "' in plot " // trim(to_str(pl % id)) - call fatal_error() + call fatal_error("Unsupported plot color type '" // trim(temp_str) & + &// "' in plot " // trim(to_str(pl % id))) end select ! Get the number of nodes and get a list of them @@ -2982,9 +2858,8 @@ contains if (n_cols /= 0) then if (pl % type == PLOT_TYPE_VOXEL) then - message = "Color specifications ignored in voxel plot " // & - trim(to_str(pl % id)) - call warning() + if (master) call warning("Color specifications ignored in voxel & + &plot " // trim(to_str(pl % id))) end if do j = 1, n_cols @@ -2994,18 +2869,16 @@ contains ! Check and make sure 3 values are specified for RGB if (get_arraysize_double(node_col, "rgb") /= 3) then - message = "Bad RGB " & - // "in plot " // trim(to_str(pl % id)) - call fatal_error() + call fatal_error("Bad RGB in plot " & + &// trim(to_str(pl % id))) end if ! Ensure that there is an id for this color specification if (check_for_node(node_col, "id")) then call get_node_value(node_col, "id", col_id) else - message = "Must specify id for color specification in plot " // & - trim(to_str(pl % id)) - call fatal_error() + call fatal_error("Must specify id for color specification in & + &plot " // trim(to_str(pl % id))) end if ! Add RGB @@ -3015,9 +2888,8 @@ contains col_id = cell_dict % get_key(col_id) call get_node_array(node_col, "rgb", pl % colors(col_id) % rgb) else - message = "Could not find cell " // trim(to_str(col_id)) // & - " specified in plot " // trim(to_str(pl % id)) - call fatal_error() + call fatal_error("Could not find cell " // trim(to_str(col_id)) & + &// " specified in plot " // trim(to_str(pl % id))) end if else if (pl % color_by == PLOT_COLOR_MATS) then @@ -3026,31 +2898,153 @@ contains col_id = material_dict % get_key(col_id) call get_node_array(node_col, "rgb", pl % colors(col_id) % rgb) else - message = "Could not find material " // trim(to_str(col_id)) // & - " specified in plot " // trim(to_str(pl % id)) - call fatal_error() + call fatal_error("Could not find material " & + &// trim(to_str(col_id)) // " specified in plot " & + &// trim(to_str(pl % id))) end if end if end do end if + ! Deal with meshlines + call get_node_list(node_plot, "meshlines", node_meshline_list) + n_meshlines = get_list_size(node_meshline_list) + if (n_meshlines /= 0) then + + if (pl % type == PLOT_TYPE_VOXEL) then + call warning("Meshlines ignored in voxel plot " & + &// trim(to_str(pl % id))) + end if + + select case(n_meshlines) + case (0) + ! Skip if no meshlines are specified + case (1) + + ! Get pointer to meshlines + call get_list_item(node_meshline_list, 1, node_meshlines) + + ! Check mesh type + if (check_for_node(node_meshlines, "meshtype")) then + call get_node_value(node_meshlines, "meshtype", meshtype) + else + call fatal_error("Must specify a meshtype for meshlines & + &specification in plot " // trim(to_str(pl % id))) + end if + + ! Ensure that there is a linewidth for this meshlines specification + if (check_for_node(node_meshlines, "linewidth")) then + call get_node_value(node_meshlines, "linewidth", & + pl % meshlines_width) + else + call fatal_error("Must specify a linewidth for meshlines & + &specification in plot " // trim(to_str(pl % id))) + end if + + ! Check for color + if (check_for_node(node_meshlines, "color")) then + + ! Check and make sure 3 values are specified for RGB + if (get_arraysize_double(node_meshlines, "color") /= 3) then + call fatal_error("Bad RGB for meshlines color in plot " & + &// trim(to_str(pl % id))) + end if + + call get_node_array(node_meshlines, "color", & + pl % meshlines_color % rgb) + else + + pl % meshlines_color % rgb = (/ 0, 0, 0 /) + + end if + + ! Set mesh based on type + select case (trim(meshtype)) + case ('ufs') + + if (.not. associated(ufs_mesh)) then + call fatal_error("No UFS mesh for meshlines on plot " & + &// trim(to_str(pl % id))) + end if + + pl % meshlines_mesh => ufs_mesh + + case ('cmfd') + + if (.not. cmfd_run) then + call fatal_error("Need CMFD run to plot CMFD mesh for & + &meshlines on plot " // trim(to_str(pl % id))) + end if + + i_mesh = cmfd_tallies(1) % & + filters(cmfd_tallies(1) % find_filter(FILTER_MESH)) % & + int_bins(1) + pl % meshlines_mesh => meshes(i_mesh) + + case ('entropy') + + if (.not. associated(entropy_mesh)) then + call fatal_error("No entropy mesh for meshlines on plot " & + &// trim(to_str(pl % id))) + end if + + if (.not. allocated(entropy_mesh % dimension)) then + call fatal_error("No dimension specified on entropy mesh & + &for meshlines on plot " // trim(to_str(pl % id))) + end if + + pl % meshlines_mesh => entropy_mesh + + case ('tally') + + ! Ensure that there is a mesh id if the type is tally + if (check_for_node(node_meshlines, "id")) then + call get_node_value(node_meshlines, "id", meshid) + else + call fatal_error("Must specify a mesh id for meshlines tally & + &mesh specification in plot " // trim(to_str(pl % id))) + end if + + ! Check if the specified tally mesh exists + if (mesh_dict % has_key(meshid)) then + pl % meshlines_mesh => meshes(mesh_dict % get_key(meshid)) + if (meshes(meshid) % type /= LATTICE_RECT) then + call fatal_error("Non-rectangular mesh specified in & + &meshlines for plot " // trim(to_str(pl % id))) + end if + else + call fatal_error("Could not find mesh " & + &// trim(to_str(meshid)) // " specified in meshlines for & + &plot " // trim(to_str(pl % id))) + end if + + case default + call fatal_error("Invalid type for meshlines on plot " & + &// trim(to_str(pl % id)) // ": " // trim(meshtype)) + end select + + case default + call fatal_error("Mutliple meshlines specified in plot " & + &// trim(to_str(pl % id))) + end select + + end if + ! Deal with masks call get_node_list(node_plot, "mask", node_mask_list) n_masks = get_list_size(node_mask_list) if (n_masks /= 0) then if (pl % type == PLOT_TYPE_VOXEL) then - message = "Mask ignored in voxel plot " // & - trim(to_str(pl % id)) - call warning() + if (master) call warning("Mask ignored in voxel plot " & + &// trim(to_str(pl % id))) end if select case(n_masks) case default - message = "Mutliple masks" // & - " specified in plot " // trim(to_str(pl % id)) - call fatal_error() + call fatal_error("Mutliple masks specified in plot " & + &// trim(to_str(pl % id))) case (1) ! Get pointer to mask @@ -3060,9 +3054,8 @@ contains n_comp = 0 n_comp = get_arraysize_integer(node_mask, "components") if (n_comp == 0) then - message = "Missing in mask of plot " // & - trim(to_str(pl % id)) - call fatal_error() + call fatal_error("Missing in mask of plot " & + &// trim(to_str(pl % id))) end if allocate(iarray(n_comp)) call get_node_array(node_mask, "components", iarray) @@ -3077,9 +3070,9 @@ contains if (cell_dict % has_key(col_id)) then iarray(j) = cell_dict % get_key(col_id) else - message = "Could not find cell " // trim(to_str(col_id)) // & - " specified in the mask in plot " // trim(to_str(pl % id)) - call fatal_error() + call fatal_error("Could not find cell " & + &// trim(to_str(col_id)) // " specified in the mask in & + &plot " // trim(to_str(pl % id))) end if else if (pl % color_by == PLOT_COLOR_MATS) then @@ -3087,9 +3080,9 @@ contains if (material_dict % has_key(col_id)) then iarray(j) = material_dict % get_key(col_id) else - message = "Could not find material " // trim(to_str(col_id)) // & - " specified in the mask in plot " // trim(to_str(pl % id)) - call fatal_error() + call fatal_error("Could not find material " & + &// trim(to_str(col_id)) // " specified in the mask in & + &plot " // trim(to_str(pl % id))) end if end if @@ -3101,9 +3094,8 @@ contains if (check_for_node(node_mask, "background")) then call get_node_array(node_mask, "background", pl % colors(j) % rgb) else - message = "Missing in mask of plot " // & - trim(to_str(pl % id)) - call fatal_error() + call fatal_error("Missing in mask of plot " & + &// trim(to_str(pl % id))) end if end if end do @@ -3147,13 +3139,11 @@ contains inquire(FILE=path_cross_sections, EXIST=file_exists) if (.not. file_exists) then ! Could not find cross_sections.xml file - message = "Cross sections XML file '" // trim(path_cross_sections) // & - "' does not exist!" - call fatal_error() + call fatal_error("Cross sections XML file '" & + &// trim(path_cross_sections) // "' does not exist!") end if - message = "Reading cross sections XML file..." - call write_message(5) + call write_message("Reading cross sections XML file...", 5) ! Parse cross_sections.xml file call open_xmldoc(doc, path_cross_sections) @@ -3179,8 +3169,8 @@ contains elseif (len_trim(temp_str) == 0) then filetype = ASCII else - message = "Unknown filetype in cross_sections.xml: " // trim(temp_str) - call fatal_error() + call fatal_error("Unknown filetype in cross_sections.xml: " & + &// trim(temp_str)) end if ! copy default record length and entries for binary files @@ -3195,8 +3185,8 @@ contains ! Allocate xs_listings array if (n_listings == 0) then - message = "No ACE table listings present in cross_sections.xml file!" - call fatal_error() + call fatal_error("No ACE table listings present in cross_sections.xml & + &file!") else allocate(xs_listings(n_listings)) end if @@ -3254,8 +3244,7 @@ contains if (check_for_node(node_ace, "path")) then call get_node_value(node_ace, "path", temp_str) else - message = "Path missing for isotope " // listing % name - call fatal_error() + call fatal_error("Path missing for isotope " // listing % name) end if if (starts_with(temp_str, '/')) then @@ -3269,18 +3258,18 @@ contains end if ! create dictionary entry for both name and alias - call xs_listing_dict % add_key(listing % name, i) + call xs_listing_dict % add_key(to_lower(listing % name), i) if (check_for_node(node_ace, "alias")) then - call xs_listing_dict % add_key(listing % alias, i) + call xs_listing_dict % add_key(to_lower(listing % alias), i) end if end do ! Check that 0K nuclides are listed in the cross_sections.xml file do i = 1, n_res_scatterers_total if (.not. xs_listing_dict % has_key(trim(nuclides_0K(i) % name_0K))) then - message = "Could not find nuclide " // trim(nuclides_0K(i) % name_0K) // & - " in cross_sections.xml file!" - call fatal_error() + call fatal_error("Could not find nuclide " & + &// trim(nuclides_0K(i) % name_0K) & + &// " in cross_sections.xml file!") end if end do @@ -3309,9 +3298,8 @@ contains character(2) :: element_name element_name = name(1:2) - call lower_case(element_name) - select case (element_name) + select case (to_lower(element_name)) case ('h') call list_names % append('1001.' // xs) call list_density % append(density * 0.999885_8) @@ -4120,8 +4108,7 @@ contains call list_density % append(density * 0.992742_8) case default - message = "Cannot expand element: " // name - call fatal_error() + call fatal_error("Cannot expand element: " // name) end select diff --git a/src/interpolation.F90 b/src/interpolation.F90 index 748e187a34..5c44ed7c3d 100644 --- a/src/interpolation.F90 +++ b/src/interpolation.F90 @@ -3,7 +3,6 @@ module interpolation use constants use endf_header, only: Tab1 use error, only: fatal_error - use global, only: message use search, only: binary_search use string, only: to_str @@ -118,8 +117,7 @@ contains r = (log(x) - log(x0))/(log(x1) - log(x0)) y = exp((1-r)*log(y0) + r*log(y1)) case default - message = "Unsupported interpolation scheme: " // to_str(interp) - call fatal_error() + call fatal_error("Unsupported interpolation scheme: " // to_str(interp)) end select end function interpolate_tab1_array @@ -204,8 +202,7 @@ contains r = (log(x) - log(x0))/(log(x1) - log(x0)) y = exp((1-r)*log(y0) + r*log(y1)) case default - message = "Unsupported interpolation scheme: " // to_str(interp) - call fatal_error() + call fatal_error("Unsupported interpolation scheme: " // to_str(interp)) end select end function interpolate_tab1_object diff --git a/src/matrix_header.F90 b/src/matrix_header.F90 index 24307b10bb..3e3d4ad0db 100644 --- a/src/matrix_header.F90 +++ b/src/matrix_header.F90 @@ -20,19 +20,22 @@ module matrix_header # endif logical :: petsc_active contains - procedure :: create => matrix_create - procedure :: destroy => matrix_destroy - procedure :: add_value => matrix_add_value - procedure :: new_row => matrix_new_row - procedure :: assemble => matrix_assemble - procedure :: get_row => matrix_get_row - procedure :: get_col => matrix_get_col + procedure :: create => matrix_create + procedure :: destroy => matrix_destroy + procedure :: add_value => matrix_add_value + procedure :: new_row => matrix_new_row + procedure :: assemble => matrix_assemble + procedure :: get_row => matrix_get_row + procedure :: get_col => matrix_get_col procedure :: vector_multiply => matrix_vector_multiply -#ifdef PETSC - procedure :: transpose => matrix_transpose + procedure :: search_indices => matrix_search_indices + procedure :: write => matrix_write + procedure :: copy => matrix_copy +# ifdef PETSC procedure :: setup_petsc => matrix_setup_petsc procedure :: write_petsc_binary => matrix_write_petsc_binary -#endif + procedure :: transpose => matrix_transpose +# endif end type matrix #ifdef PETSC @@ -359,4 +362,87 @@ contains end subroutine matrix_vector_multiply +!=============================================================================== +! MATRIX_SEARCH_INDICES searches for an index in column corresponding to a row +!=============================================================================== + + subroutine matrix_search_indices(self, row, col, idx, found) + + class(Matrix), intent(inout) :: self + integer, intent(in) :: row + integer, intent(in) :: col + integer, intent(out) :: idx + logical, intent(out) :: found + + integer :: j + + found = .false. + + COLS: do j = self % get_row(row), self % get_row(row + 1) - 1 + + if (self % get_col(j) == col) then + idx = j + found = .true. + exit + end if + + end do COLS + + end subroutine matrix_search_indices + +!=============================================================================== +! MATRIX_WRITE writes a matrix to file +!=============================================================================== + + subroutine matrix_write(self, filename) + + character(*), intent(in) :: filename + class(Matrix), intent(inout) :: self + + integer :: unit_ + integer :: i + integer :: j + + open(newunit=unit_, file=filename) + + do i = 1, self % n + do j = self % get_row(i), self % get_row(i + 1) - 1 + write(unit_,*) i, self % get_col(j), self % val(j) + end do + end do + + close(unit_) + + end subroutine matrix_write + +!=============================================================================== +! MATRIX_COPY copies a matrix +!=============================================================================== + + subroutine matrix_copy(self, mattocopy) + + class(Matrix), intent(inout) :: self + type(Matrix), intent(in) :: mattocopy + + ! Set n and nnz + self % n_count = mattocopy % n_count + self % nz_count = mattocopy % nz_count + self % n = mattocopy % n + self % nnz = mattocopy % nnz + + ! Allocate vectors + if (.not.allocated(self % row)) allocate(self % row(self % n + 1)) + if (.not.allocated(self % col)) allocate(self % col(self % nnz)) + if (.not.allocated(self % val)) allocate(self % val(self % nnz)) + + ! Set PETSc active to false + self % petsc_active = .false. + + ! Copy over data + self % row = mattocopy % row + self % col = mattocopy % col + self % val = mattocopy % val + + end subroutine matrix_copy + end module matrix_header diff --git a/src/output.F90 b/src/output.F90 index 834c28e188..4d0ef7edd4 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -13,7 +13,7 @@ module output use mesh, only: mesh_indices_to_bin, bin_to_mesh_indices use particle_header, only: LocalCoord, Particle use plot_header - use string, only: upper_case, to_str + use string, only: to_upper, to_str use tally_header, only: TallyObject implicit none @@ -130,8 +130,7 @@ contains if (mod(len_trim(msg),2) == 0) m = m + 1 ! convert line to upper case - line = msg - call upper_case(line) + line = to_upper(msg) ! print header based on level select case (header_level) @@ -194,8 +193,9 @@ contains ! standard output stream. !=============================================================================== - subroutine write_message(level) + subroutine write_message(message, level) + character(*) :: message integer, optional :: level ! verbosity level integer :: i_start ! starting position @@ -1588,8 +1588,8 @@ contains write(ou,102) "Leakage Fraction", global_tallies(LEAKAGE) % sum, & global_tallies(LEAKAGE) % sum_sq else - message = "Could not compute uncertainties -- only one active batch simulated!" - call warning() + if (master) call warning("Could not compute uncertainties -- only one & + &active batch simulated!") write(ou,103) "k-effective (Collision)", global_tallies(K_COLLISION) % sum write(ou,103) "k-effective (Track-length)", global_tallies(K_TRACKLENGTH) % sum diff --git a/src/particle_restart.F90 b/src/particle_restart.F90 index db2490dd2a..6ff5d63c2d 100644 --- a/src/particle_restart.F90 +++ b/src/particle_restart.F90 @@ -16,8 +16,7 @@ module particle_restart private public :: run_particle_restart - ! Binary file - type(BinaryOutput) :: pr + type(BinaryOutput) :: pr ! Binary file contains @@ -73,9 +72,8 @@ contains type(Particle), intent(inout) :: p ! Write meessage - message = "Loading particle restart file " // trim(path_particle_restart) & - // "..." - call write_message(1) + call write_message("Loading particle restart file " & + &// trim(path_particle_restart) // "...", 1) ! Open file call pr % file_open(path_particle_restart, 'r') diff --git a/src/physics.F90 b/src/physics.F90 index 04cc66afa2..8fa0575cdc 100644 --- a/src/physics.F90 +++ b/src/physics.F90 @@ -47,17 +47,15 @@ contains ! Display information about collision if (verbosity >= 10 .or. trace) then - message = " " // trim(reaction_name(p % event_MT)) // " with " // & - trim(adjustl(nuclides(p % event_nuclide) % name)) // & - ". Energy = " // trim(to_str(p % E * 1e6_8)) // " eV." - call write_message() + call write_message(" " // trim(reaction_name(p % event_MT)) & + &// " with " // trim(adjustl(nuclides(p % event_nuclide) % name)) & + &// ". Energy = " // trim(to_str(p % E * 1e6_8)) // " eV.") end if ! check for very low energy if (p % E < 1.0e-100_8) then p % alive = .false. - message = "Killing neutron with extremely low energy" - call warning() + if (master) call warning("Killing neutron with extremely low energy") end if end subroutine collision @@ -159,8 +157,7 @@ contains ! Check to make sure that a nuclide was sampled if (i > mat % n_nuclides) then call write_particle_restart(p) - message = "Did not sample any nuclide during collision." - call fatal_error() + call fatal_error("Did not sample any nuclide during collision.") end if ! Find atom density @@ -368,9 +365,8 @@ contains ! Check to make sure inelastic scattering reaction sampled if (i > nuc % n_reaction) then call write_particle_restart(p) - message = "Did not sample any reaction for nuclide " // & - trim(nuc % name) - call fatal_error() + call fatal_error("Did not sample any reaction for nuclide " & + &// trim(nuc % name)) end if rxn => nuc % reactions(i) @@ -721,8 +717,8 @@ contains mu = sab % inelastic_data(l) % mu(k, j) else - message = "Invalid secondary energy mode on S(a,b) table " // & - trim(sab % name) + call fatal_error("Invalid secondary energy mode on S(a,b) table " & + &// trim(sab % name)) end if ! (inelastic secondary energy treatment) end if ! (elastic or inelastic) @@ -974,8 +970,7 @@ contains end do case default - message = "Not a recognized resonance scattering treatment!" - call fatal_error() + call fatal_error("Not a recognized resonance scattering treatment!") end select end subroutine sample_target_velocity @@ -1093,8 +1088,7 @@ contains call get_mesh_indices(ufs_mesh, p % coord0 % xyz, ijk, in_mesh) if (.not. in_mesh) then call write_particle_restart(p) - message = "Source site outside UFS mesh!" - call fatal_error() + call fatal_error("Source site outside UFS mesh!") end if if (source_frac(1,ijk(1),ijk(2),ijk(3)) /= ZERO) then @@ -1119,10 +1113,9 @@ contains ! Check for fission bank size getting hit if (n_bank + nu > size(fission_bank)) then - message = "Maximum number of sites in fission bank reached. This can & - &result in irreproducible results using different numbers of & - &processes/threads." - call warning() + if (master) call warning("Maximum number of sites in fission bank & + &reached. This can result in irreproducible results using different & + &numbers of processes/threads.") end if ! Bank source neutrons @@ -1247,9 +1240,8 @@ contains n_sample = n_sample + 1 if (n_sample == MAX_SAMPLE) then ! call write_particle_restart(p) - message = "Resampled energy distribution maximum number of " // & - "times for nuclide " // nuc % name - call fatal_error() + call fatal_error("Resampled energy distribution maximum number of " & + &// "times for nuclide " // nuc % name) end if end do @@ -1274,9 +1266,8 @@ contains n_sample = n_sample + 1 if (n_sample == MAX_SAMPLE) then ! call write_particle_restart(p) - message = "Resampled energy distribution maximum number of " // & - "times for nuclide " // nuc % name - call fatal_error() + call fatal_error("Resampled energy distribution maximum number of " & + &// "times for nuclide " // nuc % name) end if end do @@ -1457,8 +1448,7 @@ contains end if else ! call write_particle_restart(p) - message = "Unknown interpolation type: " // trim(to_str(interp)) - call fatal_error() + call fatal_error("Unknown interpolation type: " // trim(to_str(interp))) end if ! Because of floating-point roundoff, it may be possible for mu to be @@ -1469,8 +1459,8 @@ contains else ! call write_particle_restart(p) - message = "Unknown angular distribution type: " // trim(to_str(type)) - call fatal_error() + call fatal_error("Unknown angular distribution type: " & + &// trim(to_str(type))) end if end function sample_angle @@ -1617,9 +1607,8 @@ contains NET = int(edist % data(3 + 2*NR + NE)) if (NR > 0) then ! call write_particle_restart(p) - message = "Multiple interpolation regions not supported while & - &attempting to sample equiprobable energy bins." - call fatal_error() + call fatal_error("Multiple interpolation regions not supported while & + &attempting to sample equiprobable energy bins.") end if ! determine index on incoming energy grid and interpolation factor @@ -1682,14 +1671,12 @@ contains NR = int(edist % data(1)) NE = int(edist % data(2 + 2*NR)) if (NR == 1) then - message = "Assuming linear-linear interpolation when sampling & - &continuous tabular distribution" - call warning() + if (master) call warning("Assuming linear-linear interpolation when & + &sampling continuous tabular distribution") else if (NR > 1) then ! call write_particle_restart(p) - message = "Multiple interpolation regions not supported while & - &attempting to sample continuous tabular distribution." - call fatal_error() + call fatal_error("Multiple interpolation regions not supported while & + &attempting to sample continuous tabular distribution.") end if ! find energy bin and calculate interpolation factor -- if the energy is @@ -1747,9 +1734,8 @@ contains if (ND > 0) then ! discrete lines present ! call write_particle_restart(p) - message = "Discrete lines in continuous tabular distributed not & - &yet supported" - call fatal_error() + call fatal_error("Discrete lines in continuous tabular distributed not & + &yet supported") end if ! determine outgoing energy bin @@ -1789,8 +1775,7 @@ contains end if else ! call write_particle_restart(p) - message = "Unknown interpolation type: " // trim(to_str(INTT)) - call fatal_error() + call fatal_error("Unknown interpolation type: " // trim(to_str(INTT))) end if ! Now interpolate between incident energy bins i and i + 1 @@ -1831,8 +1816,7 @@ contains n_sample = n_sample + 1 if (n_sample == MAX_SAMPLE) then ! call write_particle_restart(p) - message = "Too many rejections on Maxwell fission spectrum." - call fatal_error() + call fatal_error("Too many rejections on Maxwell fission spectrum.") end if end do @@ -1864,8 +1848,7 @@ contains n_sample = n_sample + 1 if (n_sample == MAX_SAMPLE) then ! call write_particle_restart(p) - message = "Too many rejections on evaporation spectrum." - call fatal_error() + call fatal_error("Too many rejections on evaporation spectrum.") end if end do @@ -1906,8 +1889,7 @@ contains n_sample = n_sample + 1 if (n_sample == MAX_SAMPLE) then ! call write_particle_restart(p) - message = "Too many rejections on Watt spectrum." - call fatal_error() + call fatal_error("Too many rejections on Watt spectrum.") end if end do @@ -1917,8 +1899,7 @@ contains if (.not. present(mu_out)) then ! call write_particle_restart(p) - message = "Law 44 called without giving mu_out as argument." - call fatal_error() + call fatal_error("Law 44 called without giving mu_out as argument.") end if ! read number of interpolation regions and incoming energies @@ -1926,9 +1907,8 @@ contains NE = int(edist % data(2 + 2*NR)) if (NR > 0) then ! call write_particle_restart(p) - message = "Multiple interpolation regions not supported while & - &attempting to sample Kalbach-Mann distribution." - call fatal_error() + call fatal_error("Multiple interpolation regions not supported while & + &attempting to sample Kalbach-Mann distribution.") end if ! find energy bin and calculate interpolation factor -- if the energy is @@ -1987,9 +1967,8 @@ contains if (ND > 0) then ! discrete lines present ! call write_particle_restart(p) - message = "Discrete lines in continuous tabular distributed not & - &yet supported" - call fatal_error() + call fatal_error("Discrete lines in continuous tabular distributed not & + &yet supported") end if ! determine outgoing energy bin @@ -2043,8 +2022,7 @@ contains KM_A = A_k + (A_k1 - A_k)*(E_out - E_l_k)/(E_l_k1 - E_l_k) else ! call write_particle_restart() - message = "Unknown interpolation type: " // trim(to_str(INTT)) - call fatal_error() + call fatal_error("Unknown interpolation type: " // trim(to_str(INTT))) end if ! Now interpolate between incident energy bins i and i + 1 @@ -2070,8 +2048,7 @@ contains if (.not. present(mu_out)) then ! call write_particle_restart() - message = "Law 61 called without giving mu_out as argument." - call fatal_error() + call fatal_error("Law 61 called without giving mu_out as argument.") end if ! read number of interpolation regions and incoming energies @@ -2079,9 +2056,8 @@ contains NE = int(edist % data(2 + 2*NR)) if (NR > 0) then ! call write_particle_restart() - message = "Multiple interpolation regions not supported while & - &attempting to sample correlated energy-angle distribution." - call fatal_error() + call fatal_error("Multiple interpolation regions not supported while & + &attempting to sample correlated energy-angle distribution.") end if ! find energy bin and calculate interpolation factor -- if the energy is @@ -2140,9 +2116,8 @@ contains if (ND > 0) then ! discrete lines present ! call write_particle_restart() - message = "Discrete lines in continuous tabular distributed not & - &yet supported" - call fatal_error() + call fatal_error("Discrete lines in continuous tabular distributed not & + &yet supported") end if ! determine outgoing energy bin @@ -2183,8 +2158,7 @@ contains end if else ! call write_particle_restart() - message = "Unknown interpolation type: " // trim(to_str(INTT)) - call fatal_error() + call fatal_error("Unknown interpolation type: " // trim(to_str(INTT))) end if ! Now interpolate between incident energy bins i and i + 1 @@ -2247,8 +2221,7 @@ contains end if else ! call write_particle_restart() - message = "Unknown interpolation type: " // trim(to_str(JJ)) - call fatal_error() + call fatal_error("Unknown interpolation type: " // trim(to_str(JJ))) end if case (66) diff --git a/src/plot.F90 b/src/plot.F90 index e7642314f9..1297b746c4 100644 --- a/src/plot.F90 +++ b/src/plot.F90 @@ -5,6 +5,7 @@ module plot use geometry, only: find_cell, check_cell_overlap use geometry_header, only: Cell, BASE_UNIVERSE use global + use mesh, only: get_mesh_indices use output, only: write_message use particle_header, only: deallocate_coord, Particle use plot_header @@ -30,8 +31,8 @@ contains pl => plots(i) ! Display output message - message = "Processing plot " // trim(to_str(pl % id)) // "..." - call write_message(5) + call write_message("Processing plot " // trim(to_str(pl % id)) & + &// "...", 5) if (pl % type == PLOT_TYPE_SLICE) then ! create 2d image @@ -118,27 +119,24 @@ contains call init_image(img) call allocate_image(img, pl % pixels(1), pl % pixels(2)) + in_pixel = pl % width(1)/dble(pl % pixels(1)) + out_pixel = pl % width(2)/dble(pl % pixels(2)) + if (pl % basis == PLOT_BASIS_XY) then in_i = 1 out_i = 2 - in_pixel = pl % width(1)/dble(pl % pixels(1)) - out_pixel = pl % width(2)/dble(pl % pixels(2)) xyz(1) = pl % origin(1) - pl % width(1) / 2.0 xyz(2) = pl % origin(2) + pl % width(2) / 2.0 xyz(3) = pl % origin(3) else if (pl % basis == PLOT_BASIS_XZ) then in_i = 1 out_i = 3 - in_pixel = pl % width(1)/dble(pl % pixels(1)) - out_pixel = pl % width(2)/dble(pl % pixels(2)) xyz(1) = pl % origin(1) - pl % width(1) / 2.0 xyz(2) = pl % origin(2) xyz(3) = pl % origin(3) + pl % width(2) / 2.0 else if (pl % basis == PLOT_BASIS_YZ) then in_i = 2 out_i = 3 - in_pixel = pl % width(1)/dble(pl % pixels(1)) - out_pixel = pl % width(2)/dble(pl % pixels(2)) xyz(1) = pl % origin(1) xyz(2) = pl % origin(2) - pl % width(1) / 2.0 xyz(3) = pl % origin(3) + pl % width(2) / 2.0 @@ -169,6 +167,9 @@ contains p % coord0 % xyz(out_i) = p % coord0 % xyz(out_i) - out_pixel end do + ! Draw tally mesh boundaries on the image if requested + if (associated(pl % meshlines_mesh)) call draw_mesh_lines(pl, img) + ! Write out the ppm to a file call output_ppm(pl,img) @@ -180,6 +181,111 @@ contains end subroutine create_ppm +!=============================================================================== +! DRAW_MESH_LINES draws mesh line boundaries on an image +!=============================================================================== + subroutine draw_mesh_lines(pl, img) + + type(ObjectPlot), pointer, intent(in) :: pl + type(Image), intent(inout) :: img + + logical :: in_mesh + integer :: out_, in_ ! pixel location + integer :: r, g, b ! RGB color for meshlines pixels + integer :: outrange(2), inrange(2) ! range of pixel locations + integer :: i, j ! loop indices + integer :: plus + integer :: ijk_ll(3) ! mesh bin ijk indicies of plot lower left + integer :: ijk_ur(3) ! mesh bin ijk indicies of plot upper right + integer :: outer, inner + real(8) :: frac + real(8) :: width(3) ! real widths of the plot + real(8) :: xyz_ll_plot(3) ! lower left xyz of plot image + real(8) :: xyz_ur_plot(3) ! upper right xyz of plot image + real(8) :: xyz_ll(3) ! lower left xyz + real(8) :: xyz_ur(3) ! upper right xyz + type(StructuredMesh), pointer :: m => null() + + m => pl % meshlines_mesh + + r = pl % meshlines_color % rgb(1) + g = pl % meshlines_color % rgb(2) + b = pl % meshlines_color % rgb(3) + + select case (pl % basis) + case(PLOT_BASIS_XY) + outer = 1 + inner = 2 + case(PLOT_BASIS_XZ) + outer = 1 + inner = 3 + case(PLOT_BASIS_YZ) + outer = 2 + inner = 3 + end select + + xyz_ll_plot = pl % origin + xyz_ur_plot = pl % origin + + xyz_ll_plot(outer) = pl % origin(1) - pl % width(1) / 2.0 + xyz_ll_plot(inner) = pl % origin(2) - pl % width(2) / 2.0 + xyz_ur_plot(outer) = pl % origin(1) + pl % width(1) / 2.0 + xyz_ur_plot(inner) = pl % origin(2) + pl % width(2) / 2.0 + + width = xyz_ur_plot - xyz_ll_plot + + call get_mesh_indices(m, xyz_ll_plot, ijk_ll(:m % n_dimension), in_mesh) + call get_mesh_indices(m, xyz_ur_plot, ijk_ur(:m % n_dimension), in_mesh) + + ! sweep through all meshbins on this plane and draw borders + do i = ijk_ll(outer), ijk_ur(outer) + do j = ijk_ll(inner), ijk_ur(inner) + ! check if we're in the mesh for this ijk + if (i > 0 .and. i <= m % dimension(outer) .and. & + j > 0 .and. j <= m % dimension(inner)) then + + ! get xyz's of lower left and upper right of this mesh cell + xyz_ll(outer) = m % lower_left(outer) + m % width(outer) * (i - 1) + xyz_ll(inner) = m % lower_left(inner) + m % width(inner) * (j - 1) + xyz_ur(outer) = m % lower_left(outer) + m % width(outer) * i + xyz_ur(inner) = m % lower_left(inner) + m % width(inner) * j + + ! map the xyz ranges to pixel ranges + + frac = (xyz_ll(outer) - xyz_ll_plot(outer)) / width(outer) + outrange(1) = int(frac * real(img % width, 8)) + frac = (xyz_ur(outer) - xyz_ll_plot(outer)) / width(outer) + outrange(2) = int(frac * real(img % width, 8)) + + frac = (xyz_ll(inner) - xyz_ll_plot(inner)) / width(inner) + inrange(1) = int(frac * real(img % height, 8)) + frac = (xyz_ur(inner) - xyz_ll_plot(inner)) / width(inner) + inrange(2) = int(frac * real(img % height, 8)) + + ! draw lines + do out_ = outrange(1), outrange(2) + do plus = 0, pl % meshlines_width + call set_pixel(img, out_, inrange(1) + plus, r, g, b) + call set_pixel(img, out_, inrange(2) + plus, r, g, b) + call set_pixel(img, out_, inrange(1) - plus, r, g, b) + call set_pixel(img, out_, inrange(2) - plus, r, g, b) + end do + end do + do in_ = inrange(1), inrange(2) + do plus = 0, pl % meshlines_width + call set_pixel(img, outrange(1) + plus, in_, r, g, b) + call set_pixel(img, outrange(2) + plus, in_, r, g, b) + call set_pixel(img, outrange(1) - plus, in_, r, g, b) + call set_pixel(img, outrange(2) - plus, in_, r, g, b) + end do + end do + + end if + end do + end do + + end subroutine draw_mesh_lines + !=============================================================================== ! OUTPUT_PPM writes out a previously generated image to a PPM file !=============================================================================== diff --git a/src/plot_header.F90 b/src/plot_header.F90 index 6956bfaf93..68eb89a11a 100644 --- a/src/plot_header.F90 +++ b/src/plot_header.F90 @@ -1,6 +1,7 @@ module plot_header use constants + use mesh_header, only: StructuredMesh implicit none @@ -25,6 +26,9 @@ module plot_header real(8) :: width(3) ! xyz widths of plot integer :: basis ! direction of plot slice integer :: pixels(3) ! pixel width/height of plot slice + integer :: meshlines_width ! pixel width of meshlines + type(StructuredMesh), pointer :: meshlines_mesh => null() ! mesh to plot + type(ObjectColor) :: meshlines_color ! Color for meshlines type(ObjectColor) :: not_found ! color for positions where no cell found type(ObjectColor), allocatable :: colors(:) ! colors of cells/mats end type ObjectPlot diff --git a/src/relaxng/cmfd.rnc b/src/relaxng/cmfd.rnc index cbcb7709ae..bbb7fc9e6b 100644 --- a/src/relaxng/cmfd.rnc +++ b/src/relaxng/cmfd.rnc @@ -22,15 +22,9 @@ element cmfd { element feedback { xsd:boolean }? & - element n_cmfd_procs { xsd:int }? & - - element reset { xsd:boolean }? & - - element balance { xsd:boolean }? & - element downscatter { xsd:boolean }? & - element run_2grp { xsd:boolean }? & + element dhat_reset { xsd:boolean }? & element solver { xsd:string }? & @@ -40,8 +34,6 @@ element cmfd { element power_monitor { xsd:boolean }? & - element write_balance { xsd:boolean }? & - element write_matrices { xsd:boolean }? & element run_adjoint { xsd:boolean }? & @@ -50,9 +42,18 @@ element cmfd { element begin { xsd:int }? & - element inactive { xsd:boolean }? & + element tally_reset { list { xsd:int+ } }? & - element active_flush { xsd:int }? & + element display { xsd:string }? & + + element spectral { xsd:double }? & + + element shift { xsd:double }? & + + element ktol { xsd: double }? & + + element stol { xsd: double }? & + + element gauss_seidel_tolerance { list { xsd:double+ } }? - element keff_tol { xsd:double }? } diff --git a/src/relaxng/plots.rnc b/src/relaxng/plots.rnc index 3c29ba4e0b..27b2ae7f72 100644 --- a/src/relaxng/plots.rnc +++ b/src/relaxng/plots.rnc @@ -3,29 +3,38 @@ element plots { (element id { xsd:int } | attribute id { xsd:int })? & (element filename { xsd:string { maxLength = "50" } } | attribute filename { xsd:string { maxLength = "50" } })? & - (element type { "slice" } | attribute type { "slice" })? & + (element type { "slice" | "voxel" } | + attribute type { "slice" | "voxel" })? & (element color { ( "cell" | "mat" | "material" ) } | attribute color { ( "cell" | "mat" | "material" ) })? & - (element origin { list { xsd:double+ } } | + (element origin { list { xsd:double+ } } | attribute origin { list { xsd:double+ } })? & - (element width { list { xsd:double+ } } | + (element width { list { xsd:double+ } } | attribute width { list { xsd:double+ } })? & (element basis { ( "xy" | "yz" | "xz" ) } | attribute basis { ( "xy" | "yz" | "xz" ) })? & - (element pixels { list { xsd:int+ } } | + (element pixels { list { xsd:int+ } } | attribute pixels { list { xsd:int+ } })? & (element background { list { xsd:int+ } } | attribute background { list { xsd:int+ } })? & element col_spec { (element id { xsd:int } | attribute id { xsd:int }) & - (element rgb { list { xsd:int+ } } | + (element rgb { list { xsd:int+ } } | attribute rgb { list { xsd:int+ } }) }* & element mask { - (element components { list { xsd:int+ } } | + (element components { list { xsd:int+ } } | attribute components { list { xsd:int+ } }) & - (element background { list { xsd:int+ } } | + (element background { list { xsd:int+ } } | attribute background { list { xsd:int+ } }) + }* & + element meshlines { + (element meshtype { ( "tally" | "entropy" | "ufs" | "cmfd" ) } | + attribute meshtype { ( "tally" | "entropy" | "ufs" | "cmfd" ) }) & + (element id { xsd:int } | attribute id { xsd:int })? & + (element linewidth { xsd:int } | attribute linewidth { xsd:int }) & + (element color { list { xsd:int+ } } | + attribute color { list { xsd:int+ } })? }* }* } diff --git a/src/relaxng/settings.rnc b/src/relaxng/settings.rnc index dd4b91dc7d..707ecbbc80 100644 --- a/src/relaxng/settings.rnc +++ b/src/relaxng/settings.rnc @@ -85,7 +85,8 @@ element settings { attribute interplation { xsd:string { maxLength = "10" } })? & (element parameters { list { xsd:double+ } } | attribute parameters { list { xsd:double+ } })? - }? + }? & + (element write_initial { xsd:boolean } | attribute write_initial { xsd:boolean })? }? & element state_point { diff --git a/src/search.F90 b/src/search.F90 index 962874a7f9..c8fc71d365 100644 --- a/src/search.F90 +++ b/src/search.F90 @@ -1,7 +1,7 @@ module search + use constants use error, only: fatal_error - use global, only: message integer, parameter :: MAX_ITERATION = 64 @@ -32,8 +32,7 @@ contains R = n if (val < array(L) .or. val > array(R)) then - message = "Value outside of array during binary search" - call fatal_error() + call fatal_error("Value outside of array during binary search") end if n_iteration = 0 @@ -60,8 +59,8 @@ contains ! check for large number of iterations n_iteration = n_iteration + 1 if (n_iteration == MAX_ITERATION) then - message = "Reached maximum number of iterations on binary search." - call fatal_error() + call fatal_error("Reached maximum number of iterations on binary & + &search.") end if end do @@ -85,8 +84,7 @@ contains R = n if (val < array(L) .or. val > array(R)) then - message = "Value outside of array during binary search" - call fatal_error() + call fatal_error("Value outside of array during binary search") end if n_iteration = 0 @@ -113,8 +111,8 @@ contains ! check for large number of iterations n_iteration = n_iteration + 1 if (n_iteration == MAX_ITERATION) then - message = "Reached maximum number of iterations on binary search." - call fatal_error() + call fatal_error("Reached maximum number of iterations on binary & + &search.") end if end do @@ -138,8 +136,7 @@ contains R = n if (val < array(L) .or. val > array(R)) then - message = "Value outside of array during binary search" - call fatal_error() + call fatal_error("Value outside of array during binary search") end if n_iteration = 0 @@ -166,8 +163,8 @@ contains ! check for large number of iterations n_iteration = n_iteration + 1 if (n_iteration == MAX_ITERATION) then - message = "Reached maximum number of iterations on binary search." - call fatal_error() + call fatal_error("Reached maximum number of iterations on binary & + &search.") end if end do diff --git a/src/solver_interface.F90 b/src/solver_interface.F90 index b629a0789f..043b3fd116 100644 --- a/src/solver_interface.F90 +++ b/src/solver_interface.F90 @@ -1,7 +1,6 @@ module solver_interface use error, only: fatal_error - use global, only: message use matrix_header, only: Matrix use vector_header, only: Vector diff --git a/src/source.F90 b/src/source.F90 index 5f928436b9..07c1bfb411 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -27,21 +27,21 @@ contains subroutine initialize_source() + character(MAX_FILE_LEN) :: filename integer(8) :: i ! loop index over bank sites integer(8) :: id ! particle id integer(4) :: itmp ! temporary integer type(Bank), pointer :: src => null() ! source bank site type(BinaryOutput) :: sp ! statepoint/source binary file - message = "Initializing source particles..." - call write_message(6) + call write_message("Initializing source particles...", 6) if (path_source /= '') then ! Read the source from a binary file instead of sampling from some ! assumed source distribution - message = 'Reading source file from ' // trim(path_source) // '...' - call write_message(6) + call write_message('Reading source file from ' // trim(path_source) & + &// '...', 6) ! Open the binary file call sp % file_open(path_source, 'r', serial = .false.) @@ -51,8 +51,8 @@ contains ! Check to make sure this is a source file if (itmp /= FILETYPE_SOURCE) then - message = "Specified starting source file not a source file type." - call fatal_error() + call fatal_error("Specified starting source file not a source file & + &type.") end if ! Read in the source bank @@ -76,6 +76,19 @@ contains end do end if + ! Write out initial source + if (write_initial_source) then + call write_message('Writing out initial source guess...', 1) +#ifdef HDF5 + filename = trim(path_output) // 'initial_source.h5' +#else + filename = trim(path_output) // 'initial_source.binary' +#endif + call sp % file_create(filename, serial = .false.) + call sp % write_source_bank() + call sp % file_close() + end if + end subroutine initialize_source !=============================================================================== @@ -127,9 +140,8 @@ contains if (.not. found) then num_resamples = num_resamples + 1 if (num_resamples == MAX_EXTSRC_RESAMPLES) then - message = "Maximum number of external source spatial resamples & - &reached!" - call fatal_error() + call fatal_error("Maximum number of external source spatial & + &resamples reached!") end if end if end do @@ -157,9 +169,8 @@ contains if (.not. found) then num_resamples = num_resamples + 1 if (num_resamples == MAX_EXTSRC_RESAMPLES) then - message = "Maximum number of external source spatial resamples & - &reached!" - call fatal_error() + call fatal_error("Maximum number of external source spatial & + &resamples reached!") end if cycle end if @@ -192,8 +203,7 @@ contains site % uvw = external_source % params_angle case default - message = "No angle distribution specified for external source!" - call fatal_error() + call fatal_error("No angle distribution specified for external source!") end select ! Sample energy distribution @@ -224,8 +234,7 @@ contains end do case default - message = "No energy distribution specified for external source!" - call fatal_error() + call fatal_error("No energy distribution specified for external source!") end select ! Set the random number generator back to the tracking stream. diff --git a/src/state_point.F90 b/src/state_point.F90 index 0e8d9e5617..495fd11384 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -26,7 +26,7 @@ module state_point implicit none - type(BinaryOutput) :: sp ! statepoint/source output file + type(BinaryOutput) :: sp ! Statepoint/source output file contains @@ -54,8 +54,7 @@ contains #endif ! Write message - message = "Creating state point " // trim(filename) // "..." - call write_message(1) + call write_message("Creating state point " // trim(filename) // "...", 1) if (master) then ! Create statepoint file @@ -314,8 +313,8 @@ contains #endif ! Write message for new file creation - message = "Creating source file " // trim(filename) // "..." - call write_message(1) + call write_message("Creating source file " // trim(filename) // "...", & + &1) ! Create separate source file call sp % file_create(filename, serial = .false.) @@ -359,8 +358,7 @@ contains #endif ! Write message for new file creation - message = "Creating source file " // trim(filename) // "..." - call write_message(1) + call write_message("Creating source file " // trim(filename) // "...", 1) ! Always create this file because it will be overwritten call sp % file_create(filename, serial = .false.) @@ -530,8 +528,8 @@ contains type(TallyObject), pointer :: t => null() ! Write message - message = "Loading state point " // trim(path_state_point) // "..." - call write_message(1) + call write_message("Loading state point " // trim(path_state_point) & + &// "...", 1) ! Open file for reading call sp % file_open(path_state_point, 'r', serial = .false.) @@ -543,9 +541,8 @@ contains ! current version call sp % read_data(int_array(1), "revision") if (int_array(1) /= REVISION_STATEPOINT) then - message = "State point version does not match current version " & - // "in OpenMC." - call fatal_error() + call fatal_error("State point version does not match current version & + &in OpenMC.") end if ! Read OpenMC version @@ -554,9 +551,8 @@ contains call sp % read_data(int_array(3), "version_release") if (int_array(1) /= VERSION_MAJOR .or. int_array(2) /= VERSION_MINOR & .or. int_array(3) /= VERSION_RELEASE) then - message = "State point file was created with a different version " & - // "of OpenMC." - call warning() + if (master) call warning("State point file was created with a different & + &version of OpenMC.") end if ! Read date and time @@ -668,8 +664,8 @@ contains ! Check size of tally results array if (int_array(1) /= t % total_score_bins .and. & int_array(2) /= t % total_filter_bins) then - message = "Input file tally structure is different from restart." - call fatal_error() + call fatal_error("Input file tally structure is different from & + &restart.") end if ! Read number of filters @@ -742,8 +738,8 @@ contains ! Check to make sure source bank is present if (path_source_point == path_state_point .and. .not. source_present) then - message = "Source bank must be contained in statepoint restart file" - call fatal_error() + call fatal_error("Source bank must be contained in statepoint restart & + &file") end if ! Read tallies to master @@ -755,8 +751,8 @@ contains ! Read number of global tallies call sp % read_data(int_array(1), "n_global_tallies", collect=.false.) if (int_array(1) /= N_GLOBAL_TALLIES) then - message = "Number of global tallies does not match in state point." - call fatal_error() + call fatal_error("Number of global tallies does not match in state & + &point.") end if ! Read global tally data @@ -792,8 +788,8 @@ contains call sp % file_close() ! Write message - message = "Loading source file " // trim(path_source_point) // "..." - call write_message(1) + call write_message("Loading source file " // trim(path_source_point) & + &// "...", 1) ! Open source file call sp % file_open(path_source_point, 'r', serial = .false.) diff --git a/src/string.F90 b/src/string.F90 index f78b94d07f..cff69bb16e 100644 --- a/src/string.F90 +++ b/src/string.F90 @@ -2,7 +2,7 @@ module string use constants, only: MAX_WORDS, MAX_LINE_LEN, ERROR_INT, ERROR_REAL use error, only: fatal_error, warning - use global, only: message + use global, only: master implicit none @@ -49,9 +49,8 @@ contains if (i_end > 0) then n = n + 1 if (i_end - i_start + 1 > len(words(n))) then - message = "The word '" // string(i_start:i_end) // & - "' is longer than the space allocated for it." - call warning() + if (master) call warning("The word '" // string(i_start:i_end) & + &// "' is longer than the space allocated for it.") end if words(n) = string(i_start:i_end) ! reset indices @@ -152,37 +151,47 @@ contains ! LOWER_CASE converts a string to all lower case characters !=============================================================================== - elemental subroutine lower_case(word) + elemental function to_lower(word) result(word_lower) - character(*), intent(inout) :: word + character(*), intent(in) :: word + character(len=len(word)) :: word_lower integer :: i integer :: ic do i = 1, len(word) ic = ichar(word(i:i)) - if (ic >= 65 .and. ic <= 90) word(i:i) = char(ic+32) + if (ic >= 65 .and. ic <= 90) then + word_lower(i:i) = char(ic+32) + else + word_lower(i:i) = word(i:i) + end if end do - end subroutine lower_case + end function to_lower !=============================================================================== ! UPPER_CASE converts a string to all upper case characters !=============================================================================== - elemental subroutine upper_case(word) + elemental function to_upper(word) result(word_upper) - character(*), intent(inout) :: word + character(*), intent(in) :: word + character(len=len(word)) :: word_upper integer :: i integer :: ic do i = 1, len(word) ic = ichar(word(i:i)) - if (ic >= 97 .and. ic <= 122) word(i:i) = char(ic-32) + if (ic >= 97 .and. ic <= 122) then + word_upper(i:i) = char(ic-32) + else + word_upper(i:i) = word(i:i) + end if end do - end subroutine upper_case + end function to_upper !=============================================================================== ! ZERO_PADDED returns a string of the input integer padded with zeros to the @@ -200,8 +209,8 @@ function zero_padded(num, n_digits) result(str) ! Make sure n_digits is reasonable. 10 digits is the maximum needed for the ! largest integer(4). if (n_digits > 10) then - message = 'zero_padded called with an unreasonably large n_digits (>10)' - call fatal_error() + call fatal_error('zero_padded called with an unreasonably large & + &n_digits (>10)') end if ! Write a format string of the form '(In.m)' where n is the max width and @@ -317,7 +326,7 @@ end function zero_padded ! the loop automatically exits when n_digits = 10. n_digits = n_digits + 1 end do - + end function count_digits !=============================================================================== @@ -349,21 +358,21 @@ end function zero_padded end function int8_to_str !=============================================================================== -! STR_TO_INT converts a string to an integer. +! STR_TO_INT converts a string to an integer. !=============================================================================== function str_to_int(str) result(num) character(*), intent(in) :: str integer(8) :: num - + character(5) :: fmt integer :: w integer :: ioError ! Determine width of string w = len_trim(str) - + ! Create format specifier for reading string write(UNIT=fmt, FMT='("(I",I2,")")') w @@ -404,7 +413,7 @@ end function zero_padded integer :: decimal ! number of places after decimal integer :: width ! total field width - real(8) :: num2 ! absolute value of number + real(8) :: num2 ! absolute value of number character(9) :: fmt ! format specifier for writing number ! set default field width diff --git a/src/tally.F90 b/src/tally.F90 index f86d67d168..87f5a2cab7 100644 --- a/src/tally.F90 +++ b/src/tally.F90 @@ -21,8 +21,7 @@ module tally implicit none - ! Tally map positioning array - integer :: position(N_FILTER_TYPES - 3) = 0 + integer :: position(N_FILTER_TYPES - 3) = 0 ! Tally map positioning array !$omp threadprivate(position) contains @@ -871,8 +870,8 @@ contains end do REACTION_LOOP else - message = "Invalid score type on tally " // to_str(t % id) // "." - call fatal_error() + call fatal_error("Invalid score type on tally " & + &// to_str(t % id) // ".") end if end select @@ -1009,8 +1008,8 @@ contains end do else - message = "Invalid score type on tally " // to_str(t % id) // "." - call fatal_error() + call fatal_error("Invalid score type on tally " & + &// to_str(t % id) // ".") end if end select end if @@ -1209,8 +1208,8 @@ contains end do REACTION_LOOP else - message = "Invalid score type on tally " // to_str(t % id) // "." - call fatal_error() + call fatal_error("Invalid score type on tally " & + &// to_str(t % id) // ".") end if end select @@ -1361,8 +1360,8 @@ contains end do else - message = "Invalid score type on tally " // to_str(t % id) // "." - call fatal_error() + call fatal_error("Invalid score type on tally " & + &// to_str(t % id) // ".") end if end select @@ -1707,9 +1706,8 @@ contains case (SCORE_EVENTS) score = ONE case default - message = "Invalid score type on tally " // & - to_str(t % id) // "." - call fatal_error() + call fatal_error("Invalid score type on tally " & + &// to_str(t % id) // ".") end select else @@ -1783,9 +1781,8 @@ contains case (SCORE_EVENTS) score = ONE case default - message = "Invalid score type on tally " // & - to_str(t % id) // "." - call fatal_error() + call fatal_error("Invalid score type on tally " & + &// to_str(t % id) // ".") end select end if @@ -2223,8 +2220,7 @@ contains ! Check for errors if (filter_index <= 0 .or. filter_index > & t % total_filter_bins) then - message = "Score index outside range." - call fatal_error() + call fatal_error("Score index outside range.") end if ! Add to surface current tally @@ -2563,18 +2559,16 @@ contains ! check to see if any of the active tally lists has been allocated if (active_tallies % size() > 0) then - message = "Active tallies should not exist before CMFD tallies!" - call fatal_error() + call fatal_error("Active tallies should not exist before CMFD tallies!") else if (active_analog_tallies % size() > 0) then - message = 'Active analog tallies should not exist before CMFD tallies!' - call fatal_error() + call fatal_error('Active analog tallies should not exist before CMFD & + &tallies!') else if (active_tracklength_tallies % size() > 0) then - message = "Active tracklength tallies should not exist before CMFD & - &tallies!" - call fatal_error() + call fatal_error("Active tracklength tallies should not exist before & + &CMFD tallies!") else if (active_current_tallies % size() > 0) then - message = "Active current tallies should not exist before CMFD tallies!" - call fatal_error() + call fatal_error("Active current tallies should not exist before CMFD & + &tallies!") end if do i = 1, n_cmfd_tallies diff --git a/src/tracking.F90 b/src/tracking.F90 index 68a0e3d09a..9545d41ee6 100644 --- a/src/tracking.F90 +++ b/src/tracking.F90 @@ -39,8 +39,7 @@ contains ! Display message if high verbosity or trace is on if (verbosity >= 9 .or. trace) then - message = "Simulating Particle " // trim(to_str(p % id)) - call write_message() + call write_message("Simulating Particle " // trim(to_str(p % id))) end if ! If the cell hasn't been determined based on the particle's location, @@ -50,8 +49,7 @@ contains ! Particle couldn't be located if (.not. found_cell) then - message = "Could not locate particle " // trim(to_str(p % id)) - call fatal_error() + call fatal_error("Could not locate particle " // trim(to_str(p % id))) end if ! set birth cell attribute @@ -198,9 +196,8 @@ contains ! If particle has too many events, display warning and kill it n_event = n_event + 1 if (n_event == MAX_EVENTS) then - message = "Particle " // trim(to_str(p%id)) // " underwent maximum & - &number of events." - call warning() + if (master) call warning("Particle " // trim(to_str(p%id)) & + &// " underwent maximum number of events.") p % alive = .false. end if diff --git a/src/utils/setup.py b/src/utils/setup.py index 1000e509b3..4c2f1ae246 100644 --- a/src/utils/setup.py +++ b/src/utils/setup.py @@ -3,7 +3,7 @@ from distutils.core import setup setup(name='statepoint', - version='0.6.0', + version='0.6.1', description='OpenMC StatePoint', author='Paul Romano', author_email='paul.k.romano@gmail.com', diff --git a/src/vector_header.F90 b/src/vector_header.F90 index e050981c76..05cc7e3cde 100644 --- a/src/vector_header.F90 +++ b/src/vector_header.F90 @@ -21,10 +21,11 @@ module vector_header procedure :: create => vector_create procedure :: destroy => vector_destroy procedure :: add_value => vector_add_value -#ifdef PETSC + procedure :: copy => vector_copy +# ifdef PETSC procedure :: setup_petsc => vector_setup_petsc procedure :: write_petsc_binary => vector_write_petsc_binary -#endif +# endif end type Vector #ifdef PETSC @@ -127,4 +128,28 @@ contains end subroutine vector_write_petsc_binary #endif +!=============================================================================== +! VECTOR_COPY allocates a separate vector and copies +!=============================================================================== + + subroutine vector_copy(self, vectocopy) + + class(Vector), target, intent(inout) :: self + type(Vector), intent(in) :: vectocopy + + ! Preallocate vector + if (.not.allocated(self % data)) allocate(self % data(vectocopy % n)) + self % val => self % data(1:vectocopy % n) + + ! Set n + self % n = vectocopy % n + + ! Copy values + self % val = vectocopy % val + + ! Petsc is default not active + self % petsc_active = .false. + + end subroutine vector_copy + end module vector_header diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index 64842a69ec..1a24bc14b3 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -2,7 +2,6 @@ module xml_interface use constants, only: MAX_LINE_LEN use error, only: fatal_error - use global, only: message use openmc_fox implicit none @@ -201,9 +200,8 @@ contains ! Leave if it was not found if (.not. found) then - message = "Node " // node_name // " not part of Node " // & - getNodeName(ptr) // "." - call fatal_error() + call fatal_error("Node " // node_name // " not part of Node " & + &// getNodeName(ptr) // ".") end if ! Extract value @@ -234,9 +232,8 @@ contains ! Leave if it was not found if (.not. found) then - message = "Node " // node_name // " not part of Node " // & - getNodeName(ptr) // "." - call fatal_error() + call fatal_error("Node " // node_name // " not part of Node " & + &// getNodeName(ptr) // ".") end if ! Extract value @@ -267,9 +264,8 @@ contains ! Leave if it was not found if (.not. found) then - message = "Node " // node_name // " not part of Node " // & - getNodeName(ptr) // "." - call fatal_error() + call fatal_error("Node " // node_name // " not part of Node " & + &// getNodeName(ptr) // ".") end if ! Extract value @@ -300,9 +296,8 @@ contains ! Leave if it was not found if (.not. found) then - message = "Node " // node_name // " not part of Node " // & - getNodeName(ptr) // "." - call fatal_error() + call fatal_error("Node " // node_name // " not part of Node " & + &// getNodeName(ptr) // ".") end if ! Extract value @@ -333,9 +328,8 @@ contains ! Leave if it was not found if (.not. found) then - message = "Node " // node_name // " not part of Node " // & - getNodeName(ptr) // "." - call fatal_error() + call fatal_error("Node " // node_name // " not part of Node " & + &// getNodeName(ptr) // ".") end if ! Extract value @@ -366,9 +360,8 @@ contains ! Leave if it was not found if (.not. found) then - message = "Node " // node_name // " not part of Node " // & - getNodeName(ptr) // "." - call fatal_error() + call fatal_error("Node " // node_name // " not part of Node " & + &// getNodeName(ptr) // ".") end if ! Extract value @@ -399,9 +392,8 @@ contains ! Leave if it was not found if (.not. found) then - message = "Node " // node_name // " not part of Node " // & - getNodeName(ptr) // "." - call fatal_error() + call fatal_error("Node " // node_name // " not part of Node " // & + getNodeName(ptr) // ".") end if ! Extract value @@ -432,9 +424,8 @@ contains ! Leave if it was not found if (.not. found) then - message = "Node " // node_name // " not part of Node " // & - getNodeName(ptr) // "." - call fatal_error() + call fatal_error("Node " // node_name // " not part of Node " & + &// getNodeName(ptr) // ".") end if ! Get the size @@ -461,9 +452,8 @@ contains ! Leave if it was not found if (.not. found) then - message = "Node " // node_name // " not part of Node " // & - getNodeName(ptr) // "." - call fatal_error() + call fatal_error("Node " // node_name // " not part of Node " & + &// getNodeName(ptr) // ".") end if ! Get the size @@ -490,9 +480,8 @@ contains ! Leave if it was not found if (.not. found) then - message = "Node " // node_name // " not part of Node " // & - getNodeName(ptr) // "." - call fatal_error() + call fatal_error("Node " // node_name // " not part of Node " // & + getNodeName(ptr) // ".") end if ! Get the size diff --git a/tests/test_basic/results.py b/tests/test_basic/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_basic/results.py +++ b/tests/test_basic/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_cmfd_feed/cmfd.xml b/tests/test_cmfd_feed/cmfd.xml index 51def74088..228b260a46 100644 --- a/tests/test_cmfd_feed/cmfd.xml +++ b/tests/test_cmfd_feed/cmfd.xml @@ -12,5 +12,5 @@ dominance power true - + 1.e-15 1.e-20 diff --git a/tests/test_cmfd_feed/results.py b/tests/test_cmfd_feed/results.py index 26bcb36d9f..bf969b4b04 100644 --- a/tests/test_cmfd_feed/results.py +++ b/tests/test_cmfd_feed/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_cmfd_jfnk/results.py b/tests/test_cmfd_jfnk/results.py index 26bcb36d9f..bf969b4b04 100644 --- a/tests/test_cmfd_jfnk/results.py +++ b/tests/test_cmfd_jfnk/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_cmfd_nofeed/cmfd.xml b/tests/test_cmfd_nofeed/cmfd.xml index c36a396043..eb6c2c721a 100644 --- a/tests/test_cmfd_nofeed/cmfd.xml +++ b/tests/test_cmfd_nofeed/cmfd.xml @@ -12,5 +12,6 @@ dominance power false + 1.e-15 1.e-20 diff --git a/tests/test_cmfd_nofeed/results.py b/tests/test_cmfd_nofeed/results.py index 26bcb36d9f..bf969b4b04 100644 --- a/tests/test_cmfd_nofeed/results.py +++ b/tests/test_cmfd_nofeed/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_confidence_intervals/results.py b/tests/test_confidence_intervals/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_confidence_intervals/results.py +++ b/tests/test_confidence_intervals/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_density_atombcm/results.py b/tests/test_density_atombcm/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_density_atombcm/results.py +++ b/tests/test_density_atombcm/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_density_atomcm3/results.py b/tests/test_density_atomcm3/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_density_atomcm3/results.py +++ b/tests/test_density_atomcm3/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_density_kgm3/results.py b/tests/test_density_kgm3/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_density_kgm3/results.py +++ b/tests/test_density_kgm3/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_density_sum/results.py b/tests/test_density_sum/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_density_sum/results.py +++ b/tests/test_density_sum/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_eigenvalue_genperbatch/results.py b/tests/test_eigenvalue_genperbatch/results.py index 36b1a0a5f2..e2bb49e235 100644 --- a/tests/test_eigenvalue_genperbatch/results.py +++ b/tests/test_eigenvalue_genperbatch/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_eigenvalue_no_inactive/results.py b/tests/test_eigenvalue_no_inactive/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_eigenvalue_no_inactive/results.py +++ b/tests/test_eigenvalue_no_inactive/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_energy_grid/results.py b/tests/test_energy_grid/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_energy_grid/results.py +++ b/tests/test_energy_grid/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_entropy/results.py b/tests/test_entropy/results.py index 9118335186..6c56b22c19 100644 --- a/tests/test_entropy/results.py +++ b/tests/test_entropy/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_filter_cell/results.py b/tests/test_filter_cell/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_filter_cell/results.py +++ b/tests/test_filter_cell/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_filter_cellborn/results.py b/tests/test_filter_cellborn/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_filter_cellborn/results.py +++ b/tests/test_filter_cellborn/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_filter_energy/results.py b/tests/test_filter_energy/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_filter_energy/results.py +++ b/tests/test_filter_energy/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_filter_energyout/results.py b/tests/test_filter_energyout/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_filter_energyout/results.py +++ b/tests/test_filter_energyout/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_filter_group_transfer/results.py b/tests/test_filter_group_transfer/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_filter_group_transfer/results.py +++ b/tests/test_filter_group_transfer/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_filter_material/results.py b/tests/test_filter_material/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_filter_material/results.py +++ b/tests/test_filter_material/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_filter_mesh_2d/results.py b/tests/test_filter_mesh_2d/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_filter_mesh_2d/results.py +++ b/tests/test_filter_mesh_2d/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_filter_mesh_3d/results.py b/tests/test_filter_mesh_3d/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_filter_mesh_3d/results.py +++ b/tests/test_filter_mesh_3d/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_filter_universe/results.py b/tests/test_filter_universe/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_filter_universe/results.py +++ b/tests/test_filter_universe/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_fixed_source/results.py b/tests/test_fixed_source/results.py index ed0c8fd3a7..39695b1e47 100644 --- a/tests/test_fixed_source/results.py +++ b/tests/test_fixed_source/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_lattice/results.py b/tests/test_lattice/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_lattice/results.py +++ b/tests/test_lattice/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_lattice_multiple/results.py b/tests/test_lattice_multiple/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_lattice_multiple/results.py +++ b/tests/test_lattice_multiple/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_natural_element/results.py b/tests/test_natural_element/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_natural_element/results.py +++ b/tests/test_natural_element/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_output/results.py b/tests/test_output/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_output/results.py +++ b/tests/test_output/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_particle_restart_eigval/results.py b/tests/test_particle_restart_eigval/results.py index 23670872be..5f522b0848 100644 --- a/tests/test_particle_restart_eigval/results.py +++ b/tests/test_particle_restart_eigval/results.py @@ -3,7 +3,7 @@ import sys # import particle restart -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import particle_restart as pr # read in particle restart file diff --git a/tests/test_particle_restart_fixed/results.py b/tests/test_particle_restart_fixed/results.py index e84cb88cff..84956f1d0b 100644 --- a/tests/test_particle_restart_fixed/results.py +++ b/tests/test_particle_restart_fixed/results.py @@ -3,7 +3,7 @@ import sys # import particle restart -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import particle_restart as pr # read in particle restart file diff --git a/tests/test_ptables_off/results.py b/tests/test_ptables_off/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_ptables_off/results.py +++ b/tests/test_ptables_off/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_reflective_cone/results.py b/tests/test_reflective_cone/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_reflective_cone/results.py +++ b/tests/test_reflective_cone/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_reflective_cylinder/results.py b/tests/test_reflective_cylinder/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_reflective_cylinder/results.py +++ b/tests/test_reflective_cylinder/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_reflective_plane/results.py b/tests/test_reflective_plane/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_reflective_plane/results.py +++ b/tests/test_reflective_plane/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_reflective_sphere/results.py b/tests/test_reflective_sphere/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_reflective_sphere/results.py +++ b/tests/test_reflective_sphere/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_resonance_scattering/results.py b/tests/test_resonance_scattering/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_resonance_scattering/results.py +++ b/tests/test_resonance_scattering/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_rotation/results.py b/tests/test_rotation/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_rotation/results.py +++ b/tests/test_rotation/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_salphabeta/results.py b/tests/test_salphabeta/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_salphabeta/results.py +++ b/tests/test_salphabeta/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_salphabeta_multiple/results.py b/tests/test_salphabeta_multiple/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_salphabeta_multiple/results.py +++ b/tests/test_salphabeta_multiple/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_MT/results.py b/tests/test_score_MT/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_score_MT/results.py +++ b/tests/test_score_MT/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_absorption/results.py b/tests/test_score_absorption/results.py index f4c32a893f..fa385bc37f 100644 --- a/tests/test_score_absorption/results.py +++ b/tests/test_score_absorption/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_current/results.py b/tests/test_score_current/results.py index f4c32a893f..fa385bc37f 100644 --- a/tests/test_score_current/results.py +++ b/tests/test_score_current/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_events/results.py b/tests/test_score_events/results.py index f4c32a893f..fa385bc37f 100644 --- a/tests/test_score_events/results.py +++ b/tests/test_score_events/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_fission/results.py b/tests/test_score_fission/results.py index f4c32a893f..fa385bc37f 100644 --- a/tests/test_score_fission/results.py +++ b/tests/test_score_fission/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_flux/results.py b/tests/test_score_flux/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_score_flux/results.py +++ b/tests/test_score_flux/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_flux_yn/results.py b/tests/test_score_flux_yn/results.py index f4c32a893f..fa385bc37f 100644 --- a/tests/test_score_flux_yn/results.py +++ b/tests/test_score_flux_yn/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_kappafission/results.py b/tests/test_score_kappafission/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_score_kappafission/results.py +++ b/tests/test_score_kappafission/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_nufission/results.py b/tests/test_score_nufission/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_score_nufission/results.py +++ b/tests/test_score_nufission/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_nuscatter/results.py b/tests/test_score_nuscatter/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_score_nuscatter/results.py +++ b/tests/test_score_nuscatter/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_nuscatter_n/results.py b/tests/test_score_nuscatter_n/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_score_nuscatter_n/results.py +++ b/tests/test_score_nuscatter_n/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_nuscatter_pn/results.py b/tests/test_score_nuscatter_pn/results.py index f4c32a893f..fa385bc37f 100644 --- a/tests/test_score_nuscatter_pn/results.py +++ b/tests/test_score_nuscatter_pn/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_nuscatter_yn/results.py b/tests/test_score_nuscatter_yn/results.py index f4c32a893f..fa385bc37f 100644 --- a/tests/test_score_nuscatter_yn/results.py +++ b/tests/test_score_nuscatter_yn/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_scatter/results.py b/tests/test_score_scatter/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_score_scatter/results.py +++ b/tests/test_score_scatter/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_scatter_n/results.py b/tests/test_score_scatter_n/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_score_scatter_n/results.py +++ b/tests/test_score_scatter_n/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_scatter_pn/results.py b/tests/test_score_scatter_pn/results.py index f4c32a893f..fa385bc37f 100644 --- a/tests/test_score_scatter_pn/results.py +++ b/tests/test_score_scatter_pn/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_scatter_yn/results.py b/tests/test_score_scatter_yn/results.py index f4c32a893f..fa385bc37f 100644 --- a/tests/test_score_scatter_yn/results.py +++ b/tests/test_score_scatter_yn/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_total/results.py b/tests/test_score_total/results.py index 93371960ae..fd27b041a2 100644 --- a/tests/test_score_total/results.py +++ b/tests/test_score_total/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_score_total_yn/results.py b/tests/test_score_total_yn/results.py index f4c32a893f..fa385bc37f 100644 --- a/tests/test_score_total_yn/results.py +++ b/tests/test_score_total_yn/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_seed/results.py b/tests/test_seed/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_seed/results.py +++ b/tests/test_seed/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_source_angle_mono/results.py b/tests/test_source_angle_mono/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_source_angle_mono/results.py +++ b/tests/test_source_angle_mono/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_source_energy_maxwell/results.py b/tests/test_source_energy_maxwell/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_source_energy_maxwell/results.py +++ b/tests/test_source_energy_maxwell/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_source_energy_mono/results.py b/tests/test_source_energy_mono/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_source_energy_mono/results.py +++ b/tests/test_source_energy_mono/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_source_file/results.py b/tests/test_source_file/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_source_file/results.py +++ b/tests/test_source_file/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_source_point/results.py b/tests/test_source_point/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_source_point/results.py +++ b/tests/test_source_point/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_sourcepoint_batch/results.py b/tests/test_sourcepoint_batch/results.py index 96984b2fae..1f9f4f1a8d 100644 --- a/tests/test_sourcepoint_batch/results.py +++ b/tests/test_sourcepoint_batch/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_sourcepoint_interval/results.py b/tests/test_sourcepoint_interval/results.py index 96984b2fae..1f9f4f1a8d 100644 --- a/tests/test_sourcepoint_interval/results.py +++ b/tests/test_sourcepoint_interval/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_sourcepoint_latest/results.py b/tests/test_sourcepoint_latest/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_sourcepoint_latest/results.py +++ b/tests/test_sourcepoint_latest/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_sourcepoint_restart/results.py b/tests/test_sourcepoint_restart/results.py index f4c32a893f..fa385bc37f 100644 --- a/tests/test_sourcepoint_restart/results.py +++ b/tests/test_sourcepoint_restart/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_statepoint_batch/results.py b/tests/test_statepoint_batch/results.py index 112c005167..39f6af12f2 100644 --- a/tests/test_statepoint_batch/results.py +++ b/tests/test_statepoint_batch/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_statepoint_interval/results.py b/tests/test_statepoint_interval/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_statepoint_interval/results.py +++ b/tests/test_statepoint_interval/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_statepoint_restart/results.py b/tests/test_statepoint_restart/results.py index dd9dedd035..b5daa76874 100644 --- a/tests/test_statepoint_restart/results.py +++ b/tests/test_statepoint_restart/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_statepoint_sourcesep/results.py b/tests/test_statepoint_sourcesep/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_statepoint_sourcesep/results.py +++ b/tests/test_statepoint_sourcesep/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_survival_biasing/results.py b/tests/test_survival_biasing/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_survival_biasing/results.py +++ b/tests/test_survival_biasing/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_tally_assumesep/results.py b/tests/test_tally_assumesep/results.py index 3cba794205..9b283b63a3 100644 --- a/tests/test_tally_assumesep/results.py +++ b/tests/test_tally_assumesep/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_tally_nuclides/results.py b/tests/test_tally_nuclides/results.py index 3aa0946a5d..4f155fb0f9 100644 --- a/tests/test_tally_nuclides/results.py +++ b/tests/test_tally_nuclides/results.py @@ -4,7 +4,7 @@ import sys import numpy as np # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_trace/results.py b/tests/test_trace/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_trace/results.py +++ b/tests/test_trace/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_trace/test_trace.py b/tests/test_trace/test_trace.py index 59488412c7..eab3f922aa 100644 --- a/tests/test_trace/test_trace.py +++ b/tests/test_trace/test_trace.py @@ -23,7 +23,7 @@ def test_run(): print(stdout) returncode = proc.returncode assert returncode == 0, 'OpenMC did not exit successfully.' - assert stdout.find('Simulating Particle 453') != -1 + assert stdout.find(b'Simulating Particle 453') != -1 def test_created_statepoint(): statepoint = glob.glob(os.path.join(cwd, 'statepoint.10.*')) diff --git a/tests/test_translation/results.py b/tests/test_translation/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_translation/results.py +++ b/tests/test_translation/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_uniform_fs/results.py b/tests/test_uniform_fs/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_uniform_fs/results.py +++ b/tests/test_uniform_fs/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_universe/results.py b/tests/test_universe/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_universe/results.py +++ b/tests/test_universe/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file diff --git a/tests/test_void/results.py b/tests/test_void/results.py index 8ff10971cd..be13ee66f1 100644 --- a/tests/test_void/results.py +++ b/tests/test_void/results.py @@ -3,7 +3,7 @@ import sys # import statepoint -sys.path.append('../../src/utils') +sys.path.insert(0, '../../src/utils') import statepoint # read in statepoint file