From b0ae16c9b2699e4b16e65ec69bd7554fc7af61a2 Mon Sep 17 00:00:00 2001 From: jdhughes-usgs Date: Wed, 19 Jul 2023 08:57:34 -0500 Subject: [PATCH] feat(ArrayReaders): add better error trapping for binary input data (#1301) * modify ArrayReaders to use standard errmsg variable * standardize store_error calls in ArrayReaders * modify list reader so cellid is only checked if no error condition * add binary array input examples to mf6io * add binary array input file specifications for each discretization type * add binary list input file specifications * add description of change to ReleaseNotes * Update doc/mf6io/framework/array_data.tex --------- Co-authored-by: langevin-usgs --- doc/ReleaseNotes/develop.tex | 10 +- doc/mf6io/body.tex | 10 +- doc/mf6io/{gwf => framework}/array_data.tex | 48 ++--- doc/mf6io/framework/binary_array_input.tex | 88 ++++++++ doc/mf6io/framework/binary_list_input.tex | 18 ++ doc/mf6io/{gwf => framework}/binaryoutput.tex | 0 doc/mf6io/{ => framework}/form_of_input.tex | 0 doc/mf6io/framework/list_data.tex | 38 ++++ .../{ => framework}/processing_of_input.tex | 0 doc/mf6io/gwf/gwf.tex | 2 - src/Utilities/ArrayReaders.f90 | 192 +++++++++++------- src/Utilities/ListReader.f90 | 8 +- 12 files changed, 293 insertions(+), 121 deletions(-) rename doc/mf6io/{gwf => framework}/array_data.tex (53%) create mode 100644 doc/mf6io/framework/binary_array_input.tex create mode 100644 doc/mf6io/framework/binary_list_input.tex rename doc/mf6io/{gwf => framework}/binaryoutput.tex (100%) rename doc/mf6io/{ => framework}/form_of_input.tex (100%) create mode 100644 doc/mf6io/framework/list_data.tex rename doc/mf6io/{ => framework}/processing_of_input.tex (100%) diff --git a/doc/ReleaseNotes/develop.tex b/doc/ReleaseNotes/develop.tex index 5993caf38d2..9dab6e9899a 100644 --- a/doc/ReleaseNotes/develop.tex +++ b/doc/ReleaseNotes/develop.tex @@ -17,13 +17,13 @@ % \item xxx %\end{itemize} - %\textbf{\underline{BUG FIXES AND OTHER CHANGES TO EXISTING FUNCTIONALITY}} \\ - %\underline{BASIC FUNCTIONALITY} - %\begin{itemize} - % \item xxx + \textbf{\underline{BUG FIXES AND OTHER CHANGES TO EXISTING FUNCTIONALITY}} \\ + \underline{BASIC FUNCTIONALITY} + \begin{itemize} + \item Improve error message if the size of data read from a binary array file is inconsistent with READARRAY control line and variable description keywords. % \item xxx % \item xxx - %\end{itemize} + \end{itemize} %\underline{INTERNAL FLOW PACKAGES} %\begin{itemize} diff --git a/doc/mf6io/body.tex b/doc/mf6io/body.tex index b34c0b6f72b..32f0150cf94 100644 --- a/doc/mf6io/body.tex +++ b/doc/mf6io/body.tex @@ -11,11 +11,15 @@ %General form of input instructions \SECTION{Form of Input Instructions} -\input{form_of_input.tex} +\input{framework/form_of_input.tex} +\input{framework/array_data.tex} +\input{framework/binary_array_input} +\input{framework/list_data.tex} +\input{framework/binary_list_input} %Processing of program input \SECTION{Processing of Program Input} -\input{processing_of_input.tex} +\input{framework/processing_of_input.tex} %Simulation name file \newpage @@ -59,7 +63,7 @@ %Binary files \newpage \SECTION{Description of Binary Output Files for the Groundwater Flow (GWF) and Groundwater Transport (GWT) Models } -\input{gwf/binaryoutput} +\input{framework/binaryoutput} \newpage \ifx\usgsdirector\undefined diff --git a/doc/mf6io/gwf/array_data.tex b/doc/mf6io/framework/array_data.tex similarity index 53% rename from doc/mf6io/gwf/array_data.tex rename to doc/mf6io/framework/array_data.tex index 4a2c400bc79..ac36dec8c92 100644 --- a/doc/mf6io/gwf/array_data.tex +++ b/doc/mf6io/framework/array_data.tex @@ -1,5 +1,5 @@ \subsection{Array Input (READARRAY)} -Some GWF Model packages require arrays of information to be provided by the user. This information is read using a generic READARRAY capability in \mf. Within this user guide, variables that are read with READARRAY are marked accordingly, as shown in example input instructions for a DATA block. +Some \mf Model packages require arrays of information to be provided by the user. This information is read using a generic READARRAY capability in \mf. Within this user guide, variables that are read with READARRAY are marked accordingly, as shown in example input instructions for a DATA block. \begin{lstlisting}[style=blockdefinition] BEGIN DATA @@ -96,7 +96,7 @@ \subsubsection{READARRAY Examples} \end{lstlisting} -Some arrays define information that is required for the entire model grid, or part of a model grid. This type of information is provided in a special type of data block called a ``GRIDDATA'' block. For example, hydraulic conductivity is required for every cell in the model grid. Hydraulic conductivity is read from a ``GRIDDATA'' block in the NPF Package input file. For GRIDDATA arrays with one value for every cell in the model grid, the arrays can optionally be read in a LAYERED format, in which an array is provided for each layer of the grid. Alternatively, the array can be read for the entire model grid. As an example, consider the GRIDDATA block for the IC Package shown below: +Some arrays define information that is required for the entire model grid, or part of a model grid. This type of information is provided in a special type of data block called a ``GRIDDATA'' block. For example, hydraulic conductivity is required for every cell in the model grid. Hydraulic conductivity is read from a ``GRIDDATA'' block in the \mf GWF NPF Package input file. For GRIDDATA arrays with one value for every cell in the model grid, the arrays can optionally be read in a LAYERED format, in which an array is provided for each layer of the grid. Alternatively, the array can be read for the entire model grid. As an example, consider the GRIDDATA block for the \mf GWF or GWT IC Package shown below: \lstinputlisting[style=blockdefinition]{./mf6ivar/tex/gwf-ic-griddata.dat} @@ -104,7 +104,7 @@ \subsubsection{READARRAY Examples} For a structured DIS model, the READARRAY utility is used to read arrays that are dimensioned to the full size of the grid (of size \texttt{nlay*nrow*ncol}). This utility first reads an array name, which associates the input to be read with the desired array. For these arrays, an optional keyword ``LAYERED'' can be located next to the array name. If ``LAYERED'' is detected, then a control line is provided for each layer and the array is filled with values for each model layer. If the ``LAYERED'' keyword is absent, then a single control line is used and the entire array is filled at once. -For example, the following block shows one way the starting head array (STRT) could be specified for a model with 4 layers. Following the array name and the ``LAYERED'' keyword are four control lines, one for each layer. +For example, the following block shows one way the \mf GWF model starting head array (STRT) could be specified for a model with 4 layers. Following the array name and the ``LAYERED'' keyword are four control lines, one for each layer. \begin{lstlisting}[style=inputfile] STRT LAYERED @@ -121,41 +121,21 @@ \subsubsection{READARRAY Examples} CONSTANT 10.0 #applies to all cells in the grid \end{lstlisting} -\subsection{List Input} -Some items consist of several variables, such as layer, row, column, stage, and conductance, for example. List input refers to a block of data with a separate item on each line. For some common list types, the first set of variables is a cell identifier (denoted as \texttt{cellid} in this guide), such as layer, row, and column. With lists, the input data for each item must start on a new line. All variables for an item are assumed to be contained in a single line. Each input variable has a data type, which can be Double Precision, Integer, or Character. Integers are whole numbers and must not include a decimal point or exponent. Double Precision numbers can include a decimal point and an exponent. If no decimal point is included in the entered value, then the decimal point is assumed to be at the right side of the value. Any printable character is allowed for character variables. - -Variables starting with the letters I-N are most commonly integers; however, in some instances, a character string may start with the letters I-N. Variables starting with the letters A-H and O-Z are primarily double precision numbers; however, these variable names may also be used for character data. In \mf all variables are explicitly declared within the source code, as opposed to the implicit type declaration in previous MODFLOW versions. This explicit declaration means that the variable type can be easily determined from the source code. - -Free formatting is used throughout the input instructions. With free format, values are not required to occupy a fixed number of columns in a line. Each value can occupy one or more columns as required to represent the value; however, the values must still be included in the prescribed order. One or more spaces, or a single comma optionally combined with spaces, must separate adjacent values. Also, a numeric value of zero must be explicitly represented with 0 and not by one or more spaces when free format is used, because detecting the difference between a space that represents 0 and a space that represents a value separator is not possible. Free format is similar to Fortran's list directed input. - -Two capabilities included in Fortran's list-directed input are not included in the free-format input implemented in \mf. Null values in which input values are left unchanged from their previous values are not allowed. In general, MODFLOW's input values are not defined prior to their input. A ``/'' cannot be used to terminate an input line without including values for all the variables; data values for all required input variables must be explicitly specified on an input line. For character data, MODFLOW's free format implementation is less stringent than the list-directed input of Fortran. Fortran requires character data to be delineated by apostrophes. MODFLOW does not require apostrophes unless a blank or a comma is part of a character variable. - -As an example of a list, consider the PERIOD block for the GHB Package. The input format is shown below: - -\lstinputlisting[style=blockdefinition]{./mf6ivar/tex/gwf-ghb-period.dat} - -Each line represents a separate item, which consists of variables. In this case, the first variable of the item, \texttt{cellid} is an array of size \texttt{ncelldim}. The next two variables of the item are \texttt{bhead} and \texttt{cond}. Lastly, the item has two optional variables, \texttt{aux} and \texttt{boundname}. Three of the variables shown in the list are colored in blue. Variables that are colored in blue mean that they can be represented with a time series. The time series capability is described in the section on Time-Variable Input in this document. - -The following is simple example of a PERIOD block for the GHB Package, which shows how a list is entered by the user. +In the next example, the ``LAYERED'' keyword is present and binary files are used for each layer. A control line with the ``BINARY'' keyword is required for each layer. \begin{lstlisting}[style=inputfile] -BEGIN PERIOD 1 -# lay row col stage cond - 1 13 1 988.0 0.038 - 1 14 9 1045.0 0.038 -END PERIOD -\end{lstlisting} - -As described earlier in the section on ``Block and Keyword Input,'' block information can be read from a separate text file. To activate reading a list from separate text file, the first and only entry in the block must be a control line of the following form: - -\begin{lstlisting}[style=blockdefinition] - OPEN/CLOSE + STRT LAYERED + OPEN/CLOSE strt.layer1.bin (BINARY) #layer1 + OPEN/CLOSE strt.layer2.bin (BINARY) #layer2 + OPEN/CLOSE strt.layer3.bin (BINARY) #layer3 + OPEN/CLOSE strt.layer4.bin (BINARY) #layer4 \end{lstlisting} -\noindent where \texttt{fname} is the name of the file containing the list. Lists for the stress packages (CHD, WEL, DRN, RIV, GHB, RCH, and EVT) have an additional BINARY option. The BINARY option is not supported for the advanced stress packages (LAK, MAW, SFR, UZF). The BINARY options is specified as follows: +In the next example, the ``LAYERED'' keyword is absent. In this case, a single control line with the ``BINARY'' keyword is required and the binary file will include the entire data array. -\begin{lstlisting}[style=blockdefinition] - OPEN/CLOSE [(BINARY)] +\begin{lstlisting}[style=inputfile] + STRT + OPEN/CLOSE strt.bin (BINARY) #layers1-4 \end{lstlisting} -If the (BINARY) keyword is found on the control line, then the file is opened as an unformatted file on unit 99, and the list is read. There are a number of requirements for using the (BINARY) option for lists. All stress package lists begin with integer values for the \texttt{cellid} (layer, row, and column, for example). These values must be represented as integer numbers in the unformatted file. Also, all auxiliary data must be included in the binary file; auxiliary data must be represented as double precision numbers. Lastly, the (BINARY) option does not support entry of \texttt{boundname}, and so the BOUNDNAMES option should not be activated in the OPTIONS block for the package. \ No newline at end of file +A consequence of the way binary input files have been implemented in \mf, simulated dependent variable binary output (for example, head and concentration) cannot be used as binary array input for a model. Instead, simulated dependent variable binary output must be processed and split into separate binary files for each layer or combined into a single array equal to the size of the grid (for DIS grids this would be an array equal to NCOL * NROW * NLAY). diff --git a/doc/mf6io/framework/binary_array_input.tex b/doc/mf6io/framework/binary_array_input.tex new file mode 100644 index 00000000000..73bfbd38f1d --- /dev/null +++ b/doc/mf6io/framework/binary_array_input.tex @@ -0,0 +1,88 @@ +\subsubsection{Description of Binary Array Input Files} +All floating point variables are written to the binary input files as DOUBLE PRECISION Fortran variables. Integer variables are written to the input files as Fortran integer variables. Some variables are character strings and are indicated as so in the following descriptions. Binary array data are written using the following two records: + +\vspace{5mm} +\noindent Record 1: \texttt{KSTP,KPER,PERTIM,TOTIM,TEXT,M1,M2,M3} \\ +\noindent Record 2: \texttt{DATA} \\ + +\vspace{5mm} +\noindent where + +\begin{description} \itemsep0pt \parskip0pt \parsep0pt +\item \texttt{KSTP} is the time step number; +\item \texttt{KPER} is the stress period number; +\item \texttt{PERTIM} is the time value for the current stress period; +\item \texttt{TOTIM} is the total simulation time; +\item \texttt{TEXT} is a character string (character*16); +\item \texttt{M1} is the length of the data in the fastest varying direction; +\item \texttt{M2} is the length of the data in the second fastest varying direction; +\item \texttt{M3} can be any value but is typically 1 or the layer number for the data; and +\item \texttt{DATA} is the array data of size (M1*M2). +\end{description} + +\noindent The values specified for \texttt{M1}, \texttt{M2}, and \texttt{M3} in Record 1 are dependent on the grid type and if the ``LAYERED'' keyword is present on the READARRAY control line. For binary array data, \texttt{KSTP}, \texttt{KPER}, \texttt{PERTIM}, \texttt{TOTIM}, and \texttt{TEXT} can be set to any value. Binary array input file specifications for each discretization type are given below. + +\paragraph{DIS Grids} +For DIS grids, \texttt{M1=NCOL}, \texttt{M2=NROW}, and \texttt{M3=ILAY} when the ``LAYERED'' keyword is present on the READARRAY control line. For this case, record 1 and 2 should be written as: + +\vspace{5mm} +\noindent Record 1: \texttt{KSTP,KPER,PERTIM,TOTIM,TEXT,M1,M2,M3} \\ +\noindent Record 2: \texttt{((DATA(J,I,ILAY),J=1,NCOL),I=1,NROW)} \\ + +\vspace{5mm} +\noindent where + +\begin{description} \itemsep0pt \parskip0pt \parsep0pt +\item \texttt{NCOL} is the number of columns; +\item \texttt{NROW} is the number of rows; and +\item \texttt{ILAY} is the layer number. +\end{description} + +\noindent For DIS grids, \texttt{M1=NCOL*NROW*NLAY}, \texttt{M2=1}, and \texttt{M3=1} when the ``LAYERED'' keyword is absent on the READARRAY control line. For this case, record 1 and 2 should be written as: + +\vspace{5mm} +\noindent Record 1: \texttt{KSTP,KPER,PERTIM,TOTIM,TEXT,M1,M2,M3} \\ +\noindent Record 2: \texttt{(((DATA(J,I,K),J=1,NCOL),I=1,NROW),K=1,NLAY)} \\ + +\vspace{5mm} +\noindent where + +\begin{description} \itemsep0pt \parskip0pt \parsep0pt +\item \texttt{NLAY} is the number of layers. +\end{description} + +\paragraph{DISV Grids} +For DISV grids, \texttt{M1=NCPL}, \texttt{M2=1}, and \texttt{M3=ILAY} when the ``LAYERED'' keyword is present on the READARRAY control line. For this case, record 1 and 2 should be written as: + +\vspace{5mm} +\noindent Record 1: \texttt{KSTP,KPER,PERTIM,TOTIM,TEXT,M1,M2,M3} \\ +\noindent Record 2: \texttt{(DATA(J,ILAY),J=1,NCPL)} \\ + +\vspace{5mm} +\noindent where + +\begin{description} \itemsep0pt \parskip0pt \parsep0pt +\item \texttt{NCPL} is the number of cells per layer. +\end{description} + +\noindent For DISV grids, \texttt{M1=NCPL*NLAY}, \texttt{M2=1}, and \texttt{M3=1} when the ``LAYERED'' keyword is absent on the READARRAY control line. For this case, record 1 and 2 should be written as: + +\vspace{5mm} +\noindent Record 1: \texttt{KSTP,KPER,PERTIM,TOTIM,TEXT,M1,M2,M3} \\ +\noindent Record 2: \texttt{((DATA(J,K),J=1,NCPL),K=1,NLAY)} \\ + + +\paragraph{DISU Grids} +For DISU grids, \texttt{M1=NODES}, \texttt{M2=1}, \texttt{M3=1}. For this case, record 1 and 2 should be written as: + + +\vspace{5mm} +\noindent Record 1: \texttt{KSTP,KPER,PERTIM,TOTIM,TEXT,M1,M2,M3} \\ +\noindent Record 2: \texttt{(DATA(N),N=1,NODES)} \\ + +\vspace{5mm} +\noindent where + +\begin{description} \itemsep0pt \parskip0pt \parsep0pt +\item \texttt{NODES} is the number cells in the model grid. +\end{description} diff --git a/doc/mf6io/framework/binary_list_input.tex b/doc/mf6io/framework/binary_list_input.tex new file mode 100644 index 00000000000..10ee136062a --- /dev/null +++ b/doc/mf6io/framework/binary_list_input.tex @@ -0,0 +1,18 @@ +\subsubsection{Description of Binary List Input Files} +All floating point variables are written to the binary input files as DOUBLE PRECISION Fortran variables. Integer variables are written to the input files as Fortran integer variables. Auxiliary variables can be included in binary list input files but as indicated previously binary list input files can not be used for packages that include BOUNDNAMES keyword in the OPTIONS block. The format of binary list data are described below. + +\vspace{5mm} +\noindent Record 1: \texttt{(CELLID(N),(RLIST(I,N),I=1,NDAT)(AUXVAR(I,N),I=1, NAUX), N=1,NLIST)}\\ + +\noindent where + +\begin{description} \itemsep0pt \parskip0pt \parsep0pt +\item \texttt{CELLID} is the cell identifier, and depends on the type of grid that is used for the simulation.; +\item \texttt{RLIST} is a double precision two-dimensional array of size (NDAT,NLIST) containing the stress package PERIOD data; +\item \texttt{NDAT} is the number of columns in RLIST, which is the number of columns of real data in the stress package PERIOD data; +\item \texttt{AUXVAR} is a double precision two-dimensional array of size (NAUX,NLIST) containing the auxilary data for the stress package PERIOD data; +\item \texttt{NAUX} is the number of columns in AUXVAR, which is the number of columns of real auxiliary data the in stress package PERIOD data; +\item \texttt{NLIST} is the size of the list; +\end{description} + +\noindent For a structured grid that uses the DIS input file, CELLID is the layer, row, and column. For a grid that uses the DISV input file, CELLID is the layer and CELL2D number. If the model uses the unstructured discretization (DISU) input file, CELLID is the node number for the cell. \texttt{NLIST} must be less than or equal to \texttt{MAXBOUND} for a stress package. \texttt{NAUX} is determined by the number of \texttt{AUXILIARY} variable names define in the OPTIONS block for the stress package. diff --git a/doc/mf6io/gwf/binaryoutput.tex b/doc/mf6io/framework/binaryoutput.tex similarity index 100% rename from doc/mf6io/gwf/binaryoutput.tex rename to doc/mf6io/framework/binaryoutput.tex diff --git a/doc/mf6io/form_of_input.tex b/doc/mf6io/framework/form_of_input.tex similarity index 100% rename from doc/mf6io/form_of_input.tex rename to doc/mf6io/framework/form_of_input.tex diff --git a/doc/mf6io/framework/list_data.tex b/doc/mf6io/framework/list_data.tex new file mode 100644 index 00000000000..59669b62316 --- /dev/null +++ b/doc/mf6io/framework/list_data.tex @@ -0,0 +1,38 @@ +\subsection{List Input} +Some items consist of several variables, such as layer, row, column, stage, and conductance, for example. List input refers to a block of data with a separate item on each line. For some common list types, the first set of variables is a cell identifier (denoted as \texttt{cellid} in this guide), such as layer, row, and column. With lists, the input data for each item must start on a new line. All variables for an item are assumed to be contained in a single line. Each input variable has a data type, which can be Double Precision, Integer, or Character. Integers are whole numbers and must not include a decimal point or exponent. Double Precision numbers can include a decimal point and an exponent. If no decimal point is included in the entered value, then the decimal point is assumed to be at the right side of the value. Any printable character is allowed for character variables. + +Variables starting with the letters I-N are most commonly integers; however, in some instances, a character string may start with the letters I-N. Variables starting with the letters A-H and O-Z are primarily double precision numbers; however, these variable names may also be used for character data. In \mf all variables are explicitly declared within the source code, as opposed to the implicit type declaration in previous MODFLOW versions. This explicit declaration means that the variable type can be easily determined from the source code. + +Free formatting is used throughout the input instructions. With free format, values are not required to occupy a fixed number of columns in a line. Each value can occupy one or more columns as required to represent the value; however, the values must still be included in the prescribed order. One or more spaces, or a single comma optionally combined with spaces, must separate adjacent values. Also, a numeric value of zero must be explicitly represented with 0 and not by one or more spaces when free format is used, because detecting the difference between a space that represents 0 and a space that represents a value separator is not possible. Free format is similar to Fortran's list directed input. + +Two capabilities included in Fortran's list-directed input are not included in the free-format input implemented in \mf. Null values in which input values are left unchanged from their previous values are not allowed. In general, MODFLOW's input values are not defined prior to their input. A ``/'' cannot be used to terminate an input line without including values for all the variables; data values for all required input variables must be explicitly specified on an input line. For character data, MODFLOW's free format implementation is less stringent than the list-directed input of Fortran. Fortran requires character data to be delineated by apostrophes. MODFLOW does not require apostrophes unless a blank or a comma is part of a character variable. + +As an example of a list, consider the PERIOD block for the GHB Package. The input format is shown below: + +\lstinputlisting[style=blockdefinition]{./mf6ivar/tex/gwf-ghb-period.dat} + +Each line represents a separate item, which consists of variables. In this case, the first variable of the item, \texttt{cellid} is an array of size \texttt{ncelldim}. The next two variables of the item are \texttt{bhead} and \texttt{cond}. Lastly, the item has two optional variables, \texttt{aux} and \texttt{boundname}. Three of the variables shown in the list are colored in blue. Variables that are colored in blue mean that they can be represented with a time series. The time series capability is described in the section on Time-Variable Input in this document. + +The following is simple example of a PERIOD block for the GHB Package, which shows how a list is entered by the user. + +\begin{lstlisting}[style=inputfile] +BEGIN PERIOD 1 +# lay row col stage cond + 1 13 1 988.0 0.038 + 1 14 9 1045.0 0.038 +END PERIOD +\end{lstlisting} + +As described earlier in the section on ``Block and Keyword Input,'' block information can be read from a separate text file. To activate reading a list from separate text file, the first and only entry in the block must be a control line of the following form: + +\begin{lstlisting}[style=blockdefinition] + OPEN/CLOSE +\end{lstlisting} + +\noindent where \texttt{fname} is the name of the file containing the list. Lists for the stress packages (CHD, WEL, DRN, RIV, GHB, RCH, and EVT) have an additional BINARY option. The BINARY option is not supported for the advanced stress packages (LAK, MAW, SFR, UZF, LKT, MWT, SFT, UZT). The BINARY options is specified as follows: + +\begin{lstlisting}[style=blockdefinition] + OPEN/CLOSE [(BINARY)] +\end{lstlisting} + +If the (BINARY) keyword is found on the control line, then the file is opened as an unformatted file on unit 99, and the list is read. There are a number of requirements for using the (BINARY) option for lists. All stress package lists begin with integer values for the \texttt{cellid} (layer, row, and column, for example). These values must be represented as integer numbers in the unformatted file. Also, all auxiliary data must be included in the binary file; auxiliary data must be represented as double precision numbers. Lastly, the (BINARY) option does not support entry of \texttt{boundname}, and so the BOUNDNAMES option should not be activated in the OPTIONS block for the package. \ No newline at end of file diff --git a/doc/mf6io/processing_of_input.tex b/doc/mf6io/framework/processing_of_input.tex similarity index 100% rename from doc/mf6io/processing_of_input.tex rename to doc/mf6io/framework/processing_of_input.tex diff --git a/doc/mf6io/gwf/gwf.tex b/doc/mf6io/gwf/gwf.tex index 452122446ef..3e3e77a8c5d 100644 --- a/doc/mf6io/gwf/gwf.tex +++ b/doc/mf6io/gwf/gwf.tex @@ -11,8 +11,6 @@ \subsection{Information for Existing MODFLOW Users} \input{gwf/info_existing_users.tex} -\input{gwf/array_data.tex} - \subsection{Units of Length and Time} The GWF Model formulates the groundwater flow equation without using prescribed length and time units. Any consistent units of length and time can be used when specifying the input data for a simulation. This capability gives a certain amount of freedom to the user, but care must be exercised to avoid mixing units. The program cannot detect the use of inconsistent units. For example, if hydraulic conductivity is entered in units of feet per day and pumpage as cubic meters per second, the program will run, but the results will be meaningless. Other processes generally are expected to work with consistent length and time units; however, other processes could conceivably place restrictions on which units are supported. diff --git a/src/Utilities/ArrayReaders.f90 b/src/Utilities/ArrayReaders.f90 index 537e0d47f50..37d0141b369 100644 --- a/src/Utilities/ArrayReaders.f90 +++ b/src/Utilities/ArrayReaders.f90 @@ -6,9 +6,10 @@ module ArrayReadersModule use InputOutputModule, only: openfile, u9rdcom, urword, ucolno, ulaprw, & BuildFixedFormat, BuildFloatFormat, & BuildIntFormat - use KindModule, only: DP, I4B + use KindModule, only: DP, I4B, LGP use OpenSpecModule, only: ACCESS, FORM use SimModule, only: store_error, store_error_unit + use SimVariablesModule, only: errmsg implicit none @@ -57,13 +58,14 @@ subroutine read_array_int1d(iu, iarr, aname, ndim, jj, iout, k) integer(I4B), intent(in) :: ndim ! dis%ndim integer(I4B), intent(in) :: k ! layer number; 0 to not print ! -- local + logical(LGP) :: isok integer(I4B) :: iclose, iconst, iprn, j, locat, ncpl, ndig integer(I4B) :: nval, nvalt logical :: prowcolnum character(len=100) :: prfmt integer(I4B) :: istat character(len=30) :: arrname - character(len=MAXCHARLEN) :: ermsg, ermsgr + character(len=MAXCHARLEN) :: ermsgr ! -- formats 2 format(/, 1x, a, ' = ', i0, ' FOR LAYER ', i0) 3 format(/, 1x, a, ' = ', i0) @@ -89,9 +91,9 @@ subroutine read_array_int1d(iu, iarr, aname, ndim, jj, iout, k) read (locat, *, iostat=istat, iomsg=ermsgr) (iarr(j), j=1, jj) if (istat /= 0) then arrname = adjustl(aname) - ermsg = 'Error reading data for array: '//trim(arrname) - call store_error(ermsg) - call store_error(ermsgr) + errmsg = "Error reading data for array '"//trim(arrname)// & + "'. "//trim(adjustl(ermsgr)) + call store_error(errmsg) call store_error_unit(locat) end if do j = 1, jj @@ -106,13 +108,15 @@ subroutine read_array_int1d(iu, iarr, aname, ndim, jj, iout, k) nvalt = 0 do call read_binary_header(locat, iout, aname, nval) + isok = check_binary_size(nval, nvalt, size(iarr), aname, locat) + if (isok .EQV. .FALSE.) exit read (locat, iostat=istat, iomsg=ermsgr) & (iarr(j), j=nvalt + 1, nvalt + nval) if (istat /= 0) then arrname = adjustl(aname) - ermsg = 'Error reading data for array: '//trim(arrname) - call store_error(ermsg) - call store_error(ermsgr) + errmsg = "Error reading data for array '"//trim(arrname)// & + "'. "//trim(adjustl(ermsgr)) + call store_error(errmsg) call store_error_unit(locat) end if nvalt = nvalt + nval @@ -150,13 +154,14 @@ subroutine read_array_int2d(iu, iarr, aname, ndim, jj, ii, iout, k) integer(I4B), intent(in) :: ndim ! dis%ndim integer(I4B), intent(in) :: k ! layer number; 0 to not print ! -- local + logical(LGP) :: isok integer(I4B) :: i, iclose, iconst, iprn, j, locat, ncpl, ndig integer(I4B) :: nval logical :: prowcolnum character(len=100) :: prfmt integer(I4B) :: istat character(len=30) :: arrname - character(len=MAXCHARLEN) :: ermsg, ermsgr + character(len=MAXCHARLEN) :: ermsgr ! -- formats 2 format(/, 1x, a, ' = ', i0, ' FOR LAYER ', i0) 3 format(/, 1x, a, ' = ', i0) @@ -185,9 +190,9 @@ subroutine read_array_int2d(iu, iarr, aname, ndim, jj, ii, iout, k) read (locat, *, iostat=istat, iomsg=ermsgr) (iarr(j, i), j=1, jj) if (istat /= 0) then arrname = adjustl(aname) - ermsg = 'Error reading data for array: '//trim(arrname) - call store_error(ermsg) - call store_error(ermsgr) + errmsg = "Error reading data for array '"//trim(arrname)// & + "'. "//trim(adjustl(ermsgr)) + call store_error(errmsg) call store_error_unit(locat) end if do j = 1, jj @@ -201,19 +206,22 @@ subroutine read_array_int2d(iu, iarr, aname, ndim, jj, ii, iout, k) ! -- Read data as binary locat = -locat call read_binary_header(locat, iout, aname, nval) - do i = 1, ii - read (locat, iostat=istat, iomsg=ermsgr) (iarr(j, i), j=1, jj) - if (istat /= 0) then - arrname = adjustl(aname) - ermsg = 'Error reading data for array: '//trim(arrname) - call store_error(ermsg) - call store_error(ermsgr) - call store_error_unit(locat) - end if - do j = 1, jj - iarr(j, i) = iarr(j, i) * iconst + isok = check_binary_size(nval, 0, size(iarr), aname, locat) + if (isok) then + do i = 1, ii + read (locat, iostat=istat, iomsg=ermsgr) (iarr(j, i), j=1, jj) + if (istat /= 0) then + arrname = adjustl(aname) + errmsg = "Error reading data for array '"//trim(arrname)// & + "'. "//trim(adjustl(ermsgr)) + call store_error(errmsg) + call store_error_unit(locat) + end if + do j = 1, jj + iarr(j, i) = iarr(j, i) * iconst + end do end do - end do + end if if (iclose == 1) then close (locat) end if @@ -310,6 +318,7 @@ subroutine read_array_dbl1d(iu, darr, aname, ndim, jj, iout, k) integer(I4B), intent(in) :: ndim ! dis%ndim integer(I4B), intent(in) :: k ! layer number; 0 to not print ! -- local + logical(LGP) :: isok integer(I4B) :: j, iclose, iprn, locat, ncpl, ndig real(DP) :: cnstnt logical :: prowcolnum @@ -317,7 +326,7 @@ subroutine read_array_dbl1d(iu, darr, aname, ndim, jj, iout, k) integer(I4B) :: istat integer(I4B) :: nvalt, nval character(len=30) :: arrname - character(len=MAXCHARLEN) :: ermsg, ermsgr + character(len=MAXCHARLEN) :: ermsgr ! -- formats 2 format(/, 1x, a, ' = ', g14.7, ' FOR LAYER ', i0) 3 format(/, 1x, a, ' = ', g14.7) @@ -343,9 +352,9 @@ subroutine read_array_dbl1d(iu, darr, aname, ndim, jj, iout, k) read (locat, *, iostat=istat, iomsg=ermsgr) (darr(j), j=1, jj) if (istat /= 0) then arrname = adjustl(aname) - ermsg = 'Error reading data for array: '//trim(arrname) - call store_error(ermsg) - call store_error(ermsgr) + errmsg = "Error reading data for array '"// & + trim(adjustl(arrname))//"'. "//trim(adjustl(ermsgr)) + call store_error(errmsg) call store_error_unit(locat) end if do j = 1, jj @@ -360,13 +369,15 @@ subroutine read_array_dbl1d(iu, darr, aname, ndim, jj, iout, k) nvalt = 0 do call read_binary_header(locat, iout, aname, nval) + isok = check_binary_size(nval, nvalt, size(darr), aname, locat) + if (isok .EQV. .FALSE.) exit read (locat, iostat=istat, iomsg=ermsgr) & (darr(j), j=nvalt + 1, nvalt + nval) if (istat /= 0) then arrname = adjustl(aname) - ermsg = 'Error reading data for array: '//trim(arrname) - call store_error(ermsg) - call store_error(ermsgr) + errmsg = "Error reading data for array '"// & + trim(adjustl(arrname))//"'. "//trim(adjustl(ermsgr)) + call store_error(errmsg) call store_error_unit(locat) end if nvalt = nvalt + nval @@ -404,6 +415,7 @@ subroutine read_array_dbl2d(iu, darr, aname, ndim, jj, ii, iout, k) integer(I4B), intent(in) :: ndim ! dis%ndim integer(I4B), intent(in) :: k ! layer number; 0 to not print ! -- local + logical(LGP) :: isok integer(I4B) :: i, iclose, iprn, j, locat, ncpl, ndig integer(I4B) :: nval real(DP) :: cnstnt @@ -411,7 +423,7 @@ subroutine read_array_dbl2d(iu, darr, aname, ndim, jj, ii, iout, k) character(len=100) :: prfmt integer(I4B) :: istat character(len=30) :: arrname - character(len=MAXCHARLEN) :: ermsg, ermsgr + character(len=MAXCHARLEN) :: ermsgr ! -- formats 2 format(/, 1x, a, ' = ', g14.7, ' FOR LAYER ', i0) 3 format(/, 1x, a, ' = ', g14.7) @@ -440,9 +452,9 @@ subroutine read_array_dbl2d(iu, darr, aname, ndim, jj, ii, iout, k) read (locat, *, iostat=istat, iomsg=ermsgr) (darr(j, i), j=1, jj) if (istat /= 0) then arrname = adjustl(aname) - ermsg = 'Error reading data for array: '//trim(arrname) - call store_error(ermsg) - call store_error(ermsgr) + errmsg = "Error reading data for array '"// & + trim(adjustl(arrname))//"'. "//trim(adjustl(ermsgr)) + call store_error(errmsg) call store_error_unit(locat) end if do j = 1, jj @@ -456,19 +468,22 @@ subroutine read_array_dbl2d(iu, darr, aname, ndim, jj, ii, iout, k) ! -- Read data as binary locat = -locat call read_binary_header(locat, iout, aname, nval) - do i = 1, ii - read (locat, iostat=istat, iomsg=ermsgr) (darr(j, i), j=1, jj) - if (istat /= 0) then - arrname = adjustl(aname) - ermsg = 'Error reading data for array: '//trim(arrname) - call store_error(ermsg) - call store_error(ermsgr) - call store_error_unit(locat) - end if - do j = 1, jj - darr(j, i) = darr(j, i) * cnstnt + isok = check_binary_size(nval, 0, size(darr), aname, locat) + if (isok) then + do i = 1, ii + read (locat, iostat=istat, iomsg=ermsgr) (darr(j, i), j=1, jj) + if (istat /= 0) then + arrname = adjustl(aname) + errmsg = "Error reading data for array '"// & + trim(adjustl(arrname))//"'. "//trim(adjustl(ermsgr)) + call store_error(errmsg) + call store_error_unit(locat) + end if + do j = 1, jj + darr(j, i) = darr(j, i) * cnstnt + end do end do - end do + end if if (iclose == 1) then close (locat) end if @@ -674,7 +689,6 @@ subroutine read_control_1(iu, iout, aname, locat, iclose, line, icol, fname) integer(I4B) :: istart, istop, n integer(I4B) :: ierr real(DP) :: r - character(len=MAXCHARLEN) :: ermsg ! ! -- Read array control record. call u9rdcom(iu, iout, line, ierr) @@ -693,12 +707,10 @@ subroutine read_control_1(iu, iout, aname, locat, iclose, line, icol, fname) locat = -1 iclose = 1 else - write (ermsg, *) 'ERROR READING CONTROL RECORD FOR '// & - trim(adjustl(aname)) - call store_error(ermsg) - call store_error(trim(adjustl(line))) - write (ermsg, *) 'Use CONSTANT, INTERNAL, or OPEN/CLOSE.' - call store_error(ermsg) + errmsg = 'READING CONTROL RECORD FOR '// & + trim(adjustl(aname))//"'. "// & + 'Use CONSTANT, INTERNAL, or OPEN/CLOSE.' + call store_error(errmsg) call store_error_unit(iu) end if ! @@ -718,7 +730,6 @@ subroutine read_control_2(iu, iout, fname, line, icol, & integer(I4B) :: i, n, istart, istop, lenkey real(DP) :: r character(len=MAXCHARLEN) :: keyword - character(len=LENBIGLINE) :: ermsg logical :: binary ! iprn = -1 ! Printing is turned off by default @@ -734,9 +745,9 @@ subroutine read_control_2(iu, iout, fname, line, icol, & select case (keyword) case ('(BINARY)') if (iclose == 0) then - ermsg = '"(BINARY)" option for array input is valid only if'// & - ' OPEN/CLOSE is also specified.' - call store_error(ermsg) + errmsg = '"(BINARY)" option for array input is valid only if'// & + ' OPEN/CLOSE is also specified.' + call store_error(errmsg) call store_error_unit(iu) end if binary = .true. @@ -747,9 +758,9 @@ subroutine read_control_2(iu, iout, fname, line, icol, & case ('') exit case default - ermsg = 'Invalid option found in array-control record: "' & - //trim(keyword)//'"' - call store_error(ermsg) + errmsg = 'Invalid option found in array-control record: "' & + //trim(keyword)//'"' + call store_error(errmsg) call store_error_unit(iu) end select end do @@ -982,7 +993,6 @@ subroutine print_array_int(iarr, aname, iout, jj, ii, k, prfmt, & logical, intent(in) :: prowcolnum ! Print row & column numbers ! -- local integer(I4B) :: i, j - character(len=MAXCHARLEN) :: ermsg ! -- formats 2 format(/, 1x, a, 1x, 'FOR LAYER ', i0) 3 format(/, 1x, a) @@ -1007,9 +1017,9 @@ subroutine print_array_int(iarr, aname, iout, jj, ii, k, prfmt, & end do else if (ii > 1) then - ermsg = 'Program error printing array '//trim(aname)// & - ': ii > 1 when prowcolnum is false.' - call store_error(ermsg, terminate=.TRUE.) + errmsg = 'Program error printing array '//trim(aname)// & + ': ii > 1 when prowcolnum is false.' + call store_error(errmsg, terminate=.TRUE.) end if ! ! -- Write array values, without row numbers @@ -1031,7 +1041,6 @@ subroutine print_array_dbl(darr, aname, iout, jj, ii, k, prfmt, & logical, intent(in) :: prowcolnum ! Print row & column numbers ! -- local integer(I4B) :: i, j - character(len=MAXCHARLEN) :: ermsg ! -- formats 2 format(/, 1x, a, 1x, 'FOR LAYER ', i0) 3 format(/, 1x, a) @@ -1056,9 +1065,9 @@ subroutine print_array_dbl(darr, aname, iout, jj, ii, k, prfmt, & end do else if (ii > 1) then - ermsg = 'Program error printing array '//trim(aname)// & - ': ii > 1 when prowcolnum is false.' - call store_error(ermsg, terminate=.TRUE.) + errmsg = 'Program error printing array '//trim(aname)// & + ': ii > 1 when prowcolnum is false.' + call store_error(errmsg, terminate=.TRUE.) end if ! ! -- Write array values, without row numbers @@ -1079,7 +1088,7 @@ subroutine read_binary_header(locat, iout, arrname, nval) integer(I4B) :: kstp, kper, m1, m2, m3 real(DP) :: pertim, totim character(len=16) :: text - character(len=MAXCHARLEN) :: ermsg, ermsgr + character(len=MAXCHARLEN) :: ermsgr character(len=*), parameter :: fmthdr = & "(/,1X,'HEADER FROM BINARY FILE HAS FOLLOWING ENTRIES',& &/,4X,'KSTP: ',I0,' KPER: ',I0,& @@ -1093,9 +1102,9 @@ subroutine read_binary_header(locat, iout, arrname, nval) ! ! -- Check for errors if (istat /= 0) then - ermsg = 'Error reading data for array: '//adjustl(trim(arrname)) - call store_error(ermsg) - call store_error(ermsgr) + errmsg = "Error reading data for array '"//adjustl(trim(arrname))// & + "'. "//trim(adjustl(ermsgr)) + call store_error(errmsg) call store_error_unit(locat) end if ! @@ -1111,4 +1120,41 @@ subroutine read_binary_header(locat, iout, arrname, nval) return end subroutine read_binary_header + !> @ brief Check the binary data size + !! + !! Check the size of the binary data that will be read + !! relative to the unfilled elements in the array . + !! + !< + function check_binary_size(nval, nvalt, arrsize, aname, locat) result(isok) + ! -- dummy + integer(I4B), intent(in) :: nval !< number of array + integer(I4B), intent(in) :: nvalt !< current data index + integer(I4B), intent(in) :: arrsize !< size of the array + character(len=*), intent(in) :: aname !< name of array + integer(I4B), intent(in) :: locat !< binary file unit + ! + ! -- local variables + logical(LGP) :: isok + ! + ! -- initialize isok + isok = .TRUE. + ! + if (nvalt + nval > arrsize) then + write (errmsg, '(a,i0,a,1x,a,1x,a,i0,a,1x,i0,3(1x,a))') & + 'The size of the data array calculated from the binary header (', & + nval, ') will exceed the remainder of the', trim(adjustl(aname)), & + 'data array (', arrsize, ') array by', nvalt + nval - arrsize, & + 'elements. This is usually caused by incorrect assignment of', & + '(m1,m2,m3) in the binary header. See the mf6io.pdf document', & + 'for information on assigning (m1,m2,m3).' + call store_error(errmsg) + call store_error_unit(locat) + isok = .FALSE. + end if + ! + ! -- return + return + end function check_binary_size + end module ArrayReadersModule diff --git a/src/Utilities/ListReader.f90 b/src/Utilities/ListReader.f90 index 99177280d38..fc5150937ac 100644 --- a/src/Utilities/ListReader.f90 +++ b/src/Utilities/ListReader.f90 @@ -314,14 +314,14 @@ subroutine read_binary(this) ! ! -- read layer, row, col, or cell number read (this%inlist, iostat=this%ierr) cellid - - ! -- ensure cellid is valid, store an error otherwise - call check_cellid(ii, cellid, this%mshape, this%ndim) - + ! ! -- If not end of record, then store nodenumber, else ! calculate lstend and nlist, and exit readloop select case (this%ierr) case (0) + ! + ! -- ensure cellid is valid, store an error otherwise + call check_cellid(ii, cellid, this%mshape, this%ndim) ! ! -- Check range if (ii > mxlist) then