diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..a0bae5b3 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,27 @@ +name: Build and publish PyTOUGH to PyPI + +on: + push: + branches: + - master + +jobs: + deploy: + runs-on: ubuntu-latest + environment: release + permissions: + id-token: write + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + - name: Install dependencies + run: | + pip install setuptools wheel build + - name: Build + run: | + python -m build + - name: Publish + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/pythontest.yml b/.github/workflows/pythontest.yml index df586d6a..10d018fd 100644 --- a/.github/workflows/pythontest.yml +++ b/.github/workflows/pythontest.yml @@ -10,16 +10,16 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9] + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - pip install --user numpy scipy + pip install --user numpy scipy layermesh pip install --user . - name: Unit tests run: | diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..de70b52b --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,21 @@ +# File: .readthedocs.yaml + +version: 2 + +build: + os: "ubuntu-22.04" + tools: + python: "3.10" + +# Build from the docs/ directory with Sphinx +sphinx: + configuration: doc/source/conf.py + +# Explicitly set the version of Python and its requirements +python: + install: + - requirements: doc/source/requirements.txt + +formats: + - pdf + - epub diff --git a/IAPWS97.py b/IAPWS97.py index 33008fb0..c7dc33f2 100755 --- a/IAPWS97.py +++ b/IAPWS97.py @@ -467,9 +467,11 @@ def density_temperature_plot(plt, subplot = 111): plt.plot(t, dw, color = 'k', marker = '', linestyle = '-') t = np.linspace(350, tcritical, 50) p = np.array([sat(tx) for tx in t]) - def f(dx): return super(dx, tx)[0] - px - ds = np.array([fsolve(f, 120. + 25. / 200 * (tx - 350.)) for tx, px in zip(t, p)]) - dw = np.array([fsolve(f, 560. - 25. / 200 * (tx - 350.)) for tx, px in zip(t, p)]) + def f(dx, tx, px): return super(dx, tx)[0] - px + ds = np.array([fsolve(f, 120. + 25. / 200 * (tx - 350.), (tx, px)) + for tx, px in zip(t, p)]) + dw = np.array([fsolve(f, 560. - 25. / 200 * (tx - 350.), (tx, px)) + for tx, px in zip(t, p)]) plt.plot(t, ds, color = 'k', marker = '', linestyle = '-') plt.plot(t, dw, color = 'k', marker = '', linestyle = '-') @@ -495,8 +497,8 @@ def f(dx): return super(dx, tx)[0] - px d = np.array([cowat(tx, p)[0] for tx in t]) plt.plot(t, d, color = 'k', marker = '', linestyle = '--') t = np.linspace(350, 800, 100) - def g(dx): return super(dx, tx)[0] - p - d = np.array([fsolve(g, 1100 - tx) for tx in t]) + def g(dx, tx, p): return super(dx, tx)[0] - p + d = np.array([fsolve(g, 1100 - tx, (tx, p)) for tx in t]) plt.plot(t, d, color = 'k', marker = '', linestyle = '--') plt.axis([0, 800, 0, 1100]) diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..65c5ca88 --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.md b/README.md index 7586f13e..0f3bcf27 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -![Unit tests](https://github.com/acroucher/PyTOUGH/workflows/Unit%20tests/badge.svg) +![Unit tests](https://github.com/acroucher/PyTOUGH/workflows/Unit%20tests/badge.svg) [![Documentation Status](https://readthedocs.org/projects/PyTOUGH/badge/?version=latest)](https://PyTOUGH.readthedocs.io/en/latest/?badge=latest) [![PyPI version](https://badge.fury.io/py/PyTOUGH.svg)](https://badge.fury.io/py/PyTOUGH) ![PyPI - Downloads](https://img.shields.io/pypi/dm/PyTOUGH) + # What is PyTOUGH? @@ -6,23 +7,31 @@ PyTOUGH (Python TOUGH) is a Python library for simplifying, extending and automa # Installing PyTOUGH: -First, make sure you have [Python](http://www.python.org) and the [Numerical Python](http://numpy.scipy.org/) library installed on your machine. (For some features you will need other libraries such as [Scientific Python](http://www.scipy.org/) or [Matplotlib](http://matplotlib.sourceforge.net/)- consult the user guide for details.) - -Click the _Clone or download_ button at the right of the PyTOUGH web page and then click [Download ZIP](https://github.com/acroucher/PyTOUGH/archive/master.zip) , and save the .zip file to your computer. Unzip this to any directory on your computer. This will create a directory containing a file called `setup.py`. At the command line type `python setup.py install`. - -(Alternatively, if you are confident using the Git version control system, you can clone the PyTOUGH repository instead of downloading a .zip file.) +From version 1.6.0, PyTOUGH can be installed via the [`pip`](https://pip.pypa.io) Python package installer: + +``` +pip install PyTOUGH +``` +You can also install a particular version of PyTOUGH, e.g. to install version 1.6.0: + +``` +pip install PyTOUGH==1.6.0 +``` +To uninstall PyTOUGH: +``` +pip uninstall PyTOUGH +``` +To install the `testing` branch, to get the most recent changes being tested for the next stable release: +``` +pip install git+https://github.com/acroucher/PyTOUGH.git@testing +``` # More information: -For more detailed information on PyTOUGH, consult the user guide (PDF format, in the 'doc' directory of your PyTOUGH install) and the PyTOUGH [wiki](https://github.com/acroucher/PyTOUGH/wiki/), which has links to published articles on PyTOUGH. +For more detailed information on PyTOUGH, consult the [user guide](https://pytough.readthedocs.io) (html, or you can download PDF or Epub versions) and the PyTOUGH [wiki](https://github.com/acroucher/PyTOUGH/wiki/), which has links to published articles on PyTOUGH. # What's new in PyTOUGH? -The latest stable version is 1.5.5, which has: - -* various bug fixes and other minor enhancements. - -# Where's the user guide? - -Since PyTOUGH version 1.3.6, the PyTOUGH user guide (PyTOUGH-guide.pdf) is now included in the 'doc' directory of your PyTOUGH install. Previously this was available separately from the 'Downloads' section on the website, but GitHub decided to phase out this 'Downloads' section. +The latest stable version is 1.6.6, which has: +* ability to convert EOS3 models to Waiwera JSON input diff --git a/doc/IAPWS97.tex b/doc/IAPWS97.tex deleted file mode 100755 index f96a185f..00000000 --- a/doc/IAPWS97.tex +++ /dev/null @@ -1,251 +0,0 @@ -\chapter{IAPWS-97 thermodynamics} -\label{iapws97} -\index{thermodynamics!IAPWS-97} -\index{PyTOUGH!thermodynamics!IAPWS-97} - -\section{Introduction} -The \texttt{IAPWS97} library in PyTOUGH contains a Python implementation of the main functions of the International Association for the Properties of Water and Steam 1997 (IAPWS-97) thermodynamic formulation \citep{iapws_2000}. These can be used to calculate the thermodynamic properties of water, steam and supercritical water. The IAPWS-97 supersedes the IFC-67 formulation used in TOUGH2 (see section \ref{t2thermo}), being generally faster and more accurate, as well as having a simpler representation of the thermodynamic region around the critical point. - -The operating range of the IAPWS-97 formulation is shown in the pressure-temperature plot below. It covers temperatures up to 800 $\degree$C and pressures up to 100 MPa, and is divided into four thermodynamic regions: - -\begin{enumerate} - \item liquid water - \item dry steam - \item supercritical fluid - \item two-phase -\end{enumerate} - -The two-phase region (4) follows the saturation line on the pressure-temperature plot (the boundary between liquid water and dry steam), up to the critical point $C$ ($T$ = 373.946 $\degree$C, $P$ = 22.064 MPa), where the distinction between liquid water and steam disappears. Region 3 covers supercritcal fluid (above the critical point) and also near-critical fluid, just below the critical point. The boundary between regions 1 and 3 (liquid water and supercritical) is aribitrarily set at $T$ = 350 $\degree$C. The boundary between regions 2 and 3 (dry steam and supercritical) is described by the \texttt{b23p} and \texttt{b23t} functions, given in section \ref{region23_boundary}. - -\index{thermodynamics!IAPWS-97!operating range} -\begin {figure} - \begin{center} - \includegraphics{ptplot.eps} - \caption{Operating range of the IAPWS-97 thermodynamic formulation} - \label{fg:iapws97_range} - \end{center} -\end {figure} - -The \texttt{IAPWS97} library can be imported using the command: - -\begin{lstlisting} - from IAPWS97 import * -\end{lstlisting} - -The functions available through the \texttt{IAPWS97} library are listed in Table \ref{tb:iapws97_functions} and described below. - -\index{thermodynamics!IAPWS-97!functions} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|p{65mm}|} - \hline - \textbf{Function} & \textbf{Type} & \textbf{Description}\\ - \hline - \hyperref[sec:iapws97:b23p]{\texttt{b23p}} & float & pressure on boundary between steam and supercritical regions, as a function of temperature\\ - \hyperref[sec:iapws97:b23t]{\texttt{b23t}} & float & temperature on boundary between steam and supercritical regions, as a function of pressure\\ - \hyperref[sec:iapws97:cowat]{\texttt{cowat}} & tuple & density and internal energy of liquid water\\ - \hyperref[sec:iapws97:density_temperature_plot]{\texttt{density\_temperature\_plot}} & -- & draws region boundaries on a density-temperature plot\\ - \hyperref[sec:iapws97:pressure_temperature_plot]{\texttt{pressure\_temperature\_plot}} & -- & draws region boundaries on a pressure-temperature plot\\ - \hyperref[sec:iapws97:region]{\texttt{region}} & integer & thermodynamic region\\ - \hyperref[sec:iapws97:sat]{\texttt{sat}} & float & saturation pressure as a function of temperature\\ - \hyperref[sec:iapws97:super]{\texttt{super}} & tuple & pressure and internal energy of supercritical fluid\\ - \hyperref[sec:iapws97:supst]{\texttt{supst}} & tuple & density and internal energy of dry steam\\ - \hyperref[sec:iapws97:tsat]{\texttt{tsat}} & float & saturation temperature as a function of pressure\\ - \hyperref[sec:iapws97:visc]{\texttt{visc}} & float & dynamic viscosity of water, steam or supercritical fluid\\ - \hline - \end{tabular} - \caption{\texttt{IAPWS97} functions} - \label{tb:iapws97_functions} - \end{center} -\end{table} - -\section{Thermodynamic functions} - -The IAPWS-97 formulation provides thermodynamic functions for liquid water, dry steam and supercritical fluid. These functions calculate secondary parameters from the primary thermodynamic variables. - -\begin{snugshade} -\subsection{Liquid water: \texttt{cowat(\emph{t,p})}} -\end{snugshade} -\label{sec:iapws97:cowat} -\index{thermodynamics!IAPWS-97!liquid water properties} - -The \texttt{cowat} function returns a two-element tuple (\texttt{d},\texttt{u}) of density (kg/m$^3$) and internal energy (J/kg) of liquid water as a function of temperature \texttt{t} ($\degree$C) and pressure \texttt{p} (Pa). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{t}: float\\ - Temperature ($\degree$C) -\item \textbf{p}: float\\ - Pressure (Pa) -\end{itemize} - -\begin{snugshade} -\subsection{Dry steam: \texttt{supst(\emph{t,p})}} -\end{snugshade} -\label{sec:iapws97:supst} -\index{thermodynamics!IAPWS-97!dry steam properties} - -The \texttt{supst} function returns a two-element tuple (\texttt{d},\texttt{u}) of density (kg/m$^3$) and internal energy (J/kg) of dry steam as a function of temperature \texttt{t} ($\degree$C) and pressure \texttt{p} (Pa). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{t}: float\\ - Temperature ($\degree$C) -\item \textbf{p}: float\\ - Pressure (Pa) -\end{itemize} - -\begin{snugshade} -\subsection{Supercritical fluid: \texttt{super(\emph{d,t})}} -\end{snugshade} -\label{sec:iapws97:super} -\index{thermodynamics!IAPWS-97!supercritical fluid properties} - -The \texttt{super} function returns a two-element tuple (\texttt{p},\texttt{u}) of pressure (Pa) and internal energy (J/kg) of supercritical fluid as a function of density \texttt{d} (kg/m$^3$) and temperature \texttt{t} ($\degree$C). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{d}: float\\ - Density (kg/m$^3$) -\item \textbf{t}: float\\ - Temperature ($\degree$C) -\end{itemize} - -\begin{snugshade} -\section{Viscosity: \texttt{visc(\emph{d,t})}} -\end{snugshade} -\label{sec:iapws97:visc} -\index{thermodynamics!IAPWS-97!viscosity} - -The \texttt{visc} function returns the dynamic viscosity (Pa.s) of liquid water, dry steam or supercritical fluid as a function of density \texttt{d} (kg/m$^3$) and temperature \texttt{t} ($\degree$C). This function is based on the supplementary ``IAPWS Formulation 2008 for the Viscosity of Ordinary Water Substance'', without the critical enhancement of viscosity near the critical point. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{d}: float\\ - Density (kg/m$^3$) -\item \textbf{t}: float\\ - Temperature ($\degree$C) -\end{itemize} - -\section{Region boundaries} - -These functions describe the boundaries between the four thermodynamic regions of the IAPWS-97 formulation (see Figure \ref{fg:iapws97_range}). There is no equation for the boundary between regions 1 and 3 as this is simply the line $T = 350 \degree$C. - -\subsection{Saturation line: \texttt{sat(\emph{t})} and \texttt{tsat(\emph{p})}} - -\begin{snugshade} -\subsubsection{\texttt{sat(\emph{t})}} -\end{snugshade} -\label{sec:iapws97:sat} -\index{thermodynamics!IAPWS-97!saturation line} - -The \texttt{sat} function returns the saturation pressure (Pa) at a given temperature \texttt{t} ($\degree$C), for temperatures below the critical temperature. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{t}: float\\ - Temperature ($\degree$C) -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{tsat(\emph{p})}} -\end{snugshade} -\label{sec:iapws97:tsat} -\index{thermodynamics!IAPWS-97!saturation line} - -The \texttt{tsat} function returns the saturation temperature ($\degree$C) at a given pressure \texttt{p} (Pa), for pressures below the critical pressure. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{p}: float\\ - Pressure (Pa) -\end{itemize} - -\subsection{Steam/supercritical boundary} -\label{region23_boundary} - -\begin{snugshade} -\subsubsection{\texttt{b23p(\emph{t})}} -\end{snugshade} -\label{sec:iapws97:b23p} -\index{thermodynamics!IAPWS-97!region boundaries} - -The \texttt{b23p} function returns the pressure (Pa) on the boundary of the dry steam and supercritical regions (regions 2 and 3) at a given temperature \texttt{t} ($\degree$C). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{t}: float\\ - Temperature ($\degree$C) -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{b23t(\emph{p})}} -\end{snugshade} -\label{sec:iapws97:b23t} -\index{thermodynamics!IAPWS-97!region boundaries} - -The \texttt{b23t} function returns the temperature ($\degree$C) on the boundary of the dry steam and supercritical regions (regions 2 and 3) at a given pressure \texttt{p} (Pa). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{p}: float\\ - Pressure (Pa) -\end{itemize} - -\section{Determining thermodynamic region} - -\begin{snugshade} -\subsubsection{\texttt{region(\emph{t}, \emph{p})}} -\end{snugshade} -\label{sec:iapws97:region} -\index{thermodynamics!IAPWS97!region} - -Returns the thermodynamic region (integer, or \texttt{None}) corresponding to the given temperature ($\degree$C) and pressure (Pa), as defined by the IAPWS-97 specification. The regions are: - -\begin{enumerate} - \item liquid water - \item dry steam - \item supercritical -\end{enumerate} - -If the input temperature and/or pressure are outside the operating range of the IAPWS-97 formulation, the routine will return \texttt{None}. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{t}: float\\ - Temperature ($\degree$C) -\item \textbf{Pressure}: float\\ - Pressure (Pa) -\end{itemize} - -\section{Plotting functions} - -The \texttt{IAPWS97} library contains two functions used for including the IAPWS-97 thermodynamic region boundaries on plots. - -\begin{snugshade} -\subsection{\texttt{pressure\_temperature\_plot(\emph{plt})}} -\end{snugshade} -\label{sec:iapws97:pressure_temperature_plot} -\index{thermodynamics!IAPWS-97!pressure-temperature plots} - -Draws the IAPWS-97 thermodynamic region boundaries on a pressure-temperature diagram. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{plt}: \texttt{matplotlib.pyplot} instance\\ - An instance of the \texttt{matplotlib.pyplot} library, imported in the calling script using e.g. \texttt{import matplotlib.pyplot as plt}. -\end{itemize} - -\begin{snugshade} -\subsection{\texttt{density\_temperature\_plot(\emph{plt})}} -\end{snugshade} -\label{sec:iapws97:density_temperature_plot} -\index{thermodynamics!IAPWS-97!density-temperature plots} - -Draws the IAPWS-97 thermodynamic region boundaries on a density-temperature diagram. (This function requires the Scientific Python (\texttt{scipy}) library to be installed.) - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{plt}: \texttt{matplotlib.pyplot} instance\\ - An instance of the \texttt{matplotlib.pyplot} library, imported in the calling script using e.g. \texttt{import matplotlib.pyplot as plt}. -\end{itemize} diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 00000000..f02ec2c0 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = PyTOUGH +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/doc/PyTOUGH-guide.pdf b/doc/PyTOUGH-guide.pdf deleted file mode 100755 index 8e5ed1d8..00000000 Binary files a/doc/PyTOUGH-guide.pdf and /dev/null differ diff --git a/doc/PyTOUGH-guide.tex b/doc/PyTOUGH-guide.tex deleted file mode 100755 index e8f4e3a0..00000000 --- a/doc/PyTOUGH-guide.tex +++ /dev/null @@ -1,78 +0,0 @@ -\documentclass[a4paper,10pt]{report} - -% PyTOUGH user's guide -\usepackage{lmodern} -\usepackage[T1]{fontenc} -\usepackage{gensymb} -\usepackage{makeidx} -\usepackage{listings} -\usepackage{framed,color} -\definecolor{shadecolor}{gray}{0.9} -\definecolor{codehighlight}{cmyk}{0.64,0,0.95,0.40} -\definecolor{codestring}{rgb}{0.58,0,0.82} -\definecolor{codecomment}{rgb}{0,0.6,0} -\usepackage[authoryear,round]{natbib} % for references -\usepackage[hmargin=3.5cm]{geometry} % change margin -\usepackage{rotating} % for sideways tables -\usepackage{hyperref} -\usepackage{longtable} - -\makeindex - -\hypersetup{ - pdftitle = PyTOUGH User's Guide, - pdfauthor = Adrian Croucher, - colorlinks = true, % Colours links instead of ugly boxes - urlcolor = blue, % Colour for external hyperlinks - linkcolor = blue, % Colour of internal links - citecolor = red % Colour of citations -} - -% code listings: -\lstset{language=Python, - basicstyle=\ttfamily, - basewidth={.5em,0.5em}, - keywordstyle=\color{codehighlight}, - stringstyle=\color{codestring}, - commentstyle=\color{codecomment}, - showstringspaces=false, - breaklines=true, - breakatwhitespace=true, - frame=bt} - -%------------------------------------------------------------------------ -\begin{document} - -\include{titlepage} - -%------------------------------------------------------------------------ -\tableofcontents -%\listoffigures -\listoftables - -%------------------------------------------------------------------------ -\include{introduction} -\include{mulgrids} -\include{t2grids} -\include{t2data} -\include{t2incons} -\include{t2listing} -\include{t2thermo} -\include{IAPWS97} -%------------------------------------------------------------------------ -\renewcommand\bibname{References} % rather than 'Bibliography' -\bibliographystyle{PyTOUGH} -\addcontentsline{toc}{chapter}{References} % adds to table of contents -\bibliography{PyTOUGH} - -\appendix -\include{appendix_geometry_format} - -\clearpage -\addcontentsline{toc}{chapter}{Index} -\printindex - - -%------------------------------------------------------------------------ - -\end{document} diff --git a/doc/PyTOUGH.bib b/doc/PyTOUGH.bib deleted file mode 100755 index fccb1180..00000000 --- a/doc/PyTOUGH.bib +++ /dev/null @@ -1,65 +0,0 @@ -@manual{tough2, -author="Pruess, Karsten and Oldenburg, Kurt and Moridis, George", -year=1999, -title="{TOUGH2} user's guide, version 2.0", -organization="Lawrence Berkeley National Laboratory, University of California, Berkeley", -month="November" -} - -@manual{GMINC, -author="Pruess, Karsten", -year=1983, -title="{GMINC} -- a mesh generator for flow simulations in fractured reservoirs", -organization="Lawrence Berkeley National Laboratory, University of California, Berkeley", -month="March" -} - -@manual{tough2mp, -author="Zhang, Keni and Wu, Yu-Shu and Pruess, Karsten", -year=2008, -title="User's guide for {TOUGH2-MP} -- a massively parallel version of the {TOUGH2} code", -organization="Lawrence Berkeley National Laboratory, University of California, Berkeley", -month="May" -} - -@inproceedings{mulgraph, -author="O'Sullivan, Michael and Bullivant, David", -year=1995, -title="A graphical interface for the {TOUGH2} family of flow simulators", -booktitle="Proceedings of the {TOUGH} workshop 1995", -organization="Lawrence Berkeley National Laboratory, University of California, Berkeley", -} - -@article{iapws_2000, -author="Wagner, W. and Cooper, J.R. and Dittman, A. and Kijima, J. and Kretzschmar, H.-J. and Kruse, A. and Mare\v{s}, R. and Oguchi, K. and Sato, H. and St{\"{o}}cker, I. and \v{S}ifner, O. and Takaishi, Y. and Tanishita, I. and Tr{\"{u}}benbach, J. and Willkommen, Th.", -year=2000, -title="The {IAPWS} {I}ndustrial {F}ormulation 1997 for the {T}hermodynamic {P}roperties of {W}ater and {S}team", -journal="Transactions of the ASME", -volume=122, -pages="150--182" -} - -@manual{IFC_67, -author="IFC", -year=1967, -title="A formulation of the thermodynamic properties of ordinary water substance", -organization="International Formulation Committee, D{\"{u}}sseldorf, Germany" -} - -@article{barker_1988, -author="Barker, J.A.", -year=1988, -title="A generalized radial flow model for hydraulic tests in fractured rock", -journal="Water Resources Research", -volume=24, -number=10, -pages="1796--1804" -} - -@manual{AMESH, -author="Haukwa, Charles B.", -year=1998, -title="{AMESH} -- a mesh creating program for the integral finite difference method", -organization="Lawrence Berkeley National Laboratory, University of California, Berkeley", -month="March" -} diff --git a/doc/PyTOUGH.bst b/doc/PyTOUGH.bst deleted file mode 100755 index 5368ba43..00000000 --- a/doc/PyTOUGH.bst +++ /dev/null @@ -1,1375 +0,0 @@ -% BibTeX standard bibliography style `dcu' (one of the harvard family) - % version 0.99a for BibTeX versions 0.99a or later, LaTeX version 2.09. - % Copyright (C) 1991, all rights reserved. - % Copying of this file is authorized only if either - % (1) you make absolutely no changes to your copy, including name, or - % (2) if you do make changes, you name it something other than - % btxbst.doc, plain.bst, unsrt.bst, alpha.bst, abbrv.bst, agsm.bst, - % dcu.bst or kluwer.bst. - % This restriction helps ensure that all standard styles are identical. - % The file harvard.tex has the documentation for this style. - -% ACKNOWLEDGEMENT: -% This document is a modified version of alpha.bst to which it owes much of -% its functionality. - -% AUTHOR -% Peter Williams, Key Centre for Design Quality, Sydney University -% e-mail: peterw@archsci.arch.su.oz.au - -ENTRY - { address - author - booktitle - chapter - edition - editor - howpublished - institution - journal - key - month - note - number - organization - pages - publisher - school - series - title - type - URL - volume - year - } - { field.used etal.allowed etal.required } - { extra.label sort.label list.year } - -INTEGERS { output.state before.all mid.sentence after.sentence after.block } - -FUNCTION {init.state.consts} -{ #0 'before.all := - #1 'mid.sentence := - #2 'after.sentence := - #3 'after.block := -} - -STRINGS { s t f } - -FUNCTION {output.nonnull} -{ 's := - output.state mid.sentence = - { ", " * write$ } - { output.state after.block = - { add.period$ write$ - newline$ - "\newblock " write$ - } - { output.state before.all = - 'write$ - { add.period$ " " * write$ } - if$ - } - if$ - mid.sentence 'output.state := - } - if$ - s -} - -FUNCTION {output} -{ duplicate$ empty$ - 'pop$ - 'output.nonnull - if$ -} - -FUNCTION {output.check} -{ 't := - duplicate$ empty$ - { pop$ "empty " t * " in " * cite$ * warning$ } - 'output.nonnull - if$ -} - -FUNCTION {item.check} -{ 't := - empty$ - { "empty " t * " in " * cite$ * warning$ } - { skip$ } - if$ -} - -FUNCTION {fin.entry} -{ add.period$ - write$ - newline$ -} - -FUNCTION {new.block} -{ output.state before.all = - 'skip$ - { after.block 'output.state := } - if$ -} - -FUNCTION {not} -{ { #0 } - { #1 } - if$ -} - -FUNCTION {and} -{ 'skip$ - { pop$ #0 } - if$ -} - -FUNCTION {or} -{ { pop$ #1 } - 'skip$ - if$ -} - -FUNCTION {field.or.null} -{ duplicate$ empty$ - { pop$ "" } - 'skip$ - if$ -} - -FUNCTION {emphasize} -{ duplicate$ empty$ - { pop$ "" } - { "{\em " swap$ * "}" * } - if$ -} - -FUNCTION {embolden} -{ duplicate$ empty$ - { pop$ "" } - { "{\bf " swap$ * "}" * } - if$ -} - -FUNCTION {quote} -{ duplicate$ empty$ - { pop$ "" } - { "`" swap$ * "'" * } - if$ -} - -FUNCTION {write.url} -{ URL empty$ - { skip$ } - { "\newline\harvardurl{" URL * "}" * write$ newline$ } - if$ -} - -INTEGERS { nameptr namesleft numnames } - -FUNCTION {format.names} -{ 's := - 'f := - #1 'nameptr := - s num.names$ 'numnames := - numnames 'namesleft := - { namesleft #0 > } - { s nameptr f format.name$ 't := - nameptr #1 > - { namesleft #1 > - { ", " * t * } - { t "others" = - { " et~al." * } - { " \harvardand\ " * t * } - if$ - } - if$ - } - 't - if$ - nameptr #1 + 'nameptr := - namesleft #1 - 'namesleft := - } - while$ -} - -FUNCTION {format.authors} -{ author empty$ - { "" } - { "{vv~}{ll}{, jj}{, f.}" author format.names } - if$ -} - -FUNCTION {format.editors} -{ editor empty$ - { "" } - { "{vv~}{ll}{, jj}{, f.}" editor format.names - editor num.names$ #1 > - { " (eds)" * } - { " (ed.)" * } - if$ - } - if$ -} - -FUNCTION {format.editors.reverse} -{ editor empty$ - { "" } - { "{f.~}{vv~}{ll}{, jj}" editor format.names - editor num.names$ #1 > - { " (eds)" * } - { " (ed.)" * } - if$ - } - if$ -} - -FUNCTION {format.title} -{ title empty$ - { "" } - { title "t" change.case$ } - if$ -} - -FUNCTION {n.dashify} -{ 't := - "" - { t empty$ not } - { t #1 #1 substring$ "-" = - { t #1 #2 substring$ "--" = not - { "--" * - t #2 global.max$ substring$ 't := - } - { { t #1 #1 substring$ "-" = } - { "-" * - t #2 global.max$ substring$ 't := - } - while$ - } - if$ - } - { t #1 #1 substring$ * - t #2 global.max$ substring$ 't := - } - if$ - } - while$ -} - -FUNCTION {format.btitle} -{ title emphasize -} - -FUNCTION {tie.or.space.connect} -{ duplicate$ text.length$ #3 < - { "~" } - { " " } - if$ - swap$ * * -} - -FUNCTION {either.or.check} -{ empty$ - 'pop$ - { "can't use both " swap$ * " fields in " * cite$ * warning$ } - if$ -} - -FUNCTION {format.bvolume} -{ volume empty$ - { "" } - { "Vol." volume tie.or.space.connect - series empty$ - 'skip$ - { " of " * series emphasize * } - if$ - "volume and number" number either.or.check - } - if$ -} - -FUNCTION {format.volume} -{ volume empty$ - { "" } - { "Vol." volume tie.or.space.connect } - if$ -} - -FUNCTION {format.number.series} -{ volume empty$ - { number empty$ - { series field.or.null } - { output.state mid.sentence = - { "number" } - { "Number" } - if$ - number tie.or.space.connect - series empty$ - { "there's a number but no series in " cite$ * warning$ } - { " in " * series emphasize * } - if$ - } - if$ - } - { "" } - if$ -} - -FUNCTION {format.edition} -{ edition empty$ - { "" } - { output.state mid.sentence = - { edition "l" change.case$ " edn" * } - { edition "t" change.case$ " edn" * } - if$ - } - if$ -} - -INTEGERS { multiresult } - -FUNCTION {multi.page.check} -{ 't := - #0 'multiresult := - { multiresult not - t empty$ not - and - } - { t #1 #1 substring$ - duplicate$ "-" = - swap$ duplicate$ "," = - swap$ "+" = - or or - { #1 'multiresult := } - { t #2 global.max$ substring$ 't := } - if$ - } - while$ - multiresult -} - -FUNCTION {format.pages} -{ pages empty$ - { "" } - { pages multi.page.check - { "pp.~" pages n.dashify * } - { "p.~" pages * } - if$ - } - if$ -} - -FUNCTION {format.vol.num.pages} -{ format.volume field.or.null - number empty$ - 'skip$ - { "(" number * ")" * * - volume empty$ - { "there's a number but no volume in " cite$ * warning$ } - 'skip$ - if$ - } - if$ - pages empty$ - 'skip$ - { duplicate$ empty$ - { pop$ format.pages } - { ",~" * pages n.dashify * } - if$ - } - if$ -} - -FUNCTION {format.chapter.pages} -{ chapter empty$ - 'format.pages - { type empty$ - { "chapter" } - { type "l" change.case$ } - if$ - chapter tie.or.space.connect - pages empty$ - 'skip$ - { ", " * format.pages * } - if$ - } - if$ -} - -FUNCTION {format.in.ed.booktitle} -{ booktitle empty$ - { "" } - { editor empty$ - { booktitle emphasize } - { "{\em in} " format.editors.reverse * ", " * booktitle emphasize * } - if$ - } - if$ -} - -FUNCTION {empty.misc.check} -{ author empty$ title empty$ howpublished empty$ - month empty$ year empty$ note empty$ - and and and and and - key empty$ not and - { "all relevant fields are empty in " cite$ * warning$ } - 'skip$ - if$ -} - -FUNCTION {format.thesis.type} -{ type empty$ - 'skip$ - { pop$ - type "t" change.case$ - } - if$ -} - -FUNCTION {format.tr.number} -{ type empty$ - { "Technical Report" } - 'type - if$ - number empty$ - { "t" change.case$ } - { number tie.or.space.connect } - if$ -} - -FUNCTION {format.article.crossref} -{ key empty$ - { journal empty$ - { "need key or journal for " cite$ * " to crossref " * crossref * - warning$ - "" - } - { "in {\em " journal * "\/}" * " \cite{" * crossref * "}" * - } - if$ - } - { " {\em in} \citeasnoun{" crossref * "}" * } - if$ -} - -FUNCTION {format.book.crossref} -{ volume empty$ - { "empty volume in " cite$ * "'s crossref of " * crossref * warning$ - "in " - } - { "Vol." volume tie.or.space.connect - " of " * - } - if$ - editor empty$ - editor field.or.null author field.or.null = - or - { key empty$ - { series empty$ - { "need editor, key, or series for " cite$ * " to crossref " * - crossref * warning$ - "" * - } - { "{\em " * series * "\/}" * " \cite{" * crossref * "}" *} - if$ - } - { " \citeasnoun{" * crossref * "}" * } - if$ - } - { " \citeasnoun{" * crossref * "}" * } - if$ -} - -FUNCTION {format.incoll.inproc.crossref} -{ editor empty$ - editor field.or.null author field.or.null = - or - { - key empty$ - { booktitle empty$ - { "need editor, key, or booktitle for " cite$ * " to crossref " * - crossref * warning$ - "" - } - { "in {\em " booktitle * "\/}" * " \cite{" * crossref * "}" *} - if$ - } - { " {\em in} \citeasnoun{" crossref * "}" * } - if$ - } - { " {\em in} \citeasnoun{" crossref * "}" * } - if$ - -} - -INTEGERS { len } - -FUNCTION {chop.word} -{ 's := - 'len := - s #1 len substring$ = - { s len #1 + global.max$ substring$ } - 's - if$ -} - -INTEGERS { ind tsslen } - -STRINGS { tss ret rss istr } - -FUNCTION {replace.substring}{ - 'rss := - 'tss := - 'istr := - "" 'ret := - tss text.length$ 'tsslen := - #1 'ind := - { istr ind tsslen substring$ "" = not } - { istr ind tsslen substring$ tss = - { ret rss * 'ret := - ind tsslen + 'ind := - } - { ret istr ind #1 substring$ * 'ret := - ind #1 + 'ind := - } - if$ - } - while$ - ret -} - -FUNCTION {format.lab.names.abbr} -{ 's := - s num.names$ 'numnames := - numnames #1 > - { numnames #2 > - { s #1 "{vv~}{ll}" format.name$ " et~al." * } - { s #2 "{ff }{vv }{ll}{ jj}" format.name$ "others" = - { s #1 "{vv~}{ll}" format.name$ " et~al." * } - { s #1 "{vv~}{ll}" format.name$ " \harvardand\ " * - s #2 "{vv~}{ll}" format.name$ * - } - if$ - } - if$ - } - { s #1 "{vv~}{ll}" format.name$ } - if$ -} - -FUNCTION {format.lab.names.full} -{ 's := - #1 'nameptr := - s num.names$ 'numnames := - numnames 'namesleft := - { namesleft #0 > } - { s nameptr "{vv~}{ll}" format.name$ 't := - nameptr #1 > - { namesleft #1 > - { ", " * t * } - { t "others" = - { " et~al." * } - { " \harvardand\ " * t * } - if$ - } - if$ - } - 't - if$ - nameptr #1 + 'nameptr := - namesleft #1 - 'namesleft := - } - while$ -} - -INTEGERS { author.field editor.field organization.field title.field key.field } - -FUNCTION {init.field.constants} -{ #0 'author.field := - #1 'editor.field := - #2 'organization.field := - #3 'title.field := - #4 'key.field := -} - -FUNCTION {make.list.label} -{ author.field field.used = - { format.authors } - { editor.field field.used = - { format.editors } - { organization.field field.used = - { "The " #4 organization chop.word #3 text.prefix$ } - { title.field field.used = - { format.btitle } - { key.field field.used = - { key #3 text.prefix$ } - { "Internal error :001 on " cite$ * " label" * warning$ } - if$ - } - if$ - } - if$ - } - if$ - } - if$ -} - -FUNCTION {make.full.label} -{ author.field field.used = - { author format.lab.names.full } - { editor.field field.used = - { editor format.lab.names.full } - { organization.field field.used = - { "The " #4 organization chop.word #3 text.prefix$ } - { title.field field.used = - { format.btitle } - { key.field field.used = - { key #3 text.prefix$ } - { "Internal error :001 on " cite$ * " label" * warning$ } - if$ - } - if$ - } - if$ - } - if$ - } - if$ -} - -FUNCTION {make.abbr.label} -{ etal.allowed - { author.field field.used = - { author format.lab.names.abbr } - { editor.field field.used = - { editor format.lab.names.abbr } - { organization.field field.used = - { "The " #4 organization chop.word #3 text.prefix$ } - { title.field field.used = - { format.btitle } - { key.field field.used = - { key #3 text.prefix$ } - { "Internal error :001 on " cite$ * " label" * warning$} - if$ - } - if$ - } - if$ - } - if$ - } - if$ - } - { make.full.label } - if$ -} - -FUNCTION {output.bibitem} -{ newline$ - etal.allowed %%%XXX change - etal.required - and - { - "\harvarditem[" write$ - make.abbr.label write$ - "]{" write$ - } - { - "\harvarditem{" write$ - } - if$ - make.full.label write$ - "}{" write$ - list.year write$ - "}{" write$ - cite$ write$ - "}" write$ - newline$ - "" - before.all 'output.state := -} - - -FUNCTION {list.label.output} -{ make.list.label " " * write$ -} - -FUNCTION {article} -{ output.bibitem - list.label.output - " \harvardyearleft " list.year * "\harvardyearright " * output.nonnull - new.block - author "author" item.check - title.field field.used = - { skip$ } - { format.title "title" output.check } - if$ - crossref missing$ - { journal emphasize "journal" duplicate$ item.check - " " * format.vol.num.pages * output - } - { format.article.crossref output.nonnull - format.pages output - } - if$ - new.block - note output - fin.entry - write.url -} - -FUNCTION {book} -{ output.bibitem - list.label.output - " \harvardyearleft " list.year * "\harvardyearright " * output.nonnull - new.block - author empty$ - { editor "author and editor" item.check } - { crossref missing$ - { "author and editor" editor either.or.check } - 'skip$ - if$ - } - if$ - title.field field.used = - { skip$ } - { format.btitle "title" output.check } - if$ - crossref missing$ - { format.bvolume output - format.number.series output - format.edition output - publisher "publisher" output.check - address output - } - { format.book.crossref output.nonnull - format.edition output - } - if$ - new.block - note output - fin.entry - write.url -} - -FUNCTION {booklet} -{ output.bibitem - list.label.output - " \harvardyearleft " list.year * "\harvardyearright " * output.nonnull - new.block - title.field field.used = - { skip$ } - { format.title "title" output.check } - if$ - howpublished output - address output - new.block - note output - fin.entry - write.url -} - -FUNCTION {inbook} -{ output.bibitem - list.label.output - " \harvardyearleft " list.year * "\harvardyearright " * output.nonnull - new.block - author empty$ - { editor "author and editor" item.check } - { crossref missing$ - { "author and editor" editor either.or.check } - 'skip$ - if$ - } - if$ - title.field field.used = - { skip$ } - { format.btitle "title" output.check } - if$ - crossref missing$ - { format.bvolume output - format.number.series output - format.edition output - publisher "publisher" output.check - address output - } - { format.book.crossref output.nonnull - format.edition output - } - if$ - format.chapter.pages "chapter and pages" output.check - new.block - note output - fin.entry - write.url -} - -FUNCTION {incollection} -{ output.bibitem - list.label.output - " \harvardyearleft " list.year * "\harvardyearright " * output.nonnull - new.block - title.field field.used = - { skip$ } - { format.title "title" output.check } - if$ - author "author" item.check - crossref missing$ - { format.in.ed.booktitle "booktitle" output.check - format.edition output - format.bvolume output - format.number.series output - publisher "publisher" output.check - address output - } - { format.incoll.inproc.crossref output.nonnull - } - if$ - format.chapter.pages output - new.block - note output - fin.entry - write.url -} - -FUNCTION {inproceedings} -{ output.bibitem - list.label.output - " \harvardyearleft " list.year * "\harvardyearright " * output.nonnull - new.block - title.field field.used = - { skip$ } - { format.title "title" output.check } - if$ - author "author" item.check - crossref missing$ - { format.in.ed.booktitle "booktitle" output.check - format.bvolume output - format.number.series output - address empty$ - { organization output - publisher output - } - { organization output - publisher output - address output.nonnull - } - if$ - } - { format.incoll.inproc.crossref output.nonnull - } - if$ - format.pages output - new.block - note output - fin.entry - write.url -} - -FUNCTION {conference} { inproceedings } - -FUNCTION {manual} -{ output.bibitem - list.label.output - " \harvardyearleft " list.year * "\harvardyearright " * output.nonnull - new.block - title.field field.used = - { skip$ } - { format.btitle "title" output.check } - if$ - format.edition output - author empty$ - { organization empty$ - { address output - } - 'skip$ - if$ - } - { organization output - address output - } - if$ - new.block - note output - fin.entry - write.url -} - -FUNCTION {mastersthesis} -{ output.bibitem - list.label.output - " \harvardyearleft " list.year * "\harvardyearright " * output.nonnull - new.block - author "author" item.check - title.field field.used = - { skip$ } - { format.title emphasize "title" output.check } - if$ - "Master's thesis" format.thesis.type output.nonnull - school "school" output.check - address output - new.block - note output - fin.entry - write.url -} - -FUNCTION {misc} -{ output.bibitem - list.label.output - " \harvardyearleft " list.year * "\harvardyearright " * output.nonnull - new.block - title.field field.used = - { skip$ } - { format.title output } - if$ - howpublished output - new.block - note output - fin.entry - write.url - empty.misc.check -} - -FUNCTION {phdthesis} -{ output.bibitem - list.label.output - " \harvardyearleft " list.year * "\harvardyearright " * output.nonnull - new.block - author "author" item.check - title.field field.used = - { skip$ } - { format.btitle "title" output.check } - if$ - "PhD thesis" format.thesis.type output.nonnull - school "school" output.check - address output - new.block - note output - fin.entry - write.url -} - -FUNCTION {proceedings} -{ output.bibitem - list.label.output - " \harvardyearleft " list.year * "\harvardyearright " * output.nonnull - new.block - title.field field.used = - { skip$ } - { format.btitle "title" output.check } - if$ - format.bvolume output - format.number.series output - address empty$ - { editor empty$ - { skip$ } - { organization output - } - if$ - publisher output - } - { editor empty$ - 'skip$ - { organization output } - if$ - publisher output - address output.nonnull - } - if$ - new.block - note output - fin.entry - write.url -} - -FUNCTION {techreport} -{ output.bibitem - list.label.output - " \harvardyearleft " list.year * "\harvardyearright " * output.nonnull - new.block - author "author" item.check - title.field field.used = - { skip$ } - { format.title "title" output.check } - if$ - format.tr.number emphasize output.nonnull - institution "institution" output.check - address output - new.block - note output - fin.entry - write.url -} - -FUNCTION {unpublished} -{ output.bibitem - list.label.output - " \harvardyearleft " list.year * "\harvardyearright " * output.nonnull - new.block - author "author" item.check - title.field field.used = - { skip$ } - { format.title "title" output.check } - if$ - new.block - note "note" output.check - fin.entry - write.url -} - -FUNCTION {default.type} { misc } - -MACRO {jan} {"January"} - -MACRO {feb} {"February"} - -MACRO {mar} {"March"} - -MACRO {apr} {"April"} - -MACRO {may} {"May"} - -MACRO {jun} {"June"} - -MACRO {jul} {"July"} - -MACRO {aug} {"August"} - -MACRO {sep} {"September"} - -MACRO {oct} {"October"} - -MACRO {nov} {"November"} - -MACRO {dec} {"December"} - -MACRO {acmcs} {"ACM Computing Surveys"} - -MACRO {acta} {"Acta Informatica"} - -MACRO {cacm} {"Communications of the ACM"} - -MACRO {ibmjrd} {"IBM Journal of Research and Development"} - -MACRO {ibmsj} {"IBM Systems Journal"} - -MACRO {ieeese} {"IEEE Transactions on Software Engineering"} - -MACRO {ieeetc} {"IEEE Transactions on Computers"} - -MACRO {ieeetcad} - {"IEEE Transactions on Computer-Aided Design of Integrated Circuits"} - -MACRO {ipl} {"Information Processing Letters"} - -MACRO {jacm} {"Journal of the ACM"} - -MACRO {jcss} {"Journal of Computer and System Sciences"} - -MACRO {scp} {"Science of Computer Programming"} - -MACRO {sicomp} {"SIAM Journal on Computing"} - -MACRO {tocs} {"ACM Transactions on Computer Systems"} - -MACRO {tods} {"ACM Transactions on Database Systems"} - -MACRO {tog} {"ACM Transactions on Graphics"} - -MACRO {toms} {"ACM Transactions on Mathematical Software"} - -MACRO {toois} {"ACM Transactions on Office Information Systems"} - -MACRO {toplas} {"ACM Transactions on Programming Languages and Systems"} - -MACRO {tcs} {"Theoretical Computer Science"} - -READ - -EXECUTE {init.field.constants} - -FUNCTION {sortify} -{ purify$ - "l" change.case$ -} - -FUNCTION {sortify.names} -{ " \harvardand\ " " " replace.substring - " et~al." " zzz" replace.substring - sortify -} - -FUNCTION {author.key.label} -{ author empty$ - { key empty$ - { title.field 'field.used := } - { key.field 'field.used := } - if$ - } - { author.field 'field.used := } - if$ -} - -FUNCTION {author.editor.key.label} -{ author empty$ - { editor empty$ - { key empty$ - { title.field 'field.used := } - { key.field 'field.used := } - if$ - } - { editor.field 'field.used := } - if$ - } - { author.field 'field.used := } - if$ -} - -FUNCTION {author.key.organization.label} -{ author empty$ - { key empty$ - { organization empty$ - { title.field 'field.used := } - { organization.field 'field.used := } - if$ - } - { key.field 'field.used := } - if$ - } - { author.field 'field.used := } - if$ -} - -FUNCTION {editor.key.organization.label} -{ editor empty$ - { key empty$ - { organization empty$ - { title.field 'field.used := } - { organization.field 'field.used := } - if$ - } - { key.field 'field.used := } - if$ - } - { editor.field 'field.used := } - if$ -} - -FUNCTION {sort.format.title} -{ 't := - "A " #2 - "An " #3 - "The " #4 t chop.word - chop.word - chop.word - sortify - #1 global.max$ substring$ -} - -FUNCTION {calc.label} -{ make.abbr.label - title.field field.used = - { sort.format.title } - { sortify.names } - if$ - year field.or.null purify$ #-1 #4 substring$ sortify - * - 'sort.label := -} - -FUNCTION {preliminaries} %%%XXX change -{ type$ "book" = - type$ "inbook" = - or - 'author.editor.key.label - { type$ "proceedings" = - 'editor.key.organization.label - { type$ "manual" = - 'author.key.organization.label - 'author.key.label - if$ - } - if$ - } - if$ - author.field field.used = %%%XXX change - { - author num.names$ #2 > - { #1 } - { #0 } - if$ - 'etal.required := - } - { - editor.field field.used = - { - editor num.names$ #2 > - { #1 } - { #0 } - if$ - } - { #0 } - if$ - 'etal.required := - } - if$ - #1 'etal.allowed := -} - -FUNCTION {first.presort} -{ calc.label - sort.label - title.field field.used = - { skip$ } - { " " - * - make.list.label sortify.names - * - " " - * - title field.or.null - sort.format.title - * - } - if$ - #1 entry.max$ substring$ - 'sort.key$ := -} - -ITERATE {preliminaries} - -ITERATE {first.presort} - -SORT - -STRINGS { last.sort.label next.extra last.full.label } - -INTEGERS { last.extra.num last.etal.allowed } - -FUNCTION {initialize.confusion} -{ #0 int.to.chr$ 'last.sort.label := - #0 int.to.chr$ 'last.full.label := - #1 'last.etal.allowed := -} - -FUNCTION {confusion.pass} -{ last.sort.label sort.label = - { last.etal.allowed - { last.full.label make.full.label sortify.names = - { skip$ } - { #0 'etal.allowed := - #0 'last.etal.allowed := - } - if$ - } - { #0 'etal.allowed := } - if$ - } - { sort.label 'last.sort.label := - make.full.label sortify.names 'last.full.label := - #1 'last.etal.allowed := - } - if$ -} - -EXECUTE {initialize.confusion} - -ITERATE {confusion.pass} - -EXECUTE {initialize.confusion} - -REVERSE {confusion.pass} - -FUNCTION {initialize.last.extra.num} -{ #0 int.to.chr$ 'last.sort.label := - "" 'next.extra := - #0 'last.extra.num := -} - -FUNCTION {forward.pass} -{ last.sort.label sort.label = - { last.extra.num #1 + 'last.extra.num := - last.extra.num int.to.chr$ 'extra.label := - } - { "a" chr.to.int$ 'last.extra.num := - "" 'extra.label := - sort.label 'last.sort.label := - } - if$ -} - -FUNCTION {reverse.pass} -{ next.extra "b" = - { "a" 'extra.label := } - 'skip$ - if$ - year empty$ - { "n.d." extra.label * 'list.year := } - { year extra.label * 'list.year := } - if$ - extra.label 'next.extra := -} - -ITERATE {first.presort} - -SORT - -EXECUTE {initialize.last.extra.num} - -ITERATE {forward.pass} - -REVERSE {reverse.pass} - -FUNCTION {second.presort} -{ make.list.label - title.field field.used = - { sort.format.title } - { sortify.names } - if$ - " " - * - list.year field.or.null sortify - * - " " - * - title.field field.used = - { skip$ } - { title field.or.null - sort.format.title - * - } - if$ - #1 entry.max$ substring$ - 'sort.key$ := -} - -ITERATE {second.presort} - -SORT - -FUNCTION {begin.bib} -{ preamble$ empty$ - 'skip$ - { "\harvardpreambledefs{%" write$ newline$ - preamble$ write$ "}" write$ newline$ - "\harvardpreambletext{%" write$ newline$ - preamble$ write$ "}" write$ newline$ } - if$ - "\begin{thebibliography}{xx}" write$ newline$ -} - -EXECUTE {begin.bib} - -EXECUTE {init.state.consts} - -ITERATE {call.type$} - -FUNCTION {end.bib} -{ newline$ - "\end{thebibliography}" write$ newline$ -} - -EXECUTE {end.bib} diff --git a/doc/UoA_logo.eps b/doc/UoA_logo.eps deleted file mode 100755 index a464503f..00000000 Binary files a/doc/UoA_logo.eps and /dev/null differ diff --git a/doc/appendix_geometry_format.tex b/doc/appendix_geometry_format.tex deleted file mode 100644 index 83be2668..00000000 --- a/doc/appendix_geometry_format.tex +++ /dev/null @@ -1,285 +0,0 @@ -\chapter{MULgraph geometry file format} -\label{geometry_file_format} -\index{MULgraph geometry!file format} - -\section{Introduction} -This appendix gives a format specification of the MULgraph geometry file. These files can be used to give a geometrical description of a TOUGH2 model grid, useful for creating grids and visualizing simulation results. - -MULgraph geometry files were originally developed for use with MULgraph, a graphical post-processor for TOUGH2 and AUTOUGH2 \citep{mulgraph} developed at the University of Auckland in the 1990s. However, MULgraph geometry files can be used independently of MULgraph. PyTOUGH is able to represent the contents of a MULgraph geometry file in a Python script via the \hyperref[mulgrids]{\texttt{mulgrid}} class. - -\section{Grid structure} -\index{MULgraph geometry!grid structure} - -\subsection{Layers and columns} -\index{MULgraph geometry!layers} -\index{MULgraph geometry!columns} -MULgraph geometry files implicitly assume a layered structure, with blocks arranged in layers and columns, and the same arrangement of columns in each layer. The only exception to this is at the top surface of the model, where layers are allowed to be incomplete (i.e. not contain all columns) in order to represent topography. - -The layers are always of constant vertical thickness. However, the blocks in the top layer are allowed to vary in height, again to represent variations in ground surface elevation. - -\subsection{Atmosphere blocks} -\index{MULgraph geometry!blocks!atmosphere} -The blocks in the top layer may optionally be connected to the atmosphere- either a single atmosphere block connected to all columns, or a separate atmosphere block over each column (see section \ref{geometry_format_conventions}). - -\subsection{Tilted geometries} -\index{MULgraph geometry!tilting} -It is possible to tilt the geometry coordinate axes with respect to the vertical, to represent non-horizontal geometries. When a TOUGH2 grid is created from such a tilted geometry, only the gravity cosines of the grid connections are affected. - -\subsection{Rotating permeability directions} -\index{MULgraph geometry!permeability directions} -It is also possible to rotate the permeability principal directions with respect to the coordinate axes- for example, to align permeabilities with a dominant fault direction. When a TOUGH2 grid is created, this can change the permeability index associated with each connection. - -\section{Geometry types} -\index{MULgraph geometry!geometry type} -The original MULgraph file specification allowed for three types of geometry: `general', `rectangular' and `radial'. Only the `general' geometry type is supported by PyTOUGH. It is intended for representing general grids with arbitrary, possibly unstructured horizontal column arrangements. - -The `rectangular' type was a special type for grids with rectangular horizontal column structures. These can also be represented using the `general' geometry type. Since PyTOUGH contains \hyperref[sec:mulgrid:rectangular]{methods} for constructing rectangular grids within the `general' geometry type, there is usually no longer any significant benefit from using the `rectangular' type. - -The `radial' type was intended for grids with radial horizontal column structure. PyTOUGH also contains \hyperref[sec:t2grid:radial]{methods} for creating radial TOUGH2 grids. Simulation results from radial models can also be visualized using a simple one- or two-dimensional rectangular `general' geometry to represent the grid structure in the radial direction. - -\section{Naming conventions and atmosphere types} -\label{geometry_format_conventions} -\index{MULgraph geometry!blocks!naming convention} -\index{MULgraph geometry!columns!naming convention} -\index{MULgraph geometry!nodes!naming convention} -\index{MULgraph geometry!layers!naming convention} -\index{MULgraph geometry!atmosphere type} - -The grid block naming convention and atmosphere type used in a MULgraph geometry file are both integers which can be given the value 0, 1 or 2. The meanings of these values are shown in Table \ref{tb:mulgrid_conventions} and \ref{tb:mulgrid_atmosphere_types}. - -Note that the grid nodes (vertices) are also named according to the column part of the block naming convention. If naming nodes, columns or layers manually, while the names can in principle be arbitrary (within the naming convention), it is safest to right-justify them. - -The MULgraph block naming conventions all use part of the block name to indicate the layer, and part of it to indicate the column. In PyTOUGH, it is also possible to use MULgraph geometry files in conjunction with TOUGH2 grids that follow other naming conventions, by means of a \hyperref[sec:mulgrid:blockmappings]{block mapping} dictionary. - -\begin{table}[h] - \begin{center} - \begin{tabular}{|l|p{85mm}|} - \hline - 0 & 3 characters for column followed by 2 digits for layer \\ - 1 & 3 characters for layer followed by 2 digits for column \\ - 2 & 2 characters for layer followed by 3 digits for column \\ - \hline - \end{tabular} - \caption{MULgraph geometry file naming conventions} - \label{tb:mulgrid_conventions} - \end{center} -\end{table} - -\begin{table}[h] - \begin{center} - \begin{tabular}{|l|p{85mm}|} - \hline - 0 & A single atmosphere block \\ - 1 & One atmosphere block over each column \\ - 2 & No atmosphere blocks \\ - \hline - \end{tabular} - \caption{MULgraph geometry file atmosphere types} - \label{tb:mulgrid_atmosphere_types} - \end{center} -\end{table} - -\section{File format} -MUlgraph geometry files are simple formatted ASCII text files with a header line at the top, followed by a number of sections. Each section begins with a keyword and ends with a blank line. Each line has \textbf{fixed} format, so the different values have to be specified in the right text columns. - -If you use PyTOUGH scripts to create and manipulate your grid geometries, you don't need to know anything about the format of a MULgraph geometry file, because PyTOUGH will handle reading and writing them for you. If, however, for some reason you do need to know how these files are structured, the format specification for a `general' type geometry file is given below. - -\subsection{Header} -\index{MULgraph geometry!header} - -This is a single line containing a number of global parameters of the geometry. Its format is given in table \ref{tb:mulgraph_format_header}. - -Note that the block ordering parameter is an extension to the original MULgraph file format. - -\begin{table} - \begin{center} - \begin{tabular}{|p{20mm}|l|l|l|p{50mm}|} - \hline - \textbf{Name} & \textbf{Type} & \textbf{Length} & \textbf{Columns} & \textbf{Description}\\ - \hline - \textbf{Geometry type} & character & 5 & 1--5 & `GENER' for general geometry type; `RECTA' or `RADIA' for other types (but these are not supported by PyTOUGH)\\ - \hline - \textbf{Naming convention} & integer & 1 & 6 & Block naming convention: see table \ref{tb:mulgrid_conventions}\\ - \hline - \textbf{Atmosphere type} & integer & 1 & 7 & Type of atmosphere: see table \ref{tb:mulgrid_atmosphere_types}\\ - \hline - \textbf{Atmosphere volume} & float & 10 & 8--17 & Volume of each atmosphere block (default $10^{20} m^3$)\\ - \hline - \textbf{Atmosphere connection distance} & float & 10 & 18--27 & Connection distance for each atmosphere block (default $10^{-6} m$) \\ - \hline - \textbf{Length unit} & character & 5 & 28--32 & Default is metres (blank); for feet specify `FEET'\\ - \hline - \textbf{x-direction cosine} & float & 10 & 33--42 & Cosine of angle between x-axis and gravity vector (default zero); set positive for tilt in the x-direction\\ - \hline - \textbf{y-direction cosine} & float & 10 & 43--52 & Cosine of angle between y-axis and gravity vector (default zero); set positive for tilt in the y-direction\\ - \hline - \textbf{Connection type} & integer & 1 & 53 & Method of calculating connection parameters (default zero)- not supported by PyTOUGH\\ - \hline - \textbf{Permeability angle} & float & 10 & 54--63 & Horizontal angle (degrees anti-clockwise) between first permeability direction and x-axis\\ - \hline - \textbf{Block ordering} & integer & 2 & 64--65 & Block ordering scheme: 0 for original MULgraph layer/column ordering; 1 for PETSc DMPlex ordering (sorted by block type)\\ - \hline - \end{tabular} - \caption{MULgraph geometry file header line format} - \label{tb:mulgraph_format_header} - \end{center} -\end{table} - -\subsection{Vertices} -\index{MULgraph geometry!nodes!format} -This section defines the horizontal locations of the grid vertices (nodes), at the corners of the columns. The first line just contains the keyword `VERTI'. Each subsequent line defines the position of a vertex, and has the format given in table \ref{tb:mulgraph_format_vertices}. The vertices section is terminated by a blank line. - -\begin{table} - \begin{center} - \begin{tabular}{|p{20mm}|l|l|l|p{50mm}|} - \hline - \textbf{Name} & \textbf{Type} & \textbf{Length} & \textbf{Columns} & \textbf{Description}\\ - \hline - \textbf{Vertex name} & character & 3 & 1--3 & Name of the vertex (honouring the \hyperref[tb:mulgrid_conventions]{column naming convention})\\ - \hline - \textbf{x} & float & 10 & 4--13 & x-coordinate of the vertex\\ - \hline - \textbf{y} & float & 10 & 14--23 & y-coordinate of the vertex\\ - \hline - \end{tabular} - \caption{MULgraph geometry file vertices format} - \label{tb:mulgraph_format_vertices} - \end{center} -\end{table} - -\subsection{Grid} -\index{MULgraph geometry!columns!format} -This section specifies the vertices making up each column. The first line just contains the keyword `GRID'. - -For each grid column, there is then a sub-header line with information about the column, followed by a line for each vertex making up the column. The sub-header line has the format given in table \ref{tb:mulgraph_format_column_header}, and the line for each vertex has the format given in table \ref{tb:mulgraph_format_column_vertex}. There are no blank lines between the definitions of the grid columns, but there is a blank line at the end of the section. - -\begin{table}[h] - \begin{center} - \begin{tabular}{|p{20mm}|l|l|l|p{50mm}|} - \hline - \textbf{Name} & \textbf{Type} & \textbf{Length} & \textbf{Columns} & \textbf{Description}\\ - \hline - \textbf{Column name} & character & 3 & 1--3 & Name of the column (honouring the \hyperref[tb:mulgrid_conventions]{column naming convention})\\ - \hline - \textbf{Centre specified} & integer & 1 & 4--5 & Set non-zero to specify the column centre location, or zero (default) to calculate it as the centroid of the column\\ - \hline - \textbf{Number of vertices} & integer & 2 & 6--7 & Number of vertices in the column\\ - \hline - \textbf{Column centre x} & float & 10 & 8--17 & x-coordinate of column centre\\ - \hline - \textbf{Column centre y} & float & 10 & 18--27 & y-coordinate of column centre\\ - \hline - \end{tabular} - \caption{MULgraph geometry file column header format} - \label{tb:mulgraph_format_column_header} - \end{center} -\end{table} - -\begin{table}[h] - \begin{center} - \begin{tabular}{|p{20mm}|l|l|l|p{50mm}|} - \hline - \textbf{Name} & \textbf{Type} & \textbf{Length} & \textbf{Columns} & \textbf{Description}\\ - \hline - \textbf{Vertex name} & character & 3 & 1--3 & Name of the vertex, as specified in the vertices section\\ - \hline - \end{tabular} - \caption{MULgraph geometry file column vertex format} - \label{tb:mulgraph_format_column_vertex} - \end{center} -\end{table} - -\subsection{Connections} -\index{MULgraph geometry!connections!format} -This section defines the horizontal connections between columns. The first line just contains the keyword `CONNE'. - -Each subsequent line defines a connection between two columns, and has the format given in table \ref{tb:mulgraph_format_connection}. There is a blank line at the end of the section. - -\begin{table}[h] - \begin{center} - \begin{tabular}{|p{20mm}|l|l|l|p{50mm}|} - \hline - \textbf{Name} & \textbf{Type} & \textbf{Length} & \textbf{Columns} & \textbf{Description}\\ - \hline - \textbf{First column name} & character & 3 & 1--3 & Name of the first column\\ - \hline - \textbf{Second column name} & character & 3 & 4--6 & Name of the second column\\ - \hline - \end{tabular} - \caption{MULgraph geometry file connection format} - \label{tb:mulgraph_format_connection} - \end{center} -\end{table} - -\subsection{Layers} -\index{MULgraph geometry!layers!format} -This section defines the grid layers. The first line just contains the keyword `LAYER'. - -Each subsequent line defines a layer, with format given by table \ref{tb:mulgraph_format_layer}. There are no blank lines between layers, but there is a blank line at the end of the section. - -\begin{table}[h] - \begin{center} - \begin{tabular}{|p{20mm}|l|l|l|p{50mm}|} - \hline - \textbf{Name} & \textbf{Type} & \textbf{Length} & \textbf{Columns} & \textbf{Description}\\ - \hline - \textbf{Layer name} & character & 3 & 1--3 & Name of the layer (honouring the \hyperref[tb:mulgrid_conventions]{layer naming convention})\\ - \hline - \textbf{Bottom elevation} & float & 10 & 4--13 & Elevation of the bottom of the layer\\ - \hline - \textbf{Centre elevation} & float & 10 & 14--23 & Elevation of the centre of the layer\\ - \hline - \end{tabular} - \caption{MULgraph geometry file layer format} - \label{tb:mulgraph_format_layer} - \end{center} -\end{table} - -\subsection{Surface elevation} -\index{MULgraph geometry!columns!surface elevation} -This section is optional, and can be used to define the surface elevation at any or all columns in the grid, to represent topography. The first line just contains the keyword `SURFA'. - -Each subsequent line defines the surface elevation at a column, with format given by table \ref{tb:mulgraph_format_surface}. There is a blank line at the end of the section. - -\begin{table}[h] - \begin{center} - \begin{tabular}{|p{20mm}|l|l|l|p{50mm}|} - \hline - \textbf{Name} & \textbf{Type} & \textbf{Length} & \textbf{Columns} & \textbf{Description}\\ - \hline - \textbf{Column name} & character & 3 & 1--3 & Name of the column\\ - \hline - \textbf{Surface elevation} & float & 10 & 4--13 & Surface elevation of the column\\ - \hline - \end{tabular} - \caption{MULgraph geometry file surface elevation format} - \label{tb:mulgraph_format_surface} - \end{center} -\end{table} - -\subsection{Wells} -\index{MULgraph geometry!wells!format} -This section is optional, and can be used to define the positions of wells (including their tracks) within the geometry. Deviated wells are supported. The first line of the section just contains the keyword `WELLS'. - -Each subsequent line defines the location of one point on a well track, with format given by table \ref{tb:mulgraph_format_wells}. At least two points are required to define each well (one for the wellhead and one for the bottom), with more than two points needed to define a deviated well. There is a blank line at the end of the section. - -\begin{table}[h] - \begin{center} - \begin{tabular}{|p{20mm}|l|l|l|p{50mm}|} - \hline - \textbf{Name} & \textbf{Type} & \textbf{Length} & \textbf{Columns} & \textbf{Description}\\ - \hline - \textbf{Well name} & character & 5 & 1--5 & Name of the well\\ - \hline - \textbf{x} & float & 10 & 6--15 & x-coordinate of the well location\\ - \hline - \textbf{y} & float & 10 & 16--25 & y-coordinate of the well location\\ - \hline - \textbf{z} & float & 10 & 26--35 & z-coordinate of the well location\\ - \hline - \end{tabular} - \caption{MULgraph geometry file well format} - \label{tb:mulgraph_format_wells} - \end{center} -\end{table} - diff --git a/doc/coverpic.eps b/doc/coverpic.eps deleted file mode 100755 index dba29eab..00000000 --- a/doc/coverpic.eps +++ /dev/null @@ -1,3391 +0,0 @@ -%!PS-Adobe-3.0 EPSF-3.0 -%%Creator: GIMP PostScript file plugin V 1.17 by Peter Kirchgessner -%%Title: coverpic.eps -%%CreationDate: Fri Feb 26 15:20:43 2010 -%%DocumentData: Clean7Bit -%%LanguageLevel: 2 -%%Pages: 1 -%%BoundingBox: 14 14 861 797 -%%EndComments -%%BeginProlog -% Use own dictionary to avoid conflicts -10 dict begin -%%EndProlog -%%Page: 1 1 -% Translate for offset -14.173228346456694 14.173228346456694 translate -% Translate to begin of first scanline -0 782 translate -846 -782 scale -% Image geometry -846 782 8 -% Transformation matrix -[ 846 0 0 782 0 0 ] -% Strings to hold RGB-samples per scanline -/rstr 846 string def -/gstr 846 string def -/bstr 846 string def -{currentfile /ASCII85Decode filter /RunLengthDecode filter rstr readstring pop} -{currentfile /ASCII85Decode filter /RunLengthDecode filter gstr readstring pop} -{currentfile /ASCII85Decode filter /RunLengthDecode filter bstr readstring pop} -true 3 -%%BeginData: 161803 ASCII Bytes -colorimage -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$pAb%bJcC<$_uG5~> -JcC<$JcC<$pAb&%JcC<$_uG5~> -JcC<$JcC<$pAb'#JcC<$_uG5~> -JcC<$JcC<$pAah\JcC<$ao?k~> -JcC<$JcC<$pAahtJcC<$ao?k~> -JcC<$JcC<$pAairJcC<$ao?k~> -JcC<$JcC<$p]'_WJcC<$cMrC~> -JcC<$JcC<$p]'_oJcC<$cMrC~> -JcC<$JcC<$p]'`mJcC<$cMrC~> -JcC<$JcC<$p]'PRJcC<$e,Op~> -JcC<$JcC<$p]'PjJcC<$e,Op~> -JcC<$JcC<$p]'QhJcC<$e,Op~> -JcC<$JcC<$p]'AMJcC<$f`-I~> -JcC<$JcC<$p]'AeJcC<$f`-I~> -JcC<$JcC<$p]'BcJcC<$f`-I~> -JcC<$JcC<$p]'/GJcC<$hZ&*~> -JcC<$JcC<$p]'/_JcC<$hZ&*~> -JcC<$JcC<$p]'0]JcC<$hZ&*~> -JcC<$JcEaiqFnL#g.d.uJcFd1J,~> -JcC<$JcEaiqII2;g1>j8JcFd1J,~> -JcC<$JcEaiqR='9g:2_6JcFd1J,~> -JcC<$JcF=$ekFBheP1VpJcFs6J,~> -JcC<$JcF=$en!)+eRa=3JcFs6J,~> -JcC<$JcF=$f!is)e[U21JcFs6J,~> -JcC<$JcFm4YtX3XcqT)kJcG-;J,~> -JcC<$JcFm4Z"2npct.e.JcG-;J,~> -JcC<$JcFm4Z+&cnd("Z,JcG-;J,~> -JcC<$JcGHDN(j$Hb"[HeJcG?AJ,~> -JcC<$JcGHDN+D_`b%6/(JcG?AJ,~> -JcC<$JcGHDN48T^b.*$&JcG?AJ,~> -JcC<$L];p!nk@"sJcC<$q#>j~> -JcC<$L];p9nmo^NJcC<$q#>j~> -JcC<$L];q7o!cTJJcC<$q#>j~> -JcC<$N;nH&mS(AiJcC<$rVqB~> -JcC<$N;nH>mUX(DJcC<$rVqB~> -JcC<$N;nI -JcC<$N;iksJP>N+\kRbUK)^?~> -JcC<$N;il(JRn4[\n-HmK)^?~> -JcC<$N;ilLJ[b*W]"!=kK)^?~> -JcC<$N;rnsJP>Q,ZqZ,OM#Vu~> -JcC<$N;ro(JRn7\Zt4ggM#Vu~> -JcC<$N;roLJ[b-X[((\eM#Vu~> -JcC<$N;rkrJP>T-Y>'TJNW4M~> -JcC<$N;rl'JRn:]Y@W:bNW4M~> -JcC<$N;rlKJ[b0YYIK/`NW4M~> -JcC<$N;rhqJP>Z/WD.sDP5g%~> -JcC<$N;ri&JRn@_WF^Y\P5g%~> -JcC<$N;riJJ[b6[WORNZP5g%~> -JcC<$N;repJP>]0UeQF?QiDR~> -JcC<$N;rf%JRnC`Uh,,WQiDR~> -JcC<$N;rfIJ[b9\Upu!UQiDR~> -JcC<$N;rboJP>`1ThU+ -JcC<$N;rc$JRnFaTk/fTRf@m~> -JcC<$N;rcHJ[b<]Tt#[RRf@m~> -JcC<$N;iktq\t)[p.V#OJcD>AJ,~> -JcC<$N;r`#JRnIbSRmBPT)X<~> -JcC<$N;r`GJ[b?^S[a7NT)X<~> -JcC<$N;rntq\t)[pe7)MJcDGDJ,~> -JcC<$N;r]"JRnOdR:UsLU&TW~> -JcC<$N;r]FJ[bE`RCIhJU&TW~> -JcC<$N;rksq\t)[q+R&JJcDSHJ,~> -JcC<$N;rZ!JRnReQ">OHV>l&~> -JcC<$N;rZEJ[bHaQ+2DFV>l&~> -JcC<$N;rhrq\t)[qFm&HJcD\KJ,~> -JcC<$N;rVuJRnUfP%B4EW;hA~> -JcC<$N;rWDJ[bKbP.6)CW;hA~> -JcC<$N;reqq\t)[qb3#EJcDhOJ,~> -JcC<$N;rStJRnXgNb*eAXT*e~> -JcC<$N;rTCJ[bNcNjsZ?XT*e~> -JcC<$N;reqqAXuZr(N#CJcDqRJ,~> -JcC<$N;il)p*G9#r+(^sJcDqRJ,~> -JcC<$N;ilMp.9hEr3qToJcDqRJ,~> -JcC<$N;rbpqAXuZr_/&@JcE(VJ,~> -JcC<$N;ro)p*G9#ra^apJcE(VJ,~> -JcC<$N;roMp.9hErjRWlJcE(VJ,~> -JcC<$N;r_oqAXuZs%J&>JcE1YJ,~> -JcC<$N;rl(p*G9#s($anJcE1YJ,~> -JcC<$N;rlLp.9hEs0mWjJcE1YJ,~> -JcC<$N;r\nq\t)[s%Iu -JcC<$N;ri'pEbB$s($[ls(-c]])R9~> -JcC<$N;riKpITqFs0mQhs1!X[])R9~> -JcC<$N;ikuq&K8NJP6&;JPH&:JcEF`J,~> -JcC<$N;rf&pEbB$!+^Xlr+1HZ^&NT~> -JcC<$N;rfJpITqF!4RNhr4%=X^&NT~> -JcC<$N;rnuq&K8NJkU_ipJ$5=_>f#~> -JcC<$N;rc%pEbE%JS"ReJcERdJ,~> -JcC<$N;rcIpITtGJ[kHaJcERdJ,~> -JcC<$N;rktq&K8NK1phjoM'o:`;b>~> -JcC<$N;r`$pEbH&JS"IbJcE[gJ,~> -JcC<$N;r`HpIU"HJ[k?^JcE[gJ,~> -JcC<$O8o2TrZ1bRq\t2^JPGW.JcEgkJ,~> -JcC<$O8o2kq'Ph^KOfXFn7@1NaT$b~> -JcC<$O8o3jq+CBQKXZNBn@4&LaT$b~> -JcC<$PlLMSrZ1bRq\t5_JPGN+JcEpnJ,~> -JcC<$PlLMjq'Ph^Kk,aGm:CkKbQ!(~> -JcC<$PlLNiq+CBQKsuWCmC7`IbQ!(~> -JcC<$RK)hRrZ1bRq\t8`JPGB'JcF'rJ,~> -JcC<$RK)hiq'Ph^L1GjHl",GGci8L~> -JcC<$RK)ihq+CBQL:;`Dl*u -JcC<$ScA(QrZ1bRq\t>bJPG6#JcF0uJ,~> -JcC<$ScA(hqBkn^Lh)'Jj^j#Cdf4g~> -JcC<$ScA)gqF^HQLpqrFjg]mAdf4g~> -JcC<$UAsCPrZ1bRq\tAcJPG)tJcF=$J,~> -JcC<$UAsCgqBkn^M.D0KiFRT?f)L7~> -JcC<$UAsDfqF^HQM78&GiOFI=f)L7~> -JcC<$VuP^OrZ1bRq\tDdJPFrpJcFI(J,~> -JcC<$VuP^fqBkn^MI_9Lh.;0;gAc[~> -JcC<$VuP_eqF^HQMRS/Hh7/%9gAc[~> -JcC<$XT.'Or>k\RqAY>dJPFimJcFR+J,~> -JcC<$XT.'fq'Pe]Me%BMg1>j8h>`!~> -JcC<$XT.(eq+C?PMmn8Ig:2_6h>`!~> -JcC<$YlEk\RqAYDfJPFZhJcF^/J,~> -JcC<$YlE -JcC<$YlE=dq+C?PNOOJKe[U21iW"E~> -JcC<$[K"WMr>k\RqAYGgJPFQeJcFg2J,~> -JcC<$[K"Wdq'Pe]Nb!]PdUe"0jSs`~> -JcC<$[K"Xcq+C?PNjjSLd^Xl.jSs`~> -JcC<$])TrLr>k\RqAYJhJPFZhr[*3rjSs`~> -JcC<$])Trcq'Pe]O( -JcC<$])Tsbq+C?PO10\Me[YThJcFg2J,~> -JcC<$^]28Kr>k\Rq\tSiJPF`jr$I!pjSs`~> -JcC<$^]28bq'Ph^O( -JcC<$^]29aq+CBQO10\Mf=:`hJcFg2J,~> -JcC<$_uIMJr>k\Rq\tYkJPFflq'L[mjSs`~> -JcC<$_uIMaq'Ph^O^s#Sfk't8JcFg2J,~> -JcC<$_uIN`q+CBQOgfnOfspigJcFg2J,~> -JcC<$aT&hIrZ1bRq\t\lr??eei(`qmJcFd1J,~> -JcC<$aT&h`q'Ph^P%=MDJS!bNpG7C#j8XW~> -JcC<$aT&i_q+CBQP.1BoJ[jXJpLJjTj8XW~> -JcC<$c2Y.HrZ1bRq\t_mpEG/_ktUdsJcFd1J,~> -JcC<$c2Y._q'Ph^P@XD?JS"(WoJ;'uj8XW~> -JcC<$c2Y/^q+CBQPIL9jJ[jsSoONOQj8XW~> -JcC<$dJpCGrZ1bRq\tbnnfiWZn4iI#JcFd1J,~> -JcC<$dJpC^q'Ph^P[s>;JS"=^nhYjsj8XW~> -JcC<$dJpD]q+CBQPdg3fJ[k3Znmm=Oj8XW~> -JcC<$f)M^FrZ1bRq\teollq!Tq+^<)JcFd1J,~> -JcC<$f)M^]qBkn^Q"956JS"Xgmk]Opj8XW~> -JcC<$f)M_\qF^HQQ+-*aJ[kNcmpq"Lj8XW~> -JcC<$g]+$ErZ1bRq\thpk9>IOlm@;`j8XW~> -JcC<$g]+$\qBkn^Q=T/2JS"1(JcFd1J,~> -JcC<$g]+%[qF^HQQFH$]J[k&WJcFd1J,~> -JcC<$i;]?DrZ1bRq\tkqi?F.RkpCu]j8XW~> -JcC<$i;]?[qBkn^QXo&-MIl$.JcFd1J,~> -JcC<$i;]@ZqF^HQQabpXMR_n]JcFd1J,~> -JcC<$jStWDr>kYQq\tnrg`hkTk9bc[j8XW~> -JcC<$jStW[q'Pe]Qt4u)O_*]3JcFd1J,~> -JcC<$jStXZq+C?PR((jTOgsRbJcFd1J,~> -JcC<$kl6lCr>k\RqA\*]p*BJZdnJ=:RSDljJcFa0J,~> -JcC<$kl6lZq'Pe]fjse2qCpB/eh3D&jYMJfir=N~> -JcC<$kl6mYq+C?PfsgZ`qHq^\em"TQj^`rBir=N~> -JcC<$li3&Br>k\RqA\lsi?[b7i_92Af-6q`i[06Vir=N~> -JcC<$li3&Yq'Pe]mptB3lnIF0qI8&/U1N=?JcFa0J,~> -JcC<$li3'Xq+C?Pn$h7alsJb]qR5![U:B2nJcFa0J,~> -JcC<$mf/5Ar>k\RqA[d,hBVM`oh3j[X%hM!s%e4Gj8XW~> -JcC<$mf/5Xq'Pe]cnXB2n7;3$eh4"7i%tCFJcFd1J,~> -JcC<$mf/6Wq+C?Pcsbd -JcC<$nc+D@r>k\Rq]!m-cm/N`mn;F[rZSL?i?nCsJcFj3J,~> -JcC<$nc+DWq'Ph^cnWm$rabJ,gb/RXY[ucLqIY -JcC<$nc+EVq+CBQcsb:.rjV@)gfsb\YdiY&qRV7Xjo9i~> -JcC<$o`'S?rZ1bRr>X'.c6MRHi?J.B[S>[,pJ6A?k5Tr~> -JcC<$o`'SVq'Pn`cS<^!l!iU(q^o6fi%t+>JcFm4J,~> -JcC<$o`'TUq+CHScXG++l*fPTqcUA;i+2SnJcFm4J,~> -JcC<$p]#e?!)NS7p`08PcQpd\j@eVZq'!@Ei$S"jJcFs6J,~> -JcC<$p]#eV!+u$RqBaHBc7n -JcC<$p]#fU!4qtuqFT"Ac -JcC<$qu;4CqbR23p`0;QcQrE]pETW2iZm8Pllu-H_+ii7m8&<5lMlA~> -JcC<$qu;4Zqe#XNq^'QCr*oCSrFOtBh.#_'p+=*li%sb4JcG$8J,~> -JcC<$qu;5YqmuSqqao+Br3l?,rOCioh6uZSp0#5Ai+25dJcG$8J,~> -JcC<$rr7OFpJ:c/p`'8QcQr'SohG-)r(m/6fLtcZs%W,/`_GA -JcC<$rr7O]pLa4Jr$BZDnm_9'p+ZDVqCpQ2nh0rRoI\'oi%sY1JcG'9J,~> -JcC<$rr7P\pU^/mr(54Co!\5$p0[a/qHqm`nlu.(oNB2Di+2,aJcG'9J,~> -JcC<$JPYl5rZ1bRd3S`fnk/rtmS3[*peUl6dS'E\qG$T*bY@"Bj\LI-mJh\~> -JcC<$JS+Lcq'Q%dd4tZ5nmVS=mUcAOph'LZdUN&*qIT:Nb[o]gj^s)DmJh\~> -JcC<$J\(Haq+CTWd:*'do!SNWm^W7'pq$H3d^K!VqRH0#bdcSAjgp$CmJh\~> -JcCE'JPY`1rZ1eSjWs.f!#>)u!#=ipo2!Y)!$_,2oHDXWi$RAXJcG3=J,~> -JcCE'JS+@_qBl.ejY?(5!$1Z?!$(?:o4H9W!&!tVoI\L&i%s;'JcG3=J,~> -JcCE'J\(<]qF^]Xj^IJd!''SY!&s8So=E5U!*f0,oNBVPi+1cWJcG3=J,~> -JcCN*JPYW.r>k_SqBXWf!#>)u!#=lqmSD8(nkJa"fM19NgeWM$nGe"~> -JcCN*JS+7\q'Q(eqD$Q5!$1Z?!$(B;mUjmVnn%GFfO`tsgh)-;nGe"~> -JcCN*J\(3Zq+CWXqI.sd!''SY!&s;Tm^giTo!n -JcCW-JPYK*r>k_ScqaWVo1T)umS2qAfh:^,oHE$bi$R)PJcG<@J,~> -JcCW-JS++Xq'Q+f!%-')!$1Z?ruCHM#fOf^7o)F4~> -JcCW-J\('Vq+CZY!(tVL!''SYs#9AUl+5QVmC;dkh72BRfXcY6o)F4~> -JcC`0JPY?&r>k_ScqaWVoLo3!mnN%B!%$>lktUdnj%\GYdnbPpo`'F~> -JcC`0JS*tTq'H.h-77)O!$(W?ruCK=l"0oMg1';XoI]*7i%rhoJcGBBJ,~> -JcC`0J\'pRq+:][8kRIA!&sPYs#9DVl+-k%g:$7UoNC4ai+1 -JcCi3JPY3"r>k_Sd8'`Woh59!n4i1Dr[$>mjA#7iktU(_cVK,lp&BO~> -JcCi3JS*hPp*S1!!$1`ArZ(E=l=U#Mgg]>UoI]<=i%r\kJcGECJ,~> -JcCi3J\'dNp.E`D!''Y[r\s>VlFQt%gpZ:RoNCFgi+10FJcGECJ,~> -JcCr6JPY&sr>kkW!$2%YdSBiXoh59!!DOa.:$b[R-/D5Q:%TUs:#@;@:P&Q?s*t~> -JcCr6JS*\LqBc1ir[-'*!$1`ArYtrM/b3K>B)FC>@c:nj/ad3-1WsB]s7cPD~> -JcCr6J\'XJqFU`\r^tVM!''Y[r\jkf>4T2i])5pk[c+ag=mifWABVQ8s7cPD~> -JcD&9JPXoorZ(tYrb_]VrZ'?^oh5<"r(R/7p.amLq^()lh+dMbo1e-i`_V0cq>Ys~> -JcD&9JS*PHr?hN%!%7amdUr/]ruCrJ!%mnUlt6,LhI>;PoI]ZGi%rAbJcGQGJ,~> -JcD&9J\'LFrC[(6!)*;`d^o+[s#9kc!*]*+m(3($hR;7MoNCdqi+0j=JcGQGJ,~> -JcD/3#pItW2qFpr6mSEX)i(M]soHF$)i$Q<:JcGTHJ,~> -JcD/8#r[-*+!$1cBpLO=VqIKXYmUl8Li*t>MoI]lMi%r5^JcGTHJ,~> -JcD/ -JcD8?JPXooo5=RKrZ'E`!#>6$o1f6.r_<>8o2#*,iChZpoHF6/h^6'5JcGZJJ,~> -JcD8?JS*PHo8EVrr[--,!$1fCo4@qRral$[o4I_OiF:;JoI^)Sh_VuYJcGZJJ,~> -JcD8?J\'LFo?I;.r^t\O!''_]o=4g(rj_o1o=F['iO77GoND4(hdjI4JcGZJJ,~> -JcDABJPXuqm;DqErZ'E`q+L-Mruq4_pJ:K/j%I]mp*&BA\PIeVrr7K~> -JcDABJS*VJm>Lulr[--,q-rc'pau5Pp+bT:cXlf%iA8#VJcG`LJ,~> -JcDABJ\'RHmEPZ(r^t\Oq6o_$pfdF'p0Zjgca`[OiFKL1JcG`LJ,~> -JcDJEJPY&skAL;?ruBNa!#>B(l:q$tr(lr2j@dZjq'"fG[82ARs8RT~> -JcDJEJS*\LkDT?fs!H6-!$1rGl=K`Br+>RUjC6;Dq(:Y`[:Y!is8RT~> -JcDJEJ\'XJkKX$"s%:eP!''kalF?Umr4;N-jL37Aq,uch[CUqhs8RT~> -JcDSHJPY)tibnc:ruBQb!#>E)k"YUpng3Bja%cITk9dE/JcC6~> -JcDSHJS*_Mif!gas!H9.!$1uHk%4<>nhK68a(>0#k;0>SJcC6~> -JcDSHJ\'[Kim%Krs%:hQ!''nbk.(1inmCLea12%Mk@Cg.JcC6~> -JcD\KJPY0!gi!-4ruBQbr(I#3k"Y^so-NNl_G1%RkpEK-KE$H~> -JcD\KJS*eOgl)1[s!H9.r*oXVk%4EAo.fB:_I`a!kqfDQKE$H~> -JcD\KJ\'aMgs,jls%:hQr3lT+k.(:lo3^Xg_RTVKl"$m,KE$H~> -JcDhOr_S/Aj%n6DruM+ZekZ8\rCd&2j\>^uo-NQm^eO,AW)&*HJ,~> -JcDhOrb-jpj(?l#s!Rgnen+n&rF5[Uj^nECo.fE;^h)gfW+L__J,~> -JcDhOrk!`mj1 -JcDnQqG;`=l;,cE!$2%Yf1uA]r_!59qB4ino-NTn_G0>CUJHXEJ,~> -JcDnQqIkFll=SD$!%7amf4G"'raGj]qCL]=o.fH<_I`$hULo8\J,~> -JcDnQqR_ -JcDtSp/$<9nP@;F!$2%YfM;J^r_)u.kY;(&o-NZp_bKDCTML@CJ,~> -JcDtSp1T"hnRfq%!%7amfOb+(raPUQk[j9=l=.YBl8,)FL];l~> -JcDtSp:Gmen[clf!)*;`fX_&BrjMQ&kd^Y#o3^dk_mnuBTXopYJ,~> -JcE%Unkam5peShG!$2%YfM;P`92"q#lV7@(o-N]q`D,VERnnn@J,~> -JcE%Unnr)~> -JcE%Uo"0Iapq"Dg!)*;`fX_,C[^Pj1laZq%o3^gl`OP2DS%=IVJ,~> -JcE.Xm8/@0s%g@H!$2%Yktg_Anf[Err$:k[m7YDmpEoo7pEl2%MuS;~> -JcE.Xm:_&_s(9!'!%7aml"9?pngs9AmP4*<`F\d"s(1p\Q=c'TJ,~> -JcE.XmCRq\s15qh!)*;`l+6;mnlP=jr*],'mC(!DpLO<6pLKT$MuS;~> -JcE4Zktm+1\8D9erZ(Z.!$1&nn0%?tqBYYYmRtSppEo`2q^.M&N;nD~> -JcE4Zl"Gf`\;L>7r[.AO!%6c;n1=3ClnRp;a(>!$qITO[P@fdRJ,~> -JcE4Zl+;\]\BP"Hr^upr!);I_n5o7lqI&o%m^C0GpLO-1qdbo%N;nD~> -JcE:\jA:k4Z>KX_rZ(]/ruL,omND:!pa#GWmn:bspEoT.s!Eb%NrOV~> -JcE:\jCjQcZAS]1r[.DPs!Qi -JcE:\jL^G`ZHWABr^usss%VO`mT91npgE]#n$^?JpLO!-s(%/$NrOV~> -JcE@^i)#\7XDS"YrZ(`0rZ1)p!)2`%r(Zr3nKn-.oh3G%pEoE)N),Q8J,~> -JcE@^i+SBfXG['+r[.GQr[6f=!+bFIr+5XXs"a1#rFPY2b%:<'nR[YkOT0h~> -JcE@^i4G8cXN^` -JcEF`geaA6!%t -JcEF`gh<'e!'@6>!%7amqI][Rpgrl(kq)Tar\F+#s"a@\qdRc\p+u)INFn=QJ,~> -JcEF`gq/rb!+2eO!)*;`qRZW"ppfb$!*JMfojIE!s'YW3qmO_Yp13R#NOk8PJ,~> -JcELbfMJ#4r[t9i!$2%YqbR,1q+Ui/mnE^(p`TS[pE9;Xm3TMApEo-!N),Z;J,~> -JcELbfP$^cr]@3>!%7amqe#aRq.0OQmptZ6qD%n'oeHG$^Ld-ql",fcPQ-.~> -JcELbfXmT`ra2bO!)*;`qmu]"q7$E!n$i9upfIJtpK.#qoj@]+^UX#Kl*u\`PQ-.~> -JcEUednlW3r`]7QY&=7[rZ(i3qAoH-ruCN%r#tq[r#tkYq]tPT!)17(pEnrqN),`=J,~> -JcEUedqG=brcJ*#Y)E<-r[.PTqBu/Os!I5Gkq)]dq_In#rA"@`_I`HtjCO9^Q2c@~> -JcEUee%;3_rkeV@Y0Hu>r_!+"!)EDcqmcZ#n@/ -JcE[gcVUB4q-*_LYAX@\rZ(l4q&TB-!Z`7#r(QfWqB>e[q'#YYr?UbVqb5(*pEnclNDGl?J,~> -JcE[gcY0(cq/lQsYD`E.r[.SUq'Z)O![f -JcE[gcb#s`q83);YKd)?r_!.#r_EAcr4)`#r3uC.qH3\tq,mProjIQ%ra>W4aLLtThmeZWQN)I~> -JcEaib>>*4oih>IYAX@\ruCu5p`9<-ruM(Yr_3>6pe1B*ruh%ZrZM4dlm;C"c;"!ZgeE`'R/_[~> -JcEaib@mecolU0pYD`E.s!I\Vpa?#Os!Rdmrac$XpgWJ=s"X7'qD%q(pLMQ\p+tK8N+SIWJ,~> -JcEaibIa[`otp]8YKd)?s%<7$rD*8brODc"rjVo,ppTs"s&\qsr`Ac#qHs2/pUAGXp12sgN4PDV -J,~> -JcEgka&&j5n65fDY\sI]ruCu2p`9?.rZ1tXs%NA5qFg)s!$LVSo1[S,pEnKdN),oBJ,~> -JcEgka(VPdn9"XkY`&N/s!I\Tpa?&Pr[7[ls()-Zs!deRkq)K_r%\.*o469\pG:E4N+SOYJ,~> -JcEgka1JFanA>03Yg*2@s%<7!rD*8brj_i"s0qr+qR5Zk!*Ai$r*TD1o=*/XpLMmcN4PJXJ,~> -JcEmm_bdR5lrsB@Z>TG2p`9B/r#PeW!)383r(H8tmNq9pfM2)edS5]sS,\!~> -JcEmm_e?8dlu`4gZA\Kgpa?)Qr$VLk!+c$Xs!dkTkq)K_rA":,mUXs]pG:6/NFn[[J,~> -JcEmm_n3.am)&a/ZH`0RrD*8bs1%o"!4Vi)r3kilojI`*ra>0'fXUZdd^Y:JS,\!~> -JcEso^JM=6kZ[p;\8Ln3nK%WLqB,Z/kooLAl:g&/pEn0[N)-#EJ,~> -JcEso^M(#ek]Hbb\;Trhpa6%iq^;Cjq^MRprF5"BoeQP%rA*YKhIZF;c=MrHSc=3~> -JcEso^Upnbked:*\BXWSrD*8b!)E>aqGdF%kudUds'P_4lF5W[pLMRZN4PS[J,~> -JcF'r\PTn6j')C6^2E@4nJqTLp`KN/q'#W0pEB&Qk"Of0p*RpVN)-)GJ,~> -JcF'r\S/Tej)k5]^5MDiq'Z1jq^2@jqC;Los'kdTqdoLQnM96;j(7p?b%6NDTDsE~> -JcF'r\\#Jbj21b%^ -JcF-t[8=V6hcg"3`,=d4nK% -JcF-t[:m -JcF-t[Ca2bhnoA"`6IMTrD*>dr_E>bp/LokpUL$$nmLIdkd^@t`OLo=T`9N~> -JcF4!Yu&A7g04J.b&634o,[iRp`KJYnkK$*o-N -JcF4!Z"V'fg3!7ir$VFkr$VLppaZ7onn%_Mo.o67!&EnPpG9WsN+SgaJ,~> -JcF4!Z+Ircg; -JcF:#X\d)7elr&*c#*M&iCrkar?2%\rD2iUrZUn[iCs4sn4iU']M4>\V#Pr~> -JcF:#X_>dfeo^mQc&2QNiFMm7r$_Lkr@@grrFYJ/r[d[siFMpCn7D;L]Od%6V#Pr~> -JcF:#Xh2Zcf#%Dnc-65ciOASWrD!5krD`a,m^W6uoO-XeraYH-p11hGN4PhbJ,~> -JcF@%WDLi8dTZT%cYib(iCs4k!$:qVr#bt]r_M`PoHjJZk=kb!o1em)\4qrYV>l&~> -JcF@%WG'OgdWGFLc\qfPiFMp8!%Idns!R^p!%Rt[k[jBAr[d5Cq_IbSoeX*hNFo$eJ,~> -JcF@%WOpEdd_briccuJeiOAPVs%WDl!*&s/kd^7mr`J?mqd]6-ojkSBNOktdJ,~> -JcFF'V,5Q8c -JcFF'V.e7gc?0"Hd>J&V.4O7erFc0[q^;Flr[[qZk@O9@!+PjWlX]lJs"j=Zp+s'eN+T!fJ,~> -JcFF'V7Y-dcGKNedEM_k;,Q>^rO_o$!) -JcFO*TMX38a]eZrdr#.nrZM4^kY1bJ!$;"XrD2QMoHjB-r#kN'q^).epeCH/Y>'sOWW.J~> -JcFO*TP2nga`RMDdu+3Qr[\!tk[aI%!%IdnrFY2'oJ6;Qr%.AKq_J(*pgs.TY@WZ)WW.J~> -JcFO*TY&ddahn$ae'.m3r_rhokdU?!qG."#k.(D!pL*m,r)iKup1*m-p11A:N4Q"gJ,~> -JcFU,S5@s9`*3-mfP^UprZM4^m7d(IruLr3rugT$o-O0)r#k],qBbtbqb?c2r[.[eY"apPWW.J~> -JcFU,S7pYh`,tu?fSfZSr[\!tm:>d$s![_Us"*GHo.p)Mr%.PPqD.n'qdo@T!%G'^Nb59jJ,~> -JcFU,S@dOe`5;L\fZj?5r_rhomC2Yus%`F%s&\Kqr*fA.ppU*&p:'X'qmc6.!)fu0Nk24iJ,~> -JcF[.Qr)[9_-6dih/;spruh:^o1\RKpeUi2mS3R)!%.Ogo1T!(qb6l5pEoi5p*TeerD!;5Y>($Q -WrIS~> -JcF[.QtYAh_0#W;h2D#Ss""'to479&ph'IUmUbuFo4%VKqdfRZpG;bZoJ?GWs!b-_Nb5 -JcF[.R(M7e_8?.Xh9G]5s&8noo=+/"pq$E*m^W.&!+Pe4o="R!qmZ**rODK1rODl*YIKV(WrIS~> -JcFa0PYgF:]NY7dibn?qrZM4^pIt!OohYH-nkK!-r[.Ohmn -JcFa0P\B,i]QF*6if!DTr[\!tpLN]*ok+(Pnn%DJmpc2G!+bs^oJ6JYo/$5Ss!b*^O(PHmJ,~> -JcFa0Pe6"f]YaVSim%)6r_rhopUBS&ot($%o!nR*raPe5n$`-r!4VB+!4_W2qRHQ'Y.0P(X8d\~> -JcFg2O&5(:\6AkakAK]qrZM4^r(QEQ!`'s_pJ1c4qB5K+q^).er[$i&rZM.ckU- -JcFg2O(dci\9.^3kDSbTr[\!tr+,,,!bNu7pLa4Pp1 -JcFg2O1XYf\AJ5PkKWG6r_rhor3u"(!kMH7pUU?,qH*C#qdKD3ra>$"r`JSuojma*s&-#0Oh.Un -J,~> -JcFm4Mbrh;ZWd>\lu)&qrZM4^r_*;7n50$Yr?:u4r?;"[qb?r6qBc1gk">^t-0Q]T-h0n2*j/5" -:TXSH~> -JcFm4MeMNjZZQ1.m#1+Tr[\!traZ!Zn7_`3r@IbVp+6)Po.oH;!A$"f1\##V.B[6EAuu#_~> -JcFm4MnADgZbl]Km*4e6r_rhorjMl,n@SV0rE&g+rE/ntqmcN3qI0G4k-b7lmUQ!%n[JQsY.0V* -XT*e~> -JcFs6LJ[P;Y?LoXnS[GrrZM4^!)3A7oMG?Zq]Yi4q]YeYs%WA:qBl/7kY(q%l6cKXm7mU%Y"b'T -XoEn~> -JcFs6LM66jYB9b*nVcLUr[\!t!+c'ZoP"&4q^hVVoIU#RoJ6AUk[FQI1?UI\AcF3^AWgZ-s*t~> -JcFs6LV*,gYJU9Gn]g17r_rho!4Vr,oXjq1qcE[+qcN\rs1%r7qI9E4kdCJ#mUQ''mC<0oY.0Y+ -XoEn~> -JcG!7KM_D=X'5HSp28erruh=_r#kf3p.tK-r_N;4q]Pe_qBku2mS!L)lR)WZkY2$uX\G$UXoEn~> -JcG!7KP:*lX*";%p5@jUs""*ur%%SVp1F+Orat^PoeQ;QmU?/Nr\E^ok[a`CX_!`/XoEn~> -JcG!7KY-uiX2=gBp -JcG'9JPZ>Cq+q#;X'5HSqecC;h-g"#qbR88p.kZ4p`TW5p*'AWr[%CfoM"jLqBbPVr[6f%ruS=; -PY\I^J,~> -JcG'9JS5$sq.BX_X*";%qhkGrh0\oIqe,s\p1=:VpacDWngsMjoORQ&s"XF,lSIX6s!b*^P\.)u -J,~> -JcG'9J\(ooq7?T2X2=gBqoo,Xh8T@lqc*I)!*8g)!*K"t!4ql+rE&u,qI9*+o="[.l!s^'jgb=g -Y.0_-Y5a"~> -JcG*:JPc;AqbI2Sr[+[Lr\"1fr*%rb![(KCqB5`5rZLu3rZ_.]s!%.[ru_:fqBkZ)pe1B.n0S2a -:J]qDruSXDqBV8#YQ'+~> -JcG*:JS>!qqdoh"r\LTsr]C+;r,geB!\82-qCDMXr[dhVr[mpss" -JcG*:J\1lmqmlc9qI#MuYQ'+~> -JcG0 -JcG0 -JcG0 -JcG3=JPc&:s%iPTr[+=Bk@!s_qB,c7q]Pc3qBGk]q]beYr?gc&!@1>1-1rVb-h^?q:&c9Y:$ -JcG3=JS=ajs(;1#r\L6ikBll?qC;PZq^hVVqCVXsq_%XprA*VJ!\e( -JcG3=J\1Wfs18,=r`l/6kJd,/qG[I,qc -JcG9?JPbo6qE4^'U0?nkn5]B/![f09q]Pi5p`f_]p`fSYs!Hu(rZq._od0Ydnk\4=!$9]ch^"=[ -Z2]=~> -JcG9?JS=UfqFUWAU3,aKn8J4T!];S_q^h\XpauLspb)Fps"`hLr\4"#oeQS)nn-il!%HK1h_C7* -Z2]=~> -JcG9?J\1KbqIKP0U;H99nA"m#!bGJ9qc<[,pfRPtpf[Jrs'k6%ra5>.rabe6!+k`4iOAkcf!sUF -Oh.gtJ,~> -JcG<@JPbr7r)EMTr[+@CnR1]`r?2%_qB5f6!$ghSr?:E!rZq1`od9\dmSDh:ruU,neg-ARZ2]=~> -JcG<@JS=Xgr,):%r\L9jnU'V@r@@h!qCMYY!&+'up+H:pl"'cIp+l:ss"s.Uiar-=iFURpO_1lu -J,~> -JcG<@J\1Ncr4`#rl*pY!s'b\4qdfS6s(1W0ijf"diOIHH -Oh.gtJ,~> -JcGBBJPbr7pf.)Pr[+@Cp0d&`ru_;;r$)"\!%7ajq',h`p*0M[rug]&rZq1`od0Ydl;-A5!$:E" -f-L/ -JcGBBJS=Xgphfk!r\L9jp3Yt@s!n(]r%@jt!&OU-q(;V"p+H:pm:?2Mp+l:s!&WtQiFN! -JcGBBJ\1NcpqHT8r`l27p;Q40s&9!0r)ihs!+c(3q,mZ#p0%>rmC3(%rF,P4q.0G6!+kH,iOAkc -lF>GRq-a95OLhatJ,~> -JcGECJPbr7oi1cMr[+CDqI&DbqbR,6r?1t^q]bt`pEBS\r?1T's!7=bod9\dk"ju2ruUi-f-L,; -pe:`'OAE4_J,~> -JcGECJS=XgokjOsr\LO~> -JcGECJ\1NcotL95r`l58qShR2qmu]-rDreuqcNf#p0%>rn@/F)qdKG5p1+57AS(#8ijf"dosiU] -pgNs-!&p"NZi>O~> -JcGKEJPbr7nPo?Ir[+CDra=kgpJ:Z1ruh+^r?D+`q',b\rZLi,s!7=bp*T`2j%nZ/!Zk'1eg1&; -mS/d=Zi>O~> -JcGKEJS=XgnSS+or\L -JcGKEJ\1Ncn\4j1r`l58rl+$7pU^6(s&SqurE/r#oi_8roXFj-q-j;5p14-1j1=6[!`HPO~> -JcGNFJPbu8m8WpEr[+FEr_rkBl;dFOq]be[rZ_._q]bn\r?1o0rZq:cp*TQ-jA4c0!$9uBq'G,s -!#:Sk[/YX~> -JcGNFJS=[hm;;\kr\L?lrbVWhl>Q9+q_%Xsr[mq!q_%atr@IbTr\4.'p+uJQjC[C_!%HJRj^eE3 -OCkm"J,~> -JcGNFJ\1QdmCrF-r`l89rk8A8lG)r%qcNVrr`Ju"oi_5qq7$?1q-iu-ppT7pij\tdhI-.fjgY:L -OLhh!J,~> -JcGTHJPbr7l;[UBr[,rprDh]XqG[G>moAdOr?;4gpEKY^pETV\q'#\ZrCm29q'H%gpa5W+jA5&8 -i$A+EgJ*c*[/YX~> -JcGTHJS=Xgl>?Ahr\MlBrGUP7qJ?3dmr.W+r@S(+pFcLupFlItq(;OrrFGm]q(ht,pbVPOjC[[g -fJ2,lOCkm"J,~> -JcGTHJ\1NclG!+*r`mddrP.4+qRur4n%\;%rE'&(pK7JupKI5mrE0$-ra5J0s'kb6oX -JcGWIJPbu8k#D1>r[-)tpf6BYoi(o9o2Y'Os!%Fhp`oebp*0P]p*'DXrZq7br[7Rimn;_;oM+9i -pa+B`!#:Sk[Jta~> -JcGWIJS=[hk&'rdr\N#Fpi#58oka[_o5Eo+s"=:,pb2Y&p+HCup+?7pr\4+&r\XL.mpb?ioOZZ0 -dq&M!OCkp#J,~> -JcGWIJ\1Qdk.^\&r`mphpqPn,otCE/o=sS%s&f8)pf[W%p0.,lr`K/,pgO/2qdf0(jLXuno3q)q -pgMX]!&p"N[Jta~> -JcG]KJPbu8i`,b:r?g0#oMt*YnPfK5pf6KQr_`V@qBPqbp*95R!?`Z_-2].h-L4=d:]4#H,l&b. -9oi)Ys*t~> -JcG]KJS=[hibeN`rA3)JoP`r8nSJ7[pi#>-rb; -JcG]KJ\1QdikG8"rES!loY9V,n\,!+pqQ"'rk&,5qH -JcG`LJPc#9hGj>6r?g<'n5\gYm8O'1rDi#Vq,.);r$2(bp*98Sq'Gkb!%-i$n5&g*l6Q@%!$q@d -^eFeDOAE=bJ,~> -JcG`LJS=^ihJN*\rA35Nn8IZ8m;2hWrGUk2q.]d`r%Iq&p+Q+kq(he'!&NbHn7LW6raPXW^h!Kc -OCks$J,~> -JcG`LJ\1TehS/hsrES-pnA">,mCiR'rP.O,q7HT0r)ro%p0./mq-j,/!+b6#n@IRerjDf7q-h(K -!&p"N[f:j~> -Jc>`MJPbu8g/Ro2r[-Q,lW*LZkYqX/ -Jc>`MJS=[hg26[Xr\NJSlYl?9k\UDUDf72mB(Tg91]B''1A* -Jc>`MJ\1Qdg:mDor`nBulbE#-ke7.%^VQ.e$>kM:r=o2Y&@KBV6kI(A#qmtZnp9js0 -\%%L1\,Us~> -JcC9toha4=qE4a(jBC_Hr)s(pr*&2]r_ -JcC:7ok;onqFUZBjE0R'r,_pMr,h%;ral*\mq2''rA+F,pG2Cpq^q_$q)&+2r[e+#lsfuLs"r[h -m:*P9!$%&3\Gq'~> -JcC;5ot/ehqIKS1jML)pr5&HDr5.R0rj_u1n%/##rEB8)pK[ApqcWi*q-sA7r`K5'm'cq(s(1.t -mC'L6!&otM\Gq'~> -KE$L!oha(9qE4a(kZ["H!*]:p!*\J`qG$u5oM=dMr[%Uop*KAVr?;.dq'Z+jq]Z"_nk9!/r$UkX -r(Q6G[S;fY\Gq'~> -KE$L9ok;cjqFUZBk]Gj'!-J-M!-I=>qIT[XoOdE'r\FO4p+l:or@S"(q)&%0q^hdunm_VRr&!dq -r++r![UkM3\Gq'~> -KE$M7ot/YdqIKS1kecAp!5eZD!5dj3qRHQ-oXaA#r`]A/p0@8orE9,.q-s;5qcNo$o!\R.r+58( -r3tgs[^_C0\Gq'~> -K`?U"oh`t6qE4a(lrrCKpeBsOp.bQ1q+p0Nr`&nHp*K>U!?ica-iGLp:\c47+S\V0-2Aq_-1Xal -9=@Op'ou/$s*t~> -K`?U:ok;ZgqFUZBlu_6*pgrZ+p1=7Tq.Af(rb_Znp+l7n!A-)%2#TB6B)+%Y/,3TR1&3[#1%Jl; -@^]hJ*04^Bs*t~> -K`?V8ot/PaqIKS1m)%bsppfP%p:1-)q7>b$rkAD=p0@5n!Eik+@f9g;](oM/=ST?)A,0^1@Igni -[^N[G2ikP\s*t~> -LAug$oh`h2qE4a(n64dNohFgQnP0$,r_M]Sq,I>Bq'H"ep*9A\r$qV?q'#e]rCd/:pEou8-0\+c -9=7KS:V-RV~> -LAug-q/-*hq(hq*p+Q4ur&FUdq(2RsrF5d]pG;n]1$N62 -@^Td-B"J"m~> -LAuh:ot/D]qIKS1nA=/!osjD'n[SU$rjq:)q7ci7p0@;pq-s84r4;],!*K3.s'te7!OlM%\)t-, -\;n/3s*t~> -R/[.\nc+GBs%MoYa&Mmtr[.2>s!6]+n4iIQm7mU(!Dap);=mif.JkFh-MSh_-i>Ig:\c17-2/eJ -9]f!U9`Poh:V-RV~> -R/[.tnc+GZs((V4a)1ZEr\O+es"WVRn7D0-m:H;K!G3qKC%Q9B2Z#9-1\`X#2#K?-B)+"Y1&!Nc -AEI@/AH3^2B"J"m~> -R/[/rnc+HXs0qL0a1hC\r`o$2s'4[#n@8&'mC<0u!P2G!]C`i;?M7Y!>5Me,AG<\2=TDt+A`U>_ -[^EUF2ikP]s*t~> -RK*.Yp]$(HqFpQY_c6Ipr[.8@r?UW-lqR1QktM.RrZ^f.mo9<9r?_7dq]l%er$q;6q]bh`iCrB, -ZqZQV]DmB~> -RK*.qp]$(`qIK84_eo6Ar\O1grA!PTlt,m-l"'j,r[mSQmqr(_rA+1)q_.n)r&F:[q^qV#iFM([ -Zt580]DmB~> -RK*/op]$)^qR?.0_nPtXr`o*4rESU%m'uc'l*p`)r`S^%n%Sg.oj%AtrF5\8o=Fg%q.8Nok-hY, -Nk2k&J,~> -RfE(Ur;VUMp.Y9Y^f:.mr?h8BqBYK/kY;(V!)WVnl:q.OrZ^r2lW!m5s!@Cdr?M:hr$q/2rZ_7f -h+[!)ZqUN8N_d=fJ,~> -RfE(mr;VUep13u4^hrp>rA41iqD%DVk[jd2!,2=Jl=Kj)r[m_UlYZY[s"a=)r@e.,r&F.Wr[n%) -h.5\XZt04WNb5s(J,~> -RfE)kr;VVcp:'k0^qTYUrET*6qHWI'kd^Z,!5&3DlF?`&r`Sj)lb -S,`"QM,g.^X%ZqZQV -]`3K~> -S,`"iM.lKkrFii\q+:QAq/lBdno*]&qIo2+pLaCVpM&i.r+,OXs"=@/r&F"S!\H5cg19>TZt580 -]`3K~> -S,`#gM7`AgrO]_Vq.0J0q82o3o"X@tqRc('pU^?,pUfY'r3uE*s&oD3r+,-)!a0)Gg:-4Q[().- -]`3K~> -ScA"MN_o1<\Q&Aer[.SIrClu3p/U9Mp/(3Po2#?0r))WVpe:E/r@7&-!@8oB9]f!T9`Pog:VHdY~> -ScA"eNbIll\S_.6r\OLprFG[Xp2B,*p1Wo+o4ItRr+Y>2pgj+TrAa%R!AZ4ZAEI@.AH3^1B"e4p~> -ScA#cNk=bh\\@lMr`oE=rO;Q,p:oe#p:Ke'o=Fp(r4D.+pp^!&rFG0(!FT'`\)t-+\,\IJ]"S,o~> -T)[qIP>LR=\Q&Dfr[%VKq+UQ/qc2fRnkesQmSEg+s&%uZoM#'-s!m8/r[%Ldge?j'ZV?ET^Ai]~> -T)[qaPA'8m\S_17r\FOrq.07TqetY/nn@Z,mUlGMs(U\6oORbRs#B7Tr\FF'ggoPVZXo,.^Ai]~> -T)[r_PIp.i\\@oNr`fH?q7$-(qnM=(o"4P(m^iC#s1@L/oXFX$s((B*ra>\-gpcFSZac"+^Ai]~> -TE!kER8E$>\lAMgoh>-+rDik@lV6bC!DQGg:$POR,5rhS;ue'69`HC=.f(Il.dBh/-3#Ip,5`VA -9]f!S9`Pog:VQjZ~> -TE!k]R:t_n\o%:8ojmhPrGV]flXfHs!G-'DAa3n,/cIHkD#c-[AH+@a3;PE239k,T1B0?60)R9X -AEI@-AH3^1B"n:q~> -TE!l[RChUj]"\#Oosa^$rP/A8laZ>o!P#f>\E^[)=oMUl^&V:.\,Tk:@/=+.AEpc*@/jd;>PqX] -\)t-*\,\IJ]"\2p~> -T`fk"l"Sr?Lc1![]$4q+Uf7!%\%Os!n!p!$UW'rZqOo -rDW\>r#t&nk"E!SNDI=hJ,~> -T` -T`"5So!eC!s1eJ7n@7hsqn)p=k.:T)rEA[(!aSo1q7$B4!+u5Hs'Yh2!*STura5_: -rOr27r)r$gk-hS*NOln)J,~> -U&W_=UJTiA]2\2lq'>r:r#t`-l;77Qkth1Rr?Ll4!%Imoruh86s!7Xqq,m_J-N,+V:];[?.f!$B -,5i\G9]f!R9`Pof:Vd!\~> -U&W_UUM/Oq]5?tHq(_kar%7SQl=fs.l"9g,r@meZ!&jg4s"++Zs"OL6q/ZQr1]8ooB)XRc3;ICi -0)[?^AEI@,AH3^0B#+Fs~> -U&W`SUV#Em]>!^Bq-q8!)>>l@go])H+;AGNq< ->Q%^c\)t-)\,\II]"n>r~> -UAr\:Vbl,A^f9PlrZq;:r#to2l;7(LmSERSr?M#8!%IkHr?_Uh,pb)7=TBfH+nJM/-3#Ie;uS!> -+m2P]9 -UAr\RVeFgq^hr=Hr\=4ar%7bVl=fd)mUl3-r@mq^!&jdorA+O,0ek]]Er\#o/b -UAr]PVn:]m^qT'Br`o93r)rm+lFZZ#m^i/)rEAp/!+l,DrETM-@:r~> -U]8V6XAIMB`)Pkmp/1`2rCuTSmnrXOo25H2qG[GDpf7>@!%[\E!%Rpor#tf1rZqOomo030!$V4a -m7cV7Z:t<6NDICjJ,~> -U]8VNXD$3r`,4XIp1sRXrFP;.mq_K+o4e.WqJ?3jpi$0g!'0[m!'1!6r%7YTr\=I5mqhtU!%e"# -m:> -U]8WLXLm)n`4kBCp:U<+rOD1*n%A5&o=Os)qRur@pqQi7!+tl>!+u17r)rd*ra5_:n%J^)!*T2* -mC22cZFBlnNOlt+J,~> -V#SP2Yu&nCa].CrnPT3-r_EM5!$^qX -p.XUAYt^0Q_Z,,~> -V#SPJZ"VTsa_g0NnSA%Sral-_n7_*&pL`l.r%S.f!&jIfs"jCjs#L'urA4@'qe#g_s#B=\!&".& -pFl5Nk$kT,N+Ts,J,~> -V#SQHZ+JJoahHoHn\"d&rji)5n@RtupU]h*r*'-7!+kf;s'>B8s(;8FrET8'qmuc7s((H/!*eu" -p:'1mZ+,b(_Z,,~> -V>nJ.[ntCEbZ*^umSNs-qG7,8oMF^JqbQNTr?Lo6!%Igmr)s%LqH*YC!EpiK+ob@;-3#IZ;ZJ*, -,QCI"9 -V>nJF[qO)ub\cKQmV;eSqI]a[oP!E'qe#/.r@mh]!&sg3r,_luqJlKj!H^-r/cTG^1B0>uD"04R -0E5SF@^0JE*0"RIs*t~> -V>nKD\%BtqbeE5Km^rO&qRZ]1oXj;!qmu+*rEAg-!+l(6r5&DFqS3#7!Q&+?=oZ)4@/jd%]`;0u -?N7Ls[^!=B2iYDcs*t~> -VZ4D*]MQdFd8]7%lr!IPs!%88k#).+r_M]SrE&_nr[@hEq^)AI.Ot0oqc`tBrZqOokYqL,js0jL -ktA6TN).@kJ,~> -VZ4DB]P,K!d;A#Vltc<,s"=+\k%juRrat>-rGhRMr\jgmq_J:p3B?hRqfMfhr\=I5k\TB8s"F@( -l!gl.N+U!-J,~> -VZ4E@]Xu@rdD"bPm(E&'s'#61k.L_'rjq:)o=t99!5/27!l8J\pV6UBr`T5.!+b<,s&e;fl*dh+ -N4Qq,J,~> -VuO>&_,/0Gel:a)m8pK@YB,q:D.;u\-C:#@2n,4eI3 -9`Pof:W*3_~> -VuO>>_.^l"ensMZm;)6(!&"4`k%joP!+tG/pMp+Lr\sajr%n(epN-Kh1,q8WD#Z3hA`tiC0DmHo -@^'DD*0"RJs*t~> -VuO?<_7Rasf"U7TmC_u#!*]?5k.LY%!4qC+o"Y08r4Mr5pV6I>"'K#BAE^`,=T>nb?LtV0\,\II -]#4Pu~> -W;j8"`_aQHg/J%qk=k5>m8!g.:a74M;>_gA;!LpL-3,@g=7fYr.d'b1,kOgg,Q&ba -9<_+j'oYr,s*t~> -W;j8:`b<8#g2-g@k@Eppm:HGQBJ2%(C]$$hB^0='E<'qV2u%1f1B93,EV+>Q39P,X0_ArI0^^Ul -0)]7`AH3^/B#O_"~> -W;j98`k0-tg:dPikI9fjmCEC']3JV+]_l7&]CN`9Ab`q;>l\.+^\#ACAEUZ+=S0,W?N%=:\,\IH -]#=W!~> -WW01sb>>rIh,OD#rup]%m8<.Er_WS=mns!1qbZZW!%%;>s!n$toMPN4r`Abrs""*pku7R,lV67] -Y>#!3N).FmJ,~> -WW026b@nY$h/30Is"*JHm;)!!rb29amq_hXqe5A2!&F4es#C$;oP=@[rc.UQs#C$5l"p>QlXfrI -jY*fK!$$r0`W(G~> -WW034bIbNuh7inps&nZsmC__qrk&/7n%AR-kIU_!otUH:oXt*,rkJ-Cs().8l+R'ulaYhYYIFQk -N4R".J,~> -WrK+od87GKhc0M"!$Uc(m8<1Fq,%)9o25E5pJC!*B.HqHEhJq]tQ- -s!6Z"rClH#s!4jD!#:Gg`W(G~> -WrK,2d:g.&hei9H!%dPKm;)$"q.Td]o5"7\pLs#0rA+4hr]'mnp1jXbrG2?e!-.uoqK2Zqq_@JS -s"NMFrFG.Gs"Uci!$$r0`W(G~> -WrK-0dC[$"hnK"o!*Sa!mC_brq7HZ3o=Y!1jh(IrqS3#@rk8$ -X8f%kekihLi`,_"!$Ur-m8<1FohbW4pJLl:nkegQr?_GEr[RbCqG@5?q,.,;r$MAGr@@hHq]t]1 -s!6Z"nkAX"n0GbC!#:Gg`rCP~> -X8f&.enDO'ibeKH!%d_Pm;)$"ok==XpM9^ann@N,rA+@lr]'ajqJ-'fq.osbr%n:nrAaaoq_@VW -s"NMFnmq>Fn1h[h!$$r0`rCP~> -X8f',f"8E#ikG4o!*Sp&mC_brot13.pUpH6iOf%nrkJGDqRua -XT+tggJG4Mj](q"!$V&0!$gr0lVI%IrZh> -XT+u*gM!p(j_a]H!%dhS!&*eVlXo[#r\+1brA -XT,!(gUjf$jhCFo!*T$)!*ep)lalVur`oB6rF4m#!+br -XoFqdhb^LMkZ%1#ruq23r?Lr3lVHkDrZh@hktq=U!%7G:!%%58!%@J:s!RXis"!Y?q]u#:s!6W! -j%RfFhG!tbN).LoJ,~> -XoFr'he93(k\]rIs"*tVr@deYlXoKsr\+4,l"L$0!&X@_!&F.^!&jIbs"sR.s#BRfq_@q`s"NJE -j(-LkhIQ[,N+U-1J,~> -XoFs%hn-)$ke?[ps&o0,rEJp,lalGpr`oD2jgtM'ot(3/o=Y-7oXt35qHa,3o"Y'-qRuo2j1+-Y -csY_?!&okJa8^Y~> -Y5ak`jA;pOl;[a6oHjM\r(Qr3peg?QkYV7+k"l%*o2,Q:,9uq]r[7akp/(f;r_ND!%7Rf -r_rhBj%\Q-!?WW),je;+'oPl/s*t~> -Y5al#jCkW*l>?M]oJ6Ftr+,XWphT2-k\0rPk%F`Oo4\7_0JN6;r\aa0p1jXbrau$`!''Le!&XL+ -rbVTgj(77]!%Pf>m:?8;Me:'1J,~> -Y5am!jL_M&lG!7,oNhK"r3uN,pq5q(ke$h'k.:V(o=G'6>?gj8raZ"4p:LB7rjqu4!+bW7!+>V. -rk8>6j1+-Y!E*@C@-tH(2iG8gs*t~> -YQ'e\ktn=a;#i!B;#V^;;uS3D:]D[:;?&$? -:];^@-1k4;-iR!;,iVPZ9E?4Yn0Ppsr(Zf0QVYa'J,~> -YQ'etl"I#+m;;b^o.gD!A7TJ,~> -YQ'frl+Pl)5>OT/e]Dl7=@ck<&@fK[+])Z@>]Dc%4^&DR@])Pt2]`2I; -])H"8?M)#4@/de6?2n$d\E(+";H*'n_I3;OoO4)tao?k~> -YlB_XmnfcQnPoHor%.Idr(m)4rDEG9oM=^Js!?`&r[.Ofqb[>@pe^r7r[@S -YlB_pmqAJ,nSS5Nr&XI*r+>^WrG29_oOd?$s"`YKr\OI*qe6$eph9X\r\jRas"FCfs"sF]!&OL- -q/66iphB[]!&3DDj(/0@N%q>Eao?k~> -YlB`nn%5@(n\4t@r*o;(r4;Z-rOi#3oXa;!s'Xp"raPe0qmui -Z2]YTpJCf]UJ(Y1o2PQnq^hLgpeUZ0!)`M8q+p3N!%$]&r[.Xsr[dsl!Dt6::]VdhoM5H7oM>E^ -q^)2@!%7D9!$pPuj%KIq+jVIj,gRdK:Wi]f~> -Z2]YlpLsM7ULX?To54>Mq`=L-ph':S!,M?^q.Ai(!&EVKr\OR:r]9s1!GOC_BE9eFoO\([oOe&8 -q_J+g!&X=^!& -Z2]ZjpUgC4UUL5&o=k(?qdT>+pq$6)!5/)2q7>e%!+=m"raPn?raPe0!P;k6])c,HoXY$3oXb"5 -qd069!+>H2!+"Toj0o%e;pSn.@*`8n]#t&'~> -ZN#SPr)!2^VbI+5oi1Tkq^h[loM><.s&B%GpEKQ3ktqC+j\Yn&!%n+Us"+-o!$_&3![^VcohPT7 -+ne_,;#;O=-hC18,l8eL:#ntL+!Dg%9Z\UJ,ePGJ:Wrcg~> -ZN#Shr+Pn8Ve#fXokjAJq`=[2oOdqQs).lnpFcDVl"L)Pj_4TK!'L1*s#U-4!&!nW!]+C>ok"4[ -/bWfOB_sOb2"PD]0`*KdA`RA'.P*.FA:ZK)1!O=Rs*t~> -ZN#Tfr4Dd5Vml\*otL+j/]Y[fTH+;tmcC@I7I8@ElC,s*t~> -Zi>SNs%W,YXA&U9p/LQhr%%gon5'$.qc*VCocia!s!?c'r?_LrpL!qOr?D&6!%.S;q+h2@1Ga>= -4!FGroMGQ6!$h)4s!7VSpEK'#j%TIrk=iuDr?S -Zi>Sfs(1h3XCV;\p20>Gr&Og5n7MYQqelHjpFcJ!ltHDSj_4TK!'L"%s#U*2q.KaaqdoS8"u8M3 -Q:OOuqI]A5s"XR-o4e4Ys+'hrmq(Nis"*>DJMHLGi+C14J,~> -Zi>Tds1%^0XLJ1.p:g(9r*fY3n@JU'qnE,;oipdps'Xs#rF,b>pVm/ArEK*0!+Yo9q76c=C22+, -AQel@oXk-0!*o--s',NEpK[0sj1#%ekI8QArEuR4i4@,3J,~> -[/Y\Os%VuUYY>!;n5'*Zr?M)7s!7>Kp`fE+j%KLr+X,H2eKsf6UF#J,bl<1~> -[/Y\gs(1\/Y[m\_phfADs#TOZpM^'fq_%n_r\*bS!&EYLrA+F9o6UBsr\"._!&O@\!%n1^s"XR@ -r0I@R!%n1^n7Vf5r@dq[s"O1tpb)8Oj(&3@/1fC`eM6YOUG;=Obl<1~> -[/Y]es1%R,YdaR1pqH+6s'kB,pV6`7qcj*4r`em(!+=p#rF,b>o>U];r`f?5!+Yc5!*]B5s'YnB -r6YIT!*T<4n@J\1rET-0s',6=pg!O&j0o(f;cD;WeRA&YULE`)bl<1~> -[JtePs%VfP[7pK@q,HZelqd4Qs!@^iqGI59ohbZ7k"u"'s"+4"oj7\Nra5UTruqFinkB-21\c:q -3rgMr:]Mg>:&Q=7;#hm2@fRhL,5k6s9EH:Z+i[4;-/^,J--ZC>~> -[Jtehs(1M*[:K1cq/,GDlt5j-s"aX-qJ#p^ok=@\k%O]Ls#^9>om-U#rd+N(s"4:-nmqhW6MQlG -8,t=6BE0dbAc4:[B`KjVJ,hD"0)]ABA-+53/B25^1#Ojc1!KZJ~> -[Jtffs1%C'[C?'5q7c16m(2f's'>\0qRc`2ot163k.CS$s(;=>ou-oDrl+hEs'#J5o!e^/CA?0T -AH6!+])Z18\G]\1])Z()a8fTA>lD1n[fTH+;oZ<.@GkUm@Eeb%~> -[f:nQs%VWK]1i&Dqc)iflqd+Np/1f5qG@2c6g(Jbl<1~> -[f:nis(1>%]4CagqebVElt5a*p1aLZqIomak%F`Mr\OR:r,r&uqK`-(q0N$$!BVn+AHFSP6M-TC -8cLI -[f:ogs1%4"]=7W9qnD@7m(2]$p:L<.qRcc8k.:V%raPn?r5/MBqS`GIq8N>A!Ff7*\,p&QC@omP -BE)62])Z13\G]Y4>O]]6@JX@->lIsi[fKB*[C>g;c=4=mbl<1~> -\,V"Rs%VKG^J+GGr)Drgm8*1NnkoB1r_NS@k"u"'r[mnL!%IGFr@J!mqFq)=1, -\,V"js(12!^L[-jr,(_Fm:Pg*nnJ(Vrb)9ek%O`L!&OU:piZWqnTt-q!%n%Y">N2.N/[Irr1*^W -!&+4_nmqeSn:1L"r-.lkpFYfD!\,`PWFWUsbV@?AJ,~> -\,V#hs1%'s^UO#n\tH9!*]6/"CPtZc->t/r6bIU -!*oE5o!e[)nAkT:pKR'p!`DrqWOKKKb[J`qJ,~> -\Gq+Ss%VlH&H,PNDT9EH:Z+fS-8*iULC--ZC>~> -\Gq+ks(1"q`+8Wnrb^qHlt5^)mV2\S!&E_Nr%e:7oQC3moQpIgr]C-3ral*qqMb9:pRM4Ss"=@, -nRVemQ9%)!J,hY&GQ'Dq0D@'kA-+53/?*.[.B,,[1!KZJ~> -\Gq,is1$mn`4,M@rk@[:m(2Z#m^rL'!+>!%r*fV -\c74Ts%V0>a@u=Ns&A8jlqd(Mlqmd0,Nqu&-N>Rr=nGo#.pQ=.pgQ?5F,PND[9E?4YW([16Q6u,Jbl<1~> -\c74ls(0kmaCP#qs)%%Ilt5^)ltHJU0Bd*J1B0?7F7aVX3d5d+pj2^fr]:-461p3:QL^RG8cM9O -AH56sQN'#BJ,hY#G5sPn0D@'rA-"/2W+5lYQ8A%cbl<1~> -\c75js1$ajaLCnCs1[d;m(2Z#m(3:+>j/fu@K0m=_=YVIB#MVqpr3$MraGn3CA#jOd.5T_BCiV5 -AbsdR@f".G@I_89?M%Ot>Ptr2;H'O'!)`>tUgik&bl<1~> -])R=Us%V!9btRgRl!FE7m8*1Nlqmd.-2f(U;#Ma?/,`TN-i$sH/,NTO.K*WFB)jOTE;qogJH7l^ -rAXd#peC@+s""+#nQc)CmN_NO!ZrR/WD!:5L*k+DJ,~> -])R=ms(0\hc"-Mul$<=^m:Pg*ltHJR1ArlnB`0^c3W4"!2#2:p3r=.$2Z7snK)e")N;lBpgs&_rAsIms#09uq(2Xt/1dc2!%EXYbQ!(~> -])R>ks1$Rec+!CGl,B)BOH@/\(9aT,fAc2VDHd/e"0 -rFZ+7ppfqBs'l":n\kH6mToWm!`DrqWODk'i+&ZobQ!(~> -]DmFVs%Ug4dS0?Wr_Vofr?Cc-moA^OpET6*r?hLopfRSEr`]@Qp0@VHo4J.ZBeV78E;q`pE;_ci -Jbjio3;cM?H2oMd>lQ8L;!fJ/+sA,.9E62i-HlF>~> -]DmFns(0McdU`&%rb1VDr@[VPmr.Q,pFl)NrA+@4pi?ElrcS9&p36Npo7I-0Kg%]pN;l6FN;Z6> -SGJ]Q7f6ieQ2iu9GQ0StB^IGS/M&HOA,n-71X#fK~> -]DmGls1$C`d^SpLrk%L8rEJg'n%\5$s&o/&o"4j/rakl@!+5`C!+trCs'YH?")#beAG=@KB`QEV -B(+.GBDf:/ci@ME`;a0?]Brl*>>nJ![fB:MA%H-ms*t~> -]`3OWs%U[0ekGc[qG?Ter[7UfpJ:3Wlr!d-rEK(Bp/(]7s"!\Do3D<)s"*nW!&4=bs"O=h!&XRl -rAaY)p3us"!$UiX!&jgtoN_DFo2,!&run=7!$I"CbQ!(~> -]`3Oos(0A_en"J)qIo;Cr\XO*pL`i3ltQJQrH@uip1XC[s#K[lo6:4`s#Tn,!'gC8s$$==!(6XB -rC6XRp6tqL!&!c,!(?gIoQU -]`3Pms1$7\f!k?PqRc17ra5S-pU]e-m(<:#rPSG9p:L93s(1fL[Ls'k`E!,)5Ps()#O!,;>T -rFYNGrFZ+7nB_/Hrm(/Ss'G -^&NXXs%UL+gJ%;`p/(9dq^;:cqG7):nl>$Rs!.>Dq'5c5r?hOpo3))C!%@PGqI9IUr+?!`pM'X] -!cTtQpMg'js+0Zgs#'@n!&jUnqHX%LlVRC%!$S:7!?NA<-HlF>~> -^&NXps(02ZgLU".p1WuBq_\4'qI]^^no*l/s"F1kq(MVYrA+C5o5jpk!&aIoqL8H*r.=u6pP&W2 -!fTK6pPf&?s.&SIs$ZFD!(?UCqKMrtlY-)I!%b'Z!@]OR1X#fK~> -^&NYns1$(WgUHlUp:Kk6qd98*qRZZ4o"XP's&o08q-1H;!+5H -^&NXX!):='hb4"lpE9$'oO%A@r$))8q^(r9lr!d-oNV/:!)WV@r[S"N-MCUB.fEKL./R9B@fIt[ -B/&pknSnLf!&sIt!':+(rAF@r!&jCh![oKHjA>k$VbI+5ngB\LbQ!(~> -^&NXp!+j#Vhdc^9pFGfJoQp9hr%@q[q_Ik`ltQJQoQL'a!,2_UjIfDG0 -K0Ss]nVmK;!(HII!(m0TrC$FH!(?C=r\ihNs"C9\s"!XtPr$V`J,~> -^&NYn!4]nShmWS_pJg^qoYpT4r*0-2qd0!2m(<:#oY^N1!5&2tUgrt)bQ!(~> -^AiaY!):."j@oRqrCm,3mSNXboI'S]!)N>8qGmM>nPT91n6>f8r[.Upr)WkFqcWqKpfRPEs'5,( -!%mhWr\3k]q`"7upiu`prH\H%nT+^e.H4/(+fJ'9*ZuW`-`JH8s*t~> -^Aiaq!+iiQjCJ9>rFGgVmV)?@oJHM!!+ts\qJZ?dnS.tUn94^_r\FI5r,D]nqfDcspi?Bls*+$_ -!'Bh,s#pEInVm?6pR:lBrC$UM!(?@ -^Aibo!4]_NjL>.drO;](m^r54oO%Q$!4qo2qS3#7n[nd'nAG0/raGe;r4`5 -^]/jZ!)9srktM+!q+Lc2l;7=anL+>\oHjH9q]kc2rZgc6s!%Ljr[[_E"=@8S.Ot'ms!mYM!A7j\ -/bj&[0`GAX0_TGc2#V1d2uA'pGlKW$2Xtkg1Z;0.9`H6Y-bLeKs*t~> -^]/jr!+iZLl"'fCq.'IUl=g$?nML7uoJ6A`q_.VVr\*V]s"=@-r]0^m">k1@3&pPNs#BY"!Bbi1 -48=O055oj-5PC$96iDf;7JiPEQ2X2AO8hN)BW#D@/'Bf_1Wo`J~> -^]/kp!4]PIl*p[iq6p?'lFZo3nR)<#oNqL2qcrg+r`SU*s',P4raki="CAp\A7Y>Js'bQ?!G9CO -@ee+GAcBXJAG47JB`6-OB)C!Hci7VXAb"(GADt.u\,T=KA@uEps*t~> -^]/jZZqKd[ru^l,l;7Fdr[m[g!&*mh!ECWF,Pb49,O/J7-n?h> -^]/jrZsrE5s!dSNl=g-Br]B[-!'Tm.!H0pm0DT>]0C!]^2E -^]/kp['oA2s&&EtlF[#6raYM+!+bY,!P`(=>ku#2=mEm+?=d^>^Aqg:^]7mD_>ds=a8]cMa8KKF -aoH#K`;X:Jk$"AC"c.s*t~> -_#Js[Y=nF[ru^`(l;.Lg>5TuF-iG^j-2f=h,Q1L=,O/M5-ibdi=8s]L-M:L:=oTuL@K8%U@f7nY -0_J`N0EH,Lo4n7`!&X4j!&smrr\O:i!&NP0VG."4!$S3ei?l_IJ,~> -_#JssY@@'5s!dGJl=^3EFSn5n2#TT01As0-0E#Va0C!`\2?5c0EW7rt1\GeaF8n5tIK2M*If2A. -54s4#566^1o8!<6!(6:A!(QsHr^$:>!(#OVVI]]W!%b!)iA8XVJ,~> -_#JtqYI=#2s&&9plFR)9_Ydp:?i=O-?N"=.>lD;6=mEp)?3"R/^].dD?M2,0_>dsA`rK`Ha8KKI -AbF"@A-'qgo?7)En]q2KrltFSpW`bLi4Gkqs%rboVI\Xqb5Zt~> -_>f'\X%W.[ru^T$m83^ar%7[kq(MLhqC;1`llZSI -CA]p^GQ9\uE;_TkEr\#S:]MZ:9EQ=Y+X->'UaPd]b5Zt~> -_>f'tX((d5s!d;Fm:cE>r&a[1q*"L.qD\+$l?<.b!&O=gr\sgns"jLloQ1$ls#fq&!BWL8GlU(t -L\sO5PlO;LN;Z'@NrVK(BE0W^A-482/1g9KUc%d#b5Zt~> -_>f(rX1%`2s&&-lmCW;5r+#M/q.08,qI02)r`SO(rEf34raYrAs'GQ;oYLQ9s(:oD!FfX1_uO?: -bPYuGcN%YSc2D8Qc2_D5])Yj/[f]E';Gr]hoOH&DJ,~> -_>f*]VG$e[ru^Dtnkf*br%7dnp+Q:hpF?"al -_>f*uVIKF5s!d,Ann@f?r&ad4p-&:.pG_q%l?<.brc.roqJlNoq/--frc7OMnpUU$2Xu%m3W;/5 -LB'UGN;Z'"BVo>?.fh1Qh);;kao?k~> -_>f+sVRHB2s&%sgo"4\6r+#V2p14&,pL3u)s&nX)ra,T=s'GQ:s'tl=s'G]?n\Oe:!G#U._uO9G -A+[qEBDosLB)L!TB_9RKAcQB!];E(;;?'OLABFk%~> -_Z,3^UIt_19M?NMj%T/Co32&Ds!R@js!@:erZg]4r[@VEs!IbIs!dS>!%.8>!%InM!%R\O!\IAN -r[@GF!%\"Lr\!t\pLX4Y!&s@chb^1D" -_Z,4!ULF?QA7R, -_Z,4tUUC;!\$o43j1"`oo>:E8s'P>-s'G>+s',P,lGNU-qS3&:s1eSDoY(<3o"b<9rkSSCp;[5F -^:sSSou$i>rP/>ApriERqdohAn'0b%VRHS%;>sIKABFk%~> -_uGHcr_!5cU.YS/9HPA89E611;"%Td/H@^!0*!s&.KCm`>Q-&D=8s`A;unCHr[.&9!%RkTs"4+I -r[@VK!%[hGr\"+`nn%hXmr/7_r?LB"!$S45ru_@d+sONFcmQs8J,~> -_uGI&raGk=U1+3OA18tdA,n+TB^^'A3rh\=4TIqB2ZP`%G5aDmEW7uiD#lM2r\X%a!''k)s#^*p -r\jUu!'0gor]L+6nppa-mu.64r@d5F!%k'Ys!n.%/M4Ijco&rFJ,~> -_uGJ$rjDg:U:(.t[o$#][fB;$]C3W8@fU03A,p65@/s^%_u=!8^].g9^&_]Wra,$/!+GcFs()#@ -ra>T@!+P` -_uGKdSkK)(!DFTo9`H42;!V9b/c@U"0E!j'.fCdb>Q-&D -_uGL'SmhdK-Y+[bAH+.UB^9a?48hS>4oIhC2uPW'G5aDmE<.jMmVVq[qf;`rolC6qIXX@?pMKmf -rcnK(oPsjm!'p%(!Bbo35P:!762*%uBE0W\A-4>5/1pT_Xu>;tao?k~> -_uGM%T!nYm!Okqb\,T>%]Bd<6A,U'4AGp-6@JsU'_u=!8^].U-^A_I9_#S!<_$"2ea(/`T^A_O> -_uO9=^]%^CBCO"DABFg^p!*JIpK[$r!*,l)!)`_npR[IWao?k~> -`;bQdT1f/(!$C/nrZD&7l!"%:r&4='qD\4"q'bo?r[@G@s!RhGs!7D>!%@S@r[.):!%mb@s!Rjt -n5oT?o47nSqdoa]qJcEi!+5[Pocs!'V+_"5+<_mPjYa1hp*[r]J,~> -`;bR'T4.mL-RUA%AH"(VB^'X;SGYeM4o.VC2u,?(G5aDmEW7ipD>u?dD#cHfD>lB\Er\;mD>uKo -3Uh%f3qIk%4Ss[.5Pg?<5lasa2"`NlBVf5B.4Zr#/`f=%2YK#Ls*t~> -`;bS%T=4_m!)_^ar_`U*l,*DHr+Ge8qI]P3q-`m5ra,94s'>Z=s'>H7!+P]:ra,'0!+bZ7s'Ph9 -nA"s4o>puCqoShKqT]"N!)WW$oj%%!V7-J$;>sJTStfi;s*t~> -`W(ZeT1f/(i(X(krD;]]qLSQ&q_.\&r$qCgq-3eGs!IMB!%7VBs!7SC!%@D;s!I;>!@];`<<"98 -=8Z(u/F-mH1&l4l1B:e[FSdHY,PY*29`H49+X5;k[OB5sJ,~> -`W([(T4.jK-R[h_r@7bZk]HZkr'^EFpI5!=p,2YhrAO[3pi-9iqJH6erbVWmnnnC`noFgj1%9&[ -1@fG_DuVlaJGqe5N;c*C -`W(\&T=4_mi4&Y^rO_:1qU,4RqdfG5r*f;,q8<)9pqHf7qS)u8rk8AAo"P-1o"b?9>k#K-?1#E, -A*_89AcL$SAH5R)cMLuA>kkq,\,T>*;UM)EAB=e$~> -`W(]fSkK&'iCs1ls%WMikZ[\2r&4O-oJce"o-jQAr@%\Gr?hGD!%7J>!$q&5"".%p-MCUB.K3-C -,kY+<-;7,3-21L8 -`W(^)Smq[HiFMm:s(24Dk]HNgr'^NIoL8d=o/6JirAO[orA4@k!&O=c!&e_DuMfaJ,hh55lasYOo%!.0E5\c/Yut\.4Zr#/\XQn2TYoK~> -`W(_'T!nVliOAb_s1&*@ked&@r+H">oOe,3o3hO7rEfN=rEfE:!+#<4!+#*."(AoM?M;58@fEt= ->kPf4?amNC?1#E+A*h>9AH5R"cMLuA>lVD9 -`rCcfT1f/(iCs1lqG$9ZnU^Ws!)E,E!''?oqc3SA!EUZA-MpL8 --2Ud7 -`rCd)T4.jK-R[k`r@7YTlZDZerBpWco0iXKnMU8'qeuEh!HBpgD#uII1$riX2#;:n2u7.d1&5Yd -1](_]1Ac(^DuViu4R[h#5lasSOo%!10;N.Y.fh1QX$YQ!aT$b~> -`rCe'T=4_miOAb_qRGk.n^7;J!+GJ2!,(h0rEfB9rE]N>?1ko5>[:Y&^AhO9_>e!@])Q%2^&VO; -\,BV!^].dDAFIY?AH5QqcMLuD>bnk/;?'OpT%dess*t~> -a8^lgT1f/(i(X+lp.b!Zm=G:Or_E2HrRq62nL4-3!@B2e;ue6C;?%p/<<" -9`6:;=79/g/G!HQ0)o;BFSdHb,PiRb9EZCZ+X&%s2=;m6s*t~> -a8^m*T4.jK-R[h_r[RVQmr[res+UGPpI5%6!(QT -a8^n(T=4_mi4&\_p:0S.mEtsVraGP5rF5h?oO.Z)pV-`8?1ko3>lD>8?1#B+?N.e@@eRA5>l;;9 -?h_&.?1#E+A+RhBA,fBhcMLuJ>l%[+[fK9%r_f,TmUXE=J,~> -a8^lgTM,8)i(X+lnP/XZm=G.Kr_E>Lq:Yg.odKK5!E:?=;ue6?;?%p/;uni'?AcF@_=l=E$-iP@b+gOfA+9E]1,).tqs*t~> -a8^m*TOIsL-R[h_r[RGLoQ9Jjqh>#LqaL=6!(Q]?s#0Og!GsRcD#c?eC]?-VD#lIPnn.nV!GsXa -AGn:RDZVb0C&L*eJH%_3>2YPB2#]0&/[Ape.fq7R/VZU4s*t~> -a8^n(TXOhni4&\_n[S5.mEtgRraG\9q-sD;pL+#-otLOA?2)&5>ku&4?1#?,?=NK:s',T=?h:c, -?2n7$^B.fT]DZ76ao>iN>2[^*?iF=)>I(a<;>sIJT#^k]~> -aT$uhT1f2)i(X+lm7m@Zm=G"Gr_EJPp"BC*q'bo9r)p/M)? -qGI/Ar+5m\!*S-"!&aNrpE@(@" -aT%!+T47gJi+2g:m:H'7m@ -aT%")T=4bni4&\_mC;r.mEt[NraGh=oj[u7qHs;0otUF=!+#K9oXt30n@em1n%&L%n[JM/ra,<5 -!+>Z8r+#ZHs'l%4h9H"5pg!Pt\[MT8;>sIIT#^k]~> -ao@&hTM,8)iCs1lktV+[m=FkC!)E(p/D# -ao@'+TOIsL-R[k`r@7&CrH.Fso7[0EnV$p3r\aCf!&O=c!&F([s"X._s"sbU!%mbR!)iefoORc0 -r\F@h!&X@_rAk%24?Qg'OoIi?0D@'5AH+.]/1iH-6HB+V~> -ao@()TXOhniOAb_l+$]/mEtOJ!+GD-s(D==p:gT4p:^N3n\"m-oY1?5s3UgMo""g8ratf3q7?Q3 -qnE,=ot104"3G%_>2@L(BC]=%>JIZI;>sIIT#^k]~> -b5[/iTM,8)hb=%lkY;%[m=FhBmFqS"r[%D@oMk?XrDpf%5@qN:h&pJ1f87fJ5B48;MB:];[? -<<+E:;?&0J/m7"Ps#'*epE@OM" -b5[0,TOIsL-R[e^s!m5DrcIOtnqHIas$luCqJZ"CnSA):s"X:c!&XPP!%mqW!AIFNU]!dZAG'R6 -1B27m2">;[3WV?Hh3%_*lS%ua`af3$.kE5'KL3`ZJ,~> -b5[1*TXOhnhm`V_kd^W/mEtLIm:60/ra,H9oY0k.rO`)8pq?`9qU#:HpUUB3C&ZQZB)&k.])H%9 -^Aq[3]Dc:?>2@L(BBrgs>K4,Q:Jq*hKV$6aJ,~> -b5[/iThGA*g.^g,!ZtTLm=FhBle2=ur]gB.r`/Vj"=6As4 -b5[0,Tjn!Kg19M\!\/;6m@W_E8hfsS!&=:_s"XFg!&XDL!%n([s&/]g -rCQotnR_eW2",,Z:0QJ9r^>g#pas^#s!n.%/M2WBa8^Y~> -b5[1*Tsb"r9ha9MlaR!m_SY]fo#g9.rFc+9rkJ-?"C,POB3b/C!*f92s'>Q:!+>NJ!*]92s(V>X -rFYu:n[\a-@.1].C0KH=rb(:spfktOs&&bnKV$6aJ,~> -bQ!8jTM#8*r(Yj;oMGQ5s!RB#" -bQ!9-TOImKr+4Pln7DVLrakPgnqHC_!BWdJ9E-aI1%oGb1/@e?s%38FqJ5_:s"XO1nu;gEAh[+\ -p7qRX!&!YN!&*qX!]!M;hN@_(r\<@bpasp)s!n+$/VHI1s*t~> -bQ!:+TXFhpr4(Fhn@8Kprj_FEo#g9.!&+1]s'>\2p:^Q4B)U0XB)l35]_&i9?2n='d0*Xa?>4-I -df!bX>OB!%>k>Q/?>=*/cMV>S?K#/a>LL"\;?'OIT#Ue\~> -bl<>jTh>A+p.a77ohGH.q+^L4nnIFDs'POc!'pN0qG[JA6MZ<749-W*;=RKOOo[p>nqd<-r\X[" -lqd@Q![U)sr%mATrAFU`pa+lDpEA6a" -blpontZ4Vr^-Z< -lt6!+!]!MGr'KG+rC$ZbpbC_\pFY*0"=YiP/1lNA`rCP~> -bl<@+Tsaqpp:/hcosk#uq7-(Eo#g9.s"F+Zra#B7!F074df3q[>lMD+]C4,JC2*7er+5q5m(2r' -!a]VVr+>!7rFc.)pg2oapKQ@\!DcSl;M:X]s*t~> -blA+n4h\3q+^l2oM,+3o4dOEra5FbrBU?.r_rkDoS!6,nPB9QPs=i&s$Q72q_\?tlq[U+ -r)!G?r]pL)qD7>WrAFR_nKm0>pEAHgru_=c,(hbms*t~> -bl -bls3CIQkg'->r_;ohi`u4^i4&_`s&+K@`rCP~> -c2WJlTh>A+lV62/!$2#4!$C])r.4HRm+VAEpcnU?r[%)fs$-Q.p.t`6qiq.Gp6,DBs#]e-!'g69 -!$^c*!@(J@5koEs1[]A`2>ptL-/p0D+m2N%*ZuUL,(hbms*t~> -c2WK/Tje!LlXem_!%.YU!%RJLr1*A4m"G9upe1HXs"OL/nXohT!&+%Z!&+5M!)*$cr_WVcntQ1Z -pd5$6lt-8OC&Um&O8;32Oo7]H<9l9u0D@']A-=>3/1`B*6H9%U~> -c2WL-TsaqplaYc[!)*:#!)W0pr6b,YlsorHnRql-!*o'Bs(278p:C<.qp>CTpN`Hr:Jq*hK:^*_J,~> -cMrPlV+Uq291p9Gk"Xi/!$2&5ru^Z's+0`TmFqJF!(Qrqq`jj@nUpm(r'16+qG7/:pm(kDqi^qG -rIk/3pPAf5oec^nmnWp/r]p'rqD7_brAFO^q`*jeiZn1@m7dO&![/a3KJ``I`rCP~> -cMrQ/V.'QR@U^]4k%3O_!%.\Vs!mGJs.&Y6m=bC!!!<-jqb-]YnXfePr(I)DqIfj^poFEfql9Wl -rLX!ZpS7^^og8^3mq)PRr_E'GqEje9rC$T`qa]p+i\1$Xm:?5I!\>oTKL,Y^`rCP~> -cMrR-V7$Lt[^Jk(k.'E[!)*=$s%r-ns3^D[m:6&I!"]&nnn7L@qdoh4qRZ`2psK+QqpGCXrR1[W -pX9%Uok"2.n%&L*rbD&MqI]?ErFc('qkE@^i`u4^mC3*mr_eE@!(b-0J,~> -cMrSmV+^h.s%<>4iD&K/ruM,6ru^W&!.jZSrEo-Lr*KFnrLs0ts$-9Dr]9_*s#^$A!$_D=!$h-$ -!'gIGr^6L7s#^(5rBKp3s#C''o1oH64?u1"F8@]gFSn#p@IlJE-/g*D+nSG2*ZuUL,(hbls*t~> -cMrT0V.0HNs'bsUiFV1_s!IbWs!mDI!1`S5rB0Y(r&Xl[r<3'is%E,]r^ZXRs%)rZ!&+=b!&*uF -!)* -cMrU.V7-Cps0_o%iOJ'[s%EC%s%r*m!7C>ZrA!kQr%A$\r?(u&o4RaEpLXD0rjr/6o[*YMrmC^[ -q9o:TqU5=WoOe,9!*f'+!+,Y>m`tZ@q9JnNrCumXlX&OTpfmC"!DcSl;M1R[s*t~> -f)PY@s8RaHV+^h.!D=Hh:$PCT*?Ios+72c&.K6%4C&)-1gAG:X7erbV@/3(a3r=U/48(o>61C0> -6hH'348t'756>Ft -f)PYWs8Ra`V.0HN!FdG4Aa3e/-7<@S.d^dI2?!aML&!k*K`!n3!r2uu8,u'R:&7BWTD_=S9E7fV -X8c#gT)V@\U]!mg93,*e6i9FS0)&uW9)q`NO8;3COo.WH<9m'?1#Xe\/c'#X.fq7R/V?C/s*t~> -f)PZVs8Rb^V7-Cp!Ob\X\E^O+92)il;!kamPG`-C?Wt>B)g0SBDtWkU?c.E>khP"\,T>);M1R[s*t~> -f)P&/PYIuIrZ(o2j%TLnpeC6OruD.^+!9lE!$:s0pM'>.qqCsT!(QUSra>XhpH&>-r&aO9ns05B -n:Ud'rBC9orlG(>q).t.!$_;9r]K1]r%n=0rF+bHlQtL7p`Tb]+<_mPJi1OIJ,~> -f)P&FP\$\#r[%PSj(/3;pgrr*s!@dp.Osgs!%@XGpP&<'qh4l/!!;cqr^?ZYpIG7Ur(-HRnuDde -;"1%V8Gu6_9E)a+ -f)P'EPdmQur^d$tj1#(]ppfh&s%khM(:Jq*hJtBs]J,~> -f)OGsWD0*[rZ(Z+gJ.?7ruM+\!$CQ%!%IT7qe>V.!8@?X!(QIOra>akp,`+5!b,MDo0E#7s$HcH -n:Ud)r]^BpqT/Y:r&+1.![:Dcr]K"X!])\krF+MAlQtL7rZV:a![/a3Ji1OIJ,~> -f)OH5WF_f5r[%;LgL^%gs!Ian!%R>H!&aEPqh=T'!/183!!;Wmr^?c\p.,$O!_bgCo1\kYs%`Va -n=BVQr_3AcqS<)2r'U0J!\S(=r_)(.!^]:CrD_TClS7?Or[n.#!\>oTJjRHVJ,~> -f)OI4WOS\2r^cdmgUQpcs%EAc!)W$l!*/\6qo\Ia!+Yp[!"\Q@r]0U`r+6!t1c4f9pY8!aE6BrbC97!bQ(XrCu+BlX&OTr`fD"r_e?>`W(G~> -f)Nib\P8hlruCN%geIW=!$2%[lqII/n=fkrs$lHa!(Q=Kra>I]rAt!greg\^o0E2 -f)Nj$\RhOFs!@/Fgh$=m!%.[mlt$/Sn-&cFrrVdSrAsu\lid>V!)*&VrC?o^rhKHWo1]%^rD3D^ -o:GtT!_l<\p;$Z.s$QEKr_)(.!^f@DrD_?]H~> -f)Nk#\[\ECs%)Xggpm3i!)*;bm'm$qn/q\Qrt"]Ur@\-]llZ6f!(6KnrFQ$srmLd&o4ImIrFc+A -o@!VQs$6:`r`B,3oOn/ -ec30Qb"\[)rZ(2spItW-ktV1OruM+\rb([o!%I6-r^QKd!(Q+E!+>Xar]:*hqMP8Zo0E>@r'U?A -pk8T/!+tcB!`n"-of3"7giNHPrF+#3lQtg@"!Aa1,(_\js*t~> -ec30hb%7AXr[$i?pLO=Ol"0m*s!Ianre'ZN!&a'FrW;dUr]:)]jobZWqFCNU!)36ar]g!Js/Z&n -qaU@Pr(HRq!`lM.og]!TglMJ&!(?cbcU[:Ul7W5i/1`B)6H&nS~> -ec31gb.+7Ur^c=`pUC2ql+$c&s%EAcrlOW=!*/>,rX\]Wr\"6^jrXRjqIfe>!(6V2r\X4Ys3^aW -qe#WOrFu`;b>~> -ec2O?geFV -ec2OVgh!-78^>qLeEO!%@"5r;ugXs#U2^iWK6SrC?lY!)3*]r]g'Lr(m?sq*t@T -s*jo>!4q[8qlfcbglMG%rD^j.l7qf^!\5fRJO7 -ec2PUgpj2hr^c+Zj1,(]r_311oXFlqk9'X5qD&"'!"\'2!(6]ts(26upSn?VqSph/C-rb)1(aJ.s+nQZ,m;Gr(;`;b>~> -ec1q.m7jEMrZ'ljk"YjrrZ:cGpItW:j.ZE@ra5[phOt$Z3]ggGMZ1*aMZ-]P5lF(IS,55B@f3G% -aS4Bb3W:elF8e#uART#_39hga,QAne,(VVis*t~> -ec1qEm:E,'r[$N6k%+Q@-N5CdKD/pW2<+i~> -ec1rDmC9"$r^c"Wk.(Far_3%-pUC3'iue41r\=E`hB)heB4aO+df.,_df(EbB)QB>d/.GOa8F'O -ZM0OZBDuN$cMhGX6qpCkU?Z(Z>lIWPT#CYZ~> -ec1=rr_97_rZ'lj!$(/rrZ2"[nRqbo!%IN5rc.D#r^Q@MrG:qrpknpWrg3Ucq`t-Jpm_7[oj[m' -q8i-[r]0j2jE(>Yrac(<\N&k#r?D@e+sMUr!#``TJ,~> -ec1>4rahs9r[$N6!%-l?s!Ijp-R\G:r+,jco*#&Qmf`Ukmf`TFmf`E\qS)s2r[I_oTJjKG\`;b>~> -ec1?3rj\i6r^c"W!(uFar_*8bn]C^=!*/V4rlsmRrX\H>rQO^PpsT#UrmC]gqe#h?psB#PouI$Q -q6KRSrb)+?jNdd"Jt;s5`;b>~> -ec11nW([.1s%VKHruC5srZ)&?*Zj]]ntGl!hkBg]n=fc;p:($fr`B(as$QRNqGHoprNH1@nCd_D -qD.>Wr\aXcZ8h4truh@dJMk@FJ,~> -ec120W+,cQs(12#s!Hr@r[.bc-R\;6nc\cKhZW_1n-&Zap:gO.r[Ih?s%iEoqJ#VOrOVsCn?;b5 -qEaD-r^6WZZ:F::s"".%JO79SJ,~> -ec13/W4)^ss1%'ts%;Lbr_!=-9MFiqnfR\Ph]MX;n/qSVp;6gVrZV8Qs(D,RqRcF@rQ5#Dn<3^e -qI]$ -ec11nW(R+1peBsGruCq[m7mU$qc3,unY,Vqr1X.Rl_3ren=]i>o!eXcrDrtdq4%%SpgO4Tql^%# -?LleE>PU&CF8e&qAZ@`^,m#56,(VVhs*t~> -ec120W+#`QpgrZ"s!IXrm:H;FqettVnHANFqum&"lNHj9n,r`do"P.+r@%_Bq69O+pjN3)qmlg< -:\(^aG54GmO8_NF:9$hV0`imX/V-7+s*t~> -ec13/W3u[sppfOss%<2nmC<0hqnMXBnK7GKr#bsdlQ>cCn/hYYo"tFSr?2/Rq9\fHpr -ec11nWD!72nP/LHrZ(YUo1f6*p/UcunY,Glqi^EAn8.`jmC3+^!`q`VrL<7Qr*o[Xr3-$u!+,BP -o3;&Ho5jmgrFM``qBZ.c,(VTO28fHE~> -ec120WFGlRnR_3#r[.@lo4@qLp2BVVnHA?Aql9)fn;-]>mCrV&!\$l>rNQ9:nnJ#Ns#^.ir3u`& -p94(2qEa_6r^6WZUIY/8!A#gR63'?is*t~> -ec13/WODgtn[S(tr^uoho=4fnp:p:BnK78FqpFjSnBCMHmDAnN![2,drQtPVo"4h?s()&Lr5o"1 -p6,$bqI]?Erb)0tUS@Tks&+E>!1q+.J,~> -ec11nX\/X2r_!52ktUqHr>b`0p*'9,rZ1Q0q.K8ln8\/boV(Vfpn?r]s,-eClVRXkr[n,Qp8[uB -mp#TCpiHElrFMKY![T$7JMdEb_uG5~> -ec120X^V8PraGjRl"0X#r?hGRp+H2Qr[78Tq1J5@n;d4BoE=N:p]Tiqs.fQgqJ5Y9!.Oo/rjqg5 -qQK@2q*Fe:r^6WZS4 -ec13/XgS3mrjDetl+$MtrC[!tp07C'r_)gsq8r1LnBL[SoH3GDp`Jb;s3gmXqRlC2!6>(LrlOlV -qNC$'k;Gr(;!1q+.J,~> -ec11nX\/a591p8`:&dod*;3,#+ob::*UQl+BCqnt`rMh'EUf4fU$$7iF69q3MWRe9YO\^ng$)`: -1B)@o2>q*T2usm@JMdHc,/aG2~> -ec120X^VAT@U^YBAcH -ec13/XgSQ;559C9Lnan-\TZiD3 -ec14o!#Mb1!uOZN*8sTl9`?%$:&6%5*UHc-4D/="U&)h&EU8hgU&Ku6D"r)9\)L9UWoRC\1B061 -AXYR[)'0uQ2#oMNs*t~> -ec151!$87O"!D&!-/iD8AH!qFAbn%Z-gYaQ7>$6c"T#q,NpNbG"TG4\M"m%q^@H(0C%AdpG5=N) -6iBcK7fjh^/V-4g6GibQ~> -ec160!'IBl"$O(`8E%&Y\,K"h\GBG09C0CpT#1MX~> -ec11nY"Sd3r(Q0Gr#Fur![/R)m8X-0ntGctrGhgDl]V+<l<8m^M"Lp8[%Tr\FL2rFMEW!Z<%' -JMmHb_Z,,~> -ec120Y$qDR+T,d2Ac*n?AHFJ3-gbgP/Fir;_#9;mHL%U-V!Np0O7'aLC[l-4\DVQ062s=N:7=ZS -+Xepn6N9Bhs*t~> -ec13/Y."?nr3tas!(d&\lF6n"92"Ze!)N/-p9aeCrl+3NqU4`SoZcg@mC_u$p<)=2rau1CrC!*K -!`2oqJY)n5_Z,,~> -ec11nY"Sd3oM"UGr>b#q!.=-5!'KkHn]1K5n<3I&nSI]9o!d@Nq5W1R!&a^dRQ(Be*ZcMV2?,PN -s*t~> -ec120Y%%DRoOR<"r?g`>!1<+k!(HJ^n\G!In>c/WnVH[qo"O@(o4e$1gNEQarD&eO"!K'@/V-7g -6GibQ~> -ec13/Y."?noXF1s!(d)]kdUW>m(WO$o,mM0qShsOp!WBSnBLO@l+HW"q9%I0!,)6uR\Bm\<`W+L -T)YD!s*t~> -ec11nY=nm4l:gnGr>b#q!$1ranl5ZPo:b]poj@BTnVd'VlZ;p3rCl$Es!@YDrMnUV!%RqYRQ(9b -rZ6VJr\_RmJ,~> -ec120Y@7MS+R3M2Ac3t>AH4(SR.>.q7IpZP_"E$HWpunXM!L,u^AVU*C\MQC1B).h\D)0,2uQL? -7fX_3."O_a6GibQ~> -ec13/YI=HolF6Jss%*/^kdUUbqp=oK!*8\5n[.u.p!NrQ -ec11nY=nm4i_0@k:&H!p:%`o1ta7*I4P3e,'D!,m>\+iYU;>qm=;uf_L>Q7V$AXPLX -)>tV-2?,N61;a'A~> -ec120Y@7MS+Q?o4,A$d"k[jFSqJZDD!%d[Do"aa@qPrnXqM= -ec13/YI=HoijSqV\Gf&!9'EeXd/(NWaoG6.+7cf.`VS!Kde_2Nc2YZU\*(F$]Dc17^&Yb"_uF3G -7%'bL;#OFGT)YA^=Mk)g~> -eGk(mYY5!5i_0@k:&Z+4:ZtLQ*V"OWpMIr\_OlJ,~> -eGk)/Y[[VTia`'7Ac="VB'f#~> -eGk*.YdXQpijSqV\Gf#!]'-X(9(C:.^\Q%H+7cf)`iPYY( -s.l*X!)WVpJY)k4_>f#~> -eGk(mYtP*6iCj4ir>bZ0mS3+soW.qan]1!'qci?DrHd[Kn4`1Gr([/Ar`&Sej]ha_q)P',ru(eW -JMmB`_>f#~> -eGk)/Z!m_U+Q6i2,Q/tcB's6uR.Roe"S':oHM_mNV#?VCO76]HC]%cC3rX!iB'"49=SYR]8,jb3 -."O_`6G`\P~> -eGk*.Z*sZqiO8eTrCHdrm^V]3oZI+Sn[.Z%qnq^IrQscQn@.bsr4)`:rkA*;jhq+-q4sXWs%r\q -JY)h3_>f#~> -eGk(mYtP*6i_9@jrZ(N*ohFk%n#QCgp;cN,pKR*Epj2IO!)WZ9lV-eFpItWenPAIT!\.)GoJr^, -ru(eWJMm?__>f#~> -eGk)/Z!m_U+Q?r3,Q9%]B(]a'R.%Q`=8'GrHM;UOV"g8GNrWkmO6^?GC\MB?D=M6(FojU)=S,4] -8,jb3."O__6G`\P~> -eGk*.Z*sZqij\qUr^cXlosjG:n&kT3p9a2*pVZIJpsAQU!5&6TlaQArpUC4;n[e&(!b#PIoVA:W -s%r\qJY)e2_>f#~> -eGk(mYtP*6i_9@jrZ(<$r(R/3msOQ(q,dI6lsK?go8E6eolL%-ktL\Go1e^MmooiC;H!!EW&Xep -rZ6VJr&"Hu_>f#~> -eGk)/Z"!_Uiai'6r[.#Er+,jTn!NOQq,dI/m!A8Ho;)#EooK#al"'C$o4@E'mreak=BP&bW(6k2 -r[<=ar'CB8_>f#~> -eGk*.Z*sZqij\qUr^cFfr3u`!n'LLFq-!U%m)AS6o@!9Xp!*EHl*p8so=4;$n&@Uei&W1s;g -r`4TAr13mb_>f#~> -eGk(mYtG'6i_9=iruC5u!);m/n#HFVrLs$gs2Y+Rn6bTfpl"WfqK)C-ktLkLmS31Ho328G;Gug@ -XZ6=urZ6VJq_\@=_>f#~> -eGk)/Z!m\Uiai$5s!HrA!+kScn$i?jr<2pts1nVhn9XMGpn[DFqN(Aal"'R)mUbm"o6(0o=BOl] -X[iC7r[<=aqa(9R_>f#~> -eGk*.Z*jWqij\nTs%)@b!4_IDn&bW3r?(i:W<@UeZ! -XePhlr`4TAqjmdg_>f#~> -eGk+n!#Mn5!#X]hrYbbVk>2,1nU1@8n>cOWpn@Ueq.f\3rGh.-r/9les)[d.k=kkPl:p_Cpfm_q -juK\+!#bbWJMm9]_#Jo~> -eGk,0!$8CS!$L93rZ_Cik@XabnX0>mn@/Hkp]ULrq1n`krJp2er1rYEs,Zbbk@FR-l=KErpicX= -k";mK!$V=iJO93"_#Jo~> -eGk-/!'INp!'T>Nr^6`WkIU]@n^. -eGk(mYtG'6j%TCik>)(!qLeNiqG['f!(?IQ!*Af-rEoA?nQZ$2!*fA'l?_kpkY1,5n5&sdr_h]8 -[Q"7)rZ6VJq),tfJ,~> -eGk)/Z!m\Uj(/*5k@O]AqORAJqJ>i7!))qe!*AfJrHe:"nTOqj!-\9\lB^jOk[`gen7MTAr`J,O -[RU<@r[<=aq*MmsJ,~> -eGk*.Z*jWqj1"tTkILXdqU,&aqRuRE!*Ae7!*Ss;rPeTWn\bCO!5n`MlHnt2kdT]an@JP8raOi^ -[\D%J,~> -eGk(mYY4"MrYjrq!$:cbrI"69qQ'=`r(us!ol9Y/qel:5ms=4pkY1,5n4s*2;Gu70]/Td.rZ6VJ -pbfkeJ,~> -eGk)/Y[ZX'rZgTln!<3Ok[`gen7D_U=BO -eGk*.YdWT$r^>q\!))t3rQt2MqTALRr4DOCp!*0Iqp##Rn'L=2kdT]an@A[-@Ue)f]:o:%r`4TA -pn#;$J,~> -eGk(mYY4p?kY1pnk>1Q!n5KAC![K?qeiCN*!#b_VJMm6\ -!%PeaJ,~> -eGk)/Y[[Pbk[aW:k@X1Rn8/)JpB:8)p4r-tqf_[kqf;:oj_3mhj^n'r!\c] -eGk*.YdXL=kdULYkIU-0n@eh>pE01Bp<2sMqnr-PqnVgMjh'cdjgaro!a]DHetg*U!)WVpJY)\/ -!*6o8J,~> -eGk(mX\7h&rYjrq!$:W^n5K-fqktV-2>B&Ds*t~> -eGk)/X^^HIrZgT -eGk*.Xg[D$r^>q\!))h/n@eY9q]GIBrlb;Wm*5RJo?I6L!+>NIi4B$!\EgWc\G3W:?=[NmUrB&. -;#OFGT(nnls*t~> -eGk(mP=tlnkYD1"mt:+.oN^o_qH<6+!])Map20jcr`K1Kpj2m9q,%&bmSNdTlV-A6rD3DEbW3g* -!#b_VJMm0Z^]/f~> -eGk)/P@FM9k[jfBn"&raoQTh=qK)(b!^T"6p58o:rc8#rpm1koq.Tb=mV)K/lX]'frFZ$jbY$#J -!$V:hJO9)t^]/f~> -eGk*.PICHYkdgaen'UX -eGk(mP=tookt_=#:[`r*;tO>b;"e&_EW7im0`,be1&tGU.0"&YHMLD>:Jam`:%D-U:$PF4:&cfc -3R9Yb)>tV-2>8s.(Vg!#~> -eGk)/P@FP:l"0rDB();[D"MrAB_HN -eGk*.PICKZl+-mg]'n&9^%B85]Cs)3c2VAQAGaXNB)oaH@/nFHd.tTM\FR9,\E^Q`\Gp*[ -UsGb8;#OFGT(efV9Y^UX~> -eGk(mPY2&n)B.j9!$2&[nq6F1oN^o_nlb[+r\O@(s).cjr[@YYs*FH;r(m&]pJCHUl:g54`&Z=, -!#b_VJMm-Y!$]2XJ,~> -eGk)/P[X\7,9uGb!%7c,nt#8doQTh=noOMbr^$?Ds,6hAr\aS.s-EFqr+>\7pLs/0l=Apd`(JNL -!$V:hJO9&s!%ktcJ,~> -eGk*.PdUWR6q$:N!)*=:o$Qs?oYg:3o"k%Erakn9s3:LTra>WHs3UPRr4;X4pUg%,lF5f``2(nW -!)WVpJY)S,!)UH1J,~> -eGk(mPY2#m)X1&t*VjdNKDedC;uV1;>lQ&@;!qK_EW7imAc=C^;>i6M./7NW,Pk:3:\@H\;"%-D -9]Adb3S?@l)>tV-2>/m.*'h:=J,~> -eGk)/P[XY7,O&h?-i&htSc*F!D#TgpGQ0>gB^TsDq,0`#SZB(]a6B^]Nt -AE%1=9%c]7+oNg?6M<_H-:koOJ,~> -eGk*.PdUTQ7-V;_9DQm-dJ^fQ^&HCO`;a*7]C*N3c2VAQao5iL]DZ@B@/.nF>l),-](NT3]C38p -\)Op9UtMIB;#OFGT(\`V;c^[5J,~> -eGk(mPY2#m)X1#t*V4@NKD/@B;u(h<>lC/l,PY+$=oE=@E&7<$qb[GA1/C^@CAfCV;=mZ@:$YIQ -,iVP93Sl^q)>tV-2>/m.(EC0hJ,~> -eGk)/P[XY7,O&e?-hEDtSbI!uD#'IqGQ"cG0DK5HF8_+"N'[8iqe6-f6"Xa(L]&q+C\25qAa -eGk*.PdUTQ7-V8_9CpI-dJ(BP^%p%P`;S'_>kkqs_>MOXAbjIF])uDQ]"7[[rET?7oX`nhlaHk% -ijcn%hP(!HrDnK@p7;:Y;ni9^~> -e,OtlPtM)mkt_:"mrS"unpL8&!$hD:]"o*,QC[B,4\M0-/MAb -9`HBp9uB!O2ujip*e?3A2?+Z1s*t~> -e,Ou.Q!s_7l"0oCmuR!UnsK6W!&+7`rcddO"#atU62Z]=JGq5%C&]scB)@A^0`Pqh/b3KR1#?L1 -AH+C@A]%.#7fX_2."O_X6N8CHs*t~> -e,P!-Q*pZQl+-jfn':I;lHfN9r4DsHm_K!5^!1-dc25W^>lVG9?i.G3bPb]G]`;7,\c?-p\F6p, -@,AE2UuS0L;#OFGT(S]T;ni9^~> -e,OtlPtM)mnP&gUrDE'*nl51-opYt,;#4Gg=oToJ;u1J5AcGL(,QB%k/RMl]:]>,.,Q:X?,5+h4 --/2/d9`? -e,Ou.Q!s_7nRVN/rG1n`nnmrbornHMB_lrEF8n/rD#0+jJH'3[0E3d/4F2>MB)[Sb0`Gne/bWfV -1#$:3AH"=:A]%..8,d3$7fX_2."O_X6N/=Gs*t~> -e,P!-Q*pZQn[JD,rOhXFo"O85!F385`:LhF@/R_7c2Yoc\Gfb5?NOfX`VR1FbPkcG^&;..])Q0m -\Fd90@+i'-V!auV]DeN);#OFGT(S]S;ni9^~> -e,OtlZ:k0jkY(pklV.%Mq/cOLoi1ihomHA#p0@>p!%@V@qf)F,"=5WO/1gNkrZhCfq]c#8rCm2: -fM2B>r_*2`r$9if_`@9Ipe^u3JMm*XrYtnf^Ai]~> -e,Ou.Z=W)%3]d"Iq_7n!rau+>r\<&: -s()+ -e,P!-ZF9bAkdLLOlaQW$q9Ju^otL@=kKrq@pV?i;pq?RLqmZc9AS#C_pU^6,qca!1rO;c6fXUsj -rjMd7r*\*c_kcjtpq-Q,JY)P+r_iet^Ai]~> -e,Otl]1_cikt;!ll:gbGs)[pKqc*8hqgA")oN_5qr`&Sis)7ug+oP+8,lTCq/-&]H:Br1'.4HJe -,Y\%Ype9:6qFg`3d7qM=pGh_?JMm3[ru;%d9"t=U~> -e,Ou.]41DCl!aW6l=BI$s,Zo.qec%Fqj?uZoQU.Qrb_@Fs,6t -e,P!-]=.@@l*^RPlF6>ss3CA]qnDd=mEkLDqSE0Fp:^LN!F]11\,]h6AcH9@^\YMD?YNnUr`fH4 -qHE['f=:[cr*[p^_kd%$o"0IEpn%La!F/F7s*t~> -e,Otl`(TAhl:V*mlV-eFnS.PorE&Z]n6Grqqc*Dj""-l?E;_6d+oO"m=Su1u:B2^>@K04&9`H=/ -9\W;79?U&I3W(l':k:M'2?"Qq-GBG0~> -e,Ou.`+&"Bl='`7lX]L#nV-OPrGhM7n9=kQqec1G"#OV%N;Y^?/NZ$V4E>K?DuO>K1&ZP#C&DNA -/bET.AbBNUA]%.<8*jo?6N0=N-3,j8s*t~> -e,P!-`4"s?lF$[QlaQArn]L:7p!E3Hrk\KGqRlu=]"6/b@0TnGA7]>f_#1tH\c?+9`rE[K\,TY( -\(eFc[`c1uV#6tR]7HRQT)MXg>eU/f~> -e,Otlc:d%glUq3nlV-hGnS.Mnq,dE^mp#lC./-[C;cH^DqbR2@s"4(Ls&B:P,rK(=@8Tnc9]/Y7 -9?U&H3Ta.k2?"Tq-GBG0~> -e,Ou.c=5[AlXBi8lX]O$nV-LOq/Q88mrndl2YV%jCi+#Gqe#ges#^'ts)/-"0gpA:I;*EZADh%g -@`r?"9'0f?6N/>31VNg=~> -e,P!-cF2W>la?dRlaQDsn]L+2qU"]L!FfI2^&u'_^0piLraGh;qS<-G#@UtR`Q#p0]BH`^\F?rM -\AG'(]7cdT;u^+9s*t~> -e,Otlf1XXfnOrj+s%NJ/lqHnGnS.PooN2'^s![DB!%RJ;q+guBr)a/&==GXH9hkRoj%SN.lq>Jq -a#W'SMDYGO*$"r)^&NT~> -e,Ou.f4*9@nRDJNs()0Olt#U$nV-OPoPso8s#0Ck!''Ibq.9Ugr,N!YE],d4AS(gej(.4^lse+K -a%G9'MF%@c-78^J^&NT~> -e,P!-f='5=n[AF)s0r%hm'lJsn]Kt.s3UdRn\bE:n@n_0!+c#C#/7l^@:?[UrPdg+jLFQbb.,Kf -i4Fl -e,Otli(M3dqb-l4pJ(!JnPSgjnQ#-]s&8nFoiqJHlr -g/*hfruD"b!'.amJ,~> -e,Ou.i*si>qdTLWpLW]%nS@ZKnSdu -e,P!-i3pe;qmQH2pUKS!n\"D7ikPN,ra>E;!+P!l\;Fj1"6^kI/LBbbWt) -O.QAMr`fG*^&NT~> -e,Otll:\lcs!-u,nP&pVnPSgjnQ#-]qc!JBqHO"Moi(o:p/Lrcr([&fs&&ej!)NP9i_8l:s!@\> -i_.BfcoKTPPr8O\s!@b#;SE*\~> -e,Ou.l=.M=s"EhPnRVW1nS@ZKnSdur+5bBs(hXG!+u0\iahRjs"aUc -iaU#@cq;f$PsYHss"a[>8\P.S~> -e,P!-lF+I:s'Y<*n[JM-n\"D7ikPB(ra>T@!+PW8!*o63r4;g9qnE*Cs0r'?rE8-hn[SU,s1%'s -agf]ne@V!;r_ri%!FeI1s*t~> -e,Otlo1QJbg.h-8!&!hVr[%5;nQ5Kar[7bOmo094qG[GCq+q#b!@:bF<<"?I;'u9 -r?VG -e,Ou.o4#+?r\ab#mqi%YqJ?3iq.BYuHoCGc$&!%n+"iah^n -rA"@ahI=Q;eOn,#RmI/t-78^J!'\'qJ,~> -e,P!-o -e,Otlr(F(ag.h3:r)iqNphBLLqc32goMb*U!$qMB!%@Y=s!Ialr`/tHs%rYir?D1_iCs&Ar?^,i -a%b8JaAANeruD"bs"M+cJ,~> -e,Ou.r*l^;g1Bnlr,Vd!pkAJuqeu%FoPEl2!&4@g!&aRarA+@js#'girFZ![r@Ho -e,P!-r3iZ8g:6dfr4r;Bpr`A -e,OtlV+fT;pJLTcr\".`r&XU!r[.8=oi(*SohYT7s!e(M-n+shrD3G;q]Orn!%%M:r?]rda%bDN -_,.$erZ(qbr\2"bJ,~> -e,Ou.V.A:kpM9GBr]L.5r(-T -e,P!-V750gpUp17rabuMr+Pk3ra5<4otBV(ot(04s()8H@Uf5HrOW#4qcMpg!+Yo9rF"-`a11!% -_7QV:r_ri%raNP?J,~> -e,P7tS4qX2q,-Q^qf)Enr[%G?r[.MDoi(-Tn5'']![poCrD<>dq&nlpr[%M;r$B]_`_GJR]25Ue -";`$t-78^G!(OX$J,~> -e,P86S7L>bq.oD=qi(DCr\F@er\OFkok`o1n7M]9!]Fb,rFl%>q(1`?r\FF`r%cW.`b"1&]4e<5 -" -e,P95S@@4^q7Q.2qp#"Ora,K8ra5Q;otBY)n@JY4!+Pi;qRZK)kdUP$rODc5d'gs(j.s -e,PM&mn<^'VbGf=qbcc`ol0mkoi(i:ohti9n5KB\r`&>^!%.P>peUT+m7dF(r_<>;b=ffKkW-%I -Z8MY%r[.Y!!BF2ns*t~> -e,PM=mplDJVe"LmqePV?oo/l@okaU`okXU^n8//7rb_+9!&OOgs(V7?p+5TAr%e7_rA)Q*`F\7* -[:lj4s!I_$s#pHQ6+m5J~> -e,PNs1A';p/p^kr+#`9rF!gW`OP-- -[C``9s&8o%s'c"?35#9A~> -df5_.k"GasVG,`=rDDrano4.ur)!5grZq,8r(HNTr[%VDrD -df5_Ek%"HAVI\FmrG1e@nr3-Ur+PqCr\3t]r*o/-r\FOjrG)Ek/birM/Fm?I1&tna1n%56"*as*t~> -df5`Dk-k=hVRP -df5t5hFmnkVG,Q8n6#7#nl,3X!E2hm,l(F79CksX-N6pD-iHjA,5P+++SJM)-2^I;-,E6n:%:6C -:qegd*<.Ab0`O/Fs*t~> -df5tLhIHU9VI\7hn8e)Ynndu4!GlHI0_oS\@e4411]D1j2>q1g/c')M/Gn$56"*as*t~> -df5uKhRo"F_.!PQ,C>ku,/[e%*/A,a.AAGs+?=o,`#=SB6"A,Nh:@)9:j\FHAo -]=sm9;u^,%@fL#Os*t~> -df67=JP>l5e4oR;no4+tnPK-/r`&M_nk]97!E1EB:]M^=:]=#V+T"k.-2:3L9>jQu3MTEg2?+Zq --N>e!3P5 -df67TJRnRee7J8mnr3*TnS%hSrb_::nn.n[!Gj[iBE0X`B)Z9//GiuR1AGFq@`2jO8u$(;6N8D3 -1]KZ=7K>igs*t~> -df68SJ[bHae@>.do$-^])Z"5])K/-=SoT'A,*RK[`#]LUnbQ>T)Vag ->lS:5AH5iEs*t~> -df6OEJP>Q,d7sF=nSn%tmSNg,!E:*29CbmY;>3o_9CX/)9_g"+9ts?>:&6l@:rtTp*<%;b0`F'4 -::pOV~> -df6O\JRn7\d:N,onVm$TmV)MP!Gs4U@e+.2C%l<:@du#LAGJ"PA\V]mAbo#iBZWU@-N5h$55n"O -9Y:=T~> -df6P[J[b-XdCB"fn]gX#[dq$&]D8o*=T>l+A+R4F[`#]PUn,->T)Vag>lS:4AH5?7 -s*t~> -df6dLJP>9$c;":?nSn"slVRR+ruL](nPB0^r)!#Ynf[cTmnLMha%ZO[r&L*?cSb\As!I_!s#C'F -])R9~> -df6dcJRmtTc=QuqnVm!SlY-8O!@T>e@e"(1C&DZ:@e))H1@Jeg@`N$[1&OY1B[9$E-N>n$56"(P -9tL@T~> -df6ebJ[ajPcFEkhn]gU;lb!.$!DcJ^[dgs*]C`Q&=ScOr\A#Dl\,]q7UmJ^>T)M[g>lJ43A\.nl~> -df7'T\kE)GVbHJP!))K7rDE&WoH=)YktSlbb>&!_rAg!:eMRCE*$"r) -s"OI5s$=6rJ,~> -df7'k\mtdjVe#1+!+Or!n;QpSk\('1-n$J6@e=:42#V.X@e;5M1?iAa@`r?_1&X_,B[oEN*[W65 -1]KZ=7fQ2os*t~> -df7(j]!hZlS:3A\.nl~> -df7?\YtP->WD*= -df7?sZ"*haWFZ#anR_Q+k%+QEB)Y$fN;YO)D!bp4B)a7Z-h1[AB)FLcC$T7&/H7KeA\DR!Ac=F` -8rIB:6N8D31]KZ<7fQ2os*t~> -df7@rZ*s^3WOMn;n[SG(k-tFm])H:6c2D#B^$WH.])PP+:@Ydg])6.>]Bcp"=T;mm\@o>s\Gfn8 -Ul2k=T)Vag>lS:2A\.nl~> -df7TcWD!:6XA&R=oM,!OktM.$q+puEqf)6TrDW8`o2#?+oLo31r(m,9k=bUni(^mXe4oies#Gd0 -i&1KRs!I_!s#L*F!'n-qJ,~> -df7U%WFPuYXCV8boO[])l"'iGq.BUjqi(5(rG;% -df7V$WODk+XLJ. -df7lkTM,>-YY=m>p.b$Lm7dR(nk]9?!-%p6r\sTurCm&6r_W,\rZ1c,!%%Rjs%iD;r_!56k=bar -gJ,@SfM25h!',U,r_Vc8!Z)grr[.Y!s#L'E!*$Q0J,~> -df7m-TO\$PY[mScp1<`&m:?8Knn.nd!0$njr^HT;rFGa[rb1h6s!RjppgO4XrA4Icpb_tZ!%[5A -r@H]7]jfW*q_8(CJS>$rk<]4k-78^Js$$HPr_3A^])R9~> -df7n,TXOo"YdaI=p:0V#mC3-so"+j4!6tMTrb;.7rO;W3rk%^3s%`VippL04rFGr>pgsH5!*A?l -rE.ga]scS'qd9E"J\1onkFM`K;cE[#s'kn;!'e'pJ,~> -df8,rJP=3[q'Gc0l:^YA!$Uc+pe^oJs!@81!#kB'rZ1o0!%%Igq'Yu2!$Lr-r(Zu[ekNeMh+d\k -JPc, -df8-4JRlo6q(h\Ul=09p!%dPMph9Ups"a1V!$^rFs!Rjpr*fX\qD7q(q-j=Soj[i1q-qod]OK]. -q([G6ph9"\s!I_$s$$EOs%NJk\c70~> -df8.3J[`e2q.'0/lF-5m!*8O!pq-K=s'PB,!(6:fs%`Vir3cT8qIKD5q6g9)osXe.q6nka]XHY+ -q-\ccpq,mas&8o%s'kn;!+WS>J,~> -df8E%JP=!UqBbl1j\,5?!$Uc+nPJdMn5'$(s!@Lgr([/ -df8E -df8F;J[`S,qIB90jgOfk!*8O!n[nA$n@JWq!`!68qIKN6rak`1!*B$*pUL11dC?itiOJ_lJ\1Ha -nt,hZs'5P4rFQ"=\c70~> -df8]-JP -df8]DJRlN+qD.YRj'qdp!%dMLnS%K(!$^oEs![b#pgrr(!\7_ho47o2c"4Lrk%4?CJS=@_pd4s, -s"jX -df8^CJ[`D'qIB-,j0n`m!*8Kun[nA$!(67es%iN&ppfh%!`uh=o=+e/c+(Bok.(4pJ\16[pn%I` -s'5P4r+5n<\c70~> -df8r4JP?#9!$AsLqBbT)i_02D!$U])nPJaLoM>;"4ruD"bs"OF4 -s$?ZV!)C*)J,~> -df8rKJRn^i!%P`oqD.MNiaVgs!%dJKnS%H'oOdr2!&O1Wo4#1?]41&:qD!P7k@j]Ys!I_$s$$EO -s%WMk!)0s'J,~> -df8sJJ[bTe!)pYAqIB!(ijScp!*8Htn[n>#oXan/!+bZ1o -df95]0!$B*PqBbB#j%KDH!$U])n5/XK!#kc2i_B/?Z;%o'mS3O(JPbN+!%%M"rZ(qbs"OF4 -s$?WU!*6Z1J,~> -df95SJRnC`!%PlsqD.;Hj'r%"!%dJKn7_?&!$_>QiaqjnZ=UUVmUc5LJS=4[!&FF -df96RJ[b9\!)peEqIAd"j0nut!*8Htn@S5"!(6[qije`kZFIKSm^W+$J\1*W!+>\qr_ri%s'kn; -raPpf\c70~> -df4klmS!U&a%Z:Tj\=l1!$V>7lqd+InOrs%:Y\\B9<:k!9CkmR-%SnU;#_j>2?"Tq-N5_!3WDDE -8H;Prs*t~> -df4l/mUQ;Ia(5!$j^mR`!%e+Ylt5a#nRMYEB&$tq@]X.P@e41,0nF$$B`Bjc6N/>31]BT<7fQ3^ -;?0_,s*t~> -df4m-m^E0pa1(kSjgaH]!*9*-m(2\un[ANd]%jgn[]I!M[e%$)@"GrQ]Dl::T)M[g>lJ42Ac?0> -?b$,d~> -df0:gJP>K*!$B?WqBb&okt;""pe13Sr(ZcZq+UNW!#s]f!)'1`]1_ilqB^\hk"u((!%Ik(!Z)gr -r[.Y!s#L'Es%35d\Gq'~> -df0:rJRn1Z!%Q-%qD-u?l!aWDpgWi,r+5J5q.052!$p?2!+Mg:]41JFqD!P7k%OcM!&sjC!Zi[8 -r\OR -df0;EJ[b'V!)q%LqIAHnl*^RmppTe*r4)@1q7$+.!(PbR!4Jc7]=.FCqI"ldk.CY$!+Yu!!_cNk -r`oJ4r+5n -df9=gJP;.(s!dn(ruD"bs"OF4s$?ZV -r_L'(J,~> -df9=rJRl?&q(g`:ls]rGnm_B+n7DW5!$-W%lt*q@s"X:[q([G6k@jiMs#9mCs!I_$s$$EOs%WMk -r`-K.J,~> -df9>EJ[`5"q.&3im'Zmpo!\>)n@8M1!'#P>m'sg=s'kc5q-\cckI^_$s'u#!s&8o%s'kn;s'u%8 -\Gq'~> -df9:fJP -df9:qJRlH)qD-W5n6uAKlsfs+JS!>B`+&a$s"aC]qD!P7k%O`Ls#9mCr[.Y$r]^?Os%WJjs&B%j -\Gq'~> -df9;DJ[`>%qIA*dn?r`4#VRq7-9/J\1'Vra>b9rLX!es'5P4rFPt -dJs1eJP -dJs1pJRlT-qD-H0fj`WG!$p`=!%=.EaC>0(rA+7]q([G6k%O`Lr\sgC!Zi[8r\OR -dJs2CJ[`J)qI@p_fs]SD!(Q.]!)SukaL:tTqmcH0J\1'Vra>_8rgj0b;cE[#s'kn;s'u(9!Eq+c -s*t~> -dJs.dJP>H)!$BlfqBaB\gIo4S!$7A!btIjYr([#6JPbH)r[%Llr\a]qr[.Y!r]1!Es%32c!*-N. -J,~> -dJs.oJRn.Y!%QZ4qD-<,gL@j-!%=(Cc!p]-q_J(\qD!P7j_4WKrAX^Bs!I_$s$$EOs%WMkr`&r" -\,Us~> -dJs/BJ[b$U!)qR[qI@d[gU=f*!)Soic*mFWr4)T2J\1$Ura>\7rgs-gr`oJ4r+5n -dJs.dJP>E(!$C#jqBa3WhFkIT!$7=ud7a3[r_<27JPbH)r[%Iks#'cqs!I_!s#L*Fr^m,cs&HQ. -J,~> -dJs.oJRn+X!%Qf8qD--'hI=*.!%=%Bd:3,1q(hq\q([G6j_4WKr&=XBr[.Y$r]^?Os%WJjs&K)# -\,Us~> -dJs/BJ[b!T!)q^_qI@UVhR:&+!)SlhdC/dYrj_c3J\1$Ura>Y6s.93gs'5P4rFPt -dJs+cJP>E(!$C/nq'EjOj%HsX!$77sek>]_s%<>dq'CSgj\Yq&r$_Y#2?"Tp-N>e"3W;>E8cMTc - -dJs+nJRn+X!%Qr -dJs,AJ[b!T!)qjcq.%7Nj0lP/!)Sfff!b9]s0_p;q-\ccjh(M"r*ob?T)M[f>lS:2AcH9??iF6< -s*t~> -dJs(bJP>E(!$C8qqB`[HkY(59!# -dJs(mJRn+X!%R&?qD,Tmk[Njh!$0Et!%gLC1;pbM\Tr@rk:jCnNJrAXa?s$H`4r\OR -dJs)@J[b!T!)qsfqI@(GkdKfe!'&?8!)S`dgU?fbqR6?1J\1!Tra>\7s(DA's&8o%s'kn;s'u%8 -s&oC^\,Us~> -dJs%aJP=Kcq'E@Alq>l'!#t#o!$7.phb3Yhoh0j4jA>h%r@._"!&a`qs!I_!s#L*Fr^m,cs&B)& -;n)dW~> -dJs%lJRm2>q(f9flseLV!$pZ;!% -dJs&?J[a(:q.$b@m'bHS!(Q([!)S]chmW5fosTF`jLbD!rF>h>!1s0gs'5P4rFPt -dJs"`JP=TfqB`4;nOnF-^eNl_p*K3$JPbQ,r[%Llr@n3js!I_!s#L'Es%35dr`&r$[f:j~> -dJs"kJRm;AqD,-`nR@&\^h)S9s"X:(n7$r^k\0rNrAX[=r[.Y$r]^?Or_ -dJs#>J[a1=qI?V:n[="Y^prI6p1!O"J\1-Xra>\7rFc+.s'5P4r+5n -d/Wn_JP>B'!$C](qB_q3pIk?VfhMH>!#pJ`n4i+>p*K#tJPb]0r[%Llr%S-jr[.Y!r]1!Es%32c -s&K)%[f:j~> -d/WnjJRn(W!%RJKqD+jXpL -d/Wo=J[asS!)rBrqI?>2pU9q-fsq$j!(VUMn@7\jp1!?rJ\19\ra>\7r+H%.r`oJ4r+5n -d/Wk^JP>B'!$Ci,q'DV,qb-"[f:j~> -d/WkiJRn(W!%RVOq(eOQqdSr'nR_<&!$m,,m:GWns"X=)k$imTn7_hWr&=O;"@OR`1Gf"os$m#_ -r_ibrs&]5$!(jX!J,~> -d/Wl -d/Wh]JP>B'!$Cr/qB_G%btJ*VJPGK*n4WL%iCe_uohbW6r$hRus#0lss!I_!s#L'Es%35dr`/u$ -!*-K-J,~> -d/WhhJRn(W!%R_RqD+@Jc"$f"JS"1Zn7)DPq(go -d/Wi;J[asS!)rX$qI>i$c*m[CJ[k'Vn@&(#iO4 -d/Wh]JP>?&!$D)3q'D8"bY/!UJPGE(oh5$*ge32pq,%&:r$hRur\a`rr[.Y!r]1!Es%32cs&K)% -!+!#4J,~> -d/WhhJRn%V!%RkVq(e1Gb[^]!JS"+Xoj[qUq(g`7JS=jmr\FC1rBC3K!%.\$s$$EOs%WMkr`/u" -s&K+u[Jta~> -d/Wi;J[apR!)rd(q.#Z!bdRRBJ[k!TosXU(gpVdGq7HW6r+#\ -d/We\JP>?&!$D26qB_7ubY/!Uj@oUqKh_D:p*J?aJPc;Ar[%Ikr@n1.r[.Y!r]1!Er^m,cs&K)% -s'<&4J,~> -d/WegJRn%V!%RtYqD+1Eb[^]!jCJ<>Kk:*is"X:(fOBDFrFl0cr&=R -d/Wf:J[apR!)rm+qI>YtbdRRBjL>1dKt-ufp0u[_J\1lmra>Y6rFc(Cr`oJ4r+5k;s'Pe0s&K(o -[Jta~> -d/Wb[JP><%qB_4tb"MdSj@oUqLJ7aqr(H`0dn>6gr[%Ikr@n.-s!I_!s#L'Es%35dr`/u$s'<&4 -J,~> -d/WbfJRn"UqD+.Db%(JtjCJ<>LLgHAr*o[\pbL<-JS>$ -d/Wc9J[amQqI>Vsb-q@@jL>1dLU[=pr3l<.e$ah>ra>Y6rFc%Bs'5P4r+5n -d/W_ZJP><%r[!Oua\2[Rj@oUqMbX-sr_)r2c:`jfr[%Ikr@n.-#!4.Y0JGLIr^$QUr_NPos&oA- -!*6N-J,~> -d/W_eJRn"Ur\BIEa^bAsjCJ<>Me2iCraPm^pbL-(KkUH@r&=R -d/W`8J[amQraUqtagV7?jL>1dMn&^rrjMN0cF/G=ra>Y6rFc%B"D_7I@Uf7Hs'u%8s'#G&s%WPR -[Jta~> -ci6#ND7Mg!#srm!$7.pq^);9p*IgRM,=0tr$hRur&+[>-71E#r]1!Er^m,cs&K)%r`oM& -[Jta~> -ci -ci!(Z(Z!)S]cqd]]8p0u.PM7`apr+#\[1`Ir+5k;s'Pe0s&K%n!%#&Q -J,~> -ci3"O%m\h!#srm!$77sq'Ph^_b6"dr[%Llr%S%,s#U3-r\4@4s$?WUs%iVos&oA-!+3,5 -J,~> -ci -ci\7r+GqAs(_U>raPh;raYt8r`]>%s%WPb[/YX~> -ci0!P"iqi!#srm!$7D"p*TS]^.XVcr[%Llr%S(-rAt-/0JGLIr^$QUr_NPos&oA-s'N/5 -J,~> -ci -ci\7r+GtBrG)I>@Uf7Hs'u%8s'#G&s%`Sb[/YX~> -ci)tPYK.k!#sol!$7M%oHsG]\P&5br[%Llr%S(-r&ap4r]1!Er^m,cs&K)%ra#P2[/YX~> -ci -ci\7r+GtBr+l=Ar+5k;s'Pe0s&K%ns%'NsJ,~> -ci&sQ;,=l!#sol!$7V(ng=;]ZqHiar[%Llr%S(-r&Y*J0JGLI5sbKAs%iYpr`T8,s'Gb+ -[/YX~> -ci -ci\7r+GtBqeQ4@rFPt -ci#rR8(Unru6M`r_;o0r["a@T2>M5r$hOtrAOR8"@+Xp3B@()s%32cs&K)%s'>Y3!F\a1 -s*t~> -ci -ciT=b)1r+#Y;rFu.Es'kn;s'u%8s'#G&s%`Sb!C.X3s*t~> -ci,s'Gb7Zi>O~> -ciO~> -ci3uCn54bGi4#2c_m]V9J[k,Y!+Mt:UV$M5r+#Y;rFu+D!+Pk;s'u%8s'#G&r_EJa!'mmjJ,~> -cN!>SJP=rpSP?sp!#pScm3\Z&Vbm@=r$hOtrAOR8r]gT?3B9Akr^m,cs&K&$s'>Y3s'`86J,~> -cN!>^JRmYKSRoZK!$m5/m5(SJVeH&br&=O;rC$QUr_ -cMmlBn54bGhm]/d_RBM8KXgD[V7*;6ra>Y6r+GtBq.ft>rFPtO~> -cN!;RJP=ooT2!0r!#pYem3\K!X&/dAr$hRur&4L8r':9Br^$QUs%iVos&oA-s'Pb6Zi>O~> -cN!;]JRmVJT4PlM!$m;1m5(DEX(_Jfr&=RO~> -cN!oBn54bGhRB,e_RBM8L:HV]TXLo5ra>Y6rFc%BphTe=s'u(9r`]>%s%`Sbr]mgiJ,~> -cN!8QJP=lnU.rEs!#pbhm3\;qY>G3Er$hRur&4L8r'1HT3B9Ak8P/nWs&K)%ra#P2s'Yn+Zi>O~> -cN!8\JRmSIU1M,N!$mD4m5(5@YA!njr&=RO~> -cN!lAn54bGh7',g^pa;6M7Dq`S$oN4ra>Y6rFc%BpM9_=s'u%8s'#G&r_EJas$-Q1Zi>O~> -cN!8QJP=flUeSTt!#pnlm3\)kZV^WIr$hRur&4L8r'1BR3B9Akr^m,cs&K&$s'>Y3raE,4J,~> -cN!8\JRmMGUh.;O!$mP8m5(#:ZY9=nr&=R -cN!i@nPOkHgUEug^UF25NO\@dQ+"$2ra>Y6rFc%BpM9_=raYt8s'#D%s%`Sbr]mdhJ,~> -cN!5PJP=ckVG4g!!#sch!$8I@m3[of[o!&Mr$hRur&4L8r':9N!'1$Es%35dr`/u$s'>Y3rF*#3 -J,~> -cN!5[JRmJFVIdMQ!$pE4!%>0bm5'i5[qParr&=R -cN!i@n54bGg:*rh^UF25hREP^ZafC5OLDX1ra>Y6rFc%Bp1sV%s%`SbrBR[gJ,~> -cN!2OJP=`jWD1'"!#sch!$9'Qs!@>4m3[`a]28JQr@.Xur&4L8r':6M"?Ab68P/nWs&K)%ra#P2 -rF*#3J,~> -cN!2ZJRmGEWF`bR!$pE4!%>css"j=Zm5'Z0]4h1!rAXX -cN!f?n54bGfsdrj]sdu3hREP^`45hWosjC"Mmg70ra>\7r+GqAokXP -cN!/NJP=]iX%g6#!#sch!$96Vs!@;3lm@H[^JOnUr@.Xur&4L8r':3Ls$?WUs%iYpr`T8,s'P_5 -!*-?)J,~> -cN!/YJRmDDX(AqS!$pE4!%>s#s"j:YlnaB*^M*U%rAXX -cN!c>n54bGfXIok]XIl2hREP^agh@\oXO6uL:4k/ra>\7r+GqAoP=J -c2[&MJP=ZhX\HH%!#s`g!$9E[s!@51m3[BW!)(4+r[%Llr%S%,rB'pBrC$o_5s\(6:J^sis&oA- -s'P\4!)p3'J,~> -c2[&XJRmACX_#.U!$pB3!%?-(s"j4Wm5'<&!+NiZr\FF2r'($HrCZu_rDEi"9heM` -c2[Z=n54bGf=.ll]XIl2h7*G]cFEmao!n'tJ[PQe_n5nUrF>b%s%`Sb -r'16.ZN#F~> -c2[#LJP=WgY>)W&!#s`g!$9T`s!@)-mj, -s'P\4Z2]=~> -c2[#WJRm>BY@Y=V!$pB3!%?<-s"j(Smk]N(qdRTZs"aL2r'('Ir(?o_r)*Yt9heM`r`/u"s&T+t -s%rYd!'mghJ,~> -c2[W -c2ZuKJP=TfohG?X_bI[8!#s`g!$9ces!?l'o-T#]p.NV+r[%Ikr%S(-r&ajBr'gWX!($TUs%iYp -r`T8,s'PY3Z2]=~> -c2ZuVJRm;Aok"&3_e$Ah!$pB3!%?K2s"ikMo.tr,p0u6Zr\FC1r'('Ir(?o_r)3Pp!) -c2[T;n54bGe[P4]r3sSR\[MQ/h7*G]fXUrkkd^4pJ[Y9\be*j^r+#Y;rFtk=s(VOFraYt8s'#D% -s%`SbrBUB?Z2]=~> -c2RbckT5:Ke4fX3p.WP%\P)uBge@bih+dnqj%]4oJP5N+cqs]fr$hOtrAOR8rBU9Lr^m;Z8P)]R -r`/u$s'>Y3qI-Z/J,~> -c2RbnkUD'ne7A>cp126U\RY[cggpI6h.?UBj(7p?JR\.YctND6r&=O;rC$QUrD*8gr`'(q;GpLp -r`B,#s&8qor(?uSZ2]=~> -c2RcBqGQcZJ[a.Y6r+GtBo5">?"D)(Q?XNe9 -s&K(os%*&Qs#%(]J,~> -c2[eckoPCLdS0O4nP%/$\4clAge@bii_BG!h+dbnJP5<%e56,jr$hOtrAOR8rBU9LrC[)]r_NPo -s&o>,s'PY3!*-<(J,~> -c2[enkp_0odU`5dnRTjT\7>RbggpI6iar-Gh.?I>JR[qSe7eh:r&=O;rC$QUrD*8grDikqr`/u" -s&T+ts%rYds$-QEZ2]=~> -c2[fBqGQf[J[a(:qmc1*bdb0l!(YhS!)V[bs'slpqdb8dlaGY6r+GtBo5";>s'u%8s'#G& -r_EJarBUB?!%tPVJ,~> -c2[bbkoPCLd7jR7l:fW#[nQf@_+jqUf1l8lJP5,ufMMPnr$hOtrAOR8rBU9LrCR8h8P)]RY3qI'@nZ2]=~> -c2[bmkp_0od:E8gl=A=S[q,La_.EX&f4Ftr&=O;rC$QUrD*8grDa&(;GpLp=',B% -s&8qorC[&S!#;d=J,~> -c2[cAqGQf[J[a%9s1%@'d^Zcqs$so8s'sZjs($\hk-ipSra>Y6r+GtBo5";>#&%LV?XHu?r`/tn -s%*)Rr\XZhZ2]=~> -c2[bbkT5:K[7o.G[S-Z?`D-@YdS59ci_/''r[%Ikr@n.-r&ajBrC-WV"AVN[:J^sis&oA-s'PS1 -YlB4~> -c2[bmkUD'n[:Ij"[U]@``F]'*dUdu=iaU\Vr\FC1rBC-Ir(?o_rDNPn"B\Q" -c2[cAq,6]ZJ[`%rf!r/t!(Y#%s%`Sbr':6= -YlB4~> -bl@YakT5:KYtWnH[7gQ>b"_m^bY,s'PS1 -YlB4~> -bl@YlkUD'nZ"2U#[:B7_b%:T/b[l?7iF:_Yr\FC1rBC-Ir(?o_rDNPns&]8!r`/u"s&T+ts%rYd -rBRUeJ,~> -bl@Z@q,6]ZJ[_nngUOZ#!(Y2As's9_J[XOGi4Jtrr+#\ -bl@V`kT5:KY"[_I[7pT>cV=Ec`_D"Wi(N--r[%Ikr@n.-rB'pBrC-WVr_ -bl@VkkUD'nY%6F$[:K:_cXm,4`as^1i*tb\r\FC1rBC-IrCZu_rDNPnr`B;$ -bl@W?q,6]ZJ[_ekhmg)'s$tDFs's'YJ[XLFjLbD!r+#\$Co*s%`SbrBU9< -YlB4~> -bl7Ycr?(5EJP<(;j\<'T!#sB]s!>KUJP4imkYV7)r$hRur&4L8r':3Lr(?o_s%iVos&oA-s'PP0 -!)p-%J,~> -bl7Ynr@7"[JRkckj^kc/!$p$)s"hK&JR[JFk\0rNr&=R -bl@T>q,6]ZJ[_Ygjg_Y+!(YMJs'rmTJ[XFDke$h%r+#\ -bl@\cr?(5EJP;q7l:nQX!#sQbs!>9OJP4ckm83d.r@.Xur&4L8r':3Lr(?o_##\5m -bl@\nr@7"[JRkWgl=I83!$p3.s"h8uJR[DDm:cJSrAXX -bl@Q=qGQcZJ[_MclF=./!(Y\Os'r[NJ[X@BmCW@*rF>b$=p+r_EJarBU<= -!@ebes*t~> -bl@Ybr?(8FJP;e3mS0u\ru9`frZnsIJP4`jnPK32r@.Xur&4L8r':3Lr(?o_"B&#kY3 -pgF//YQ'+~> -bl@Ymr@7%\JRkKcmU`\7s!6B2r\:lnJR[ACnS%nWrAXX -bl@Nb$Co*s%`SbrBU9< -!%P2PJ,~> -bl@VarZC>FJP;Y/oM)P`!#solp`oh[[nVEHfh;$8r[%Ikr%S%,rB'pBrC-ZWr(d8i!)NSos&oA- -s'PP0s'Do/J,~> -bl@Vlr[R+\JRk?_oOY7;!$pQ8pb;ar[q1,"fjaYgr\FC1r'($HrCZu_rDNSor)iu&!*0#"s&T.u -s%rVcrBUB@YQ'+~> -bl@NY6r+GqAoP=D?r+Q+B!+5_/s&K(os%*&Q -rAFU)YQ'+~> -bl@S`rZC>FJP;P,pe@qc!#t2tng+2T[S; -bl7Yor@@jskUD'nRq2;*Y@IVYl=KN@r[5-cJR[8@qIoj`r&=O;r'^KUr(d2grDibtr`TM+='&F' -;cEZlrC[#Rr\gt[J,~> -bl7ZBqG[2fn54bGS%&1&YI=L%lF?Cmr_9i2J[X4>qRc`7r+#Y;r+Ye=rG)7Dral@A>$=p+:/:dZ -rBU<=r[P,OJ,~> -bQ%M`r?(5EJP;D(rCsIh!#tE%lm2NM[7u3Fge7Ynqb[8\#YQ'+~> -bPqPnr[[pskUD'nQXp&+Y@IVYn7Cr@r?o!aJR[DD!AQD-B`9db2uGl<7/T[T:An)h5q\' - -bPqQAqG[2fn54bGQacq'YI=L%n@7gmrCs]0J[X@B!FfR9]Dc49A,L!ho -8GtgO2?,-+*3]Bm~> -bQ%J_r?(5EJP:Vg_+XhJpeBiqr#N1>JP4imr[%J=r[%Ikr%S(-r&ajBr'gQVr(d2g#Zar*=]o$9 -?XNS:ra#RoYQ'+~> -bQ%Snr[[pskUD'nJRlT-!$q>NjY?Z\[:OnuhI?I?r+Q'br&=O;rC$QUrD*8grDibtrE9J2='&F' -;c?Lhr(?oQr\a`kYQ'+~> -bQ%TAqG[2fn54bGJ[`J)!(Zgoj^A!\[CCdrhR$=p+:/4>N -r':3 -bQ%G^r?(5EJP:Vg_+XnL9hjLjr#N+ -bQ%Snr@@grkUD'nJRlT-![M>gh_G$VZXn\siaMmBr\OIcr\FC1r'('Ir(?o_r)3Jnr)io$#$Y/1 -<`W-tr_WPcr':6>Y5a"~> -bQ%TAq,@,fmnnYFJ[`J)!_9c1hdH@VZabRpijJhrraYlY6r+GtBo5";>r+Q%@#%Lh? -bQ%D]r?(5EJP:Vg_FsqKh'Dkr$hOtrAOR8rBU9LrC[#`r)3Pq!*0#$s'>Y3 -pgO,-Y5a"~> -bQ%Pmr@@grkUD'nJRlW.!$pATq^8[\JR[bN!&=C-rFl0cr&=O;rC$QUrD*8grDieur*',)!*B/# -s&8qorCZuQrALhYJ,~> -bQ%Q@q,@,fmnnYFJ[`M*!(YjQqb=B+J[X^L!+Pk9rO`&:r+#Y;rFtk=rG):Er+>t=!*]A%s%`Sb -rBU9 -bQ%A\r?(8FJP:Vg_FsqSh'Dh;Z;#mCktCgur_WS?r$hRur&4I7rBU9LrC[#`r)3Mp#$"f/?!UfE -pgO),Y5a"~> -bQ%Mlr@@grkp_0oJRlW.!&34`qBrR[JR[kQ!&==+rb29dr&=R -bQ%N?q,@,fn54bGJ[`M*ggKtQZFGIol*^S$qIBN:ra>Y6rFc%Bo5";>rFl+@raQ.9 -bPqPcq&oYYkT5:KJP=3[q'4iDr(Qr-Yt]dBlq@+"s%r\@r$hRur&4I7rBU9LrC[#`r)3Jos&oA- -ra5G/rE]InY5a"~> -bPqPor@Imtr[R+\JRj=Bb%1<%iA(7@qBrOZJR[tTpbVq`r\FC1rBC-Ir(?o_r)3JnrE/u$rEK;& -s&8nnrCZuQrAFWjY5a"~> -bQ%K>qG[2fn54bGJ[`e2fjO`eqG"6)J[XpRpga?9ra>Y6rFc%Bo5";>rFl+@rF>k-s%`ParBU9< -r@.d\Y5a"~> -bQ%ScqB5_YkT5:KJP=Kcng!3&YYB[An4WO&s!IeCr[%Ikr@n.-r&ajBr'gQVrD*8grDj,+ -=]o$9?XNS:r*HN*J,~> -bQ%Sor@Ipur@7"[JRj=BdU_o&k:uX?qBrLYJR\+X!&=:*s"sdir\FC1rBC-Ir(?o_r)3JnrE/u$ -rEBJ2<`W-t:f.*_r':0 -bQ%H=qG[2fn54bGJ[a(:fO4B]qG"3(J[Y'V!+Pb6s()2Bra>Y6rFc%Bo5";>rFl+@rF6%> -b5_JbqB5_YkT5:KJP=cklQbdBmS*HtYYB[Ao1Sj)r?qRjr@.Xur&4I7rBU9LrC[#`r)3Jo#$P/4 -?!UfEpgO),!*QH(J,~> -b5_Jnr@Ipur@7"[JRj=Bg19M'm4n$>qBrLYJR\4[!&=:*rAFR0rAXX -b5_?bn;#%1G2:/4>N -r':3 -b5_GaqB5_YkT5:KJP>&sj'R@pIk6,r?qRjr@.Xur&4L8r':3Lr(?o_r)3Jos&oJ, -?![G:pgO),!*QH(J,~> -b5_Gmr[e!ur@7"[JRj=Biah+(oJ,N>q'W@WJR\@_!&=7)rAFR0rAXX -b5_?bn;s'Pn.:/:dZ -rBU9 -b5_D`qB5_YkT5:KJP>?&h';bAi(WqeY"aI?qFgT0q^2@ir$hOtr&4L8r':3Lr(?o_rDNPor`TG- -?!UfEpgO),s&lK(J,~> -b5_Dlr[e!ur@7"[JRj=Bl=A^)qD$o=q'W=VJR\IbpbVe)!&FI2r'($HrCZu_rDNPnrE0#%r*0/* -"BJH":f.'^rBU9=s!b)MJ,~> -b5_<;q,@,fmnnYFJ[apRemRFFq+\$%J[YE`pga24!+>_7r+GqAoP=A>rFl.Ar+#_6"BA2l84`YD -rAFL&rueHDJ,~> -b5VGdrZV([r?(5EJP:VgmnDCZfM))]Y"aI?rCco3q'Z(hr%S%,rB'pBrC-WVrD*;hr)Wc"s'>Y3 -pgO),s&f=kXoEn~> -b5VGor[n("r@@grkUD'nJRn4[feD&hq'W=VJR\Re!&=:*q)/(/r'($HrCZu_rDNPnrE0#%r*0,) -s&8qor(?oQr&4R(!#;X9J,~> -b5VHAqGd8hqGQcZJ[^3>n$gtXfXLZNY.0%krO)]8q.'53r+#Y;r+Ye=r+c1DrFYt;rF#Y#s%*&Q -rAFL&ru_:TXoEn~> -b5_JdrZV([rZC;EJP:VgmnD[bcqO6UX\F@>pa,\_r$hOtr&4L8r':3Lr(?o_rDNPorEB5,ra5G/ -r*KD+XT*e~> -b5_Jor[n("r@@jsk:(smJRn4[i@rVhq'W:UJRSU6q(qe'r&=O;r'^KUr(d2gr)N\trEB,'r*05# -r_WPcr':0 -b5_KAqGd;iq,6ZYJ[^3>n$h7`d'rgFXgiqj!+Pb6pgjG;rF>b -b5_Jdr?;"[r?(5EJP:VgmnDpia@uCMXA+CApEfV_q^MFsr&4L8r':3Lr(?o_rDNPorEB2+s'PP0 -r*KA*XT*e~> -b5_Jor@Rt!r[[pskUD'nJRn4[kV1(gq'W7TKjk$:pbV_'q`"F:r'^KUr(d2gr)N\trEB,'r*98# -s%rVcrBU9=r[FrKJ,~> -b5_KAq,I2hq,6]ZJ[^3>n$hLgaLCt>XLNtm!+P_5q.9P;raYk=r+Ye=r+c1DrFYt;rEob5:/4>N -r':3 -b5_Gcr?;"[r?(5EJP:Vgn4`9q^eFSFX%e@Bpa,_`qC2=rrAOR8r':3Lr(?o_rDNPorEB2+s'PP0 -r*K>)XT*e~> -b5_Gnr@Rt!r[[pskUD'nJRn7\mkDOfqBr=TLLU$7q)/"-r'('Ir(?l^rDNPnrE0#%r*0&'!`Mol -r(?oQr&4L&XT*e~> -b5_H@q,I2hq,6]ZJ[^3>n@.jo^pj/7X13qnpga,2r+>t=r+GtBo5"8=rFl.Ar+#\5s',V#84`YD -rAFL&r?/3AJ,~> -aoD>br?;"[r?(5EJP:VgnP&X$\4l`>W_JCEpa,\_qC2=rrAOR8r':3LrCZu_rDNSpr*')*ra5G/ -r*KA*!(O'iJ,~> -aoD>mr@S""r@@grkUD'nJRn:]p+X!eqBr:SMdcZ@q(qe'qD\=9rC$QUr(d2grDibtrEB/(q- -aoD??qGd8hq,6]ZJ[^3>n[J4"\@;rFtk=rG)7DrFZ" -aoD;ar?;"[r?(5E!$QneJP>Z/r?JO?qAlP0O%]6opa>kdr%S(-r&ajBr'gQVr(d2grDri"rE]G1 -pgO),rE0+aXT*e~> -aoD;lr@S""r@@grkU?K_JRj=Bo47eRY[mSXWF_-"!&=:*pbhk+r'('Ir(?o_r)3Jnr)io$rEK&% -s%rVcrBU9=r@%^IXT*e~> -aoD<>qGd8hqGQcZ!*Ol^J[b6[rEuk=qG!s!O1#$tq.'22qIT_;r+GtBo5";>r+Q%@rF>b5rET@n -r':3 -ao;>dr?M:bq&oVXkoTgIJP:VgT1o)%WD/OKpEfV_q'c:or@n.-r&ajBr'gQVr(d2grDri"rETG3 -?he(,?N"$-s*t~> -ao;>pr@\($r@Imtr@7%\r[`V2JRkB`qBr7RP%"DGpbV_'q)8:6rBC-Ir(?o_r)3Jnr)io$rEK&% -!F&Cu9)V$P2Z5)*s*t~> -ao;?ArDi\nq,@,fn590fJ[^3>T==YkWOS,"!+P_5q.9A6r+GqAo5";>r+Q%@rF>b5rEKA*8GkaO -2>\m%+KPNm~> -aoDAdr?M:bq&oYYkoTaGJP:VgTM52&W(iOMpa,_`paQ4nr\47.r&ajBr'gQVr(d2grDri"rETJ4 -?XNS:r*K;(X8d\~> -aoDApr@\($r@Imtr[R.]r%*D0JRkEaqBr4QQ"'MEq).q+s#L'Br'^HTrD*8grDibtrEB/(pfml) -:f.*_r':0 -aoDBArDi\nqG[2fnPT3eJ[^3>TXXblW48,$pga,2ph'G:r+Yb -aoD>crZh@bqB5_Yl5oaEJP:VgUJ1J(VbNRPpa,\_q'l7ms"O@/r&ajBr'gQVr(d2grDrl#r*B>1 -!+5M0r*K;(!)T`rJ,~> -aoD>or\".$r@Ipur@7(]q(.)-JRkNdq'W(OR:6.Nq(qe'q)A74s$$?Kr(?o_r)3Jnr)io$rEK&% -s'#J"r(?oQr&4I%!#M^9J,~> -aoD?@r`/bnqG[2fnko3cJ[^3>UUU%nVmr/'!+Pb6pgs85qe,h@o5";>r+Q%@rF>e6r*98(!(d#Q -rAFL&r#btTX8d\~> -aoD;brZh@bqB5_YlQ5dDJP:VgUeLS)VG3USpEfV_q'l4ls"O@/r&ajBr'gQVr(d2grDrl#r*B;0 -pgO&+r)j"hX8d\~> -aoD;nr\".$r@Ipur@7+^pFLl+JRkQeq'W%NSRMRRpbV_'q)A43s$$?Kr(?o_r)3Jnr)io$rEK&% -r`f:mrBU6 -aoDUpp.oVRW2*!+P_5q.9A6qIf_?o5";>r+Q%@rF>e6r*95'r':3< -q^VLd!#)F5J,~> -ao;>frZqFer?;"[r?(AIoc\l[JP;n6q&Q>,TM,#*q'YteqC;P"r&4I7rBU9LrCZu_rDNSprEB/* -r*];.r*K;(!Dkt\s*t~> -ao;>rr\4:'r@S""r@@grlm_TYJRj=BV.BBLVIcE0!&=7)q).t,qDeO>r'^HTrD*8grDibtrEB/( -q-bd;s*t~> -ao;?Cr`8ttqGd8hq,6i^oiZjTJ[_Jbq+[`rTXFi0pga,2q.BG8r+Yb -aSu5erZqFer?;"[r?(DJo-&ZYJP;q7q&Q;+UJ(A.pa>kdq()A$r&ajBr'gQVr(d2grDrl#rE]>/ -!+Yh5r*K;(s%o`qJ,~> -aSu5qr\4:'r@S""r@@grm4%WXJRj=BVI]KMV.HE2pbV\&q)A+0r'^HTrD*8grDibtrEB/(q- -aSu6Br`8ttqGd8hq,6l_o3$XRJ[_Mcq+[]qUUKr-pgs85q.KV>o5";>r+Q%@rF>e6rET8&!)ib\ -rAFL&r#l"TWrIS~> -aT)8erZqFerZV([r?(GKn0*?VJP<"9q&Q8*Vb?e2pa>kdq()IrrAOU9r':0KrC[#`r)3JorEB/* -r*TM7q-j/,r)s%rWrIS~> -aT)8qr\4:'r[n("r@@grmO@WVJRj=BW+>]OUh-H5!&=:*pbhk+q)SI9rC$TVr(d/frDieur*'&' -q- -aT)9Br`8ttqGd8hqGQu`n6(=OJ[_Seq+[ZpVmZS7q.'22q.BG8s(M@Fo5";>rFl+@rF>e6rET8& -!)ib\rAFI%r#l"TWrIS~> -aT)8er?V=drZV([r?(GKmid6UJP<%:q&Q8*W_<(4q'Ytepac=pr\j^:r':3Lr(?o_r)3JorEB/* -r*]P7q-j2-r)s"qWrIS~> -aT)8qr@n1&r[n("r@@grmO@TUJRj=BWFYfPUh-Q8!&=7)q).t,pc8=7r^?]Wr(d2gr)N\tr*'&' -q- -aT)9BrDrksqGd8hqGQu`mob4NJ[_Vfq+[ZpWjVn:pga,2q.BD7rb2:FoP=A>rFl+@rF>e6rET8& -s&/h]r&+C%r#ktSWrIS~> -aT)5dr?V@er?;"[r?(GKm3.$SJP<(;q&Q5)Y"SL8q'Yqdq()@os#0g;r':3Lr(?o_r)3JorEB/* -r*]M6qI0;.r)s"q!(O!gJ,~> -aT)5pr@n4'r@Rt!r[[psmO@NSJRj=BWatoQULgT;!&=7)q).q+q)S@6s$ZfXr(d2gr)N\tr*'&' -q- -aT)6ArDrntq,I2hq,6o`m9,"LJ[_Ygq+[WoY."(7q.9>5q.KV=s(V+@r+c1Dr+>n;rF#S/r)iqr -r]pE>r$qUerY>JIWrIS~> -aT)2cr?V@er?;"[r?(JLlQLgQJP<+kdq()=ns#0g;r':3Lr(?o_r)3Jo -rEB/*rF#P5qdKA.r)s"q!'[F_J,~> -aT)2or@n4'r@Rt!r[[psmj[QRJRj=BX(;&STk1N=!&=7)pbhk+q)S=5s$ZfXr(d2gr)N\tr*'&' -q- -aT)3@rDrntq,I2hq,6ralWJeJJ[_\hqG!ZnZF0gEA7YOHpgs85q.KSn;rF#S/ -rE/tqs$6N?q^VLdrY>JCWrIS~> -aSu5fr?hLhr?M7aqB5_Yn/gd:JP:VgX\AR3ThVpf!%%Rjr$D+cq'l+iq(2G*r':3Lr(?o_r)3Jo -rEB/*rF#M4qdKD/r)rtpWW.J~> -aSu5srA+@+r@\%#r[e!ur@7:ckU_9qJRklnqBqtJ[:0+jpbV\&q)A+0q)\FGr(d2gr)N\tr*'&' -q- -aSu6CrE0&"rDi\nq,@)epeg6XJ[^3>Xge.$Tt%M=!+Ytn;rF#S/ -rE/qp!'pH>r$qUer>)C4J,~> -aT)8fr?hLhr?M7aqB5_YnK-g9JP:VgY"\[4TM;si!%%Lhr?_7eq'l(hq()LtrB'pBrC-WVrD*8g -rDrl#rE]A0qd]M3r*K;(r(sBmJ,~> -aT)8srA+@+r@\%#r[e!ur@7=djt)'oJRkooqBqqI\RGOnpG;V&q)A(/q)SL -aT)9CrE0&"rDi\nq,@)eq,-9WJ[^3>Y.+7%TX_P@!+Yn:rF>Y7q.BD7q.KbCoP=A>rFl+@rF>e6 -rET;'qbmGZrAFL&r#knQWW.J~> -a8c/er?hLhr?M:bq&oVXnfHj8JP:VgY>"d5T2!$m!%%Ces!@IgpaQ"hq(2Lsr]C$CrC-WVrD*;h -r)Wc"rE]A0qIBG3qd02'r(sBmJ,~> -a8c/rrA+@+r@\($r@Imtr@7@ej=GjmJRkrpqBqnH^1%'sp+uM%pc&"/q)\L;r_!)`rDNPnrE0#% -r*/u%rEK,#!)!2Tq_n@$r"l@4J,~> -a8c0BrE0&"rDi\nq,@)eqGHYIF@&T=DVD!+Ye7s'tk9ph'>7q.TbBokXJ?rFl.Ar+#\5 -rET;'qGI;YrAFI%r#knQWW.J~> -a8c,dr[.Rhr?M:bq&oVXo,cm7JP:VgYtXs6T2!-p!%%=cpa>kdq()7lr%7q1r':3Lr(?o_rDNPo -rEB/*rF#G2r*fM0r)rqo!'[C^J,~> -a8c,qr\FF+r@\($r@Imtr@7Cfi[fXkJRl#rq'VeG_.!C!p+uJ$q)A+0q)\F9s%<2arDNPnrE0#% -r*/u%rEK)"r':0 -a8c-Ar`K,"rDi\nq,@,fqGH6TJ[^3>Z+'O'T=D_G!+Y_5pgs85q.KM:r+bq@r+c1DrFYt;rF#S/ -rE/knr&+C%r#knQ!"Gq-J,~> -a8c)cr[.RhrZh@bq&oYYo,cd4JP:VgZV:08Sk[0s!%%=c!%@Xjq'l(hq(2Cp!'C-Er'gNUrD*;h -r)Wc"rE]A0q-sD:rF,V1qcWhnW;hA~> -a8c)pr\FF+r\".$r@Imtr[RLgh^j=hJRl)tq'VbF`F8g%p+uJ$q)A(/q)\C8!)!2br)3GmrE0#% -r*/u%rEK)"!)ib\r&4F$r"l=3J,~> -a8c*@r`K,"r`/bnqG[2fqbc6RJ[^3>Za]a)T")bJ!+Y_5!+u% -a8Z,gr[7Xkr?V@er?;"[r?(VPhB@GDJP -a8Z,tr\XR/r@n4'r@Rt!r[[pso-rNIJRj=BZt/t[SRooP!&=1'pbhh*q)S73qE+aMrD*8grDibt -rEB,'q- -a8Z-Cr`T2%rDrntq,I2hq,7)ehH>E=J[`"qq+[EiagM1Zp14/7qdoP7q.KM:qJ,b?rG)7DrFYt; -rF#S/rE/kns%*,Hq^VLdq\H.1J,~> -a8c/gr[7XkrZqFer?;"[r?(YQg`_5BJP -a8c/tr\XR/r\4:'r@S""r@@groI8QHJRj=B[:K(\SRp#S!&=1'pGMb*q)S73q)eXArD*8grDibt -rEB,'q- -a8c0Cr`T2%r`8ttqGd8hq,7,fgf]3;J[`%rq+[EibdIL]p14)5r+5\9q.KM:q.fY>rG)7DrFYt; -rF#S/rE/kns%*)Gr$qUeqA-%0J,~> -a8c,fr[7XkrZqFerZV([r?(\Rg*)#@JP -a8c,sr\XR/r\4:'r[n("r@@grodSTGJRj=B[Uf1]S7U)W!&=.&pGMb*pc8.2q)n[Ar_EAhrDibt -rEB,'q-q^MCY!#;I4J,~> -a8c-Br`T2%r`8ttqGd8hq,7/gg0'!9J[`(sq+[BhdC'$bojmo2rakn;ph0D9q.fY>rG)7DrFYt; -rF#S/rE/knr^d#Gr$qRdq\B/FW;hA~> -a8c,fr?qRkr?V=drZV([r?(_Sf-,]=JP -a8c,srA=L/r@n1&r[n("r@@grp*nTEJRj=B\7GC_Rq:,Z!&=1'p,2Y)pc8.2q)nU?s%`JirDibt -rEB,'q-r$hIY!!0%uJ,~> -a8c-BrE9,%rDrksqGd8hq,72hf3*[6J[`.uq+[?ge[>Hfp13o0!+kt -`rH#er?qRkr?V@er?:tZr?(bTeKKK;JP7s'bh3r)rhlVuM8~> -`rH#rrA=L/r@n4'r@Rt!r@@grpF4WDJRj=B\RbL`Rq:5]!&=1'p,2V(q)S73q)nO=!)EGirDibt -rEB/(pg!c%q,mPms$6K?r$hFXVuM8~> -`rH$ArE9,%rDrntq,I/gqGR;ieQII4J[`2!q+[?gfX:cip13l/!,)+>q.KM:q.fS -`r?#hr[Igor?hIgrZh@bq&oYYpE%d,JP:Vg\kMr@R8)I0!%%=cp*f\bq()7lpau=rr'gQVr(d2g -rDri"rE]A0q.';6!+Ge3qcW_kVuM8~> -`r?#ur\ja4rA+=*r\".$r@Imtr[RXkdk$&\JRl?&qBq\BggU7 -`r?$Cr`];(rE0#!r`/bnqG[2fs&%6JJ[^3>]!qN1RCM%\!+Y_5p1F>;qI]P9q.TPb5rET;'q,7)\!&a[(q]P\MVuM8~> -`r?#hr@.^nr?hLhr?M7aqB5_Yp`@g+JP:Vg]1i&AQqcL3!%%=cp*f\bpac.kq(;Crr'gQVr(d2g -rDri"rE]A0q.';6!+Gb2r)rekVuM8~> -`r?#urAOX3rA+@+r@\%#r@Ipur@7Rkd4BiZJRlB'qBqYAi*l[@p+uD"pc%t.q)\=6q*4dTrDibt -rEB/(pg!c%q,mMl!'pE>r$hCWVuM8~> -`r?$CrEB2'rE0&"rDiYmqG[2f!)_-HJ[^3>]=7W2R(2(_!+Y_5p1F89r+>_:q.TS=oP=D?r+Q%@ -rF>b5rET;'q,7)\!&aX'r#kbMVuM8~> -`rH&hr@.^nr?hLhr?M7aqB5_Yq&[g)JP:Vg]hJ8CR8!Z-j@]J!od9A\pF5ngq(2=nq(Vh(r^H`W -r(d2grDri"rE]A0q.'85!+Ge3r)rek!'[=\J,~> -`rH&urAOX3rA+@+r@\%#r[e!ur@7Ulc7FNWJRlH)qBq\B!&N\F!&=.&p,2S'q)S73q)nI;s$?W^ -r)NYsrEB/(pg!c%q,mJk!'pH?r$hCW!!0"tJ,~> -`rH'CrEB2'rE0&"rDi\nq,@)ec<5_-J[`>%qG!Bf!+b0!!+Y\4p1F27rFYkb5rET;'q,7&[!&a[(r#kbM!"Gk+J,~> -`rH#gr[Idnr?hLhr?M:bq&oVXqB!j(JP:Vg^J+GDSk],/k">\#od9A\pF5ngq(2=nq(Vb&s$ciX -r(d2grDri"rE]A0q.'54r*K8'peUrSVuM8~> -`rH#tr\j^3rA+@+r@\($r@Imtr@7Xms"2MIJRj=B^L[-fSn7gUk$e -`rH$Br`]8'rE0&"rDi\nq,@,fs'!]NJ[^3>^UO#5T"+]/k-b8"ojmc.q.K_>q.KM:q.f_@rG;LL -r+c.CrFZ" -`rGufr[Idnr[.Rhr?M:bq&oVXqB!g'JP:Vg^eFPEUJ:P1k=Ye$p*TJ]p*obeq(2=nq(V_%!(HfX -rD*8grDri"rE]A0q.'54!,)49r)rbjVZ2/~> -`rGusr\j^3r\FF+r@\($r@Imtr@7Xmr[lDHJRj=B^h!6gULj6Wk@+EGp+uD"p,Db,q)\=6q*4dC -!)i_prE/u$rEK&%rEK)"q,.2cr&4I%pD9_,J,~> -`rH!Ar`]8'r`K,"rDi\nq,@,fr`[TMJ[^3>^pj,6UU^,1kI(A#p13l/m:Z6.q.f_@r+l@Kr+c1D -r+>n;r*]J.rE/knq+(KMr$qUepD0Y+J,~> -`r?#kr@@jrr?qRkr?V@er?:tZr?(nXaWZ4/JP -`r?$#rAad7rA=L/r@n4'r@Rt!r@@grq^MFpc7FNWJRlT-q'W(Op+trB!&=1'p,2P&pc8.2q)nI; -qEb$ZrE/u$rEK&%rEK)"q,75crAOR&pD3f2VZ2/~> -`r?$DrEK8)rE9,%rDrntq,I/gqbm>qc<5_-J[`J)q+[csp13Er!+Y_5p1F#2s(M7Aq.TS=q.ohC -r+c1Dr+>n;rF#P.rE/knq+1NMr@7^fpD*`>VZ2/~> -`W#ojr@@jrr[7Xkr?V@er?:tZr?(qY`Z]n,JP -`W#p"rAad7r\XR/r@n4'r@Rt!r@@grr$hIobq+EVJRlZ/q'W7Tnh]WAs"X4'p,2P&pc8.2q)nI; -q*FpYrE/u$rEK&%rEK)"q,75crAOO%pD3f2VZ2/~> -`W#pCrEK8)r`T2%rDrntq,I/gr)3ApbuoV,J[`P+q+[s#nmq*qs'tb5p1F#2rFl+Aq.TS=q.oeB -r+c1Dr+>n;rF#P.rE/knq+1NMr@7[epD*`>VZ2/~> -`W,rjr[[prr[7XkrZqFer?;"[r?(qY`$'\*JP=!Uq&Q\6mj7Bp!%%:bp*fV`pac+jq(;CrqD/(, -rC[#`r)3JorEB,)rF#G2q.BV=rEfD)pJ:l\5,S@1~> -`W,s"r]'j7r\XR/r\4:'r@Rt!r[[psr$hCmbq+EVJRl]0q'WCXmkaB@!&=.&p,2P&pc8+1q)nI; -qEb-KrDieur*'&'pg!c%q,mGjr_*2Jr$h@V! -`W,sCr`f>)r`T2%r`8ttq,I2hr)3;nbuoV,J[`S,q+\*'mptjp!+Y\4p1F#2r+Q%Aph9Je6r*92&q,7#Zr]g?2r#k_L!=oX:s*t~> -`W,oir[[prr[7XkrZqFer?;"[r?(tZ_BFJ(JP=$Vq&Qk;lQu$n!%%:bp*fV`pFH%jq(;Crq(hq* -r_!,ar)3JorEB,)rF#G2q.BS -`W,p!r]'j7r\XR/r\4:'r@S""r@@grr@.Ckc7FNWJRl`1q'WR]lSJ$>!&=.&p,2P&pGr%1q)nI; -q*G!Ir`/o!r*'&'pg!c%q,mGjrCd,Jr$h@VrrN0$VZ2/~> -`W,pBr`f>)r`T2%r`8ttqGd8hrDN;lc<5_-J[`V-q+\9,lX]Ln!+Y\4p1F#2qIok@q.TS=q.oeB -rb_XLrFl+@rF>e6r*92&q,7#ZrBL92r#k_Lrso)=VZ2/~> -`W,oir@@jrr?qRkr?V=drZV([r?)"[^`e8&JP='Wq&R"?kU#dm!%%=codKP`p+,qiq(;Crq(hk( -s%<5br)3JorEB/*r*]>1q.BP;ra,M*pJCl[V>l&~> -`W,p!rAad7rA=L/r@n1&r[n("r@@grr[IFjc7FNWJRlc2q'W^akVMd=!&=1'oelJ&p,Vq0q)nI; -q*FpGs&K#"r*'&'q- -`W,pBrEK8)rE9,%rDrksqGd8hr_i>kc<5_-J[`Y.q+\E0k[a7m!+Y_5ok*r2pLjP>q.TS=q.oeB -r,)IKrFl+@rF>e6rET8&q,7#Zr'101r#k_Lrstt+J,~> -`W#onr\"."r[Idnr[.Rhr?M:bq&oVXruSisJP:Vga@uCM^.mZ7oLf01p*TJ]p*o\cq(2:mq(VY# -qD/+DrD*8grDrl#rE]>/q.'54qe,q8r)rbjr^WgbJ,~> -`W#p&r]C' -`W#pDr`oD+r`]8'r`K,"rDi\nq,@;knQO1?J[^3>aLCt>^:<67oX4a0p13l/p1O)4!,;:CphKV? -qJ?"F!,DIGr+>n;rF#S/r)ibmq+1BIs!mphpD3`=V>l&~> -`W,rnr\"."r[Igor?hIgrZh@bq&oVX!$8`qnOrp+JP:Vgg._>`_G/r7pIbK4od9A\p*o\cpal4m -q(VV"qD/+/rD*8grDrl#rE]>/q.'54qe,q8r)rbjrC<^aJ,~> -`W,s&r]C' -`W,sDr`oD+r`];(rE0#!r`/bnq,78kmomt=n[AL%J[^3>g:-oQ_RSN7pU1'3ojmc.p1O)4rb;7C -q.f_@q/#nE!,haKr+>n;rF#S/r)ibmq+1BIs!mphpD3]l&~> -`W,omr\"1#r@.^nr?hIgrZh@bq&oVX!$8]po1]'*JP:Vgh+[Yc`_GA;s!IM6!%%:bp*fV`p+,nh -q(;Crq(hb%!&a^MrDNPorEB/*r*]>1q.BJ9s'GV+pJCiZ!'[7ZJ,~> -`W,p%r]C*=rAOX3rA+=*r\".$r@Imtr@.drmOc[7o47bNJRj=Bh.6@0`b"!_pgO4XoeZ;!p,D\* -r&ajl&~> -`W,pCr`oG,rEB2'rE0#!r`/bnq,78kmTRkh7*5T`jjl9ppL04ojmc.p1O)4r+Z+C -q.f_@q/#kD!,haKrFYt;rF#S/r)ibmq+1?Hs!mphpD3`=!"Ge)J,~> -`;fimr@\("r@.^nr?hLhr?M7aqB5_Y!$8Wnoh>*'JP:VgiCs(gb>$n@q^2/4!%%:bp*fV`p+,nh -q(;Crq(hb%r\a[MrDNPorEB/*r*]>1q.BG8!+,S+pJCfYV#Pr~> -`;fj%rB(! -`;fjCrET>+rEB2'rE0&"rDiYmqGRAllWVS:osa[!J[^3>iOAYXbIH8:qR-B6ojmc.p1O)4qe?%C -q.f_@q/#kDrbh^KrFYt;rF#S/r)ibmq+1 -`;fflr@\("r[Idnr?hLhr?M7aqB5_Y!$\ippe:9&JP:Vgj\5IjcV<=Dpa5o3!%%=codKM_pFGqg -q(;Crq(he&r&+LLrDNPorEB/*r*]>1q.BD7r)r_irC<[`J,~> -`;fg$rB(! -`;fgBrET>+r`]8'rE0&"rDi\nq,78tl<;G8pp]iuJ[^3>jgY%[ca_S;r3cT8p13i.p1O,5phBbA -q.f_@q/#nEr,2OJrFYt;rF#S/r)ibmq+19Fr#k\Kr=>_(J,~> -`;fckr\"."r[Idnr[.Rhr?M:bq&oYYrZ\coqFp<#JP:VgktLmne4njIoHsQ1!%%=cp*fV`p+,hf -q(;Crq(hb%q_\@KrDNPorEB/*r*]>1q.BD7!+u.3pJCcXV#Pr~> -`;fd#r]C' -`;fdAr`oD+r`]8'r`K,"rDi\nqG[Atl<;G8qR?H.nQLLQJ[amQq+]AKdq&Hd!+Y_5p1F#2p1X/6 -q.f_@q/#kDqecCIrFYt;rF#S/r)ibmq+19F!&aZspD3Z;V#Pr~> -`;f`jr\"."r[Idnr[.Rhr?M:bq&o\Zr$&Qmr(Q>uJP:Vgm7d -`;fa"r]C' -`;fa@r`oD+r`]8'r`K,"rDi\nqc!Dsl<;G8r3uK+nQLLQJ[b$Uq+]PPcX[#cojmc.p1O)4p1aPB -qeGqBq/#kDq/6.Cr+#\5rET;'pepoYpcea9r#k_Lr!rc:V#Pr~> -`;f]ir\"1#r@.^nr?hIgrZh@bq&fYZq'*9kr_2DsJP:Vgn4`Wuh+cfRmNqo]p*TJ]p*o\cp+6.p -r%7^uq(he&q)/13r_iYprEB/*rF#D1q.BD7s(;13pJCcX!'[4YJ,~> -`;f^!r]C*=rAOX3rA+=*r\".$r@Imt!%Rdsl7L:4rabXRnh#E'JRn7\q'Y!0bqf>Vp+uD"p,D\* -p,i49r&jd>q*FjEq*kipe1WRr$h@VquQj!V#Pr~> -`;f^?r`oG,rEB2'rE0#!r`/bnr) -`;f]ir@\("r@.^nr?hLhr?M7aq&oDUPp(]PJP>]0q&SHhh^._ -`;f^!rB(!ipe1TQr@.IWqZ -`;f^?rET>+rEB2'rE0&"rDiYmrDWGpl<;G8n6>EdJ[^3>oXFWjijd6Dr+5Y6p1F#2p1X27qJ6(F -q.oeBq/,kDrau( -`;]fsq_J+&r[[prr[7XkrZqFer?;"[!$h1]\01hrg`_5BJP>i4q&STli$Ie -`;]g*q`k$@r]'j7r\XR/r\4:'r@Rt!r[Rt%p+GV^cR]&Jln7f[JRj=Bpgj1Jk%2dnq)%t)oelG% -p,Vk/pcSU>q*4[@q*XsHrC?lkr*/u%rEK&!q,6u\rBgK4pD -`;f[>rET>+rEB2'rE0&"rDiYmr_rJol<;J9ls'!`J[^3>pp^&nk.&ZHq.9G6ok*o1p1X27phKkE -q.oeBq/,eBs(;1=rF#S/rE/hmq+19FrAFQspD3T9U]5i~> -_uKQgr\"."r[Idnr?hLhr?M7aqB5e_p*,SAJP:Vgqb6f+lV6:`mO%WTr$M(bp*o_dp+5qj!%n"# -q(he&q)/%/!)ibrr*'&)rF#D1q.BD7r+>q2pJC]VU]5i~> -_uKQtr]C' -_uKR=r`oD+r`]8'rE0&"rDi\nrDW>ml<;P;kZdR\J[^3>qmZAqlaY2Mp1=/4ok*o1pLs87pM0bD -q.oeBqJGhA!+u1>r*]J.rE/hmq+19Fr&+HrpD3T9U]5i~> -_uB]rqD/%&r@@jrr?qRkr?V=drZV+\r?M"[N?NjHJP?);q&Soui$Ie -_uB^)qEOs@rAad7rA=L/r@n1&r[n("r[[q#p+GV^dOaE2nL]<&JRndkq'YWBbqnrJr\O7*p,D\* -p,ht2!'pBFq*FgDq*k-MrEK&%rEK&!q,6u\r'LB3pD -_uKOs0qeun$pVQo4@o3p1F#2p1X/6p1jYE -qJ5nCq/,\?rF>b5rET;'pepoYpcn[6rZLqNq@ -_uK`rqD/%&r@@jrr?qRkr?V=drZV.]q]kbXN$3aGJkYt\oM+6imO%BM!%.Cfp*o\cp+5qjpb;M! -q(qh(qDeL9r`8r#rE]A0pga,3ph0J9s&o(mq*tEKU]5i~> -_uKa)qEOs@rAad7rA=L/r@n1&r[n("s"!t"oe,M]eL]Q0nh#E'Jn4[)oOZ9'mkb##p,2P&p,Vh. -pHAI?r'1!Cq*XsHqFCQWr`f/&rEK&!q,6u\qa1<3pD -_uKL;r`oD+r`]8'r`K,"rDi\ns&8Ajl<;_@hHTPSJ[^6?q+^Lkc"-9S!+be8p1O)4p1a8:rG;CI -q/#kDno"JGraYk6rET;'pepoYpcnX5ruh%Oq%!H7U]5i~> -_uK]qq_J+&r[[prr[7Xkr?V@er?;(]q'5PVM'7FDL.qFapeBWlmO%?LodKM_p+,hfpFc1oq(he& -q).t-r]:'[r*'&)rF#D1q.BD7qI]b1pJCZUUAo`~> -_uK^(q`k$@r]'j7r\XR/r@n4'r@Rt!!%[juoe,M]f.>W.nL]<&L1L-.pgqZ*mPOSop,D\*p,i"3 -q`t*Eq*FjEq*k*Lr^m,opg!c%pfR>ipe1HMs!d[Yq#[;jJ,~> -_uKL;rET>+rEB2'rE0#!r`/bn!)r8hl<;eBg0=)NJ[^BCqG$apb[g-Qok*o1p1X/6pM9YBrG24F -qJGb?rbqgHr*]J.rE/hmq+19FqDJ9qpD3Q8UAo`~> -_uKZpq_J+&r[[prr[7XkrZqFer?;+^p*98TL*;+AMG3jer(Z)qmO%?Lo-j;]p+,hfpFc1oq(hb% -q)/".rAssZr*'&)rF#G2ph';6q.9V0pJCWTUAo`~> -_uK['q`k$@r]'j7r\XR/r\4:'r@Rt!p+Q+ll7L[?eLpADJRjXKqBu2PbqncEo/65#p,Vh.pHA=; -s$- -_uKI:rET>+rEB2'rE0&"rDiYmlW`XXficj0nQLLQMRWFVr4(!^mUc!%p1O)4p1a8:q/$(Hq/#kD -no"DEs'tt7rET;'q,6uYpcnR3!$M"Op^a,!J,~> -_uKZpqD/%&r@@jrr?qOjrZqFer?;._oHX&RKHYn?N_K6hh^.\;mO.QRp*o\cpFQ"jpFuCuq(qh( -qDe@5!*9#$rE]A0q.'23ph0A6pJCTSUAo`~> -_uK['qEOs@rAad7rA=I.r\4:'r@S""!&4(!p+GV^gFU](nh#E'Nb%r5bVSZDnhp,"p,Vk/p-&.8 -q*FgDq*k-Mqagclpg!c%q,mDipe1BKpD -_uKF9r`oD+r`]8'rE0&"rDi\n!*nenl<;qFcs-'EJ[^ZKq+](DmUbs$p1O)4pM'>:mqhf:no">C -!+Yq7rET;'q,6uYpcnO2pD3K6UAo`~> -_uKWoqD/%&r@@jrr?qRkr?V=dr?;1`ng!iPJK]S -_uKX&qEOs@rAad7rA=L/r@n1&r@S%#r@mpup+GV^h(6c&nL]<&P%=D:cnk)HnMTtupGqq/p-&.8 -!(-KIqEt'Iq+(9Qpg!c%q,mDipe1BK!%IXYp]:ErUAo`~> -_uKC8r`oD+r`]8'r`K,"rDi_orESYml<<"HbZjU@J[^fOqG#=ImUbp#ok4#4p1a59pM9hHq/#nE -nS\2Ar*]J.rE/knpdk0Epb`$opD3N7!"G\&J,~> -_Z0NnqD/%&r@@jrr?qRkr?V=drZV:an0@WNJKb"9JP;A'q]4fFmO%?Ln0muZp+,hfp+H(npbMY$ -q)/".q)SL3IUAo`~> -_Z0O%qEOs@rAad7rA=L/r@n1&r[n.$q_7^sp+GS]i%2o$nL]<&Q=Tk?dkgDKn29nup,Vh.p-&.8 -r^-KJq*XsHqFC?Q!)*,mrEK)"pepl[pHnTjpAt -_Z0:7r`oD+r`]8'r`K,"rDibpqcrGkl!!"Ja'8(;J[^rSqb>OMmUbm"p1O)4p1a59pMBhGqeZ(F -no"8A!-%mErET;'q,6uYpcnL1pD3K6!"G\&J,~> -_Z0Kmq_J+&r[[prr[7Xkr?V@er?2.`mN_BKJKat8JP;J*r#P&KmO%?LmO7cXp+,hfp+H(npbMY$ -q).t-qE"U+rF#G2ph';6pLXD7pJCTSU&TW~> -_Z0L$q`k$@r]'j7r\XR/r@n4'r@J"#q(VLqoe,M]i[hu"nL]<&R:Q4Cf/)hOmPX\sp,Vh.p-&.8 -r'L?Jq*XsHq+(9Qr_*)mrEK)"pepl[pHej:pD -_Z0:7rET>+rEB2'rE0#!r`/kqq-<2hl<<1M_cuY7J[_&Vr(YdRmUbfup1O)4p1a59pMBbErG;:H -nS\2Arc%mFr*92&q,6uYpcnL1!%@RWpCEttJ,~> -_Z0Hlq_J+&r[[prr[7Xkr?V@er?2.`m3D9JJKak5JP;V.r>k;Pm3_6KmjRfWp+,hfpFc.npbMY$ -q).t-qE"R;s'#>+rF#G2ph';6pLaG7pe^ZSU&TW~> -_Z0I#q`k$@r]'j7r\XR/r@n4'r@J"#pb;Cpoe,M]j=J"tnL]<&SRh[Hg,&.Rmkt&'pc%n,p,i"3 -p-AI@s$?HJq*k*LqFUWYqHWu'q,mDipe1?Js"sKep&^rfJ,~> -_Z076rET>+rEB2'rE0#!r`/kqpg!)gl<<7O^0C,2J[_2ZrCu!VmUbj!oOml2p1a8:p2'SBs(qLJ -nS\2ArG_dEr*92&q,6uYpcnL1s![XXp(*ksJ,~> -_Z'U#q`+F-r\"."r[Idnr?hLhr?M7al6H!HJKae3JP;_1rZ1MTmO%?LmO7ZUp+,hfpFc.npG2P# -q)/".q)\F9!*];+rF#G2q.BA6pLaD6q,$`SU&TW~> -_Z'U-qaC9Fr]C'!($EJq*k-Mq+:KWqcs)(q,mGjpIk6Ir\XEeo`CieJ,~> -_Z'U@oNhH$rEK8)rE9)$r`8ttrD`Jql_0rE/knq+16EpGMpuq%iW6U&TW~> -_Z0X#q`+F-r\"."r[Idnr?hLhr?M:bs!6kSp*,&2oc\l[UeLe/od/uPmO.HOnL=/^pFQ"jp+Z4r -!&XO.q).t-qE"I8r*B8/q.'54pLj54raPY!p.#*HU&TW~> -_Z0X-qaC9Fr]C' -_Z0X@oNhH$rEK8)rE9)$r`8ttr`&o'p0?lel!!=S[9N0)J[_Gas%VH_mUbj!nRqQ/pM'>:p2'J? -!,hXMnS\2AqJc@;rE/knq+16EpGMpup_NQ6!"GY%J,~> -_Z0X#qDe=,r\"."r[Idnr[.Rhr?M=cr$:SQp*.s/rZ\*\JP<"9!$1h\mO%?LmO7QRp+,kgp+H%m -pG2b*q_S%*q)J43q)eX@rE]A0q.'54pLj54rF5S!og]!GU&TW~> -_Z0X-qF(0Er]C'ho/Q_.p,Vk/ -p-&+7pHn[Cq*k*LqFUKU!) -_Z0X@o3M?#rEK8)rE9,%rDrkss&Ao%p0?ofl!!CUYZpX$J[_Se!);QcmUbfun7VH.pM'>:p2'J? -s).^NnS\2Api-1:rE/knq+16EpGMmtq%iW6!"GY%J,~> -_Z0U"qDe@-r@\("r@.[mr[.Rhr?D:cqBYAOochp0qBD^YJP<.=!$qIgm3_6KmjRWRp+,hfp+H(n -p+lS'r%n.+qDe:3qE4a@ra#J1q.'54pLj54r*oM!oLGSSJ,~> -_Z0U,qF(3FrB(! -_Z0U?oNhE#r`f>)rE9,%rDrks!*&f#p0?lel<#$mUbj!mq;?-p1a59pMBP? -rGMOMno"8ApMg+:rE/knq+16EpGMjsqA/]6T`9N~> -_>jL!qDe@-r@\("r@.^nr?hIgrZ_Cdpa#/Moci!2p*-7TJPs'>S2q.'54pLj54qdTCuoLGSSJ,~> -_>jL+qF(3FrB(!hml:;-pc8%0 -p-&.8p-SOAq*k*LqFUKUrD!;srEK)"q,6r[pHna6qA8c*T`9N~> -_>jL>oNhE#r`f>)r`T2%rDrnt!*&`!p0?lel< -_>jHuq`+F-r@\("r@.^nr?hLhr?M"]l6H!Hfcfl0UES2^hB\1VpE]bbmO%?LmjRTQoIKVdp+H(n -p+lG#!&OF.q)J43q)nR=!+#M1q.'54pLj54qI9=uoLAmFT`9N~> -_>jI*qaC9FrB(! -_>jI=oNhE#r`f>)r`T2%r`8ttp0IAsl -_>jEtq`+F-r\"."r[Idnr?hLhrZhFip*AoJp*/67m38AM[3V)4p*BG[ng -_>jF)qaC9Fr]C'p-\XEqFC?QqFgZZr*/u!q,6r[pHn^5q\Si*!!/bmJ,~> -_>jFkDr)ibmq+16EpGMdqq\Jc6!"GV$J,~> -_>jEtqDe=,r\"."r[Idnr[.RhrZhChp*AoJp*/<9kT[#M[3V;:pE];Up*T/SmjRTQnLO;apFc.n -pG2G!s#0a3q)J43q)nL;!'pKjq.'54pLj54q-s4to1,GQJ,~> -_>jF)qF(0Er]C' -_>jFq,7#ZpHSC0q'c+Uo+.JnJ,~> -_>jBsqDe=,r\"1#r@.[mr[.Rhs!.Cfp*ArKoci<;r?LMMXs(5!U.k;,lm;HSmO.ENmj[]UpFQ"j -p+Z4rp,)_+rAFC1q)\@7qEFmDra>P3q.BA6pLa20r(urSTDsE~> -_>jC(qF(0Er]C*=rAOU2r\FF+s"=1)p+c7pp+Q(kl7M`]r@aQkr@PH4U1F!PlnS;lmPO>hml9hu -s#Kg8p-&+7pHnR@q*k*Lq+:EUqG$rcr`f2#q,6r[pHnX3r"no*TDsE~> -_>jC;o3M?#r`f>)rE9,%rDrquqd/u%p0?lel<: -p2'J?p29hIoksSDo5O\Kr`Jtoq+16EpGM^or"ei6TDsE~> -_>j?rqDe@-r@\("r@.^nr?hIg!$h:dp*ArKociB=p`nuHZQZb&WD*"2jsBsQm3h?NmO@TT!%mpr -p+H%mpG2J"q_nC1qDe:3qE4R;rBUBjq.'54pLj54pL=(tnOK5OJ,~> -_>j@'qF(3FrB(! -_>j@:oNhE#r`f>)r`T2%rDinuq-Nc#p0?lel<=!dpfs7l[UQ5Fp0d#krFG;,mq2!#mq;]9pM'>: -p2'J?pMTkHp29\Eno4PIs&f(pq+16EpGM[nr>+o6TDsE~> -_>jV4q.BA6pLa,.rD<&T!'[%TJ,~> -_>j=&qaC9FrB(!\i%Y3fmPO>hml9bsr&jd: -p-&+7pHnUApI4mJqFUKUqG$i`!*f8$q,6r[pHnR1r>5#+!!/_lJ,~> -_>j=9oNhE#r`f>)r`T2%rDrZ!p0IAsl -_#O3pq`+F-r\"."r[Idnr?hLh!%7Iep*AoJp*/WBmj%$?]HO^/[7pV4q.BA6pLa,.rD<#S!'[%TJ,~> -_#O4%qaC9Fr]C'1#pFtGYr%mh!mksMmmlL8, -q`FL7pH\F=pI"[Dq+(6PqFgZZqG7/rq,mGjpIk6Ip,)^anH&[kTDsE~> -_#O48oNhH$rEK8)rE9)$r`0#+p0RGup0?lel!"'hmp)Vl[UQYRpL)idr*o/,mq2!#mqDN3qe>b> -pMBP?pMT_Dq/6"Ho5OPG!*K%pq+16EpGMXmr>+o6!"GS#J,~> -_#O3pqDe=,r\"."r[Idnr?hOir[7Cdp*AoJp*/]DlQbR:_'-64]hJ)EhBhkBoI'&Tmj[ZTp+?4o -p+H(np+lA!pGVh,q)\@7q*+X?!(?Wmq.BA6pLa)-rD<#ST)X<~> -_#O4%qF(0Er]C'I+rA"+%hD+^[oJGtnml9_r -p,r:8p-&.8p-SL@pI5*Qqa^HRqFgWYqG7/iq,mGjpIk6IoecU`nH,<^J,~> -_#O48o3M?#rEK8)rE9)$s&T,+p0RGup0?lekun'ilWg>l[UQqZrF,M0hI6+eoO[H&mq;'%p1jP? -p1sD=p20PApM^(Nqel4Jno4DEq,7#ZpHSC0odKbSnIM5kJ,~> -_#FC(q*4^8qD/%&r@@jrr?qRks!7Lip*TJYl6H!Hl65:7kT[th[Nrsgmj-a9n0[oXmO7KPmjmr\ -s"*jrpFu:rpGDV&pc/+2q)nL;qEY$JqIB>5pLj54oO@ern40)MJ,~> -_#FC.q+:ENqEOs@rAad7rA=L/s"O@-p+uCsoe?(mp+GV^s"NXiX=gq9a^jors"N:an2'hrmPXDl -mlL#%s#]p;pH\F=pI"[DrCHiVqFUKUqG$f_r_iSpq,6r[pHnL/rYP&*T)X<~> -_#FC:q-rlW`XXs'=hnXBW,Bag^eKs'X\ln7)0(mUkm"mqD9, -s(V1BpMBP?pMT_DrG_[PqJYqDpMp%-q+16EpGMRkrYFu6T)X<~> -_#OF(q*4^8q_J+&r[[prr?qRk!$q@fp*TJYlQc'Hm31I6k9A%l[NsBsm3LC3l6cBUmO7NQmORcY -!%djsp+Z1qpGDV&pc/(1qE4R;qEY!Iqd]G6pLj54o4%_rmmiuLJ,~> -_#OF.q+:ENq`k$@r]'j7rA=L/!&44*p+uCsp+Z1noe,V`q_74eYV*@=eR\&%q(gqal8/;omPXGm -mQ0i"!'Bp -_#OF:q-+rEB/&r`B/$q-Wi%p0IAslll=0X%mUkp#mV)*) -!,;1Cp2'G>pMT_Dqf)OPq/>hCp2Tt-q+16EpGMOjrtb&6T)X<~> -_#OC'q*4a9qD/"%r[[prr[7XkpF#Y]p*ArKocifIhBV5.c69VAi(WPaeKs6&s!R1^mj[]Umk+Ml -pb)7opG2G!pGVe+q)\=6qEFa@qaC?lq.BA6pL`u*s%r2T!'["SJ,~> -_#OC-q+:HOqEOp?r]'j7r\XR/pGDS"p+c7pp+Q(kmOe/ekV'=r[P?]NmP4,bkV;*Rs"s+#ml9bs -ml^S5pc\=9pHnR@pI4mK!(ZiVq+LQYqG@&er)ibmpIk6Inhg@_n,`RjT)X<~> -_#OC9q-+rEB2'rE/f$p0RGup0?lemTT?jkZkN"[US1(mU>Nmk[EL\s(1S0mq;*&mqVN2 -p2'J?p29VCq/?=Oq/>hCoPse,q+16EpGMLirtb)7!"GP"J,~> -_#O@&q*4a9qD/%&r@@jrr?qOjp*]P\p*ArKocilKg*>c)dil.FlqHdlcm@Qr!%I=amO@TTmk+Di -qC_IqpG2G!pGVb*qE"F7qEF^?qaC?lq.BA6pL`u*s%r/S!'["SJ,~> -_#O@,q+:HOqEOs@rAad7rA=I.p,)J!p+c7pp+Q(kn1F5ckV'J![P@,Zm4mWVmP3TT!&j7&mPsYr -ml^J2qE=O;pHnR@pI4jJ!(ZlWq+LQYq,$rdr)ibmpIk6Inhg@_mfEIiT)X<~> -_#O@8q-!^!+bM0mUu!%mqVK1 -p2'J?p29VCpi$4NqJYqDo5X\+q+16EpGMLirtb&6!"GP"J,~> -_#O@&q*4^8qD/%&r@@jrr?qRks!dXhp*TJYl6H!HnfcO.kT\V%[NtWAm3Kh#h^81KnL3iTmjmiY -pFlFsp+Z4rp,)M%pGht0qE4R;qEXjErF>Y8pLj54nR;JpmRNiJJ,~> -_#O@,q+:ENqEOs@rAad7rA=L/s#9X.p+uCsp+Z.mp+GnfmP*iX^+QiKpgibHf.utQh_Y*enMTbp -mlKo"pHJL -_#O@8q-rDNG^pHSC0nL+AQmLPlgJ,~> -_#O=%q*4^8qD/%&r@@jrr?qUlr$h@fp*TJYl6H!HoHDR+kT\b)V'O#+h^7tEodK5WmjmiYoIp1r -p+Z4rpGDS%pGht0q)nL;qEXdCraYb9pLj54n6uAomRNiJJ,~> -_#O=+q+:ENqEOs@rAad7rA=O0r&=@,p+uCsp+Z.mp+Gthl7hBS_ChZ>bVK#Lh_Xm_oel.smlKo" -oKN7;p-A@=pI"XCpIP9Ur(6]WqG$f_pJUumq,6r[pHn@+!#P&)Sc=3~> -_#O=7q- -^]44$q*4^8q_J+&r[[prr?hRlqC2.dp*TJYl6GsGpEBM^cQhTsi$#?N^a7hamO@oZmj[]UmOdr^ -!&"""p+lA!p,;Y)q)\=6qEF^?p-epjq.BA6pL`l'm73`IJ,~> -^]44*q+:ENq`k$@r]'j7rA4L0qD\.*p+uCsoe?(moe,tjr\ -^]446q-+rEB/&!*K)*pL!W#p0I>rl -^]41#q*4a9qD/"%r[[prr[.[mpaPqbp*TGXlQc'Hq'#P[cQhWtj<:uX`?j1akUHEXmO@TTmk*u] -p+Z1qpGDV&p,Mh.qE4R;qEX[@!+Yh:pLj54mpZ9!mRI7@Sc=3~> -^]41)q+:HOqEOp?r]'j7r\OU1pc%q(p+u@rp+Z1noe-%lq(_+fk:b"2[kYiWp,(hdkVi>rmPsYr -ml^&&p-A= -^]415q-+rEB2'!*K#(pL!W#oj.8rlA0mqD0)mq_W5 -p20PApM]bEqJlORno3u9!)iV`pHSC0mjJ/TmLK:,Sc=3~> -^]41#q*4^8qD/%&r@@gqr[7Cgp*]P\p*ArKocj8Vod.p/k9B.6[j9?Ie0a-'s![=bmOR`Xmk=>f -p+lA!pGVb*pH&+4qEFa@njN:cpLj54mpZ9!m7..?Sc=3~> -^]41)q+:ENqEOs@rAaa6r\X=-p,)J!p+c7pp+Q(kq^qIqkV23RcRu[]d5'uDlSIKXs#'7)mQ0f! -mlpD0p-SL@pI4gIpIb6SqG$f_l;I@LpHn=*!$LY1!!/YjJ,~> -^]415q- -^]4."q*4^8qD/%&r@@jrr[7^rp*o\_p*TJYl6H!Hr?:YVcQhWtm3/qaga18nh^SLRn1!fVmk*u] -o.]knpGDV&pGhn.q)nI:qEXX?!(luupLj54mpc -^]4.(q+:ENqEOs@rAad7r\XX8p,D\%p+uCsp+Z.mp+H:qn1j,\kV(F<[kZYnh_ajZh_sg\mlKo" -mlp>.p-SL@pI4gIpIYEZqFgWYqG?c]!*8nnpIk6Imkk%em/igXJ,~> -^]4.4q- -^]4+!q*4^8qD/%&r@@jrs!R^ppF5e`p*TJYl6H!Hrup_TcQhTsnfbIfkU"=th^S:LoI92Ymk*u] -nhBbmpGDV&pGhn.q)nI:qEXU>s%3'!pLj54mpc8um73]HJ,~> -^]4+'q+:ENqEOs@rAad7s"sX6pG_e&p+uCsp+Z.mp+H@slnR]XkV(R@[k[)%dkpeTh_saZmQ0f! -mlp;-p-SL@pI4gIpIbEYr(Hi[qG?`\s&StopIk6Imkk"dm/igXJ,~> -^]4+3q-)r*f>-p0[N"p0IAsl -^]+:0q*t0EqDe@-r@\("r@.aoq^_Cip*]P\p*AoJp*'J`kU"P"kT]^D[j:Pk`$X@jnLF8^mjmiY -mk=/apG2G!pGVb*pH&(3qEF^?o0iOKqe#S8pL`l'rE/GWSH"*~> -^]+:0q+UTUqF(3FrB(!Nfqbm/]pHn=*r?1J/SH"*~> -^]+:0q,mGtoNhE#r`f>)s&o5-pL*]%p0RGup0?le!+4&XkZlqJWFEqYrF4YpnR_H-mqD0)mq_E/ -pMKVApM]eFno=ABlZ)lBqagHGpGMCfr>Y,7SH"*~> -^]4=0q*t0Eq`+F-r@\("r@%^oq().fpF#Y]p*AoJp`fV_kU"P"k9BdH[j:u"_'[qdlRMc\mOR`X -mk=,`pG2G!pGVb*pH&(3q*+X?o0iIIr+>\9pL`l'r)iAW!'ZqQJ,~> -^]4=0q+UTUqaC9FrB(!Hdr)38^pHn=*r#kD/!!/ViJ,~> -^]4=0q,mGtoNhE#r`f>)!*T,+p0dW%p0RGuoj$lgr*RcUkZm(N[:6s^r+4htlXfs+mV)'(mq_B. -pMKVApM]eFn8\2AluDoAr(-QHpGMCfr#>&7!"GIuJ,~> -^An4/qF:9FqDe=,r@\("r@.Ikp*o_`p*TGXlQc6MpEo)McQhTs!$UhWaqFUWtpLj54mpc2sm73ZGJ,~> -^An4/qFp]VqF(0ErB(!au&WIk;MpI4gIpIb3S!)*/_qG?`\qH!PmpIk6Imkjqbm/idWJ,~> -^An4/qH3Puo3M<"r`f>)pL3c'pL!W#oj.8rmp#NncX#a>k$,<(b%'sQk@E[crb(\5mV;6,mqhZ6 -p29VCpMfP?q/GY>qJcC(pHSC0mjS)Qm15]dJ,~> -^An4/q*t0EqDe=,r\"."r[@h!p+,hcp*]P\p*ArKr$(_ZkU"P"l6<\`eL&]ni$n^YmjmiYmk=,` -oJ6.tp,;Y)pH&%2!'g?Eq*=O>pdtHspLj54mpc/rm73ZGJ,~> -^An4/q+UTUqF(0Er]C'?ar_iJ`pHn=*q]P8-S,\!~> -^An4/q,mGto3M?#rEK8)!+Y_2p0dT$p0RGup0@,lo3]gLkZmCW[pmltk%3pji+2n$mqD0)mq_B. -oPO>?p2B\EluDoAlZ)]=r^ccJpGMCfq]"o5S,\!~> -^An1.q*t0EqDe=,r\"."s!dmupFGqdp*]P\p*AoJs!$kXkU"P"mio4ei?lbti%"UUngj,[mk=/a -nhTqrp,;Y)pH&%2r]gpdtHspLj54mpc,qm73ZGJ,~> -^An1.q+UTUqF(0Er]C'Mdq`F@0 -mQBr%n3HP2pI"XCpIP$NpItQ]r([#anl#6`r_iJ`pHn=*qB5/,S,\!~> -^An1.q,mGto3M?#rEK;*rF>S1p0dT$p0RGup0@2nmU+:GkZmR\[pn<+g1Bkdi+;dunn@H+mq_E/ -nnn,=p2B\El>c`@lZ)]=r^ccJpGMCfqA\f4S,\!~> -^An.-q*t0EqDe@-r@\%!!%IdspFGqdp*]P\p*AoJ!$^_UkU"P"o-1XilmBe&h^\:NodfJ_mk=,` -nM9hqp,;Y)pH&%2rBL6EqEXU>p.>9rpLj54mpc)pmRI7@S,\!~> -^An.-q+UTUqF(3FrB's;!&j^9pGqq+p,)J!p+c7poe-:tlSI`ZkV23Ro.IL-lnm$IqDR4jofMh- -ml^&&mm-D0pI"XCpIP$NpItN\rD!,bnl#0^s&/SapHn=*q&o),!!/ShJ,~> -^An.-q,mGtoNhE#rEB8*qd]A/p0dT$p0RGup0@5olX.tDkZm^`[pn]6cXlr`hduInok -^An+,q*t0Eq`+F-r@\("!%I^qpFGqdp*]P\p*ArKr[6qWkU"P"p`d0npa3j,h^\(Hq((kbmk=,` -n1s_ppGV_)pH&%2q`k*Eq*=O>oLT$ppLj54mpc&omRI7@S,\!~> -^An+,q+UTUqaC9FrB(!+ -mQBr%mm-A/pI"[Dp.4pMpItHZs%W;co2>3]!)iPapHn=*p`Su+!!/ShJ,~> -^An+,q,mGtoNhE#r`]A+q.'/-p0dT$p0RGup0@;qk$QG?kZmme[po,B_drgYhdu7hq.T22mq_B. -n87o;pM]bEkAgN@lZ)Q9!(ciKpGMCfp`&W3!"GFtJ,~> -^An+,q*t0EqDe=,r@\("pFZ(hpF5e`p*TJYm3DHTkpGU?c6N93_Bnn&_^Y@2j=9CGr%%4fmk=,` -mkXo#pb__'p,Mh.pH848!'^6EnjN(@pLj54mpc&om73WFJ,~> -^An+,q+UTUqF(0ErB(!cBcr&X:/ -mlp2*mm@%Bpd=dEp.4sNpIt?W!)<;dnl#'[pIk6Imkje^m/iaVJ,~> -^An+,q,mGto3M<"r`f,,p0m]'p0[N"p0I>rq-2iccX#^=rEI-Jp1)!Oph&Sul"Bp-mqV<,mqhH0 -p29VCp2K,5!-%7Do5OFgpGMCfp`&T2Rf@m~> -^An(+q*t0EqDe=,r\"."!&+'up+,kdp*]P\od'#OodAuNkU"P"[jKBLm47KPi[t0\mk*u]mkF5d -q_e4,pGV_)pcA.3pHJXDqEXX?o1AsWph0>5mpc#nm73WFJ,~> -^An(+q+UTUqF(0Er]C'0\s'#1jpHn=*pE8i)Rf@m~> -^An(+q,mGto3M?#rEJu*p0m]'p0[N"oj.8rqchl`cX#a>[q!]pm:PZqib/:)mqV<,mqhK1oPXDA -p2K)4!-7FGo5O_JpcnL1mjRoLm15ZcJ,~> -^&Rt*q*t0EqDe=,r\"1#r\+!tp+,hcpF#V\p*B2RnL*QJkU"\&[jKfXi[aLJh_"g\n1F)^n1a;d -pbht+pGVb*pH&%2pHSXCr'9jAnk&gUq.KG6mpbumm73WFJ,~> -^&Rt*q+UTUqF(0Er]C*=r]U!;p,Vh*pGDP!p+c7pr@d[tkqhNXkV2?V[kl_si]6Kfh`Lg$n3$/' -n3HG/pdG$IpI4gIpIb0RpJ1N\o2>-[r`]+jpHn=*p)r`(Rf@m~> -^&Rt*q,mGto3M?#r`f#)p0mZ&pL!T"p0IAsrEIr^cX#mB[q"-'ib%[khe2q&n7qE-n8.Q1o5=;@ -pMf/4rc7FHno4SHq*4U2mjRlKm15ZcJ,~> -^&Rq)q*t0EqDe@-r@\+#r%Idrp+,hcp*]P\p*B8Tm3h*EkU"k+[jL2cf.6PEi%=[VoI]MbmkF5d -oJQV)pGVb*pH&%2pHSRAr]p$Bo1AmUq.KG6mpbrlmRI7@Rf@m~> -^&Rq)q+UTUqF(3FrB($=r&sd9p,Vh*p,)J!p+c7ps"EarkVMEWkV2N[[km,)f/`Oai&gZsoK;S+ -mm-A/oL/[GpI4gIpIb0RpJ1N\nl#'[rEB"ipHn=*ocWZ(!!/PgJ,~> -^&Rq)q,mGtoNhE#s',&(p0mZ&p0[N"p0IAss'*u[cX$'G[q"N2f4O_fi+MduoP3i1mqhK1nS\)> -pMf/4r,V7Go5OYHq*4U2mjRiJmLK:,Rf@m~> -^&Rn(qF:6Eq`+F-r@S(#qChRpp+,hcp*]P\p*B>V!%@(ZkpGU?g`s9TnL;X3r$pVVnLsVgmk=,` -n1sMk!&XF.pGhn.pH817qa(3Ho0i.@r(d*$pL`l'oN:NO!'ZkOJ,~> -^&Rn(qFpZUqaC9FrAt!=qE=R7p,Vh*p,)J!p+c7p!&j'ukqhNXkV2Z_[kmP5b;oJ[i&gHmpH7n. -mm-A/nO*@EpI4gIpIb0RpJ1K[o2>-[r*&qipHn=*oH -^&Rn(qH3MtoNhE#!*er&p0mZ&p0[N"p0IAs!+=2]cX$3K[q"r>b@^Z`i+MRopM0/4mqhK1n8@u= -pMf/4qf;1Gno4MFqEO^3mjRfImLK:,Rf@m~> -^&Rn(q*t0EqDe=,r@[jup+?"hp*o\_p*TJYp`o\bkpP[AkU#14[jM#%_COLqlS&,emk=,`mkXAi -s#9^2pGhn.pH817q*>!GnjN(@qG-p#pL`l'oN:KNRK%d~> -^&Rn(q+UTUqF(0ErB'd;p,i"/p,D\%p+uCspb;V(kqqT[kV;9TqCf<6r&;JQs#f7%lTY2.mlp2* -mm?M3s$uiQpIP$NpIt -^&Rn(q,mGto3M<"m:5m!p0dT$p0RGupg!`)csP+)qHUL>r+3`Ws(LA(lY662mq_B.mqqQ3p2B\E -j)Om8nT"/>qJZ6qpGMCfoGd0.RK%d~> -^&Rk'q*t0EqDe=,r[n1+pFl4lpFGqdp*]P\p*BJZpF,8RkpGU?jWh5]aXc$rjY-Tbmk=,`n1sJj -q`"@0pGhn.pH817pd+I=o1A^Pr+Gb9mpbljm73TEJ,~> -^&Rk'q+UTUqF(0Er]:*GpHA43pGqq+p,)J!p+cCtpGV7mkqhNXkV2uhgb[_BaZ8rTk<%chr]KX5 -mm-A/n3m.DqF1-LpIb0RpJ1K[!)rJeo2bWir(HcNmkjVYm/i^UJ,~> -^&Rk'q,mGto3M?#!+kM,pL3c'p0[N"p0IN"pL)BUcX$NTggf,Ma_(-Xk@`mjrbCn;mqhK1n8@l: -pMf/4pN#kFo5OJCr'0p5mjRcHm15WbJ,~> -^&Rh&q*t0EqDe=,s"=:+pFl4lpFGqdp*]P\od'J\o-iiNkU,L>l6Ebbe19$#i%G'cn1X5amkXAi -q)A1/pGhn.pH817pd+I=nk&RNrFbk:mpbiim73TEJ,~> -^&Rh&q+UTUqF(0Es#^3GpHA43pGqq+p,)J!oeHD!o/>hikVMEWl7f\(e2c_Tm5s5i!($*. -n3m(BqaL6MpIb0RpJ1K[r_rJfnlGKgrCclOmkjSXm/i^UJ,~> -^&Rh&q,mGto3MB$rakG+pL3c'p0[N"oj.N$o3fpPd9X4le7RoXm:Y?k!,D"=mqhH0n8@i9pMf/4 -olB_Fno4>ArBL$6mjR`Gm15WbJ,~> -^&Re%q*t0EqDe=,!&"1)p+Q.lp+,kdp*]P\od'P^mO7 -^&Re%q+UTUqF(0E!'C*Ep-&.3p,Vk+p,)J!oeHJ#mPa;dkqhNXmP)+,h`9LTo/kkoqEa^:mm->. -n3lt?rC-HOpIb0RpJ1K[r)<;eo2bQgrCclOmkjPWmK*@hRK%d~> -^&Re%q,mGto3D?$r+55)p0m]'p0[N"oj.T&mU4FLeQoXphe(\Xo4QuqqJ,V;mqhH0n8@f8pMf/4 -o5aPEo5ODArBL$6mjR]FmLK:,RK%d~> -]`7_%q*t-Dq`+F-s"=1(p+Q.lp+,hcpF#V\p*AoOkpP[AkU#gF[jLDkaXuF(oeQ1nmkF5dn29bq -s#0U2pH&%2pHSF=nO2q>ohPL!pL`i&nQ>3L!'ZhNJ,~> -]`7_%q+UQTqaC9Fs#^*Dp-&.3p,Vh*pGDP!p+bhjkqqT[kV;Z_[l!D2d6.4di'-g"ofhh0n3ZV4 -nji^KpIP$NpItfpHn:)nK@6$!!/MfJ,~> -]`7_%q,mDsoNhK%qdo,(p0mZ&pL!T"p0HrkcsPL4[q+f:d:i>gi+_k!okX#4n87Z4n8J&?j)OU0 -pi5kDol'gopGM@enJgm,!"G@rJ,~> -]`7\$q*t0EqDe=,!&"(&p+Q.lp+,hcp*]P\pa#efl6tjDkpGU?pER-op++'5i%OOSq(M1jmkXAi -n2C>-pGhn.pH817pd+C;o1AOKs(D( -]`7\$q+UTUqF(0E!'C!Bp-&.3p,Vh*p,)J!pbD_,l8Ii_kqhNXpFs'5p,U,Ts#].&mm6b5mm->. -n3lb9!(loUpIb0RpJ1K[q,@&do2bHds%E)QmPOGVm/i[TJ,~> -]`7\$q,mGto3D?$q.8o&p0mZ&p0[N"pg*i,ls$-68mO7TEm15TaJ,~> -]`7Y#q*t0EqDdt"p+Q.lp+,hcp*]P\q'>edkpYdDkU,L>r$/Zt__([ -]`7Y#q+UTUqF(0EpHeL9pH/(/p,D\%p+uM!qDd^tkqqT[kV;uh[ktubgcjsgr''R7n3ZS3n43FK -q+16PpIt?WpJCT_qGQocnQGocpHn:)n0%*"R/_[~> -]`7Y#q,mGto3M*%lso`tp0dT$p0RQ#qI/VrcsPg=[q*Biq.eu)l"^-3mqhK1mr%W5ol/u3m;i&C -o5O5 -]`7V"q*t0EqDe@-s#0R+pFl7mp+,hcp*]P\q]thal6tjDkpGU?!$h1a_C3\]mP*oZj"^TfmkF2c -n29\oq)S=3pH&%2pcnL=n3lh=n4r^imUGZf!))]LR/_[~> -]`7V"q+UTUqF(3Fs$cWIpHA74p,Vh*p,)J!q_@b'l8Ii_kqhNX!&4+'_DTV$f084\!']j:mm?M3 -n43:GqagHRpIt?WpJCN]r)3)dn65QQmPODUliNRSJ,~> -]`7V"q,mGtoNhK/p1 -]`7S!q*t0EqDeC.rAO@)pFl4lpFGqdp*]P\r?Un_kpYdDkU,XB[j]o]j"TsUiA1?hnM'Den29\o -p,W+3p,_q1pcnL=mmQb=n4s!_ph&r'n6#]`mRI7@R/_[~> -]`7S!q+UTUqF(6GrC-EGpHA43pGqq+p,)J!rA!h%kr.c_kVMQ[[l)i$dQZVUrC$0@mm?M3n431D -r^c`TpIt?WpJCH[rDN5fn65itpd4C*n0%'!!!/JeJ,~> -]`7S!q,mGtoj.N.p1mqqQ3n8Io;j)O=( -rGhFJn8J>CpbhIfn/MB=mLK:,R/_[~> -]`7S!q*t-Dq`+L/qDS('pFl4lpFGqdp*]P\s!6q\l6tjDkpGmG[j^;hfJ*"PiA1-boJ#bimksSn -o/Qe1pH&%2pHSC -]`7S!q+UQTqaC?HqF1-EpHA43pGqq+p,)J!s"Wk"l8Ii_kqhf`[l*5/bs()PpI+X=n3ZS3n43(A -!(ciVpIt -]`7S!q,mDsp0IN,pLW]$pL3c'p0[N"s'=u"l -]`7Ouq*t0EqDeF/pbqk%pFl4lp+,kdp*]P\!%[:`kpYdDkU,sK[j^_tbV8rJiA0p\pb;.ln29\o -n2^5(pH817pHe48o1AFHrD`T)mUGZfr_)WKQiDR~> -]`7Ouq+UTUqF(9HpdOpCpHA43p,Vk+p,)J!!'9@(kr.c_kVMld[l*Y;a$/HJnO3.;mm?M3n42t> -pIb0RpJ1HZmo'3ao2b?arE]4cmPODUl2m=PJ,~> -]`7Ouq,mGtp0IH*pLW]$p0m]'p0[N"!+P2$l!VkFoj,% -]DqFtq*t0EqDeI0p,;Y#pFl4lp+,hcpF#__q^^q]kpYaCkpH3P[j_,*__C"(lS\PnmkXAin2Khs -r]Bp:pH817pHe48!(ulTmnWd[qI]/)n6#W^m73NCJ,~> -]DqFtq+UTUqF( -r_*&ZpIt -]DqFtq,mGtpKdK)pLW]$p0mZ&pL!]%qdSi!l!VkFq-CI@qe3l^s(gV/lYHB6mqqQ3n8I]5j`0@% -!-.XMmr/,?qDI[hn/M<;m15Q`J,~> -]Dhk4nk]'Nq*4^8s"a4)pG2Irp+>tgp*o\_q^2%fkpkpGkpP[Ar[#u@jseA*gG7kJs"Nmsn29Yn -n2^A-q`=I6pHSC -]Dhk#nk]'Tq+:ENs$--EpHeO:p,ht.p,D\%q_Rt-kr@ockqqT[r\;hYju:@FgHjphs$6$>n3l_8 -n4ELMqb$TVpJ1HZmSj-eoN(HbqH`tbmPODUkQ7+NJ,~> -]Dhjpnk]'`q-<]#oOdi/lso`tp0dT$qd0#+l!`gacsQcXdUhQQaD1?_k\B3ps(q.@n8@]5l>Yp+ -jDt9?oPj; -]Dhk4nk]'NpcnU7s"a1(pG2FqpFZ(hp*o\_r?h(cl72$HkU5XB[jff\eM?)@s#BL'n29\on2^5) -rAs[8pHSC -]Dhk#nk]'Tpdt -]Dhjpnk]'`pg!T"o4I`.lso`tp0dT$rEf&(l=&pbd9j@pe8"2_mV:]rs(q1An8@`6k&BR)jDt3= -p2KJ=q/#sdmO7QDq[i0+!"G:pJ,~> -]Dqn4nk]'NpcnX8s"a+&pG2FqpFZ(hp*o\_s!I.akpkpGkpPmG[jg2gcnaQ;q)J"%mksSnn2^,& -s#Tm:pHSC -]Dqn#nk]'Tpdt?Ns$-$BpHeL9pH/(/p,D\%s"j((kr@ockqqfa[l<2.cp?VYq+1-EmmQY8n4E7F -s%<#ZpJ1HZmSj!apf?iepKd_amPODUk5kVaQiDR~> -]Dqmpnk]'`pg!W#nRhN,lso`tp0dT$s'G,&l!`gaemGmuheLt_oP3?#q/#\?mr%W5j)F=(jDt-; -pi,\?pMBdcmO7QDq@N'*!"G:pJ,~> -]Dqk3nk]'Nq*4^8!&F"$pG2FqpFZ(hp*o\_l7;*KkpYaCo-Upql7gC:iAL3dpG24qml0_rnN6J- -pHSC -]Dqk"nk]'Tq+:EN!'fp@pHeL9pH/(/p,D\%l8n/hkr.`^o/!j7l9 -]Dqjonk]'`q-<`$mq2<*lso`tp0dT$l=0!dl!W4P[q=o?dqeboiG8%$pMKD;mr.-'m;_3-q/Q4L -n8J#:rAF!kn/M07mLPW`J,~> -]Dqh2nk]'Nq*4^8m5F\op+Q.lp+,hcpa?"lkpu!Jl6tjDpEm?uoe=B@iAL$_q(hFsn2Khsn2pV4 -q)nC9pHe48pIb -]Dqh!nk]'Tq+:ENm7$b8p-&.3p,Vh*pb_q2krS&gl8Ii_pG99;ofgA]iC**)q*OR=n42t>n4WaT -q+UNXpJC9VpK.5mmooBir^-$0n0$foQN)I~> -]Dqgnnk]'`q-)$l!`jbhI!a(okMQ`s(^M.mVM]9n8@`6hf.q%jDt!7 -qf)"BokaUbmO7QDq%2p(QN)I~> -]Dqh2nPAsMq*4a9s#oa-p+l@qp+?"hp*ohcpatb]kpkpGkU69T[j_;2`A67,kW&MrmksSnn2^## -qE=^ -]Dqh!nPAsSq+:HOs%;ZIp-JF9p,i"/p,Dh)pcIb%kr@ockVW2n[l4:N`Bj;gj[A<'rBfs@n42t> -nOr^Qqb6`ZpJC9VoiM&ln65Ehs$H-1n0$fo!!/DcJ,~> -]DqgnnPAs_q- -])V_1nk]'Npcn[9rB9O+p+l@qp+>tgp*oqfoI];Xl72$HkU-?WnL;L/cSF-1i]$onn29Ynn2^## -pc\O;pHSCrCQHQnl,U$mUGZfpIjpE!'Z_KJ,~> -])V^unk]'TpdtBOrCZHGp-JF9p,ht.p,Dq,oK2:ul8\#dkVN8qnMeKKcU$tflU9`'!(-0Cmmlk= -nOrXOr(Qi[pJC9VoN1ulmoo9f!(-*1n0$cn!!/DcJ,~> -])V^mnk]'`pg!`&l"9X#lso`tp0di+oOI,pl=&pbk?maka(N_6mVV9*ibSU3n8@]5i,Iq#jDsm4 -rG_4Dnn\:`mO7QDpCQa'!"G7oJ,~> -])V\0nk]'Nq*4d:q`X:(pG2Irp+>tgp*otgnL`uUkpkpGlmK7mg+q,7iAg`snhonqn2]u"of`:: -pHSF=m6pM;s%2ZSn5Sgbmo] -])V[tnk]'Tq+:KPqb$3DpHeO:p,ht.p,Dt-nN5trkr@oclnl13g-OafnO2>,r(?NJn42t>n4WFK -r_3&]pe^?VnlPilmoo3dmPOATj8tVHJ,~> -])V[lnk]'`q- -])VY/nk]'Nq*4g;q*"(&pG2Irp+>tgp*fthln.KQkpkpGn0b[qjYG+=iAgNmoel4tn2]u"niZt8 -pHSF=mR6M9!(lZTn5TEmmpb`fp.OgDQ2c@~> -])VXsnk]'Tq+:NQq+C!BpHeO:p,ht.p,;t.loXJnkr@ocn2.U7j[%NfpI*t2p.G!Gn42t>n4W=H -!)3,^pe^BWmoKKin65 -])VXknk]'`q-H -n8If4s(:^Jmi1s3mLPT_J,~> -])VY/nPAsMq*4g;pc[t%pG2Irp+>tgpF6+pl7M6NkpkpGod@4!n1r*CiAg -])VXsnPAsSq+:NQpe'mApHeO:p,ht.pG`+8l9"5kkr@ocoea- -])VXknPAs_q-1@n8$9crG1A.n87u=mr.-'j`0C&mVr5G -n8If4s(:^Jmi1s3m15K^J,~> -])VV.nPAsMq*4j -])VUrnPAsSq+:QRp.F[?pHeL9pH/(/q)A.5l9"8lkr@ocq)#Q@qa&4jham$)r'^$En4E+BnP/mV -qG-f^mSiOTr`eqsn6Q!#n20SViW9)\Q2c@~> -])VUjnPAs_q- -])VS-nk]'Npcnd -])VRqnk]'TpdtKRoLeI=pHeL9pH/(/q`"43l9"5kkr@ocr\Vt^krQpTgIUBts$Z?Hn4E+BnP/dS -rD*)`mSiOTr*/brn6Ps"nMK\Wi;ru[Q2c@~> -])VRink]'`pg!i)he)Uolso`tqdB,.l=9'fl!`gara4#bl"I\Gl"oEts).=Ehf.XrjDs[.r,D7G -n8/#7nL3iFoFUF$!"G4nJ,~> -])VP,nk]'Nq*4m=nNH7tpG2FqpFZ(hr@.:il7M6NkpksH\1H,ekW/)kiB.$'nMfqtnN65'o0;t8 -mR6J8qG6cZn5T6hnm_&io1SLAPlH7~> -])VOpnk]'Tq+:TSnOi1;pHeL9pH/(/rAX:1l9"5kkr@rd\2r,.ek"alr_;oQn4E.Cn4iROs%`;b -mSiOTqHNVrn6PlunhfeXhu]/CJ,~> -])VOhnk]'`q-9qI]:H -mi1j0mLPQ^J,~> -\c;G+nk]'Nq*+j=mlg%rpG2Fqp+?"hs!d=fl7M9OkUQ$L\1HMph)Y-fiB-g!of)A#n2p,&niuk7 -mR6J8q+pZYnPo9go4%/jnk8C@PlH7~> -\c;Fonk]'Tq+1QSmn2t9pHeL9p,i"/s#9=.l9"8lkW&#h\2rM9d7E4gpeCEOn4E+Bn4iILpJC9V -mT93goiglkpgE\Mmi^KhPlH7~> -\c;Fgnk]'`q-3o*gLg1klXTZts'Y5)l=9*gk[Epf\7iKkok`8tol&`,iGmq!mW&&Aol'A:ph'+G -mi1g/mLPQ^J,~> -\c;G+nPAsMq*470m5F\op+Q+kpF?4tl7V?Rkpu!Jo-h+"l8@!Mq`*Orniun.n2]u"nNHA+pHe48 -mRm4LpJ13Wpg!E"mo]-Rm73E@J,~> -\c;FonPAsSq+9sGm7$b8p-&+2pGi4l:&ofiC`Z8pdagFn4W:Gn5&dTmSiOT -pKRAqn6Pfso/,nYh?&rAJ,~> -\c;FgnPAs_q-;TbpLWZ#pL+&5l=B0il!imco3f(A]5@F]dV\bthf.Imj`9d/pMfkFn8.l3o-j&H -ndt1!PlH7~> -\c;D*nk]'Nq*4mJm60hpp+l@qp+?(jq_7:fl7M9OkplEU\1I;1a#W*?lp(A+n2^##n3-;+og/"6 -mRm.Jq+gEYp0@6!mo]*QmRI7@PlH7~> -\c;Cnnk]'Tq+:T_m7Qb7p-JF9p,i(1q`j@/l9"8lkrADq\2s:Oa%5/]lqR@In4E.Cn4iFKohb'T -mT9'cq-*8np0dMLmi^Hg!!/>aJ,~> -\c;Cfnk]'`q-b=p1EqFmi1d. -mLK:,PlH7~> -\c;A)nk]'NqEOpIm60hpp+l@qp+?.lpFtkbl7M6NkplTZ\1@V;`]PQ-.~> -\c;@mnk]'TqFUW^m7Qb7p-JF9p,i.3pHRq+l9"5kkrAT!\2jUY`^ohsk=Xo3r^ZHLn4W7FnPAdR -mSiOTo3;&pn6P]poec+[h#`f?J,~> -\c;@enk]'`qHWu1fk0qhlsop$pLWW#l=9'fl!aKt\7iiupMS5mrbp\5fQ$.rmW%i;qJYk>ok*kF -mi1a-mLPN]J,~> -\c;>(nk]'NqEOmHm60hpp+l@qp+?4no.]G^l7M6NkpcZ]nLVa5coLGTm5s;l!(-08n2p/'n3?M1 -mR6J8nPB'Xn5T!ap1!Jmmn<(=PQ-.~> -\c;=lnk]'TqFUT]m7Qb7p-JF9p,i45o0;M'l9"5kkr8Z$nN+`Rcq*Lrm7QA4!)W/Vn4W:Gn5&XP -mSiOTnQYopn6PWnp,)4\g]E]>J,~> -\c;=dnk]'`qHWr0fk0qhlsp!&o4@2tl=9'fl!XR"nS$!Xcu&,ec#)?WeT'nqmW%c9r,;(@o4I\E -mi1^,mLPN]J,~> -\c;>(nPAsMq`jpGm60eopG2Irp+?:pmP*oYl7M6Nm4,UtgH"4To/knqqa0m7nN65'nNZP0mR6J8 -mn`mWn5Ss`pL -\c;=lnPAsSqapW\m7Q_6pHeO:p,i:7mQ]u"l9"5km5VU -\c;=dnPAs_qcru/fOjkhlsp'(mUbZol=9'fm:!M=gMPnec#)K[d;ePomW%]7rGV1Ann.VEmi1[+ -mLPN]J,~> -\c;;'nPAsMr'0sFm60eopG2FqpFYYal7V?Rkpu9R\1Qi'ei`aiiBR!%pGq_)n3-;+nNlP1mRlnC -r_Dr^n6G`tmo]!Nm73B?J,~> -\c;:knPAsSr(6Z[m7Q_6pHeL9pH.Y)l94DokrS>o\3/nEek>g2iD&uApIXjIn4iFKnPJUOmT8g\ -r`\esn6l#Jmi^ -\c;:cnPAs_r*9#.fOjkhlso -\Gu2&nk]'Nr'0mDm60eopG2FqpauJ#l7hHTl7M9Ood[I'n2\]Ts#]1(mm?k2n2p/'n3?G/lpU86 -lq[UVn5Sj]pgW\om7Zk;!'ZVHJ,~> -\Gu1jnk]'Tr(6TYm7Q_6pHeL9pcJI?l9FMrl9"8lof0HDn4:brs%;6EmnijPn4W:Gn5&RNlr3=R -lrsHnn6PKjpb_F^g&_6TPQ-.~> -\Gu1bnk]'`r*8r,fOjkhm:5 -\Gu/%nk]'NrBKpCm60eopG2FqqCVLul7hKUl7M6NqC9!,q`2\Zh`p?lrAjC0n3-8*nNlJ/mRlb? -n5Sg\q-replq?b:P5g%~> -\Gu.ink]'TrCQWXm7Q_6pHeL9qE+L -\Gu.ank]'`rESu+fOjkhmpk?gl=B0il!jKt\7thWa):fnb\c`da)U]kmW%N2n8.N)q'b\Nlk&Rq -P5g%~> -\Gu/%nPAsMr]fsBm60eopG2Fqr%7Rsl7hKUl7M6Nr[QGMj=e_>g-=Uan2p,&nNZP0ksXr3l;-nK -ls0Brmo\mKmRNH?J,~> -\Gu.inPAsSr^lZWm7Q_6pHeL9r&aR:l9FPsl9"5kr]&Fjj?Cd\nP8@Dj -\Gu.anPAs_r`o#*fOjkhnRLEel=B0il!jX#eS4)Zb\lrhb\cig`GtNjmW%N2s)%=Clt6)Bmi1R( -mLPK\J,~> -\GlP0o2kHXq*tBKn3u_-lo+Snp+QCsn1s8_l7V?Rl79Cuf0%SJi^3Q4o/lG)nNZP0kX=i2l;-kJ -lWj -\GlOdo1&7Gq+Uf[n5/LElp^Y7p-&C:n3H8'l94Dol8lI>f1Y7$n4hn=r)<2[n4iFKnPJFJmT8[X -mp56eq_[aaf)h-8J,~> -\GlOZo02\?q,mZ%n7C6apLWr+fk'GZl=9*g\SB`6k&8I\o5MQdp2]S@l>Z]=o5*`)q^CnPl4E@o -P5g%~> -\GlP0nlPBXpdP6JmR?J*m5FYnpFcLuln[i[l7V?RmOPh$iB5LPi^3?.p,he-n3?G/k="`1l;-kJ -l!4-qmo\jJm73?>J,~> -\GlOdnj`1Gpe1ZZmSN7Bm7$_7pH8L -\GlOZnilV?pfIN$mUb$_p1eBl>ZW;ok`l)r$_"Ql4E=n -P5g%~> -\GuS0nlPBXq*k?KlUC2(m5FYnpb2V'lS@`Zl7V -\GuRdnj`1Gq+Lc[lVQt@m7$_7pc\UClTj`"l94Ano/aEGlq>`$r(Z0In5K0Vn4iFKn5/7GmT8[X -mTo$arA -\GuRZnilV?q,dW%lXea]p1=#/rasYkl=B-ho4#7Flu(3hb\d2q]5d[fmW%N2q/,hAk[scAmi1L& -mLK:,P5g%~> -\GuP/o2kHXq*k?Kl:()'m5FYnqCh\%l8%WYl7V?RpFEd-pH6M]s$Pg5l:1_6n3-;+nNl5(mRlb? -mSr@SrF54tkY(>6OoKq~> -\GuOco1&7Gq+Lc[l;6k?m7$_7qE=[Al9OW!l94DopH#iKpIiS&s&%fQl;RXSn4iFKnPJ:FmT8[X -mTo!`rA -\GuOYo02\?q,dW%l=JX\p1=)1qI\2fl=B0ipL:[JpMRuhb\d;t\8hFemW%N2phfbAk@XZ@mi1I% -mLPH[J,~> -\,ZG.o2kHXq*sR:m60hpp+lP!p,)%il7hKUl7Ml`^+]C=a?T>bksFH#!'B^6n3?G/j$`<-l;-eH -k$7mpmo\aGmRNE>J,~> -\,ZFbo1&7Gq+U!Km7Qb7p-JU>p-S%0l9FPsl9"l(^-;H[aA2D*ktpG>!))iVn5&RNj&>AIl -\,ZFXo02\?q,lilfk0qhr+#G4dq.fTl=9^#^2!R]]Pl#2!-#>crc7FHl>ZH6ph]))r[@4Sk7I%l -OoKq~> -\,ZG.nlP?WqF:KYkXFl%lo+Snr\*dulS@`Zkq;6Q!%[gt_D0M(l9k#,i^Wo>nicJ,nNl/&mRlb? -m8W.Os'kG!k"G,4OoKq~> -\,ZFbnj`.FqFpofkYUY=lp^Y7r]Td -\,ZFXnilS>qH3c+k[iCYpLX>6nRg9^l"''h!+P_<_J?f&cu&buZZ,tcmW%N2oPOG@jC\E?mi1C# -mLPH[J,~> -\,ZD-nlPBXqF:EWkXFl%lo+Sn!&*^rl8%WYl7VHU\M;r*ha@$&j$rf9of_h0n3Q#$!(cQNl;-eH -i`lImmo\^FmRI7@OoKq~> -\,ZCanj`1GqFpidkYUY=lp^Y7!'T^9l9OW!l94Mr\No"Hhbj#Bj&>_QohFsPn5/(B!*8Pil -\,ZCWnilV?qH3])k[iCYpLO>7m:OgYl=B9l\SJirent1uTlK@:nnn;@iar0=mi1C#mLK:,OoKq~> -\,ZA,nlPBXqaUHVkXFl%lo+Vos#'+!l8%WYl7VTY\M<>5e3j(!j$rT3pc\.3nNl,%rCHEMl;-bG -iEYhRj\,#3OT0h~> -\,Z@`nj`1Gqb6lckYUY=lp^\8s$Z0>l9OW!l94Z!\NoCSe5?'=j&>MKpeC9SnPJ1CrDrDhl -\,Z@VnilV?qcN`(k[iCYpgr]%dq.fTnRK+F]PlSBnnb(@l>Z60rG:G)mi1@"mLPEZJ,~> -\,Z>+o2kHXr'pHTksau&lo+\qq_dXqlS@]Yl7Vc^\M<\?b!Q.qj$rB-r&sO6nNl,%qFL0Ll;-bG -i*67on6"aEmRNB=J,~> -\,Z=_o1&7Gr(Qlaktpb>lp^b:qaB^9lTj]!l94i&\Noa]b#&.8j&>;Er(ZZVnPJ1CqH!/gl -\,Z=Uo02\?r)i`&l"/LZqIS`"dq.fTp1(XK]PleHltiMZ0.s(pV*!*Se1j:L_iOT0h~> -\,Z>+nlP?WrC6KSksau&lo+bsp,2.ml8%WYl7Vob\M=(Ja@#_mkXO],s#oj9nNl,%pIOpKl;-_F -iEZCpn6"^DmRNB=J,~> -\,Z=_nj`.FrClo`ktpb>lp^h -\,Z=UnilS>rE/c%l"/LZr+4eudq.fTqI@'O`GbNhk\]HuVfD!@m;;l>iF`*mn/LC!mLPEZJ,~> -\,Z;*nlPBXrC6EQksau&lo+eto/5hjl8%WYkq;ufj>#4@cpR1jmRH2.s%`,Ln3Q#$oLS[Jl;-_F -i*?7nnQ=dDmRNB=J,~> -\,Z:^nj`1GrCli^ktpb>lp^k=o0hn2l9OW!kro&.j?V9_cr'10mSi+Fs&ehfn5/(BoN(Zel -\,Z:TnilV?rE/]#l"/LZrFOesdq.cSs't,*eni]Om;D?/ib\X0WH%3BlYYQlr`Sb1iXkMgOT0h~> -[f?2)nlPBXrC6BPksau&lo+l!mksAel8%WYln5h(gI'sjo1%b4q+gTInNl,%n4<=Hl;-_Fs'=Vh -r+>M%i_/]0!'ZMEJ,~> -[f?1]nj`1GrClf]ktpb>lp^q?mmQG-l9OW!lohmGgJQs0o2F[Lq,m;cnPJ1Cn5f -[f?1SnilV?rE/Z"l"/LZs(0hpdq.lV\SU#=ibn@)j)"O+X)[EDlYZZ7ib&*knf-R"mLK:,OT0h~> -[f?/(o2kHXr^QEOksau&lo"i(lS[ubl8%WYn1M7,j[7Zjq*sC:o1o'FnNl,%m7@(Gl;-_FrE\Gg -qe#G%iCiT/O8j_~> -[f?.\o1&7Gr_2i\ktpb>lpUnElU:&*l9OW!n3+ -[f?.Ro02\?r`J]!l"/LZ!,'hodq/#Z\SUAGfP^M%j)"=%X` -[f?/(nlP?Ws$lHNksar%ml()(lS[ubl8%WYoId[0n3bGjs$l$@m8!RDn3Q#$l:ChFl;-_Fqd&;g -q.B8$i(NK.O8j_~> -[f?.\nj`.Fs%Ml[ktp_=mm[.ElU:&*l9OW!oKB`On57G0s&7rXm9'9^n5/(Bl;mgal -[f?.RnilS>s&e_ul"/R\r+FPldq//^\SUbRc#3Puj)"*tYAriHlYZN3j_" -[f?,'nlP?W!(Q?Lksar%nM^/&lS[ral8%WYq(B35qa8IqqFg!Dk>)%AnNl)$k=GSEl;-_Fq-E,f -q.B5#i(NK.O8j_~> -[f?+[nj`.F!)2cYktp_=nO<4ClU:#)l9OW!q)u8TqbbI7qGucZk?.a[nPJ.Bk>qR`l -[f?+QnilS>!*JVsl"/X^ph/)gdq/>c\SV.]a_prlkA9 -[f?)&nlPBX!(Q9Jksar%o/?2#lS[ubl8%WYr@ZJQkqpXTn4W+?jA,_Qnj25&j@K;Cl;-_Fpg*&f -pLa&"hb3B-O8j_~> -[f?(Znj`1G!)2]Wktp_=o0r7@lU:&*l9OW!rB8OpksWcqn5emUjB2Fcnke:DjAu:^ln -pKdD6bQ -[f?(PnilV?!*JPql"/^`o4QTcdq/Jg\STN.n87N.jD+^/ZZ58LlYZE0k@XHkoGc[!mLPBYJ,~> -[f?&%o2kHXktL>1lok#"mlB\klSRl^l7fb*fLP'snOhk;q,6lOiC)Wql;-_Fp0Hlep1Eu"hb3B- -NrOV~> -[f?%Yo1&7Gku6hClq6q>mml\3lU'l%l9DgIfMq!6nQ"XPq-*GfiD\]9l6bQ -[f?%Oo02\?l!`gfi+DFjeRm9A\STo9k&'X)jD+L)[;kJNlYZ?.k[sNkoc)d"mLP?XJ,~> -[f?&%nlPBXs&SAXkXFl%pGV=tlS[ral8%f^\MW;9ge7B6j%f8FpHdb+i^j#BlqcqHoNg`eoOdf! -hFm9,NrOV~> -[f?%Ynj`1Gs&SA_kYUY=pI4CFVgfF/Lj&ktXpJBgIi`?"\ls&d]oO7#moNh/5 -b6!_(J,~> -[f?%OnilV?s&\Gnk[iddlY"^ZfO_U0ibmOgpMAl)okCpUk]#j)oOmH$oNCl,h@T)cNrOV~> -[f?#$nlPEYrDr/VkXFl%q)7@qlS[ubl8%rb\MW\Dd7aF1j%f&@qa'..i^io?mSE.Jnm1Qdo4I`! -h+R0+NrOV~> -[f?"Xnj`4HrDr/]kYUY=q*jF9lU:&*l9Or*\O>gad8p3Gj&kbRqbZ3Li`>nYmT]!_nmUilo3M)5 -ao[V'J,~> -[f?"NnilY@rE&5lk[ijfk%E4Vgh"$4m;C -[K#o#nlPEYr)W&UkXFl%q_mFolS[ubl8&)f\MX%Nb"L_hku.LHiC)WqpJ(-QlW!@Zm9K0npfQ -[K#nWnj`4Hr)W&\kYUY=qaKL7lU:&*l9P).\O?0kb#\LEj]LbNr_VQPiE#\Un6>3an6t]lnQko4 -aT@M&J,~> -[K#nMnilY@r)`,kk[iphib-eRi+9H8pMS,jrbU\2l"RkOk&BX'n7V-#nQGW+g^rlaNrOV~> -[K#l"nlPHZqGuiSkXFl%rANLmlS[ral8&8k^,5aGb=gVcjAOr)iCNT8nk\RNmTo6cnRhPugIps) -NrOV~> -[K#kVnj`7IqGuiZkYUY=rC,R5lU:#)l9P83^-qlfb?"4;l<*+NiD\]9oNC`hlWrpemU5Qjp`RWZ -NrOV~> -[K#kLnil\AqH)oik[j!jhIk>Mj^l/B`bk<`l>#Bo_/\RUlYZ'&mUkulp`%s!mLP?XJ,~> -[K#l"nlPHZpf?WQkXFl%s#/Rkl8@lal7r>npGBcGeP"OijAPkVj@%uun4iONlW!4Vn6GEoq,lBE -mRN9:J,~> -[K#kVnj`7Ipf?WXkYUY=s$bX3l9sr)l9G>6pI)nfeQ1p;n6"aTqcr2\i`>POoNUWels]Bkmp5`3 -a9%A$J,~> -[K#kLnil\ApfH]gk[j'lg1SoIl"0U*QYku'n7q#uqddZcj)F=$lt>g"mofH*gCWc`NW4M~> -[K#i!nlPK[p/^EOkXFl%eN)qQlSRua\MiA=dn\t%oiL -[K#hUnj`:Jp/^EVkYUY=eOSpnlU'u(\OGFWhHBPSjBD4]k>UA@m90-elWrgbnR1`iqB3f[NW4M~> -[K#hKnil_Bp/gKek[j-neS!EEm:DZ%hJDn#jD"9taDp3YlYYs#nRh/kqA\-"mLP -[Jp4qo2bE`p.t<]ktL;0lTNcYlS[ranhRa6l:]Guj&5AJl9sW&lV7+LlW!+Snm(NnqcMNEmRN9: -J,~> -[Jp4>o/6).p.t<]ku6eBlUo\tlU:#)nj'`Sl;lD;r)hu_n6G6Wi`>AJpKQrhl!a-jls9K2`WD/" -J,~> -[Jp4>o.0Aqp.t<^l!`demUjm[e7S8_U2ACrr+b8+n79^^iGe+"l"BR!lrj3)fb!Q^NW4M~> -[K$7qo2bE`pJ:?\ktL;0kWRKWl8@lap+j0:oh3G&j&5/DmR6#)kY:kKlW!%QoN^Znr)hTEmRN9: -J,~> -[K$7>o/6).pJ:?\ku6eBkXsDrl9sr)p-?/WoiB4 -[K$7>o.0AqpJ:?]l!`den7KsYe7SDcX_l3s!,CS/l=A1[i,J"!k@aF!l<4$(fF[H]NW4M~> -[K$4poN(NapJ:9ZktL;0juq6TlS[ubqD,T>s%CL0hbrN:nO2>,j\>VJlVutOoj$`nrE.ZEmmd@A -NW4M~> -[K$4=oJQ2/pJ:9Zku6eBk"=/olU:&*qEVS[s&R9Fp06fcjBV4RiE#&CqciAlj^Igil!=61`<$#? -NW4M~> -[K$4=oIKJrpJ:9[l!`denn-!VeRnYh[r'9(p1il,jCHYXhf.muj_+6ukums(f+@B]!"FqfJ,~> -[/^.po2bE`peU -[/^.=o/6).peU#@rEJPmjC.djk["-0_ubnt -J,~> -[/^.=o.0AqpeUbn7g`ieT'Dal>>TppL`Vlr>X?"mLP9VJ,~> -[/^+oo2bE`q+p?XktL;0iB>aPl8@rc\N&D?ePb(%pI+"3hG+#Gl;ZbKpfuuor`I`EmRN69J,~> -[/^+ -[/^+>Nnph&\lrYsE"mLP9VJ,~> -[/^(no2bE`q+p9Vl:gD1h`]LMlS\2h\N&eJcr/=oqaBC6gJ-s4iEc7aj_"NpeP#=#N;nD~> -[/^(;o/6).q+p9Vl;QnChb)EhlU:80\Obpeem&#Tdp2cKiE"]9l -[/^(;o.0Aqq+p9Wl=&mfph%0OgLe!6k\Sgbqe=MigMttel>>Kmq.A_kru9K"mLP9VJ,~> -[/^%mo2bHaq+p3TktL>1h*':KlS\>l\N'.TbYl_frC#U8gJ.ZFlr;kJqHW)n!*I`Dmmi<9J,~> -[/^%:o/6,/q+p3Tku6hCh+H3flU:D4\Oc9obZi@trDVZUgKFM\ls8LWqI&Ai!$K,[MuS;~> -[/^%:o.0Drq+p3Ul!`gfqI[6Mhe'E:nncNbc=GaNgMm.0lttWmqI\ek!#sE!mgk?VJ,~> -[/^%mo2bE`qG66SktL>1gHF(Il8ADp\N'O_b>S(:c;t!6iCM]tqbli]h-Kq`ib$Y4mmi<9J,~> -[/^%:o/6).qG66Sku6hCgIg!dl9tJ8\Oc[%b?O^Hc -[/^%:o.0AqqG66Tl!`gfr+<i>rG9YlpL_]]i,RLjqekh=h.QXti`X_Amgk?VJ,~> -[/^"lo2bE`qbQ9RktL>1fKIeGl8APtff7qOd8K@6dT?BQj@J!!q,6Z\gKjb_ib$Y4mmi98J,~> -[/^"9o/6).qbQ9Rku6hCfLj^bl9tV -[/^"9o.0AqqbQ9Sl!`gfrarBIk@V8Bd;&&\dU`;_jDimmq/5Y -[/]tko2bE`r(lr)~> -[/]t8o/6).r(lp;>p0@,nfjXngia)R^_?,SoJ,~> -[/]t8o.0Aqr(l -ZiBkjo2bE`rD2?PktLA2r^,1!eN*+V\N8kNfN@!3nQkENg.h0:o2O=I!*nMqrbU%Vn4/?8J,~> -ZiBk7o/6).rD2?Pku6kDr_D$:eOT*s\OkpffNm?=nQkE\g0+#Po3KsV!+=elr_gT7M>r)~> -ZiBk7o.0AqrD2?Ql!`jgVe>L2\SgPEfOiuNnQtL#g2QY$o53)l!+t4nr^YEKn.1BUJ,~> -ZiBkjo2bE`rD29NktLG4qEiareN*7Z\N94XcWK1.ls9!LgJ.-7oi0LJ!,(>(r+shTnOJE8J,~> -ZiBk7o/6).rD29Nku6qFqG,U6eOT7"\Ol9pcX#O8ls9!ZgKEuMoj--W!++\kr)1E6M#Vu~> -ZiBk7o.0AqrD29Ol!`piUM'42\SgnOcXu0IlsB(!gMlV!oki8m!*\Dcr(#3InILHUJ,~> -ZiBhio2bE`r_M -ZiBh6o/6).r_M -ZiBh6o.0Aqr_M -ZiBeho2bE`r_M9LktLP7nj:njeN*Rcb<#)U^0'f)i*GtGg.gg0q,GpNrFb5(q/"PRnOJB7J,~> -ZiBe5o/6).r_M9Lku7%InkRb.eOTR+b=V.s^0U/3i*GtUg0*ZFq-DQ[rEeSkq,5-4L];l~> -ZiBe5o.0Aqr_M9Ml!a$lRqM\3bAQce^1QeDi*Q%qg2Q:oq/+\qrEA;cq+&pGnILETJ,~> -ZiBehnlG<_s%h -ZiBe5nhou-s%h*eOR/E^0U>8gKjPSg0*QCqd%c]qd/Djq,5-4LAuc~> -ZiBe5ngj8ps%h -ZiBbgo2bE`s%h6IktL\;l9a#ag,Z]-^0(24eQr#Bg.gU*rD_?Rq.Jo'pMA;OnjeH7J,~> -ZiBb4o/6).s%h6Iku71Ml;#l%g./\J^0UP>eQr#Pg0*H@rE[u_q-N8jpJSp2LAuc~> -ZiBb4o.0Aqs%h6Jl!a0pQY3jI^1R1OeR&)lg2Q(irGC+uq-)ubpIE[DndgKTJ,~> -ZiB_fo2bE`!)M-GktLb=j[.N]hDr,1^0(A9cs?T@g.gO(r`%HSpLi`&p2&2No1+N7J,~> -ZiB_3o/6).!)M-Gku77Oj\FB!hFG+N^0U_Ccs?TNg0*B>ra")`pKm)ip/8j2L&ZZ~> -ZiB_3o.0Aq!)M-Hl!a6rQY3jI^1R@TcsHZjg2Q"grb^5!pKHfap.*RCo+-QTJ,~> -ZN'Veo2bE`doP^,pI4%-eii"Q\j"#6qd.NQ!(bj=h,45Pem7r]lXod*dnB:&L&ZZ~> -ZN'V2o/6).doP^3pJBgDek4pl\kS#e!*7iWh-9qcem\5XlWs-\`<(bnJ,~> -ZN'V2o.0AqdoYdBpLT\(\o5Juqd7TS!--b4h/340en=YZlWNjMdh)*]L&ZZ~> -ZN'VenlG?`s'O5UktLnAh*T[UjuL":bujLBd9Z`]geHR%s&mrZoOmK%o5)oLo1+K6J,~> -ZN'V2nhp#.s%LmBku7CSh+lNnk"!!WNEt4?gg'kdf4";XlWs*[`WChnJ,~> -ZN'V2ngj;qs$Y=;l!aC!QY3mJc"?KQd9cfPgi2%ds(U(toNLQ`o1.:Ao+-NSJ,~> -ZN'Sdo2bHarEn#SktLtCfg=4PlT)O?f3%3Bf3S/]hbDm(r)q]Ynn7?%nSH]JoLFQ6J,~> -ZN'S1o/6,/rCk[@ku7IUfhU'ilUSN\P?laBgg'bafjXGXm9T6[`r^nnJ,~> -ZN'S1o.0DrrC#+9l!aI#QY3mJf4O2Qf3\5Phf.@gr+XhsnlkE`nOM(?oFHTSJ,~> -ZN'Pco2bKbqd7fQktM%Ee3__Lml@sCi`OuBgg0M]i_A3+q,uEWnRq9%nSHZIoLFQ6J,~> -ZN'P0o/6/0qb5I>ku7OWe5"Remmjr`QsJ3Egg'Y^g0sMXmTo?\`WCemJ,~> -ZN'P0o.0GsqaAn7l!aO%QY3mJib$tQgg9SPic*[jq.\PqnQP?`nOM%>oFHTSJ,~> -ZN'Mbo2bNcq-VTOktM+GcpH;Ho/XBGlr_\Bia(q]j\=N.p0$0Vmq;-%mqgHGogaW6J,~> -ZN'M/o/621q+T7 -ZN'M/o.0Jtq*`\5l!aU'QY3mJlt4[Qia2"Pj`'!mp1`;pmoo3`mmkh -ZN'MbnlGEbpg;KNktK;feiiaf\j#giaBn(9k@ -ZN'M/nhp)0pe9.;ku5f$ek5[,\kSu+cWT"-o3^?_m9] -ZN'M/ngjAspdES4l!]ok\o7:SaC".;k>gbcgMZUshIl4ZnQG9Mdh)0_K)^?~> -ZN'Jao2bNcp0Z9LlV.7IbX0iCr&M>P]jC)3ia_=LgJ[NDi*GY]o4I?*e4]I)JcC6~> -ZN'J.o/621p.Wq9lVmaZbYH\\r("=mWEmbOgKa5Ti*kqXo3L]\a9$qmJ,~> -ZN'J.o.0Jtp-dA2lXBa'RqKagMZLpi+M@Zo3(EMe.D9`JcC6~> -Z2aA`o2bQdoO$'Jm7d:FbX0lD!'0L3bX0!Lo3L-]mnMS8m9/CRl"BX#lYP'Dp.']6!<7Q~> -Z2aA-o/652oM!_7m8NdWbYH_]!(ZKPbYbQLb$!M)m9eg\l!F!flVbh+Jc>`MJ,~> -Z2aA-o.0MuoL./0m:#d$TP(iS]jLA;gf`MJ,~> -Z2a>_o2bTenmBjHmnE@DbX0uG\jF):q-DQ]nkIn;l<3.Qk\'R#l"njBp.']6!<7Q~> -Z2a>,o/683nk@M5mo/jUbYHh`\l#k@a'%2&l`MJ,~> -Z2a>,o.0Q!njLr.mpYj"Uh@8W]jLSAelD9[ghuCkj(IRZoi^NLe.D`MJ,~> -Z2a>_nlGNen6aXFn4`@BbX1,K\jF):ra!o]ohF4>k?6nPk%FC"l"ngApIBf7s8RT~> -Z2a>,nhp23n4_;3n5JjSbYHtd\l$%E`ECu$k?m=Zk$Iaeku,V)JcGcMJ,~> -Z2a>,ngjK!n3k`,n6tiuW+W\[]jLbFd8fjYghu:hj_*^Zp0$WMdh)6aJcGcMJ,~> -Z2a;^o2bWfmU+FDnkAF@bX18O\jF8?r++)_peBOAjB:YOjCe7"kA8U?pIBf7s8RT~> -Z2a;+o/6;4mS))1nl+pQbYI+h\l$=Mr*%'Bgg&oIk@*FXpg*&\aT@%ns8RT~> -Z2a;+o.0T"mR5N*nmUosXCo+__I*4Ac;jXYghu1ek@`jZpfZcMdh)6aJcGcMJ,~> -Z2a8]o2bZglsJ4BoM"I=bX1GT\jFVIo46*Ur(YpDiE>DNib/(!k%rO?pIBf7rr7K~> -Z2a8*o/6>5lqGl/oMasNbYI:m\l$[Wo304 -Z2a8*o.0W#lpT<(oO6rpZ"LXdb[9sBbuO[\gMYtal"B!Zq,uiMe.D?bJcG`LJ,~> -Z2a5\o2b]hl -Z2a5)o/6A6l:fZ-p/C$LbYIFq\l%$akuu85gK`TBlXA^Xqd&8\b6!7prVqB~> -Z2a5)o.0Z$l9s*&p0m#n[:d'hemIZBc;jj_gMYk^lY#-ZqcVuMe.DBcJcG]KJ,~> -Z2a5\nlGTgl!Mn?pe9U9bX1\[]0bF^hdk#BgJZd/m9SR]r+=u*e4]R,JcG]KJ,~> -Z2a5)nhp85ktKQ,pf$*JbYIOt]2@Klhce<.gK`K?m:"jXr*A>\b6!7prVqB~> -Z2a5)ngjQ#ksX!%pgN)l\7`Eli*YABc;iV:gM-%fhHKh[j%%T1p^_rUrVqB~> -YlF,[o2b]hk?l\=qFo[7bX1h_]0bdheR[0>qe"\_fNIZKh.Q[uj(n^-dnBL,JcGZJJ,~> -YlF,(o/6A6k=j?*qGZ0HbYI\#]2@j!eQUI*qbu?VfO+)Uh-U%cj&,k9b6!7pr;V9~> -YlF,(o.0Z$k=!d#qI//j]P"ipl -YlF)Zo2b`ij^6J;r(Pa5b -YlF)'o/6D7j\4-(r);6Fb>.b']2A3+b?ES%p/BsUeR.fSgKskbj&5n9b6!7pr;V9~> -YlF)'o.0]%j[@R!r*e5h^h:8toO#dBfi@`=ibm:\n7UKZr`S8Os!uP0q%&&Vr;V9~> -YlF&Yo2bcjj'U89r_1d2bX2.h]0cL'_.;G5n7Lc[dTQ-Hfk1:sj)"[+e4]U-JcGWIJ,~> -YlF&&o/6G8j%Rp&r_q9CbYJ",]2AQ5_-5`!n5JFRdU2QRfj4Yaj&5h7bQ<@qqu;0~> -YlF&&o.0`&j$_?traF8e`Flf$ra3KBhc9/=j_iL\nn6WZ!*S>Pr@?A/q%&&Vqu;0~> -YlF&YnlG]jiEt&7!)1^/bX2:lj$MEh^1?;7lXo?YcWTmGf4X,[r+=GOq+$#9qu;0~> -YlF&&nhpA8iCq^$!)q3@bYJ.0j&+K/^09T#lVm"PcX6 -YlF&&ngjZ&iC)-r!+F2ba_/5(^/j;plV$GZcY<#af373)r%$8.q%&&Vqu;0~> -YlF#Xo2bfkhd=l6r_Lg0bsK$4^1?M=j_!gVbZXUEfOt5$j_Xd*e4]X.JcGTHJ,~> -YlF#%o/6J9hb;O#r`.6@btblM^09f)j\tJMb[:$OfO"SVj\kq6blWIrqYu'~> -YlF#%o.0c'haGsqra=#_c"FY,^/jN!j\+oWb\?`_fNS;Gj[].te.DHeJcGTHJ,~> -YlEuWo2bilh-\`6qG5C,d6bH8^1?_Che):Sa]\@DfOt/"k%sj*e4][/JcGQGJ,~> -YlEu$o/6M:h+ZC#qGkgYs~> -YlEu$o.0f(h*fgqqI%T[d:^(0^/j`'hb3BTa_CK^fNS5Ek"#4te.DKfJcGQGJ,~> -YlErVo2blmgL&T6p.rq'ej?u=^1?nHg1KkQ```+CfOt,!kA9p*dnBR.JcGQGJ,~> -YlEr#o/6P;gJ$7#p/T@7ekWhV^0:24g/INH`aAOMfO"JSk>M(6blWIrq>Ys~> -YlEr#o.0i)gI0[qp0c-Ven;U5^/jo,g.UsR`bG6]fNS2Dk=>:tdh)BeJcGQGJ,~> -YQ*lVnl>`lg0`N6nk[M#g-WDA^1@+Ne7S>N_cckBfOt%tk\U!*e4][/JcGNFJ,~> -YQ*l#nhgD:g.^1#nlj~> -YQ*l#nga](g-jUqnmK^Rg1S$9^/k,2e4]FO_eK!\fNS,BkXY@te.DKfJcGNFJ,~> -YQ*iUo2blmfjEH6mn_1uhEnhE^174RcXuoL^fgVAfOstrl>6-*e4][/JcGNFJ,~> -YQ*i"o/6P;fhC+#mo@V0hG1[^^01M>cVsRC^gI%KfO">Ol;I:6c2rRsq#>j~> -YQ*i"o.0i)fgOOqmpOCOhIjH=^/b56cV+"M^hNa[fNS&@l::Lte.DKfJcGNFJ,~> -YQ*fTo2Yimf3d<6l;,\qi^17Ia(4sXc=ZrO]ik>?fOsnplYQ3*e4]^0JcGKEJ,~> -YQ*f!o/-M;f1at#l;c,,i_I*ba'/7;c;XUF]jLbIfO"8MlVd@6cN8[tp]#a~> -YQ*f!o.'f)f0nCqllUURte.DNgJcGKEJ,~> -YQ*cSo2aCKhH&c7c:@"`]1:7ZmUsgWr_g=1!+"5ioPNu3nn-BErC;G=pA]X~> -YQ*buo/5&lhH&c=c;E_!]2[0]mSqJ1r`m$A!+FMdoMb-Tni+nsJcGHDJ,~> -YQ*buo./?ZhH/iIc=Q-N]5Q(tmS(o!rbf;]!,'qfoLS@>ngi3$r==JZpA]X~> -YQ*cSo2blteR.68i_RfhlU&3RgLTAXcXm/T\QSrBfk9kmmVME*e4]a1JcGHDJ,~> -YQ*buo/6P/eP+n%i`46#lV>&kgKNZ;cVjgK[pRfpnl+sSnMeerJcGHDJ,~> -YQ*buo.0hpeO8=siaC#BlY!hJgK*B/cV"7U\S;(Mfimr;mRQdte.DQhJcGHDJ,~> -YQ"Sfk=,#DrEe#]i`=o3bt%4h]1:sng1SlHrF``Fr+"Jon87Z2n7L0CrC;G=p&BO~> -YQ"S'k7[DErArJ&i`=o9bu*q)]2[lqg/QO"rC=;'gKsAJn5Aa7ciSdup&BO~> -YQ"S4k7[D;r@lbii`FuEc"6?V]5Qe3g.]sgrB%W*r*J,]n4<%=n13!"r==JZp&BO~> -YQ+Vfk=,&Eqd.f[jAsu1bt%@l]1;=#ctD!Cph. -YQ+V'k7[GFq`<8$jAsu7bu+(-]2\6&crAXrpd_c"gg9JKn5A^6ciSdup&BO~> -YQ+V4k7[G -Y5\Jek!euEq-MTYk#U&/bt%Lp]1;[-`b4.?nn5dAp1)rlmVVN2m:Oj@r^VP>o`'F~> -Y5\J&jq@AFq)[&"k#U&5bu+41]2\T0``1ennjg,qhHoVKnl"j6d/nn!o`'F~> -Y5\J3jq@Aek#^,Ac"6W^]5RLG`_>5^niO[%p0QTZmRZn=m46ZtrXXS[o`'F~> -Y5eMek=,)FpKlBWkZ6),bt%[u_ajMs^LuP -Y5eM&k7[JGpH$hukZ6)2bu+C6_c6G6^Js2km74Tli*PbKo2=p6dK5""oDa=~> -Y5eM3k7[J=pFt,ckZ?/>c"6fc_f,?f^J*W[m5r7#o3U?Ylq$_ -Y5eJdk=,)Fp0Q9Vl;l/*bsqb#ogd%s^LubBk@_h?31l=SO=s$qY?oDa=~> -Y5eJ%k7[JGp,^_tl;l/0bu"I9oi/t6^JsDqk=;sfia1nKoht'6dK5""oDa=~> -Y5eJ2k7[J=p+Y#bl;u5 -Y5eJdk!f#FoNp'TlrM5(cpt]B^LuqGib-D:mUP9ik\^$0l"8I=s$qY?o)F4~> -Y5eJ%jq@DGoK(MrlrM5.cr%DW^JsT!i^^Ibj'LqJp/:-6dfP+#o)F4~> -Y5eJ2jq@D=oJ"f`lrV;:ct0h+^J+#fi]G:smU"pWkXbD;kpt9qrss\\o)F4~> -Y5eGck!f&Gnm9jRmT.;&e47,F^M!.Mgh4l7lXT$hk&'m0k[r=;s$qY?o)F4~> -Y5eG$jq@GHniG;pmT.;,e5 -Y5eG1jq@G>nhAT^mT7A8e7H7/^J+5lgcNbplX&[Vk",8;kUY-orss\\o)F4~> -Y5eDbk=,/Hn6XXPn5dA$fLNPJ^M!=Rf4WH5k[Wdgj_ad/k@W4:!(VV?nc++~> -Y5eD#k7[PIn2f)nn5dA*fMT7_^Jsu,f13;Wk?d7Kpep96dfP+#nc++~> -Y5eD0k7[P?n1`B\n5mG6fO_[3^J+Dqf/q>nk[*FUj[f/:k:>$n!"XY\nc++~> -Y5eAak=,2ImU"FNnlED!h+,(O^M!OXd:^p2j^[Ofj)+X/j_!"8JcG6>J,~> -Y5eA"k7[SJmQ/llnlED'h,1dd^Jt22d7:ZQl!ECKqGQE6e,k4$nGe"~> -Y5eA/k7[S@mP*0ZnlNJ3h.=38^J+W"d6#fkj^.1Tj%0#:jX\glJcG6>J,~> -XoJ;ak!f,IlsA4LoN&ItiCCLS_e8p]c=b^2ia_:eiGJI.jCZn7JcG6>J,~> -XoJ;"jq@MJloNZjoN&J%iDI3h_c4cK[pSH-iD]VOj>YQiJcG6>J,~> -XoJ;/jq@M@lnHsXoN/P1iFTW<_bC"kc9'Tkia1qSiCNi9j=A^kJcG6>J,~> -XoJ8`k!f/Jl<`"JoiAIrj[ZpWc"HW]cY(m5hdc"ci,/F/ib$_6JcG3=J,~> -XoJ8!jq@PKl8mHhoiAJ#j\`WlbuDML[U8B-i)BSPi]#BhJcG3=J,~> -XoJ8.jq@PAl7gaVoiJP/j^l&@btR^kcTBcnhd5YQi(3f:i[`OjJcG3=J,~> -XoJ5_k=,8Kk[)eHpK"Opksr?[f4X>]cY)!8ggfbbhJN7.ib$_6JcG0 -XoJ4uk7[YLkW76fpK"P!ku#&pf2T4L[U8H/hGaDOi]#BhJcG0 -XoJ5-k7[YBkV1OTpK+V-l".JDf1bEkcTBlqgg9DPhFRW9i[`OjJcG0 -XoJ5_k!f/Jk?c\Gq,XUnm74c_i+Lt]cY)- -XoJ4ujq@PKk;q-eq,XUtm8:Jti)HjL[U8N1gf"2Ni&B3gJcG-;J,~> -XoJ5-jq@PAk:kFSq,a\+m:EnHi(W&kcTC#ufO"&NgdhE8i%*@iJcG-;J,~> -XoJ2^k!f2Kj^-JEqc9[lnOL2cl=\[]cY)6?emn5_g25_Zf1U\pm/MS~> -XoJ1tjq@SLjZ:pcqc9[rnPQo#l;XQL[pSZ3g/Hlff)gO'm/MS~> -XoJ2,jq@SBjY54QqcBb)nR]=Ll:fbkcTC-#em@lMg.:*Of+ -XoJ/]k=,;Lj'L8CrDo^ip.)_hoOlB]cY)?Bdpqu^g2-h&hIbD5JcG'9J,~> -XoJ.sk7[\Mj#Y^arDo^op//G(oMh8L[pS`5g/HfdfE-X(li2J~> -XoJ/+k7[\Cj"T"OrE#e&p1:jQoM!IkcTC6&dpDWLg.22phCI4iJcG'9J,~> -XoJ,\k=,>MiEk&As&PdgqFA.lrb')]cY)HEcsu`]g26h%hIbD5JcG'9J,~> -XoJ+rk7[_NiB#L_s&PdmqGFk,r`"tL[pSf7g/H`bfE-X(li2J~> -XoJ,*k7[_Di@reMs&Yk$qIR9Ur_10kcTC?)csHBKg.;2ohCI4iJcG'9J,~> -XT/&\k!f8Mhd4i?!*Pjgr^Ya -XT/%rjq@YNh`B:]S5oPcgfWK>NCUHbpKlNOg,I[dJcG$8J,~> -XT/&*jq@YDh_ -XT/#[k!f;NhHnc?r)oUe]2l[[cY)ZKb%(6[fkpV!he(P7JcG!7J,~> -XT/"qjq@\OhE'4]R8pk(NCUHbq-M`Qff.RcJcG!7J,~> -XT/#)jq@\EhD!MKr)K=r]4erAcTCQ/b$OmIfgtukh^d@kJcG!7J,~> -XT.uZk=,DOgg8W?pfX=e]2l[[cY)cNa(,!ZfkpOthe(S8JcFs6J,~> -XT.tpk7[ePgcF(]R8pk(NCUHbqd.rSf/MCbJcFs6J,~> -XT.u(k7[eFgb@AKpf4%r]4erAcTCZ2a'SXHfgtoih^dClJcFs6J,~> -XT.rYk=#AOg0WK?oNA%e]2l[[cY)lQ`+/^Xg26Rshe(V9JcFp5J,~> -XT.qok7RbPg,dq]R8pk(NCUHbr*J)UeMl4aJcFp5J,~> -XT.r'k7RbFg+_5KoMqbr]4erAcTCc5`*W@Fg.:rhh^dFmJcFp5J,~> -XT.rYk!dp/ggBPRW_^6-^M(r+pK>=Brb'r%p1N5oge34uk5Tr~> -XT.qojq?<-ge=,9]3L=c[pT,@g/HHZg]E',k5Tr~> -XT.r'jq?<#gdLX3WaNGP^HBh(pM%HMr`\#Hp+5&Ng^o&:k5Tr~> -XT.oXk!dm.hI#VPY"uZ1^hD#+qH:RCs(C&&oOm#mh+N>!jo9i~> -XT.nnjq?9,hFs>;]3L=c\6o8Bg/HBXh#`0-jo9i~> -XT.o&jq?9"hF-^1Y$ekT^c]n(qJ!]Ns'",IoISiLh%5/;jo9i~> -XT.lWk=,DEf3[BBkZP2e]2l^\cY*/Y]OTZ4nn6fkhFiG"jSs`~> -XT.kmk7[e.f/hh`R8pk(N^pQcg/H -XT.l%k7[e/f.c,NkZ+or]4euBcTD&=]O';_ngrWJh@P8 -X8hfWk=,>Cf3[ECj]T#f]2l^\cY*8\]OUqUgMQInhe(_ -X8hemk7[_,f/hkaRT6t)N^pZfr`@iQcSs\^JcFd1J,~> -X8hf%k7[_-f.c/Oj]/`s]4euBcTD/@]O(S5gIUich^dOpJcFd1J,~> -X8hcVkXGABf3[KEi*!Zf]N2d\d:`Jf]jpqSh/2Unhe(b=JcFa0J,~> -X8hblkS!b+f/hqcRT7"*O%94*]jLY -X8hc$kS!b,f.c5Qi)RBs]P,&Bd6%@V]jCS3h+6uch^dRqJcFa0J,~> -X8h`UksbDAf3[QGgf_Bf]N2d\f4Xnf^gm.Shehanhe(b=JcFa0J,~> -X8h_kkn -X8h`#kn -X8h]Tl:(G@f3[WIfNH*f]N2d\gh67f_di@SiGImnhe(e>JcF^/J,~> -X8h\jl4Wh)f/i(gRT7"*RRd!*_dE( -X8h]"l4Wh*f.cAUfN#gs]P,&BgcP-V_d<"3iCN8ch^dUrJcF^/J,~> -X8h]Tl:(D?em@TJe60gf]N2d\iFhUf`aeRSj)+'ohIb_>JcF[.J,~> -X8h\jl4We(eiN%hRT7"*T1A?*`aA: -X8h]"l4We)ehH>Ve5aOs]P,&BiB-KV`a843j%/GdhCIOrJcF[.J,~> -X8hZSlUCG>em@ZLcrnOf]N2d\k@a$fa^adSj_a3ohIbb?JcFX-J,~> -X8hYilOrh'eiN+jRT7"*V+9c*a^=L -X8hZ!lOrh(ehHDXcrJ7s]P,&Bk<%oVa^4F3j[eSdhCIRsJcFX-J,~> -X8hWRlUCD=em@`NbZW7f]N2d\lt>Bfb[^$Tk&'6nhe(k@JcFU,J,~> -X8hVhlOre&eiN1lRT7"*W^l,*b[9a=k#92WiW=]2hZ&*~> -X8hVulOre'ehHJZbZ2ts]P,&BloX8Vb[0[4k"+Vch^d[tJcFU,J,~> -WrMNQlp^G -WrMMglk8h%eiN7nRT7"*YXdP*cX5s=k>T8WirXf3h>`!~> -WrMMtlk8h&ehHP\aAp\s]P,&BniP\VcX,m4k=F\ch^d^uJcFR+J,~> -WrMNQlp^A:em@lR`*(\f]N2d\pLi/fdUVHTl##Hni+;saj@b((h#Dm~> -WrMMglk8b#eiN=pRT7"*[7An*dU20=ku5GXirXf3h#Dm~> -WrMMtlk8b$ehHV^`)YDs]P,&BpH.%VdU)*4kt'hci%"d>j:HnBh#Dm~> -WrMKPm7$D9em@rT^KK>f]N2d\r+FMfeRRZTlYYTnib&0bj\(1)g])d~> -WrMJfm1Se"eiNCrRT7"*\jt7*eR.B=lVkYZiW=]2g])d~> -WrMJsm1Se#ehH\`^K'&s]P,&Br&`CVeR%<4lU]tci[b!?jUd"Cg])d~> -WrMHOmR?G8emA#V]34&f]N2g]!+O8_fONlTm;:coib&-ak"C:*gAc[~> -WrMGemLnh!eiNItRT7"*^dl[*fO*T=m8Lh[iW=]2gAc[~> -WrMGrmLnh"ehHbb]2dcs]P,)Cc7%=OiD8i3i%3k -WrMHOmR?A6emA)X[oqcf]N30gp0uBWg1/uSmqpooj(A6bk"C:*g&HR~> -WrMGemLnateiNP!RT7"*b"'E+g0`] -WrMGrmLnauehHhd[oMKs]P,GM`@0GHhG -WrMENmmZD5emA/ZZWZKf]N3Kpm:+FNh.,2SnSR&oj_"Bbk"C:*g&HR~> -WrMDdmh4dseiNV#RT7"*dmq&+h-\o -WrMDqmh4dtehHnfZW63s]P,bV]I;TBgJ@?1gaqP;q[qs%JcFF'J,~> -WrMBMn3uG4emA2[YZ^ -WrMAcn.OgreiNY$RoR++h++b+i*Y, -WrMApn.OgsehHqgYZ:$t]P-+`Z7+X;fMD*0g+;A:q@Vm%JcFC&J,~> -WW29Ln3uD3emA8]XBG$g]N40.g1&E;j($YTo53/nk[sTbkY$L,fDg@~> -WW28bn.OdqeiN_&RoR++k!uC+j'UA=o2EF`hZAB/fDg@~> -WW28on.OdrehI"iXB"at]P-FiW@6e5ekbm.fdu>;p^u^$JcF@%J,~> -WW29Ln3u>1emA>_W*/^f]iOW9csk@1k$ukToki;nl"9Zbkt?U-f)L7~> -WW28bn.O^oeiNe(RT7%+n40*+k$QS=oi&UahZAB/f)L7~> -WW28on.O^pehI(kW)`Fs]kHmtT.&i.dnfX-f.?/:pCZX$JcF=$J,~> -WW26KnO;A0em8>`UfmFf]iOuCa(!A'l!r(TpMJGnlXofbl:Z^.ec1.~> -WW25anIjaneiEe)RT7%+qF?f+l!Me=pJ\gch?&9.ec1.~> -WW25nnIjaoeh@(lUfI.s]kI7)Ppkm'cqjC,eL^#:ob$I#JcF:#J,~> -WW23JnjVD/f3\JaU07@haB&"C^gbVulsn:Tq/+VolXofbl:Z^.eGk%~> -WW22`ne0dmf/iq+S5m7-N\]s.bua^>^,OmMJcF7"J,~> -WW22mne0dnf.d4nU/h(uaCt9PN[X7#btn.+e1Bo9ob$I#JcF7"J,~> -WW23JnjV>-fj=P_Tih@kp/]7C^gbVumpjLTqeabom:PrblUug/e,Op~> -WW22`ne0^kffK")TN/[1N\^'1b#eI=^,OjLJcF4!J,~> -WW22mne0^lfeE:lTiD)#p1VNPN[X@&b"qn*dOac9o+C:"JcF4!J,~> -W;l*Io0qD-g0XM\UfkB>^gbVunmf^TrGBnomUl#blq;p0df4g~> -W;l)_o+Kdkg,et&UfG*5N\^04a&i4<]f4aKJcF0uJ,~> -W;l)lo+Kdlg+`7iUfG*5N[XI)a%uY)cn+T8ne(4"JcF0uJ,~> -VuQ!HoL7G,gg9SZW*-fB^gbVuojbpTs)$%omq2)bm7W$1dJn^~> -VuPu^oFfgjgcG%$W)^N9N\^97`)lt;]JnXJJcF-tJ,~> -VuPukoFfgkgbA=gW)^N9N[XR,`)$D(c7JE7nIb."JcF-tJ,~> -VuPsGogRJ+hHoYXXBE5F^gbVupg_-T!,]tnnRh5bmRr-2d/SU~> -VuPr]ob,jihE(+"XAur=N\^B:_,gV8]f4^JJcF*sJ,~> -VuPrjob,jjhD"CeXAur=N[X[/_+t&%bq/B8mh+t!JcF*sJ,~> -VZ5mGogRD)i*P_VYZ\YJ^gbVuqd[EV!,9Vhnn.;bmRr-2d/SU~> -VZ5l]ob,dgi&^0uYZ8AAN\^K=^fLM#]JnRHJcF*sJ,~> -VZ5ljob,dhi%XIcYZ8AAN[Xd2^eXqdb:N37mLejuJcF*sJ,~> -V>odFogRA(ia1eTZrt(N^gbZ!rF -V>oc\ob,afi]?6sZrOeEO#$Z@^fUJ!^,OaIJcF'rJ,~> -V>ociob,agi\9OaZrOeEO!ss5^eanbbUi?9m1JatJcF'rJ,~> -V>oaEp-mD'jBgkR\66LR^gbZ!!+WfZqJ"/doOdGbmn863cMrC~> -V>o`[p(Gdej>u -V>o`hp(Gdfj=oU_\5g4IO!k!7^eae_bUiB:lk/[tJcF$qJ,~> -V#TXDpI3G&k$HqP]NMpV^gbf%rD^XKpM%iap1ESbn4S?4c2W:~> -V#TWZpCbgdjuVBo]N)XMP;<(R_H6Ir_)L$KJcF!pJ,~> -V#TWgpCbgejtP[]]N)XMP:6AG_GBn^bUiH -U]9RDpI3A$k?cqN^fe?Z^gc#+pJf+HoP)N^pL`YbnOnH5bl<1~> -U]9QZpCbabk;qBm^fA'QR54LR`E2[r_Dg-LJcEsoJ,~> -U]9QgpCback:k[[^fA'QR4.eG`D?+^bUiK=kn3FsJcEsoJ,~> -U]9OCpdND#l!E"L`*'c^^gc20nl3\FnS-3[ph&_bnk4Q6bQ!(~> -U]9NYp_(dakrRHk`)XKUShfjRaB.mr_`-6MJcEpnJ,~> -U]9Nfp_(dbkqLaY`)XKUSga.GaA;=^bUiN>kRm@sJcEpnJ,~> -UAsFBq*iG"lX&(JaB?2b^gcA5m8V5CmqL!YqI\kbo1OZ7b5Zt~> -UAsEXq%Cg`lT3NiaAooYUGD3Rb#e$r`AcENJcEmmJ,~> -UAsEeq%CgalS-gWaAooYUF>LGb"qI^bUiT@jq71rJcEmmJ,~> -U&X@Bq*i@um9\.HbZVVf^gcS;k>]]@ltO^WqI\kbo1OZ7ao?k~> -U&X?Xq%Ca^m5iTgbZ2>]WA -U&X?eq%Ca_m4cmUbZ2>]W@6pGbtm[^bq/]Ajq71rJcEjlJ,~> -T`=7AqF/Ctmp=4Fcrn%j^gcb@i`+9>l=nITr+>"boLjc8aT$b~> -T`=6Wq@^d]mlJZecrIbaXtnuRcr]Ksa#DTOJcEgkJ,~> -T`=6dq@^d^mkDsScrIbaXsi9Gcqip_bUiZBj:V"qJcEgkJ,~> -T`=4@qF/@snQs:De60In^gcqEh,Mj -T`=3Vq@^a\nN+`ce5a1eZSL>RdoY]sa>_ZOJcEgkJ,~> -T`=3cq@^a]nM&$Qe5a1eZRFWGdnf-_bUi]Cit:npJcEgkJ,~> -TE"+?qaJCro3T@BfNGmr^gd+JfMpF:jCuhNs(:4boh0l9a8^Y~> -TE"*Uq\$d[o/afafN#Ui\2)\RelUosau@iPJcEdjJ,~> -TE"*bq\$d\o.\*OfN#Ui\1#uGekb?_bUicEi=Y_oJcEdjJ,~> -T)\%?qaJ=poj5F@gf_=!_.*CPdT"n7iG$MK!+t.ap.Ku:`rCP~> -T)\$Uq\$^YofBl_gf;$m^,"+RfiR,sb;[rQJcEaiJ,~> -T)\$bq\$^Zoe=0Mgf;$m^*qDGfh^Q_bU``Ei">YoJcEaiJ,~> -T)\">r'eCpp0PC=i*!a%`+$qighP\[c"@#Pi(iu=JcE^hJ,~> -T)\!Tr"?dYp,]i\i)RHq`%oORgfN>sbr=,RJcE^hJ,~> -T)\!ar"?dZp+X-Ji)RHq`$kU%cPPA4hD -Sc@n=rC+Fopg1I;jB90)c=4[jhJ1eZc=d/Qi(j#>JcE[gJ,~> -Sc@mSr=ZgXpc>oZjAiluc8*9ShH/Grc8X5SJcE[gJ,~> -Sc@m`r=ZgYpb93HjAiluc7&<%ckkP7gG@,+h@]JnJcE[gJ,~> -SH%eRhbNr>JcEXfJ,~> -SH%dRrXujWqDtuXkZ,<$f.soSiE+Yrco9DTJcEXfJ,~> -SH%d_rXujXqCo9FkZ,<$f-or%ckkY:fJCl*g_';mJcEXfJ,~> -S,__ -S,_^RrXudUr&V&VlrC`(iA.VSjB'krdPoSUJcEUeJ,~> -S,_^_rXudVr%P?DlrC`(i@*Y%ckkb=eMGW)g(F,lJcEUeJ,~> -S,_\;s$aFkra)[5n6*G5l=-YjkA&I[dqAMQhbO#@JcERdJ,~> -S,_[Qrt;gTr]7,Tn5[/,l8#7Sk?$+sdl5YUJcERdJ,~> -S,_[^rt;gUr\1EBn5[/,l6t:%ckkk@dkfH(fFdrkJcERdJ,~> -RfDS:s$aCjs'DX2oi\t:o4":jl>"[[eS"YQhbO&AJcEOcJ,~> -RfDRPrt;dSs#R)Qoi8\1o.lmSl;u=seMkhVJcEOcJ,~> -RfDR]rt;dTs"LB?oi8\1o-hp%ckktCcnj3'ee.cjJcEOcJ,~> -RK)M:s$a=h!*uF.q,tC>rF2!jm:sm[en=_Qi(j/BJcELbJ,~> -RK)LPrt;^Q!&UNHq,P+5rA'TSm8qOsei1qWJcELbJ,~> -RK)L]rt;^R!%Og6q,P+5r@#W%ckl(Fbqmp%ee.cjJcELbJ,~> -RK)J9!(F:hr*?.+rE7TXlXu;Yn7p*[fOskQi(j/BJcELbJ,~> -RK)IO!"u[Qr%t6ErDh -RK)I\!"u[Rr$nO3rDh -R/cA8[7p@$V-j10_.Sj/o4l<[g1U%RhbO)BJcEIaJ,~> -R/c@N[1i -R/c@[[1rBcV+(>f_+MN\ckl:La"uF#dLlBgJcEIaJ,~> -QiH;8s$aCjoNeG']k -QiH:NrrTYCoJEOA]i1"0p/f0sgc*FYJcEF`J,~> -QiH:[rsuRQoI?h/]h6*XcklCO`&$1"ck63fJcEF`J,~> -QiH;8rC+7jn6N/']k -QiH:Nr;sMCn2.7A]i1"0q,bBshD`UZJcEC_J,~> -QiH:[r=?FQn1(P/]h6*XcklLR_)'q!c4U$eJcEC_J,~> -QN-58r'e1jls6l']k -QN-4NquXGClnktA]i1"0r)^Tsh`&^[JcE@^J,~> -QN-4[r"$@Qlmf8/]h6*XcklUU^,+Xtbn9seJcE@^J,~> -Q2g/8qF/%jkZtT']k -Q2g.Nq?";CkVT\A]i1"0s&Ziti&Ad[JcE=]J,~> -Q2g.[q@C4QkUNu/]h6*Xckl^X]JJIsbRsjdJcE=]J,~> -PlL)8pdMnjjB]<']k -PlL(Np]A/Cj>=DA]i1%1\Nf(4aT@%n\c70~> -PlL([p^b(Qj=7]/]h6*Xd20>Li[i4a!"a_]\c70~> -PlL&7pI2kki*F$']k -PlL%MpB&,Di&&,A]i146quXDnj>Y-]JcE7[J,~> -PlL%ZpCG%Ri$uE/]h6*Xeee5q\MN:ta:X"'\Gq'~> -PQ0u7ogQ_kgg.a']k -PQ0tMo`DuDgbciA]i1C;pB%liju:<^JcE4ZJ,~> -PQ0tZoaenRga^-/]h6*XgDBSq\MNA!`t -P5jo7o0pSkfNlI'^1X';nk6N-k@`sRiD+t([f:j~> -P5jnMo)ciDfJLQA^/LX@ncH?dk;UE_JcE1YJ,~> -P5jnZo+/bRfIFj/^.Q0Xi"tqq\MND"`t -P5jo7nO:Gke6U1'^1X6@m7Y!(l"B*Rj%b1*[/YX~> -P5jnMnH-]De259A^/LgEm/jg_kr6WaJcE+WJ,~> -P5jnZnINVRe1/R/^.Q3Yj;71p\MNJ$`t -OoOi7n3t>jd9Y"(^1XEEkY&I#lY#6RjA(:+Zi>O~> -OoOhMn,gTCd59*B^/M!JkQ8:ZlSlfbJcE(VJ,~> -OoOhZn.3MQd43C0^.Q3YkniOp\MNP&`Y!e%Zi>O~> -OT4c7mR>2jc<\e(^1XTJj%Hpsm:YBRk"^L-Z2]=~> -OT4bMmK1HCc8 -OT4bZmLRAQc7710^.Q3YmMFmp\MNV(`Y!e%Z2]=~> -O8n]7lp]&jb$EM(^1XcOhFkFom:Y?Qk>$U.YlB4~> -O8n\MliP -O8n\Zljq5Qastn0^.Q3Yo,$6p\hi_)`Y!e%YlB4~> -O8n]7l:&oj`a.5(^1XrTfh8njmq:NRk>$U.YQ'+~> -O8n\Ml2o0C`\c=B^/MNYf`J`Lml/2eJcDqRJ,~> -O8n\Zl4;)Q`[]V0^.Q3Yp_VTp\hie+`=[\$YQ'+~> -NrST6ks`lk_Hkr(^1Y,Ye4[AenRpZRktZg0XoEn~> -NrSSLklT-D_DL%B^/M]^e,m3GnMeDgJcDkPJ,~> -NrSSYkmu&R_CF>0^.Q3Yr>3rp\hik-`=[\$XoEn~> -NW8N6k=*`k^0TZ(^1YA`r_1[3\nUbKgJ@*0JcDhOJ,~> -NW8MLk5s!D^,4bB^/MreblYI@o/FShJcDhOJ,~> -NW8MYk7>oR^+/&0^.Q<\rXmKg\hiq/`"@S#XT*e~> -NW8N6j[ITk\m=B(^1Y\ioh<_*\nUhMfh^s0JcDbMJ,~> -NW8MLjT -NW8MYjU]cR\glc0^.QWeob#O^\hj"1`"@S#WrIS~> -N;rH6j$hHk[U&*(^1Z"rlqGf"\S:bMfMCm0JcD_LJ,~> -N;rGLir[^D[P[2B^/NT"]E5W.p,BnkJcD_LJ,~> -N;rGYit'WR[OUK0^.Qrnlk.VV\MNq1`"@S#WW.J~> -MuWB6i^MBkZ&j%Rin\S:hOekb^/JcD\KJ,~> -MuWALiW@XDZ8CoB^/No+ZN@[%pc$(lJcD\KJ,~> -MuWAYiXaQRZ7>30^.R9"it9ZM\MO"3_\%J"W;hA~> -MZ<<6i'l6kY$LO(^1ZY/g.]me\S:nQe5,R/JcDVIJ,~> -MZ<;Lhu_LDXu,WB^/O54WWK^qqDZ:nJcDVIJ,~> -MZ<;Yi"+ERXt&p0^.RT+g(D^D\MO(5_\%J"VZ2/~> -MZ<<6hF6*kWa57(^1["9cqMh[\nV%SdnfI.JcDSHJ,~> -MZ<;Lh?)@DW\j?B^/OS>TE;\hq_u@nJcDSHJ,~> -MZ<;Yh@J9RW[dX0^.Rr5ck4Y:\hj47_@_A!V>l&~> -M?!66gdTskVHrt(^1[=Ba%XlR\nV+Ud80=.JcDMFJ,~> -M?!5Lg]H4DVDS'B^/OnGQNF`_rAVRpJcDMFJ,~> -M?!5Yg^i-RVCM@0^.S8>`t?]1\hj:9_@_A!U]5i~> -M#[-5gI9plU0[\(b\-NX_+`6L\nV.Vcqj7.JcDJEJ,~> -M#[,KgB-1EU,;dBbZ"*nOTN*Yr\q[qJcDJEJ,~> -M#[,XgCN*SU+6(0bY%IY_%G'+\hj=:_@_A!UAo`~> -M#[-5fgXakT3VG(p1MKX_+`6L\nM.Wc;4(-JcDGDJ,~> -M#[,Kf`L"DT/6OBp/B'nOTN*Y!&q^qJcDGDJ,~> -M#[,XfalpRT.0h0p.EFY_%G'+\ha=;_%D7uU&TW~> -L]@'5f1"UkSmB'@_+`6L]P7C@btn%.JcDABJ,~> -L]@&Kf)jkDSi"/AOTN0[^]K)eTDsE~> -L]@&Xf+6dRSgqH+_%G'+]JI>8JcDABJ,~> -LB%!5eOAIkSmB'@_+`6L^1mO@btn(/JcD>AJ,~> -LB$uKeH4_DSi"/AOTN6]^B/udT)X<~> -LB$uXeIUXRSgqH+_%G'+^,*M9JcD>AJ,~> -LB%!5e4&CkSmB'@_+`6L_.ia@c;410JcD;@J,~> -LB$uKe,nYDSi"/AOTN?`]`NcbSc=3~> -LB$uXe.:RRSgqH+_%G'+_)&b:JcD;@J,~> -L&^p5dRE7kSmB'@_G&J,~> -L&^oKdK8MDSi"/AOTNHc]E3ZaS,\!~> -L&^oXdLYFRSgqH+_@b-+`&#%J,~> -K`Cj5cpd+kSR'!@_G& -K`CiKciWADSM\)AOTNQf\cRH_Rf@m~> -K`CiXck#:RSLVB+_@b-+a"t:=JcD2=J,~> -KE(d5c:-tkSR'!@_G&?Ma_C<@btn74JcD,;J,~> -KE(cKc3!5DSM\)AOoi`i\H7?^R/_[~> -KE(cXc4B.RSLVB+_@b0,aYUI>JcD,;J,~> -KE(d5bXLhkSR'!@_G&?Mb\?N@btn:5JcD):J,~> -KE(cKbQ@)DSM\)AOoiil[fV-\QiDR~> -KE(cXbRa"RSLVB+_@b0,bVQ^?JcD):J,~> -K)b[4b=1elSR'!@_G&?McY;`@btn=6JcD&9J,~> -K)bZJb6%&ESM\)AOoiro[/tpZQN)I~> -K)bZWb7EtSSLVB+_@b0,cSMs@JcD&9J,~> -JcGU4a[PYlSR'!@_G&?Md:ql@bte=7JcCu7J,~> -JcGTJaTCoESM\)AOoj#q[/tpZPlH7~> -JcGTWaUdhSSLVB+_@b0,d5/0BJcCu7J,~> -JcGU4a$oMlSR'!@_G&?Me7n)@c;+F8JcCr6J,~> -JcGTJ`rbcESM\)AOoj,tZN>^XPQ-.~> -JcGTW`t.\SSLVB+_@b0,e2+ECJcCr6J,~> -Jc>`Mr'87jnk>T[^LY2Td7`)*jA+#sJcCl4J,~> -Jc>`MquOe=ne7QN^H%GYf0?PBJcCl4J,~> -Jc>`Mr!p^Wne@WE^Fd;7d1FncZ4VZfOoKq~> -JcGcMrBS:ioLtf]^LY2Td7`2-iD.crJcCf2J,~> -JcGcMr;jh -JcGcMr=6aVoG!iG^Fd;7d1G"fYn;QeO8j_~> -JcG`Lr]n=hoh:r_^LY2Td7`;0hbMQpJcCc1J,~> -JcG`LrW0k;ob3oR^H%GYh*8(EJcCc1J,~> -JcG`LrXQdUob -JcG`Lr]n7fpIq/a^LY2Td7`D3geQ -JcG`LrW0e9pCj,T^H%GYi'4@GJcC]/J,~> -JcG`LrXQ^SpCs2K^Fd;7d1G4lXq?6bN;nD~> -JcG]Ks$4:eq+RAc^LY2Td7`J5g.p0oJcCW-J,~> -JcG]KrrKh8q%K>V^H%GYi]jRIJcCW-J,~> -JcG]KrslaRq%TDM^Fd;7d1G:nXq?6bMZ82~> -JcGZJs$47dqb3Se^LY2Td7`S8f1spnJcCQ+J,~> -JcGZJrrKe7q\,PX^H%GYjZfjKJcCQ+J,~> -JcGZJrsl^Qq\5VO^Fd;7d1GCqXV$-aM#Vu~> -JcGZJs$41brCieg^LY2Td7`\;e5"XlJcCN*J,~> -JcGZJrrK_5r=bbZ^H%GYkWc*LJcCN*J,~> -JcGZJrslXOr=khQ^Fd;7d1GLtWtBp_L];l~> -JcGWI!'n(`s%K"i^LY2TdS&k>d8&CkJcCH(J,~> -JcGWI!!0V3rtCt\^H%JZl9D9MJcCH(J,~> -JcGWI!"QOMrtM%S^Fd;7dLb\"WY'g^L&ZZ~> -JcGTH\4$BkSmB*A_G&BNm:jj@geWM$KE$H~> -JcGTH\,lXDSi"2BP61,9W<.YNKE$H~> -JcGTH\.8QRSgqK,_@b3-m5(^RJcCB&J,~> -JcGQG\4-BdTj>ED_G&BNn7g'@hG8_&JcC6~> -JcGQG[0!$Y^H%JZn3 -JcGQG[1Ar\^Fd;7dLbn(W"FU\JcC6~> -JcGKE\jcKcUg:`G_G&BNnnH6AhG8_&Jc>`MJ,~> -JcGKEZi[$[^H%JZnis#RJcC<$!<7Q~> -JcGKEZk&r^^Fd;7dLbt*V\+L[Jc>`MJ,~> -JcGEC]LDQaW*R/K_bAHNokDHAi(nq(JcG`LJ,~> -JcGECZ3$s]^H%JZofo;TJcC<$rr7K~> -JcGECZ4El`^Fd>8d1Gt,V@eCZJcG`LJ,~> -JcG?A^.%W_XBiSO_bAHNph@ZAi_P.*JcGZJJ,~> -JcG?AYQCm_^H%JZpckSVJcC<$r;V9~> -JcG?AYRdfb^Fd>8d1H(/V%J:YJcGZJJ,~> -JcG9?^d[]]Y[,"S_bAHNqe -JcG9?Xobga^H%JZq`ghWJcC<$qu;0~> -JcG9?Xq.`d^Fd>8d1H12UCi(WJcGWIJ,~> -JcG6>_+!ZZZsCFW_bAHNrb9)Aj\LI-JcGQGJ,~> -JcG6>WrfXb^H%JZr]d+YJcC<$q>Ys~> -JcG6>Wt2Qe^Fd>8d1H:5U(MtVJcGQGJ,~> -JcG0<_F -JcG0^XJcGKEJ,~> -JcG08d1Du`JcC<$p]#a~> -JcG*:`'r`V]Nr9__bAHN]2%TdJcC<$p&BO~> -JcG*:VZOLf^H$iH[/tpZJcGECJ,~> -JcG*:V[pEi^Fd>8d1E&bJcC<$p&BO~> -JcG$8`^SfT^g4]c_bAHN]2%WeJcC<$o`'F~> -JcG$8V#nFh^H$iH[K;$[JcGBBJ,~> -JcG$8V%:?k^Fd>8d1E)cJcC<$o`'F~> -JcFs6a@4lR`*L,g_bAHN]M@cgJcC<$o)F4~> -JcFs6UB8@j^H$iH\,q6]JcG<@J,~> -JcFs6UCY9m^Fd>8d1E/eJcC<$o)F4~> -JcFm4b!jrPaBcPk_bAKO]2%`hJcC<$nGe"~> -JcFm4T`W:l^H$iH\cRH_JcG6>J,~> -JcFm4Tb#3o^Fd>8dL`;gJcC<$nGe"~> -JcFj3b=0rNb?_kn_bAKO]2%fjJcC<$mf.e~> -JcFj3T*!1m^H$iH]E3ZaJcG0 -JcFj3T+B*p^Fd>8dL`AiJcC<$mf.e~> -JcFd1bsg#LcX":r_bAKO]2%ikJcC<$mJh\~> -JcFd1SH@+o^H$iH]`NcbJcG-;J,~> -JcFd1SIa$r^Fd>8dL`DjJcC<$mJh\~> -JcF^/cUH)Jdp9_!_bAKO]2%omJcC<$li2J~> -JcF^/Rf_%q^H$iH^B/udJcG'9J,~> -JcF^/Rh*st^Fd>8dL`JlJcC<$li2J~> -JcFX-d7)/Hf3Q.%_bAKO]2%uoJcC<$l2Q8~> -JcFX-R0(ts^H$iH_#f2fJcG!7J,~> -JcFX-R1In!^Fd>8dL`PnJcC<$l2Q8~> -JcFR+dm_5FgKhR)_bAKO]2&&qJcC<$kPp&~> -JcFR+QNGnu^H$iH_ZGDhJcFp5J,~> -JcFR+QOhh#^Fd>8dL`VpJcC<$kPp&~> -JcFO*e4%2Chd+!-_bAKO]2&)rJcC<$k5Tr~> -JcFO*PQK`!^H$iH_ubMiJcFm4J,~> -JcFO*PRlY$^Fd>8dL`YqJcC<$k5Tr~> -JcFI(ej[8Aj'BE1_bAKO]MA5tJcC<$jSs`~> -JcFI(OojZ#^H$iH`WC_kJcFg2J,~> -JcFI(Oq6S&^Fd>8dL`_sJcC<$jSs`~> -JcFC&fL<>?k?Yi5_bAKO]MA -JcFC&O94T%^H$iHa9$qmJcFa0J,~> -JcFC&O:UM(^Fd>8dL`euJcC<$ir=N~> -JcF=$fgW>=lWq;:_bAHN]MAB#JcC<$i;\<~> -JcF=$NWSN'^c?rIaT@%nJcF[.J,~> -JcF=$NXtG*^b*G9d1Ec!JcC<$i;\<~> -JcF7"gI8G -JcF7"N<8N)^c?rIao[.oJcFX-J,~> -JcF7"N=YG,^b*G9d1Ef"JcC<$huA3~> -JcF0uh*nM:nm0%A_bAKO]2&B%JcC<$h>`!~> -JcF0uMZWH+^c?rIbQ<@qJcFR+J,~> -JcF0uM\#A.^b*G9dL`r$JcC<$h>`!~> -JcF-thF4J7p0GIE_bAKO]2!qWJcFL)J,~> -JcF-tL][9,^c?rIc2rRsJcFL)J,~> -JcF-tL_'2/^b*G9dLa#&JcC<$g])d~> -JcF'ri'jP5qH^mI_bAKO]hX.YJcFF'J,~> -JcF'rL'%3.^c?rIciSduJcFF'J,~> -JcF'rL(F,1^b*G9dLa)(JcC<$g&HR~> -JcF!pi^KV3ra"MoThNm-^eTI\JcF=$J,~> -JcF!pKED-0i]2PkYl]LVJcF=$J,~> -JcF!pKFe&3i[pi9dLa2+JcC<$f)L7~> -JcEpnj@,_2JkXTc_bPd_JcF4!J,~> -JcEpnJHCB(QNDa -JcEpnK+EPGdLa;.JcC<$e,Op~> -JcEjlk!bq4JkXTc`_M*bJcF*sJ,~> -JcEjlJHCB(S-"9AJcF*sJ,~> -JcEjlKb&bIdLaD1JcC<$d/SU~> -JcEdjkXD.6JkXTcaA. -JcEdjJHCB(TE9]EJcF$qJ,~> -JcEdjLC\tKdLaJ3JcC<$cMrC~> -JcEaiks_77JkXTcb>*WgJcEpnJ,~> -JcEaiJHCB(U]Q,IJcEpnJ,~> -JcEaiL_#(LdLaS6JcC<$bQ!(~> -JcE[glU@I9JkXTcc;&rjJcEgkJ,~> -JcE[gJHCB(W<.YNJcEgkJ,~> -JcE[gM@Y:NdLa\9JcC<$aT$b~> -JcEUem7![;JkXTcd8#8mJcE^hJ,~> -JcEUeJHCB(Xoa1SJcE^hJ,~> -JcEUeN":LPdLae -JcEOcmmWm=JkXTcdnYJoJcEXfJ,~> -JcEOcJHCB(Z3#UWJcEXfJ,~> -JcEOcNXp^RdLak>JcC<$_uG5~> -JcEIan3s$?JkXTcekUerJcEOcJ,~> -JcEIaJHCB([fV-\JcEOcJ,~> -JcEIaO:QpTdLatAJcC<$_#Jo~> -JcEF`nO9-@JkXWdfM7"tJcEF`J,~> -JcEF`JHCB(])mQ`JcEF`J,~> -JcEF`OUm$Udh(.DJcC<$^&NT~> -JcE@^o0o?BJkXWdgJ3>"JcE=]J,~> -JcE@^JHCB(^]K)eJcE=]J,~> -JcE@^P7N6Wdh(7GJcC<$])R9~> -JcE:\ogPQDJkXWdh+iP$JcE7[J,~> -JcE:\JHCB(_ubMiJcE7[J,~> -JcE:\Pn/HYdh(=IJcC<$\Gq'~> -JcE4ZpI1cFJkXWdi(ek'JcE.XJ,~> -JcE4ZJHCB(aT@%nJcE.XJ,~> -JcE4ZQOeZ[dh(FLJcC<$[Jta~> -JcE.Xq*guHK1s]dj%b1*JcE%UJ,~> -JcE.XJHCB(c2rRsJcE%UJ,~> -JcE.XR1Fo^dLbFNJcC<$ZN#F~> -JcE(VqaI/IKM9fek"^L-JcDqRJ,~> -JcE(VJHCB(dfP+#JcDqRJ,~> -JcE(VRLb&`dLbOQJcC<$YQ'+~> -JcE%Ur'd8JKM9fekY?^/JcDkPJ,~> -JcE%UJHCB(ecLF&JcDkPJ,~> -JcE%URh(/adLbUSJcC<$XoEn~> -JcDtSr^EJLKM9felV<$2JcDbMJ,~> -JcDtSJHCB(gB)s+JcDbMJ,~> -JcDtSSI^AcdLb^VJcC<$WrIS~> -JcDnQ!(EPMKM9femS8?5JcDYJJ,~> -JcDnQJHCB(hu\K0JcDYJJ,~> -JcDnQT+?SedLbgYJcC<$VuM8~> -JcDeNU.0f0dS'+*JcC<$V#Pr~> -JcDeNJHCB(joU,6JcDPGJ,~> -JcDeNU(;nhdLbp\JcC<$V#Pr~> -JcD\KV+-,3dS'1,JcC<$UAo`~> -JcD\KJHCB(lN2Y;JcDJEJ,~> -JcD\KV%84kdLc!^JcC<$UAo`~> -JcDPGWCDP7dS':/JcC<$TDsE~> -JcDPGJHCB(ncFCBJcDABJ,~> -JcDPGW=OXodLc*aJcC<$TDsE~> -JcDDCX[[t;dnBI2JcC<$SH"*~> -JcDDCJHCB(q#Z-IJcD8?J,~> -JcDDCXUg'sdh)9dJcC<$SH"*~> -JcD8?YssC?dnBR5JcC<$RK%d~> -JcD8?JHCB(!!7`OJcD/ -JcD8?Yn)L"dh)BgJcC<$RK%d~> -JcD/ -JcD/ -JcD/ -JcD#8\42-Fdn9[9JcC<$PlH7~> -JcD#8JHCc3JcC<$PlH7~> -JcD#8\.=6)dguKkJcC<$PlH7~> -JcCl4]LIQJek:SoJcCi3J,~> -JcCl4JHD&;JcC<$OT0h~> -JcCl4]FTZ-ee!E4JcCi3J,~> -JcC`0^d`uNgIm+tJcCZ.J,~> -JcC`0JHDADJcC<$MuS;~> -JcC`0^^l)1gCSr9JcCZ.J,~> -JcCT,`(#DRi(JY$JcCK)J,~> -JcCT,JHD\MJcC<$LAuc~> -JcCT,`".M5i"1J>JcCK)J,~> -JcCK)a$t_Uj\(1)JcC<$J,~> -JcCK)JHDtUJcC<$JcC6~> -JcCK)`t*h8jUd"CJcC<$J,~> -JcC?%b=7.Ykt?U-JcC<$rVqB~> -JcC?%JHE7]JcC<$JcG]KJ,~> -JcC?%b7B7 -JcC<$rr:;6KhUl,JcC<$JcGNFJ,~> -JcC<$rr7NN^B/udJcC<$q#>j~> -JcC<$rr::TKb<\`JcC<$JcGNFJ,~> -JcC<$qZ##6KhV&1JcC<$JcG?AJ,~> -JcC<$qYu*Ja9$qmJcC<$oDa=~> -JcC<$qZ#"TKb -JcC<$pA``6KhV25JcC<$JcG3=J,~> -JcC<$pA][FciSduJcC<$n,In~> -JcC<$pA`_TKb="iJcC<$JcG3=J,~> -JcC<$oDdN6KhVA:JcC<$JcG$8J,~> -JcC<$oDa@CfE-X(JcC<$lMlA~> -JcC<$oDdMTKb=1nJcC<$JcG$8J,~> -JcC<$n,M66KhMJ>JcC<$JcFj3J,~> -JcC<$n,Iq?i<"T1JcC<$jo9i~> -JcC<$n,M5TKb4:rJcC<$JcFj3J,~> -JcC<$li5s6N(eg&JcC<$h>`!~> -JcC<$li2M;m/hk=JcC<$h>`!~> -JcC<$li5rTN"LX@JcC<$h>`!~> -JcC<$kPs[6PY?Z.JcC<$ec1.~> -JcC<$kPp)7q#Z-IJcC<$ec1.~> -JcC<$kPsZTPS&KHJcC<$ec1.~> -JcC<$jT"I6S4nM6JcC<$c2W:~> -JcC<$jSsl7JcC<$JcF!pJ,~> -JcC<$jT"HTS.U>PJcC<$c2W:~> -JcC<$i;`16V+cI?JcC<$`;b>~> -JcC<$i;\o@JcC<$JcE[gJ,~> -JcC<$i;`0TV%J:YJcC<$`;b>~> -JcC<$h#Hn6X\= -JcC<$h#EoHJcC<$JcEC_J,~> -JcC<$h#HmTXV$-aJcC<$]`3K~> -JcC<$f`1V6[S28PJcC<$Zi>O~> -JcC<$f`.rQJcC<$JcE(VJ,~> -JcC<$f`1UT[Ln)jJcC<$Zi>O~> -JcC<$eGo>6^.a+XJcC<$X8d\~> -JcC<$eGlrYJcC<$JcDeNJ,~> -JcC<$eGo=T^(GqrJcC<$X8d\~> -JcC<$dJs,6ekCYpJcC<$PQ-.~> -JcC<$dJqSqJcC<$JcCr6J,~> -JcC<$dJs+Tee*K5JcC<$PQ-.~> -JcC<$c2Rc5pe68=JcC<$JcG9?J,~> -JcC<$c2[M>JcC<$JcC<$nc++~> -JcC<$c2RbSp^r)WJcC<$JcG9?J,~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -JcC<$JcC<$JcC<$Zi>O~> -%%EndData -showpage -%%Trailer -end -%%EOF diff --git a/doc/introduction.tex b/doc/introduction.tex deleted file mode 100755 index 3fbe8604..00000000 --- a/doc/introduction.tex +++ /dev/null @@ -1,233 +0,0 @@ -\chapter{Introduction} - -\section{What is PyTOUGH?} -\index{PyTOUGH} - -PyTOUGH (\textbf{Py}thon \textbf{TOUGH}) is a set of Python software routines for making it easier to use the TOUGH2 geothermal reservoir simulator. Using PyTOUGH, it is possible to automate the creation and editing of TOUGH2 model grids and data files, and the analysis and display of model simulation results. - -\section{What are TOUGH2 and AUTOUGH2?} -\index{TOUGH2} -\index{TOUGH2!AUTOUGH2} - -TOUGH2 \citep{tough2} is a general-purpose simulator for modelling subsurface fluid and heat flow, often used for simulating geothermal reservoirs. - -AUTOUGH2 is the University of Auckland version of TOUGH2. The main differences between AUTOUGH2 and TOUGH2 are: - -\begin{itemize} - \item \textbf{EOS handling}: AUTOUGH2 includes all different equations of state (EOSes) in a single executable program, whereas TOUGH2 uses different executables for each EOS. As a result, the main input data file for an AUTOUGH2 simulation also includes extra data blocks to specify which EOS is to be used. - \item \textbf{Generator types}: AUTOUGH2 includes a variety of extra generator types developed for geothermal reservoir simulation (e.g. makeup and reinjection wells). -\end{itemize} - -\index{TOUGH2!TOUGH2-MP} -\index{TOUGH2!TOUGH+} -TOUGH2\_MP \citep{tough2mp} is a multi-processor version of TOUGH2. TOUGH+ is a redeveloped version of TOUGH2, with a more modular code structure implemented in Fortran-95. - -\subsection{TOUGH2 data files} -\index{TOUGH2 data files} -\index{TOUGH2} -\index{TOUGH2!AUTOUGH2} - -TOUGH2 takes its main input from a \textbf{data file}, which contains information about the model grid, simulation parameters, time stepping, sources of heat and mass etc. The data file formats for TOUGH2 and AUTOUGH2 are almost identical, with minor differences. TOUGH2\_MP can read TOUGH2 data files, but also supports some extensions (e.g. for 8-character instead of 5-character block names) to this format. PyTOUGH does not currently support the TOUGH2\_MP extensions. TOUGH+ data files can also have some extensions, which PyTOUGH does not support as yet. - -Because TOUGH2 uses a finite volume formulation, the only model grid data it needs are the volumes of the grid blocks and the distances and areas associated with the connections between blocks. Hence, the TOUGH2 data file need not contain any information about the specific locations of the blocks in space, and it contains no information about the locations of the vertices or edges of the blocks. This makes it easy to use TOUGH2 to simulate one-, two- or three-dimensional models, all with the same format of data file. However, this lack of reference to any coordinate system also makes it more difficult to generate model grids, and to visualise simulation results in space. - -\subsection{MULgraph geometry files} -\index{MULgraph geometry!files} - -For this reason, a separate \textbf{geometry file} can be used to create grids for TOUGH2 simulations and visualise simulation results. The geometry file contains information about the locations of the grid block vertices. The geometry file can be used to visualise results using the \textbf{MULgraph} graphical post-processor for TOUGH2 and AUTOUGH2 \citep{mulgraph}, developed at the University of Auckland in the 1990s. - -The MULgraph geometry file assumes the grid has a layered structure, with blocks arranged in layers and columns, and the same arrangement of columns on each layer. (At the top of the model grid, blocks in some columns may be missing, to allow the grid to follow the surface topography.) - -If you do not have a MULgraph geometry file for your model, it is easy to create one for a \hyperref[sec:mulgrid:rectangular]{rectangular} grid. In fact, PyTOUGH is able to \hyperref[sec:t2grid:rectgeo]{reverse-engineer} a MULgraph geometry from a TOUGH2 data file containing a rectangular grid. - -A specification of the MULgraph geometry file format can be found in Appendix \ref{geometry_file_format}. - -\subsection{TOUGH2 listing files} -\index{TOUGH2 listing files} - -The output from TOUGH2 is written to a \textbf{listing file}, which is a text file containing tables of results for each time step (or only selected time steps, if preferred). At each time step there is an `element table', containing results for block properties (e.g. pressure, temperature etc.). There may also be a `connection table', containing results for flows between blocks, and a `generation table', containing results (e.g. flow rates) at the generators in the model (e.g. wells). - -The formats of the listing files produced by TOUGH2, AUTOUGH2, TOUGH2\_MP and TOUGH+ are all slightly different, and also vary depending on the EOS used. However, PyTOUGH attempts to detect and read all of these formats. - -\section{What is Python?} -\index{Python} -\index{Python!3.x} - -Python is a general-purpose programming language. It is free and open-source, and runs on many different computer operating systems (Linux, Windows, Mac OS X and others). Python can be downloaded from the Python website (\url{http://www.python.org}), which also contains detailed reference material about the Python language. If you are using Linux you probably already have Python, as it is included in most Linux distributions. - -PyTOUGH should run on any version of Python 2.x newer than 2.4 (though version 2.6 or newer is recommended). PyTOUGH version 1.5 or later should also run on Python 3.x. - -If you are unfamiliar with Python (even if you have used another programming language before), it is highly recommended that you do one of the many Python tutorials available online, e.g. - -\begin{itemize} - \item \url{http://docs.python.org/tutorial/} - \item \url{http://wiki.python.org/moin/BeginnersGuide} -\end{itemize} - -\subsection{Python basics} - -\subsubsection{Objects} -\index{Python!objects} - -Python is what is known as an \textbf{object-oriented} language, which means that it is possible to create special customised data types, or `classes', to encapsulate all the properties and behaviour of the things (objects) we are dealing with in a program. This is a very useful way of simplifying complex programs. (In fact, in Python, everything is treated as an object, even simple things like integers and strings.) - -For example, in a TOUGH2 model grid we have collections of grid blocks, and we need to store the names of these blocks and their volumes and rock types. In a non-object-oriented language, these could be stored in three separate arrays: a string array for the names, a real (or `float') array for the volumes and another string array for the rock types. In an object-oriented language like Python, we can define a new data type (or `class') for blocks, which holds the name, volume and rock type of the block. If we declare an object called \texttt{blk} of this block class, we can access or edit its volume by referring to \texttt{blk.volume}. In this way, we can store our blocks in one single array of block objects. When we add or delete blocks from our grid, we can just add or delete block objects from the array, rather than having to keep track of three separate arrays. - -In general, an object not only has \textbf{properties} (like \texttt{blk.volume}) but also \textbf{methods}, which are functions the object can carry out. For example, if we wanted to rotate a MULgraph geometry file by $30\degree$, we could do this in PyTOUGH by declaring a MULgraph geometry file object called \texttt{geo}, and calling its \texttt{rotate} method: \texttt{geo.rotate(30)}. The methods of an object are accessed in the same way that its properties are accessed: by adding a dot (.) after the object's name and then adding the name of the property or method. Any arguments of the method (e.g. the angle in the \texttt{rotate} function above) are added in parentheses afterwards. - -\subsubsection{Lists, dictionaries, tuples and sets} -\index{Python!lists} -\index{Python!dictionaries} -\index{Python!tuples} -\index{Python!sets} - -Most programming languages have simple data types built in, e.g. float, double precision or integer numbers, strings, and arrays of these. Python has some other data types which are very useful and are used a lot. - -The first of these is the \textbf{list}. A list can contain any ordered collection of objects, of any type, or even of different types, and is delimited by square brackets. So for example we can declare a list \texttt{things = [1, 'two',3.0]} containing an integer, a string and a float. We can access the list's elements in much the same way as we access the elements of an array, for example \texttt{things[1]} would return the value \texttt{'two'} (note that in Python, as in most other languages besides Fortran, the indices of arrays and lists start at 0, not 1). Additional elements can be added to a list at any time, without having to re-declare the size of the list: for example, \texttt{things.append('IV')} would add an extra element to the end of the list, giving it the value \texttt{[1, 'two', 3.0, 'IV']}. It is also possible to remove elements from a list, e.g. \texttt{things.remove(3.0)}, which would give our list the value \texttt{[1, 'two', 'IV']}. - -Another useful Python data type is the \textbf{dictionary}. Dictionaries are mainly used to store collections of objects (again, of any type or of different types) that are referenced by name rather than by index (as in an array or list). A dictionary is delimited by curly brackets. So for example we can declare a dictionary \texttt{phone = \{'Eric':8155, 'Fred':2350, 'Wilma':4667\}} and then find Fred's phone number from \texttt{phone['Fred']}, which would return \texttt{2350}. For TOUGH2 models, blocks, generators, rock types and other objects are often referred to by name rather than index, so dictionaries are an appropriate way to store them. - -A third Python data type, similar to a list, is the \textbf{tuple}. A tuple is essentially a list that cannot be changed, and is often used just for grouping objects together. A tuple is delimited by parentheses. For example, \texttt{things = (1, 'two', 3.0)} declares a tuple with three elements. We can still refer to the elements of a tuple using e.g. \texttt{a[1]}, but we cannot assign new values to these elements or add or remove elements from the tuple once it has been declared. - -Python also has a \textbf{set} data type, which represents a mathematical set - an unordered collection of objects. One of the useful aspects of sets is that they cannot contain duplicate items. As a result, for example, duplicate items can be removed from a list \texttt{x} simply by converting it to a set, and then back to a list: \texttt{x = list(set(x))}. - -\subsection{How to run Python} -\index{Python!running} - -Python can be run either interactively or via scripts. - -\subsubsection{Running Python interactively} -\label{python_interactive} - -The simplest way to run Python interactively is just by typing \texttt{python} at the command line. (On Windows the directory that Python was installed into may have to be added to your \texttt{PATH} environment variable first.) The command line then becomes an interactive Python environment in which you can type Python commands at the Python command prompt \texttt{>>>}, e.g. in Windows: - -\begin{verbatim} -C:\>python -Python 2.6.4 (r264:75708, Oct 26 2009, 08:23:19) [MSC v.1500 32 bit (Intel)] -on win32 -Type "help", "copyright", "credits" or "license" for more information. ->>> things = [1, 'two', 3.0] ->>> print things[1] -two ->>> exit() - -C:\> -\end{verbatim} - -In the interactive Python environment, you can view help on the properties and methods of any Python object by typing \texttt{help(\emph{objectname})}, where \texttt{\emph{objectname}} is the name of an object that has been declared. This will list the properties and methods of the object and a description of each one. - -You can exit the interactive Python environment by typing \texttt{exit()} or \texttt{Ctrl-Z} on Windows, or \texttt{Ctrl-D} on Linux. - -\subsubsection{Python scripts} -\index{Python!scripts} - -The real power of Python, however, lies in using it to write \textbf{scripts} to automate repetitive or complex tasks. You can just type Python commands into a text file, save it with the file extension \texttt{.py}, and execute it by typing \texttt{python \emph{filename.py}}, where \texttt{\emph{filename.py}} is the name of the file. (Once again, on Windows the directory that Python was installed into may have to be added to your \texttt{PATH} environment variable first.) - -You can also debug a Python script using the `pdb' command-line debugger. Typing \texttt{python -m pdb \emph{filename.py}} will start debugging the script \emph{filename.py}. - -It is also possible to run a Python script from within the interactive Python environment. From the Python environment command line, typing \texttt{execfile(\emph{'filename.py'})} will execute the script \emph{filename.py}. - -\subsection{Python libraries} -\label{pylibraries} -\index{Python!libraries} - -Python comes with a large number of features already built in, but for specialised tasks, additional \textbf{libraries} of Python software can be imported into Python as you need them. PyTOUGH itself is a set of such libraries, and it in turn makes use of some other third-party Python libraries. - -\subsubsection{Numerical Python} -\index{Python!Numerical Python (numpy)} - -The most important of these is Numerical Python (`numpy'), which you will need to have installed on your computer before you can use PyTOUGH at all \footnote{PyTOUGH will run using Numeric, a now-obsolete predecessor of Numerical Python, though the PyTOUGH plotting functions will not work. In general it is recommended to use Numerical Python if possible.}. Numerical Python adds a special \texttt{numpy.array} class for fast multi-dimensional arrays, which PyTOUGH makes heavy use of, and a whole range of other features, e.g. linear algebra routines, Fourier transforms and statistics. - -\subsubsection{Other libraries} - -\index{Python!matplotlib} -\index{Python!scipy} -\index{Visualization Tool Kit (VTK)} - -Some parts of PyTOUGH use other Python libraries. You do not need these libraries unless you are using the parts of PyTOUGH that depend on them. - -\begin{itemize} -\item \textbf{Scientific Python} (\url{http://www.scipy.org/}), a library of advanced mathematical functions (e.g. interpolation, calculus, optimisation) -\item \textbf{matplotlib} (\url{http://matplotlib.sourceforge.net/}), a library of graphical plotting routines -\item \textbf{VTK}, a Python interface to the Visualization Tool Kit (\url{http://www.vtk.org/}), a library for 3D visualisation of data via VTK itself, or software such as ParaView, Mayavi etc. -\end{itemize} - -\subsection{Installing third-party Python libraries} - -\subsubsection{Linux} - -On Linux you can install third-party Python libraries via your package manager, e.g. on Debian or Debian-based distributions like Ubuntu: - -\texttt{apt-get install python-numpy python-scipy python-matplotlib python-vtk} - -\subsubsection{MS Windows} - -On MS Windows systems, the easiest way to install these libraries is by using the \textbf{pip} tool. This is a Python package management tool which allows the user to install Python packages and also manages dependencies (when one package depends on other packages). - -If you have Python version 2.7.9 or a more recent one then you should have pip already available on your system. If not, the best thing is probably to upgrade to a newer version of Python. If that is not possible for some reason, you can install pip separately by following the instructions at \url{https://pip.pypa.io/en/stable/installing/}. - -It is recommended to download libraries to use with PyTOUGH (as *.whl files) from Christoph Gohlke's Python package site: - -\url{http://www.lfd.uci.edu/\textasciitilde gohlke/pythonlibs/} - -Here you can find Windows Python packages for numpy, scipy, matplotlib and VTK (and lots of others as well). Be sure to choose the version of each package appropriate for your version of Python. For example, if you are using 64-bit Python 2.7, then choose *.whl files with names including ``cp27'', and ``amd64''. - -Once you have downloaded them, you can install them by opening a command prompt in the download directory and typing \texttt{pip install} followed by the exact name of the *.whl file. - -\subsubsection{Importing libraries} -\index{Python!libraries!importing} - -To use any Python library, you just need to \textbf{import} it first. For example, once you have installed Numerical Python, you can make it available (in the interactive Python environment or in a Python script) by typing the command \texttt{import numpy}, or alternatively \texttt{from numpy import *}. This imports all classes and commands from Numerical Python and makes them available for use. (You can also import only parts of a library rather than the whole thing, e.g. \texttt{from numpy import linalg} just imports the linear algebra routines from Numerical Python.) - -When you import a library, you can also change its name. For example, PyTOUGH imports Numerical Python using the command \texttt{import numpy as np}, which renames \texttt{numpy} as the abbreviated \texttt{np}. This means it can, for example, access the Numerical Python \texttt{numpy.array} data type as \texttt{np.array}. It also means you have access to Numerical Python as \texttt{np} in your own scripts and in the interactive Python environment, without having to import it yourself. - -\section{Installing and accessing PyTOUGH} -\label{installing} -\index{PyTOUGH!website} -\index{PyTOUGH!installing} - -The latest version of PyTOUGH can always be downloaded from the PyTOUGH website: - -\url{https://github.com/acroucher/PyTOUGH/} - -First, make sure you have Python, and the Numerical Python library (see section \ref{pylibraries}) installed. On Windows, you may have to add the directory where Python has installed (e.g. \texttt{C:\textbackslash Python27} or similar, depending on which version you have) to your PATH environment variable, before you can access Python from the command line. - -On the PyTOUGH website, click the `Download ZIP' button at the right of the page: - -\url{https://github.com/acroucher/PyTOUGH/archive/master.zip} - -to download PyTOUGH as a .zip file. Unzip this to any directory on your computer. This will create a directory containing a file called \texttt{setup.py}. - -To install PyTOUGH, you will need administrator (`root' on Linux) privileges on your computer. As administrator, open a command prompt, navigate to this new directory and type: - -\begin{verbatim} -python setup.py install -\end{verbatim} - -You should now be able to import the PyTOUGH libraries into the Python interactive environment or your Python scripts, from any directory on your computer. For example, you can import the MULgraph geometry library using \texttt{from mulgrids import *} (see chapter \ref{mulgrids}). - -\section{Testing PyTOUGH} -\label{unittests} -\index{PyTOUGH!testing} -\index{unit tests} - -PyTOUGH includes a suite of ``unit tests'' which can be used to verify that it is working correctly. These are located in the \texttt{tests/} directory, which includes a number of Python scripts for testing individual PyTOUGH modules. - -These unit test modules may be run individually, the same way as any other Python script would be run. If the tests in the script all pass, the last message printed out to the console will read \texttt{OK}. If not, details will be output regarding which tests did not pass. - -It is also possible to run the unit tests for all modules by running the following command in the \texttt{tests/} directory: - -\begin{verbatim} -python -m unittest discover -\end{verbatim} - -or with the \texttt{-v} (verbose) flag to output more detail on which tests are being run: - -\begin{verbatim} -python -m unittest discover -v -\end{verbatim} - -\section{Licensing} -\index{PyTOUGH!license} - -PyTOUGH is free software, distributed under the GNU Lesser General Public License (LGPL). For more information, see \url{http://www.gnu.org/licenses/}. diff --git a/doc/makefile b/doc/makefile deleted file mode 100644 index d37f38f0..00000000 --- a/doc/makefile +++ /dev/null @@ -1,11 +0,0 @@ -FILENAME=PyTOUGH-guide - -$(FILENAME).pdf: $(FILENAME).dvi - dvipdf $(FILENAME).dvi - -$(FILENAME).dvi: *.tex *.bib - latex $(FILENAME) - makeindex $(FILENAME) - bibtex $(FILENAME) - latex $(FILENAME) - latex $(FILENAME) \ No newline at end of file diff --git a/doc/mulgrids.tex b/doc/mulgrids.tex deleted file mode 100755 index 3dbb16dd..00000000 --- a/doc/mulgrids.tex +++ /dev/null @@ -1,2095 +0,0 @@ -\chapter{MULgraph geometry files} -\label{mulgrids} - -\section{Introduction} -The \texttt{mulgrids} library in PyTOUGH contains classes and routines for creating, editing and saving MULgraph geometry files. It can be imported using the command: - -\begin{lstlisting} - from mulgrids import * -\end{lstlisting} - -\section{\texttt{mulgrid} objects} -\index{MULgraph geometry!objects} -\index{MULgraph geometry!creating} -\index{PyTOUGH!classes!\texttt{mulgrid}} - -The \texttt{mulgrids} library defines a \texttt{mulgrid} class, used for representing MULgraph geometry files. - -\textbf{Example:} - -\begin{lstlisting} -geo = mulgrid() -\end{lstlisting} - -creates an empty \texttt{mulgrid} object called \texttt{geo}. - -\begin{lstlisting} -geo = mulgrid('geom.dat') -\end{lstlisting} - -creates a \texttt{mulgrid} object called \texttt{geo} and reads its contents from a file named \texttt{'geom.dat'}. - -Printing a \texttt{mulgrid} object (e.g. \texttt{print geo}) displays a summary of information about the grid: how many nodes, columns, layers, blocks and wells it contains, as well as its naming convention and atmosphere type. - -A specification of the MULgraph geometry file format can be found in Appendix \ref{geometry_file_format}. - -\subsection{Properties} -\index{MULgraph geometry!properties} - -The main properties of a \texttt{mulgrid} object are listed in Table \ref{tb:mulgrid_properties}. Some of these properties are `header' information, corresponding to the data at the start of a MULgraph geometry file (\texttt{type}, \texttt{convention}, \texttt{atmosphere\_type}, \texttt{atmosphere\_volume}, \texttt{atmosphere\_connection} and \texttt{unit\_type}). - -The most important properties of a \texttt{mulgrid} object are \texttt{node}, \texttt{column}, \texttt{connection}, \texttt{layer} and \texttt{well}, which are dictionaries of the grid nodes, columns, connections, layers and wells, accessed by name. For example, grid layer `AA' of a \texttt{mulgrid} object \texttt{geo} can be accessed by \texttt{geo.layer['AA']}. (The \texttt{nodelist}, \texttt{columnlist}, \texttt{connectionlist}, \texttt{layerlist} and \texttt{welllist} properties offer access to the nodes, columns, connections, layers and wells by index, which is sometimes useful e.g. for looping over all columns in the grid.) - -Connections are slightly different from nodes, columns etc. in that they are not named individually. However, they can be accessed by the names of the columns connected by the connection. For example, the connection between columns ` 10' and ` 11' in a \texttt{mulgrid} called \texttt{geo} is given by \texttt{geo.connection[' 10',' 11']}. - -The elements of these lists and dictionaries are of type \texttt{node}, \texttt{column}, \texttt{connection}, \texttt{layer} and \texttt{well} respectively. These are additional object classes to represent nodes, columns, connections, layers and wells, defined in the \texttt{mulgrids} library (see section \ref{other_mulgrid_objects}). - -\subsubsection{Grid diagnostics} -\index{MULgraph geometry!diagnostics} - -A \texttt{mulgrid} object has some properties (and methods) for evaluating its integrity. The property \texttt{column\_angle\_ratio} returns an \texttt{np.array} of the `angle ratio' for each column (the ratio of largest to smallest interior angles - see section \ref{columnobjects}), a measure of skewness. The \texttt{column\_side\_ratio} returns an \texttt{np.array} of the `side ratio' for each column (the ratio of largest to smallest side length), a measure of elongation. These array properties can be plotted using the \texttt{layer\_plot} method (see section \ref{mulgridmethods}) for a graphical overview of grid quality. - -There is also a \texttt{connection\_angle\_cosine} property, which returns an \texttt{np.array} of the angle cosine for each connection (the cosine of the angle between a line joining the nodes in the connection and a line joining the centres of the blocks in the connection). In general it is desirable for these lines to be as close to perpendicular as possible, making the cosines close to zero. - -The \texttt{bad\_columns}, \texttt{bad\_layers}, \texttt{missing\_connections}, \texttt{extra\_connections} and \texttt{orphans} properties return actual problems with the grid which should be fixed. A summary of all these problems is given by the \texttt{check} method (see section \ref{mulgridmethods}). - -Blocks at the ground surface that have very small vertical thickness can sometimes cause problems. The \texttt{min\_surface\_block\_thickness} property gives a tuple containing the minimum surface block thickness and the name of the column in which it occurs. Thin surface blocks of this type can be eliminated using the \texttt{snap\_columns\_to\_layers()} method. - -\subsubsection{Functions for reading data from file} -\label{mulgridreadfunctions} -\index{MULgraph geometry!file format} -\index{MULgraph geometry!reading} - -A \texttt{mulgrid} object has a \texttt{read\_function} property which controls how data are read from file. This property is a dictionary with six keys: `d', `f', `e', `g', `s' and `x', denoting respectively integer, float, exponential, general, string and blank. Each item in the dictionary is a function which converts a string from the file on disk into the appropriate value. For example, \texttt{read\_function['f']} converts a string to a floating point value. By default, the built-in Python \texttt{float} function is used for this (although it is modified slightly so that it returns \texttt{None} if the input string is blank). There is a dictionary of default reading functions included in PyTOUGH, called \texttt{default\_read\_function}. - -However, the user can specify other functions if needed. In particular, files produced from Fortran programs sometimes have formatting that is not readable by the default functions, if some more exotic Fortran formatting options have been used. For example, a `d' can also be used to represent an exponent (like `e'), or spaces can be included within a number, or the exponent identifier (e.g. `e') can be omitted. PyTOUGH includes a second set of reading functions, called \texttt{fortran\_read\_function}, for handling Fortran formatting. These are slightly slower than the default reading functions. - -The reading functions for a \texttt{mulgrid} object can be specified when the object is being created, e.g.: - -\begin{lstlisting} -geo = mulgrid('geom.dat', read_function = fortran_read_function) -\end{lstlisting} - -\subsubsection{Block ordering schemes} -\label{sec:mulgrid:blockordering} -\index{MULgraph geometry!block ordering} - -By default, the blocks in a TOUGH2 grid created from a \texttt{mulgrid} geometry are ordered -by layer, from the atmosphere down to the bottom of the model, with the blocks within -each layer ordered by column (following the ordering of the \texttt{columnlist} property, which -is the same as the column order specified in the geometry file). - -It is also possible to sort the blocks according to their geometrical type (8-node hexahedrons -and 6-node wedges, corresponding to 4-node or 3-node columns respectively). This is useful for -exporting the model to Waiwera, which uses the PETSc DMPlex mesh representation, which sorts -cells by cell type in this way. - -This can be done by setting the \texttt{block\_order} property of the geometry. This can be set -when the \texttt{mulgrid} object is created or read from file, as an optional parameter, e.g.: - -\begin{lstlisting} -geo = mulgrid('geom.dat', block_order = 'dmplex') -\end{lstlisting} - -It can also be specified after creation. The \texttt{block\_order} property is a string which can -take the value \textbf{`layer\_column'} for layer/column block ordering, or \textbf{`dmplex'} -if the blocks are to be sorted by geometrical type. It can also take the value \texttt{None} -which gives the default layer/column ordering. - -\begin{lstlisting} -geo.block_order = 'layer_column' -\end{lstlisting} - -The block ordering scheme can be stored in the MULgraph geometry file, via an integer flag in -the header (see Appendix \ref{geometry_file_format}). This flag is an extension to the original -MULgraph geometry file format. If a \texttt{mulgrid} object is created by reading a file in -which this flag is not present, its \texttt{block\_order} property will be \texttt{None}, in -which case the default layer/column ordering will be used. When a geometry file is read in, -and a block ordering is specified via the \texttt{block\_order} parameter, this will override -any block ordering stored in the file. - -\subsubsection{Tilted geometries} -\index{MULgraph geometry!tilting} - -Non-horizontal (i.e. tilted) geometries can be constructed by setting the \texttt{mulgrid} properties \texttt{gdcx} and \texttt{gdcy} non-zero. These properties represent the cosines of the angles the x- and y-axes make with the gravity vector. By default they are both zero, giving a horizontal grid. A geometry with \texttt{gdcx} = 1 can be used to construct a 2-D vertical slice grid with a non-layered structure. When a \texttt{t2grid} object is created from a tilted geometry, e.g. using the \texttt{t2grid} \hyperref[sec:t2grid:fromgeo]{fromgeo()} method, only the gravity cosines of the connections are affected (the \texttt{dircos} property of each connection). - -\subsubsection{Rotating permeability directions} -\index{MULgraph geometry!permeability directions} - -It is possible to rotate the permeability principal directions of a \texttt{mulgrid} object with respect to the coordinate axes- for example, to align permeabilities with a dominant fault direction- by specifying the \texttt{permeability\_angle} property. When a \texttt{t2grid} object is created, e.g. using the \texttt{t2grid} \hyperref[sec:t2grid:fromgeo]{fromgeo()} method, this can change the \texttt{direction} property of each connection. - -\index{MULgraph geometry!properties} -\begin{center} - \begin{longtable}{|l|l|p{75mm}|} - \hline - \textbf{Property} & \textbf{Type} & \textbf{Description}\\ - \hline - \texttt{area} & float & total horizontal area covered by the grid \\ - \texttt{atmosphere\_connection} & float & connection distance to atmosphere blocks\\ - \texttt{atmosphere\_type} & integer & type of atmosphere\\ - \texttt{atmosphere\_volume} & float & volume of atmosphere blocks\\ - \texttt{bad\_columns} & set & columns that do not contain their own centres\\ - \texttt{bad\_layers} & set & layers that do not contain their own centres\\ - \texttt{block\_connection\_name\_index} & dictionary & indices of block connections (by name)\\ - \texttt{block\_connection\_name\_list} & list & names of block connections (by index)\\ - \texttt{block\_name\_index} & dictionary & indices of blocks (by name)\\ - \texttt{block\_name\_list} & list & names of blocks (by index)\\ - \texttt{block\_order} & string & block ordering scheme\\ - \texttt{boundary\_columns} & set & set of columns on the outer boundary of the grid \\ - \texttt{boundary\_nodes} & list & ordered list of nodes on the outer boundary of the grid \\ - \texttt{boundary\_polygon} & list & list of points representing grid boundary (extra colinear points removed) \\ - \texttt{bounds} & list & [bottom left, top right] horizontal bounds of grid\\ - \texttt{centre} & \texttt{np.array} & position of horizontal centre of the grid \\ - \texttt{columnlist} & list & columns (by index, e.g. \texttt{columnlist[23]})\\ - \texttt{column\_angle\_ratio} & \texttt{np.array} & angle ratio for each column\\ - \texttt{column\_side\_ratio} & \texttt{np.array} & side ratio for each column\\ - \texttt{column} & dictionary & columns (by name, e.g. \texttt{column['AA']})\\ - \texttt{connectionlist} & list & connections between columns (by index)\\ - \texttt{connection\_angle\_cosine} & \texttt{np.array} & angle cosines for all connections\\ - \texttt{convention} & integer & naming convention for columns and layers\\ - \texttt{default\_surface} & Boolean & \texttt{True} if all columns have default surface elevation\\ - \texttt{extra\_connections} & set & connections defined between columns that are not against each other\\ - \texttt{filename} & string & file name on disk\\ - \texttt{gdcx}, \texttt{gdcy} & float & cosines of angles x- and y-axes make with gravity vector\\ - \texttt{node\_kdtree} & \texttt{cKDTree} & tree structure for fast searching for nodes \\ - \texttt{layerlist} & list & layers (by index)\\ - \texttt{layer} & dictionary & layers (by name)\\ - \texttt{min\_surface\_block\_thickness} & (float, string) & thickness of thinnest surface block (and associated column name)\\ - \texttt{missing\_connections} & set & missing connections between columns\\ - \texttt{nodelist} & list & nodes (by index)\\ - \texttt{node} & dictionary & nodes (by name)\\ - \texttt{num\_atmosphere\_blocks} & integer & number of atmosphere blocks\\ - \texttt{num\_blocks} & integer & total number of blocks in the grid\\ - \texttt{num\_block\_connections} & integer & total number of block connections in the grid\\ - \texttt{num\_columns} & integer & number of columns\\ - \texttt{num\_connections} & integer & number of connections between columns\\ - \texttt{num\_layers} & integer & number of layers\\ - \texttt{num\_nodes} & integer & number of nodes\\ - \texttt{num\_underground\_blocks} & integer & number of non-atmosphere blocks\\ - \texttt{num\_wells} & integer & number of wells\\ - \texttt{orphans} & set & orphaned nodes (nodes not belonging to any column)\\ - \texttt{permeability\_angle} & float & rotation angle (degrees anticlockwise) of first horizontal permeability direction \\ - \texttt{read\_function} & dictionary & dictionary of functions used to read data from file\\ - \texttt{type} & string & type of geometry (currently only `GENER' supported)\\ - \texttt{unit\_type} & string & distance unit (blank for metres, `FEET' for ft)\\ - \texttt{welllist} & list & wells (by index)\\ - \texttt{well} & dictionary & wells (by name)\\ - \hline - \caption{Properties of a \texttt{mulgrid} object} - \label{tb:mulgrid_properties} - \end{longtable} -\end{center} - -\subsection{Methods} -\label{mulgridmethods} - -The main methods of a \texttt{mulgrid} object are listed in Table \ref{tb:mulgrid_methods}. Details of these methods are given below. - -\index{MULgraph geometry!methods} -\begin{center} -\begin{longtable}{|l|l|p{70mm}|} - \hline - \textbf{Method} & \textbf{Type} & \textbf{Description}\\ - \hline - \hyperref[sec:mulgrid:add_column]{\texttt{add\_column}} & -- & adds a column to the grid\\ - \hyperref[sec:mulgrid:add_connection]{\texttt{add\_connection}} & -- & adds a connection to the grid\\ - \hyperref[sec:mulgrid:add_layer]{\texttt{add\_layer}} & -- & adds a layer to the grid\\ - \hyperref[sec:mulgrid:add_node]{\texttt{add\_node}} & -- & adds a node to the grid\\ - \hyperref[sec:mulgrid:add_well]{\texttt{add\_well}} & -- & adds a well to the grid\\ - \hyperref[sec:mulgrid:block_centre]{\texttt{block\_centre}} & \texttt{np.array} & block centre\\ - \hyperref[sec:mulgrid:block_contains_point]{\texttt{block\_contains\_point}} & Boolean & whether a block contains a 3D point\\ - \hyperref[sec:mulgrid:block_mapping]{\texttt{block\_mapping}} & dictionary & mapping from the blocks of another \texttt{mulgrid} object\\ - \hyperref[sec:mulgrid:block_name]{\texttt{block\_name}} & string & name of block at given layer and column\\ - \hyperref[sec:mulgrid:block_name_containing_point]{\texttt{block\_name\_containing\_point}} & string & name of block containing specified point\\ - \hyperref[sec:mulgrid:block_surface]{\texttt{block\_surface}} & float & block top elevation\\ - \hyperref[sec:mulgrid:block_volume]{\texttt{block\_volume}} & float & block volume\\ - \hyperref[sec:mulgrid:check]{\texttt{check}} & Boolean & checks grid for errors (and optionally fixes them)\\ - \hyperref[sec:mulgrid:column_boundary_nodes]{\texttt{column\_boundary\_nodes}} & list & nodes around the outer boundary of a group of columns\\ - \hyperref[sec:mulgrid:column_bounds]{\texttt{column\_bounds}} & list & bounding rectangle around a list of columns\\ - \hyperref[sec:mulgrid:column_containing_point]{\texttt{column\_containing\_point}} & column & column containing specified horizontal point\\ - \hyperref[sec:mulgrid:column_mapping]{\texttt{column\_mapping}} & dictionary & mapping from the columns of another \texttt{mulgrid} object\\ - \hyperref[sec:mulgrid:column_name]{\texttt{column\_name}} & string & column name of a block name\\ - \hyperref[sec:mulgrid:column_neighbour_groups]{\texttt{column\_neighbour\_groups}} & list & groups connected columns\\ - \hyperref[sec:mulgrid:column_quadtree]{\texttt{column\_quadtree}} & quadtree & quadtree structure for searching columns\\ - \hyperref[sec:mulgrid:column_surface_layer]{\texttt{column\_surface\_layer}} & \hyperref[layerobjects]{\texttt{layer}} & surface layer for a specified column\\ - \hyperref[sec:mulgrid:column_values]{\texttt{column\_values}} & tuple & values of a variable down a column\\ - \hyperref[sec:mulgrid:columns_in_polygon]{\texttt{columns\_in\_polygon}} & list & columns inside a specified polygon (or rectangle)\\ - \hyperref[sec:mulgrid:connects]{\texttt{connects}} & Boolean & whether the grid has a connection between two specified columns\\ - \hyperref[sec:mulgrid:copy_layers_from]{\texttt{copy\_layers\_from}} & -- & copies layer structure from another geometry\\ - \hyperref[sec:mulgrid:copy_wells_from]{\texttt{copy\_wells\_from}} & -- & copies wells from another geometry\\ - \hyperref[sec:mulgrid:decompose_columns]{\texttt{decompose\_columns}} & -- & decomposes columns into triangles and quadrilaterals\\ - \hyperref[sec:mulgrid:delete_column]{\texttt{delete\_column}} & -- & deletes a column from the grid\\ - \hyperref[sec:mulgrid:delete_connection]{\texttt{delete\_connection}} & -- & deletes a connection from the grid\\ - \hyperref[sec:mulgrid:delete_layer]{\texttt{delete\_layer}} & -- & deletes a layer from the grid\\ - \hyperref[sec:mulgrid:delete_node]{\texttt{delete\_node}} & -- & deletes a node from the grid\\ - \hyperref[sec:mulgrid:delete_orphans]{\texttt{delete\_orphans}} & -- & deletes any orphaned nodes from the grid\\ - \hyperref[sec:mulgrid:delete_orphan_wells]{\texttt{delete\_orphan\_wells}} & -- & deletes any orphaned wells from the grid\\ - \hyperref[sec:mulgrid:delete_well]{\texttt{delete\_well}} & -- & deletes a well from the grid\\ - \hyperref[sec:mulgrid:empty]{\texttt{empty}} & -- & empties contents of grid\\ - \hyperref[sec:mulgrid:export_surfer]{\texttt{export\_surfer}} & -- & exports to various files on disk for visualization in Surfer\\ - \hyperref[sec:mulgrid:fit_columns]{\texttt{fit\_columns}} & \texttt{np.array} or dictionary & fits scattered data to column centres\\ - \hyperref[sec:mulgrid:fit_surface]{\texttt{fit\_surface}} & -- & fits column surface elevations from data\\ - \hyperref[sec:mulgrid:from_amesh]{\texttt{from\_amesh}} & (\hyperref[mulgrids]{\texttt{mulgrid}}, dict) & creates Voronoi geometry from AMESH grid\\ - \hyperref[sec:mulgrid:from_gmsh]{\texttt{from\_gmsh}} & \hyperref[mulgrids]{\texttt{mulgrid}} & creates geometry from a \texttt{gmsh} grid\\ - \hyperref[sec:mulgrid:layer_containing_elevation]{\texttt{layer\_containing\_elevation}} & layer & layer containing specified vertical elevation\\ - \hyperref[sec:mulgrid:layer_mapping]{\texttt{layer\_mapping}} & dictionary & mapping from the layers of another \texttt{mulgrid} object\\ - \hyperref[sec:mulgrid:layer_name]{\texttt{layer\_name}} & string & layer name of a block name\\ - \hyperref[sec:mulgrid:layer_plot]{\texttt{layer\_plot}} & -- & plots a variable over a layer of the grid\\ - \hyperref[sec:mulgrid:line_plot]{\texttt{line\_plot}} & -- & plots a variable along an arbitrary line through the grid\\ - \hyperref[sec:mulgrid:line_values]{\texttt{line\_values}} & tuple & values of a variable along an arbitrary line through the grid\\ - \hyperref[sec:mulgrid:meshio_grid]{\texttt{meshio\_grid}} & tuple & mesh in \texttt{meshio} format\\ - \hyperref[sec:mulgrid:minc_array]{\texttt{minc\_array}} & array & values for a particular level in a MINC grid\\ - \hyperref[sec:mulgrid:nodes_in_columns]{\texttt{nodes\_in\_columns}} & list & nodes in a specified list of columns\\ - \hyperref[sec:mulgrid:nodes_in_polygon]{\texttt{nodes\_in\_polygon}} & list & nodes inside a specified polygon (or rectangle)\\ - \hyperref[sec:mulgrid:node_nearest_to]{\texttt{node\_nearest\_to}} & \hyperref[nodeobjects]{\texttt{node}} & node nearest to a specified point\\ - \hyperref[sec:mulgrid:optimize]{\texttt{optimize}} & -- & adjusts node positions to optimize grid quality\\ - \hyperref[sec:mulgrid:polyline_values]{\texttt{polyline\_values}} & tuple & values of a variable along an arbitrary polyline through the grid\\ - \hyperref[sec:mulgrid:read]{\texttt{read}} & \hyperref[mulgrids]{\texttt{mulgrid}} & reads geometry file from disk\\ - \hyperref[sec:mulgrid:rectangular]{\texttt{rectangular}} & \hyperref[mulgrids]{\texttt{mulgrid}} & creates rectangular grid\\ - \hyperref[sec:mulgrid:reduce]{\texttt{reduce}} & -- & reduces a grid to contain only specified columns\\ - \hyperref[sec:mulgrid:refine]{\texttt{refine}} & -- & refines specified columns in the grid\\ - \hyperref[sec:mulgrid:refine_layers]{\texttt{refine\_layers}} & -- & refines specified layers in the grid\\ - \hyperref[sec:mulgrid:rename_column]{\texttt{rename\_column}} & Boolean & renames a column\\ - \hyperref[sec:mulgrid:rename_layer]{\texttt{rename\_layer}} & Boolean & renames a layer\\ - \hyperref[sec:mulgrid:rotate]{\texttt{rotate}} & -- & rotates a grid in the horizontal plane\\ - \hyperref[sec:mulgrid:slice_plot]{\texttt{slice\_plot}} & -- & plots a variable over a vertical slice through the grid\\ - \hyperref[sec:mulgrid:snap_columns_to_layers]{\texttt{snap\_columns\_to\_layers}} & -- & snaps column surfaces to layer bottoms\\ - \hyperref[sec:mulgrid:snap_columns_to_nearest_layers]{\texttt{snap\_columns\_to\_nearest\_layers}} & -- & snaps column surfaces to nearest layer elevations \\ - \hyperref[sec:mulgrid:split_column]{\texttt{split\_column}} & Boolean & splits a quadrilateral column into two triangles\\ - \hyperref[sec:mulgrid:translate]{\texttt{translate}} & -- & moves a grid by simple translation in 3D\\ - \hyperref[sec:mulgrid:well_values]{\texttt{well\_values}} & tuple & values of a variable down a well\\ - \hyperref[sec:mulgrid:write]{\texttt{write}} & -- & writes to geometry file on disk\\ - \hyperref[sec:mulgrid:write_bna]{\texttt{write\_bna}} & -- & writes to Atlas BNA file on disk\\ - \hyperref[sec:mulgrid:write_exodusii]{\texttt{write\_exodusii}} & -- & writes to ExodusII file on disk\\ - \hyperref[sec:mulgrid:write_mesh]{\texttt{write\_mesh}} & -- & writes to mesh file (various formats) on disk\\ - \hyperref[sec:mulgrid:write_vtk]{\texttt{write\_vtk}} & -- & writes to VTK file on disk\\ - \hline - \caption{Methods of a \texttt{mulgrid} object} - \label{tb:mulgrid_methods} -\end{longtable} -\end{center} - -\begin{snugshade}\subsubsection{\texttt{add\_column(\emph{col})}}\end{snugshade} -\label{sec:mulgrid:add_column} -\index{MULgraph geometry!adding!columns} -\index{MULgraph geometry!columns!adding} - -Adds a \hyperref[columnobjects]{\texttt{column}} object \texttt{col} -to the grid. If a column with the same name already exists, no new -column is added. - -\begin{snugshade}\subsubsection{\texttt{add\_connection(\emph{con})}}\end{snugshade} -\label{sec:mulgrid:add_connection} -\index{MULgraph geometry!adding!connections} -\index{MULgraph geometry!connections!adding} - -Adds a \hyperref[connectionobjects]{\texttt{connection}} object -\texttt{con} to the grid. If a connection with the same name already -exists, no new connection is added. - -\begin{snugshade}\subsubsection{\texttt{add\_layer(\emph{lay})}}\end{snugshade} -\label{sec:mulgrid:add_layer} -\index{MULgraph geometry!adding!layers} -\index{MULgraph geometry!layers!adding} - -Adds a \hyperref[layerobjects]{\texttt{layer}} object \texttt{lay} to -the grid. If a layer with the same name already exists, no new layer -is added. - -\begin{snugshade}\subsubsection{\texttt{add\_node(\emph{n})}}\end{snugshade} -\label{sec:mulgrid:add_node} -\index{MULgraph geometry!adding!nodes} -\index{MULgraph geometry!nodes!adding} - -Adds a \hyperref[nodeobjects]{\texttt{node}} object \texttt{n} to the -grid. If a node with the same name already exists, no new node is -added. - -\begin{snugshade}\subsubsection{\texttt{add\_well(\emph{w})}}\end{snugshade} -\label{sec:mulgrid:add_well} -\index{MULgraph geometry!adding!wells} -\index{MULgraph geometry!wells!adding} - -Adds a \hyperref[wellobjects]{\texttt{well}} object \texttt{w} to the -grid. If a well with the same name already exists, no new well is -added. - -\begin{snugshade}\subsubsection{\texttt{block\_contains\_point(\emph{blockname}, \emph{pos})}}\end{snugshade} -\label{sec:mulgrid:block_contains_point} -\index{MULgraph geometry!finding!blocks} -\index{MULgraph geometry!blocks!finding} - -Returns \texttt{True} if the grid block with the given name contains the 3D point \texttt{pos}. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{blockname}: string\\ - The name of the block. -\item \textbf{pos}: \texttt{np.array}\\ - 3-element array representing the 3D point. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{block\_centre(\emph{lay}, \emph{col})}}\end{snugshade} -\label{sec:mulgrid:block_centre} -\index{MULgraph geometry!blocks!centres} - -Returns the centre of the block corresponding to the given layer and column. - -The horizontal centre is given by the column centre. The vertical centre is given by the layer centre, except for surface blocks with column surface lower than the layer top, in which case it is the midpoint between the column surface and the layer bottom. (For surface blocks with column surface higher than the layer top, the vertical centre is still the layer centre, to give a uniform pressure reference.) - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{lay}: \hyperref[layerobjects]{\texttt{layer}} or string\\ - The specified layer or layer name. -\item \textbf{col}: \hyperref[columnobjects]{\texttt{column}} or string\\ - The specified column or column name. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{block\_mapping(\emph{geo}, \emph{column\_mapping}=\texttt{False})}}\end{snugshade} -\label{sec:mulgrid:block_mapping} - -Returns a dictionary mapping each block name in the \texttt{mulgrid} object \texttt{geo} to the name of the nearest block in the object's own geometry. Can optionally also return the associated column mapping. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - The \texttt{mulgrid} object to create a block mapping from. -\item \textbf{column\_mapping}: Boolean\\ - If \texttt{True}, the column mapping will also be returned (i.e. the function will return a tuple containing the block mapping and the column mapping). Default value is \texttt{False}. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{block\_name(\emph{layer\_name}, \emph{column\_name}, \emph{blockmap} = \{\})}}\end{snugshade} -\label{sec:mulgrid:block_name} -\index{MULgraph geometry!blocks!names} -\index{MULgraph geometry!names!of blocks} - -Gives the name of the block corresponding to the specified layer and column names, according to the naming convention of the grid. - -An optional block name mapping can be applied. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{layer\_name}, \textbf{column\_name}: string\\ - Name of layer and column (the widths of these strings are determined by the grid's naming convention). -\item \textbf{blockmap}: dictionary\\ - Dictionary mapping the block names in the geometry to another block naming system. This dictionary need not contain entries for all blocks in the geometry- those not included in the mapping will not be altered. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{block\_name\_containing\_point(\emph{pos}, \emph{qtree}=None, \emph{blockmap}=\{\})}}\end{snugshade} -\label{sec:mulgrid:block_name_containing_point} -\index{MULgraph geometry!finding!blocks} -\index{MULgraph geometry!blocks!finding} -\index{MULgraph geometry!columns!quadtrees} -\index{quadtrees} - -Gives the name of the block containing a specified 3-D position in the grid (returns \texttt{None} if the point lies outside the grid). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{pos}: \texttt{np.array}\\ - Position of point in 3-D -\item \textbf{qtree}: \texttt{quadtree}\\ - Quadtree object for fast searching of grid columns (can be constructed using the \hyperref[sec:mulgrid:column_quadtree]{\texttt{column\_quadtree()}} method). -\item \textbf{blockmap}: dictionary\\ - Dictionary mapping the block names in the geometry to another block naming system. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{block\_surface(\emph{lay}, \emph{col})}}\end{snugshade} -\label{sec:mulgrid:block_surface} -\index{MULgraph geometry!blocks!surfaces} - -Returns the elevation of the top surface of the block corresponding to the given layer and column. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{lay}: \hyperref[layerobjects]{\texttt{layer}}\\ - The specified layer. -\item \textbf{col}: \hyperref[columnobjects]{\texttt{column}}\\ - The specified column. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{block\_volume(\emph{lay}, \emph{col})}}\end{snugshade} -\label{sec:mulgrid:block_volume} -\index{MULgraph geometry!blocks!volumes} - -Returns the volume of the block corresponding to the given layer and column. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{lay}: \hyperref[layerobjects]{\texttt{layer}}\\ - The specified layer. -\item \textbf{col}: \hyperref[columnobjects]{\texttt{column}}\\ - The specified column. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{check(\emph{fix}=False,\emph{silent}=False)}}\end{snugshade} -\label{sec:mulgrid:check} -\index{MULgraph geometry!checking} -\index{checking!MULgraph geometry} - -Checks a grid for errors and optionally fixes them. Errors checked for are: missing connections, extra connections, orphaned nodes, and columns and layers that do not contain their own centres. Returns \texttt{True} if no errors were found, and \texttt{False} otherwise. If \texttt{fix} is \texttt{True}, any identified problems will be fixed. If \texttt{silent} is \texttt{True}, there is no printout (only really useful if \texttt{fix} is \texttt{True}). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{fix}: Boolean\\ - Whether to fix any problems identified. -\item \textbf{silent}: Boolean\\ - Whether to print out feedback or not. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{column\_boundary\_nodes(\emph{columns})}}\end{snugshade} -\label{sec:mulgrid:column_boundary_nodes} -\index{MULgraph geometry!nodes!finding} -\index{MULgraph geometry!finding!nodes} - -Returns the nodes around the outer boundary of a list of columns. The list is ordered, in a counter-clockwise direction. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{columns}: list\\ - The list of columns for which the boundary is required. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{column\_bounds(\emph{columns})}}\end{snugshade} -\label{sec:mulgrid:column_bounds} - -Returns a bounding rectangle around a list of columns. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{columns}: list\\ - The list of columns for which the bounds are required. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{column\_containing\_point(\emph{pos}, \emph{columns}=None, \emph{guess}=None, \emph{bounds}=None,\\ -\emph{qtree}=None)}}\end{snugshade} -\label{sec:mulgrid:column_containing_point} -\index{MULgraph geometry!finding!columns} -\index{MULgraph geometry!columns!finding} -\index{MULgraph geometry!columns!quadtrees} -\index{quadtrees} - -Returns the grid column containing the specified horizontal point. If \texttt{columns} is specified, only columns in the given list will be searched. An initial \texttt{guess} column can optionally be specified. If \texttt{bounds} is specified, points outside the given polygon will always return \texttt{None}. A quadtree structure can also be specified to speed up searching. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{pos}: \texttt{np.array}\\ - Horizontal position (\emph{x}, \emph{y}) -\item \textbf{columns}: list of \hyperref[columnobjects]{\texttt{column}} (or \texttt{None})\\ - List of columns to search. If \texttt{None}, the entire grid will be searched. -\item \textbf{guess}: \hyperref[columnobjects]{\texttt{column}} (or \texttt{None})\\ - Guess of required column. If specified, this column will be tested first, followed (if necessary) by its neighbours; only if none of these contain the point will the remaining columns be searched. This can speed up the process if data follow a sequential pattern in space, e.g. a grid or lines. - \item \textbf{bounds}: list of \texttt{np.array} (or \texttt{None})\\ - Polygon or rectangle representing e.g. the boundary of the grid: points outside this polygon will always return \texttt{None}. If the polygon has only two points, it will be interpreted as a rectangle [bottom left, top right]. - \item \textbf{qtree}: \texttt{quadtree} \\ - A quadtree object for searching the columns of the grid. If many points are to be located, this option can speed up the search. The quadtree can be constructed before searching using the \hyperref[sec:mulgrid:column_quadtree]{\texttt{column\_quadtree()}} method. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{column\_mapping(\emph{geo})}}\end{snugshade} -\label{sec:mulgrid:column_mapping} - -Returns a dictionary mapping each column name in the \texttt{mulgrid} object \texttt{geo} to the name of the nearest column in the object's own geometry. If the SciPy library is available, a KDTree structure is used to speed searching. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - The \texttt{mulgrid} object to create a column mapping from. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{column\_name(\emph{block\_name})}}\end{snugshade} -\label{sec:mulgrid:column_name} -\index{MULgraph geometry!columns!names} -\index{MULgraph geometry!names!of columns} - -Gives the name of the column corresponding to the specified block name, according to the naming convention of the grid. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{block\_name}: string\\ - Block name. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{column\_neighbour\_groups(\emph{columns})}}\end{snugshade} -\label{sec:mulgrid:column_neighbour_groups} - -From the given list or set of columns, finds sets of columns that are connected together, and returns a list of them. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{columns}: list or set\\ - List or set of columns to group. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{column\_quadtree(\emph{columns}=None)}}\end{snugshade} -\label{sec:mulgrid:column_quadtree} -\index{MULgraph geometry!columns!quadtrees} -\index{PyTOUGH!classes!\texttt{quadtree}} -\index{quadtrees} - -Returns a quadtree structure for fast searching of grid columns, to find which column a given point lies in. This can then be passed into various other \texttt{mulgrid} methods that do such searching, e.g. \hyperref[sec:mulgrid:block_name_containing_point]{\texttt{block\_name\_containing\_point()}} or \hyperref[sec:mulgrid:well_values]{\texttt{well\_values()}}, to speed them up (useful for large grids). - -The quadtree is an instance of a \texttt{quadtree} class, defined in the \texttt{mulgrids} module. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{columns}: list (or \texttt{None})\\ - A list of columns in the grid, specifying the search area. This parameter can be used to further speed searching if it is only necessary to search columns in a defined area. If \texttt{None}, the search area is the whole grid (all columns). -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{column\_surface\_layer(\emph{col})}}\end{snugshade} -\label{sec:mulgrid:column_surface_layer} -\index{MULgraph geometry!columns!surface elevation} - -Returns the layer containing the surface elevation of a specified column. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{col}: \hyperref[columnobjects]{\texttt{column}}\\ - The column for which the surface layer is to be found. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{column\_values(\emph{col}, \emph{variable}, \emph{depth} = False)}}\end{snugshade} -\label{sec:mulgrid:column_values} -\index{MULgraph geometry!values!down a column} - -Returns values of a specified variable down a specified column. The variable can be a list or \texttt{np.array} containing a value for every block in the grid. - -The routine returns a tuple of two arrays (\texttt{d},\texttt{v}), the first (\texttt{d}) containing the elevation (or depth from surface if the \texttt{depth} parameter is set to \texttt{True}), and the second (\texttt{v}) containing the value of the variable at each block in the column. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{col}: \hyperref[columnobjects]{\texttt{column}} or string\\ - The column for which values are to be found. -\item \textbf{variable}: list (or \texttt{np.array})\\ - Values of variable, of length equal to the number of blocks in the grid. -\item \textbf{depth}: Boolean\\ - Set to \texttt{True} to give depths from surface, instead of elevations, as the first returned array. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{columns\_in\_polygon(\emph{polygon})}}\end{snugshade} -\label{sec:mulgrid:columns_in_polygon} -\index{MULgraph geometry!finding!columns} -\index{MULgraph geometry!columns!finding} - -Returns a list of all columns with centres inside the specified polygon or rectangle. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{polygon}: list (of \texttt{np.array})\\ - List of points defining the polygon (each point is a two-element \texttt{np.array}). If the list has only two points, it will be interpreted as a rectangle [bottom left, top right]. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{connects(\emph{column1, column2})}}\end{snugshade} -\label{sec:mulgrid:connects} - -Returns \texttt{True} if the geometry contains a connection connecting the two specified columns. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{column1, column2}: \hyperref[columnobjects]{\texttt{column}}\\ - Two columns in the geometry. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{copy\_layers\_from(\emph{geo})}}\end{snugshade} -\label{sec:mulgrid:copy_layers_from} - -Copies the layer structure from the geometry \texttt{geo} (deleting any existing layers first). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - The geometry to copy layers from. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{copy\_wells\_from(\emph{geo})}}\end{snugshade} -\label{sec:mulgrid:copy_wells_from} - -Copies the wells from the geometry \texttt{geo} (deleting any existing wells first). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - The geometry to copy wells from. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{decompose\_columns(\emph{columns} = [], \emph{mapping} = False, \emph{chars} = ascii\_lowercase)}}\end{snugshade} -\label{sec:mulgrid:decompose_columns} -\index{MULgraph geometry!columns!decomposing} - -Decomposes columns with more than four sides into triangular and quadrilateral columns. This can be useful when carrying out calculations on the geometry that rely on finite element methods (e.g. the \texttt{fit\_columns()} method uses it). - -In general, columns are decomposed by adding a node at the column centroid and forming triangles around it. However, there are special cases for columns with lower numbers of sides (less than 9) and `straight' nodes, i.e. nodes on a straight line between their neighbouring nodes in the column). These make use of simpler decompositions. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{columns}: list\\ - List of columns to be decomposed. If the list is empty (the default), all columns are decomposed. -\item \textbf{mapping}: Boolean\\ - If \texttt{True}, return a dictionary mapping each original column name to a list of decomposed columns that replace it. -\item \textbf{chars}: string\\ - Specifies a string of characters to use when forming new node and column names. Default is lowercase letters. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{delete\_column(\emph{colname})}}\end{snugshade} -\label{sec:mulgrid:delete_column} -\index{MULgraph geometry!deleting!columns} -\index{MULgraph geometry!columns!deleting} - -Deletes the column with the specified name from the grid. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{colname}: string\\ - Name of the column to be deleted. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{delete\_connection(\emph{colnames})}}\end{snugshade} -\label{sec:mulgrid:delete_connection} -\index{MULgraph geometry!deleting!connections} -\index{MULgraph geometry!connections!deleting} - -Deletes the connection between the specified columns from the grid. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{colnames}: tuple of string\\ - Tuple of two column names. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{delete\_layer(\emph{layername})}}\end{snugshade} -\label{sec:mulgrid:delete_layer} -\index{MULgraph geometry!deleting!layers} -\index{MULgraph geometry!deleting!layers} - -Deletes the layer with the specified name from the grid. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{layername}: string\\ - Name of the layer to be deleted. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{delete\_node(\emph{nodename})}}\end{snugshade} -\label{sec:mulgrid:delete_node} -\index{MULgraph geometry!deleting!nodes} -\index{MULgraph geometry!nodes!deleting} - -Deletes the node with the specified name from the grid. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{nodename}: string\\ - Name of the node to be deleted. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{delete\_orphans()}}\end{snugshade} -\label{sec:mulgrid:delete_orphans} -\index{MULgraph geometry!deleting!nodes} -\index{MULgraph geometry!nodes!deleting} - -Deletes any orphaned nodes (those not belonging to any column) from the grid. - -\begin{snugshade}\subsubsection{\texttt{delete\_orphan\_wells()}}\end{snugshade} -\label{sec:mulgrid:delete_orphan_wells} -\index{MULgraph geometry!deleting!wells} -\index{MULgraph geometry!wells!deleting} - -Deletes any orphaned wells (those with wellheads outside the grid). - -\begin{snugshade}\subsubsection{\texttt{delete\_well(\emph{wellname})}}\end{snugshade} -\label{sec:mulgrid:delete_well} -\index{MULgraph geometry!deleting!wells} -\index{MULgraph geometry!wells!deleting} - -Deletes the well with the specified name from the grid. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{layername}: string\\ - Name of the layer to be deleted. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{empty()}}\end{snugshade} -\label{sec:mulgrid:empty} -\index{MULgraph geometry!emptying} - -Empties the grid of all its nodes, columns, layers, wells and connections. Other properties are unaffected. - -\begin{snugshade}\subsubsection{\texttt{export\_surfer(\emph{filename}='', \emph{aspect}=8.0, \emph{left}=0.0)}}\end{snugshade} -\label{sec:mulgrid:export_surfer} -\index{MULgraph geometry!exporting} - -Exports the grid to files on disk useful for visualization in Surfer. Six files are written out: - -\begin{itemize} -\item an Atlas BNA file (\texttt{filename.bna}) representing the grid columns -\item a CSV file (\texttt{filename\_column\_names.csv}) containing the column names -\item a Golden Software blanking file (\texttt{filename\_layers.bln}) file representing the grid layers -\item a CSV file (\texttt{filename\_layer\_bottom\_elevations.csv}) containing the bottom elevations of the layers -\item a CSV file (\texttt{filename\_layer\_centres.csv}) containing the elevations of the centres of the layers -\item a CSV file (\texttt{filename\_layer\_names.csv}) containing the names of the layers -\end{itemize} - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{filename}: string\\ - Base name for the exported files. If it is not specified, the \texttt{filename} property of the \texttt{mulgrid} object itself is used (unless this is also blank, in which case a default name is used), with its extension removed. -\item \textbf{aspect}: float\\ - Aspect ratio for the layer plot, so that the width is the total height of the grid divided by \texttt{aspect} (default 8.0). -\item \textbf{left}: float\\ - Coordinate value of the left hand side of the layer plot (default zero). -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{fit\_columns(\emph{data}, \emph{alpha}=0.1, \emph{beta}=0.1, \emph{columns}=[], \emph{min\_columns}=[], \\ - \emph{grid\_boundary}=False, \emph{silent}=False, \emph{output\_dict}=False)}}\end{snugshade} -\label{sec:mulgrid:fit_columns} -\index{MULgraph geometry!fitting data to columns} -\index{MULgraph geometry!columns!fitting data} - -Fits scattered data to column centres, using bilinear least-squares finite element fitting with Sobolev smoothing. Smoothing is useful when data density is low in some areas of the grid, in which case least-squares fitting without smoothing can fail (e.g. if there are any columns which do not contain any data points). - -By default, this method returns an \texttt{np.array} with length given by the number of columns to be fitted. Each value in the array represents the fitted data value at the centre of the corresponding column. If the \texttt{output\_dict} parameter is set to \texttt{True}, a dictionary is returned, with fitted values indexed by column names. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{data}: \texttt{np.array}\\ - Two-dimensional array of data to fit. Each row of the array should contain the x,y co-ordinates for each data point, followed by the corresponding data value. Such an array can be conveniently read from a text file using the \texttt{np.loadtxt()} method. -\item \textbf{alpha}: float\\ - Smoothing parameter for first derivatives - increasing its value results in solutions with lower gradients (but may result in extrema being smoothed out). -\item \textbf{beta}: float\\ - Smoothing parameter for second derivatives - increasing its value results in solutions with lower curvature. -\item \textbf{columns}: list of string or \hyperref[columnobjects]{\texttt{column}}\\ - Columns, or names of columns to be fitted. If empty (the default), then all columns will be fitted. -\item \textbf{min\_columns}: list of string or \hyperref[columnobjects]{\texttt{column}}\\ - Columns, or names of columns for which fitted data will be determined from the minimum of the fitted nodal values (fitted values at all other columns are determined from the average of the fitted nodal values). -\item \textbf{grid\_boundary}: Boolean\\ - If \texttt{True}, test each data point first to see if it lies inside the boundary polygon of the grid. This can speed up the fitting process if there are many data points outside the grid, and the grid has a simple boundary (e.g. a rectangle). In general if there are many data points outside the grid, it is best to clip the data set before fitting, particularly if it is to be used more than once. -\item \textbf{silent}: Boolean\\ - Set to \texttt{True} to suppress printing fitting progress. -\item \textbf{output\_dict}: Boolean\\ - Set \texttt{True} to return results as a dictionary of fitted values indexed by column names, instead of an array. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{fit\_surface(\emph{data}, \emph{alpha}=0.1, \emph{beta}=0.1, \emph{columns}=[], \emph{min\_columns}=[], \\ - \emph{grid\_boundary}=False, \emph{layer\_snap}=0.0, \emph{silent}=False)}}\end{snugshade} -\label{sec:mulgrid:fit_surface} -\index{MULgraph geometry!fitting surface elevations} -\index{MULgraph geometry!columns!surface elevation} - -Fits column surface elevations from data, using bilinear least-squares finite element fitting with Sobolev smoothing (using the \hyperref[sec:mulgrid:fit_columns]{\texttt{fit\_columns()}} method). Smoothing is useful when data density is low in some areas of the grid, in which case least-squares fitting without smoothing can fail (e.g. if there are any columns which do not contain any data points). Use the \texttt{layer\_snap} parameter to eliminate surface blocks with very small thickness. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{data}: \texttt{np.array}\\ - Two-dimensional array of data to fit. Each row of the array should contain the x,y,z values for each data point. Such an array can be conveniently read from a text file using the \texttt{np.loadtxt()} method. -\item \textbf{alpha}: float\\ - Smoothing parameter for first derivatives - increasing its value results in solutions with lower gradients (but may result in extrema being smoothed out). -\item \textbf{beta}: float\\ - Smoothing parameter for second derivatives - increasing its value results in solutions with lower curvature. -\item \textbf{columns}: list of string or \hyperref[columnobjects]{\texttt{column}}\\ - Columns, or names of columns to be fitted. If empty (the default), then all columns will be fitted. -\item \textbf{min\_columns}: list of string or \hyperref[columnobjects]{\texttt{column}}\\ - Columns, or names of columns for which elevations will be determined from the minimum of the fitted nodal elevations (elevations at all other columns are determined from the average of the fitted nodal elevations). -\item \textbf{grid\_boundary}: Boolean\\ - If \texttt{True}, test each data point first to see if it lies inside the boundary polygon of the grid. This can speed up the fitting process if there are many data points outside the grid, and the grid has a simple boundary (e.g. a rectangle). In general if there are many data points outside the grid, it is best to clip the data set before fitting, particularly if it is to be used more than once. -\item \textbf{layer\_snap}: float\\ - Smallest desired surface block thickness. Set to a positive value to prevent columns being assigned surface elevations that are very close to the bottom of a layer (resulting in very thin surface blocks). Default value is zero (i.e. no layer snapping). -\item \textbf{silent}: Boolean\\ - Set to \texttt{True} to suppress printing fitting progress. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{from\_amesh(\emph{input\_filename}='in', \emph{segment\_filename}='segmt', \emph{convention}=0, \\ - \emph{node\_tolerance}=None, \emph{justify}='r', \emph{chars}=ascii\_lowercase, \emph{spaces}=\texttt{True},\\ - \emph{block\_order}=None)}}\end{snugshade} -\label{sec:mulgrid:from_amesh} -\index{MULgraph geometry!creating!Voronoi} - -Returns a \texttt{mulgrid} object (and a block mapping dictionary) from a Voronoi mesh previously created by the AMESH utility \citep{AMESH}, or by other software that uses AMESH (e.g. WinGridder or Steinar). - -The block naming convention for the output \texttt{mulgrid} object can be specified via the \texttt{convention} parameter. Note that in general this may not be the same as the block naming convention of the original mesh created by AMESH. In fact, AMESH can create meshes with block naming conventions that do not correspond to any of the \hyperref[tb:mulgrid_conventions]{MULgraph conventions}. This is why the \texttt{from\_amesh()} method also returns a block mapping dictionary, which maps block names in the \texttt{mulgrid} geometry to the block names in the original AMESH grid. - -The optional \texttt{justify} and \texttt{case} parameters control the formatting of the character part of the block names. Additionally, the characters used to form node/column or layer names can be specified using the \texttt{chars} parameter. (This can be useful for example for grids with large numbers of nodes and/or columns, for which lowercase letters alone may not be enough.) - -The \texttt{from\_amesh()} method assumes the original AMESH grid has layers of constant thickness (i.e. all blocks in each layer of the AMESH input file have the same specified thickness). Grids with layers of non-constant thickness cannot be represented by a \texttt{mulgrid} object and will cause an exception to be raised. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{input\_filename}: string\\ - Filename for AMESH input file. Default is `in'. -\item \textbf{segment\_filename}: string\\ - Filename for AMESH output segment file. Default is `segmt'. -\item \textbf{convention}: integer\\ - Naming convention for grid columns and layers. -\item \textbf{node\_tolerance}: float or \texttt{None}\\ - Horizontal tolerance for identifying distinct nodes in the segment file. If a node is read in with horizontal distance from an existing node less than the tolerance, then the two nodes are assumed to be identical. If \texttt{None} (the default), then the tolerance is set to 90\% of the smallest segment length. If errors are encountered in identifying nodes belonging to the grid columns, it may be worth adjusting this parameter. -\item \textbf{justify}: string\\ - Specify `r' for the character part of the block names (first three characters) to be right-justified, `l' for left-justified. -\item \textbf{chars}: string\\ - Specify a string of characters to be used to form the character part of block names. For example, to use both lowercase and uppercase characters, set \texttt{chars} to \texttt{ascii\_lowercase + ascii\_uppercase}, or to use uppercase letters only, specify \texttt{ascii\_uppercase}. -\item \textbf{spaces}: Boolean\\ - Specify \texttt{False} to disallow spaces in character part of block names. In this case, the first element of the \texttt{chars} parameter functions like a `zero' and replaces spaces. -\item \textbf{block\_order}: string or \texttt{None}\\ - Specify \texttt{None} or `layer\_column' for default block ordering by layer and column, starting from the atmosphere. Specify `dmplex' to order blocks by geometrical type (8-node hexahedrons first followed by 6-node wedges) as in PETSc DMPlex meshes. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{from\_gmsh(\emph{filename}, \emph{layers}, \emph{convention}=0, \emph{atmosphere\_type}=2,\\ - \emph{top\_elevation}=0, \emph{chars} = ascii\_lowercase, \emph{spaces}=\texttt{True},\\ - \emph{block\_order}=None)}}\end{snugshade} -\label{sec:mulgrid:from_gmsh} -\index{MULgraph geometry!importing from \texttt{gmsh}} - -Imports a 2-D \texttt{gmsh} mesh into a geometry object. \texttt{gmsh} is a grid generation program (see \url{http://geuz.org/gmsh/}). The horizontal structure of the geometry object is created from the \texttt{gmsh} mesh, while the layer structure is specified via the \texttt{layers} parameter, a list of layer thicknesses. The elevation of the top surface can also be specified, as well as the naming convention and atmosphere type. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{filename}: string\\ - Name of the \texttt{gmsh} mesh file. -\item \textbf{layers}: list\\ - List of floats containing the desired layer thicknesses. -\item \textbf{convention}: integer\\ - Naming convention for grid columns and layers. -\item \textbf{atmosphere\_type}: integer\\ - Type of atmosphere. -\item \textbf{top\_elevation}: float\\ - Elevation of the top surface of the model (default is zero). -\item \textbf{chars}: string\\ - Specifies a string of characters to use when forming the character part of block names. Default is lowercase letters. -\item \textbf{spaces}: Boolean\\ - Specify \texttt{False} to disallow spaces in character part of block names. In this case, the first element of the \texttt{chars} parameter functions like a `zero' and replaces spaces. -\item \textbf{block\_order}: string or \texttt{None}\\ - Specify \texttt{None} or `layer\_column' for default block ordering by layer and column, starting from the atmosphere. Specify `dmplex' to order blocks by geometrical type (8-node hexahedrons first followed by 6-node wedges) as in PETSc DMPlex meshes. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{layer\_containing\_elevation(\emph{elevation})}}\end{snugshade} -\label{sec:mulgrid:layer_containing_elevation} -\index{MULgraph geometry!finding!layers} -\index{MULgraph geometry!layers!finding} - -Returns the grid layer containing the specified vertical elevation. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{elevation}: float\\ - Vertical elevation. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{layer\_mapping(\emph{geo})}}\end{snugshade} -\label{sec:mulgrid:layer_mapping} - -Returns a dictionary mapping each layer name in the \texttt{mulgrid} object \texttt{geo} to the name of the nearest layer in the object's own geometry. (Note: this mapping takes no account of the grid surface, which may alter which layer is nearest in a given column.) - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - The \texttt{mulgrid} object to create a layer mapping from. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{layer\_name(\emph{block\_name})}}\end{snugshade} -\label{sec:mulgrid:layer_name} -\index{MULgraph geometry!layers!names} -\index{MULgraph geometry!names!of layers} - -Gives the name of the layer corresponding to the specified block name, according to the naming convention of the grid. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{block\_name}: string\\ - Block name. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{layer\_plot(\emph{layer}, \emph{variable}=None, \emph{variable\_name}=None, \emph{unit}=None,\\ - \emph{column\_names}=None, \emph{node\_names}=None, \emph{column\_centres}=None, \emph{nodes}=None,\\ - \emph{colourmap}=None, \emph{linewidth}=0.2, \emph{linecolour}='black', \emph{aspect}='equal', \emph{plt}=None,\\ - \emph{subplot}=111, \emph{title}=None, \emph{xlabel}='x (m)', \emph{ylabel}='y (m)', \emph{contours}=False,\\ - \emph{contour\_label\_format}='\%3.0f', \emph{contour\_grid\_divisions}=(100,100),\\ - \emph{connections}=None, \emph{colourbar\_limits}=None, \emph{plot\_limits}=None, \emph{wells}=None,\\ - \emph{well\_names}=True, \emph{hide\_wells\_outside}=True, \emph{wellcolour}='blue', \\ - \emph{welllinewidth}=1.0, \emph{wellname\_bottom}=True, \emph{rocktypes}=None, \emph{allrocks}=False,\\ - \emph{rockgroup}=None, \emph{flow}=None, \emph{grid}=None, \emph{flux\_matrix}=None,\\ - \emph{flow\_variable\_name}=None, \emph{flow\_unit}=None, \emph{flow\_scale}=None,\\ - \emph{flow\_scale\_pos}=(0.5, 0.02), \emph{flow\_arrow\_width}=None, \emph{connection\_flows}=False,\\ - \emph{blockmap} = \{\}, \emph{block\_names}=None})}\end{snugshade} -\label{sec:mulgrid:layer_plot} -\index{MULgraph geometry!layers!plotting} -\index{MULgraph geometry!plotting!layers} - -Plots a variable over a layer of the grid, using the \texttt{matplotlib} plotting library. The required layer can be specified by name or as an elevation (in which case the routine will find the corresponding layer). Specifying the layer as \texttt{None} gives a plot over the ground surface of the geometry (i.e. the surface layer for each column). - -The variable can be a list or \texttt{np.array} containing a value for every block (or column) in the grid, in the order given by the \texttt{block\_name\_list} property of the geometry. If no variable is specified, only the grid in the layer is plotted, without shading. If the variable contains a value for each column in the grid, these values are extended down each column to fill the entire grid. - -The name and units of the variable can optionally be specified, and the names of the columns and nodes can also optionally be displayed on the plot, as well as the column centres (represented by crosses). The colour map and limits of the variable shading, the line width of the grid columns and the aspect ratio of the plot can also be set, as can the title and x- and y-axis labels, and the plot limits. - -When a variable is plotted over the grid, contours at specified levels can also be drawn, and optionally labelled with their values. - -Well tracks can also optionally be plotted. Each well is drawn as a line following the well track, with the well name at the bottom (or optionally the top) of the well. For surface plots (\texttt{layer} = \texttt{None}), wells are drawn with solid lines; otherwise, wells are drawn with dotted lines except where they pass through the specified layer, where they are drawn with solid lines. - -Rock types can be shown on the layer plot by specifying a \hyperref[t2grids]{\texttt{t2grid}} object as the \texttt{rocktypes} parameter. It is possible to group similar rock types (e.g. those in the same geological formation but with slightly different permeabilities) to simplify the plot if there are a lot of rock types. - -Flows can be shown on the layer by specifying an array of connection flow values (e.g mass flow) as the \texttt{flow} parameter. Flows will then be drawn on the slice by arrows at the block centres, each representing the average flux (flow per unit area) over the block, projected onto the layer. (For example, connection values of mass flow in kg/s will be represented as block-average mass fluxes in kg/$m^2$/s.) Alternatively, flows through the connection faces can be plotted by setting the \texttt{connection\_flows} parameter to \texttt{True}. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{layer}: \hyperref[layerobjects]{\texttt{layer}}, string, integer, float or \texttt{None}\\ - Layer or name (string) of layer to plot, or elevation (float or integer). Specifying \texttt{None} gives a surface plot. -\item \textbf{variable}: list (or \texttt{np.array})\\ - Variable to be plotted, of length equal to the number of blocks or columns in the grid (or \texttt{None} just to plot the grid). -\item \textbf{variable\_name}: string\\ - Name of the variable (as it will appear on the scale of the plot). -\item \textbf{unit}: string\\ - Units of the variable (as it will appear on the scale of the plot). -\item \textbf{column\_names}: Boolean or list\\ - Set to \texttt{True} if column names are to be indicated on the plot, or to a list of names of columns to be named. -\item \textbf{node\_names}: Boolean or list\\ - Set to \texttt{True} if node names are to be indicated on the plot, or to a list of names of nodes to be named. -\item \textbf{column\_centres}: Boolean or list\\ - Set to \texttt{True} if column centres are to be indicated on the plot (as crosses), or to a list of names of columns to be indicated. -\item \textbf{nodes}: Boolean or list\\ - Set to \texttt{True} if nodes are to be indicated on the plot (as crosses), or to a list of names of nodes to be indicated. -\item \textbf{colourmap}: string\\ - Name of \texttt{matplotlib} colour map to use for shading the variable. -\item \textbf{linewidth}: float\\ - Line width to use for drawing the grid. -\item \textbf{linecolour}: string\\ - Line colour to use for drawing the grid. -\item \textbf{aspect}: string\\ - Aspect ratio to use for drawing the grid (default is `equal' (i.e. 1:1). -\item \textbf{plt}: \texttt{matplotlib.pyplot} instance\\ - An instance of the \texttt{matplotlib.pyplot} library, imported in the calling script using e.g. \texttt{import matplotlib.pyplot as plt}. -\item \textbf{subplot}: integer\\ - Subplot number for multi-plots, e.g. set 223 to draw the third plot in a 2-by-2 multiplot (default is 111). -\item \textbf{title}: string\\ - Plot title. If set to \texttt{None} (the default value), a title will be constructed from the other plot parameters. Set to `' for no title. -\item \textbf{xlabel}: string\\ - x axis label (default is `x (m)'). -\item \textbf{ylabel}: string\\ - y axis label (default is `y (m)'). -\item \textbf{contours}: Boolean, list or \texttt{np.array}\\ - Set to \texttt{True} or to a list or array of contour values to draw contours on the plot (default \texttt{False}). -\item \textbf{contour\_label\_format}: string\\ - Format string for contour labels (default `\%3.0f'). -\item \textbf{contour\_grid\_divisions}: tuple (of integer)\\ - Number of divisions in the x- and y-directions in the regular grid superimposed on the model grid, and used to produce the contours (default (100,100)). -\item \textbf{connections}: float (or \texttt{None})\\ - Set non-zero to plot connections in the grid, shaded by absolute value of the connection angle cosine. The value specifies the lower cut-off value, above which connections will be plotted. Connections are shaded in greyscale from white (0.0) to black (1.0). This can be used to check orthogonality of grid connections, as less orthogonal connections (with larger angle cosine) will show up darker on the plot. If set to \texttt{None}, no connections will be plotted. -\item \textbf{colourbar\_limits}: tuple, list, \texttt{np.array} (or \texttt{None})\\ - Specify a two-element tuple, list or \texttt{np.array} to set the limits of the colour scale. Default (\texttt{None}) will auto-scale. -\item \textbf{plot\_limits}: tuple or list (or \texttt{None})\\ - Specify a two-element tuple (or list) of plot axis ranges, each itself being a tuple (or list) of minimum and maximum values, i.e. ((xmin,xmax),(ymin,ymax)). Default is \texttt{False} which will auto-scale. -\item \textbf{wells}: Boolean or list (or \texttt{None})\\ - Specify \texttt{True} to plot all well tracks, \texttt{False} or \texttt{None} not to plot them, or a list of wells or well names to specify only particular wells. -\item \textbf{well\_names}: Boolean or list (or \texttt{None})\\ - Specify \texttt{True} to label each well with its name , \texttt{False} or \texttt{None} not to label them, or a list of wells or well names to label only particular wells. -\item \textbf{hide\_wells\_outside}: Boolean\\ - Set to \texttt{True} if wells that do not intersect the specified layer are to be hidden. -\item \textbf{wellcolour}: string\\ - Colour to use for drawing the wells. -\item \textbf{welllinewidth}: float\\ - Line width for drawing the wells. -\item \textbf{wellname\_bottom}: Boolean\\ - Set to \texttt{False} to label wells at the wellhead rather than the bottom. -\item \textbf{rocktypes}: \hyperref[t2grids]{\texttt{t2grid}} (or \texttt{None})\\ - To plot rock types, specify a \texttt{t2grid} object containing rock types for the grid. If \texttt{None}, no rock types will be plotted. -\item \textbf{allrocks}: Boolean\\ - If \texttt{False} (the default), only rock types present on the specified layer will be shown in the colour bar; others will be omitted. If \texttt{True}, all rocks present in the model grid will be shown on the colour bar, regardless of whether they appear in the specified layer. -\item \textbf{rockgroup}: tuple, list, string (or \texttt{None})\\ - To group similar rock types into one colour, specify a tuple or list of integers, representing the significant characters of the rock type names. For example, to group rock types having the same first two characters, specify (0,1). Alternatively, specify a 5-character string mask containing asterisks in positions that are not significant, and any other characters in the significant positions (e.g. `++***'). -\item \textbf{flow}: \texttt{np.array} (or \texttt{None})\\ - To plot flows, specify an array of connection flow values (one floating point value for each connection in the grid). These may for example be extracted from the columns of the connection table in a \hyperref[listingfiles]{\texttt{t2listing}} object. -\item \textbf{grid}: \hyperref[t2grids]{\texttt{t2grid}} (or \texttt{None})\\ - Specify a \texttt{t2grid} object associated with the grid, to be used to calculate the `flux matrix' which converts the connection flow values to block-average fluxes. If this is not specified (and neither is the \texttt{flux\_matrix} parameter), then a \texttt{t2grid} object will be created internally. -\item \textbf{flux\_matrix}: \texttt{scipy.sparse.lil\_matrix} (or \texttt{None})\\ - A sparse matrix used to convert the connection flow values to block-average fluxes. Such a matrix can be created using the \hyperref[sec:t2grid:flux_matrix]{\texttt{flux\_matrix()}} method of a \texttt{t2grid} object and an appropriate \texttt{mulgrid} object. If no flux matrix is specified, one will be created internally. This can be time-consuming for large grids, so for multiple flow plots it is faster to pre-calculate a flux matrix in your script and pass it via this parameter. If this parameter is specified, there is no need also to specify the \texttt{grid} parameter. -\item \textbf{flow\_variable\_name}: string (or \texttt{None})\\ - Name of the flow variable (as it will appear on the scale of the plot). -\item \textbf{flow\_unit}: string (or \texttt{None})\\ - Units of the flow variable (as it will appear on the scale of the plot, divided by area). -\item \textbf{flow\_scale}: string (or \texttt{None})\\ - Length of flow scale arrow. If not specified, this will be calculated. -\item \textbf{flow\_scale\_pos}: tuple\\ - Position of the flow scale on the plot, in units of dimensionless plot size. The default (0.5, 0.02) draws the flow scale in the horizontal centre of the plot, slightly above the bottom axis. If you want the flow scale below the bottom axis (so it doesn't get mixed up with the actual flow arrows), specify this parameter with a small negative second component, e.g. (0.8, -0.1). -\item \textbf{flow\_arrow\_width}: float (or \texttt{None})\\ - Width of the flow arrows, in units of dimensionless plot width. If not specified, this will be calculated internally. -\item \textbf{connection\_flows}: Boolean\\ - Set to \texttt{True} to plot flows through connection faces, rather than block-averaged fluxes. In this case, usually the \texttt{grid} parameter should also be specified (but not \texttt{flux\_matrix}), otherwise a grid will be calculated internally. -\item \textbf{blockmap}: dictionary\\ - Dictionary mapping the block names in the geometry to another block naming system. This has an effect only on the block names displayed on the plot via the \texttt{block\_names} parameter, and on the rock types displayed. Note that if a mapping is used, then the \texttt{block\_names} list should contain mapped block names. -\item \textbf{block\_names}: Boolean or list\\ - Set to \texttt{True} if block names are to be indicated on the plot, or to a list of names of blocks to be named. -\end{itemize} - -\textbf{Example:} - -\begin{lstlisting} -geo.layer_plot(-500., t, 'Temperature', '$\degree$C', contours = np.arange(100,200,25)) -\end{lstlisting} - -plots the variable \texttt{t} at elevation -500 m over the grid, with the values as Temperature ($\degree$C), and with contours drawn from 100$\degree$C to 200$\degree$C with a contour interval of 25$\degree$C. - -\begin{snugshade} -\subsubsection{\texttt{line\_plot(\emph{start}=None, \emph{end}=None, \emph{variable}, \emph{variable\_name}=None,\\ -\emph{unit}=None, \emph{divisions}=100, \emph{plt}=None, \emph{subplot}=111, \emph{title}='',\\ -\emph{xlabel}='distance (m)', \emph{coordinate}=\texttt{False})}}\end{snugshade} -\label{sec:mulgrid:line_plot} -\index{MULgraph geometry!plotting!along a line} - -Plots a variable along a line through the grid, using the \texttt{matplotlib} plotting library. The line is specified by its start and end points in 3D. The variable can be a list or \texttt{np.array} containing a value for every block (or column) in the grid. If the variable contains a value for each column in the grid, these values are extended down each column to fill the entire grid. The name and units of the variable can optionally be specified, as well as the number of divisions the line is divided into (default 100), the plot title and the axis labels. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{start}, \textbf{end}: list, tuple or \texttt{np.array}\\ - Start and end point of the line, each of length 3 (\texttt{None} to plot across the bounds of the grid). -\item \textbf{variable}: list (or \texttt{np.array})\\ - Variable to be plotted, of length equal to the number of blocks (or columns) in the grid. -\item \textbf{variable\_name}: string\\ - Name of the variable (as it will appear on the scale of the plot). -\item \textbf{unit}: string\\ - Units of the variable (as it will appear on the scale of the plot). -\item \textbf{divisions}: integer\\ - Number of divisions to divide the line into (default 100). -\item \textbf{plt}: \texttt{matplotlib.pyplot} instance\\ - An instance of the \texttt{matplotlib.pyplot} library, imported in the calling script using e.g. \texttt{import matplotlib.pyplot as plt}. -\item \textbf{subplot}: integer\\ - Subplot number for multi-plots, e.g. set 223 to draw the third plot in a 2-by-2 multiplot (default is 111). -\item \textbf{title}: string\\ - Plot title. If set to \texttt{None} (the default value), a title will be constructed from the other plot parameters. Set to `' for no title. -\item \textbf{xlabel}: string\\ - x axis label (default is `distance (m)'). -\item \textbf{coordinate}: integer or Boolean\\ - If \texttt{False}, plot against distance along the line, otherwise plot against specified coordinate (0,1 or 2) values. -\end{itemize} - -\textbf{Example:} - -\begin{lstlisting} -geo.line_plot([0.,0.,500.], [1000.,0.,500.], t, 'Temperature', '$\degree$C') -\end{lstlisting} - -plots the variable \texttt{t} along a line from (0,0,500) to (1000,0,500) through the grid, with the values as Temperature ($\degree$C). - -\begin{snugshade}\subsubsection{\texttt{line\_values(\emph{start}, \emph{end}, \emph{variable}, \emph{divisions}=100, \emph{coordinate}=\texttt{False},\\ - \emph{qtree}=None)}}\end{snugshade} -\label{sec:mulgrid:line_values} -\index{MULgraph geometry!values!along a line} - -Returns values of a specified variable along an arbitrary line through the grid. The start and end points of the line (\texttt{start} and \texttt{end}) are 3-element lists, tuples or \texttt{np.arrays} specifying points in 3D. The variable can be a list or \texttt{np.array} containing a value for every block in the grid. The number of divisions along the line (default 100) can be optionally specified. - -The routine returns a tuple of two arrays (\emph{l},\emph{v}), the first (\emph{l}) containing the distance from the start (or the appropriate coordinate (0,1, or 2) if \texttt{coordinate} is specified) for each point along the line, and the second (\emph{v}) containing the value of the variable at that point. The value of the variable at any point is the (block average) value at the block containing the point. - -\index{MULgraph geometry!columns!quadtrees} -\index{quadtrees} - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{start}, \textbf{end}: list, tuple or \texttt{np.array} (of length 3)\\ - Start and end points of the line in 3D. -\item \textbf{variable}: list (or \texttt{np.array})\\ - Variable to be plotted, of length equal to the number of blocks in the grid. -\item \textbf{divisions}: integer\\ - Number of segments the line is divided up into (default 100). -\item \textbf{coordinate}: integer or Boolean\\ - If \texttt{False}, return distance along the line in first array, otherwise return specified coordinate (0,1 or 2) values. -\item \textbf{qtree}: \texttt{quadtree}\\ - Quadtree object for fast searching of grid columns (can be constructed using the \texttt{column\_quadtree()} method). -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{meshio\_grid(\emph{surface\_snap} = 0.1, \emph{dimension} = 3, \emph{slice} = \texttt{None})}}\end{snugshade} -\label{sec:mulgrid:meshio_grid} -\index{MULgraph geometry!meshio grid} - -Returns mesh corresponding to the geometry, in the format used by the \texttt{meshio} library (\url{https://pypi.python.org/pypi/meshio}). This consists of a two-element tuple: firstly, an \texttt{np.array} of nodal coordinates, and secondly a dictionary of element definitions, indexed by number of nodes in the elements. - -The primary use of this is as an interchange format for input/output of meshes in different formats. Note that exporting the geometry directly to a mesh file can also be done using the \hyperref[sec:mulgrid:write_mesh]{\texttt{write\_mesh()}} method (which is just a wrapper for this one). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{surface\_snap}: float\\ - Tolerance for eliminating elements with very small vertical thickness at the top of the mesh. -\item \textbf{dimension}: integer\\ - Dimension of the mesh: when set to 3, return the full 3-D mesh. When set to 2, return a 2-D mesh, corresponding either to the horizontal mesh only (the default), or a vertical slice mesh if the \texttt{slice} parameter is used. -\item \textbf{slice}: list, string, float or \texttt{None}\\ - Horizontal line defining the slice for vertical 2-D meshes. This can be a list of two horizontal (\emph{x},\emph{y}) points (\texttt{np.arrays}) defining the endpoints of the slice line, or string `x' or `y' to specify the \emph{x}- or \emph{y}-axis, or northing (float) through grid centre. If set to \texttt{None} (the default) then the horizontal 2-D mesh is returned. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{minc\_array(\emph{vals}, \emph{minc\_indices}, \emph{level}=0, \emph{outside}=0.0)}}\end{snugshade} -\label{sec:mulgrid:minc_array} -\index{MULgraph geometry!MINC arrays} -\index{MINC!arrays} - -Returns an array for all blocks in the geometry, with values taken from the input \texttt{vals} array, for the specified MINC level. Indexing of MINC blocks is specified by the \texttt{minc\_indices} array (returned by the \texttt{t2grid} \hyperref[sec:t2grid:MINC]{\texttt{minc()}} method). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{vals}: \texttt{np.array}\\ - Array of values over the entire MINC grid, with values for all MINC levels, obtained e.g. from a column of the element table of a \hyperref[listingfiles]{\texttt{t2listing}} object. -\item \textbf{minc\_indices}: \texttt{np.array} (of integer)\\ - Rank-2 array containing integer indices for each MINC level, obtained from the output of the \texttt{t2grid} \hyperref[sec:t2grid:MINC]{\texttt{minc()}} method. -\item \textbf{level}: integer\\ - MINC level, 0 being the fracture level and higher levels being the matrix levels. -\item \textbf{outside}: Boolean or float\\ - Determines how blocks outside the MINC part of the grid are handled. If \texttt{True}, include porous medium values outside the MINC part of the grid. If a float value is given, assign that value instead. If \texttt{False}, the value zero will be assigned. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{nodes\_in\_columns(\emph{columns})}}\end{snugshade} -\label{sec:mulgrid:nodes_in_columns} -\index{MULgraph geometry!finding!nodes} -\index{MULgraph geometry!nodes!finding} - -Returns a list of all nodes in a specified list of columns. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{columns}: list (of \hyperref[columnobjects]{\texttt{column}})\\ - List of columns in which to find nodes. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{nodes\_in\_polygon(\emph{polygon})}}\end{snugshade} -\label{sec:mulgrid:nodes_in_polygon} -\index{MULgraph geometry!finding!nodes} -\index{MULgraph geometry!nodes!finding} - -Returns a list of all nodes inside the specified polygon or rectangle. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{polygon}: list (of \texttt{np.array})\\ - List of points defining the polygon (each point is a two-element \texttt{np.array}). If the list has only two points, it will be interpreted as a rectangle [bottom left, top right]. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{node\_nearest\_to(\emph{point}, \emph{kdtree}=None)}}\end{snugshade} -\label{sec:mulgrid:node_nearest_to} -\index{MULgraph geometry!finding!nodes} -\index{MULgraph geometry!nodes!finding} - -Returns the node nearest to a specified point. An optional kd-tree structure can be specified to speed searching - useful if searching for many points. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{point}: \texttt{np.array}, list or tuple\\ - Array or list of length 2, specifying the required point in 2-D. -\item \textbf{kdtree}: \texttt{cKDTree}\\ - kd-tree structure for searching for nodes. Such a tree can be constructed using the \texttt{node\_kdtree} property of a \texttt{mulgrid} object. You will need the \texttt{scipy} library installed before you can use this property. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{optimize(\emph{nodenames}=None, \emph{connection\_angle\_weight}=1.0,\\ - \emph{column\_aspect\_weight}=0.0, \emph{column\_skewness\_weight}=0.0, \emph{pest}=False)}}\end{snugshade} -\label{sec:mulgrid:optimize} -\index{MULgraph geometry!optimizing} - -Adjusts positions of the specified nodes to optimize grid quality. If no nodes are specified, all node positions are optimized. Grid quality can be defined as a combination of connection angle cosine, column aspect ratio and column skewness. Increasing the weight for any of these increases its importance in the evaluation of grid quality. - -Note that an error will result if the connection angle weight and either of the other two weights is set to zero - in this case there are not enough constraints to fit the parameters. - -If the \texttt{pest} parameter is set to \texttt{True}, the PEST parameter estimation software is used to carry out the optimzation (this obviously requires that PEST is installed on your machine). Otherwise, the \texttt{leastsq} routine in the \texttt{scipy} Python library is used. PEST seems to be more robust in some cases, and often gives better results when nodes on the boundary of the grid are included in the optimization. However, when \texttt{leastsq} does work satisfactorily, it is generally faster (mainly because PEST has to read the geometry from disk and write it out again each time the grid quality is evaluated during the optimization). PEST is free software and may be downloaded from \url{http://www.pesthomepage.org/}. If PEST is used, a variety of intermediate files (named \texttt{pestmesh.*}) will be written to the working directory, including the PEST run record file (\texttt{pestmesh.rec}) which contains a detailed record of the optimization process. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{nodenames}: list of string\\ - List of names of nodes to optimize. If not specified, all nodes in the grid are optimized. -\item \textbf{connection\_angle\_weight}: float\\ - Weighting to be given to connection angle cosines. A higher value will place greater priority on making connections perpendicular to the column sides. -\item \textbf{column\_aspect\_weight}: float\\ - Weighting to be given to column aspect ratios. A higher value will place greater priority on making column side ratios closer to 1.0. -\item \textbf{column\_skewness\_weight}: float\\ - Weighting to be given to column skewness. A higher value will place greater priority on making column angle ratios closer to 1.0. -\item \textbf{pest}: Boolean\\ - Set \texttt{True} to use the PEST parameter estimation software to perform the optimization. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{polyline\_values(\emph{polyline}, \emph{variable}, \emph{divisions}=100, \emph{coordinate}=\texttt{False},\\ - \emph{qtree}=None)}}\end{snugshade} -\label{sec:mulgrid:polyline_values} -\index{MULgraph geometry!values!along a polyline} - -Returns values of a specified variable along an arbitrary polyline through the grid, defined as a list of 3-element lists or \texttt{np.arrays} specifying points in 3D. The variable can be a list or \texttt{np.array} containing a value for every block in the grid. The number of divisions along the line (default 100) can be optionally specified. - -The routine returns a tuple of two arrays (\texttt{l},\texttt{v}), the first (\texttt{l}) containing the distance from the start (or the appropriate coordinate (0, 1, or 2) if \texttt{coordinate} is specified) for each point along the polyline, and the second (\texttt{v}) containing the value of the variable at that point. The value of the variable at any point is the (block average) value at the block containing the point. - -\index{MULgraph geometry!columns!quadtrees} -\index{quadtrees} - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{polyline}: list of 3-element lists or \texttt{np.arrays}\\ - Polyline points in 3D. -\item \textbf{variable}: list (or \texttt{np.array})\\ - Variable to be plotted, of length equal to the number of blocks in the grid. -\item \textbf{divisions}: integer\\ - Number of segments the line is divided up into (default 100). -\item \textbf{coordinate}: integer or Boolean\\ - If \texttt{False}, return distance along the line in first array, otherwise return specified coordinate (0, 1 or 2) values. -\item \textbf{qtree}: \texttt{quadtree}\\ - Quadtree object for fast searching of grid columns (can be constructed using the \hyperref[sec:mulgrid:column_quadtree]{\texttt{column\_quadtree()}} method). -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{read(\emph{filename})}}\end{snugshade} -\label{sec:mulgrid:read} -\index{MULgraph geometry!reading} - -Reads a \texttt{mulgrid} object from a MULgraph geometry file on disk. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{filename}: string\\ - Name of the MULgraph geometry file to be read. -\end{itemize} - -\textbf{Example:} - -\begin{lstlisting} -geo = mulgrid().read(filename) -\end{lstlisting} - -creates a \texttt{mulgrid} object and reads its contents from file \texttt{filename}. This can be done more simply just by passing the filename into the \texttt{mulgrid} creation command: - -\begin{lstlisting} -geo = mulgrid(filename) -\end{lstlisting} - -\begin{snugshade} -\subsubsection{\texttt{rectangular(\emph{xblocks}, \emph{yblocks}, \emph{zblocks}, \emph{convention}=0, \emph{atmos\_type}=2,\\ - \emph{origin}=[0,0,0], \emph{justify}='r', \emph{case}=None, \emph{chars}=ascii\_lowercase,\\ - \emph{spaces}=\texttt{True}, \emph{block\_order}=None})}\end{snugshade} -\label{sec:mulgrid:rectangular} -\index{MULgraph geometry!creating!rectangular} - -Gives a \texttt{mulgrid} geometry object a rectangular grid structure. The grid sizes in the \emph{x}, \emph{y} and \emph{z} directions can be non-uniform, and the grid column and layer naming convention, atmosphere type and origin can be specified. The optional \texttt{justify} and \texttt{case} parameters control the formatting of the character part of the block names. Additionally, the characters used to form node/column or layer names can be specified using the \texttt{chars} parameter. (This can be useful for example for grids with large numbers of nodes and/or columns, for which lowercase letters alone may not be enough.) - -Note that it is also possible to reverse-engineer a rectangular geometry from an existing TOUGH2 data file or \texttt{t2grid} object, using the \hyperref[sec:t2grid:rectgeo]{\texttt{rectgeo()}} method. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{xblocks}, \textbf{yblocks}, \textbf{zblocks}: list, tuple or \texttt{np.array}\\ - Lists (or arrays) of block sizes (float) in the \emph{x}, \emph{y} and \emph{z} directions. -\item \textbf{convention}: integer\\ - Naming convention for grid columns and layers. -\item \textbf{atmos\_type}: integer\\ - Type of atmosphere. -\item \textbf{origin}: list (or \texttt{np.array})\\ - Origin of the grid (of length 3). -\item \textbf{justify}: string\\ - Specify `r' for the character part of the block names (first three characters) to be right-justified, `l' for left-justified. -\item \textbf{case}: string\\ - Specify `l' for the character part of the block names (first three characters) to be lower case, `u' for upper case. Now deprecated - using the \texttt{chars} parameter is more flexible. -\item \textbf{chars}: string\\ - Specify a string of characters to be used to form the character part of block names. For example, to use both lowercase and uppercase characters, set \texttt{chars} to \texttt{ascii\_lowercase + ascii\_uppercase}, or to use uppercase letters only, specify \texttt{ascii\_uppercase}. -\item \textbf{spaces}: Boolean\\ - Specify \texttt{False} to disallow spaces in character part of block names. In this case, the first element of the \texttt{chars} parameter functions like a `zero' and replaces spaces. -\item \textbf{block\_order}: string or \texttt{None}\\ - Specify \texttt{None} or `layer\_column' for default block ordering by layer and column, starting from the atmosphere. Specify `dmplex' to order blocks by geometrical type (8-node hexahedrons first followed by 6-node wedges) as in PETSc DMPlex meshes. -\end{itemize} - -\textbf{Example:} - -\begin{lstlisting} -geo = mulgrid().rectangular([1000]*10, [500]*20, [100]*5+[200]*10, origin=[0,0,2500]) -\end{lstlisting} - -creates a \texttt{mulgrid} object called \texttt{geo}, and fills it with a rectangular grid of 10 blocks of size 1000 m in the \emph{x}-direction, 20 blocks of size 500 m in the \emph{x}-direction, 5 layers at the top of thickness 100 m and 10 layers underneath of thickness 200 m, and with origin (0,0,2500) m. The grid will have the default naming convention (0) and atmosphere type (2). - -\begin{snugshade}\subsubsection{\texttt{reduce(\emph{columns})}}\end{snugshade} -\label{sec:mulgrid:reduce} -\index{MULgraph geometry!columns!deleting} -\index{MULgraph geometry!deleting!columns} - -Reduces a grid so that it contains only the specified list of columns (or columns with specified names). - -\textbf{Parameters:} -\begin{itemize} - \item \textbf{columns}: list\\ - List of required columns or column names. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{refine(\emph{columns}=[], \emph{bisect}=False, \emph{bisect\_edge\_columns}=[],\\ - \emph{chars} = ascii\_lowercase, \emph{spaces}=\texttt{True})}}\end{snugshade} -\label{sec:mulgrid:refine} -\index{MULgraph geometry!refining!columns} -\index{MULgraph geometry!columns!refining} - -Refines the specified columns in the grid. Appropriate transition columns are created around the refined region. If no columns are specified, all columns are refined. All columns in the region to be refined (and in the transition region) must be either triangular or quadrilateral. Each column in split into four, unless the \texttt{bisect} parameter is \texttt{True}, in which case each column in split into two. If \texttt{bisect} is `x' or `y', columns are split in the closest direction to the axis specified; or if \texttt{bisect} is \texttt{True}, between its longest sides. - -The \texttt{bisect\_edge\_columns} parameter can be used to give more desirable column shapes in the transition region, if the original columns occupying the transition region have large aspect ratios. By default, these will become even worse when they are triangulated to form the transition columns, if they are connected to the refinement region by their shorter sides. Including them in \texttt{bisect\_edge\_columns} means they will be bisected (parallel to the edge of the refinement region) before the refinement is carried out, which should improve the aspect ratios of the transition columns. - -\textbf{Note}: TOUGH2 implicitly assumes that the connections in its finite volume grids are orthogonal, i.e. the line joining the centres of two connected blocks should be perpendicular to the connecting face. The triangular transition columns generated by the \texttt{refine()} method generally give rise to connections that are not orthogonal. However, they can be modified and made as orthogonal as possible using the \hyperref[sec:mulgrid:optimize]{\texttt{optimize()}} method. - -\textbf{Parameters:} -\begin{itemize} - \item \textbf{columns}: list\\ - List of columns or column names to be refined. - \item \textbf{bisect}: Boolean or string\\ - Set to \texttt{True} if columns are to be split into two, between their longest sides, instead of four (the default). Set to `x' or `y' to split columns along the specified axis. - \item \textbf{bisect\_edge\_columns}: list\\ - List of columns or column names in the transition region (just outside the refinement area) to be bisected prior to the refinement, to improve the aspect ratios of the transition columns. - \item \textbf{chars}: string\\ - Specifies a string of characters to use when forming the character part of block names. Default is lowercase letters. -\item \textbf{spaces}: Boolean\\ - Specify \texttt{False} to disallow spaces in character part of block names. In this case, the first element of the \texttt{chars} parameter functions like a `zero' and replaces spaces. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{refine\_layers(\emph{layers}=[], \emph{factor}=2, \emph{chars} = ascii\_lowercase, \emph{spaces}=\texttt{True})}}\end{snugshade} -\label{sec:mulgrid:refine_layers} -\index{MULgraph geometry!refining!layers} -\index{MULgraph geometry!layers!refining} - -Refines the specified layers in the grid. If no layers are specified, all layers are refined. Each layer is refined by the specified integer factor. - -\textbf{Parameters:} -\begin{itemize} - \item \textbf{layers}: list\\ - List of layers or layer names to be refined. - \item \textbf{factor}: integer\\ - Refinement factor: default is 2, which bisects each layer. - \item \textbf{chars}: string\\ - Specifies a string of characters to use when forming the character part of block names. Default is lowercase letters. -\item \textbf{spaces}: Boolean\\ - Specify \texttt{False} to disallow spaces in character part of block names. In this case, the first element of the \texttt{chars} parameter functions like a `zero' and replaces spaces. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{rename\_column(\emph{oldcolname}, \emph{newcolname})}}\end{snugshade} -\label{sec:mulgrid:rename_column} -\index{MULgraph geometry!renaming!columns} -\index{MULgraph geometry!columns!renaming} - -Renames a grid column. Returns \texttt{True} if the column was found and renamed, or \texttt{False} if the specified column does not exist. Multiple columns can be renamed at once by specifying lists of old and new column names - this is faster than calling the method multiple times, and the block and connection name lists are updated only once. - -\textbf{Parameters:} -\begin{itemize} - \item \textbf{oldcolname}: string or list of strings\\ - Name(s) of the column(s) to rename. - \item \textbf{newcolname}: string or list of strings\\ - New name(s) of the column(s). -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{rename\_layer(\emph{oldlayername}, \emph{newlayername})}}\end{snugshade} -\label{sec:mulgrid:rename_layer} -\index{MULgraph geometry!renaming!layers} -\index{MULgraph geometry!layers!renaming} - -Renames a grid layer. Returns \texttt{True} if the layer was found and renamed, or \texttt{False} if the specified layer does not exist. Multiple layers can be renamed at once by specifying lists of old and new layer names - this is faster than calling the method multiple times, and the block and connection name lists are updated only once. - -\textbf{Parameters:} -\begin{itemize} - \item \textbf{oldlayername}: string or list of strings\\ - Name(s) of the layer(s) to rename. - \item \textbf{newlayername}: string or list of strings\\ - New name(s) of the layer(s). -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{rotate(\emph{angle}, \emph{centre}=\texttt{None}, \emph{wells}=\texttt{False})}}\end{snugshade} -\label{sec:mulgrid:rotate} -\index{MULgraph geometry!rotating} - -Rotates a grid by a specified angle (in degrees) clockwise in the horizontal plane. Any wells in the grid are also rotated. The centre of rotation can be optionally specified. If it is not specified, the centre of the grid is used as the centre of rotation. If the \texttt{wells} parameter is \texttt{True}, any wells in the grid are also rotated. - -\textbf{Parameters:} -\begin{itemize} - \item \textbf{angle}: float\\ - Angle (in degrees) to rotate the grid, positive for clockwise, negative for anti-clockwise. - \item \textbf{centre}: list, tuple or \texttt{np.array}\\ - Centre of rotation in the horizontal \emph{x},\emph{y} plane (of length 2). - \item \textbf{wells}: Boolean\\ - Set \texttt{True} to rotate wells. -\end{itemize} - -\textbf{Example:} - -\begin{lstlisting} -geo.rotate(30) -\end{lstlisting} - -rotates the grid \texttt{geo} clockwise by 30$\degree$ about its centre in the horizontal plane. - -\begin{snugshade} -\subsubsection{\texttt{slice\_plot(\emph{line}=None, \emph{variable}=None, \emph{variable\_name}=None, \emph{unit}=None,\\ - \emph{block\_names}=None, \emph{colourmap}=None, \emph{linewidth}=0.2, \emph{linecolour}='black',\\ - \emph{aspect}='auto', \emph{plt}=None, \emph{subplot}=111, \emph{title}=None, \emph{xlabel}='',\\ - \emph{ylabel}='elevation (m)', \emph{contours}=False, \emph{contour\_label\_format}='\%3.0f',\\ - \emph{contour\_grid\_divisions}=(100,100), \emph{colourbar\_limits}=None, \emph{plot\_limits}=None,\\ - \emph{column\_axis}=False, \emph{layer\_axis}=False, \emph{wells}=None, \emph{well\_names}=True,\\ - \emph{hide\_wells\_outside}=False, \emph{wellcolour}='blue', \emph{welllinewidth}=1.0,\\ - \emph{wellname\_bottom}=False, \emph{rocktypes}=None, \emph{allrocks}=False, \emph{rockgroup}=None,\\ - \emph{flow}=None, \emph{grid}=None, \emph{flux\_matrix}=None, \emph{flow\_variable\_name}=None,\\ - \emph{flow\_unit}=None, \emph{flow\_scale}=None, \emph{flow\_scale\_pos}=(0.5, 0.02),\\ - \emph{flow\_arrow\_width}=None, \emph{connection\_flows}=False, \emph{blockmap} = \{\})}} -\end{snugshade} -\label{sec:mulgrid:slice_plot} -\index{MULgraph geometry!plotting!slices} - -Plots a variable over a vertical slice through the grid, using the \texttt{matplotlib} plotting library. The required slice is specified by a horizontal line through the grid, defined as either a two-element list of (\emph{x},\emph{y}) points (\texttt{np.arrays}), or as a string `x' or `y' which defines the \emph{x}- or \emph{y}-axes respectively, or as a northing (in degrees) through the centre of the grid. If no line is specified, the line is taken to be across the bounds of the grid. For slice plots along the x- or y-axis, the horizontal coordinate represents the x- or y-coordinate; for other slice directions it represents distance along the slice line. - -The variable can be a list or \texttt{np.array} containing a value for every block (or column) in the grid, in the order given by the \texttt{block\_name\_list} property of the geometry. If no variable is specified, only the grid is plotted, without shading. If the variable contains a value for each column in the grid, these values are extended down each column to fill the entire grid. - -The name and units of the variable can optionally be specified, and the name of each block can also optionally be displayed on the plot. The colour map and limits of the variable shading, the line width of the grid columns and the aspect ratio of the plot can also be set, as can the plot title and x- and z-axis labels, and the plot limits. - -When a variable is plotted over the grid, contours at specified levels can also be drawn, and optionally labelled with their values. - -Well tracks can also optionally be plotted. Each well is drawn as a line following the well track, with the well name at the top (or optionally the bottom) of the well. If \texttt{hide\_wells\_outside} is specified as a floating point number, wells that do not pass within the specified distance from the slice line are not shown. Well tracks are shown as solid lines over sections within the specified distance from the slice line, and dotted lines otherwise. - -Rock types can be shown on the slice plot by specifying a \texttt{t2grid} object as the \texttt{rocktypes} parameter. It is possible to group similar rock types (e.g. those in the same geological formation but with slightly different permeabilities) to simplify the plot if there are a lot of rock types. - -Flows can be shown on the slice by specifying an array of connection flow values (e.g mass flow) as the \texttt{flow} parameter. Flows will then be drawn on the slice by arrows at the block centres, each representing the average flux (flow per unit area) over the block, projected onto the slice. (For example, connection values of mass flow in kg/s will be represented as block-average mass fluxes in kg/$m^2$/s.) Alternatively, flows through the connection faces can be plotted by setting the \texttt{connection\_flows} parameter to \texttt{True}. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{line}: list, string or float\\ - List of two horizontal (\emph{x},\emph{y}) points (\texttt{np.arrays}) defining the endpoints of the line, or string `x' or `y' to specify the \emph{x}- or \emph{y}-axis, or northing (float) through grid centre. -\item \textbf{variable}: list (or \texttt{np.array})\\ - Variable to be plotted, of length equal to the number of blocks (or columns) in the grid (or \texttt{None} just to plot the grid). -\item \textbf{variable\_name}: string\\ - Name of the variable (as it will appear on the scale of the plot). -\item \textbf{unit}: string\\ - Units of the variable (as it will appear on the scale of the plot). -\item \textbf{block\_names}: Boolean or list\\ - Set to \texttt{True} if block names are to be indicated on the plot, or to a list of names of blocks to be named. -\item \textbf{colourmap}: string\\ - Name of \texttt{matplotlib} colour map to use for shading the variable. -\item \textbf{linewidth}: float\\ - Line width to use for drawing the grid. -\item \textbf{linecolour}: string\\ - Line colour to use for drawing the grid. -\item \textbf{aspect}: string\\ - Aspect ratio to use for drawing the grid (default is `auto'). -\item \textbf{plt}: \texttt{matplotlib.pyplot} instance\\ - An instance of the \texttt{matplotlib.pyplot} library, imported in the calling script using e.g. \texttt{import matplotlib.pyplot as plt}. -\item \textbf{subplot}: integer\\ - Subplot number for multi-plots, e.g. set 223 to draw the third plot in a 2-by-2 multiplot (default is 111). -\item \textbf{title}: string\\ - Plot title. If set to \texttt{None} (the default value), a title will be constructed from the other plot parameters. Set to `' for no title. -\item \textbf{xlabel}: string\\ - x axis label. If set to \texttt{None} (the default value), a label will be constructed according to the slice orientation- either `x (m)', `y (m)' or `distance (m)' as appropriate. -\item \textbf{ylabel}: string\\ - y axis label (default is `elevation (m)'). -\item \textbf{contours}: Boolean, list or \texttt{np.array}\\ - Set to \texttt{True} or to a list or array of contour values to draw contours on the plot (default \texttt{False}). -\item \textbf{contour\_label\_format}: string\\ - Format string for contour labels (default `\%3.0f'). -\item \textbf{contour\_grid\_divisions}: tuple (of integer)\\ - Number of divisions in the x- and z-directions in the regular grid superimposed on the slice, and used to produce the contours (default (100,100)). -\item \textbf{colourbar\_limits}: tuple, list, \texttt{np.array} (or \texttt{None})\\ - Specify a two-element tuple, list or \texttt{np.array} to set the limits of the colour scale. Default (\texttt{None}) will auto-scale. -\item \textbf{plot\_limits}: tuple or list (or \texttt{None})\\ - Specify a two-element tuple (or list) of plot axis ranges, each itself being a tuple (or list) of minimum and maximum values, i.e. ((xmin,xmax),(zmin,zmax)). Default is \texttt{False} which will auto-scale. -\item \textbf{column\_axis}: Boolean\\ - If \texttt{True}, show column names instead of coordinates on the horizontal axis. -\item \textbf{layer\_axis}: Boolean\\ - If \texttt{True}, show layer names instead of coordinates on the vertical axis. -\item \textbf{wells}: Boolean or list (or \texttt{None})\\ - Specify \texttt{True} to plot all well tracks, \texttt{False} or \texttt{None} not to plot them, or a list of wells or well names to specify only particular wells. -\item \textbf{well\_names}: Boolean or list (or \texttt{None})\\ - Specify \texttt{True} to label each well with its name , \texttt{False} or \texttt{None} not to label them, or a list of wells or well names to label only particular wells. -\item \textbf{hide\_wells\_outside}: \texttt{False} or float\\ - Specify distance as a floating point number to hide wells further from the slice line than the specified distance. -\item \textbf{wellcolour}: string\\ - Colour to use for drawing the wells. -\item \textbf{welllinewidth}: float\\ - Line width for drawing the wells. -\item \textbf{wellname\_bottom}: Boolean\\ - Set to \texttt{True} to label wells at the bottom rather than the wellhead. -\item \textbf{rocktypes}: \hyperref[t2grids]{\texttt{t2grid}} (or \texttt{None})\\ - To plot rock types, specify a \texttt{t2grid} object containing rock types for the grid. If \texttt{None}, no rock types will be plotted. -\item \textbf{allrocks}: Boolean\\ - If \texttt{False} (the default), only rock types present on the specified slice will be shown in the colour bar; others will be omitted. If \texttt{True}, all rocks present in the model grid will be shown on the colour bar, regardless of whether they appear in the specified slice. -\item \textbf{rockgroup}: tuple, list, string (or \texttt{None})\\ - To group similar rock types into one colour, specify a tuple or list of integers, representing the significant characters of the rock type names. For example, to group rock types having the same first two characters, specify (0,1). Alternatively, specify a 5-character string mask containing asterisks in positions that are not significant, and any other characters in the significant positions (e.g. `++***'). -\item \textbf{flow}: \texttt{np.array} (or \texttt{None})\\ - To plot flows, specify an array of connection flow values (one floating point value for each connection in the grid). These may for example be extracted from the columns of the connection table in a \texttt{t2listing} object. -\item \textbf{grid}: \hyperref[t2grids]{\texttt{t2grid}} (or \texttt{None})\\ - Specify a \texttt{t2grid} object associated with the grid, to be used to calculate the `flux matrix' which converts the connection flow values to block-average fluxes. If this is not specified (and neither is the \texttt{flux\_matrix} parameter), then a \texttt{t2grid} object will be created internally. -\item \textbf{flux\_matrix}: \texttt{scipy.sparse.lil\_matrix} (or \texttt{None})\\ - A sparse matrix used to convert the connection flow values to block-average fluxes. Such a matrix can be created using the \texttt{flux\_matrix()} method of a \texttt{t2grid} object and an appropriate \texttt{mulgrid} object. If no flux matrix is specified, one will be created internally. This can be time-consuming for large grids, so for multiple flow plots it is faster to pre-calculate a flux matrix in your script and pass it via this parameter. If this parameter is specified, there is no need also to specify the \texttt{grid} parameter. -\item \textbf{flow\_variable\_name}: string (or \texttt{None})\\ - Name of the flow variable (as it will appear on the scale of the plot). -\item \textbf{flow\_unit}: string (or \texttt{None})\\ - Units of the flow variable (as it will appear on the scale of the plot, divided by area). -\item \textbf{flow\_scale}: string (or \texttt{None})\\ - Length of flow scale arrow. If not specified, this will be calculated. -\item \textbf{flow\_scale\_pos}: tuple\\ - Position of the flow scale on the plot, in units of dimensionless plot size. The default (0.5, 0.02) draws the flow scale in the horizontal centre of the plot, slightly above the bottom axis. If you want the flow scale below the bottom axis (so it doesn't get mixed up with the actual flow arrows), specify this parameter with a small negative second component, e.g. (0.8, -0.1). -\item \textbf{flow\_arrow\_width}: float (or \texttt{None})\\ - Width of the flow arrows, in units of dimensionless plot width. If not specified, this will be calculated internally. -\item \textbf{connection\_flows}: Boolean\\ - Set to \texttt{True} to plot flows through connection faces, rather than block-averaged fluxes. In this case, usually the \texttt{grid} parameter should also be specified (but not \texttt{flux\_matrix}), otherwise a grid will be calculated internally. -\item \textbf{blockmap}: dictionary\\ - Dictionary mapping the block names in the geometry to another block naming system. This has an effect only on the block names displayed on the plot via the \texttt{block\_names} parameter, and on the rock types displayed. Note that if a mapping is used, then the \texttt{block\_names} list should contain mapped block names. -\end{itemize} - -\textbf{Example:} - -\begin{lstlisting} -geo.slice_plot(45., t, 'Temperature', '$\degree$C', contours = [100,200]) -\end{lstlisting} - -plots the variable \texttt{t} through a SW--NE vertical slice (heading 45$\degree$) through the grid, with the values as Temperature ($\degree$C) and contours drawn at 100$\degree$C and 200$\degree$C. - -\begin{lstlisting} -from matplotlib import cm -cmap = cm.get_cmap('jet', 10) -geo.slice_plot(45., t, 'Temperature', '$\degree$C', - colourbar_limits = (0., 250.), colourmap = cmap) -\end{lstlisting} - -plots the variable \texttt{t} again, but with a specified discrete colour scale with 10 divisions from zero to 250$\degree$C. - -\begin{snugshade}\subsubsection{\texttt{snap\_columns\_to\_layers(\emph{min\_thickness}=1.0, \emph{columns}=[])}}\end{snugshade} -\label{sec:mulgrid:snap_columns_to_layers} -\index{MULgraph geometry!columns!surface elevation} - -Snaps column surfaces to the bottom of their layers, if the surface block thickness is smaller than a given value. This can be carried out over an optional subset of columns in the grid, otherwise over all columns. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{min\_thickness}: float\\ - Minimum surface block thickness. Blocks with thickness less than this value will be eliminated by `snapping' the column surface elevation to the bottom of the surface layer. Values of \texttt{min\_thickness} less than or equal to zero will have no effect. -\item \textbf{columns}: list (of \hyperref[columnobjects]{\texttt{column}} or string)\\ - List of columns to process. If empty (the default), process all columns. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{snap\_columns\_to\_nearest\_layers(\emph{columns}=[])}}\end{snugshade} -\label{sec:mulgrid:snap_columns_to_nearest_layers} -\index{MULgraph geometry!columns!surface elevation} - -Snaps column surfaces to the nearest layer elevation (top or bottom). This can be carried out over an optional subset of columns in the grid, otherwise over all columns. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{columns}: list (of \hyperref[columnobjects]{\texttt{column}} or string)\\ - List of columns to process. If empty (the default), process all columns. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{split\_column(\emph{colname}, \emph{nodename}, \emph{chars} = ascii\_lowercase)}}\end{snugshade} -\label{sec:mulgrid:split_column} -\index{MULgraph geometry!columns!splitting} - -Splits a quadrilateral column with specified name into two triangular columns. The direction of the split is determined -by specifying the name of one of the splitting nodes. The method returns \texttt{True} if the split was carried out successfully. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{colname}: string\\ - Name of the quadrilateral column to be split. If the column is not quadrilateral, the method returns \texttt{False} and nothing is done to the column. -\item \textbf{nodename}: string\\ - Name of one of the splitting nodes. The column is split across this node and the one on the opposite side of the column. If the specified node is not in the column, the method returns \texttt{False} and nothing is done to the column. -\item \textbf{chars}: string\\ - Specifies a string of characters to use when forming the character part of block names. Default is lowercase letters. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{translate(\emph{shift}, \emph{wells}=\texttt{False})}}\end{snugshade} -\label{sec:mulgrid:translate} -\index{MULgraph geometry!translating} - -Translates a grid by a specified shift in the \emph{x}, \emph{y} and \emph{z} directions. If the \texttt{wells} parameter is \texttt{True}, any wells in the grid are also translated. - -\textbf{Parameters:} -\begin{itemize} - \item \textbf{shift}: list, tuple or \texttt{np.array}\\ - Distance to shift the grid in the \emph{x}, \emph{y} and \emph{z} directions (of length 3). - \item \textbf{wells}: Boolean\\\ - Set \texttt{True} to translate wells. -\end{itemize} - -\textbf{Example:} - -\begin{lstlisting} -geo.translate([10.e3, 0.0, -1000.0]) -\end{lstlisting} - -translates the grid \texttt{geo} by 10 km in the \emph{x} direction and down 1 km in the \emph{z} direction. - -\begin{snugshade} -\subsubsection{\texttt{well\_values(\emph{well\_name}, \emph{variable}, \emph{divisions}=1, \emph{elevation}=\texttt{False}, \\ - \emph{deviations}=\texttt{False}, \emph{qtree}=None, \emph{extend}=\texttt{False})}} -\end{snugshade} -\label{sec:mulgrid:well_values} -\index{MULgraph geometry!values!down a well} - -Returns values of a specified variable down a specified well. The variable can be a list or \texttt{np.array} containing a value for every block in the grid. The number of divisions between layer centres or along each well deviation (default 1) can be optionally specified (this can be increased to capture detail along a deviation that passes through several blocks). If \texttt{deviations} is \texttt{True}, values will be returned at the nodes of the well track, instead of at grid layer centres. If \texttt{extend} is \texttt{True}, the well trace is artificially extended to the bottom of the model. - -The routine returns a tuple of two arrays (\texttt{d},\texttt{v}), the first (\texttt{d}) containing the measured depth down the well (or elevation if the \texttt{elevation} parameter is set to \texttt{True}), and the second (\texttt{v}) containing the value of the variable at each point. The value of the variable at any point is the (block average) value at the block containing the point. - -\index{MULgraph geometry!columns!quadtrees} -\index{quadtrees} - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{well\_name}: string\\ - Name of the well. -\item \textbf{variable}: list (or \texttt{np.array})\\ - Variable to be plotted, of length equal to the number of blocks in the grid. -\item \textbf{divisions}: integer\\ - Number of divisions each well deviation is divided up into (default 1). -\item \textbf{elevation}: Boolean\\ - Set to \texttt{True} if elevation rather than measured depth is to be returned. -\item \textbf{deviations}: Boolean\\ - Set to \texttt{True} to return values at deviation nodes, rather than intersections of layer centres with the well track. -\item \textbf{qtree}: \texttt{quadtree}\\ - Quadtree object for fast searching of grid columns (can be constructed using the \hyperref[sec:mulgrid:column_quadtree]{\texttt{column\_quadtree()}} method). -\item \textbf{extend}: Boolean\\ - Set \texttt{True} to artificially extend the well trace to the bottom of the model. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{write(\emph{filename}='')}}\end{snugshade} -\label{sec:mulgrid:write} -\index{MULgraph geometry!writing} - -Writes a \texttt{mulgrid} object to a MULgraph geometry file on disk. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{filename}: string\\ - Name of the MULgraph geometry file to be written. If no file name is specified, the object's own \texttt{filename} property is used. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{write\_bna(\emph{filename}='')}}\end{snugshade} -\label{sec:mulgrid:write_bna} -\index{MULgraph geometry!writing!BNA files} - -Writes a geometry object to an Atlas BNA file on disk, for visualisation with Surfer or GIS tools. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{filename}: string\\ - Name of the BNA file to be written. If no file name is specified, the object's own \texttt{filename} property is used, with the extension changed to *.bna. If the object's \texttt{filename} property is not set, the default name `geometry.bna' is used. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{write\_exodusii(\emph{filename}='', \emph{arrays}=None, \emph{blockmap}=\{\})}}\end{snugshade} -\label{sec:mulgrid:write_exodusii} -\index{MULgraph geometry!writing!ExodusII files} - -Writes a \texttt{mulgrid} object to an ExodusII file on disk, for visualisation or export to other software. - -This method uses the VTK-Python library, so you will need that installed on your machine before you can use it. An alternative is to use the \hyperref[sec:mulgrid:write_mesh]{\texttt{write\_mesh}} method instead, which can also write meshes to ExodusII format (as well as others), and does not need the VTK-Python library (though you will need the \texttt{meshio} library). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{filename}: string\\ - Name of the ExodusII file to be written. If no file name is specified, the object's own \texttt{filename} property is used, with the extension changed to *.exo. If the object's \texttt{filename} property is not set, the default name `geometry.exo' is used. -\item \textbf{arrays}: dictionary or \texttt{None}\\ - Data arrays to be included in the ExodusII file. If set to \texttt{None}, default arrays (block name, layer index, column index, column area, column elevation, block number and volume) are included. -\item \textbf{blockmap}: dictionary\\ - Dictionary mapping the block names in the geometry to another block naming system. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{write\_mesh(\emph{filename}, \emph{surface\_snap} = 0.1, \emph{dimension} = 3, \emph{slice} = \texttt{None}, \\ - \emph{file\_format} = \texttt{None})}}\end{snugshade} -\label{sec:mulgrid:write_mesh} -\index{MULgraph geometry!writing!mesh files} - -Writes a \texttt{mulgrid} object to a mesh file on disk, with the specific format determined by the file extension of the specified filename. This method uses the \texttt{meshio} library: - -\url{https://pypi.python.org/pypi/meshio} - -which must be installed on your machine, and supports various mesh output formats including Dolfin XML, ExodusII, MSH, VTK, XDMF and others. The \texttt{meshio} library may be installed from PyPI (using e.g. \texttt{pip install meshio}). - -Note that many of these formats do not support columns with more than four sides. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{filename}: string\\ - Name of the mesh file to be written. -\item \textbf{surface\_snap}: float\\ - Tolerance for eliminating elements with very small vertical thickness at the top of the mesh (3-D meshes only). -\item \textbf{dimension}: integer\\ - Dimension of the mesh: when set to 3 (the default), write the full 3-D mesh. When set to 2, write a 2-D mesh, corresponding either to the horizontal mesh only (the default), or a vertical slice mesh if the \texttt{slice} parameter is used. -\item \textbf{slice}: list, string, float or \texttt{None}\\ - Horizontal line defining the slice for vertical 2-D meshes. This can be a list of two horizontal (\emph{x},\emph{y}) points (\texttt{np.arrays}) defining the endpoints of the slice line, or string `x' or `y' to specify the \emph{x}- or \emph{y}-axis, or northing (float) through grid centre. If set to \texttt{None} (the default) then the horizontal 2-D mesh is written. -\item \textbf{file\_format}: string or \texttt{None}\\ - File format for mesh output. If \texttt{None}, the file format will be decided from the filename extension (e.g. if the filename is `mesh.exo' then the mesh will be written in ExodusII format). See the \texttt{meshio} documentation for details. -\end{itemize} - -\begin{snugshade}\subsubsection{\texttt{write\_vtk(\emph{filename}='', \emph{arrays}=None, \emph{wells}=False, \emph{blockmap}=\{\}, \\ - \emph{surface\_snap}=0.1)}}\end{snugshade} -\label{sec:mulgrid:write_vtk} -\index{MULgraph geometry!writing!VTK files} -\index{Visualization Tool Kit (VTK)} - -Writes a \texttt{mulgrid} object to a VTK file on disk, for visualisation with VTK, Paraview, Mayavi etc. The grid is written as an `unstructured grid' VTK object with optional data arrays defined on cells. A separate VTK file for the wells in the grid can optionally be written. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{filename}: string\\ - Name of the VTK file to be written. If no file name is specified, the object's own \texttt{filename} property is used, with the extension changed to *.vtu. If the object's \texttt{filename} property is not set, the default name `geometry.vtu' is used. -\item \textbf{arrays}: dictionary or \texttt{None}\\ - Data arrays to be included in the VTK file. If set to \texttt{None}, default arrays (block name, layer index, column index, column area, column elevation, block number and volume) are included. -\item \textbf{wells}: Boolean\\ - If set to \texttt{True}, a separate VTK file is written representing the wells in the grid. -\item \textbf{blockmap}: dictionary\\ - Dictionary mapping the block names in the geometry to another block naming system. -\item \textbf{surface\_snap}: float\\ - Tolerance for specifying how close column surface elevations need to be before being considered ``equal'' when constructing surface nodes. -\end{itemize} - -\section{Other objects (\texttt{node}, \texttt{column}, \texttt{layer}, \texttt{connection} and \texttt{well})} -\label{other_mulgrid_objects} - -A \texttt{mulgrid} object contains lists of other types of objects: \hyperref[nodeobjects]{\texttt{node}}, \hyperref[columnobjects]{\texttt{column}}, \hyperref[layerobjects]{\texttt{layer}}, \hyperref[connectionobjects]{\texttt{connection}} and \hyperref[wellobjects]{\texttt{well}} objects. These classes are described below. - -\subsection{\texttt{node} objects} -\label{nodeobjects} -\index{MULgraph geometry!nodes} -\index{PyTOUGH!classes!\texttt{node}} - -A \texttt{node} object represents a node (i.e. vertex) in a \texttt{mulgrid} object. A \texttt{node} object has three properties: \texttt{name}, which is a string property containing the name of the node, \texttt{pos} which is an \texttt{np.array} with three elements, containing the node's position in 3D, and \texttt{column} which is a set of the columns the node belongs to. A \texttt{node} object does not have any methods. - -A \texttt{node} object \texttt{n} can be created for example using the command \texttt{n = node(name,pos)} where \texttt{name} is the node name and pos is an \texttt{np.array} (or list, or tuple) representing the node's position. - -\subsection{\texttt{column} objects} -\label{columnobjects} -\index{MULgraph geometry!columns} -\index{PyTOUGH!classes!\texttt{column}} - -A \texttt{column} object represents a column in a \texttt{mulgrid} object. The properties of a \texttt{column} object are listed in Table \ref{tb:column_properties}. - -The main properties defining a column are its \texttt{name} and \texttt{node} properties. The \texttt{name} is specified according to the naming convention of the \texttt{mulgrid} object that the column belongs to. The \texttt{node} property is a list of \texttt{node} objects (not node names) that belong to the column. A \texttt{column}'s \texttt{neighbour} property is a set of other \texttt{columns} connected to that column via a \texttt{connection} (see section \ref{connectionobjects}), and its \texttt{connection} property is a set of connections the column is part of. The \texttt{neighbourlist} property is a list of neighbouring columns, with each item corresponding to a column edge (\texttt{None} if the edge is on a grid boundary). A \texttt{column}'s \texttt{centroid} property returns the average of the positions of its vertices - which is what the \texttt{centre} property is set to, unless otherwise specified. - -A \texttt{column} object has two properties measuring `grid quality'. The \texttt{angle\_ratio} property returns the ratio of largest to smallest interior angles in the column. The \texttt{side\_ratio} property returns the ratio of largest to smallest side lengths (a generalisation of `aspect ratio' to columns with any number of sides). Values as close as possible to 1.0 for both these measures are desirable (their values are both exactly 1.0 for any regular polygon, e.g. an equilateral triangle or square). Columns with large angle ratios will be highly skewed, while those with large side ratios will be typically highly elongated in one direction. - -A \texttt{column} object \texttt{col} can be created for example using the command: - -\begin{lstlisting} -col = column(name, nodes, centre, surface) -\end{lstlisting} - -where \texttt{name} is the column name and \texttt{nodes} is a list of \hyperref[nodeobjects]{\texttt{node}} objects defining the column. The \texttt{centre} and \texttt{surface} parameters are optional. - -\texttt{column} objects have three methods, \hyperref[sec:column:contains_point]{\texttt{contains\_point}}, \hyperref[sec:column:in_polygon]{\texttt{in\_polygon}} and \hyperref[sec:column:is_against]{\texttt{is\_against}}, as described below. - -\begin{snugshade} -\subsubsection{\texttt{contains\_point(\emph{pos})}} -\end{snugshade} -\label{sec:column:contains_point} -\index{MULgraph geometry!finding!columns} -\index{MULgraph geometry!columns!finding} - -Returns \texttt{True} if a 2D point lies inside the column, and \texttt{False} otherwise. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{pos}: \texttt{np.array}\\ - Horizontal position of the point. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{in\_polygon(\emph{polygon})}} -\end{snugshade} -\label{sec:column:in_polygon} -\index{MULgraph geometry!finding!columns} -\index{MULgraph geometry!columns!finding} - -Returns \texttt{true} if the column centre is inside the specified polygon or rectangle. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{polygon}: list (of \texttt{np.array})\\ - List of points defining the polygon (each point is a two-element \texttt{np.array}). If the list has only two points, it will be interpreted as a rectangle [bottom left, top right]. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{is\_against(\emph{othercolumn})}} -\end{snugshade} -\label{sec:column:is_against} - -Returns \texttt{true} if the column is `against' \texttt{othercolumn} -- that is, if it shares more than one node with it. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{othercolumn}: \texttt{column})\\ - Any other column in the geometry. -\end{itemize} - -\index{MULgraph geometry!columns!properties} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|} - \hline - \textbf{Property} & \textbf{Type} & \textbf{Description}\\ - \hline - \texttt{angle\_ratio} & float & ratio of largest to smallest interior angles \\ - \texttt{area} & float & horizontal area of the column \\ - \texttt{centre} & \texttt{np.array} & horizontal centre of the column \\ - \texttt{centroid} & \texttt{np.array} & average position of the column's vertices \\ - \texttt{connection} & set & connections the column is in \\ - \texttt{name} & string & name of the column \\ - \texttt{neighbour} & set & set of neighbouring columns \\ - \texttt{neighbourlist} & list & ordered list of neighbouring columns \\ - \texttt{node} & list & list of nodes (vertices) belonging to the column \\ - \texttt{num\_neighbours} & integer & number of neighbouring columns \\ - \texttt{num\_nodes} & integer & number of nodes belonging to the column \\ - \texttt{num\_layers} & integer & number of layers in the column below the ground surface \\ - \texttt{side\_ratio} & float & ratio of largest to smallest side length \\ - \texttt{surface} & float & surface elevation of the column (\texttt{None} if not specified)\\ - \hline - \end{tabular} - \caption{Properties of a \texttt{column} object} - \label{tb:column_properties} - \end{center} -\end{table} - -\subsection{\texttt{layer} objects} -\label{layerobjects} -\index{MULgraph geometry!layers} -\index{PyTOUGH!classes!\texttt{layer}} - -A \texttt{layer} object represents a layer in a \texttt{mulgrid} object. The properties of a \texttt{layer} object are given in Table \ref{tb:layer_properties}. - -A \texttt{layer} object \texttt{lay} can be created for example using the command: - -\begin{lstlisting} -lay = layer(name, bottom, centre, top) -\end{lstlisting} - -where \texttt{name} is the layer name and \texttt{bottom}, \texttt{centre} and \texttt{top} specify the vertical position of the layer. - -The methods of a \texttt{layer} object are as follows: - -\begin{snugshade} -\subsubsection{\texttt{contains\_elevation(\emph{z})}} -\end{snugshade} -\index{MULgraph geometry!finding!layers} -\index{MULgraph geometry!layers!finding} - -Returns \texttt{True} if a point at a given elevation lies inside the layer, and \texttt{False} otherwise. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{z}: float\\ - Elevation of the point. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{translate(\emph{shift})}} -\end{snugshade} -\index{MULgraph geometry!layers!translating} - -Translates a layer up or down by a specified distance. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{shift}: float\\ - Distance to shift the layer (positive for up, negative for down). -\end{itemize} - -\index{MULgraph geometry!layers!properties} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|} - \hline - \textbf{Property} & \textbf{Type} & \textbf{Description}\\ - \hline - \texttt{bottom} & float & elevation of the bottom of the layer \\ - \texttt{centre} & float & elevation of the centre of the layer \\ - \texttt{thickness} & float & layer thickness (top - bottom) \\ - \texttt{top} & float & elevation of the top of the layer \\ - \texttt{name} & string & name of the layer \\ - \hline - \end{tabular} - \caption{Properties of a \texttt{layer} object} - \label{tb:layer_properties} - \end{center} -\end{table} - -\subsection{\texttt{connection} objects} -\label{connectionobjects} -\index{MULgraph geometry!connections} -\index{PyTOUGH!classes!\texttt{connection}} - -A \texttt{connection} object represents a connection between \texttt{columns} in a \texttt{mulgrid} object. It has three properties: \texttt{column}, which contains a two-element list of the \texttt{column} objects making up the connection, \texttt{node}, which contains a two-element list of the \texttt{nodes} on the face joining the two columns in the connection, and \texttt{angle\_cosine}, which gives the cosine of the angle between a line joining the nodes in the connection and a line joining the centres of the two columns. This is used as a measure of grid quality, these two lines should ideally be as close to perpendicular as possible, making the cosine of the angle zero. A \texttt{connection} has no methods. - -A \texttt{connection} object \texttt{con} can be created for example using the command - -\begin{lstlisting} -con = connection(cols) -\end{lstlisting} - -where \texttt{cols} is a two-element list of the \texttt{column} objects in the connection. - -\subsection{\texttt{well} objects} -\label{wellobjects} -\index{MULgraph geometry!wells} -\index{PyTOUGH!classes!\texttt{well}} - -A \texttt{well} object represents a well in a \texttt{mulgrid} object. The properties of a \texttt{well} object are given in Table \ref{tb:well_properties}. - -\index{MULgraph geometry!wells!properties} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|} - \hline - \textbf{Property} & \textbf{Type} & \textbf{Description}\\ - \hline - \texttt{bottom} & \texttt{np.array} & well bottom position \\ - \texttt{deviated} & Boolean & whether well is deviated\\ - \texttt{head} & \texttt{np.array} & well head position \\ - \texttt{name} & string & well name \\ - \texttt{num\_deviations} & integer & number of deviations \\ - \texttt{num\_pos} & integer & number of well track nodes \\ - \texttt{pos} & list & positions (3-D arrays) of well track nodes \\ - \texttt{pos\_depth} & \texttt{np.array} & downhole depths along well track \\ - \hline - \end{tabular} - \caption{Properties of a \texttt{well} object} - \label{tb:well_properties} - \end{center} -\end{table} - -The well track can be deviated, and is defined as a list \texttt{pos} of (at least two) 3D positions (\texttt{np.arrays}). The \texttt{num\_deviations} property returns the number of deviations in the track (one less than the \texttt{num\_pos} property, which is the number of nodes in the \texttt{pos} list). The \texttt{deviated} property returns \texttt{True} if there is more than one deviation. The \texttt{pos\_depth} property returns an array of the downhole depths at each node along the well track. - -A \texttt{well} object \texttt{w} can be created simply with the command \texttt{w = well(name,pos)}, where \texttt{name} is the well name and \texttt{pos} is a list of 3-element \texttt{np.arrays} (or lists, or tuples) representing the well trace (starting from the wellhead). - -The methods of a \texttt{well} object are listed in Table \ref{tb:well_methods} and described below. - -\index{MULgraph geometry!wells!methods} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|} - \hline - \textbf{Method} & \textbf{Type} & \textbf{Description}\\ - \hline - \hyperref[sec:well:depth_elevation]{\texttt{depth\_elevation}} & float & elevation for a given downhole depth \\ - \hyperref[sec:well:depth_pos]{\texttt{depth\_pos}} & \texttt{np.array} & position on well track for a given downhole depth \\ - \hyperref[sec:well:elevation_depth]{\texttt{elevation\_depth}} & float & downhole depth for a given elevation \\ - \hyperref[sec:well:elevation_pos]{\texttt{elevation\_pos}} & \texttt{np.array} & position on well track for a given elevation \\ - \hyperref[sec:well:pos_coordinate]{\texttt{pos\_coordinate}} & \texttt{np.array} & array of coordinates for a given index \\ - \hline - \end{tabular} - \caption{Methods of a \texttt{well} object} - \label{tb:well_methods} - \end{center} -\end{table} - -\begin{snugshade} -\subsubsection{\texttt{depth\_elevation(\emph{depth})}} -\end{snugshade} -\label{sec:well:depth_elevation} -\index{MULgraph geometry!wells!depth to elevation} - -Returns the elevation corresponding to the specified downhole \texttt{depth} (or \texttt{None} if \texttt{depth} is above the wellhead or below the bottom). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{depth}: float\\ - Downhole depth. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{depth\_pos(\emph{depth})}} -\end{snugshade} -\label{sec:well:depth_pos} -\index{MULgraph geometry!wells!depth to 3-D position} - -Returns the 3D position of the point in the well with specified downhole \texttt{depth} (or \texttt{None} if \texttt{depth} is above the wellhead or below the bottom). The position is interpolated between the deviation locations. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{depth}: float\\ - Downhole depth of the required point. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{elevation\_depth(\emph{elevation})}} -\end{snugshade} -\label{sec:well:elevation_depth} -\index{MULgraph geometry!wells!elevation to depth} - -Returns the downhole depth corresponding to the specified \texttt{elevation} (or \texttt{None} if \texttt{elevation} is above the wellhead or below the bottom). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{elevation}: float\\ - Elevation. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{elevation\_pos(\emph{elevation}, \emph{extend}=\texttt{False})}} -\end{snugshade} -\label{sec:well:elevation_pos} -\index{MULgraph geometry!wells!elevation to 3-D position} - -Returns the 3D position of the point in the well with specified \texttt{elevation} (or \texttt{None} if \texttt{elevation} is above the wellhead or below the bottom). The position is interpolated between the deviation locations. If \texttt{extend} is \texttt{True}, return extrapolated positions for elevations below the bottom of the well. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{elevation}: float\\ - Elevation of the required point. -\item \textbf{extend}: Boolean\\ - If \texttt{True}, extrapolated positions will be returned for elevations below the bottom of the well (otherwise \texttt{None} will be returned). -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{pos\_coordinate(\emph{index})}} -\end{snugshade} -\label{sec:well:pos_coordinate} -\index{MULgraph geometry!wells!deviation coordinates} - -Returns an \texttt{np.array} of the well track node coordinates for the given index (0, 1 or 2). For example, \texttt{pos\_coordinate(2)} returns an array containing the elevations of all well track nodes. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{index}: integer\\ - Index required (0, 1 or 2). -\end{itemize} - -\section{Other functions: block name conversions} - -The \texttt{mulgrids} library contains two other functions connected with working with geometry files and TOUGH2 grids: - -\begin{snugshade} -\subsubsection{\texttt{fix\_blockname(\emph{name})}} -\end{snugshade} -\label{sec:mulgrid:fix_blockname} -\index{MULgraph geometry!blocks!names} -\index{TOUGH2 grids!blocks!names} - -TOUGH2 always assumes that the last two characters of a block name represent a two-digit number. However, if that number is less than 10, the fourth character is not padded with zeros, so for example `AA101' becomes 'AA1 1' when processed by TOUGH2. - -The \texttt{fix\_blockname} function corrects this by padding the fourth character of a block name with a zero if necessary. This is only done if the third character is also a digit, e.g. when naming convention 2 is used (two characters for layer followed by three digits for column). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{name}: string\\ - Block name. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{unfix\_blockname(\emph{name})}} -\end{snugshade} -\index{MULgraph geometry!blocks!names} -\index{TOUGH2 grids!blocks!names} - -This function reverses the effect of \texttt{fix\_blockname()}. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{name}: string\\ - Block name. -\end{itemize} - -\section{Block mappings: handling other block naming conventions} -\label{sec:mulgrid:blockmappings} -\index{TOUGH2 grids!blocks!mappings} - -The MULgraph geometry format names blocks according to one of its three \hyperref[geometry_format_conventions]{naming conventions}. All of these conventions use part of the block name to indicate the layer and part of it to indicate the column. - -However, in PyTOUGH it is possible to make a \texttt{mulgrid} object handle other block naming conventions by means of a \textbf{block mapping}. This is simply a dictionary that maps the block names in a \texttt{mulgrid} to block names in a \texttt{t2grid} object. The block names in the \texttt{t2grid} can follow an arbitrary convention, not based on layers and columns. For example, blocks in TOUGH2 grids created by PetraSim may be simply numbered. - -A block mapping dictionary can be passed in as an optional parameter to many PyTOUGH methods that involve both a MULgraph geometry and TOUGH2 grid, for example the \texttt{mulgrid} \hyperref[sec:mulgrid:block_name]{\texttt{block\_name()}}, \hyperref[sec:mulgrid:slice_plot]{\texttt{slice\_plot()}} and \hyperref[sec:mulgrid:write_vtk]{\texttt{write\_vtk()}} methods, and the \texttt{write\_vtk()} methods of the \hyperref[sec:t2grid:write_vtk]{\texttt{t2grid}} and \hyperref[sec:t2listing:write_vtk]{\texttt{t2listing}} classes. - -When the \hyperref[sec:t2grid:rectgeo]{\texttt{rectgeo()}} method is used to create a \texttt{mulgrid} object from a \texttt{t2grid}, a block mapping is also created, and may be used in the PyTOUGH methods that can accept a block mapping. - -A block mapping need not contain entries for all blocks. If for example a model follows the naming convention of a MULgraph geometry in most blocks, and only a few are different, then only entries for the different block names need be present in the mapping dictionary. - -Block mappings can be saved to and loaded from disk (like any other Python object) using the \texttt{pickle} library. This is part of the standard Python library collection. For example a block mapping called \texttt{blockmap} can be saved to a file called \texttt{'blockmap.pkl'} as follows: - -\begin{lstlisting} - import pickle - pickle.dump(blockmap, file('blockmap.pkl', 'w')) -\end{lstlisting} - -It can be loaded back in again like this: - -\begin{lstlisting} - blockmap = pickle.load(file('blockmap.pkl')) -\end{lstlisting} - diff --git a/doc/ptplot.eps b/doc/ptplot.eps deleted file mode 100755 index 73616600..00000000 --- a/doc/ptplot.eps +++ /dev/null @@ -1,1350 +0,0 @@ -%!PS-Adobe-3.0 EPSF-3.0 -%%BoundingBox: 83 247 510 532 -%%HiResBoundingBox: 83.933531 247.663076 509.130381 531.127642 -%................................... -%%Creator: GPL Ghostscript 860 (pswrite) -%%CreationDate: 2009/08/24 15:19:49 -%%DocumentData: Clean7Bit -%%LanguageLevel: 2 -%%EndComments -%%BeginProlog -save -countdictstack -mark -newpath -/showpage {} def -/setpagedevice {pop} def -%%EndProlog -%%Page 1 1 -%%BeginProlog -% This copyright applies to everything between here and the %%EndProlog: -% Copyright (C) 2007 Artifex Software, Inc. All rights reserved. -%%BeginResource: procset GS_pswrite_2_0_1001 1.001 0 -/GS_pswrite_2_0_1001 80 dict dup begin -/PageSize 2 array def/setpagesize{ PageSize aload pop 3 index eq exch -4 index eq and{ pop pop pop}{ PageSize dup 1 -5 -1 roll put 0 4 -1 roll put dup null eq {false} {dup where} ifelse{ exch get exec} -{ pop/setpagedevice where -{ pop 1 dict dup /PageSize PageSize put setpagedevice} -{ /setpage where{ pop PageSize aload pop pageparams 3 {exch pop} repeat -setpage}if}ifelse}ifelse}ifelse} bind def -/!{bind def}bind def/#{load def}!/N/counttomark # -/rG{3{3 -1 roll 255 div}repeat setrgbcolor}!/G{255 div setgray}!/K{0 G}! -/r6{dup 3 -1 roll rG}!/r5{dup 3 1 roll rG}!/r3{dup rG}! -/w/setlinewidth #/J/setlinecap # -/j/setlinejoin #/M/setmiterlimit #/d/setdash #/i/setflat # -/m/moveto #/l/lineto #/c/rcurveto # -/p{N 2 idiv{N -2 roll rlineto}repeat}! -/P{N 0 gt{N -2 roll moveto p}if}! -/h{p closepath}!/H{P closepath}! -/lx{0 rlineto}!/ly{0 exch rlineto}!/v{0 0 6 2 roll c}!/y{2 copy c}! -/re{4 -2 roll m exch dup lx exch ly neg lx h}! -/^{3 index neg 3 index neg}! -/f{P fill}!/f*{P eofill}!/s{H stroke}!/S{P stroke}! -/q/gsave #/Q/grestore #/rf{re fill}! -/Y{P clip newpath}!/Y*{P eoclip newpath}!/rY{re Y}! -/|={pop exch 4 1 roll 1 array astore cvx 3 array astore cvx exch 1 index def exec}! -/|{exch string readstring |=}! -/+{dup type/nametype eq{2 index 7 add -3 bitshift 2 index mul}if}! -/@/currentfile #/${+ @ |}! -/B{{2 copy string{readstring pop}aload pop 4 array astore cvx -3 1 roll}repeat pop pop true}! -/Ix{[1 0 0 1 11 -2 roll exch neg exch neg]exch}! -/,{true exch Ix imagemask}!/If{false exch Ix imagemask}!/I{exch Ix image}! -/Ic{exch Ix false 3 colorimage}! -/F{/Columns counttomark 3 add -2 roll/Rows exch/K -1/BlackIs1 true>> -/CCITTFaxDecode filter}!/FX{< -Q -Q q -0 0 51000 0 0 66000 ^ Y -K -11957 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -15012 22572 279 553 @C -, --Q-19EX`E,O8o7[s8VY%)$-]u5\(0_(^GZ2m6D^"o<9?"25%cQ"Ulef6WU1/^qN)+=i_Xq -*.d)VEX.1*E./>6pcf#Bpcf;,n9uI?s5^t+#'h[,Js@H@)5UjS!dQO'_+Jn/GUh#:%gL/0!:\L/ -l3Dr@*Y/L._;OhPG^+Hq4?P_hiSiba%sWH=hB)EH^$cJYg:cl[eV.Nde:7~> -15392 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -15812 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -18413 22572 336 553 @C -, -3"'+cs82irp]&"cp\2/cHf!TIIsZCQmsEg`^$fk5^V.89pV"39+C[oHATS!=ZFGF$[T?KJG08qJ -VV$6(mJD6Y[dEeFhehcS*SZ#9qepD^G2D+p\(c>e^>EeppA9&NmJH;EqepB>pA8cH\*iYWhS\aS -qfcteqsF+SGAZa@qgH37mHsWEID4TU]7%GB^>Ee9p<8hTG4"%gpMSg!G4"%gpMSg!G3h;6mI&fB -Fre2las6]ius!<5A(CcAFQtC;&pd"U?n=@E*kWYUFo_Ntol1FrHqL@c&p\*)75OVtX -5QC`Us8W-!s0([:s8QI+HIpbUs0#H"9Ds1?l"tb>>4f6>Wl"D:'k"[,a>0nZa=8juL/R;"D:eKH -s6V?6m[!lS?.IPV9&Jpfm]O1&Y3P7i[[6BX[Cod;IC-Rdqe9mtpafL(^`- -18825 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -19245 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -21838 22554 351 571 @C -, -1)^SSOFU5s"p]nS0*B,9LBG@\m)T\ARG-"/)Y:7"4;I$o5^487"+ga\%L=$si.8]L#WP*.#7*:3 -ir#WG#$%M0c=!Xq*r\3P[g'+TU' -_?AZth#nR:'6Fn,PB7I['6KU`'6Fn0S#"jCh'V>9Uu%T:h$)5EPB<;EdVb>uI5ad_2libHYI@-g -5-C*!Uu&8XX/5OS?B'j7,V^N6ORDiYgli\0XtP1@;oVa0O023XF5Tu -fDj;!li0WVInT`g=6n<7?C02boj)=DoX3pu]%(prdakCD<4,A!eLFb!92qm*Pq#`J/Q/E2;PcZG -<2_9CJu(b"<"pVa-p^0,&5Yt1:dIAN1,B$fa_226P)p_+G"qbY>qF6Kj*ki9+.u:,ciX&=>%[3aB`=5j9r6sjKc5'XnL39D/IkP\+HfQ[G>%Z -h-418[&c*de[2cn)HQ.WBLF:r7r$0VO)Q'JQ2r6OF59(aq(7@%b>2b^f35K-5Cq -896A$<&)jb^]~> -22257 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -22677 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -25262 22572 370 562 @C -, -1=TlWioDm@;oeqk\DVTI9(N/$?<86[%;7Fl@%i9h(fmbB[I&iZ7c1AgeFg[5$Mn%WeuIc%m< -^O?!.qtC!_mJHA,s)5CGhf&J.IJ\KjpAOaQqsW;6hm`'hDr6$$qgH6Qqg\#1n([To\*q(MhgXIs -rP*dXJ*lt0g[5$Mg])3LIc%m<^O?!.qtC!_mJHA,s)5CGhf&J-htqm"pAOaQqsW;6hm`'hDuIE- -qgH6Qqg\#,s4d;*\*q(MhgXIsrP*dXJ*lt0g[5$Mg])3$rnkHg^O?!.qtC!_mJHA,s)5CGhf&J- -htqi:pAOaQqsW;6hm`'hDuIE-qgH6Qqg\#,s4cr@\*q(MhgXIsrP*dXJ*lt0g[5$Mg])3$rnhYm -^O?!.qtC!_mJjZMs)5CGhf&J-htqi:pAOaQqsW;6hm`'hDuG^qp\2G9rU94DIs_(MhZ!9D^V0U7 -msfh6qqp90p\2G9rU93)Hi*]thYu'om9\9rTnN~> -25690 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -26110 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -28711 22554 336 571 @C -, -1=.eR=:2C@5ZsY[JaR\R#D\kLJ`3m?*"`:j8koX3&3)r5K7j^GE.Pk9&9*8T*$FH)6,F+f*u>D! -"eecVJO!.e5iH*3+Jr`k!X@Tb"s!aSclcc6#WG#$F:_m-6+RAX5mH9Nh&tMV_k;X(S"Q7H)k`R? -S&0',FG"ZKFIT)^-L!IBh'VAl-M6+@kr23+h(,;YDNf1U2bCS^gSF\W:"AoYS(6'^FIW4Us%0n0 -?WUI\f(At+IC`e!4`=\FQg_U?%rnr -L6l;riJ%nLL(`Ht#VNrH\7!/]Y[K.%"]@Ut+Nau"A.?KZiK,QB(CWXhY/9.O_1fc1_[D))L'rVA -K*B)Ci5crJD.>YX4=<5[5NcPX7lqP,I-eWcj8]/Zs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-! -s8W-!s8W-!s5e&P\I&Lh8cbQH+?V##!:\[_0K^8!:'ZNg3W\0@ah[C*kCE&*jhi8$%gRlB_o'=L -iSibSiSj59%hEo3_qlI(pUiZX%t!.SpV$$?Hf"M1n*bV[pUl(8a -29123 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -29543 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -32135 22554 350 571 @C -, -1+35<+>DQ<-j#6e^d"6%#DAKf%g!l0*X4N5#TOC8+E2sb#7):liPj5a?X^# -+,L4j)9UbH/5m#i+Duc8*ukqM?+kr22F$+`jZDNf13 -X)si,hQE&aS#"kEf1EPJe\,68krirZd^m/^XjV,n>GZc4?Z)32olY[gI5bB2lKe0.Id?70rolYY -rq,luf(IS.f@Bl&rjT*(^Ak>$YOD/]HL(J,J,?)Bh1#D&DdVi-s6K^@I=M3FJ,ej7^Uj:As4@;N -qVV>Ls8VEbfDk1:s8)cqs8?%!s8W,ls6K^=s8Vb!s8W%Is53/BYQ%terg3Zb>Q=$hotRVY>Q''N -[_MM6fD>@$lhb$rh1)99fCkR0Ds;aodbjA+lQCFLp$I+X[I7Gf])/\-g[CunYGL"PV;AguI=$,( -0(nTagA\]u?*q;sFLu769=J36]qsIV[[$KsBm.qk1qfa?9=C1(/%1dQTY721_HhnJm=`(\0#IrF -YgVklQXPO`@>)6$&6pnt"a1:em"W&a?'*eT$aL2;f*k!S_Vp4$>VNbtL'lmn_[D2,L'r_CfJ$Pc -#DCcr$l:ub(#87C;WhNl?_*Aa*,iU\s1\NJqnMn&^\@W?pYWaZs1[t#^[Jf3n)&?`h>Zm1qjB$i -foi:M\0;f@QpKI44qMoD4)+2:[\$=Zm5q5[l1FB;:MJ_?hX18'pAVMhpA=_kGCP&`2A37BL)F -<^?/N_M0NYL(lKR-oDD]@6oSU-nq=2K*;Gd$n7;e*X#0&_!nZT0Ktcg$70bFUolB[5Q~> -32555 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -32975 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -35584 22554 358 579 @C -, -19c&WTGN1b":bubiR*:n%8JchqRo!rV(g/qsOI@^UN[Dqfhc4pO1rRpYG66mea]lIG]7_n%7?JrO_Lp -]t\I^^?sg%\*_rDmJD)*]7/SNDV`'Fhm[NJ[r:$1]A'g^gYM8bp?h;ApNq)XGoS_$n%H1,If&j; -mI&kMrP%.Vn([70D>*FHqgH0V]=77Cg[5"-p@j"4ID,An\*q&cmGIs$%(g\]k,mJZL&hZ!8]msf\npV4Drqqp5E^\<6X -pAameO?=F_$BR@^\Ene2\Io8Z`(2>d-QoSI%RY*CS/$Xm_Uc,1>5J!Fs%i~> -35988 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -36408 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -39000 22554 351 571 @C -, -1)15>P-AMc-O+jY!-h)%J`3>7!-i*NblI7_SYZAFWB42a_+k7U&3(eO5iDqN%M3SF_@@,rK7k3e -iiLH9SL`Qh(*$PWDYi--U[Q&@oqe$BN"4kr4":R7TJ[dsrLHpF#5m%UJ#3[1I&4)(;#3[2b -+G1/U%F@DP6+B3>8206Mh'V>.h'V>.h'VAF5mHR48\.XE%FT-GDNfBm:"!pCFIT)`H;2h+=%VX8 -^ot%a-s''tZ^UBJ8]C*@YH^ZQn@$d2 -D0f7"osU1pD0f7"gInZQ]dmN.V8CtZfNsiI[aO,OKWVAn>?bMdCN,QCQIa\\g=lp>.q*:JXefhd -d^BdsQ*@^6W-u:2G-"3VZKGB6+2A37B -L;Ht.8fLC?#UjO?$mc@Z@>#=K_GJre(dULu-t3piC)Bds0Wd\L)MSq`$nD[L+K7[7PTZnQ8qM_p --q])aTdgB^TV3hBJqU,?%YLG5#>To)*=#=NF<(q6DWq?F+If/DF:`>A@F>V39KGJN$&;&15rVAj -5mHR45mA>4hOC1-U_n1eH>\=POD]Yg9KEKk$,&8`2bCY/kr1meVW]B!h#uO:qJLn':!cW!\dQu/ -'6m7"D[Y$9'7+s3o\BZH]I)hie_rGYn?5VR?CqSsoG-W[Y*8/?T3%qelLaqc-eCufldr$,ldL"L -ldL@8YPN26s0);!VZ6UEf73hPs8QHps8U\np&G$kqYp0fotThdfD_qf^U1b*V/QB+hpT9I]kq[n -D:`!+?eSQ[Q*n;BD5G[igIg1*D5D)"IaYZi[&pI;[["5%KWV(f.h+"C>7"DG>=ud>>=ud>>=ud> -=D:,2-"2HAL(^`-QI$5iqe-"Ic+6A@d>UaTaaPUd<=Ka]A< -6OFZ,_*\P<-t&^a6NmZ$$l$kbn7[FsGUS]ROT9W8AQU!p+CMN20FH8PE6<\~> -39420 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -39840 22554 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -0.83022 i -25095.5 21869.6 m -5.81 23.21 9.13 28.19 19.09 30.68 c -7.47 1.74 34.87 1.74 52.3 1.74 c -83.85 0 121.21 -3.32 121.21 -67.95 c -0 -12.43 -3.32 -44.75 -6.64 -66.29 c --0.83 -3.31 -2.49 -13.26 -2.49 -15.75 c -0 -4.97 2.49 -10.77 9.96 -10.77 c -9.13 0 10.79 6.64 12.45 19.1 c -22.41 144.46 p -0.83 3.32 1.66 11.62 1.66 14.11 c -0 9.13 -8.3 9.13 -22.41 9.13 c --460.77 0 p --19.92 0 -20.75 -0.83 -26.57 -16.61 c --49.81 -146.12 p --0.83 -1.66 -4.98 -13.29 -4.98 -14.95 c -0 -4.98 4.15 -9.13 9.96 -9.13 c -8.3 0 9.13 4.14 14.11 17.4 c -44.83 128.44 66.42 143.36 189.29 143.36 c -32.38 0 p -23.25 0 23.25 -3.4 23.25 -10.03 c -0 -4.98 -2.49 -14.93 -3.32 -17.41 c --111.25 -442.79 p --7.47 -30.68 -9.96 -39.8 -98.8 -39.8 c --29.89 0 -34.87 0 -34.87 -15.92 c -0 -10.05 9.13 -10.05 14.11 -10.05 c -22.42 0 45.66 1.66 68.08 1.66 c -23.25 0 47.32 0.83 70.57 0.83 c -23.25 0 46.49 -0.83 68.91 -0.83 c -24.08 0 48.98 -1.66 72.23 -1.66 c -8.3 0 18.27 0 18.27 16.75 c -0 9.21 -6.64 9.21 -28.23 9.21 c --20.76 0 -31.55 0 -53.14 1.66 c --24.07 2.48 -30.72 4.97 -30.72 18.24 c -0 0.83 0 4.97 3.32 17.41 c -h -f -25428 21158 198 831 @C -, -1.ir\dtH[q$*&\O[%V23V+MB&.TEBFV+Qsu8sLl*WD]QbPqPap.TEBFXX[;X9!kQqV$@b+ -<4,n/X/]."W`>mqda`&5<3*$!eS.qRX/]KkF]qtN02'`rX/aV?]di -]ldU^de^/Dl`0:&+rVtdSs8Ti`s7ZKmhs^Sp^[M3Ys8LpU -hgbOlqu=;.mJlSRn%\>cJ+nH[s6]i7qu,FNhsUO!pYWaArpTT2Du5%=pYW^RpO@,=Ic%m.mJYqs -pAO`6hfnem^Mhm$h=l?TGMW"?Ic%X%hfmoDqgRAqDYq0:Dg/\Xg\1*JGMVZ@p?ha#n%3eQ]A'g^ -gUDUc[sNJ2HZnfRG4"%t^"^#CG4"%nDYEJP[r1$6GIMJG]6 -0.581154 i -25965 21811.2 m -0 70.22 -56.21 124.8 -124.54 124.8 c --69.48 0 -124.49 -56.33 -124.49 -124.22 c -0 -70.22 56.16 -124.76 124.49 -124.76 c -69.48 0 124.54 56.29 124.54 124.18 c -h -25840.5 21715 m --54.63 0 -96.47 44.29 -96.47 96.14 c -0 54.2 43 96.78 96.47 96.78 c -54.63 0 96.49 -44.33 96.49 -96.19 c -0 -54.2 -43.02 -96.73 -96.49 -96.73 c -h -f -0.83022 i -26682 21942.7 m -0 2.49 -1.77 8.33 -9.23 8.33 c --2.49 0 -3.32 -0.84 -12.44 -9.93 c --58.06 -63.64 p --7.46 11.57 -45.62 73.58 -137.68 73.58 c --184.96 0 -371.58 -183.32 -371.58 -375.74 c -0 -136.85 97.87 -227.25 224.77 -227.25 c -72.16 0 135.2 33.16 179.15 71.31 c -77.14 68 91.25 143.45 91.25 145.94 c -0 8.31 -8.38 8.31 -10.04 8.31 c --4.97 0 -9.11 -1.7 -10.77 -8.32 c --7.46 -24.01 -26.51 -82.8 -83.67 -130.82 c --57.16 -46.37 -109.35 -60.44 -152.43 -60.44 c --74.55 0 -162.36 43.09 -162.36 172.35 c -0 47.23 17.4 181.46 100.29 278.41 c -50.55 58.83 128.46 100.3 202.23 100.3 c -84.54 0 133.43 -63.78 133.43 -159.82 c -0 -33.12 -2.48 -33.95 -2.48 -42.23 c -0 -8.28 9.12 -8.28 12.43 -8.28 c -10.77 0 10.77 1.66 14.92 16.58 c -h -f -26746 21158 195 831 @C -, -5#?m0LI(?\hXKq#hAs4OmFZULgUD(V]654,G4"%nDg(O&[r1p6DV_pJpMSgSgYKJbrO[7IDYEgB -h=kgDmr*CB,h7J-mmr-t:pM\fqmJD7En%4&+\*`V\^@/]%qnDIpIs9`CIJ\KN -^AYfbmeacTp[.j-g\\bAp[>k8qgWhe^YSnf^[;&>rSR)$IfFlornmUfqnM%_htQ%-hu@p#IfFoT -J,A^7mJlqLs53SJs829brP/C0p](3lrVliss*aqIs6nkFs8RTKn,NFVs8W-!s1eU&s8W-!s8W-! -s8W-!YQ(g+s8W-!YQ+U:s8Ke5s'PI!s8Pals8)`0s0)BNfDjH0s65%5eURGp^[(apJ*DI]0E/Y7 -f<C+qQ@MSl$l@k^K&mg]e?D^?WG^Sl`Wt0qQ?m! -ldGF=02(SsVn18sLl*]dd!\;Pd!N"\P%0<2icl8sKQPPq#m8[7^m.YdInA -11332 23221 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -10923 27018 336 553 @C -, -3"'+cs82irp]&"cp\2/cHf!TIIsZCQmsEg`^$fk5^V.89pV"39+C[oHATS!=ZFGF$[T?KJG08qJ -VV$6(mJD6Y[dEeFhehcS*SZ#9qepD^G2D+p\(c>e^>EeppA9&NmJH;EqepB>pA8cH\*iYWhS\aS -qfcteqsF+SGAZa@qgH37mHsWEID4TU]7%GB^>Ee9p<8hTG4"%gpMSg!G4"%gpMSg!G3h;6mI&fB -Fre2las6]ius!<5A(CcAFQtC;&pd"U?n=@E*kWYUFo_Ntol1FrHqL@c&p\*)75OVtX -5QC`Us8W-!s0([:s8QI+HIpbUs0#H"9Ds1?l"tb>>4f6>Wl"D:'k"[,a>0nZa=8juL/R;"D:eKH -s6V?6m[!lS?.IPV9&Jpfm]O1&Y3P7i[[6BX[Cod;IC-Rdqe9mtpafL(^`- -11335 27000 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -10907 30798 370 562 @C -, -1=TlWioDm@;oeqk\DVTI9(N/$?<86[%;7Fl@%i9h(fmbB[I&iZ7c1AgeFg[5$Mn%WeuIc%m< -^O?!.qtC!_mJHA,s)5CGhf&J.IJ\KjpAOaQqsW;6hm`'hDr6$$qgH6Qqg\#1n([To\*q(MhgXIs -rP*dXJ*lt0g[5$Mg])3LIc%m<^O?!.qtC!_mJHA,s)5CGhf&J-htqm"pAOaQqsW;6hm`'hDuIE- -qgH6Qqg\#,s4d;*\*q(MhgXIsrP*dXJ*lt0g[5$Mg])3$rnkHg^O?!.qtC!_mJHA,s)5CGhf&J- -htqi:pAOaQqsW;6hm`'hDuIE-qgH6Qqg\#,s4cr@\*q(MhgXIsrP*dXJ*lt0g[5$Mg])3$rnhYm -^O?!.qtC!_mJjZMs)5CGhf&J-htqi:pAOaQqsW;6hm`'hDuG^qp\2G9rU94DIs_(MhZ!9D^V0U7 -msfh6qqp90p\2G9rU93)Hi*]thYu'om9\9rTnN~> -11335 30780 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -10915 34559 350 571 @C -, -1+35<+>DQ<-j#6e^d"6%#DAKf%g!l0*X4N5#TOC8+E2sb#7):liPj5a?X^# -+,L4j)9UbH/5m#i+Duc8*ukqM?+kr22F$+`jZDNf13 -X)si,hQE&aS#"kEf1EPJe\,68krirZd^m/^XjV,n>GZc4?Z)32olY[gI5bB2lKe0.Id?70rolYY -rq,luf(IS.f@Bl&rjT*(^Ak>$YOD/]HL(J,J,?)Bh1#D&DdVi-s6K^@I=M3FJ,ej7^Uj:As4@;N -qVV>Ls8VEbfDk1:s8)cqs8?%!s8W,ls6K^=s8Vb!s8W%Is53/BYQ%terg3Zb>Q=$hotRVY>Q''N -[_MM6fD>@$lhb$rh1)99fCkR0Ds;aodbjA+lQCFLp$I+X[I7Gf])/\-g[CunYGL"PV;AguI=$,( -0(nTagA\]u?*q;sFLu769=J36]qsIV[[$KsBm.qk1qfa?9=C1(/%1dQTY721_HhnJm=`(\0#IrF -YgVklQXPO`@>)6$&6pnt"a1:em"W&a?'*eT$aL2;f*k!S_Vp4$>VNbtL'lmn_[D2,L'r_CfJ$Pc -#DCcr$l:ub(#87C;WhNl?_*Aa*,iU\s1\NJqnMn&^\@W?pYWaZs1[t#^[Jf3n)&?`h>Zm1qjB$i -foi:M\0;f@QpKI44qMoD4)+2:[\$=Zm5q5[l1FB;:MJ_?hX18'pAVMhpA=_kGCP&`2A37BL)F -<^?/N_M0NYL(lKR-oDD]@6oSU-nq=2K*;Gd$n7;e*X#0&_!nZT0Ktcg$70bFUolB[5Q~> -11335 34559 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -10915 38339 351 571 @C -, -1)15>P-AMc-O+jY!-h)%J`3>7!-i*NblI7_SYZAFWB42a_+k7U&3(eO5iDqN%M3SF_@@,rK7k3e -iiLH9SL`Qh(*$PWDYi--U[Q&@oqe$BN"4kr4":R7TJ[dsrLHpF#5m%UJ#3[1I&4)(;#3[2b -+G1/U%F@DP6+B3>8206Mh'V>.h'V>.h'VAF5mHR48\.XE%FT-GDNfBm:"!pCFIT)`H;2h+=%VX8 -^ot%a-s''tZ^UBJ8]C*@YH^ZQn@$d2 -D0f7"osU1pD0f7"gInZQ]dmN.V8CtZfNsiI[aO,OKWVAn>?bMdCN,QCQIa\\g=lp>.q*:JXefhd -d^BdsQ*@^6W-u:2G-"3VZKGB6+2A37B -L;Ht.8fLC?#UjO?$mc@Z@>#=K_GJre(dULu-t3piC)Bds0Wd\L)MSq`$nD[L+K7[7PTZnQ8qM_p --q])aTdgB^TV3hBJqU,?%YLG5#>To)*=#=NF<(q6DWq?F+If/DF:`>A@F>V39KGJN$&;&15rVAj -5mHR45mA>4hOC1-U_n1eH>\=POD]Yg9KEKk$,&8`2bCY/kr1meVW]B!h#uO:qJLn':!cW!\dQu/ -'6m7"D[Y$9'7+s3o\BZH]I)hie_rGYn?5VR?CqSsoG-W[Y*8/?T3%qelLaqc-eCufldr$,ldL"L -ldL@8YPN26s0);!VZ6UEf73hPs8QHps8U\np&G$kqYp0fotThdfD_qf^U1b*V/QB+hpT9I]kq[n -D:`!+?eSQ[Q*n;BD5G[igIg1*D5D)"IaYZi[&pI;[["5%KWV(f.h+"C>7"DG>=ud>>=ud>>=ud> -=D:,2-"2HAL(^`-QI$5iqe-"Ic+6A@d>UaTaaPUd<=Ka]A< -6OFZ,_*\P<-t&^a6NmZ$$l$kbn7[FsGUS]ROT9W8AQU!p+CMN20FH8PE6<\~> -11335 38339 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -10538 42137 279 553 @C -, --Q-19EX`E,O8o7[s8VY%)$-]u5\(0_(^GZ2m6D^"o<9?"25%cQ"Ulef6WU1/^qN)+=i_Xq -*.d)VEX.1*E./>6pcf#Bpcf;,n9uI?s5^t+#'h[,Js@H@)5UjS!dQO'_+Jn/GUh#:%gL/0!:\L/ -l3Dr@*Y/L._;OhPG^+Hq4?P_hiSiba%sWH=hB)EH^$cJYg:cl[eV.Nde:7~> -10918 42119 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -11338 42119 353 571 @C -, -1)::tOapE!!sOOH@!sdQ%hCF_*Z?/W*Z8u<&3(eO5i@40#7*:SiGU,FV:E.Od_>@CXm*E&>LR`Z2sum\o_#mV?B]l8ICf8m^94YDe(43B^XBjq -lg!d7l$_lDl[A`[s'+s[Qhn2iFhdgSs&WS,lMoDds15uF9)np6J,Zp\s/l%ql[SoKFo243^]4;t -J,b#Ls8W,TJ,`mQFoVLAotUP,s8Q=&TE"rkqVV>Qs8W,bqu?]rs8W,rrg3*Rs8W-!s8W-!s8W,b -s53kVs8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s8W-!s'NP@s8W-!s8W-!s8W-!omd#As8W-!s8?=) -qZ$Tps8'M1s8W,t?iU0+qYgHos65"4p]&k6s8VbAs8T72p]&eDs8K5%r@@dFlMpnLs'P2ts4./G -]'o["s7LO6lMp<[lYaK&s''XGhR9nsron@7IbJ0-lYChF[(gq@hS3n\hR9nr[d3Y8]kq[nD:_D; -FLU'VFk5AQ9D6fF>H;O]Xk(q3gT,$o/Z!;7F_u#][&p0A.qr3[g/cY1Q*dZi@>(bUXefkb$mc@Z -@>#B&X>#km_H+M\_[/[=+KQDP(h!SU_'i`D$HNm[-t(/!/41O\2A@nOL?ZR0+]qRXK*iGl_M/C+ --t&^a6%YM4*X3b'9S`efbc1d@O>&&M(_+$;\/0Y~> -9590.99 31564.5 0 141.14 P -0 117.89 -86.4 233.29 -179.46 233.29 c --63.97 0 -125.52 -54.79 -125.52 -163.55 c -0 -268.99 p -0 -15.77 0 -24.91 15.92 -24.91 c -10.05 0 10.05 7.47 10.05 24.07 c -0 10.79 0.94 25.74 1.77 35.7 c -1.66 13.29 4.14 18.27 13.27 18.27 c -3.32 0 5.8 -0.83 15.75 -3.32 c -445.28 -111.25 p -32.34 -8.3 38.97 -9.96 38.97 -75.55 c -0 -14.11 0 -23.25 15.92 -23.25 c -10.05 0 10.05 9.96 10.05 12.45 c -0 23.25 -2.49 82.2 -2.49 105.44 c -0 17.43 0.83 35.7 0.83 53.13 c -0 18.27 1.66 36.53 1.66 53.96 c -0 5.81 0 16.6 -16.75 16.6 c --9.21 0 -9.21 -7.47 -9.21 -23.25 c -0 -30.71 0 -53.96 -14.91 -53.96 c --4.97 0 -9.11 1.66 -14.08 2.49 c -h -9343.53 31624.4 m --29.02 7.48 -31.54 9.14 -31.54 44.88 c -0 79.78 p -0 68.98 22.43 113.04 79.63 113.04 c -32.33 0 103.63 -16.63 133.47 -49.05 c -37.3 -41.55 43.94 -91.41 43.94 -127.98 c -0 -117.17 h -f -9229 32045 832 193 @C -, --MRfYCo.GlCmte5i(k]<_]TRdWsjMmPA1:e/'5$SO::^9-o+-4F\p0^'&40V6WREV#"ULBEBqVI -\S_TZB._YcfOA'O"gBX3l34D-QK/,"D+f/+fI9I?"gD-qk+HA0l!S*9='5VZ,$.bH0ZntP.tReh -2F.Fs#6[<9$J,n@NIdu1%L=$c3$<0k_$:)Q#TOt+6,mFDAm5k%Z0*&p=s#")mU#_Cp6Cb`dLG\roe%M8fZ[+J>=+F%Z@ -(5E0HiM^Rm*$PWBOg82E5k&MIYilWPi=J+-%F_gPJVQW(!p8NO_?:=b":RA%DiB)kTJ[a)k_h*&7NE]2b5kn2aIZ3+N&kbF;&a-DNJafDLr9V82ign -5mHR45mHQXkU,NukU,NukU%;mh#uO\kTJ\.km$>ukr1m4kr22F#X'O!2b5kn2bCSTcp>6;dVhk. -H3cH$\kUUjPB]ko2bCSTl+0Y^\kVG#$0aCGkrgBcoUDZ5T1u-Ro^j2qVR6&cZl4Q&0Mh~> -9350.24 33051.8 m --29.85 7.47 -38.25 9.13 -38.25 71.4 c -0 19.09 0 26.57 -16.75 26.57 c --9.22 0 -9.22 -8.3 -9.22 -22.42 c -0 -109.59 p -0 -21.58 0 -22.41 15.84 -32.37 c -473.11 -303.03 -469.79 -64.76 p --19.16 -2.49 -19.16 -4.15 -19.16 -25.74 c -0 -113.74 p -0 -15.77 0 -24.91 15.92 -24.91 c -10.05 0 10.05 7.47 10.05 24.07 c -0 10.79 0.94 25.74 1.77 35.7 c -1.66 13.28 4.14 18.26 13.27 18.26 c -3.32 0 5.8 -0.83 15.75 -3.32 c -422.06 -105.44 p -33.17 -8.3 59.7 -22.41 62.19 -89.66 c -0 -4.15 0.84 -14.94 15.92 -14.94 c -7.54 0 10.05 4.98 10.05 11.62 c -0 26.57 -2.49 55.63 -2.49 83.02 c -0 28.23 2.49 58.11 2.49 85.51 c -0 4.15 0 14.95 -16.75 14.95 c --9.21 0 -9.21 -9.13 -9.21 -14.95 c --0.83 -47.32 -17.43 -56.46 -36.51 -56.46 c --5.81 0 -9.96 0.83 -19.09 3.32 c --452.26 112.91 0 0.83 514.73 71.4 p -9.96 1.66 19.09 2.49 19.09 12.45 c -0 9.13 -9.14 14.11 -14.96 18.27 c --526.06 335.41 0 0.83 476.01 -118.72 p -32.39 -8.3 39.04 -9.96 39.04 -75.55 c -0 -14.11 0 -23.25 15.92 -23.25 c -10.05 0 10.05 9.96 10.05 12.45 c -0 23.25 -2.49 79.7 -2.49 102.95 c -0 34.04 2.49 69.74 2.49 103.78 c -0 4.98 0 15.77 -16.75 15.77 c --9.21 0 -9.21 -7.47 -9.21 -23.25 c -0 -30.72 0 -53.96 -14.93 -53.96 c --3.32 0 -4.98 0 -19.9 4.15 c -h -f -9590.99 33434.6 0 141.14 P -0 117.89 -86.4 233.29 -179.46 233.29 c --63.97 0 -125.52 -54.79 -125.52 -163.55 c -0 -268.99 p -0 -15.77 0 -24.91 15.92 -24.91 c -10.05 0 10.05 7.47 10.05 24.07 c -0 10.79 0.94 25.74 1.77 35.7 c -1.66 13.29 4.14 18.27 13.27 18.27 c -3.32 0 5.8 -0.83 15.75 -3.32 c -445.28 -111.25 p -32.34 -8.3 38.97 -9.96 38.97 -75.55 c -0 -14.11 0 -23.25 15.92 -23.25 c -10.05 0 10.05 9.96 10.05 12.45 c -0 23.25 -2.49 82.2 -2.49 105.44 c -0 17.43 0.83 35.7 0.83 53.13 c -0 18.27 1.66 36.53 1.66 53.96 c -0 5.81 0 16.6 -16.75 16.6 c --9.21 0 -9.21 -7.47 -9.21 -23.25 c -0 -30.71 0 -53.96 -14.91 -53.96 c --4.97 0 -9.11 1.66 -14.08 2.49 c -h -9343.53 33494.4 m --29.02 7.48 -31.54 9.14 -31.54 44.88 c -0 79.78 p -0 68.98 22.43 113.04 79.63 113.04 c -32.33 0 103.63 -16.63 133.47 -49.05 c -37.3 -41.55 43.94 -91.41 43.94 -127.98 c -0 -117.17 h -f -9539.16 34142.6 m --30.73 -14.95 -53.17 -39.03 -53.17 -76.39 c -0 -97.16 122.07 -200.13 243.21 -200.13 c -78 0 132.77 45.67 132.77 110.45 c -0 16.61 -3.31 58.13 -62.11 107.95 c -34.79 6.64 62.11 35.71 62.11 75.57 c -0 29.06 -19.07 48.16 -45.6 61.45 c --29.86 14.12 -80.44 24.96 -82.09 24.96 c --8.3 0 -8.3 -7.52 -8.3 -10.02 c -0 -8.3 3.34 -9.13 14.97 -11.62 c -54.02 -14.12 103.05 -29.07 103.05 -63.11 c -0 -22.42 -21.59 -24.91 -38.2 -24.91 c --18.28 0 -24.92 1.66 -61.47 10.8 c --34.89 9.13 -43.19 10.8 -74.76 18.27 c --116.29 29.89 p --23.26 5.81 -24.92 5.81 -28.24 5.81 c --14.12 0 -22.43 -9.96 -22.43 -24.08 c -0 -19.93 18.27 -32.39 36.55 -34.88 c -h -9754.23 34088.7 m -14.96 -4.15 16.63 -4.15 30.75 -16.61 c -45.72 -36.56 59.02 -70.62 59.02 -93.88 c -0 -41.54 -45.71 -53.17 -78.13 -53.17 c --41.56 0 -143.8 26.59 -182.04 45.69 c --49.04 25.76 -79.85 63.14 -79.85 96.38 c -0 54 68.21 65.64 73.2 65.64 c -4.99 0 9.98 -1.67 14.13 -2.5 c -h -f -9229 34320 832 193 @C -, -1Q+)p_$1Hm(dZVSC$db.f%/F&['mEk[[,Lu>?bfG95AApBj1m\D5C[;D(B=;D1o$fD(?&GBl#B. -XefI&D(B$FBj43I$!U$UfO6A.D(?#@D(B=&@4Q%o@4Q%r8f1teBindID(?#@D(?#@@>(bZ@>(bZ -$o!s$&6@5`_H%LGg/]%_(h"p'+K_JKKWHkl0Z"17$!:Ud$mc<9K!<*m(ePWR.h!Q%0U7A*L)<3Y -@45Q;X;F6*@$E@^3$."pc^`#t#h[AXf_-(be6Y"2Ueh$5ir^XPUgK%%A+8iM?s6iM^Q`'p(O8c%-Qa -#D6`pYY`C^)eQlO6QgV()[Hcg"2U)P!EfN?OP"NGO?r!F;MJCTg]:rm/KXG4TE+clpAQr(k7gFEUK -%$5D6:n`[n@6oSU'GsZ_6OF590VH6JKa\Y4-nnJ+6%Xgu$ksO->\ji`(aq-D?q@f;0N,D')2>dA -"#+Ip/?YuW?t'OqQk6jF>pp,u9'1Y:bBO2p/!g/B@&5I%$XpFpl34D-QJqp]$d3[#[MY=g@((+2 -:bk;=>EoC`/'Dm.aUMBj8/WOV>GVNp=Z16?#&,ugA-0E5-ObN=P@T6+*_h@(n2QVC5WghJhnYB~> -0.996264 i -25808 28798.1 m -0 17.91 0 22.92 -10.07 22.92 c --5.98 0 -6.98 -1.99 -12.96 -11.91 c --48.83 -78.37 p --44.84 55.55 -112.61 90.29 -184.35 90.29 c --182.36 0 -339.81 -157.37 -339.81 -360.52 c -0 -206.12 159.44 -361.46 339.81 -361.46 c -163.43 0 256.21 142.46 256.21 252.05 c -0 10.96 0 17 -10.97 17 c --9.96 0 -10.96 -4.99 -10.96 -11.96 c --8.96 -158.37 -124.5 -228.09 -221.12 -228.09 c --69.72 0 -259.96 41.8 -259.96 332.43 c -0 287.64 187.25 331.55 258.96 331.55 c -104.59 0 196.22 -88.5 216.14 -237.65 c -1.99 -11.93 1.99 -14.91 13.94 -14.91 c -13.96 0 13.96 2.98 13.96 22.88 c -h -f -19371 35550 m -0 22.93 0 24 -19.94 24 c --23.92 -27 -73.75 -64.01 -176.38 -64.01 c -0 -29 p -22.87 0 72.6 0 127.32 25.77 c -0 -518.95 p -0 -35.86 -2.99 -47.81 -90.51 -47.81 c --30.83 0 0 -29 p -26.9 1.99 123.54 1.99 156.41 1.99 c -32.88 0 128.52 0 155.42 -1.99 c -0 29 -30.89 0 p --87.6 0 -90.59 11.96 -90.59 47.84 c -h -f -31537 29409.4 -21.97 0 P --3 -16.89 -10.98 -71.53 -20.96 -87.42 c --6.99 -8.94 -63.89 -8.94 -93.83 -8.94 c --184.67 0 p -26.95 22.92 87.84 86.7 113.8 110.61 c -151.73 139.51 207.64 191.33 207.64 289.98 c -0 114.6 -90.82 191.41 -206.5 191.41 c --115.68 0 -183.49 -99.02 -183.49 -185.04 c -0 -51.01 44.01 -51.01 47.01 -51.01 c -21 0 47.01 15 47.01 47.01 c -0 28 -19.11 47.01 -47.25 47.01 c --9.05 0 -11.06 0 -14.07 0 c -18.94 67.41 72.8 113.02 137.61 113.02 c -84.77 0 136.67 -70.85 136.67 -162.55 c -0 -84.72 -48.88 -158.48 -105.68 -222.27 c --201.29 -225.27 0 -23.92 363.99 0 h -f -27849.1 37159 m --16.89 -1 -20.86 -2 -20.86 -11 c -0 -10 4.96 -10 22.85 -10 c -45.7 0 p -84.46 0 122.22 -69.82 122.22 -165.51 c -0 -130.57 -67.61 -165.46 -116.32 -165.46 c --47.71 0 -129.23 22.72 -158.06 87.93 c -31.81 -4.29 60.64 13.59 60.64 49.34 c -0 28.8 -20.87 48.68 -48.71 48.68 c --23.86 0 -49.7 -13.95 -49.7 -51.76 c -0 -88.56 88.58 -161.2 199.06 -161.2 c -118.44 0 206.11 90.73 206.11 191.42 c -0 91.72 -73.68 163.5 -169.16 180.45 c -86.53 24.93 142.22 97.71 142.22 175.47 c -0 78.76 -81.55 136.65 -178.03 136.65 c --99.46 0 -173.05 -61 -173.05 -133.99 c -0 -40 31 -48 46.01 -48 c -21 0 45 15 45 45.01 c -0 32 -24.17 46.01 -46.32 46.01 c --6.04 0 -8.05 0 -11.07 0 c -37.75 66.96 131.16 66.96 136.12 66.96 c -32.79 0 97.37 -15 97.37 -112.64 c -0 -18.93 -2.98 -74.72 -31.79 -117.56 c --29.81 -43.83 -63.59 -46.82 -90.41 -47.82 c -h -f -20474 27622 m -0 18.92 0 24.01 -14.03 24.01 c --7.98 0 -10.97 0 -18.95 -12.06 c --300.09 -464.91 0 -29 262.02 0 0 -90.35 p -0 -36.72 -1.99 -46.65 -74.72 -46.65 c --19.93 0 0 -29 p -22.91 1.99 101.62 1.99 129.52 1.99 c -27.89 0 107.6 0 130.51 -1.99 c -0 29 -19.86 0 p --71.48 0 -74.46 9.93 -74.46 46.65 c -0 90.35 100.3 0 0 29 ^ h -20408 27544.9 0 -375.85 -242.16 0 H -f -1 i -23375.9 43612.8 m -0 44.22 3.57 56.19 92.86 56.19 c -26.21 0 0 34.98 p --28.7 -2.39 -131.51 -2.39 -167.37 -2.39 c --35.86 0 -139.87 0 -168.57 2.39 c -0 -34.98 26.38 0 p -89.93 0 93.55 -11.97 93.55 -56.19 c -0 -634.66 p -0 -44.22 -3.62 -56.18 -93.55 -56.18 c --26.37 0 0 -34.98 p -28.69 2.39 131.51 2.39 167.37 2.39 c -35.87 0 139.88 0 168.57 -2.39 c -0 34.98 -26.21 0 p --89.29 0 -92.86 11.95 -92.86 56.18 c -h -f -23998.7 43719.4 m --4.78 14.34 -7.17 21.58 -23.91 21.58 c --16.73 0 -19.12 -3.65 -25.11 -22.78 c --249.86 -714.92 p --16.74 -49.02 -50.21 -80.1 -126.72 -81.3 c -0 -34.98 p -72.93 2.39 75.32 2.39 114.77 2.39 c -33.47 0 90.86 0 121.94 -2.39 c -0 34.98 p --50.21 1.19 -80.1 26.2 -80.1 59.56 c -0 7.14 0 9.53 5.98 25.01 c -54.99 158.48 301.27 0 65.75 -187.07 p -5.98 -14.29 5.98 -16.67 5.98 -20.25 c -0 -35.73 -60.97 -35.73 -90.86 -35.73 c -0 -34.98 p -27.5 2.39 121.94 2.39 155.42 2.39 c -33.47 0 119.55 0 147.05 -2.39 c -0 34.98 p --77.71 0 -99.23 0 -115.96 49.02 c -h -23941.3 43598.3 138.68 -398.33 -277.36 0 H -f -24676.5 43267 201.89 0 P -139.77 0 267.62 94.56 267.62 215.45 c -0 116.11 -115.9 221.5 -277.18 221.5 c --402.59 0 0 -34.98 23.96 0 p -86.25 0 89.84 -11.97 89.84 -56.19 c -0 -634.66 p -0 -44.22 -3.6 -56.18 -89.84 -56.18 c --23.96 0 0 -34.98 p -28.67 2.39 126.63 2.39 161.27 2.39 c -35.84 0 133.8 0 162.48 -2.39 c -0 34.98 -23.9 0 p --86.01 0 -89.59 11.94 -89.59 56.11 c -h -24675 43296 0 325.1 P -0 39.44 2.38 47.89 54.84 47.89 c -112.07 0 p -193.18 0 193.18 -135.14 193.18 -186.54 c -0 -50.2 0 -186.46 -194.37 -186.46 c -h -f -26283.5 43586.4 m -19.13 57.39 53.81 81.3 113.75 82.55 c -0 34.98 p --51.57 -2.39 -53.96 -2.39 -106.57 -2.39 c --31.09 0 -95.66 0 -124.37 2.39 c -0 -34.98 p -72.95 -1.34 94.47 -39.59 94.47 -63.5 c -0 -5.98 -3.59 -15.54 -5.98 -23.91 c --181.77 -566.68 -193.72 601.34 p --1.2 3.59 -3.59 13.15 -3.59 16.74 c -0 36 63.38 36 92.08 36 c -0 34.98 p --28.7 -2.39 -117.19 -2.39 -151.87 -2.39 c --34.68 0 -107.62 0 -138.71 2.39 c -0 -34.98 p -63.38 0 80.12 -3.73 93.27 -25.25 c -8.37 -15.54 16.74 -52.6 32.29 -95.64 c --171 -533.2 -191.33 595.37 p --5.98 16.74 -5.98 19.13 -5.98 22.71 c -0 36 60.98 36 92.08 36 c -0 34.98 p --27.51 -2.39 -118.39 -2.39 -151.87 -2.39 c --34.68 0 -107.62 0 -138.71 2.39 c -0 -34.98 p -64.57 0 87.29 0 101.64 -45.49 c -236.77 -738.83 p -5.98 -17.93 10.77 -22.71 21.52 -22.71 c -9.57 0 15.55 3.58 21.53 21.51 c -198.5 620.41 199.7 -620.41 p -5.98 -17.93 11.96 -21.51 21.52 -21.51 c -10.77 0 15.55 4.78 21.53 22.71 c -h -f -26664.5 43385.9 m --60.87 15.55 -114.57 75.36 -114.57 151.91 c -0 83.73 66.93 159.27 160.16 159.27 c -196.02 0 222.32 -194.18 229.48 -245.71 c -2.39 -14.38 2.39 -19.18 14.34 -19.18 c -13.18 0 13.18 5.98 13.18 27.53 c -0 241.79 p -0 21.54 0 27.54 -11.96 27.54 c --3.58 0 -8.36 0 -16.72 -15.62 c --39.41 -73.23 p --57.33 74.43 -136.15 88.84 -188.7 88.84 c --132.58 0 -229.32 -106.57 -229.32 -228.48 c -0 -57.37 20.29 -109.96 64.45 -157.77 c -41.77 -46.61 83.54 -57.37 168.28 -78.88 c -41.77 -9.56 107.42 -26.29 124.13 -33.46 c -56.09 -27.49 93.11 -92.03 93.11 -158.97 c -0 -89.64 -63.27 -174.5 -162.33 -174.5 c --53.7 0 -127.7 13.13 -186.18 64.46 c --69.22 62.08 -74 148.03 -75.19 187.42 c --1.2 9.55 -10.74 9.55 -13.12 9.55 c --13.13 0 -13.13 -6.05 -13.13 -27.56 c -0 -241.37 p -0 -21.51 0 -27.48 12.09 -27.48 c -7.26 0 8.47 2.39 16.93 16.72 c -4.84 9.55 30.24 53.75 39.93 71.68 c -42.85 -47.79 117.83 -88.39 220.2 -88.39 c -133.31 0 229.76 113.63 229.76 245.2 c -0 72.96 -27.46 126.79 -59.69 166.26 c --44.15 53.82 -97.86 66.98 -144.41 78.94 c -h -f -27079.2 43113 310.805 64.9609 re -f -27894 43234.8 m -0 -282.28 -125.71 -340.89 -197.53 -340.89 c --28.73 0 -92.17 3.63 -122.09 46.03 c -7.18 0 p -8.38 -2.77 51.47 4.43 51.47 48.78 c -0 26.37 -17.95 49.16 -49.07 49.16 c --31.12 0 -50.27 -20.38 -50.27 -51.49 c -0 -74.2 59.82 -124.46 163.91 -124.46 c -149.55 0 294.35 158.96 294.35 418.33 c -0 321.52 -134.08 401.72 -239.34 401.72 c --131.57 0 -247.59 -110.05 -247.59 -267.78 c -0 -157.73 111.29 -265.27 230.96 -265.27 c -88.55 0 134.03 64.48 158.03 125.38 c -h -27740.5 43172.9 m --75.34 0 -107.62 60.9 -118.39 83.58 c --19.13 45.38 -19.13 102.69 -19.13 156.42 c -0 66.87 0 124.18 31.09 173.14 c -21.52 32.24 53.81 66.95 118.38 66.95 c -68.16 0 102.84 -59.78 114.8 -87.24 c -23.92 -58.51 23.92 -160 23.92 -177.92 c -0 -100.3 -45.45 -214.93 -150.68 -214.93 c -h -f -28613.9 43629.3 0 27.61 -288.12 0 P --144.66 0 -147.05 15.48 -151.83 38.11 c --26.3 0 -37.06 -238.95 26.3 0 p -3.59 21.5 14.35 95.57 29.89 108.71 c -9.57 7.19 99.23 7.19 115.96 7.19 c -253.45 0 -126.44 -181.71 p --32.2 -46.62 -152.65 -242.63 -152.65 -467.34 c -0 -13.14 0 -60.95 48.87 -60.95 c -50.07 0 50.07 46.61 50.07 62.15 c -0 59.76 p -0 178.09 28.65 316.74 84.76 396.81 c -h -f -cleartomark end end pagesave restore - showpage -%%PageTrailer -%%Trailer -cleartomark -countdictstack -exch sub { end } repeat -restore -%%Pages: 1 -%%EOF diff --git a/doc/source/commandref.rst b/doc/source/commandref.rst new file mode 100644 index 00000000..efe1276c --- /dev/null +++ b/doc/source/commandref.rst @@ -0,0 +1,66 @@ +:tocdepth: 3 + +Command reference +================= + +The following :ref:`table ` has links to tables of the +properties and methods for all the main PyTOUGH Python classes. + +.. container:: + :name: tb:command_ref + + .. table:: Properties and methods of main PyTOUGH Python classes + + +-----------------------+-----------------------------------------------------------------------+ + | Class | | + +=======================+===================================+===================================+ + | ``column`` |:ref:`properties |:ref:`methods ` | + | |` | | + +-----------------------+-----------------------------------+-----------------------------------+ + | ``layer`` |:ref:`properties |:ref:`methods ` | + | |` | | + +-----------------------+-----------------------------------+-----------------------------------+ + | ``mulgrid`` |:ref:`properties |:ref:`methods` | + | |` | | + +-----------------------+-----------------------------------+-----------------------------------+ + | ``rocktype`` |:ref:`properties |-- | + | |` | | + +-----------------------+-----------------------------------+-----------------------------------+ + | ``t2block`` |:ref:`properties |-- | + | |` | | + +-----------------------+-----------------------------------+-----------------------------------+ + | ``t2blockincon`` |:ref:`properties |-- | + | |` | | + +-----------------------+-----------------------------------+-----------------------------------+ + | ``t2connection`` |:ref:`properties |-- | + | |` | | + +-----------------------+-----------------------------------+-----------------------------------+ + | ``t2data`` |:ref:`properties |:ref:`methods ` | + | |` | | + +-----------------------+-----------------------------------+-----------------------------------+ + | ``t2generator`` |:ref:`properties |-- | + | |` | | + +-----------------------+-----------------------------------+-----------------------------------+ + | ``t2grid`` |:ref:`properties |:ref:`methods ` | + | |` | | + +-----------------------+-----------------------------------+-----------------------------------+ + | ``t2historyfile`` |:ref:`properties | -- | + | |` | | + +-----------------------+-----------------------------------+-----------------------------------+ + | ``t2incon`` |:ref:`properties |:ref:`methods `| + | |` | | + +-----------------------+-----------------------------------+-----------------------------------+ + | ``t2listing`` |:ref:`properties |:ref:`methods | + | |` |` | + +-----------------------+-----------------------------------+-----------------------------------+ + | ``toughreact_tecplot``|:ref:`properties |:ref:`methods | + | |`|` | + +-----------------------+-----------------------------------+-----------------------------------+ + | ``well`` |:ref:`properties |:ref:`methods ` | + | |` | | + +-----------------------+-----------------------------------+-----------------------------------+ + +Other functions: + +* :ref:`IFC-67 thermodynamics ` +* :ref:`IAPWS-97 thermodynamics ` diff --git a/doc/source/conf.py b/doc/source/conf.py new file mode 100644 index 00000000..a015da74 --- /dev/null +++ b/doc/source/conf.py @@ -0,0 +1,164 @@ +# -*- coding: utf-8 -*- +# +# PyTOUGH documentation build configuration file, created by +# sphinx-quickstart on Wed Feb 17 15:47:30 2021. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +sys.path.insert(0, os.path.abspath('../../')) +sys.path.insert(0, os.path.abspath('../PyTOUGH')) + + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# 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.autodoc'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'PyTOUGH' +copyright = u'2011, Adrian Croucher' +author = u'Adrian Croucher' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'1.6' +# The full version, including alpha/beta/rc tags. +release = u'1.6.6' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = 'en' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = [] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +# html_theme = 'classic' +html_theme = 'furo' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# This is required for the alabaster theme +# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars +html_sidebars = {} + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'PyTOUGHdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'PyTOUGH.tex', u'PyTOUGH Documentation', + u'Adrian Croucher', 'manual'), +] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'PyTOUGH', u'PyTOUGH Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'PyTOUGH', u'PyTOUGH Documentation', + author, 'PyTOUGH', 'One line description of project.', + 'Miscellaneous'), +] + + + diff --git a/doc/source/coverpic.png b/doc/source/coverpic.png new file mode 100755 index 00000000..4d9ba561 Binary files /dev/null and b/doc/source/coverpic.png differ diff --git a/doc/source/iapws97.rst b/doc/source/iapws97.rst new file mode 100644 index 00000000..ad4af020 --- /dev/null +++ b/doc/source/iapws97.rst @@ -0,0 +1,365 @@ +:tocdepth: 3 + +.. _iapws97: + +IAPWS-97 thermodynamics +======================= + +.. index:: thermodynamics; IAPWS-97 + +.. _introduction-7: + +Introduction +------------ + +The ``IAPWS97`` library in PyTOUGH contains a Python implementation of +the main functions of the International Association for the Properties +of Water and Steam (`IAPWS `_) 1997 +thermodynamic formulation. These can be used to calculate the +thermodynamic properties of water, steam and supercritical water. The +IAPWS-97 supersedes the :ref:`IFC-67 ` formulation used in +TOUGH2, being generally faster and more accurate, as well as having a +simpler representation of the thermodynamic region around the critical +point. + +The operating range of the IAPWS-97 formulation is shown in the +pressure-temperature plot below. It covers temperatures up to 800°C +and pressures up to 100 MPa, and is divided into four thermodynamic +regions: + +#. liquid water + +#. dry steam + +#. supercritical fluid + +#. two-phase + +The two-phase region (4) follows the saturation line on the +pressure-temperature plot (the boundary between liquid water and dry +steam), up to the critical point :math:`C` (:math:`T` = 373.946 °C, +:math:`P` = 22.064 MPa), where the distinction between liquid water +and steam disappears. Region 3 covers supercritcal fluid (above the +critical point) and also near-critical fluid, just below the critical +point. The boundary between regions 1 and 3 (liquid water and +supercritical) is aribitrarily set at :math:`T` = 350 °C. The boundary +between regions 2 and 3 (dry steam and supercritical) is described by +the ``b23p`` and ``b23t`` :ref:`functions `. + +.. image:: iapws_regions.* + :alt: IAPWS-97 thermodynamics operating range + :width: 500 + :name: fg:iapws97_range + +The ``IAPWS97`` library can be imported using the command: + +:: + + from IAPWS97 import * + +The functions available through the ``IAPWS97`` library are listed in +the :ref:`table ` below. + +.. container:: + :name: tb:iapws97_functions + + .. table:: ``IAPWS97`` functions + + +----------------------------------------------------------------------------+----------+----------------------------+ + | **Function** | **Type** | **Description** | + +============================================================================+==========+============================+ + | :ref:`b23p ` | float | pressure on boundary | + | | | between steam and | + | | | supercritical regions, as | + | | | a function of temperature | + +----------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`b23t ` | float | temperature on boundary | + | | | between steam and | + | | | supercritical regions, as | + | | | a function of pressure | + +----------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`cowat ` | tuple | density and internal | + | | | energy of liquid water | + | | | | + +----------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`density_temperature_plot ` | – | draws region boundaries on | + | | | a density-temperature plot | + | | | | + | | | | + +----------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`pressure_temperature_plot` | – | draws region boundaries on | + | | | a pressure-temperature | + | | | plot | + | | | | + +----------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`region` | integer | thermodynamic region | + | | | | + | | | | + +----------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`sat` | float | saturation pressure as a | + | | | function of temperature | + | | | | + +----------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`super` | tuple | pressure and internal | + | | | energy of supercritical | + | | | fluid | + +----------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`supst` | tuple | density and internal | + | | | energy of dry steam | + | | | | + +----------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`tsat` | float | saturation temperature as | + | | | a function of pressure | + | | | | + +----------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`visc` | float | dynamic viscosity of | + | | | water, steam or | + | | | supercritical fluid | + +----------------------------------------------------------------------------+----------+----------------------------+ + +.. _thermodynamic-functions-1: + +Thermodynamic functions +----------------------- + +The IAPWS-97 formulation provides thermodynamic functions for liquid +water, dry steam and supercritical fluid. These functions calculate +secondary parameters from the primary thermodynamic variables. + +---- + +.. _sec:iapws97:cowat: + +Liquid water: ``cowat(t,p)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``cowat`` function returns a two-element tuple (``d``,\ ``u``) of +density (kg/m\ :math:`^3`) and internal energy (J/kg) of liquid water as +a function of temperature ``t`` (°C) and pressure ``p`` +(Pa). + +**Parameters:** + +- | **t**: float + | Temperature (°C) + +- | **p**: float + | Pressure (Pa) + +---- + +.. _sec:iapws97:supst: + +Dry steam: ``supst(t,p)`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``supst`` function returns a two-element tuple (``d``,\ ``u``) of +density (kg/m\ :math:`^3`) and internal energy (J/kg) of dry steam as a +function of temperature ``t`` (°C) and pressure ``p`` +(Pa). + +**Parameters:** + +- | **t**: float + | Temperature (°C) + +- | **p**: float + | Pressure (Pa) + +---- + +.. _sec:iapws97:super: + +Supercritical fluid: ``super(d,t)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``super`` function returns a two-element tuple (``p``,\ ``u``) of +pressure (Pa) and internal energy (J/kg) of supercritical fluid as a +function of density ``d`` (kg/m\ :math:`^3`) and temperature ``t`` +(°C). + +**Parameters:** + +- | **d**: float + | Density (kg/m\ :math:`^3`) + +- | **t**: float + | Temperature (°C) + +---- + +.. _sec:iapws97:visc: + +Viscosity: ``visc(d,t)`` +------------------------ + +The ``visc`` function returns the dynamic viscosity (Pa.s) of liquid +water, dry steam or supercritical fluid as a function of density ``d`` +(kg/m\ :math:`^3`) and temperature ``t`` (°C). This function is based +on the supplementary "IAPWS Formulation 2008 for the Viscosity of +Ordinary Water Substance", without the critical enhancement of +viscosity near the critical point. + +**Parameters:** + +- | **d**: float + | Density (kg/m\ :math:`^3`) + +- | **t**: float + | Temperature (°C) + +---- + +Region boundaries +----------------- + +These functions describe the boundaries between the four thermodynamic +:ref:`regions ` of the IAPWS-97 formulation. There +is no equation for the boundary between regions 1 and 3 as this is +simply the line :math:`T` = 350 °C. + +.. _saturation-line-satt-and-tsatp-1: + +Saturation line: ``sat(t)`` and ``tsat(p)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _sec:iapws97:sat: + +``sat(t)`` +^^^^^^^^^^ + +The ``sat`` function returns the saturation pressure (Pa) at a given +temperature ``t`` (°C), for temperatures below the +critical temperature. + +**Parameters:** + +- | **t**: float + | Temperature (°C) + +---- + +.. _sec:iapws97:tsat: + +``tsat(p)`` +^^^^^^^^^^^ + +The ``tsat`` function returns the saturation temperature +(°C) at a given pressure ``p`` (Pa), for pressures below +the critical pressure. + +**Parameters:** + +- | **p**: float + | Pressure (Pa) + +---- + +.. _region23_boundary: + +Steam/supercritical boundary +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _sec:iapws97:b23p: + +``b23p(t)`` +^^^^^^^^^^^ + +The ``b23p`` function returns the pressure (Pa) on the boundary of the +dry steam and supercritical regions (regions 2 and 3) at a given +temperature ``t`` (°C). + +**Parameters:** + +- | **t**: float + | Temperature (°C) + +---- + +.. _sec:iapws97:b23t: + +``b23t(p)`` +^^^^^^^^^^^ + +The ``b23t`` function returns the temperature (°C) on +the boundary of the dry steam and supercritical regions (regions 2 and +3) at a given pressure ``p`` (Pa). + +**Parameters:** + +- | **p**: float + | Pressure (Pa) + +---- + +.. _determining-thermodynamic-region-1: + +Determining thermodynamic region +-------------------------------- + +.. _sec:iapws97:region: + +``region(t, p)`` +~~~~~~~~~~~~~~~~ + +Returns the thermodynamic region (integer, or ``None``) corresponding to +the given temperature (°C) and pressure (Pa), as defined +by the IAPWS-97 specification. The regions are: + +#. liquid water + +#. dry steam + +#. supercritical + +If the input temperature and/or pressure are outside the operating range +of the IAPWS-97 formulation, the routine will return ``None``. + +**Parameters:** + +- | **t**: float + | Temperature (°C) + +- | **Pressure**: float + | Pressure (Pa) + +---- + +Plotting functions +------------------ + +The ``IAPWS97`` library contains two functions used for including the +IAPWS-97 thermodynamic region boundaries on plots. + +---- + +.. _sec:iapws97:pressure_temperature_plot: + +``pressure_temperature_plot(plt)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Draws the IAPWS-97 thermodynamic region boundaries on a +pressure-temperature diagram. + +**Parameters:** + +- | **plt**: ``matplotlib.pyplot`` instance + | An instance of the ``matplotlib.pyplot`` library, imported in the + calling script using e.g. ``import matplotlib.pyplot as plt``. + +---- + +.. _sec:iapws97:density_temperature_plot: + +``density_temperature_plot(plt)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Draws the IAPWS-97 thermodynamic region boundaries on a +density-temperature diagram. (This function requires the Scientific +Python (``scipy``) library to be installed.) + +**Parameters:** + +- | **plt**: ``matplotlib.pyplot`` instance + | An instance of the ``matplotlib.pyplot`` library, imported in the + calling script using e.g. ``import matplotlib.pyplot as plt``. diff --git a/doc/source/iapws_regions.pdf b/doc/source/iapws_regions.pdf new file mode 100644 index 00000000..b30c78a4 Binary files /dev/null and b/doc/source/iapws_regions.pdf differ diff --git a/doc/source/iapws_regions.svg b/doc/source/iapws_regions.svg new file mode 100755 index 00000000..c4b52337 --- /dev/null +++ b/doc/source/iapws_regions.svg @@ -0,0 +1,1171 @@ + + + + + + image/svg+xml + + SVG drawing + + + + + + + + + + + SVG drawing + This was produced by version 4.1 of GNU libplot, a free library for exporting 2-D vector graphics. + + + + 0 + + + + + + + + + + + 25 + + + + + + + + + + + 50 + + + + + + + + + + + 75 + + + + + + + + + + + 100 + + + 0 + + + + + + + + + 200 + + + + + + + + + 400 + + + + + + + + + 600 + + + + + + + + + 800 + + Pressure (MPa) + Temperature (°C) + + 1 + + 3 + + 2 + + 4 + Critical + point + + + + + + (Liquid) + (Super- + critical) + (Vapour) + + + (Two-phase) + diff --git a/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 00000000..9da36774 --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,29 @@ +.. layermesh documentation master file, created by + sphinx-quickstart on Wed Feb 17 15:47:30 2021. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +PyTOUGH user guide +================== + +.. only:: html + + Welcome to the PyTOUGH user documentation. + + .. figure:: coverpic.png + :align: left + :scale: 50% + +.. toctree:: + :hidden: + + intro + mulgrids + t2grids + t2data + t2incon + t2listing + t2thermo + iapws97 + mulformat + commandref diff --git a/doc/source/intro.rst b/doc/source/intro.rst new file mode 100644 index 00000000..5bd78e34 --- /dev/null +++ b/doc/source/intro.rst @@ -0,0 +1,503 @@ +:tocdepth: 3 + +Introduction +============ + +What is PyTOUGH? +---------------- + +.. index:: PyTOUGH + +PyTOUGH (**Py**\ thon **TOUGH**) is a set of Python software routines +for making it easier to use the TOUGH2 geothermal reservoir simulator. +Using PyTOUGH, it is possible to automate the creation and editing of +TOUGH2 model grids and data files, and the analysis and display of model +simulation results. + +What are TOUGH2 and AUTOUGH2? +----------------------------- +.. index:: TOUGH2 +.. index:: TOUGH2; AUTOUGH2 + +`TOUGH2 `_ is a general-purpose simulator for +modelling subsurface fluid and heat flow, often used for simulating +geothermal reservoirs. + +AUTOUGH2 is the `University of Auckland +`_ version of TOUGH2. The main +differences between AUTOUGH2 and TOUGH2 are: + +- **EOS handling**: AUTOUGH2 includes all different equations of state + (EOSes) in a single executable program, whereas TOUGH2 uses different + executables for each EOS. As a result, the main input data file for + an AUTOUGH2 simulation also includes extra data blocks to specify + which EOS is to be used. + +- **Generator types**: AUTOUGH2 includes a variety of extra generator + types developed for geothermal reservoir simulation (e.g. makeup and + reinjection wells). + +.. index:: TOUGH2; TOUGH2-MP +.. index:: TOUGH2; TOUGH+ +.. index:: TOUGH3 + +TOUGH2_MP is a multi-processor version of TOUGH2. TOUGH+ is a +redeveloped version of TOUGH2, with a more modular code structure +implemented in Fortran-95. TOUGH3 is another parallelized +redevelopment of TOUGH2. + +TOUGH2 data files +~~~~~~~~~~~~~~~~~ + +.. index:: TOUGH2 data files +.. index:: TOUGH2 +.. index:: TOUGH2; AUTOUGH2 + +TOUGH2 takes its main input from a **data file**, which contains +information about the model grid, simulation parameters, time +stepping, sources of heat and mass etc. The data file formats for +TOUGH2 and AUTOUGH2 are almost identical, with minor +differences. TOUGH2_MP can read TOUGH2 data files, but also supports +some extensions (e.g. for 8-character instead of 5-character block +names) to this format. PyTOUGH does not currently support the +TOUGH2_MP extensions. TOUGH+ and TOUGH3 data files can also have some +extensions, which PyTOUGH does not support as yet. + +Because TOUGH2 uses a finite volume formulation, the only model grid +data it needs are the volumes of the grid blocks and the distances and +areas associated with the connections between blocks. Hence, the TOUGH2 +data file need not contain any information about the specific locations +of the blocks in space, and it contains no information about the +locations of the vertices or edges of the blocks. This makes it easy to +use TOUGH2 to simulate one-, two- or three-dimensional models, all with +the same format of data file. However, this lack of reference to any +coordinate system also makes it more difficult to generate model grids, +and to visualise simulation results in space. + +MULgraph geometry files +~~~~~~~~~~~~~~~~~~~~~~~ + +.. index:: MULgraph geometry; files + +For this reason, a separate **geometry file** can be used to create +grids for TOUGH2 simulations and visualise simulation results. The +geometry file contains information about the locations of the grid +block vertices. The geometry file can be used to visualise results +using the `TIM `_ graphical interface for +TOUGH2 and AUTOUGH, developed at the University of Auckland. (This +file format was originally designed for use with TIM's predecessor, +MULgraph). + +The MULgraph geometry file assumes the grid has a layered structure, +with blocks arranged in layers and columns, and the same arrangement of +columns on each layer. (At the top of the model grid, blocks in some +columns may be missing, to allow the grid to follow the surface +topography.) + +If you do not have a MULgraph geometry file for your model, it is easy +to create one for a rectangular grid. In fact, PyTOUGH is able to +:ref:`reverse-engineer` a MULgraph geometry from a +TOUGH2 data file containing a rectangular grid. + +A specification of the MULgraph geometry file format can be found +:ref:`here`. + +TOUGH2 listing files +~~~~~~~~~~~~~~~~~~~~ + +.. index:: TOUGH2 listing files + +The output from TOUGH2 is written to a **listing file**, which is a text +file containing tables of results for each time step (or only selected +time steps, if preferred). At each time step there is an 'element +table', containing results for block properties (e.g. pressure, +temperature etc.). There may also be a 'connection table', containing +results for flows between blocks, and a 'generation table', containing +results (e.g. flow rates) at the generators in the model (e.g. wells). + +The formats of the listing files produced by TOUGH2, AUTOUGH2, +TOUGH2_MP, TOUGH+ and TOUGH3 are all slightly different, and also vary +depending on the EOS used. However, PyTOUGH attempts to detect and +read all of these formats. + +What is Python? +--------------- + +.. index:: Python +.. index:: Python; 3.x + +Python is a general-purpose programming language. It is free and +open-source, and runs on many different computer operating systems +(Linux, Windows, Mac OS X and others). Python can be downloaded from +the Python `website `_, which also contains +detailed reference material about the Python language. If you are +using Linux you probably already have Python, as it is included in +most Linux distributions. + +PyTOUGH should run on any version of Python 2.x newer than 2.4 (though +version 2.6 or newer is recommended). PyTOUGH version 1.5 or later +also runs on Python 3.x. + +.. index:: Python; tutorials + +If you are unfamiliar with Python (even if you have used another +programming language before), it is highly recommended that you do one +of the many Python tutorials available online, e.g. + +- http://docs.python.org/tutorial/ + +- http://wiki.python.org/moin/BeginnersGuide + +Python basics +~~~~~~~~~~~~~ + +Objects +^^^^^^^ + +.. index:: Python; objects + +Python is what is known as an **object-oriented** language, which means +that it is possible to create special customised data types, or +'classes', to encapsulate all the properties and behaviour of the things +(objects) we are dealing with in a program. This is a very useful way of +simplifying complex programs. (In fact, in Python, everything is treated +as an object, even simple things like integers and strings.) + +For example, in a TOUGH2 model grid we have collections of grid blocks, +and we need to store the names of these blocks and their volumes and +rock types. In a non-object-oriented language, these could be stored in +three separate arrays: a string array for the names, a real (or 'float') +array for the volumes and another string array for the rock types. In an +object-oriented language like Python, we can define a new data type (or +'class') for blocks, which holds the name, volume and rock type of the +block. If we declare an object called ``blk`` of this block class, we +can access or edit its volume by referring to ``blk.volume``. In this +way, we can store our blocks in one single array of block objects. When +we add or delete blocks from our grid, we can just add or delete block +objects from the array, rather than having to keep track of three +separate arrays. + +In general, an object not only has **properties** (like +``blk.volume``) but also **methods**, which are functions the object +can carry out. For example, if we wanted to rotate a MULgraph geometry +file by 30°, we could do this in PyTOUGH by declaring a MULgraph +geometry file object called ``geo``, and calling its ``rotate`` +method: ``geo.rotate(30)``. The methods of an object are accessed in +the same way that its properties are accessed: by adding a dot (.) +after the object's name and then adding the name of the property or +method. Any arguments of the method (e.g. the angle in the ``rotate`` +function above) are added in parentheses afterwards. + +Lists, dictionaries, tuples and sets +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: Python; lists +.. index:: Python; dictionaries +.. index:: Python; tuples +.. index:: Python; sets + +Most programming languages have simple data types built in, e.g. float, +double precision or integer numbers, strings, and arrays of these. +Python has some other data types which are very useful and are used a +lot. + +The first of these is the **list**. A list can contain any ordered +collection of objects, of any type, or even of different types, and is +delimited by square brackets. So for example we can declare a list +``things = [1, 'two',3.0]`` containing an integer, a string and a float. +We can access the list's elements in much the same way as we access the +elements of an array, for example ``things[1]`` would return the value +``'two'`` (note that in Python, as in most other languages besides +Fortran, the indices of arrays and lists start at 0, not 1). Additional +elements can be added to a list at any time, without having to +re-declare the size of the list: for example, ``things.append('IV')`` +would add an extra element to the end of the list, giving it the value +``[1, 'two', 3.0, 'IV']``. It is also possible to remove elements from a +list, e.g. ``things.remove(3.0)``, which would give our list the value +``[1, 'two', 'IV']``. + +Another useful Python data type is the **dictionary**. Dictionaries are +mainly used to store collections of objects (again, of any type or of +different types) that are referenced by name rather than by index (as in +an array or list). A dictionary is delimited by curly brackets. So for +example we can declare a dictionary +``phone = {'Eric':8155, 'Fred':2350, 'Wilma':4667}`` and then find +Fred's phone number from ``phone['Fred']``, which would return ``2350``. +For TOUGH2 models, blocks, generators, rock types and other objects are +often referred to by name rather than index, so dictionaries are an +appropriate way to store them. + +A third Python data type, similar to a list, is the **tuple**. A tuple +is essentially a list that cannot be changed, and is often used just for +grouping objects together. A tuple is delimited by parentheses. For +example, ``things = (1, 'two', 3.0)`` declares a tuple with three +elements. We can still refer to the elements of a tuple using e.g. +``a[1]``, but we cannot assign new values to these elements or add or +remove elements from the tuple once it has been declared. + +Python also has a **set** data type, which represents a mathematical set +- an unordered collection of objects. One of the useful aspects of sets +is that they cannot contain duplicate items. As a result, for example, +duplicate items can be removed from a list ``x`` simply by converting it +to a set, and then back to a list: ``x = list(set(x))``. + +How to run Python +~~~~~~~~~~~~~~~~~ + +.. index:: Python; running + +Python can be run either interactively or via scripts. + +.. _python_interactive: + +Running Python interactively +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The simplest way to run Python interactively is just by typing +``python`` (or possibly ``python3``) at the command line. (On Windows +the directory that Python was installed into may have to be added to +your ``PATH`` environment variable first.) The command line then becomes +an interactive Python environment in which you can type Python commands +at the Python command prompt ``>>>``, e.g.: + +:: + + bob@superbox:~$ python3 + Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux + Type "help", "copyright", "credits" or "license" for more information. + >>> things = [1, 'two', 3.0] + >>> print(things[1]) + two + >>> exit() + bob@superbox:~$ + +In the interactive Python environment, you can view help on the +properties and methods of any Python object by typing +``help(objectname)``, where ``objectname`` is the name of an object that +has been declared. This will list the properties and methods of the +object and a description of each one. + +You can exit the interactive Python environment by typing ``exit()`` or +``Ctrl-Z`` on Windows, or ``Ctrl-D`` on Linux. + +Python scripts +^^^^^^^^^^^^^^ + +.. index:: Python; scripts + +The real power of Python, however, lies in using it to write **scripts** +to automate repetitive or complex tasks. You can just type Python +commands into a text file, save it with the file extension ``.py``, and +execute it by typing ``python filename.py``, where ``filename.py`` is +the name of the file. (Once again, on Windows the directory that Python +was installed into may have to be added to your ``PATH`` environment +variable first.) + +You can also debug a Python script using the 'pdb' command-line +debugger. Typing ``python -m pdb filename.py`` will start debugging the +script *filename.py*. + +It is also possible to run a Python script from within the interactive +Python environment. From the Python environment command line, typing +``execfile('filename.py')`` will execute the script ``filename.py``. + +.. _pylibraries: + +Python libraries +~~~~~~~~~~~~~~~~ + +.. index:: Python; libraries + +Python comes with a large number of features already built in, but for +specialised tasks, additional **libraries** of Python software can be +imported into Python as you need them. PyTOUGH itself is a set of such +libraries, and it in turn makes use of some other third-party Python +libraries. The most important of these are as follows: + +Numerical Python (“NumPy”) +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: Python; numpy + +`NumPy `_ adds a special ``numpy.array`` class for +fast multi-dimensional arrays, which PyTOUGH makes heavy use of, and a +whole range of other features, e.g. linear algebra routines, Fourier +transforms and statistics. + +Scientific Python (“SciPy”) +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: Python; scipy + +`SciPy `_ is a library of advanced mathematical +functions (e.g. interpolation, calculus, optimisation), needed for some +PyTOUGH functionality. + +Matplotlib +^^^^^^^^^^ + +.. index:: Python; matplotlib + +`Matplotlib `_ is a library of graphical +plotting routines, which can be used for 2-D PyTOUGH visualization +tools like layer and slice plots. + +Other libraries +^^^^^^^^^^^^^^^ + +.. index:: Visualization Tool Kit (VTK) +.. index:: Python; meshio + +Some parts of PyTOUGH use other Python libraries. You do not need to +install these libraries unless you are using the parts of PyTOUGH that +depend on them. If you try to use parts of PyTOUGH that need these +libraries, and you don't have them installed, it will tell you so. + +Examples: + +- **VTK**, a Python interface to the `Visualization Tool Kit `_, + a library for 3D visualisation of data via + VTK itself, or software such as `ParaView `_, + `Mayavi `_ etc. + +- **meshio**, a `library `_ for 3D + mesh handling – used for exporting PyTOUGH grids to other formats + +Importing libraries +^^^^^^^^^^^^^^^^^^^ + +.. index:: Python; importing + +To use any Python library, you just need to **import** it first. For +example, once you have installed Numerical Python, you can make it +available (in the interactive Python environment or in a Python script) +by typing the command ``import numpy``, or alternatively +``from numpy import *``. This imports all classes and commands from +Numerical Python and makes them available for use. (You can also import +only parts of a library rather than the whole thing, e.g. +``from numpy import linalg`` just imports the linear algebra routines +from Numerical Python.) + +When you import a library, you can also change its name. For example, +PyTOUGH imports Numerical Python using the command +``import numpy as np``, which renames ``numpy`` as the abbreviated +``np``. This means it can, for example, access the Numerical Python +``numpy.array`` data type as ``np.array``. It also means you have access +to Numerical Python as ``np`` in your own scripts and in the interactive +Python environment, without having to import it yourself. + +.. _installing: + +Installing PyTOUGH +------------------ + +.. index:: PyTOUGH; installing + +From version 1.6.0, the easiest way to install PyTOUGH is via the +``pip`` Python package installer: + +:: + + pip install PyTOUGH + +or + +:: + + python -m pip install PyTOUGH + +either of which will install the latest version of PyTOUGH, together +with its main dependency libraries (``numpy``, ``scipy`` and +``matplotlib``) if these are not already detected on your system. + +You can also install a particular version of PyTOUGH, e.g. to install +version 1.6.0: + +:: + + pip install PyTOUGH==1.6.0 + +or upgrade your existing version of PyTOUGH: + +:: + + pip install --upgrade PyTOUGH + +There are various ways of configuring the installation of packages with +``pip``, which may be suitable for your particular system – consult the +``pip`` `documentation `_ for details. + +After installing, you should be able to import the PyTOUGH libraries +into the Python interactive environment or your Python scripts, from any +directory on your computer. For example, you can import the MULgraph +geometry library using ``from mulgrids import *`` (see :ref:`mulgrids`). + +To uninstall PyTOUGH: + +:: + + pip uninstall PyTOUGH + +Installing the testing branch +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. index:: PyTOUGH; testing branch + +The PyTOUGH code exists in two main “branches”: the ``master`` branch, +which contains the latest stable release, and the ``testing`` branch, +which includes the most recent changes being tested for inclusion in the +next stable release. + +If you need these most recent changes and can't wait for the next stable +release, it is possible to install the ``testing`` branch of PyTOUGH +using e.g.: + +:: + + pip install git+https://github.com/acroucher/PyTOUGH.git@testing + +.. _unittests: + +Testing PyTOUGH +--------------- + +.. index:: PyTOUGH; unit tests + +PyTOUGH includes a suite of “unit tests” which can be used to verify +that it is working correctly. These are located in the ``tests/`` +directory of the PyTOUGH repository, which includes a number of Python +scripts for testing individual PyTOUGH modules. + +First you will the PyTOUGH repository on your machine. This is available +`here `_. Click the ``Code`` button +which gives various options for downloading the repository, via e.g. zip +file or Git clone. + +The unit test modules in the ``tests/`` directory may be run +individually, the same way as any other Python script would be run. If +the tests in the script all pass, the last message printed out to the +console will read ``OK``. If not, details will be output regarding which +tests did not pass. + +It is also possible to run the unit tests for all modules by running the +following command in the ``tests/`` directory: + +:: + + python -m unittest discover + +or with the ``-v`` (verbose) flag to output more detail on which tests +are being run: + +:: + + python -m unittest discover -v + +Licensing +--------- + +.. index:: PyTOUGH; license + +PyTOUGH is free software, distributed under the GNU Lesser General +Public License (LGPL). More information is available +`here `_. diff --git a/doc/source/mulformat.rst b/doc/source/mulformat.rst new file mode 100644 index 00000000..14b7572a --- /dev/null +++ b/doc/source/mulformat.rst @@ -0,0 +1,492 @@ +:tocdepth: 3 + +.. _geometry_file_format: + +MULgraph geometry file format +============================= + +.. _introduction-8: + +Introduction +------------ + +This section gives a format specification of the MULgraph geometry +file. These files can be used to give a geometrical description of a +TOUGH2 model grid, useful for creating grids and visualizing simulation +results. + +MULgraph geometry files were originally developed for use with +MULgraph, a graphical interface for TOUGH2 and AUTOUGH2 developed at +the University of Auckland in the 1990s, and subsequently adopted by +the `TIM `_ graphical interface. However, +MULgraph geometry files can be used independently of MULgraph or +TIM. PyTOUGH is able to represent the contents of a MULgraph geometry +file in a Python script via the :ref:`mulgrid ` class. + +Grid structure +-------------- + +Layers and columns +~~~~~~~~~~~~~~~~~~ + +MULgraph geometry files implicitly assume a layered structure, with +blocks arranged in layers and columns, and the same arrangement of +columns in each layer. The only exception to this is at the top surface +of the model, where layers are allowed to be incomplete (i.e. not +contain all columns) in order to represent topography. + +The layers are always of constant vertical thickness. However, the +blocks in the top layer are allowed to vary in height, again to +represent variations in ground surface elevation. + +Atmosphere blocks +~~~~~~~~~~~~~~~~~ + +The blocks in the top layer may optionally be connected to the +atmosphere- either a single atmosphere block connected to all columns, +or a separate atmosphere block over each column (see +:ref:`Naming conventions and atmosphere types `). + +.. _tilted-geometries-1: + +Tilted geometries +~~~~~~~~~~~~~~~~~ + +It is possible to tilt the geometry coordinate axes with respect to the +vertical, to represent non-horizontal geometries. When a TOUGH2 grid is +created from such a tilted geometry, only the gravity cosines of the +grid connections are affected. + +.. _rotating-permeability-directions-1: + +Rotating permeability directions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is also possible to rotate the permeability principal directions with +respect to the coordinate axes- for example, to align permeabilities +with a dominant fault direction. When a TOUGH2 grid is created, this can +change the permeability index associated with each connection. + +Geometry types +-------------- + +The original MULgraph file specification allowed for three types of +geometry: 'general', 'rectangular' and 'radial'. Only the 'general' +geometry type is supported by PyTOUGH. It is intended for representing +general grids with arbitrary, possibly unstructured horizontal column +arrangements. + +The 'rectangular' type was a special type for grids with rectangular +horizontal column structures. These can also be represented using the +'general' geometry type. Since PyTOUGH contains +:ref:`methods ` for constructing +rectangular grids within the 'general' geometry type, there is usually +no longer any significant benefit from using the 'rectangular' type. + +The 'radial' type was intended for grids with radial horizontal column +structure. PyTOUGH also contains :ref:`methods ` +for creating radial TOUGH2 grids. Simulation results from radial +models can also be visualized using a simple one- or two-dimensional +rectangular 'general' geometry to represent the grid structure in the +radial direction. + +.. _geometry_format_conventions: + +Naming conventions and atmosphere types +--------------------------------------- + +The grid block naming convention and atmosphere type used in a +MULgraph geometry file are both integers and can be given values in +the range 0 -- 3 and 0 -- 2 respectively. The meanings of these +values are shown in the tables below. + +Note that the grid nodes (vertices) are also named according to the +column part of the block naming convention. If naming nodes, columns or +layers manually, while the names can in principle be arbitrary (within +the naming convention), it is safest to right-justify them. + +The MULgraph block naming conventions all use part of the block name to +indicate the layer, and part of it to indicate the column. In PyTOUGH, +it is also possible to use MULgraph geometry files in conjunction with +TOUGH2 grids that follow other naming conventions, by means of a +:ref:`block mapping ` dictionary. + +Block naming convention 3 was not supported by the original MULgraph +geometry file format, and produces block names which do not conform to +the TOUGH2 block naming requirements (having numbers in the last two +characters). It can be used to produce grids for other simulators such +as Waiwera which do not have these requirements. An alternative tool +for creating such grids is the `Layermesh +`_ library. + +.. container:: + :name: tb:mulgrid_conventions + + .. table:: MULgraph geometry file naming conventions + + +------------+-------------------------------------------------------+ + | Convention | Meaning | + +============+=======================================================+ + | 0 |3 characters for column followed by 2 digits for layer | + +------------+-------------------------------------------------------+ + | 1 |3 characters for layer followed by 2 digits for column | + +------------+-------------------------------------------------------+ + | 2 |2 characters for layer followed by 3 digits for column | + +------------+-------------------------------------------------------+ + | 3 |3 characters for column followed by 2 characters for | + | |layer | + +------------+-------------------------------------------------------+ + +.. container:: + :name: tb:mulgrid_atmosphere_types + + .. table:: MULgraph geometry file atmosphere types + + +------+---------------------------------------+ + | Type | Meaning | + +======+=======================================+ + | 0 | A single atmosphere block | + +------+---------------------------------------+ + | 1 | One atmosphere block over each column | + +------+---------------------------------------+ + | 2 | No atmosphere blocks | + +------+---------------------------------------+ + +File format +----------- + +MUlgraph geometry files are simple formatted ASCII text files with a +header line at the top, followed by a number of sections. Each section +begins with a keyword and ends with a blank line. Each line has +**fixed** format, so the different values have to be specified in the +right text columns. + +If you use PyTOUGH scripts to create and manipulate your grid +geometries, you don't need to know anything about the format of a +MULgraph geometry file, because PyTOUGH will handle reading and writing +them for you. If, however, for some reason you do need to know how these +files are structured, the format specification for a 'general' type +geometry file is given below. + +Header +~~~~~~ + +This is a single line containing a number of global parameters of the +geometry. Its format is given in the +:ref:`table ` below. + +Note that the block ordering parameter is an extension to the original +MULgraph file format. + +.. container:: + :name: tb:mulgraph_format_header + + .. table:: MULgraph geometry file header line format + + +---------------+-----------+------------+-------------+-------------------------------------------+ + | **Name** | **Type** | **Length** | **Columns** | **Description** | + | | | | | | + +===============+===========+============+=============+===========================================+ + | **Geometry | character | 5 | 1–5 | 'GENER' for general geometry type; | + | type** | | | | 'RECTA' or 'RADIA' for other types | + | | | | | (but these are not supported by | + | | | | | PyTOUGH) | + +---------------+-----------+------------+-------------+-------------------------------------------+ + | **Naming | integer | 1 | 6 | Block naming | + | convention** | | | | :ref:`convention` | + | | | | | | + | | | | | | + | | | | | | + +---------------+-----------+------------+-------------+-------------------------------------------+ + | **Atmosphere | integer | 1 | 7 | :ref:`Type ` | + | type** | | | | of atmosphere | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + +---------------+-----------+------------+-------------+-------------------------------------------+ + | **Atmosphere | float | 10 | 8–17 | Volume of each atmosphere block | + | volume** | | | | (default 10\ :sup:`20` m\ :sup:`3`) | + +---------------+-----------+------------+-------------+-------------------------------------------+ + | **Atmosphere | float | 10 | 18–27 | Connection distance for each | + | connection | | | | atmosphere block (default | + | distance** | | | | 10\ :sup:`-6` m) | + +---------------+-----------+------------+-------------+-------------------------------------------+ + | **Length | character | 5 | 28–32 | Default is metres (blank); for | + | unit** | | | | feet specify 'FEET' | + +---------------+-----------+------------+-------------+-------------------------------------------+ + | **x-direction | float | 10 | 33–42 | Cosine of angle between x-axis and | + | cosine** | | | | gravity vector (default zero); set | + | | | | | positive for tilt in the x-direction | + +---------------+-----------+------------+-------------+-------------------------------------------+ + | **y-direction | float | 10 | 43–52 | Cosine of angle between | + | cosine** | | | | y-axis and gravity vector (default | + | | | | | zero); set positive for tilt in the | + | | | | | y-direction | + +---------------+-----------+------------+-------------+-------------------------------------------+ + | **Connection | integer | 1 | 53 | Method of calculating connection | + | type** | | | | parameters (default zero)- not | + | | | | | supported by PyTOUGH | + +---------------+-----------+------------+-------------+-------------------------------------------+ + | **Permeability| float | 10 | 54–63 | Horizontal angle (degrees | + | angle** | | | | anti-clockwise) between first | + | | | | | permeability direction and x-axis | + +---------------+-----------+------------+-------------+-------------------------------------------+ + | **Block | integer | 2 | 64–65 | Block ordering scheme: 0 for original | + | ordering** | | | | MULgraph layer/column ordering; 1 for | + | | | | | PETSc DMPlex ordering (sorted by | + | | | | | block type) | + +---------------+-----------+------------+-------------+-------------------------------------------+ + +Vertices +~~~~~~~~ + +This section defines the horizontal locations of the grid vertices +(nodes), at the corners of the columns. The first line just contains the +keyword 'VERTI'. Each subsequent line defines the position of a vertex, +and has the format given in the +:ref:`table ` below. The vertices section is +terminated by a blank line. + +.. container:: + :name: tb:mulgraph_format_vertices + + .. table:: MULgraph geometry file vertices format + + +--------------+-----------+------------+-------------+-------------------------------------------------+ + | **Name** | **Type** | **Length** | **Columns** | **Description** | + | | | | | | + +==============+===========+============+=============+=================================================+ + | **Vertex | character | 3 | 1–3 | Name of the vertex (honouring the column naming | + | name** | | | | :ref:`convention ` | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + +--------------+-----------+------------+-------------+-------------------------------------------------+ + | **x** | float | 10 | 4–13 | x-coordinate | + | | | | | of the | + | | | | | vertex | + +--------------+-----------+------------+-------------+-------------------------------------------------+ + | **y** | float | 10 | 14–23 | y-coordinate | + | | | | | of the | + | | | | | vertex | + +--------------+-----------+------------+-------------+-------------------------------------------------+ + +Grid +~~~~ + +This section specifies the vertices making up each column. The first +line just contains the keyword 'GRID'. + +For each grid column, there is then a sub-header line with information +about the column, followed by a line for each vertex making up the +column. The lines for the sub-header and each vertex have the formats +given in the tables below. There are no blank lines between the +definitions of the grid columns, but there is a blank line at the end +of the section. + +.. container:: + :name: tb:mulgraph_format_column_header + + .. table:: MULgraph geometry file column header format + + +--------------+-----------+------------+-------------+---------------------------------------------------+ + | **Name** | **Type** | **Length** | **Columns** | **Description** | + | | | | | | + +==============+===========+============+=============+===================================================+ + | **Column | character | 3 | 1–3 | Name of the column (honouring the column naming | + | name** | | | | :ref:`convention `) | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + +--------------+-----------+------------+-------------+---------------------------------------------------+ + | **Centre | integer | 1 | 4–5 | Set non-zero | + | specified** | | | | to specify | + | | | | | the column | + | | | | | centre | + | | | | | location, or | + | | | | | zero | + | | | | | (default) to | + | | | | | calculate it | + | | | | | as the | + | | | | | centroid of | + | | | | | the column | + +--------------+-----------+------------+-------------+---------------------------------------------------+ + | **Number of | integer | 2 | 6–7 | Number of | + | vertices** | | | | vertices in | + | | | | | the column | + +--------------+-----------+------------+-------------+---------------------------------------------------+ + | **Column | float | 10 | 8–17 | x-coordinate | + | centre x** | | | | of column | + | | | | | centre | + +--------------+-----------+------------+-------------+---------------------------------------------------+ + | **Column | float | 10 | 18–27 | y-coordinate | + | centre y** | | | | of column | + | | | | | centre | + +--------------+-----------+------------+-------------+---------------------------------------------------+ + +.. container:: + :name: tb:mulgraph_format_column_vertex + + .. table:: MULgraph geometry file column vertex format + + +--------------+-----------+------------+-------------+----------------+ + | **Name** | **Type** | **Length** | **Columns** | **Description**| + | | | | | | + +==============+===========+============+=============+================+ + | **Vertex | character | 3 | 1–3 | Name of the | + | name** | | | | vertex, as | + | | | | | specified in | + | | | | | the vertices | + | | | | | section | + +--------------+-----------+------------+-------------+----------------+ + +Connections +~~~~~~~~~~~ + +This section defines the horizontal connections between columns. The +first line just contains the keyword 'CONNE'. + +Each subsequent line defines a connection between two columns, and has +the format given in the :ref:`table ` below. +There is a blank line at the end of the section. + +.. container:: + :name: tb:mulgraph_format_connection + + .. table:: MULgraph geometry file connection format + + +--------------+-----------+------------+-------------+----------------+ + | **Name** | **Type** | **Length** | **Columns** | **Description**| + | | | | | | + +==============+===========+============+=============+================+ + | **First | character | 3 | 1–3 | Name of the | + | column | | | | first column | + | name** | | | | | + +--------------+-----------+------------+-------------+----------------+ + | **Second | character | 3 | 4–6 | Name of the | + | column | | | | second | + | name** | | | | column | + +--------------+-----------+------------+-------------+----------------+ + +Layers +~~~~~~ + +This section defines the grid layers. The first line just contains the +keyword 'LAYER'. + +Each subsequent line defines a layer, with format given in the +:ref:`table ` below. There are no blank lines between +layers, but there is a blank line at the end of the section. + +.. container:: + :name: tb:mulgraph_format_layer + + .. table:: MULgraph geometry file layer format + + +--------------+-----------+------------+-------------+----------------------------------------------------+ + | **Name** | **Type** | **Length** | **Columns** | **Description** | + | | | | | | + +==============+===========+============+=============+====================================================+ + | **Layer | character | 3 | 1–3 | Name of the layer (honouring the layer | + | name** | | | | naming :ref:`convention `) | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + | | | | | | + +--------------+-----------+------------+-------------+----------------------------------------------------+ + | **Bottom | float | 10 | 4–13 | Elevation of | + | elevation** | | | | the bottom | + | | | | | of the layer | + +--------------+-----------+------------+-------------+----------------------------------------------------+ + | **Centre | float | 10 | 14–23 | Elevation of | + | elevation** | | | | the centre | + | | | | | of the layer | + +--------------+-----------+------------+-------------+----------------------------------------------------+ + +Surface elevation +~~~~~~~~~~~~~~~~~ + +This section is optional, and can be used to define the surface +elevation at any or all columns in the grid, to represent topography. +The first line just contains the keyword 'SURFA'. + +Each subsequent line defines the surface elevation at a column, with +format given in the :ref:`table ` below. There is a +blank line at the end of the section. + +.. container:: + :name: tb:mulgraph_format_surface + + .. table:: MULgraph geometry file surface elevation format + + +--------------+-----------+------------+-------------+----------------+ + | **Name** | **Type** | **Length** | **Columns** | **Description**| + | | | | | | + +==============+===========+============+=============+================+ + | **Column | character | 3 | 1–3 | Name of the | + | name** | | | | column | + +--------------+-----------+------------+-------------+----------------+ + | **Surface | float | 10 | 4–13 | Surface | + | elevation** | | | | elevation of | + | | | | | the column | + +--------------+-----------+------------+-------------+----------------+ + +Wells +~~~~~ + +This section is optional, and can be used to define the positions of +wells (including their tracks) within the geometry. Deviated wells are +supported. The first line of the section just contains the keyword +'WELLS'. + +Each subsequent line defines the location of one point on a well track, +with format given in the :ref:`table ` below. At +least two points are required to define each well (one for the wellhead +and one for the bottom), with more than two points needed to define a +deviated well. There is a blank line at the end of the section. + +.. container:: + :name: tb:mulgraph_format_wells + + .. table:: MULgraph geometry file well format + + +--------------+-----------+------------+-------------+----------------+ + | **Name** | **Type** | **Length** | **Columns** |**Description** | + | | | | | | + +==============+===========+============+=============+================+ + | **Well | character | 5 | 1–5 | Name of the | + | name** | | | | well | + +--------------+-----------+------------+-------------+----------------+ + | **x** | float | 10 | 6–15 | x-coordinate | + | | | | | of the well | + | | | | | location | + +--------------+-----------+------------+-------------+----------------+ + | **y** | float | 10 | 16–25 | y-coordinate | + | | | | | of the well | + | | | | | location | + +--------------+-----------+------------+-------------+----------------+ + | **z** | float | 10 | 26–35 | z-coordinate | + | | | | | of the well | + | | | | | location | + +--------------+-----------+------------+-------------+----------------+ diff --git a/doc/source/mulgrids.rst b/doc/source/mulgrids.rst new file mode 100644 index 00000000..9c9501c6 --- /dev/null +++ b/doc/source/mulgrids.rst @@ -0,0 +1,3893 @@ +:tocdepth: 3 + +.. _mulgrids: + +MULgraph geometry files +======================= + +.. _introduction-1: + +.. index:: MULgraph geometry + +Introduction +------------ + +The ``mulgrids`` library in PyTOUGH contains classes and routines for +creating, editing and saving MULgraph geometry files. It can be imported +using the command: + +:: + + from mulgrids import * + +``mulgrid`` objects +------------------- + +The ``mulgrids`` library defines a ``mulgrid`` class, used for +representing MULgraph geometry files. + +**Example:** + +:: + + geo = mulgrid() + +creates an empty ``mulgrid`` object called ``geo``. + +:: + + geo = mulgrid('geom.dat') + +creates a ``mulgrid`` object called ``geo`` and reads its contents from +a file named ``'geom.dat'``. + +Printing a ``mulgrid`` object (e.g. ``print(geo)``) displays a summary +of information about the grid: how many nodes, columns, layers, blocks +and wells it contains, as well as its naming convention and atmosphere +type. + +A specification of the MULgraph geometry file format can be found +:ref:`here`. + +Properties +~~~~~~~~~~ + +The main properties of a ``mulgrid`` object are listed in the +:ref:`table ` below. Some of these properties +are 'header' information, corresponding to the data at the start of a +MULgraph geometry file (``type``, ``convention``, ``atmosphere_type``, +``atmosphere_volume``, ``atmosphere_connection`` and ``unit_type``). + +The most important properties of a ``mulgrid`` object are ``node``, +``column``, ``connection``, ``layer`` and ``well``, which are +dictionaries of the grid nodes, columns, connections, layers and wells, +accessed by name. For example, grid layer 'AA' of a ``mulgrid`` object +``geo`` can be accessed by ``geo.layer['AA']``. (The ``nodelist``, +``columnlist``, ``connectionlist``, ``layerlist`` and ``welllist`` +properties offer access to the nodes, columns, connections, layers and +wells by index, which is sometimes useful e.g. for looping over all +columns in the grid.) + +Connections are slightly different from nodes, columns etc. in that they +are not named individually. However, they can be accessed by the names +of the columns connected by the connection. For example, the connection +between columns '10' and '11' in a ``mulgrid`` called ``geo`` is given +by ``geo.connection[' 10',' 11']``. + +The elements of these lists and dictionaries are of type ``node``, +``column``, ``connection``, ``layer`` and ``well`` respectively. These +are additional object classes to represent nodes, columns, connections, +layers and wells, defined in the ``mulgrids`` library (see +:ref:`Other objects `). + +.. container:: + :name: tb:mulgrid_properties + + .. table:: Properties of a ``mulgrid`` object + + +--------------------------------+--------------------+-----------------------+ + | **Property** | **Type** | **Description** | + +================================+====================+=======================+ + | ``area`` | float | total horizontal area | + | | | covered by the grid | + +--------------------------------+--------------------+-----------------------+ + | ``atmosphere_connection`` | float | connection distance | + | | | to atmosphere blocks | + +--------------------------------+--------------------+-----------------------+ + | ``atmosphere_type`` | integer | type of atmosphere | + +--------------------------------+--------------------+-----------------------+ + | ``atmosphere_volume`` | float | volume of atmosphere | + | | | blocks | + +--------------------------------+--------------------+-----------------------+ + | ``bad_columns`` | set | columns that do not | + | | | contain their own | + | | | centres | + +--------------------------------+--------------------+-----------------------+ + | ``bad_layers`` | set | layers that do not | + | | | contain their own | + | | | centres | + +--------------------------------+--------------------+-----------------------+ + | ``block_connection_name_index``| dictionary | indices of block | + | | | connections (by name) | + +--------------------------------+--------------------+-----------------------+ + | ``block_connection_name_list`` | list | names of block | + | | | connections (by | + | | | index) | + +--------------------------------+--------------------+-----------------------+ + | ``block_name_index`` | dictionary | indices of blocks (by | + | | | name) | + +--------------------------------+--------------------+-----------------------+ + | ``block_name_list`` | list | names of blocks (by | + | | | index) | + +--------------------------------+--------------------+-----------------------+ + | ``block_order`` | string | block ordering scheme | + +--------------------------------+--------------------+-----------------------+ + | ``boundary_columns`` | set | set of columns on the | + | | | outer boundary of the | + | | | grid | + +--------------------------------+--------------------+-----------------------+ + | ``boundary_nodes`` | list | ordered list of nodes | + | | | on the outer boundary | + | | | of the grid | + +--------------------------------+--------------------+-----------------------+ + | ``boundary_polygon`` | list | list of points | + | | | representing grid | + | | | boundary (extra | + | | | colinear points | + | | | removed) | + +--------------------------------+--------------------+-----------------------+ + | ``bounds`` | list | [bottom left, top | + | | | right] horizontal | + | | | bounds of grid | + +--------------------------------+--------------------+-----------------------+ + | ``centre`` | ``np.array`` | position of | + | | | horizontal centre of | + | | | the grid | + +--------------------------------+--------------------+-----------------------+ + | ``columnlist`` | list | columns (by index, | + | | | e.g. | + | | | ``columnlist[23]``) | + +--------------------------------+--------------------+-----------------------+ + | ``column_angle_ratio`` | ``np.array`` | angle ratio for each | + | | | column | + +--------------------------------+--------------------+-----------------------+ + | ``column_side_ratio`` | ``np.array`` | side ratio for each | + | | | column | + +--------------------------------+--------------------+-----------------------+ + | ``column`` | dictionary | columns (by name, | + | | | e.g. | + | | | ``column['AA']``) | + +--------------------------------+--------------------+-----------------------+ + | ``connectionlist`` | list | connections between | + | | | columns (by index) | + +--------------------------------+--------------------+-----------------------+ + | ``connection_angle_cosine`` | ``np.array`` | angle cosines for all | + | | | connections | + +--------------------------------+--------------------+-----------------------+ + | ``convention`` | integer | naming convention for | + | | | columns and layers | + +--------------------------------+--------------------+-----------------------+ + | ``default_surface`` | Boolean | ``True`` if all | + | | | columns have default | + | | | surface elevation | + +--------------------------------+--------------------+-----------------------+ + | ``extra_connections`` | set | connections defined | + | | | between columns that | + | | | are not against each | + | | | other | + +--------------------------------+--------------------+-----------------------+ + | ``filename`` | string | file name on disk | + +--------------------------------+--------------------+-----------------------+ + | ``gdcx``, ``gdcy`` | float | cosines of angles x- | + | | | and y-axes make with | + | | | gravity vector | + +--------------------------------+--------------------+-----------------------+ + | ``node_kdtree`` | ``cKDTree`` | tree structure for | + | | | fast searching for | + | | | nodes | + +--------------------------------+--------------------+-----------------------+ + | ``layerlist`` | list | layers (by index) | + +--------------------------------+--------------------+-----------------------+ + | ``layermesh`` | ``layermesh`` mesh | Layermesh library | + | | | mesh object | + +--------------------------------+--------------------+-----------------------+ + | ``layer`` | dictionary | layers (by name) | + +--------------------------------+--------------------+-----------------------+ + | ``min_surface_block_thickness``| (float, string) | thickness of thinnest | + | | | surface block (and | + | | | associated column | + | | | name) | + +--------------------------------+--------------------+-----------------------+ + | ``missing_connections`` | set | missing connections | + | | | between columns | + +--------------------------------+--------------------+-----------------------+ + | ``nodelist`` | list | nodes (by index) | + +--------------------------------+--------------------+-----------------------+ + | ``node`` | dictionary | nodes (by name) | + +--------------------------------+--------------------+-----------------------+ + | ``num_atmosphere_blocks`` | integer | number of atmosphere | + | | | blocks | + +--------------------------------+--------------------+-----------------------+ + | ``num_blocks`` | integer | total number of | + | | | blocks in the grid | + +--------------------------------+--------------------+-----------------------+ + | ``num_block_connections`` | integer | total number of block | + | | | connections in the | + | | | grid | + +--------------------------------+--------------------+-----------------------+ + | ``num_columns`` | integer | number of columns | + +--------------------------------+--------------------+-----------------------+ + | ``num_connections`` | integer | number of connections | + | | | between columns | + +--------------------------------+--------------------+-----------------------+ + | ``num_layers`` | integer | number of layers | + +--------------------------------+--------------------+-----------------------+ + | ``num_nodes`` | integer | number of nodes | + +--------------------------------+--------------------+-----------------------+ + | ``num_underground_blocks`` | integer | number of | + | | | non-atmosphere blocks | + +--------------------------------+--------------------+-----------------------+ + | ``num_wells`` | integer | number of wells | + +--------------------------------+--------------------+-----------------------+ + | ``orphans`` | set | orphaned nodes (nodes | + | | | not belonging to any | + | | | column) | + +--------------------------------+--------------------+-----------------------+ + | ``permeability_angle`` | float | rotation angle | + | | | (degrees | + | | | anticlockwise) of | + | | | first horizontal | + | | | permeability | + | | | direction | + +--------------------------------+--------------------+-----------------------+ + | ``read_function`` | dictionary | dictionary of | + | | | functions used to | + | | | read data from file | + +--------------------------------+--------------------+-----------------------+ + | ``type`` | string | type of geometry | + | | | (currently only | + | | | 'GENER' supported) | + +--------------------------------+--------------------+-----------------------+ + | ``unit_type`` | string | distance unit (blank | + | | | for metres, 'FEET' | + | | | for ft) | + +--------------------------------+--------------------+-----------------------+ + | ``welllist`` | list | wells (by index) | + +--------------------------------+--------------------+-----------------------+ + | ``well`` | dictionary | wells (by name) | + +--------------------------------+--------------------+-----------------------+ + +Grid diagnostics +^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; diagnostics + +A ``mulgrid`` object has some properties (and methods) for evaluating +its integrity. The property ``column_angle_ratio`` returns an +``np.array`` of the 'angle ratio' for each column (the ratio of +largest to smallest interior angles - see +:ref:`column objects `), a measure of skewness. +The ``column_side_ratio`` returns an ``np.array`` of the 'side ratio' +for each column (the ratio of largest to smallest side length), a +measure of elongation. These array properties can be plotted using the +``layer_plot`` method (see :ref:`mulgrid methods `) for a +graphical overview of grid quality. + +There is also a ``connection_angle_cosine`` property, which returns an +``np.array`` of the angle cosine for each connection (the cosine of the +angle between a line joining the nodes in the connection and a line +joining the centres of the blocks in the connection). In general it is +desirable for these lines to be as close to perpendicular as possible, +making the cosines close to zero. + +The ``bad_columns``, ``bad_layers``, ``missing_connections``, +``extra_connections`` and ``orphans`` properties return actual problems +with the grid which should be fixed. A summary of all these problems is +given by the :ref:`check() ` method). + +Blocks at the ground surface that have very small vertical thickness can +sometimes cause problems. The ``min_surface_block_thickness`` property +gives a tuple containing the minimum surface block thickness and the +name of the column in which it occurs. Thin surface blocks of this type +can be eliminated using the ``snap_columns_to_layers()`` method. + +.. _mulgridreadfunctions: + +Functions for reading data from file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; file format +.. index:: MULgraph geometry; reading + +A ``mulgrid`` object has a ``read_function`` property which controls how +data are read from file. This property is a dictionary with six keys: +'d', 'f', 'e', 'g', 's' and 'x', denoting respectively integer, float, +exponential, general, string and blank. Each item in the dictionary is a +function which converts a string from the file on disk into the +appropriate value. For example, ``read_function['f']`` converts a string +to a floating point value. By default, the built-in Python ``float`` +function is used for this (although it is modified slightly so that it +returns ``None`` if the input string is blank). There is a dictionary of +default reading functions included in PyTOUGH, called +``default_read_function``. + +However, the user can specify other functions if needed. In particular, +files produced from Fortran programs sometimes have formatting that is +not readable by the default functions, if some more exotic Fortran +formatting options have been used. For example, a 'd' can also be used +to represent an exponent (like 'e'), or spaces can be included within a +number, or the exponent identifier (e.g. 'e') can be omitted. PyTOUGH +includes a second set of reading functions, called +``fortran_read_function``, for handling Fortran formatting. These are +slightly slower than the default reading functions. + +The reading functions for a ``mulgrid`` object can be specified when the +object is being created, e.g.: + +:: + + geo = mulgrid('geom.dat', read_function = fortran_read_function) + +.. _sec:mulgrid:blockordering: + +Block ordering schemes +^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; block ordering + +By default, the blocks in a TOUGH2 grid created from a ``mulgrid`` +geometry are ordered by layer, from the atmosphere down to the bottom of +the model, with the blocks within each layer ordered by column +(following the ordering of the ``columnlist`` property, which is the +same as the column order specified in the geometry file). + +It is also possible to sort the blocks according to their geometrical +type (8-node hexahedrons and 6-node wedges, corresponding to 4-node or +3-node columns respectively). This is useful for exporting the model to +Waiwera, which uses the PETSc DMPlex mesh representation, which sorts +cells by cell type in this way. + +This can be done by setting the ``block_order`` property of the +geometry. This can be set when the ``mulgrid`` object is created or read +from file, as an optional parameter, e.g.: + +:: + + geo = mulgrid('geom.dat', block_order = 'dmplex') + +It can also be specified after creation. The ``block_order`` property is +a string which can take the value **'layer_column'** for layer/column +block ordering, or **'dmplex'** if the blocks are to be sorted by +geometrical type. It can also take the value ``None`` which gives the +default layer/column ordering. + +:: + + geo.block_order = 'layer_column' + +The block ordering scheme can be stored in the MULgraph geometry file, +via an integer flag in the header (see +:ref:`MULgraph geometry file format `). This flag +is an extension to the original MULgraph geometry file format. If a +``mulgrid`` object is created by reading a file in which this flag is +not present, its ``block_order`` property will be ``None``, in which +case the default layer/column ordering will be used. When a geometry +file is read in, and a block ordering is specified via the +``block_order`` parameter, this will override any block ordering +stored in the file. + +Tilted geometries +^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; tilting + +Non-horizontal (i.e. tilted) geometries can be constructed by setting +the ``mulgrid`` properties ``gdcx`` and ``gdcy`` non-zero. These +properties represent the cosines of the angles the x- and y-axes make +with the gravity vector. By default they are both zero, giving a +horizontal grid. A geometry with ``gdcx`` = 1 can be used to construct a +2-D vertical slice grid with a non-layered structure. When a ``t2grid`` +object is created from a tilted geometry, e.g. using the ``t2grid`` +:ref:`fromgeo() ` method, only the +gravity cosines of the connections are affected (the ``dircos`` property +of each connection). + +Rotating permeability directions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; permeability directions + +It is possible to rotate the permeability principal directions of a +``mulgrid`` object with respect to the coordinate axes- for example, to +align permeabilities with a dominant fault direction- by specifying the +``permeability_angle`` property. When a ``t2grid`` object is created, +e.g. using the ``t2grid`` :ref:`fromgeo() ` +method, this can change the ``direction`` property of each +connection. + +Conversion to and from Layermesh +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; Layermesh conversion +.. index:: Layermesh + +A ``mulgrid`` geometry may be converted to a `Layermesh +`_ mesh simply by accessing +its ``layermesh`` property. Layermesh is a dedicated library for +general layer/column meshes. Its mesh objects have capabilities +similar to those of a ``mulgrid`` object, but it has advantages such +as higher efficiency and a simpler interface. The Layermesh library +must be installed before this property can be used. + +Example: + +:: + + geo = mulgrid('gmymesh.dat') + m = geo.layermesh # m is a Layermesh mesh object + +Conversely, a Layermesh object can be imported into a ``mulgrid`` object +using the :ref:`from_layermesh() ` method. + +.. _mulgridmethods: + +Methods +~~~~~~~ + +The main methods of a ``mulgrid`` object are listed in the following +:ref:`table `. Details of these methods are +given below. + +.. container:: + :name: tb:mulgrid_methods + + .. table:: Methods of a ``mulgrid`` object + + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | **Method** | **Type** | **Description** | + +===================================================================================+==============================+======================+ + | :ref:`add_column ` | – | adds a column to the | + | | | grid | + | | | | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`add_connection ` | – | adds a connection to | + | | | the grid | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`add_layer ` | – | adds a layer to the | + | | | grid | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`add_node ` | – | adds a node to the | + | | | grid | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`add_well ` | – | adds a well to the | + | | | grid | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`block_centre ` | ``np.array`` | block centre | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`block_contains_point ` | Boolean | whether a block | + | | | contains a 3D point | + | | | | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`block_mapping ` | dictionary | mapping from the | + | | | blocks of another | + | | | ``mulgrid`` object | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`block_name ` | string | name of block at | + | | | given layer and | + | | | column | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`block_name_containing_point ` | string | name of block | + | | | containing specified | + | | | point | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`block_surface ` | float | block top elevation | + | | | | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`block_volume ` | float | block volume | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`check ` | Boolean | checks grid for | + | | | errors (and | + | | | optionally fixes | + | | | them) | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`column_boundary_nodes ` | list | nodes around the | + | | | outer boundary of a | + | | | group of columns | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`column_bounds ` | list | bounding rectangle | + | | | around a list of | + | | | columns | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`column_containing_point ` | column | column containing | + | | | specified horizontal | + | | | point | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`column_mapping ` | dictionary | mapping from the | + | | | columns of another | + | | | object | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`column_name ` | string | column name of a | + | | | block name | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`column_neighbour_groups ` | list | groups connected | + | | | columns | + | | | | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`column_quadtree ` | quadtree | quadtree structure | + | | | for searching | + | | | columns | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`column_surface_layer ` | :ref:`layer ` | surface layer for a | + | | | specified column | + | | | | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`column_values ` | tuple | values of a variable | + | | | down a column | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`columns_in_polygon ` | list | columns inside a | + | | | specified polygon | + | | | (or rectangle) | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`connects ` | Boolean | whether the grid has | + | | | a connection between | + | | | two specified | + | | | columns | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`copy_layers_from ` | – | copies layer | + | | | structure from | + | | | another geometry | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`copy_wells_from ` | – | copies wells from | + | | | another geometry | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`decompose_columns ` | – | decomposes columns | + | | | into triangles and | + | | | quadrilaterals | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`delete_column ` | – | deletes a column | + | | | from the grid | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`delete_connection ` | – | deletes a connection | + | | | from the grid | + | | | | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`delete_layer ` | – | deletes a layer from | + | | | the grid | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`delete_node ` | – | deletes a node from | + | | | the grid | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`delete_orphans ` | – | deletes any orphaned | + | | | nodes from the grid | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`delete_orphan_wells ` | – | deletes any orphaned | + | | | wells from the grid | + | | | | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`delete_well ` | – | deletes a well from | + | | | the grid | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`empty ` | – | empties contents of | + | | | grid | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`export_surfer ` | – | exports to various | + | | | files on disk for | + | | | visualization in | + | | | Surfer | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`fit_columns ` | ``np.array`` or | fits scattered data | + | | dictionary | to column centres | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`fit_surface ` | – | fits column surface | + | | | elevations from data | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`from_amesh ` | (:ref:`mulgrid `, | creates Voronoi | + | | dict) | geometry from AMESH | + | | | grid | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`from_gmsh ` | :ref:`mulgrid ` | creates geometry | + | | | from a ``gmsh`` grid | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`from_layermesh ` | :ref:`mulgrid ` | creates geometry | + | | | from a ``Layermesh`` | + | | | grid | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`layer_containing_elevation ` | layer | layer containing | + | | | specified vertical | + | | | elevation | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`layer_mapping ` | dictionary | mapping from the | + | | | layers of another | + | | | object | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`layer_name ` | string | layer name of a | + | | | block name | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`layer_plot ` | – | plots a variable | + | | | over a layer of the | + | | | grid | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`line_plot ` | – | plots a variable | + | | | along an arbitrary | + | | | line through the | + | | | grid | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`line_values ` | tuple | values of a variable | + | | | along an arbitrary | + | | | line through the | + | | | grid | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`meshio_grid ` | tuple | mesh in ``meshio`` | + | | | format | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`minc_array ` | array | values for a | + | | | particular level in | + | | | a MINC grid | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`nodes_in_columns ` | list | nodes in a specified | + | | | list of columns | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`nodes_in_polygon ` | list | nodes inside a | + | | | specified polygon | + | | | (or rectangle) | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`node_nearest_to ` | :ref:`node ` | node nearest to a | + | | | specified point | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`optimize ` | – | adjusts node | + | | | positions to | + | | | optimize grid | + | | | quality | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`polyline_values ` | tuple | values of a variable | + | | | along an arbitrary | + | | | polyline through the | + | | | grid | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`read ` | :ref:`mulgrid ` | reads geometry file | + | | | from disk | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`rectangular ` | :ref:`mulgrid ` | creates rectangular | + | | | grid | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`reduce ` | – | reduces a grid to | + | | | contain only | + | | | specified columns | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`refine ` | – | refines specified | + | | | columns in the grid | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`refine_layers ` | – | refines specified | + | | | layers in the grid | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`rename_column ` | Boolean | renames a column | + | | | | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`rename_layer ` | Boolean | renames a layer | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`rotate ` | – | rotates a grid in | + | | | the horizontal plane | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`slice_plot ` | – | plots a variable | + | | | over a vertical | + | | | slice through the | + | | | grid | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`snap_columns_to_layers ` | – | snaps column | + | | | surfaces to layer | + | | | bottoms | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`snap_columns_to_nearest_layers `| – | snaps column | + | | | surfaces to nearest | + | | | layer elevations | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`split_column ` | Boolean | splits a | + | | | quadrilateral column | + | | | into two triangles | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`translate ` | – | moves a grid by | + | | | simple translation | + | | | in 3D | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`well_values ` | tuple | values of a variable | + | | | down a well | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`write ` | – | writes to geometry | + | | | file on disk | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`write_bna ` | – | writes to Atlas BNA | + | | | file on disk | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`write_exodusii ` | – | writes to ExodusII | + | | | file on disk | + | | | | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`write_mesh ` | – | writes to mesh file | + | | | (various formats) on | + | | | disk | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + | :ref:`write_vtk ` | – | writes to VTK file | + | | | on disk | + | | | | + +-----------------------------------------------------------------------------------+------------------------------+----------------------+ + +.. _sec:mulgrid:add_column: + +``add_column(col)`` +^^^^^^^^^^^^^^^^^^^ + +Adds a :ref:`column ` object ``col`` to the grid. +If a column with the same name already exists, no new column is added. + +---- + +.. _sec:mulgrid:add_connection: + +``add_connection(con)`` +^^^^^^^^^^^^^^^^^^^^^^^ + +Adds a :ref:`connection ` object ``con`` to +the grid. If a connection with the same name already exists, no new +connection is added. + +---- + +.. _sec:mulgrid:add_layer: + +``add_layer(lay)`` +^^^^^^^^^^^^^^^^^^ + +Adds a :ref:`layer ` object ``lay`` to the grid. +If a layer with the same name already exists, no new layer is added. + +---- + +.. _sec:mulgrid:add_node: + +``add_node(n)`` +^^^^^^^^^^^^^^^ + +Adds a :ref:`node ` object ``n`` to the grid. If a +node with the same name already exists, no new node is added. + +---- + +.. _sec:mulgrid:add_well: + +``add_well(w)`` +^^^^^^^^^^^^^^^ + +Adds a :ref:`well ` object ``w`` to the grid. If a +well with the same name already exists, no new well is added. + +---- + +.. _sec:mulgrid:block_contains_point: + +``block_contains_point(blockname, pos)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; searching + +Returns ``True`` if the grid block with the given name contains the 3D +point ``pos``. + +**Parameters:** + +- | **blockname**: string + | The name of the block. + +- | **pos**: ``np.array`` + | 3-element array representing the 3D point. + +---- + +.. _sec:mulgrid:block_centre: + +``block_centre(lay, col)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns the centre of the block corresponding to the given layer and +column. + +The horizontal centre is given by the column centre. The vertical centre +is given by the layer centre, except for surface blocks with column +surface lower than the layer top, in which case it is the midpoint +between the column surface and the layer bottom. (For surface blocks +with column surface higher than the layer top, the vertical centre is +still the layer centre, to give a uniform pressure reference.) + +**Parameters:** + +- | **lay**: :ref:`layer ` or string + | The specified layer or layer name. + +- | **col**: :ref:`column ` or string + | The specified column or column name. + +---- + +.. _sec:mulgrid:block_mapping: + +``block_mapping(geo, column_mapping=False)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; block mappings + +Returns a dictionary mapping each block name in the ``mulgrid`` object +``geo`` to the name of the nearest block in the object's own geometry. +Can optionally also return the associated column mapping. + +**Parameters:** + +- | **geo**: :ref:`mulgrid ` + | The ``mulgrid`` object to create a block mapping from. + +- | **column_mapping**: Boolean + | If ``True``, the column mapping will also be returned (i.e. the + function will return a tuple containing the block mapping and the + column mapping). Default value is ``False``. + +---- + +.. _sec:mulgrid:block_name: + +``block_name(layer_name, column_name, blockmap = {})`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gives the name of the block corresponding to the specified layer and +column names, according to the naming convention of the grid. + +An optional block name mapping can be applied. + +**Parameters:** + +- | **layer_name**, **column_name**: string + | Name of layer and column (the widths of these strings are + determined by the grid's naming convention). + +- | **blockmap**: dictionary + | Dictionary mapping the block names in the geometry to another block + naming system. This dictionary need not contain entries for all + blocks in the geometry- those not included in the mapping will not + be altered. + +---- + +.. _sec:mulgrid:block_name_containing_point: + +``block_name_containing_point(pos, qtree=None, blockmap={})`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; searching +.. index:: MULgraph geometry; quadtree + +Gives the name of the block containing a specified 3-D position in the +grid (returns ``None`` if the point lies outside the grid). + +**Parameters:** + +- | **pos**: ``np.array`` + | Position of point in 3-D + +- | **qtree**: ``quadtree`` + | Quadtree object for fast searching of grid columns (can be + constructed using the :ref:`column_quadtree() ` + method). + +- | **blockmap**: dictionary + | Dictionary mapping the block names in the geometry to another block + naming system. + +---- + +.. _sec:mulgrid:block_surface: + +``block_surface(lay, col)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns the elevation of the top surface of the block corresponding to +the given layer and column. + +**Parameters:** + +- | **lay**: :ref:`layer ` + | The specified layer. + +- | **col**: :ref:`column ` + | The specified column. + +---- + +.. _sec:mulgrid:block_volume: + +``block_volume(lay, col)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns the volume of the block corresponding to the given layer and +column. + +**Parameters:** + +- | **lay**: :ref:`layer ` + | The specified layer. + +- | **col**: :ref:`column ` + | The specified column. + +---- + +.. _sec:mulgrid:check: + +``check(fix=False,silent=False)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; checking + +Checks a grid for errors and optionally fixes them. Errors checked for +are: missing connections, extra connections, orphaned nodes, and columns +and layers that do not contain their own centres. Returns ``True`` if no +errors were found, and ``False`` otherwise. If ``fix`` is ``True``, any +identified problems will be fixed. If ``silent`` is ``True``, there is +no printout (only really useful if ``fix`` is ``True``). + +**Parameters:** + +- | **fix**: Boolean + | Whether to fix any problems identified. + +- | **silent**: Boolean + | Whether to print out feedback or not. + +---- + +.. _sec:mulgrid:column_boundary_nodes: + +``column_boundary_nodes(columns)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; searching + +Returns the nodes around the outer boundary of a list of columns. The +list is ordered, in a counter-clockwise direction. + +**Parameters:** + +- | **columns**: list + | The list of columns for which the boundary is required. + +---- + +.. _sec:mulgrid:column_bounds: + +``column_bounds(columns)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns a bounding rectangle around a list of columns. + +**Parameters:** + +- | **columns**: list + | The list of columns for which the bounds are required. + +---- + +.. _sec:mulgrid:column_containing_point: + +``column_containing_point(pos, columns=None, guess=None, bounds=None, qtree=None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; searching +.. index:: MULgraph geometry; quadtree + +Returns the grid column containing the specified horizontal point. If +``columns`` is specified, only columns in the given list will be +searched. An initial ``guess`` column can optionally be specified. If +``bounds`` is specified, points outside the given polygon will always +return ``None``. A quadtree structure can also be specified to speed up +searching. + +**Parameters:** + +- | **pos**: ``np.array`` + | Horizontal position (*x*, *y*) + +- | **columns**: list of :ref:`column ` (or + ``None``) + | List of columns to search. If ``None``, the entire grid will be + searched. + +- | **guess**: :ref:`column ` (or ``None``) + | Guess of required column. If specified, this column will be tested + first, followed (if necessary) by its neighbours; only if none of + these contain the point will the remaining columns be searched. + This can speed up the process if data follow a sequential pattern + in space, e.g. a grid or lines. + +- | **bounds**: list of ``np.array`` (or ``None``) + | Polygon or rectangle representing e.g. the boundary of the grid: + points outside this polygon will always return ``None``. If the + polygon has only two points, it will be interpreted as a rectangle + [bottom left, top right]. + +- | **qtree**: ``quadtree`` + | A quadtree object for searching the columns of the grid. If many + points are to be located, this option can speed up the search. The + quadtree can be constructed before searching using the + :ref:`column_quadtree() ` + method. + +---- + +.. _sec:mulgrid:column_mapping: + +``column_mapping(geo)`` +^^^^^^^^^^^^^^^^^^^^^^^ + +Returns a dictionary mapping each column name in the ``mulgrid`` object +``geo`` to the name of the nearest column in the object's own geometry. +If the SciPy library is available, a KDTree structure is used to speed +searching. + +**Parameters:** + +- | **geo**: :ref:`mulgrid ` + | The ``mulgrid`` object to create a column mapping from. + +---- + +.. _sec:mulgrid:column_name: + +``column_name(block_name)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gives the name of the column corresponding to the specified block name, +according to the naming convention of the grid. + +**Parameters:** + +- | **block_name**: string + | Block name. + +---- + +.. _sec:mulgrid:column_neighbour_groups: + +``column_neighbour_groups(columns)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +From the given list or set of columns, finds sets of columns that are +connected together, and returns a list of them. + +**Parameters:** + +- | **columns**: list or set + | List or set of columns to group. + +---- + +.. _sec:mulgrid:column_quadtree: + +``column_quadtree(columns=None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; quadtree + +Returns a quadtree structure for fast searching of grid columns, to find +which column a given point lies in. This can then be passed into various +other ``mulgrid`` methods that do such searching, e.g. +:ref:`block_name_containing_point() ` or +:ref:`well_values() `, to speed them +up (useful for large grids). + +The quadtree is an instance of a ``quadtree`` class, defined in the +``mulgrids`` module. + +**Parameters:** + +- | **columns**: list (or ``None``) + | A list of columns in the grid, specifying the search area. This + parameter can be used to further speed searching if it is only + necessary to search columns in a defined area. If ``None``, the + search area is the whole grid (all columns). + +---- + +.. _sec:mulgrid:column_surface_layer: + +``column_surface_layer(col)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns the layer containing the surface elevation of a specified +column. + +**Parameters:** + +- | **col**: :ref:`column ` + | The column for which the surface layer is to be found. + +---- + +.. _sec:mulgrid:column_values: + +``column_values(col, variable, depth = False)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns values of a specified variable down a specified column. The +variable can be a list or ``np.array`` containing a value for every +block in the grid. + +The routine returns a tuple of two arrays (``d``,\ ``v``), the first +(``d``) containing the elevation (or depth from surface if the ``depth`` +parameter is set to ``True``), and the second (``v``) containing the +value of the variable at each block in the column. + +**Parameters:** + +- | **col**: :ref:`column ` or string + | The column for which values are to be found. + +- | **variable**: list (or ``np.array``) + | Values of variable, of length equal to the number of blocks in the + grid. + +- | **depth**: Boolean + | Set to ``True`` to give depths from surface, instead of elevations, + as the first returned array. + +---- + +.. _sec:mulgrid:columns_in_polygon: + +``columns_in_polygon(polygon)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; searching + +Returns a list of all columns with centres inside the specified polygon +or rectangle. + +**Parameters:** + +- | **polygon**: list (of ``np.array``) + | List of points defining the polygon (each point is a two-element + ``np.array``). If the list has only two points, it will be + interpreted as a rectangle [bottom left, top right]. + +---- + +.. _sec:mulgrid:connects: + +``connects(column1, column2)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns ``True`` if the geometry contains a connection connecting the +two specified columns. + +**Parameters:** + +- | **column1, column2**: :ref:`column ` + | Two columns in the geometry. + +---- + +.. _sec:mulgrid:copy_layers_from: + +``copy_layers_from(geo)`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Copies the layer structure from the geometry ``geo`` (deleting any +existing layers first). + +**Parameters:** + +- | **geo**: :ref:`mulgrid ` + | The geometry to copy layers from. + +---- + +.. _sec:mulgrid:copy_wells_from: + +``copy_wells_from(geo)`` +^^^^^^^^^^^^^^^^^^^^^^^^ + +Copies the wells from the geometry ``geo`` (deleting any existing wells +first). + +**Parameters:** + +- | **geo**: :ref:`mulgrid ` + | The geometry to copy wells from. + +---- + +.. _sec:mulgrid:decompose_columns: + +``decompose_columns(columns = [], mapping = False, chars = ascii_lowercase)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Decomposes columns with more than four sides into triangular and +quadrilateral columns. This can be useful when carrying out calculations +on the geometry that rely on finite element methods (e.g. the +``fit_columns()`` method uses it). + +In general, columns are decomposed by adding a node at the column +centroid and forming triangles around it. However, there are special +cases for columns with lower numbers of sides (less than 9) and +'straight' nodes, i.e. nodes on a straight line between their +neighbouring nodes in the column). These make use of simpler +decompositions. + +**Parameters:** + +- | **columns**: list + | List of columns to be decomposed. If the list is empty (the + default), all columns are decomposed. + +- | **mapping**: Boolean + | If ``True``, return a dictionary mapping each original column name + to a list of decomposed columns that replace it. + +- | **chars**: string + | Specifies a string of characters to use when forming new node and + column names. Default is lowercase letters. + +---- + +.. _sec:mulgrid:delete_column: + +``delete_column(colname)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Deletes the column with the specified name from the grid. + +**Parameters:** + +- | **colname**: string + | Name of the column to be deleted. + +---- + +.. _sec:mulgrid:delete_connection: + +``delete_connection(colnames)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Deletes the connection between the specified columns from the grid. + +**Parameters:** + +- | **colnames**: tuple of string + | Tuple of two column names. + +---- + +.. _sec:mulgrid:delete_layer: + +``delete_layer(layername)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Deletes the layer with the specified name from the grid. + +**Parameters:** + +- | **layername**: string + | Name of the layer to be deleted. + +---- + +.. _sec:mulgrid:delete_node: + +``delete_node(nodename)`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Deletes the node with the specified name from the grid. + +**Parameters:** + +- | **nodename**: string + | Name of the node to be deleted. + +---- + +.. _sec:mulgrid:delete_orphans: + +``delete_orphans()`` +^^^^^^^^^^^^^^^^^^^^ + +Deletes any orphaned nodes (those not belonging to any column) from the +grid. + +---- + +.. _sec:mulgrid:delete_orphan_wells: + +``delete_orphan_wells()`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Deletes any orphaned wells (those with wellheads outside the grid). + +---- + +.. _sec:mulgrid:delete_well: + +``delete_well(wellname)`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Deletes the well with the specified name from the grid. + +**Parameters:** + +- | **layername**: string + | Name of the layer to be deleted. + +---- + +.. _sec:mulgrid:empty: + +``empty()`` +^^^^^^^^^^^ + +.. index:: MULgraph geometry; emptying + +Empties the grid of all its nodes, columns, layers, wells and +connections. Other properties are unaffected. + +---- + +.. _sec:mulgrid:export_surfer: + +``export_surfer(filename='', aspect=8.0, left=0.0)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; exporting + +Exports the grid to files on disk useful for visualization in Surfer. +Six files are written out: + +- an Atlas BNA file (``filename.bna``) representing the grid columns + +- a CSV file (``filename_column_names.csv``) containing the column + names + +- a Golden Software blanking file (``filename_layers.bln``) file + representing the grid layers + +- a CSV file (``filename_layer_bottom_elevations.csv``) containing the + bottom elevations of the layers + +- a CSV file (``filename_layer_centres.csv``) containing the elevations + of the centres of the layers + +- a CSV file (``filename_layer_names.csv``) containing the names of the + layers + +**Parameters:** + +- | **filename**: string + | Base name for the exported files. If it is not specified, the + ``filename`` property of the ``mulgrid`` object itself is used + (unless this is also blank, in which case a default name is used), + with its extension removed. + +- | **aspect**: float + | Aspect ratio for the layer plot, so that the width is the total + height of the grid divided by ``aspect`` (default 8.0). + +- | **left**: float + | Coordinate value of the left hand side of the layer plot (default + zero). + +---- + +.. _sec:mulgrid:fit_columns: + +``fit_columns(data, alpha=0.1, beta=0.1, columns=[], min_columns=[], grid_boundary=False, silent=False, output_dict=False)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; fitting data + +Fits scattered data to column centres, using bilinear least-squares +finite element fitting with Sobolev smoothing. Smoothing is useful when +data density is low in some areas of the grid, in which case +least-squares fitting without smoothing can fail (e.g. if there are any +columns which do not contain any data points). + +By default, this method returns an ``np.array`` with length given by the +number of columns to be fitted. Each value in the array represents the +fitted data value at the centre of the corresponding column. If the +``output_dict`` parameter is set to ``True``, a dictionary is returned, +with fitted values indexed by column names. + +**Parameters:** + +- | **data**: ``np.array`` + | Two-dimensional array of data to fit. Each row of the array should + contain the x,y co-ordinates for each data point, followed by the + corresponding data value. Such an array can be conveniently read + from a text file using the ``np.loadtxt()`` method. + +- | **alpha**: float + | Smoothing parameter for first derivatives - increasing its value + results in solutions with lower gradients (but may result in + extrema being smoothed out). + +- | **beta**: float + | Smoothing parameter for second derivatives - increasing its value + results in solutions with lower curvature. + +- | **columns**: list of string or :ref:`column ` + | Columns, or names of columns to be fitted. If empty (the default), + then all columns will be fitted. + +- | **min_columns**: list of string or :ref:`column ` + | Columns, or names of columns for which fitted data will be + determined from the minimum of the fitted nodal values (fitted + values at all other columns are determined from the average of the + fitted nodal values). + +- | **grid_boundary**: Boolean + | If ``True``, test each data point first to see if it lies inside + the boundary polygon of the grid. This can speed up the fitting + process if there are many data points outside the grid, and the + grid has a simple boundary (e.g. a rectangle). In general if there + are many data points outside the grid, it is best to clip the data + set before fitting, particularly if it is to be used more than + once. + +- | **silent**: Boolean + | Set to ``True`` to suppress printing fitting progress. + +- | **output_dict**: Boolean + | Set ``True`` to return results as a dictionary of fitted values + indexed by column names, instead of an array. + +---- + +.. _sec:mulgrid:fit_surface: + +``fit_surface(data, alpha=0.1, beta=0.1, columns=[], min_columns=[], grid_boundary=False, layer_snap=0.0, silent=False)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; fitting surface + +Fits column surface elevations from data, using bilinear least-squares +finite element fitting with Sobolev smoothing (using the +:ref:`fit_columns() ` method). +Smoothing is useful when data density is low in some areas of the grid, +in which case least-squares fitting without smoothing can fail (e.g. if +there are any columns which do not contain any data points). Use the +``layer_snap`` parameter to eliminate surface blocks with very small +thickness. + +**Parameters:** + +- | **data**: ``np.array`` + | Two-dimensional array of data to fit. Each row of the array should + contain the x,y,z values for each data point. Such an array can be + conveniently read from a text file using the ``np.loadtxt()`` + method. + +- | **alpha**: float + | Smoothing parameter for first derivatives - increasing its value + results in solutions with lower gradients (but may result in + extrema being smoothed out). + +- | **beta**: float + | Smoothing parameter for second derivatives - increasing its value + results in solutions with lower curvature. + +- | **columns**: list of string or :ref:`column ` + | Columns, or names of columns to be fitted. If empty (the default), + then all columns will be fitted. + +- | **min_columns**: list of string or :ref:`column ` + | Columns, or names of columns for which elevations will be + determined from the minimum of the fitted nodal elevations + (elevations at all other columns are determined from the average of + the fitted nodal elevations). + +- | **grid_boundary**: Boolean + | If ``True``, test each data point first to see if it lies inside + the boundary polygon of the grid. This can speed up the fitting + process if there are many data points outside the grid, and the + grid has a simple boundary (e.g. a rectangle). In general if there + are many data points outside the grid, it is best to clip the data + set before fitting, particularly if it is to be used more than + once. + +- | **layer_snap**: float + | Smallest desired surface block thickness. Set to a positive value + to prevent columns being assigned surface elevations that are very + close to the bottom of a layer (resulting in very thin surface + blocks). Default value is zero (i.e. no layer snapping). + +- | **silent**: Boolean + | Set to ``True`` to suppress printing fitting progress. + +---- + +.. _sec:mulgrid:from_amesh: + +``from_amesh(input_filename='in', segment_filename='segmt', convention=0, node_tolerance=None, justify='r', chars=ascii_lowercase, spaces=True, block_order=None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; Voronoi + +Returns a ``mulgrid`` object (and a block mapping dictionary) from a +Voronoi mesh previously created by the `AMESH +`_ +utility, or by other software that uses AMESH (e.g. WinGridder or +Steinar). + +The block naming convention for the output ``mulgrid`` object can be +specified via the ``convention`` parameter. Note that in general this +may not be the same as the block naming convention of the original mesh +created by AMESH. In fact, AMESH can create meshes with block naming +conventions that do not correspond to any of the +:ref:`MULgraph conventions `. This is why the +``from_amesh()`` method also returns a block mapping dictionary, which +maps block names in the ``mulgrid`` geometry to the block names in the +original AMESH grid. + +The optional ``justify`` and ``case`` parameters control the formatting +of the character part of the block names. Additionally, the characters +used to form node/column or layer names can be specified using the +``chars`` parameter. (This can be useful for example for grids with +large numbers of nodes and/or columns, for which lowercase letters alone +may not be enough.) + +The ``from_amesh()`` method assumes the original AMESH grid has layers +of constant thickness (i.e. all blocks in each layer of the AMESH input +file have the same specified thickness). Grids with layers of +non-constant thickness cannot be represented by a ``mulgrid`` object and +will cause an exception to be raised. + +**Parameters:** + +- | **input_filename**: string + | Filename for AMESH input file. Default is 'in'. + +- | **segment_filename**: string + | Filename for AMESH output segment file. Default is 'segmt'. + +- | **convention**: integer + | Naming convention for grid columns and layers. + +- | **node_tolerance**: float or ``None`` + | Horizontal tolerance for identifying distinct nodes in the segment + file. If a node is read in with horizontal distance from an + existing node less than the tolerance, then the two nodes are + assumed to be identical. If ``None`` (the default), then the + tolerance is set to 90% of the smallest segment length. If errors + are encountered in identifying nodes belonging to the grid columns, + it may be worth adjusting this parameter. + +- | **justify**: string + | Specify 'r' for the character part of the block names (first three + characters) to be right-justified, 'l' for left-justified. + +- | **chars**: string + | Specify a string of characters to be used to form the character + part of block names. For example, to use both lowercase and + uppercase characters, set ``chars`` to + ``ascii_lowercase + ascii_uppercase``, or to use uppercase letters + only, specify ``ascii_uppercase``. + +- | **spaces**: Boolean + | Specify ``False`` to disallow spaces in character part of block + names. In this case, the first element of the ``chars`` parameter + functions like a 'zero' and replaces spaces. + +- | **block_order**: string or ``None`` + | Specify ``None`` or 'layer_column' for default block ordering by + layer and column, starting from the atmosphere. Specify 'dmplex' to + order blocks by geometrical type (8-node hexahedrons first followed + by 6-node wedges) as in PETSc DMPlex meshes. + +---- + +.. _sec:mulgrid:from_gmsh: + +``from_gmsh(filename, layers, convention=0, atmosphere_type=2, top_elevation=0, justify = 'r', chars = ascii_lowercase, spaces=True, block_order=None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; from GMSH + +Imports a 2-D `Gmsh `_ mesh into a geometry +object. The horizontal structure of the geometry object is created +from the Gmsh mesh, while the layer structure is specified via the +``layers`` parameter, a list of layer thicknesses. The elevation of +the top surface can also be specified, as well as the naming +convention and atmosphere type. + +**Parameters:** + +- | **filename**: string + | Name of the Gmsh mesh file. + +- | **layers**: list + | List of floats containing the desired layer thicknesses. + +- | **convention**: integer + | Naming convention for grid columns and layers. + +- | **atmosphere_type**: integer + | Type of atmosphere. + +- | **top_elevation**: float + | Elevation of the top surface of the model (default is zero). + +- | **justify**: string + | Specify 'r' for the character part of the block names (first three + characters) to be right-justified, 'l' for left-justified. + +- | **chars**: string + | Specifies a string of characters to use when forming the character + part of block names. Default is lowercase letters. + +- | **spaces**: Boolean + | Specify ``False`` to disallow spaces in character part of block + names. In this case, the first element of the ``chars`` parameter + functions like a 'zero' and replaces spaces. + +- | **block_order**: string or ``None`` + | Specify ``None`` or 'layer_column' for default block ordering by + layer and column, starting from the atmosphere. Specify 'dmplex' to + order blocks by geometrical type (8-node hexahedrons first followed + by 6-node wedges) as in PETSc DMPlex meshes. + +---- + +.. _sec:mulgrid:from_layermesh: + +``from_layermesh(mesh, convention=0, atmosphere_type=2, justify='r',  chars=ascii_lowercase, spaces=True, block_order=None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; from Layermesh + +Imports a `Layermesh `_ object +into a geometry object. + +**Parameters:** + +- | **mesh**: ``layermesh`` + | Layermesh object to import. + +- | **convention**: integer + | Naming convention for grid columns and layers. + +- | **atmosphere_type**: integer + | Type of atmosphere. + +- | **justify**: string + | Specify 'r' for the character part of the block names (first three + characters) to be right-justified, 'l' for left-justified. + +- | **chars**: string + | Specifies a string of characters to use when forming the character + part of block names. Default is lowercase letters. + +- | **spaces**: Boolean + | Specify ``False`` to disallow spaces in character part of block + names. In this case, the first element of the ``chars`` parameter + functions like a 'zero' and replaces spaces. + +- | **block_order**: string or ``None`` + | Specify ``None`` or 'layer_column' for default block ordering by + layer and column, starting from the atmosphere. Specify 'dmplex' to + order blocks by geometrical type (8-node hexahedrons first followed + by 6-node wedges) as in PETSc DMPlex meshes. + +---- + +.. _sec:mulgrid:layer_containing_elevation: + +``layer_containing_elevation(elevation)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; searching + +Returns the grid layer containing the specified vertical elevation. + +**Parameters:** + +- | **elevation**: float + | Vertical elevation. + +---- + +.. _sec:mulgrid:layer_mapping: + +``layer_mapping(geo)`` +^^^^^^^^^^^^^^^^^^^^^^ + +Returns a dictionary mapping each layer name in the ``mulgrid`` object +``geo`` to the name of the nearest layer in the object's own geometry. +(Note: this mapping takes no account of the grid surface, which may +alter which layer is nearest in a given column.) + +**Parameters:** + +- | **geo**: :ref:`mulgrid ` + | The ``mulgrid`` object to create a layer mapping from. + +---- + +.. _sec:mulgrid:layer_name: + +``layer_name(block_name)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gives the name of the layer corresponding to the specified block name, +according to the naming convention of the grid. + +**Parameters:** + +- | **block_name**: string + | Block name. + +---- + +.. _sec:mulgrid:layer_plot: + +``layer_plot(layer, variable=None, variable_name=None, unit=None, column_names=None, node_names=None, column_centres=None, nodes=None, colourmap=None, linewidth=0.2, linecolour='black', aspect='equal', plt=None, subplot=111, title=None, xlabel='x (m)', ylabel='y (m)', contours=False, contour_label_format='%3.0f', contour_grid_divisions=(100,100), connections=None, colourbar_limits=None, plot_limits=None, wells=None, well_names=True, hide_wells_outside=True, wellcolour='blue', welllinewidth=1.0, wellname_bottom=True, rocktypes=None, allrocks=False, rockgroup=None, flow=None, grid=None, flux_matrix=None, flow_variable_name=None, flow_unit=None, flow_scale=None, flow_scale_pos=(0.5, 0.02), flow_arrow_width=None, connection_flows=False, blockmap = {}, block_names=None``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; plotting + +Plots a variable over a layer of the grid, using the ``matplotlib`` +plotting library. The required layer can be specified by name or as an +elevation (in which case the routine will find the corresponding layer). +Specifying the layer as ``None`` gives a plot over the ground surface of +the geometry (i.e. the surface layer for each column). + +The variable can be a list or ``np.array`` containing a value for every +block (or column) in the grid, in the order given by the +``block_name_list`` property of the geometry. If no variable is +specified, only the grid in the layer is plotted, without shading. If +the variable contains a value for each column in the grid, these values +are extended down each column to fill the entire grid. + +The name and units of the variable can optionally be specified, and the +names of the columns and nodes can also optionally be displayed on the +plot, as well as the column centres (represented by crosses). The colour +map and limits of the variable shading, the line width of the grid +columns and the aspect ratio of the plot can also be set, as can the +title and x- and y-axis labels, and the plot limits. + +When a variable is plotted over the grid, contours at specified levels +can also be drawn, and optionally labelled with their values. + +Well tracks can also optionally be plotted. Each well is drawn as a line +following the well track, with the well name at the bottom (or +optionally the top) of the well. For surface plots (``layer`` = +``None``), wells are drawn with solid lines; otherwise, wells are drawn +with dotted lines except where they pass through the specified layer, +where they are drawn with solid lines. + +Rock types can be shown on the layer plot by specifying a +:ref:`t2grid ` object as the ``rocktypes`` parameter. It is +possible to group similar rock types (e.g. those in the same geological +formation but with slightly different permeabilities) to simplify the +plot if there are a lot of rock types. + +Flows can be shown on the layer by specifying an array of connection +flow values (e.g mass flow) as the ``flow`` parameter. Flows will then +be drawn on the slice by arrows at the block centres, each representing +the average flux (flow per unit area) over the block, projected onto the +layer. (For example, connection values of mass flow in kg/s will be +represented as block-average mass fluxes in kg/:math:`m^2`/s.) +Alternatively, flows through the connection faces can be plotted by +setting the ``connection_flows`` parameter to ``True``. + +**Parameters:** + +- | **layer**: :ref:`layer `, string, integer, + float or ``None`` + | Layer or name (string) of layer to plot, or elevation (float or + integer). Specifying ``None`` gives a surface plot. + +- | **variable**: list (or ``np.array``) + | Variable to be plotted, of length equal to the number of blocks or + columns in the grid (or ``None`` just to plot the grid). + +- | **variable_name**: string + | Name of the variable (as it will appear on the scale of the plot). + +- | **unit**: string + | Units of the variable (as it will appear on the scale of the plot). + +- | **column_names**: Boolean or list + | Set to ``True`` if column names are to be indicated on the plot, or + to a list of names of columns to be named. + +- | **node_names**: Boolean or list + | Set to ``True`` if node names are to be indicated on the plot, or + to a list of names of nodes to be named. + +- | **column_centres**: Boolean or list + | Set to ``True`` if column centres are to be indicated on the plot + (as crosses), or to a list of names of columns to be indicated. + +- | **nodes**: Boolean or list + | Set to ``True`` if nodes are to be indicated on the plot (as + crosses), or to a list of names of nodes to be indicated. + +- | **colourmap**: string + | Name of ``matplotlib`` colour map to use for shading the variable. + +- | **linewidth**: float + | Line width to use for drawing the grid. + +- | **linecolour**: string + | Line colour to use for drawing the grid. + +- | **aspect**: string + | Aspect ratio to use for drawing the grid (default is 'equal' (i.e. + 1:1). + +- | **plt**: ``matplotlib.pyplot`` instance + | An instance of the ``matplotlib.pyplot`` library, imported in the + calling script using e.g. ``import matplotlib.pyplot as plt``. + +- | **subplot**: integer + | Subplot number for multi-plots, e.g. set 223 to draw the third plot + in a 2-by-2 multiplot (default is 111). + +- | **title**: string + | Plot title. If set to ``None`` (the default value), a title will be + constructed from the other plot parameters. Set to for no title. + +- | **xlabel**: string + | x axis label (default is 'x (m)'). + +- | **ylabel**: string + | y axis label (default is 'y (m)'). + +- | **contours**: Boolean, list or ``np.array`` + | Set to ``True`` or to a list or array of contour values to draw + contours on the plot (default ``False``). + +- | **contour_label_format**: string + | Format string for contour labels (default '%3.0f'). + +- | **contour_grid_divisions**: tuple (of integer) + | Number of divisions in the x- and y-directions in the regular grid + superimposed on the model grid, and used to produce the contours + (default (100,100)). + +- | **connections**: float (or ``None``) + | Set non-zero to plot connections in the grid, shaded by absolute + value of the connection angle cosine. The value specifies the lower + cut-off value, above which connections will be plotted. Connections + are shaded in greyscale from white (0.0) to black (1.0). This can + be used to check orthogonality of grid connections, as less + orthogonal connections (with larger angle cosine) will show up + darker on the plot. If set to ``None``, no connections will be + plotted. + +- | **colourbar_limits**: tuple, list, ``np.array`` (or ``None``) + | Specify a two-element tuple, list or ``np.array`` to set the limits + of the colour scale. Default (``None``) will auto-scale. + +- | **plot_limits**: tuple or list (or ``None``) + | Specify a two-element tuple (or list) of plot axis ranges, each + itself being a tuple (or list) of minimum and maximum values, i.e. + ((xmin,xmax),(ymin,ymax)). Default is ``False`` which will + auto-scale. + +- | **wells**: Boolean or list (or ``None``) + | Specify ``True`` to plot all well tracks, ``False`` or ``None`` not + to plot them, or a list of wells or well names to specify only + particular wells. + +- | **well_names**: Boolean or list (or ``None``) + | Specify ``True`` to label each well with its name , ``False`` or + ``None`` not to label them, or a list of wells or well names to + label only particular wells. + +- | **hide_wells_outside**: Boolean + | Set to ``True`` if wells that do not intersect the specified layer + are to be hidden. + +- | **wellcolour**: string + | Colour to use for drawing the wells. + +- | **welllinewidth**: float + | Line width for drawing the wells. + +- | **wellname_bottom**: Boolean + | Set to ``False`` to label wells at the wellhead rather than the + bottom. + +- | **rocktypes**: :ref:`t2grid ` (or ``None``) + | To plot rock types, specify a ``t2grid`` object containing rock + types for the grid. If ``None``, no rock types will be plotted. + +- | **allrocks**: Boolean + | If ``False`` (the default), only rock types present on the + specified layer will be shown in the colour bar; others will be + omitted. If ``True``, all rocks present in the model grid will be + shown on the colour bar, regardless of whether they appear in the + specified layer. + +- | **rockgroup**: tuple, list, string (or ``None``) + | To group similar rock types into one colour, specify a tuple or + list of integers, representing the significant characters of the + rock type names. For example, to group rock types having the same + first two characters, specify (0,1). Alternatively, specify a + 5-character string mask containing asterisks in positions that are + not significant, and any other characters in the significant + positions (e.g. '++**\*'). + +- | **flow**: ``np.array`` (or ``None``) + | To plot flows, specify an array of connection flow values (one + floating point value for each connection in the grid). These may + for example be extracted from the columns of the connection table + in a :ref:`t2listing ` object. + +- | **grid**: :ref:`t2grid ` (or ``None``) + | Specify a ``t2grid`` object associated with the grid, to be used to + calculate the 'flux matrix' which converts the connection flow + values to block-average fluxes. If this is not specified (and + neither is the ``flux_matrix`` parameter), then a ``t2grid`` object + will be created internally. + +- | **flux_matrix**: ``scipy.sparse.lil_matrix`` (or ``None``) + | A sparse matrix used to convert the connection flow values to + block-average fluxes. Such a matrix can be created using the + :ref:`flux_matrix() ` method of + a ``t2grid`` object and an appropriate ``mulgrid`` object. If no + flux matrix is specified, one will be created internally. This can + be time-consuming for large grids, so for multiple flow plots it is + faster to pre-calculate a flux matrix in your script and pass it + via this parameter. If this parameter is specified, there is no + need also to specify the ``grid`` parameter. + +- | **flow_variable_name**: string (or ``None``) + | Name of the flow variable (as it will appear on the scale of the + plot). + +- | **flow_unit**: string (or ``None``) + | Units of the flow variable (as it will appear on the scale of the + plot, divided by area). + +- | **flow_scale**: string (or ``None``) + | Length of flow scale arrow. If not specified, this will be + calculated. + +- | **flow_scale_pos**: tuple + | Position of the flow scale on the plot, in units of dimensionless + plot size. The default (0.5, 0.02) draws the flow scale in the + horizontal centre of the plot, slightly above the bottom axis. If + you want the flow scale below the bottom axis (so it doesn't get + mixed up with the actual flow arrows), specify this parameter with + a small negative second component, e.g. (0.8, -0.1). + +- | **flow_arrow_width**: float (or ``None``) + | Width of the flow arrows, in units of dimensionless plot width. If + not specified, this will be calculated internally. + +- | **connection_flows**: Boolean + | Set to ``True`` to plot flows through connection faces, rather than + block-averaged fluxes. In this case, usually the ``grid`` parameter + should also be specified (but not ``flux_matrix``), otherwise a + grid will be calculated internally. + +- | **blockmap**: dictionary + | Dictionary mapping the block names in the geometry to another block + naming system. This has an effect only on the block names displayed + on the plot via the ``block_names`` parameter, and on the rock + types displayed. Note that if a mapping is used, then the + ``block_names`` list should contain mapped block names. + +- | **block_names**: Boolean or list + | Set to ``True`` if block names are to be indicated on the plot, or + to a list of names of blocks to be named. + +**Example:** + +:: + + geo.layer_plot(-500., t, 'Temperature', '$\degree$C', contours = np.arange(100,200,25)) + +plots the variable ``t`` at elevation -500 m over the grid, with the +values as Temperature (°C), and with contours drawn from 100°C to +200°C with a contour interval of 25°C. + +---- + +.. _sec:mulgrid:line_plot: + +``line_plot(start=None, end=None, variable, variable_name=None, unit=None, divisions=100, plt=None, subplot=111, title='', xlabel='distance (m)', coordinate=False)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; plotting + +Plots a variable along a line through the grid, using the ``matplotlib`` +plotting library. The line is specified by its start and end points in +3D. The variable can be a list or ``np.array`` containing a value for +every block (or column) in the grid. If the variable contains a value +for each column in the grid, these values are extended down each column +to fill the entire grid. The name and units of the variable can +optionally be specified, as well as the number of divisions the line is +divided into (default 100), the plot title and the axis labels. + +**Parameters:** + +- | **start**, **end**: list, tuple or ``np.array`` + | Start and end point of the line, each of length 3 (``None`` to plot + across the bounds of the grid). + +- | **variable**: list (or ``np.array``) + | Variable to be plotted, of length equal to the number of blocks (or + columns) in the grid. + +- | **variable_name**: string + | Name of the variable (as it will appear on the scale of the plot). + +- | **unit**: string + | Units of the variable (as it will appear on the scale of the plot). + +- | **divisions**: integer + | Number of divisions to divide the line into (default 100). + +- | **plt**: ``matplotlib.pyplot`` instance + | An instance of the ``matplotlib.pyplot`` library, imported in the + calling script using e.g. ``import matplotlib.pyplot as plt``. + +- | **subplot**: integer + | Subplot number for multi-plots, e.g. set 223 to draw the third plot + in a 2-by-2 multiplot (default is 111). + +- | **title**: string + | Plot title. If set to ``None`` (the default value), a title will be + constructed from the other plot parameters. Set to for no title. + +- | **xlabel**: string + | x axis label (default is 'distance (m)'). + +- | **coordinate**: integer or Boolean + | If ``False``, plot against distance along the line, otherwise plot + against specified coordinate (0,1 or 2) values. + +**Example:** + +:: + + geo.line_plot([0.,0.,500.], [1000.,0.,500.], t, 'Temperature', '$\degree$C') + +plots the variable ``t`` along a line from (0,0,500) to (1000,0,500) +through the grid, with the values as Temperature (°C). + +---- + +.. _sec:mulgrid:line_values: + +``line_values(start, end, variable, divisions=100, coordinate=False, qtree=None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns values of a specified variable along an arbitrary line through +the grid. The start and end points of the line (``start`` and ``end``) +are 3-element lists, tuples or ``np.arrays`` specifying points in 3D. +The variable can be a list or ``np.array`` containing a value for every +block in the grid. The number of divisions along the line (default 100) +can be optionally specified. + +The routine returns a tuple of two arrays (*l*,\ *v*), the first (*l*) +containing the distance from the start (or the appropriate coordinate +(0,1, or 2) if ``coordinate`` is specified) for each point along the +line, and the second (*v*) containing the value of the variable at that +point. The value of the variable at any point is the (block average) +value at the block containing the point. + +**Parameters:** + +- | **start**, **end**: list, tuple or ``np.array`` (of length 3) + | Start and end points of the line in 3D. + +- | **variable**: list (or ``np.array``) + | Variable to be plotted, of length equal to the number of blocks in + the grid. + +- | **divisions**: integer + | Number of segments the line is divided up into (default 100). + +- | **coordinate**: integer or Boolean + | If ``False``, return distance along the line in first array, + otherwise return specified coordinate (0,1 or 2) values. + +- | **qtree**: ``quadtree`` + | Quadtree object for fast searching of grid columns (can be + constructed using the ``column_quadtree()`` method). + +---- + +.. _sec:mulgrid:meshio_grid: + +``meshio_grid(surface_snap = 0.1, dimension = 3, slice = None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; meshio grid + +Returns mesh corresponding to the geometry, in the format used by the +``meshio`` library (https://pypi.python.org/pypi/meshio). This consists +of a two-element tuple: firstly, an ``np.array`` of nodal coordinates, +and secondly a dictionary of element definitions, indexed by number of +nodes in the elements. + +The primary use of this is as an interchange format for input/output of +meshes in different formats. Note that exporting the geometry directly +to a mesh file can also be done using the +:ref:`write_mesh() ` method (which is +just a wrapper for this one). + +**Parameters:** + +- | **surface_snap**: float + | Tolerance for eliminating elements with very small vertical + thickness at the top of the mesh. + +- | **dimension**: integer + | Dimension of the mesh: when set to 3, return the full 3-D mesh. + When set to 2, return a 2-D mesh, corresponding either to the + horizontal mesh only (the default), or a vertical slice mesh if the + ``slice`` parameter is used. + +- | **slice**: list, string, float or ``None`` + | Horizontal line defining the slice for vertical 2-D meshes. This + can be a list of two horizontal (*x*,\ *y*) points (``np.arrays``) + defining the endpoints of the slice line, or string 'x' or 'y' to + specify the *x*- or *y*-axis, or northing (float) through grid + centre. If set to ``None`` (the default) then the horizontal 2-D + mesh is returned. + +---- + +.. _sec:mulgrid:minc_array: + +``minc_array(vals, minc_indices, level=0, outside=0.0)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; MINC arrays + +Returns an array for all blocks in the geometry, with values taken from +the input ``vals`` array, for the specified MINC level. Indexing of MINC +blocks is specified by the ``minc_indices`` array (returned by the +``t2grid`` :ref:`minc() ` method). + +**Parameters:** + +- | **vals**: ``np.array`` + | Array of values over the entire MINC grid, with values for all MINC + levels, obtained e.g. from a column of the element table of a + :ref:`t2listing ` object. + +- | **minc_indices**: ``np.array`` (of integer) + | Rank-2 array containing integer indices for each MINC level, + obtained from the output of the ``t2grid`` + :ref:`minc() ` method. + +- | **level**: integer + | MINC level, 0 being the fracture level and higher levels being the + matrix levels. + +- | **outside**: Boolean or float + | Determines how blocks outside the MINC part of the grid are + handled. If ``True``, include porous medium values outside the MINC + part of the grid. If a float value is given, assign that value + instead. If ``False``, the value zero will be assigned. + +---- + +.. _sec:mulgrid:nodes_in_columns: + +``nodes_in_columns(columns)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; searching + +Returns a list of all nodes in a specified list of columns. + +**Parameters:** + +- | **columns**: list (of :ref:`column `) + | List of columns in which to find nodes. + +---- + +.. _sec:mulgrid:nodes_in_polygon: + +``nodes_in_polygon(polygon)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; searching + +Returns a list of all nodes inside the specified polygon or rectangle. + +**Parameters:** + +- | **polygon**: list (of ``np.array``) + | List of points defining the polygon (each point is a two-element + ``np.array``). If the list has only two points, it will be + interpreted as a rectangle [bottom left, top right]. + +---- + +.. _sec:mulgrid:node_nearest_to: + +``node_nearest_to(point, kdtree=None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; searching + +Returns the node nearest to a specified point. An optional kd-tree +structure can be specified to speed searching - useful if searching for +many points. + +**Parameters:** + +- | **point**: ``np.array``, list or tuple + | Array or list of length 2, specifying the required point in 2-D. + +- | **kdtree**: ``cKDTree`` + | kd-tree structure for searching for nodes. Such a tree can be + constructed using the ``node_kdtree`` property of a ``mulgrid`` + object. You will need the ``scipy`` library installed before you + can use this property. + +---- + +.. _sec:mulgrid:optimize: + +``optimize(nodenames=None, connection_angle_weight=1.0, column_aspect_weight=0.0, column_skewness_weight=0.0, pest=False)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; optimizing + +Adjusts positions of the specified nodes to optimize grid quality. If no +nodes are specified, all node positions are optimized. Grid quality can +be defined as a combination of connection angle cosine, column aspect +ratio and column skewness. Increasing the weight for any of these +increases its importance in the evaluation of grid quality. + +Note that an error will result if the connection angle weight and either +of the other two weights is set to zero - in this case there are not +enough constraints to fit the parameters. + +If the ``pest`` parameter is set to ``True``, the `PEST +`_ parameter estimation software is used +to carry out the optimzation (this obviously requires that PEST is +installed on your machine). Otherwise, the ``leastsq`` routine in the +``scipy`` Python library is used. PEST seems to be more robust in some +cases, and often gives better results when nodes on the boundary of +the grid are included in the optimization. However, when ``leastsq`` +does work satisfactorily, it is generally faster (mainly because PEST +has to read the geometry from disk and write it out again each time +the grid quality is evaluated during the optimization). If PEST is +used, a variety of intermediate files (named ``pestmesh.*``) will be +written to the working directory, including the PEST run record file +(``pestmesh.rec``) which contains a detailed record of the +optimization process. + +**Parameters:** + +- | **nodenames**: list of string + | List of names of nodes to optimize. If not specified, all nodes in + the grid are optimized. + +- | **connection_angle_weight**: float + | Weighting to be given to connection angle cosines. A higher value + will place greater priority on making connections perpendicular to + the column sides. + +- | **column_aspect_weight**: float + | Weighting to be given to column aspect ratios. A higher value will + place greater priority on making column side ratios closer to 1.0. + +- | **column_skewness_weight**: float + | Weighting to be given to column skewness. A higher value will place + greater priority on making column angle ratios closer to 1.0. + +- | **pest**: Boolean + | Set ``True`` to use the PEST parameter estimation software to + perform the optimization. + +---- + +.. _sec:mulgrid:polyline_values: + +``polyline_values(polyline, variable, divisions=100, coordinate=False, qtree=None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns values of a specified variable along an arbitrary polyline +through the grid, defined as a list of 3-element lists or ``np.arrays`` +specifying points in 3D. The variable can be a list or ``np.array`` +containing a value for every block in the grid. The number of divisions +along the line (default 100) can be optionally specified. + +The routine returns a tuple of two arrays (``l``,\ ``v``), the first +(``l``) containing the distance from the start (or the appropriate +coordinate (0, 1, or 2) if ``coordinate`` is specified) for each point +along the polyline, and the second (``v``) containing the value of the +variable at that point. The value of the variable at any point is the +(block average) value at the block containing the point. + +**Parameters:** + +- | **polyline**: list of 3-element lists or ``np.arrays`` + | Polyline points in 3D. + +- | **variable**: list (or ``np.array``) + | Variable to be plotted, of length equal to the number of blocks in + the grid. + +- | **divisions**: integer + | Number of segments the line is divided up into (default 100). + +- | **coordinate**: integer or Boolean + | If ``False``, return distance along the line in first array, + otherwise return specified coordinate (0, 1 or 2) values. + +- | **qtree**: ``quadtree`` + | Quadtree object for fast searching of grid columns (can be + constructed using the :ref:`column_quadtree() ` + method). + +---- + +.. _sec:mulgrid:read: + +``read(filename)`` +^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; reading + +Reads a ``mulgrid`` object from a MULgraph geometry file on disk. + +**Parameters:** + +- | **filename**: string + | Name of the MULgraph geometry file to be read. + +**Example:** + +:: + + geo = mulgrid().read(filename) + +creates a ``mulgrid`` object and reads its contents from file +``filename``. This can be done more simply just by passing the filename +into the ``mulgrid`` creation command: + +:: + + geo = mulgrid(filename) + +---- + +.. _sec:mulgrid:rectangular: + +``rectangular(xblocks, yblocks, zblocks, convention=0, atmos_type=2, origin=[0,0,0], justify='r', case=None, chars=ascii_lowercase, spaces=True, block_order=None``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; rectangular + +Gives a ``mulgrid`` geometry object a rectangular grid structure. The +grid sizes in the *x*, *y* and *z* directions can be non-uniform, and +the grid column and layer naming convention, atmosphere type and origin +can be specified. The optional ``justify`` and ``case`` parameters +control the formatting of the character part of the block names. +Additionally, the characters used to form node/column or layer names can +be specified using the ``chars`` parameter. (This can be useful for +example for grids with large numbers of nodes and/or columns, for which +lowercase letters alone may not be enough.) + +Note that it is also possible to reverse-engineer a rectangular geometry +from an existing TOUGH2 data file or ``t2grid`` object, using the +:ref:`rectgeo() ` method. + +**Parameters:** + +- | **xblocks**, **yblocks**, **zblocks**: list, tuple or ``np.array`` + | Lists (or arrays) of block sizes (float) in the *x*, *y* and *z* + directions. + +- | **convention**: integer + | Naming convention for grid columns and layers. + +- | **atmos_type**: integer + | Type of atmosphere. + +- | **origin**: list (or ``np.array``) + | Origin of the grid (of length 3). + +- | **justify**: string + | Specify 'r' for the character part of the block names (first three + characters) to be right-justified, 'l' for left-justified. + +- | **case**: string + | Specify 'l' for the character part of the block names (first three + characters) to be lower case, 'u' for upper case. Now deprecated - + using the ``chars`` parameter is more flexible. + +- | **chars**: string + | Specify a string of characters to be used to form the character + part of block names. For example, to use both lowercase and + uppercase characters, set ``chars`` to + ``ascii_lowercase + ascii_uppercase``, or to use uppercase letters + only, specify ``ascii_uppercase``. + +- | **spaces**: Boolean + | Specify ``False`` to disallow spaces in character part of block + names. In this case, the first element of the ``chars`` parameter + functions like a 'zero' and replaces spaces. + +- | **block_order**: string or ``None`` + | Specify ``None`` or 'layer_column' for default block ordering by + layer and column, starting from the atmosphere. Specify 'dmplex' to + order blocks by geometrical type (8-node hexahedrons first followed + by 6-node wedges) as in PETSc DMPlex meshes. + +**Example:** + +:: + + geo = mulgrid().rectangular([1000]*10, [500]*20, [100]*5+[200]*10, origin=[0,0,2500]) + +creates a ``mulgrid`` object called ``geo``, and fills it with a +rectangular grid of 10 blocks of size 1000 m in the *x*-direction, 20 +blocks of size 500 m in the *x*-direction, 5 layers at the top of +thickness 100 m and 10 layers underneath of thickness 200 m, and with +origin (0,0,2500) m. The grid will have the default naming convention +(0) and atmosphere type (2). + +---- + +.. _sec:mulgrid:reduce: + +``reduce(columns)`` +^^^^^^^^^^^^^^^^^^^ + +Reduces a grid so that it contains only the specified list of columns +(or columns with specified names). + +**Parameters:** + +- | **columns**: list + | List of required columns or column names. + +---- + +.. _sec:mulgrid:refine: + +``refine(columns=[], bisect=False, bisect_edge_columns=[], chars = ascii_lowercase, spaces=True)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; refining + +Refines the specified columns in the grid. Appropriate transition +columns are created around the refined region. If no columns are +specified, all columns are refined. All columns in the region to be +refined (and in the transition region) must be either triangular or +quadrilateral. Each column in split into four, unless the ``bisect`` +parameter is ``True``, in which case each column in split into two. If +``bisect`` is 'x' or 'y', columns are split in the closest direction to +the axis specified; or if ``bisect`` is ``True``, between its longest +sides. + +The ``bisect_edge_columns`` parameter can be used to give more desirable +column shapes in the transition region, if the original columns +occupying the transition region have large aspect ratios. By default, +these will become even worse when they are triangulated to form the +transition columns, if they are connected to the refinement region by +their shorter sides. Including them in ``bisect_edge_columns`` means +they will be bisected (parallel to the edge of the refinement region) +before the refinement is carried out, which should improve the aspect +ratios of the transition columns. + +**Note**: TOUGH2 implicitly assumes that the connections in its finite +volume grids are orthogonal, i.e. the line joining the centres of two +connected blocks should be perpendicular to the connecting face. The +triangular transition columns generated by the ``refine()`` method +generally give rise to connections that are not orthogonal. However, +they can be modified and made as orthogonal as possible using the +:ref:`optimize() ` method. + +**Parameters:** + +- | **columns**: list + | List of columns or column names to be refined. + +- | **bisect**: Boolean or string + | Set to ``True`` if columns are to be split into two, between their + longest sides, instead of four (the default). Set to 'x' or 'y' to + split columns along the specified axis. + +- | **bisect_edge_columns**: list + | List of columns or column names in the transition region (just + outside the refinement area) to be bisected prior to the + refinement, to improve the aspect ratios of the transition columns. + +- | **chars**: string + | Specifies a string of characters to use when forming the character + part of block names. Default is lowercase letters. + +- | **spaces**: Boolean + | Specify ``False`` to disallow spaces in character part of block + names. In this case, the first element of the ``chars`` parameter + functions like a 'zero' and replaces spaces. + +---- + +.. _sec:mulgrid:refine_layers: + +``refine_layers(layers=[], factor=2, chars = ascii_lowercase, spaces=True)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; refining + +Refines the specified layers in the grid. If no layers are specified, +all layers are refined. Each layer is refined by the specified integer +factor. + +**Parameters:** + +- | **layers**: list + | List of layers or layer names to be refined. + +- | **factor**: integer + | Refinement factor: default is 2, which bisects each layer. + +- | **chars**: string + | Specifies a string of characters to use when forming the character + part of block names. Default is lowercase letters. + +- | **spaces**: Boolean + | Specify ``False`` to disallow spaces in character part of block + names. In this case, the first element of the ``chars`` parameter + functions like a 'zero' and replaces spaces. + +---- + +.. _sec:mulgrid:rename_column: + +``rename_column(oldcolname, newcolname)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Renames a grid column. Returns ``True`` if the column was found and +renamed, or ``False`` if the specified column does not exist. Multiple +columns can be renamed at once by specifying lists of old and new column +names - this is faster than calling the method multiple times, and the +block and connection name lists are updated only once. + +**Parameters:** + +- | **oldcolname**: string or list of strings + | Name(s) of the column(s) to rename. + +- | **newcolname**: string or list of strings + | New name(s) of the column(s). + +---- + +.. _sec:mulgrid:rename_layer: + +``rename_layer(oldlayername, newlayername)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Renames a grid layer. Returns ``True`` if the layer was found and +renamed, or ``False`` if the specified layer does not exist. Multiple +layers can be renamed at once by specifying lists of old and new layer +names - this is faster than calling the method multiple times, and the +block and connection name lists are updated only once. + +**Parameters:** + +- | **oldlayername**: string or list of strings + | Name(s) of the layer(s) to rename. + +- | **newlayername**: string or list of strings + | New name(s) of the layer(s). + +---- + +.. _sec:mulgrid:rotate: + +``rotate(angle, centre=None, wells=False)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; rotating + +Rotates a grid by a specified angle (in degrees) clockwise in the +horizontal plane. Any wells in the grid are also rotated. The centre of +rotation can be optionally specified. If it is not specified, the centre +of the grid is used as the centre of rotation. If the ``wells`` +parameter is ``True``, any wells in the grid are also rotated. + +**Parameters:** + +- | **angle**: float + | Angle (in degrees) to rotate the grid, positive for clockwise, + negative for anti-clockwise. + +- | **centre**: list, tuple or ``np.array`` + | Centre of rotation in the horizontal *x*,\ *y* plane (of length 2). + +- | **wells**: Boolean + | Set ``True`` to rotate wells. + +**Example:** + +:: + + geo.rotate(30) + +rotates the grid ``geo`` clockwise by 30° about its centre in the +horizontal plane. + +---- + +.. _sec:mulgrid:slice_plot: + +``slice_plot(line=None, variable=None, variable_name=None, unit=None, block_names=None, colourmap=None, linewidth=0.2, linecolour='black', aspect='auto', plt=None, subplot=111, title=None, xlabel='', ylabel='elevation (m)', contours=False, contour_label_format='%3.0f', contour_grid_divisions=(100,100), colourbar_limits=None, plot_limits=None, column_axis=False, layer_axis=False, wells=None, well_names=True, hide_wells_outside=False, wellcolour='blue', welllinewidth=1.0, wellname_bottom=False, rocktypes=None, allrocks=False, rockgroup=None, flow=None, grid=None, flux_matrix=None, flow_variable_name=None, flow_unit=None, flow_scale=None, flow_scale_pos=(0.5, 0.02), flow_arrow_width=None, connection_flows=False, blockmap = {})`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; plotting + +Plots a variable over a vertical slice through the grid, using the +``matplotlib`` plotting library. The required slice is specified by a +horizontal line through the grid, defined as either a two-element list +of (*x*,\ *y*) points (``np.arrays``), or as a string 'x' or 'y' which +defines the *x*- or *y*-axes respectively, or as a northing (in degrees) +through the centre of the grid. If no line is specified, the line is +taken to be across the bounds of the grid. For slice plots along the x- +or y-axis, the horizontal coordinate represents the x- or y-coordinate; +for other slice directions it represents distance along the slice line. + +The variable can be a list or ``np.array`` containing a value for every +block (or column) in the grid, in the order given by the +``block_name_list`` property of the geometry. If no variable is +specified, only the grid is plotted, without shading. If the variable +contains a value for each column in the grid, these values are extended +down each column to fill the entire grid. + +The name and units of the variable can optionally be specified, and the +name of each block can also optionally be displayed on the plot. The +colour map and limits of the variable shading, the line width of the +grid columns and the aspect ratio of the plot can also be set, as can +the plot title and x- and z-axis labels, and the plot limits. + +When a variable is plotted over the grid, contours at specified levels +can also be drawn, and optionally labelled with their values. + +Well tracks can also optionally be plotted. Each well is drawn as a line +following the well track, with the well name at the top (or optionally +the bottom) of the well. If ``hide_wells_outside`` is specified as a +floating point number, wells that do not pass within the specified +distance from the slice line are not shown. Well tracks are shown as +solid lines over sections within the specified distance from the slice +line, and dotted lines otherwise. + +Rock types can be shown on the slice plot by specifying a ``t2grid`` +object as the ``rocktypes`` parameter. It is possible to group similar +rock types (e.g. those in the same geological formation but with +slightly different permeabilities) to simplify the plot if there are a +lot of rock types. + +Flows can be shown on the slice by specifying an array of connection +flow values (e.g mass flow) as the ``flow`` parameter. Flows will then +be drawn on the slice by arrows at the block centres, each representing +the average flux (flow per unit area) over the block, projected onto the +slice. (For example, connection values of mass flow in kg/s will be +represented as block-average mass fluxes in kg/:math:`m^2`/s.) +Alternatively, flows through the connection faces can be plotted by +setting the ``connection_flows`` parameter to ``True``. + +**Parameters:** + +- | **line**: list, string or float + | List of two horizontal (*x*,\ *y*) points (``np.arrays``) defining + the endpoints of the line, or string 'x' or 'y' to specify the *x*- + or *y*-axis, or northing (float) through grid centre. + +- | **variable**: list (or ``np.array``) + | Variable to be plotted, of length equal to the number of blocks (or + columns) in the grid (or ``None`` just to plot the grid). + +- | **variable_name**: string + | Name of the variable (as it will appear on the scale of the plot). + +- | **unit**: string + | Units of the variable (as it will appear on the scale of the plot). + +- | **block_names**: Boolean or list + | Set to ``True`` if block names are to be indicated on the plot, or + to a list of names of blocks to be named. + +- | **colourmap**: string + | Name of ``matplotlib`` colour map to use for shading the variable. + +- | **linewidth**: float + | Line width to use for drawing the grid. + +- | **linecolour**: string + | Line colour to use for drawing the grid. + +- | **aspect**: string + | Aspect ratio to use for drawing the grid (default is 'auto'). + +- | **plt**: ``matplotlib.pyplot`` instance + | An instance of the ``matplotlib.pyplot`` library, imported in the + calling script using e.g. ``import matplotlib.pyplot as plt``. + +- | **subplot**: integer + | Subplot number for multi-plots, e.g. set 223 to draw the third plot + in a 2-by-2 multiplot (default is 111). + +- | **title**: string + | Plot title. If set to ``None`` (the default value), a title will be + constructed from the other plot parameters. Set to for no title. + +- | **xlabel**: string + | x axis label. If set to ``None`` (the default value), a label will + be constructed according to the slice orientation- either 'x (m)', + 'y (m)' or 'distance (m)' as appropriate. + +- | **ylabel**: string + | y axis label (default is 'elevation (m)'). + +- | **contours**: Boolean, list or ``np.array`` + | Set to ``True`` or to a list or array of contour values to draw + contours on the plot (default ``False``). + +- | **contour_label_format**: string + | Format string for contour labels (default '%3.0f'). + +- | **contour_grid_divisions**: tuple (of integer) + | Number of divisions in the x- and z-directions in the regular grid + superimposed on the slice, and used to produce the contours + (default (100,100)). + +- | **colourbar_limits**: tuple, list, ``np.array`` (or ``None``) + | Specify a two-element tuple, list or ``np.array`` to set the limits + of the colour scale. Default (``None``) will auto-scale. + +- | **plot_limits**: tuple or list (or ``None``) + | Specify a two-element tuple (or list) of plot axis ranges, each + itself being a tuple (or list) of minimum and maximum values, i.e. + ((xmin,xmax),(zmin,zmax)). Default is ``False`` which will + auto-scale. + +- | **column_axis**: Boolean + | If ``True``, show column names instead of coordinates on the + horizontal axis. + +- | **layer_axis**: Boolean + | If ``True``, show layer names instead of coordinates on the + vertical axis. + +- | **wells**: Boolean or list (or ``None``) + | Specify ``True`` to plot all well tracks, ``False`` or ``None`` not + to plot them, or a list of wells or well names to specify only + particular wells. + +- | **well_names**: Boolean or list (or ``None``) + | Specify ``True`` to label each well with its name , ``False`` or + ``None`` not to label them, or a list of wells or well names to + label only particular wells. + +- | **hide_wells_outside**: ``False`` or float + | Specify distance as a floating point number to hide wells further + from the slice line than the specified distance. + +- | **wellcolour**: string + | Colour to use for drawing the wells. + +- | **welllinewidth**: float + | Line width for drawing the wells. + +- | **wellname_bottom**: Boolean + | Set to ``True`` to label wells at the bottom rather than the + wellhead. + +- | **rocktypes**: :ref:`t2grid ` (or ``None``) + | To plot rock types, specify a ``t2grid`` object containing rock + types for the grid. If ``None``, no rock types will be plotted. + +- | **allrocks**: Boolean + | If ``False`` (the default), only rock types present on the + specified slice will be shown in the colour bar; others will be + omitted. If ``True``, all rocks present in the model grid will be + shown on the colour bar, regardless of whether they appear in the + specified slice. + +- | **rockgroup**: tuple, list, string (or ``None``) + | To group similar rock types into one colour, specify a tuple or + list of integers, representing the significant characters of the + rock type names. For example, to group rock types having the same + first two characters, specify (0,1). Alternatively, specify a + 5-character string mask containing asterisks in positions that are + not significant, and any other characters in the significant + positions (e.g. '++**\*'). + +- | **flow**: ``np.array`` (or ``None``) + | To plot flows, specify an array of connection flow values (one + floating point value for each connection in the grid). These may + for example be extracted from the columns of the connection table + in a ``t2listing`` object. + +- | **grid**: :ref:`t2grid ` (or ``None``) + | Specify a ``t2grid`` object associated with the grid, to be used to + calculate the 'flux matrix' which converts the connection flow + values to block-average fluxes. If this is not specified (and + neither is the ``flux_matrix`` parameter), then a ``t2grid`` object + will be created internally. + +- | **flux_matrix**: ``scipy.sparse.lil_matrix`` (or ``None``) + | A sparse matrix used to convert the connection flow values to + block-average fluxes. Such a matrix can be created using the + ``flux_matrix()`` method of a ``t2grid`` object and an appropriate + ``mulgrid`` object. If no flux matrix is specified, one will be + created internally. This can be time-consuming for large grids, so + for multiple flow plots it is faster to pre-calculate a flux matrix + in your script and pass it via this parameter. If this parameter is + specified, there is no need also to specify the ``grid`` parameter. + +- | **flow_variable_name**: string (or ``None``) + | Name of the flow variable (as it will appear on the scale of the + plot). + +- | **flow_unit**: string (or ``None``) + | Units of the flow variable (as it will appear on the scale of the + plot, divided by area). + +- | **flow_scale**: string (or ``None``) + | Length of flow scale arrow. If not specified, this will be + calculated. + +- | **flow_scale_pos**: tuple + | Position of the flow scale on the plot, in units of dimensionless + plot size. The default (0.5, 0.02) draws the flow scale in the + horizontal centre of the plot, slightly above the bottom axis. If + you want the flow scale below the bottom axis (so it doesn't get + mixed up with the actual flow arrows), specify this parameter with + a small negative second component, e.g. (0.8, -0.1). + +- | **flow_arrow_width**: float (or ``None``) + | Width of the flow arrows, in units of dimensionless plot width. If + not specified, this will be calculated internally. + +- | **connection_flows**: Boolean + | Set to ``True`` to plot flows through connection faces, rather than + block-averaged fluxes. In this case, usually the ``grid`` parameter + should also be specified (but not ``flux_matrix``), otherwise a + grid will be calculated internally. + +- | **blockmap**: dictionary + | Dictionary mapping the block names in the geometry to another block + naming system. This has an effect only on the block names displayed + on the plot via the ``block_names`` parameter, and on the rock + types displayed. Note that if a mapping is used, then the + ``block_names`` list should contain mapped block names. + +**Example:** + +:: + + geo.slice_plot(45., t, 'Temperature', '$\degree$C', contours = [100,200]) + +plots the variable ``t`` through a SW–NE vertical slice (heading 45°) +through the grid, with the values as Temperature (°C) and contours +drawn at 100°C and 200°C. + +:: + + from matplotlib import cm + cmap = cm.get_cmap('jet', 10) + geo.slice_plot(45., t, 'Temperature', '$\degree$C', + colourbar_limits = (0., 250.), colourmap = cmap) + +plots the variable ``t`` again, but with a specified discrete colour +scale with 10 divisions from zero to 250°C. + +---- + +.. _sec:mulgrid:snap_columns_to_layers: + +``snap_columns_to_layers(min_thickness=1.0, columns=[])`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; snapping + +Snaps column surfaces to the bottom of their layers, if the surface +block thickness is smaller than a given value. This can be carried out +over an optional subset of columns in the grid, otherwise over all +columns. + +**Parameters:** + +- | **min_thickness**: float + | Minimum surface block thickness. Blocks with thickness less than + this value will be eliminated by 'snapping' the column surface + elevation to the bottom of the surface layer. Values of + ``min_thickness`` less than or equal to zero will have no effect. + +- | **columns**: list (of :ref:`column ` or + string) + | List of columns to process. If empty (the default), process all + columns. + +---- + +.. _sec:mulgrid:snap_columns_to_nearest_layers: + +``snap_columns_to_nearest_layers(columns=[])`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; snapping + +Snaps column surfaces to the nearest layer elevation (top or bottom). +This can be carried out over an optional subset of columns in the grid, +otherwise over all columns. + +**Parameters:** + +- | **columns**: list (of :ref:`column ` or + string) + | List of columns to process. If empty (the default), process all + columns. + +---- + +.. _sec:mulgrid:split_column: + +``split_column(colname, nodename, chars = ascii_lowercase)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Splits a quadrilateral column with specified name into two triangular +columns. The direction of the split is determined by specifying the name +of one of the splitting nodes. The method returns ``True`` if the split +was carried out successfully. + +**Parameters:** + +- | **colname**: string + | Name of the quadrilateral column to be split. If the column is not + quadrilateral, the method returns ``False`` and nothing is done to + the column. + +- | **nodename**: string + | Name of one of the splitting nodes. The column is split across this + node and the one on the opposite side of the column. If the + specified node is not in the column, the method returns ``False`` + and nothing is done to the column. + +- | **chars**: string + | Specifies a string of characters to use when forming the character + part of block names. Default is lowercase letters. + +---- + +.. _sec:mulgrid:translate: + +``translate(shift, wells=False)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; translating + +Translates a grid by a specified shift in the *x*, *y* and *z* +directions. If the ``wells`` parameter is ``True``, any wells in the +grid are also translated. + +**Parameters:** + +- | **shift**: list, tuple or ``np.array`` + | Distance to shift the grid in the *x*, *y* and *z* directions (of + length 3). + +- | **wells**: Boolean + |  Set ``True`` to translate wells. + +**Example:** + +:: + + geo.translate([10.e3, 0.0, -1000.0]) + +translates the grid ``geo`` by 10 km in the *x* direction and down 1 km +in the *z* direction. + +---- + +.. _sec:mulgrid:well_values: + +``well_values(well_name, variable, divisions=1, elevation=False, deviations=False, qtree=None, extend=False)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns values of a specified variable down a specified well. The +variable can be a list or ``np.array`` containing a value for every +block in the grid. The number of divisions between layer centres or +along each well deviation (default 1) can be optionally specified (this +can be increased to capture detail along a deviation that passes through +several blocks). If ``deviations`` is ``True``, values will be returned +at the nodes of the well track, instead of at grid layer centres. If +``extend`` is ``True``, the well trace is artificially extended to the +bottom of the model. + +The routine returns a tuple of two arrays (``d``,\ ``v``), the first +(``d``) containing the measured depth down the well (or elevation if the +``elevation`` parameter is set to ``True``), and the second (``v``) +containing the value of the variable at each point. The value of the +variable at any point is the (block average) value at the block +containing the point. + +**Parameters:** + +- | **well_name**: string + | Name of the well. + +- | **variable**: list (or ``np.array``) + | Variable to be plotted, of length equal to the number of blocks in + the grid. + +- | **divisions**: integer + | Number of divisions each well deviation is divided up into (default + 1). + +- | **elevation**: Boolean + | Set to ``True`` if elevation rather than measured depth is to be + returned. + +- | **deviations**: Boolean + | Set to ``True`` to return values at deviation nodes, rather than + intersections of layer centres with the well track. + +- | **qtree**: ``quadtree`` + | Quadtree object for fast searching of grid columns (can be + constructed using the :ref:`column_quadtree() ` + method). + +- | **extend**: Boolean + | Set ``True`` to artificially extend the well trace to the bottom of + the model. + +---- + +.. _sec:mulgrid:write: + +``write(filename='')`` +^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; writing + +Writes a ``mulgrid`` object to a MULgraph geometry file on disk. + +**Parameters:** + +- | **filename**: string + | Name of the MULgraph geometry file to be written. If no file name + is specified, the object's own ``filename`` property is used. + +---- + +.. _sec:mulgrid:write_bna: + +``write_bna(filename='')`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; exporting + +Writes a geometry object to an Atlas BNA file on disk, for visualisation +with Surfer or GIS tools. + +**Parameters:** + +- | **filename**: string + | Name of the BNA file to be written. If no file name is specified, + the object's own ``filename`` property is used, with the extension + changed to \*.bna. If the object's ``filename`` property is not + set, the default name 'geometry.bna' is used. + +---- + +.. _sec:mulgrid:write_exodusii: + +``write_exodusii(filename='', arrays=None, blockmap={})`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; exporting + +Writes a ``mulgrid`` object to an ExodusII file on disk, for +visualisation or export to other software. + +This method uses the VTK-Python library, so you will need that installed +on your machine before you can use it. An alternative is to use the +:ref:`write_mesh ` method instead, +which can also write meshes to ExodusII format (as well as others), and +does not need the VTK-Python library (though you will need the +``meshio`` library). + +**Parameters:** + +- | **filename**: string + | Name of the ExodusII file to be written. If no file name is + specified, the object's own ``filename`` property is used, with the + extension changed to \*.exo. If the object's ``filename`` property + is not set, the default name 'geometry.exo' is used. + +- | **arrays**: dictionary or ``None`` + | Data arrays to be included in the ExodusII file. If set to + ``None``, default arrays (block name, layer index, column index, + column area, column elevation, block number and volume) are + included. + +- | **blockmap**: dictionary + | Dictionary mapping the block names in the geometry to another block + naming system. + +---- + +.. _sec:mulgrid:write_mesh: + +``write_mesh(filename, surface_snap = 0.1, dimension = 3, slice = None, file_format = None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; exporting + +Writes a ``mulgrid`` object to a mesh file on disk, with the specific +format determined by the file extension of the specified +filename. This method uses the `meshio +`_ library, which must be +installed on your machine, and supports various mesh output formats +including Dolfin XML, ExodusII, MSH, VTK, XDMF and others. The +``meshio`` library may be installed from PyPI (using e.g. ``pip +install meshio``). + +Note that many of these formats do not support columns with more than +four sides. + +**Parameters:** + +- | **filename**: string + | Name of the mesh file to be written. + +- | **surface_snap**: float + | Tolerance for eliminating elements with very small vertical + thickness at the top of the mesh (3-D meshes only). + +- | **dimension**: integer + | Dimension of the mesh: when set to 3 (the default), write the full + 3-D mesh. When set to 2, write a 2-D mesh, corresponding either to + the horizontal mesh only (the default), or a vertical slice mesh if + the ``slice`` parameter is used. + +- | **slice**: list, string, float or ``None`` + | Horizontal line defining the slice for vertical 2-D meshes. This + can be a list of two horizontal (*x*,\ *y*) points (``np.arrays``) + defining the endpoints of the slice line, or string 'x' or 'y' to + specify the *x*- or *y*-axis, or northing (float) through grid + centre. If set to ``None`` (the default) then the horizontal 2-D + mesh is written. + +- | **file_format**: string or ``None`` + | File format for mesh output. If ``None``, the file format will be + decided from the filename extension (e.g. if the filename is + 'mesh.exo' then the mesh will be written in ExodusII format). See + the ``meshio`` documentation for details. + +---- + +.. _sec:mulgrid:write_vtk: + +``write_vtk(filename='', arrays=None, wells=False, blockmap={}, surface_snap=0.1)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; exporting + +Writes a ``mulgrid`` object to a VTK file on disk, for visualisation +with VTK, Paraview, Mayavi etc. The grid is written as an 'unstructured +grid' VTK object with optional data arrays defined on cells. A separate +VTK file for the wells in the grid can optionally be written. + +**Parameters:** + +- | **filename**: string + | Name of the VTK file to be written. If no file name is specified, + the object's own ``filename`` property is used, with the extension + changed to \*.vtu. If the object's ``filename`` property is not + set, the default name 'geometry.vtu' is used. + +- | **arrays**: dictionary or ``None`` + | Data arrays to be included in the VTK file. If set to ``None``, + default arrays (block name, layer index, column index, column area, + column elevation, block number and volume) are included. + +- | **wells**: Boolean + | If set to ``True``, a separate VTK file is written representing the + wells in the grid. + +- | **blockmap**: dictionary + | Dictionary mapping the block names in the geometry to another block + naming system. + +- | **surface_snap**: float + | Tolerance for specifying how close column surface elevations need + to be before being considered "equal" when constructing surface + nodes. + +---- + +.. _other_mulgrid_objects: + +Other objects (``node``, ``column``, ``layer``, ``connection`` and ``well``) +---------------------------------------------------------------------------- + +A ``mulgrid`` object contains lists of other types of objects: +:ref:`node `, :ref:`column `, +:ref:`layer `, +:ref:`connection ` and +:ref:`well ` objects. These classes are described below. + +.. _nodeobjects: + +``node`` objects +~~~~~~~~~~~~~~~~ + +.. index:: MULgraph geometry; nodes +.. index:: nodes + +A ``node`` object represents a node (i.e. vertex) in a ``mulgrid`` +object. A ``node`` object has three properties: ``name``, which is a +string property containing the name of the node, ``pos`` which is an +``np.array`` with three elements, containing the node's position in 3D, +and ``column`` which is a set of the columns the node belongs to. A +``node`` object does not have any methods. + +A ``node`` object ``n`` can be created for example using the command +``n = node(name,pos)`` where ``name`` is the node name and pos is an +``np.array`` (or list, or tuple) representing the node's position. + +.. _columnobjects: + +``column`` objects +~~~~~~~~~~~~~~~~~~ + +.. index:: MULgraph geometry; columns +.. index:: columns + +A ``column`` object represents a column in a ``mulgrid`` object. The +properties of a ``column`` object are listed in the +:ref:`table ` below. + +.. container:: + :name: tb:column_properties + + .. table:: Properties of a ``column`` object + + +--------------------+--------------+--------------------------------+ + | **Property** | **Type** | **Description** | + +====================+==============+================================+ + | ``angle_ratio`` | float | ratio of largest to smallest | + | | | interior angles | + +--------------------+--------------+--------------------------------+ + | ``area`` | float | horizontal area of the column | + +--------------------+--------------+--------------------------------+ + | ``centre`` | ``np.array`` | horizontal centre of the | + | | | column | + +--------------------+--------------+--------------------------------+ + | ``centroid`` | ``np.array`` | average position of the | + | | | column's vertices | + +--------------------+--------------+--------------------------------+ + | ``connection`` | set | connections the column is in | + +--------------------+--------------+--------------------------------+ + | ``name`` | string | name of the column | + +--------------------+--------------+--------------------------------+ + | ``neighbour`` | set | set of neighbouring columns | + +--------------------+--------------+--------------------------------+ + | ``neighbourlist`` | list | ordered list of neighbouring | + | | | columns | + +--------------------+--------------+--------------------------------+ + | ``node`` | list | list of nodes (vertices) | + | | | belonging to the column | + +--------------------+--------------+--------------------------------+ + | ``num_neighbours`` | integer | number of neighbouring columns | + +--------------------+--------------+--------------------------------+ + | ``num_nodes`` | integer | number of nodes belonging to | + | | | the column | + +--------------------+--------------+--------------------------------+ + | ``num_layers`` | integer | number of layers in the column | + | | | below the ground surface | + +--------------------+--------------+--------------------------------+ + | ``side_ratio`` | float | ratio of largest to smallest | + | | | side length | + +--------------------+--------------+--------------------------------+ + | ``surface`` | float | surface elevation of the | + | | | column (``None`` if not | + | | | specified) | + +--------------------+--------------+--------------------------------+ + +The main properties defining a column are its ``name`` and ``node`` +properties. The ``name`` is specified according to the naming +convention of the ``mulgrid`` object that the column belongs to. The +``node`` property is a list of ``node`` objects (not node names) that +belong to the column. A ``column``\ 's ``neighbour`` property is a set +of other ``columns`` connected to that column via a :ref:`connection +`), and its property is a set of connections the +column is part of. The ``neighbourlist`` property is a list of +neighbouring columns, with each item corresponding to a column edge +(``None`` if the edge is on a grid boundary). A ``column``\ 's +``centroid`` property returns the average of the positions of its +vertices - which is what the ``centre`` property is set to, unless +otherwise specified. + +A ``column`` object has two properties measuring 'grid quality'. The +``angle_ratio`` property returns the ratio of largest to smallest +interior angles in the column. The ``side_ratio`` property returns the +ratio of largest to smallest side lengths (a generalisation of 'aspect +ratio' to columns with any number of sides). Values as close as possible +to 1.0 for both these measures are desirable (their values are both +exactly 1.0 for any regular polygon, e.g. an equilateral triangle or +square). Columns with large angle ratios will be highly skewed, while +those with large side ratios will be typically highly elongated in one +direction. + +A ``column`` object ``col`` can be created for example using the +command: + +:: + + col = column(name, nodes, centre, surface) + +where ``name`` is the column name and ``nodes`` is a list of +:ref:`node ` objects defining the column. The +``centre`` and ``surface`` parameters are optional. + +The methods of a ``column`` object are listed in the :ref:`table +` below. + +.. container:: + :name: tb:column_methods + + .. table:: Methods of a ``column`` object + + +------------------------------------------------------+---------------------------+--------------------------+ + | **Method** | **Type** | **Description** | + +======================================================+===========================+==========================+ + | :ref:`contains_point() ` | Boolean | if column contains point | + | | | | + | | | | + +------------------------------------------------------+---------------------------+--------------------------+ + | :ref:`in_polygon() ` | Boolean | if column centre is | + | | | within a given polygon | + | | | | + +------------------------------------------------------+---------------------------+--------------------------+ + | :ref:`is_against() ` | Boolean | if two columns are | + | | | adjacent | + | | | | + +------------------------------------------------------+---------------------------+--------------------------+ + +---- + +.. _sec:column:contains_point: + +``contains_point(pos)`` +^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; searching + +Returns ``True`` if a 2D point lies inside the column, and ``False`` +otherwise. + +**Parameters:** + +- | **pos**: ``np.array`` + | Horizontal position of the point. + +---- + +.. _sec:column:in_polygon: + +``in_polygon(polygon)`` +^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; searching + +Returns ``true`` if the column centre is inside the specified polygon or +rectangle. + +**Parameters:** + +- | **polygon**: list (of ``np.array``) + | List of points defining the polygon (each point is a two-element + ``np.array``). If the list has only two points, it will be + interpreted as a rectangle [bottom left, top right]. + +---- + +.. _sec:column:is_against: + +``is_against(othercolumn)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns ``true`` if the column is 'against' ``othercolumn`` – that is, +if it shares more than one node with it. + +**Parameters:** + +- | **othercolumn**: ``column``) + | Any other column in the geometry. + +---- + +.. _layerobjects: + +``layer`` objects +~~~~~~~~~~~~~~~~~ + +.. index:: MULgraph geometry; layers +.. index:: layers + +A ``layer`` object represents a layer in a ``mulgrid`` object. The +properties of a ``layer`` object are given in the +:ref:`table ` below. + +.. container:: + :name: tb:layer_properties + + .. table:: Properties of a ``layer`` object + + +-------------+----------+--------------------------------------+ + |**Property** | **Type** | **Description** | + +=============+==========+======================================+ + |``bottom`` | float | elevation of the bottom of the layer | + +-------------+----------+--------------------------------------+ + |``centre`` | float | elevation of the centre of the layer | + +-------------+----------+--------------------------------------+ + |``thickness``| float | layer thickness (top - bottom) | + +-------------+----------+--------------------------------------+ + |``top`` | float | elevation of the top of the layer | + +-------------+----------+--------------------------------------+ + |``name`` | string | name of the layer | + +-------------+----------+--------------------------------------+ + +A ``layer`` object ``lay`` can be created for example using the command: + +:: + + lay = layer(name, bottom, centre, top) + +where ``name`` is the layer name and ``bottom``, ``centre`` and ``top`` +specify the vertical position of the layer. + +The methods of a ``layer`` object are given in the +:ref:`table ` below. + +.. container:: + :name: tb:layer_methods + + .. table:: Methods of a ``layer`` object + + +------------------------------------------------------------+---------------------------+--------------------------+ + |**Method** |**Type** |**Description** | + +============================================================+===========================+==========================+ + |:ref:`contains_elevation() ` |Boolean |if layer contains | + | | |elevation | + | | | | + +------------------------------------------------------------+---------------------------+--------------------------+ + |:ref:`translate() ` |– |translate layer up or down| + | | | | + | | | | + +------------------------------------------------------------+---------------------------+--------------------------+ + +---- + +.. _sec:layer:contains_elevation: + +``contains_elevation(z)`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; searching + +Returns ``True`` if a point at a given elevation lies inside the layer, +and ``False`` otherwise. + +**Parameters:** + +- | **z**: float + | Elevation of the point. + +---- + +.. _sec:layer:translate: + +``translate(shift)`` +^^^^^^^^^^^^^^^^^^^^ + +Translates a layer up or down by a specified distance. + +**Parameters:** + +- | **shift**: float + | Distance to shift the layer (positive for up, negative for down). + +---- + +.. _connectionobjects: + +``connection`` objects +~~~~~~~~~~~~~~~~~~~~~~ + +.. index:: MULgraph geometry; connections +.. index:: connections + +A ``connection`` object represents a connection between ``columns`` in a +``mulgrid`` object. It has three properties: ``column``, which contains +a two-element list of the ``column`` objects making up the connection, +``node``, which contains a two-element list of the ``nodes`` on the face +joining the two columns in the connection, and ``angle_cosine``, which +gives the cosine of the angle between a line joining the nodes in the +connection and a line joining the centres of the two columns. This is +used as a measure of grid quality, these two lines should ideally be as +close to perpendicular as possible, making the cosine of the angle zero. +A ``connection`` has no methods. + +A ``connection`` object ``con`` can be created for example using the +command + +:: + + con = connection(cols) + +where ``cols`` is a two-element list of the ``column`` objects in the +connection. + +.. _wellobjects: + +``well`` objects +~~~~~~~~~~~~~~~~ + +.. index:: MULgraph geometry; wells +.. index:: wells + +A ``well`` object represents a well in a ``mulgrid`` object. The +properties of a ``well`` object are given in the +:ref:`table ` below. + +.. container:: + :name: tb:well_properties + + .. table:: Properties of a ``well`` object + + +--------------------+--------------+--------------------------------------------+ + | **Property** | **Type** | **Description** | + +====================+==============+============================================+ + | ``bottom`` | ``np.array`` | well bottom position | + +--------------------+--------------+--------------------------------------------+ + | ``deviated`` | Boolean | whether well is deviated | + +--------------------+--------------+--------------------------------------------+ + | ``head`` | ``np.array`` | well head position | + +--------------------+--------------+--------------------------------------------+ + | ``name`` | string | well name | + +--------------------+--------------+--------------------------------------------+ + | ``num_deviations`` | integer | number of deviations | + +--------------------+--------------+--------------------------------------------+ + | ``num_pos`` | integer | number of well track nodes | + +--------------------+--------------+--------------------------------------------+ + | ``pos`` | list | positions (3-D arrays) of well track nodes | + +--------------------+--------------+--------------------------------------------+ + | ``pos_depth`` | ``np.array`` | downhole depths along well track | + +--------------------+--------------+--------------------------------------------+ + +The well track can be deviated, and is defined as a list ``pos`` of (at +least two) 3D positions (``np.arrays``). The ``num_deviations`` property +returns the number of deviations in the track (one less than the +``num_pos`` property, which is the number of nodes in the ``pos`` list). +The ``deviated`` property returns ``True`` if there is more than one +deviation. The ``pos_depth`` property returns an array of the downhole +depths at each node along the well track. + +A ``well`` object ``w`` can be created simply with the command +``w = well(name,pos)``, where ``name`` is the well name and ``pos`` is a +list of 3-element ``np.arrays`` (or lists, or tuples) representing the +well trace (starting from the wellhead). + +The methods of a ``well`` object are listed in the +:ref:`table ` and described below. + +.. container:: + :name: tb:well_methods + + .. table:: Methods of a ``well`` object + + +---------------------------------------------------+---------------------------+--------------------------+ + | **Method** | **Type** | **Description** | + +===================================================+===========================+==========================+ + | :ref:`depth_elevation ` | float | elevation for a given | + | | | downhole depth | + | | | | + +---------------------------------------------------+---------------------------+--------------------------+ + | :ref:`depth_pos ` | ``np.array`` | position on well track | + | | | for a given downhole | + | | | depth | + +---------------------------------------------------+---------------------------+--------------------------+ + | :ref:`elevation_depth ` | float | downhole depth for a | + | | | given elevation | + | | | | + +---------------------------------------------------+---------------------------+--------------------------+ + | :ref:`elevation_pos ` | ``np.array`` | position on well track | + | | | for a given elevation | + | | | | + +---------------------------------------------------+---------------------------+--------------------------+ + | :ref:`pos_coordinate ` | ``np.array`` | array of coordinates for | + | | | a given index | + | | | | + +---------------------------------------------------+---------------------------+--------------------------+ + +.. _sec:well:depth_elevation: + +``depth_elevation(depth)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns the elevation corresponding to the specified downhole ``depth`` +(or ``None`` if ``depth`` is above the wellhead or below the bottom). + +**Parameters:** + +- | **depth**: float + | Downhole depth. + +---- + +.. _sec:well:depth_pos: + +``depth_pos(depth)`` +^^^^^^^^^^^^^^^^^^^^ + +Returns the 3D position of the point in the well with specified downhole +``depth`` (or ``None`` if ``depth`` is above the wellhead or below the +bottom). The position is interpolated between the deviation locations. + +**Parameters:** + +- | **depth**: float + | Downhole depth of the required point. + +---- + +.. _sec:well:elevation_depth: + +``elevation_depth(elevation)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns the downhole depth corresponding to the specified ``elevation`` +(or ``None`` if ``elevation`` is above the wellhead or below the +bottom). + +**Parameters:** + +- | **elevation**: float + | Elevation. + +---- + +.. _sec:well:elevation_pos: + +``elevation_pos(elevation, extend=False)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns the 3D position of the point in the well with specified +``elevation`` (or ``None`` if ``elevation`` is above the wellhead or +below the bottom). The position is interpolated between the deviation +locations. If ``extend`` is ``True``, return extrapolated positions for +elevations below the bottom of the well. + +**Parameters:** + +- | **elevation**: float + | Elevation of the required point. + +- | **extend**: Boolean + | If ``True``, extrapolated positions will be returned for elevations + below the bottom of the well (otherwise ``None`` will be returned). + +---- + +.. _sec:well:pos_coordinate: + +``pos_coordinate(index)`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns an ``np.array`` of the well track node coordinates for the given +index (0, 1 or 2). For example, ``pos_coordinate(2)`` returns an array +containing the elevations of all well track nodes. + +**Parameters:** + +- | **index**: integer + | Index required (0, 1 or 2). + +---- + +Other functions: block name conversions +--------------------------------------- + +The ``mulgrids`` library contains two other functions connected with +working with geometry files and TOUGH2 grids: + +.. _sec:mulgrid:fix_blockname: + +``fix_blockname(name)`` +~~~~~~~~~~~~~~~~~~~~~~~ + +.. index:: MULgraph geometry; block names + +TOUGH2 always assumes that the last two characters of a block name +represent a two-digit number. However, if that number is less than 10, +the fourth character is not padded with zeros, so for example 'AA101' +becomes 'AA1 1' when processed by TOUGH2. + +The ``fix_blockname`` function corrects this by padding the fourth +character of a block name with a zero if necessary. This is only done if +the third character is also a digit, e.g. when naming convention 2 is +used (two characters for layer followed by three digits for column). + +**Parameters:** + +- | **name**: string + | Block name. + +---- + +``unfix_blockname(name)`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. index:: MULgraph geometry; block names + +This function reverses the effect of ``fix_blockname()``. + +**Parameters:** + +- | **name**: string + | Block name. + +---- + +.. _sec:mulgrid:blockmappings: + +Block mappings: handling other block naming conventions +------------------------------------------------------- + +.. index:: MULgraph geometry; block mappings + +The MULgraph geometry format names blocks according to one of its +:ref:`naming conventions `. +All of these conventions use part of the block name to indicate the layer +and part of it to indicate the column. + +However, in PyTOUGH it is possible to make a ``mulgrid`` object handle +other block naming conventions by means of a **block mapping**. This is +simply a dictionary that maps the block names in a ``mulgrid`` to block +names in a ``t2grid`` object. The block names in the ``t2grid`` can +follow an arbitrary convention, not based on layers and columns. For +example, blocks in TOUGH2 grids created by PetraSim may be simply +numbered. + +A block mapping dictionary can be passed in as an optional parameter to +many PyTOUGH methods that involve both a MULgraph geometry and TOUGH2 +grid, for example the ``mulgrid`` :ref:`block_name() `, +:ref:`slice_plot() ` and +:ref:`write_vtk() ` methods, and the ``write_vtk()`` +methods of the :ref:`t2grid ` and +:ref:`t2listing ` classes. + +When the :ref:`rectgeo() ` method is used +to create a ``mulgrid`` object from a ``t2grid``, a block mapping is +also created, and may be used in the PyTOUGH methods that can accept a +block mapping. + +A block mapping need not contain entries for all blocks. If for example +a model follows the naming convention of a MULgraph geometry in most +blocks, and only a few are different, then only entries for the +different block names need be present in the mapping dictionary. + +Block mappings can be saved to and loaded from disk (like any other +Python object) using the ``pickle`` library. This is part of the +standard Python library collection. For example a block mapping called +``blockmap`` can be saved to a file called ``'blockmap.pkl'`` as +follows: + +:: + + import pickle + pickle.dump(blockmap, open('blockmap.pkl', 'w')) + +It can be loaded back in again like this: + +:: + + blockmap = pickle.load(open('blockmap.pkl')) + diff --git a/doc/source/requirements.txt b/doc/source/requirements.txt new file mode 100644 index 00000000..b3bcc0e5 --- /dev/null +++ b/doc/source/requirements.txt @@ -0,0 +1,5 @@ +# File: doc/source/requirements.txt + +# Defining the exact version will make sure things don't break +sphinx==7.2.6 +furo==2024.1.29 diff --git a/doc/source/t2data.rst b/doc/source/t2data.rst new file mode 100644 index 00000000..b7feeda5 --- /dev/null +++ b/doc/source/t2data.rst @@ -0,0 +1,1836 @@ +:tocdepth: 3 + +.. _datafiles: + +TOUGH2 data files +================= + +.. index:: TOUGH2 data files + +.. _introduction-3: + +Introduction +------------ + +The ``t2data`` library in PyTOUGH contains classes and routines for +creating, editing and saving TOUGH2 or AUTOUGH2 data files. It can be +imported using the command: + +:: + + from t2data import * + +``t2data`` objects +------------------ + +The ``t2data`` library defines a ``t2data`` class, used for representing +TOUGH2 data files. + +**Example:** + +:: + + dat = t2data() + +creates an empty ``t2data`` object called ``dat``. + +:: + + dat = t2data(filename) + +creates a ``t2data`` object called ``dat`` and reads its contents from +file ``filename``. (It is also possible to read the mesh part of the +``t2data`` object from separate files - see below.) + +Because a ``t2data`` object contains a large number of different +parameters, it is usually easier to load one from an existing TOUGH2 +data file and edit it, rather than creating a new one from scratch. + +.. _properties-2: + +Properties +~~~~~~~~~~ + +The main properties of a ``t2data`` object are listed in the +:ref:`table ` below. In general, each of +these properties corresponds to an input block in a TOUGH2 data file. +Most of these input blocks contain a number of different parameters, so +that the ``t2data`` property corresponding to each input block is +usually in the form of a dictionary, containing a number of keys +representing sub-properties. + +For example, the maximum number of time steps for the simulation is +controlled by ``max_timesteps`` key in the ``parameter`` property, which +for a ``t2data`` object called ``dat`` would be accessed by +``dat.parameter['max_timesteps']``. + +.. container:: + :name: tb:t2data_properties + + .. table:: Properties of a ``t2data`` object + + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | **Property** | **Type** | **Description** | **Input block**| + | | | | | + +=================================================================+========================+==================+================+ + | :ref:`capillarity ` | dictionary | capillarity | RELP | + | | | function | | + | | | | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`diffusion ` | list | diffusion | DIFFU | + | | | coefficients | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`echo_extra_precision ` | Boolean | echoing extra | – | + | | | precision | | + | | | sections to | | + | | | main data file | | + | | | (AUTOUGH2 | | + | | | only) | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`end_keyword ` | string | keyword to end | ENDCY or ENDFI | + | | | file | | + | | | | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`extra_precision ` | list | data sections | – | + | | | read from | | + | | | extra | | + | | | precision | | + | | | auxiliary file | | + | | | (AUTOUGH2 | | + | | | only) | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`filename ` | string | file name on | – | + | | | disk | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`generator ` | dictionary | generators (by | GENER | + | | | block name and | | + | | | generator | | + | | | name) | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`generatorlist ` | list | generators (by | GENER | + | | | index) | | + | | | | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`grid ` | :ref:`t2grid `| model grid | ELEME, CONNE | + | | | | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`history_block ` | list | history blocks | FOFT | + | | | (TOUGH2 only) | | + | | | | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`history_connection ` | list | history | COFT | + | | | connections | | + | | | (TOUGH2 only) | | + | | | | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`history_generator ` | list | history | GOFT | + | | | generators | | + | | | (TOUGH2 only) | | + | | | | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`incon ` | dictionary | initial | INCON | + | | | conditions | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`indom ` | dictionary | rocktype-specific| INDOM | + | | | initial | | + | | | conditions | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`lineq ` | dictionary | linear | LINEQ | + | | | equation | | + | | | solver options | | + | | | (AUTOUGH2 | | + | | | only) | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`meshfilename ` | string or | file name(s) | – | + | | tuple | on disk | | + | | | containing | | + | | | mesh data | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`meshmaker ` | list | mesh | MESHM | + | | | generation | | + | | | options | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`more_option ` | array of | additional | MOMOP | + | | integer | parameter | | + | | | options | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`multi ` | dictionary | EOS | MULTI | + | | | configuration | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`noversion ` | Boolean | suppressing | NOVER | + | | | printing of | | + | | | version | | + | | | summary | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`num_generators ` | integer | number of | – | + | | | generators | | + | | | | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`output_times ` | dictionary | times to write | TIMES | + | | | output | | + | | | | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`parameter ` | dictionary | run-time | PARAM | + | | | parameters | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`relative_permeability ` | dictionary | relative | RELP | + | | | permeability | | + | | | function | | + | | | | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`selection ` | dictionary | selection | SELEC | + | | | parameters | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`short_output ` | dictionary | short output | SHORT | + | | | (AUTOUGH2 | | + | | | only) | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`simulator ` | string | simulator name | SIMUL | + | | | (AUTOUGH2 | | + | | | only) | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`solver ` | dictionary | linear | SOLVR | + | | | equation | | + | | | solver options | | + | | | (TOUGH2 only) | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`start ` | Boolean | run | START | + | | | initialisation | | + | | | option | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`title ` | string | simulation | TITLE | + | | | title | | + | | | | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + | :ref:`type ` | string | simulator type | – | + | | | (AUTOUGH2 or | | + | | | TOUGH2) | | + | | | | | + +-----------------------------------------------------------------+------------------------+------------------+----------------+ + +The details of the ``t2data`` properties are as follows. + +---- + +.. _sec:t2data:capillarity: + +``capillarity`` property +^^^^^^^^^^^^^^^^^^^^^^^^ + +A dictionary property specifying the capillarity function used, +corresponding to the second line of the **RPCAP** input block in the +TOUGH2 data file. The individual keys of this property are given in +the :ref:`table ` below. + +.. container:: + :name: tb:capillarity + + .. table:: ``capillarity`` property keys + + +----------------+----------------+----------------+----------------------+ + | **Key** | **Type** | **Description**| **TOUGH2 parameter** | + | | | | | + +================+================+================+======================+ + | ``parameters`` | array (7) of | function | CP | + | | float | parameters | | + +----------------+----------------+----------------+----------------------+ + | ``type`` | integer | type of | ICP | + | | | capillarity | | + | | | function | | + +----------------+----------------+----------------+----------------------+ + +---- + +.. _sec:t2data:diffusion: + +``diffusion`` property +^^^^^^^^^^^^^^^^^^^^^^ + +A list property specifying diffusion coefficients for each mass +component simulated, corresponding to the **DIFFU** input block in the +TOUGH2 data file. The list has length ``multi['num_components']`` (i.e. +NK in TOUGH2 terminology), and each element is a list of the diffusion +coefficients for each component (with length ``multi['num_phases']``, or +NPH). + +---- + +.. _sec:t2data:echo_extra_precision: + +``echo_extra_precision`` property +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A Boolean property (AUTOUGH2 only) governing whether data written to an +auxiliary extra-precision file is also echoed to the main data file. If +``True``, all extra-precision data sections are echoed to the main file. + +---- + +.. _sec:t2data:end_keyword: + +``end_keyword`` property +^^^^^^^^^^^^^^^^^^^^^^^^ + +A string property containing the keyword used in the data file to end +the file. Normally this is 'ENDCY', but 'ENDFI' can also be used. + +---- + +.. _sec:t2data:extra_precision: + +``extra_precision`` property +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A list property determining which data sections will be written to an +auxiliary extra-precision file (AUTOUGH2 only). Recent versions of +AUTOUGH2 support an additional data file containing some data written +with extra precision. Possible extra-precision data sections are ROCKS, +ELEME, CONNE, RPCAP and GENER. Typical usage of this extra-precision +data is for automatic model calibration using PEST or similar software, +where calculation of derivatives of model outputs with respect to model +parameters requires higher precision than is possible with the standard +TOUGH2 data file format. + +The ``extra_precision`` parameter may be a list containing names of +sections to be written in extra precision (e.g. ['RPCAP', 'GENER']), or +set to ``False`` to disable extra precision (equivalent to []), or to +``True`` to specify that all possible sections should be written in +extra precision. + +The :ref:`read() ` method of a ``t2data`` +object determines whether extra precision data are available by +searching for an additional file with the same base name as the data +file itself, but with a '.pdat' or '.PDAT' extension (depending on the +case of the main data file name). If no such file exists, then no extra +precision data will be read. + +---- + +.. _sec:t2data:filename: + +``filename`` property +^^^^^^^^^^^^^^^^^^^^^ + +A string property containing the name of the TOUGH2 data file on disk. +(This does not correspond to any parameter in the TOUGH2 data file.) + +---- + +.. _sec:t2data:generator: + +``generator`` property +^^^^^^^^^^^^^^^^^^^^^^ + +A dictionary property containing the generators for the simulation, +accessed by tuples of block name and generator name. Each generator is +an object of type :ref:`t2generator `. + +---- + +.. _sec:t2data:generatorlist: + +``generatorlist`` property +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A list property containing the generators for the simulation, accessed +by index. + +---- + +.. _sec:t2data:grid: + +``grid`` property +^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 data files; grid + +A :ref:`t2grid ` object representing the simulation grid, +corresponding to the **ELEME** and **CONNE** input blocks in a TOUGH2 +data file. + +---- + +.. _sec:t2data:history_block: + +``history_block`` property +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A list property containing blocks for which time history output is +required, corresponding to the **FOFT** input block in a TOUGH2 data +file. If the ``t2data`` object contains grid data, the items in this +list are :ref:`t2block ` objects; otherwise, +they are block names (i.e. strings). + +---- + +.. _sec:t2data:history_connection: + +``history_connection`` property +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A list property containing connections for which time history output is +required, corresponding to the **COFT** input block in a TOUGH2 data +file. If the ``t2data`` object contains grid data, the items in this +list are :ref:`t2connection ` objects; +otherwise, they are tuples of block names (i.e. tuples of strings). + +---- + +.. _sec:t2data:history_generator: + +``history_generator`` property +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A list property containing blocks in which generators are defined and +for which time history output is required, corresponding to the **GOFT** +input block in a TOUGH2 data file. If the ``t2data`` object contains +grid data, the items in this list are :ref:`t2block ` +objects; otherwise, they are block names (i.e. strings). + +---- + +.. _sec:t2data:incon: + +``incon`` property +^^^^^^^^^^^^^^^^^^ + +A dictionary property representing the initial conditions for the +simulation, accessed by block name, corresponding to the **INCON** input +block in a TOUGH2 data file. The value of each element of the dictionary +is a list consisting of the porosity of the block, followed by a list of +the specified initial primary thermodynamic variables in the block. If +the TOUGH2 NSEQ and NADD values are used, these are stored after the +thermodynamic variables. If they are not used, they can either be set to +``None`` or simply omitted. + +For example, to specify porosity 0.1 and initial conditions (101.3E3, +20.0) in block ``'AB105'`` of a ``t2data`` object called ``dat``, set +``dat.incon['AB105'] = [0.1, [101.3e3, 20.0]]``. + +To specify these same conditions but with NSEQ = 10 and NADD = 2, set +``dat.incon['AB105'] = [0.1, [101.3e3, 20.0], 10, 2]``. + +Porosity can be specified as ``None`` if default porosity (from the +rocktype) is to be used. + +---- + +.. _sec:t2data:indom: + +``indom`` property +^^^^^^^^^^^^^^^^^^ + +A dictionary property representing the initial conditions for the +simulation, accessed by rocktype name, corresponding to the **INDOM** +input block in a TOUGH2 data file. The value of each element of the +dictionary is a list consisting of the specified initial primary +thermodynamic variables for the rocktype. + +---- + +.. _sec:t2data:lineq: + +``lineq`` property +^^^^^^^^^^^^^^^^^^ + +A dictionary property representing linear equation solver options, +corresponding to the **LINEQ** input block in an AUTOUGH2 data file. The +individual keys of this property are given in the :ref:`table ` +below. + +.. container:: + :name: tb:lineq + + .. table:: ``lineq`` property keys + + +-------------------+----------+------------------+------------------+ + | **Key** | **Type** | **Description** | **AUTOUGH2 | + | | | | parameter** | + +===================+==========+==================+==================+ + | ``epsilon`` | float | solver tolerance | EPN | + +-------------------+----------+------------------+------------------+ + | ``gauss`` | integer | Gauss | IGAUSS | + | | | elimination | | + | | | parameter | | + +-------------------+----------+------------------+------------------+ + | ``max_iterations``| integer | max. number of | MAXIT | + | | | iterations | | + +-------------------+----------+------------------+------------------+ + | ``num_orthog`` | integer | number of | NORTH | + | | | or | | + | | | thogonalisations | | + +-------------------+----------+------------------+------------------+ + | ``type`` | integer | type of solver | ISOLVR | + | | | (1 or 2) | | + +-------------------+----------+------------------+------------------+ + +---- + +.. _sec:t2data:meshfilename: + +``meshfilename`` property +^^^^^^^^^^^^^^^^^^^^^^^^^ + +A string property (or tuple of strings) containing the name(s) of files +on disk containing the mesh data. (This does not correspond to any +parameter in the TOUGH2 data file.) Its default value is an empty string +which means mesh data will be read from the main data file. + +If ``meshfilename`` is a single (non-empty) string, this is interpreted +as the name of a formatted text file containing 'ELEME' and 'CONNE' +sections specifying the mesh (e.g. the 'MESH' file created by TOUGH2 or +TOUGH2_MP). + +If ``meshfilename`` is a tuple of two strings, these are interpreted as +the names of two binary files containing the mesh data, e.g. the 'MESHA' +and 'MESHB' files created by TOUGH2_MP. + +---- + +.. _sec:t2data:meshmaker: + +``meshmaker`` property +^^^^^^^^^^^^^^^^^^^^^^ + +A list property representing mesh generation options, corresponding to +the **MESHM** input block in a TOUGH2 data file. For more detail on the +use of **MESHM** data, consult the TOUGH2 users' guide. + +The **MESHM** data may contain multiple sections (e.g. creation of a +rectilinear XYZ grid followed by MINC processing), so the ``meshmaker`` +property is structured as a list of two-element tuples, each containing +the type of section (``rz2d``, ``xyz`` or ``minc``) followed by the +section data itself. + +The form of the section data varies depending on the section type. For +the ``rz2d`` type it is also structured as a list, as these types may +contain variable numbers of sub-sections. (For example, data for the +``rz2d`` type may contain multiple ``logar`` sub-sections for different +logarithmic radial parts of the mesh.) Each sub-section is again a +two-element tuple, consisting of the sub-section type (a string) +followed by a dictionary containing the data for the sub-section. + +Data for the ``xyz`` type are also structured as a list, with the first +element containing the stand-alone ``deg`` parameter (a float), followed +by the other sub-sections, corresponding to the **NX**, **NY** and +**NZ** sub-sections in the TOUGH2 data file. The ``minc`` type does not +have sub-sections so MINC data are not structured as a list but simply a +dictionary. + +Possible sub-section types for ``rz2d`` data are ``radii``, ``equid``, +``logar`` and ``layer``, corresponding to their (uppercase) keyword +counterparts in the TOUGH2 data file. Data keys for these types are +given in the :ref:`rz2d data keys ` table. Data keys for the +``xyz`` and ``minc`` data are given in :ref:`xyz data keys ` +and :ref:`minc data keys ` tables. + +**Example**: The easiest way to understand how the ``meshmaker`` +property works is to read some example input data into a ``t2data`` +object and examine the result. The **MESHM** data for the standard +TOUGH2 test problem 'rhbc' ('Production from a geothermal reservoir with +hypersaline brine') is represented as a ``t2data`` ``meshmaker`` +property as follows: + +:: + + [('rz2d',[ + ('radii', {'radii': [5.0]}), + ('equid', {'dr': 2.0, 'nequ': 1}), + ('logar', {'rlog': 100.0, 'nlog': 50}), + ('logar', {'rlog': 1000.0, 'nlog': 20}), + ('equid', {'dr': 0.0, 'nequ': 1}), + ('layer', {'layer': [500.0]}) + ]) + ] + +.. container:: + :name: tb:rz2d + + .. table:: ``rz2d`` data keys + + +---------------+-----------+----------+-----------------------------+----------------------+ + |**Sub-section**| **Key** | **Type** | **Description** | **TOUGH2 parameter** | + | | | | | | + +===============+===========+==========+=============================+======================+ + |**radii** | ``radii`` | list | specified mesh radii | RC | + +---------------+-----------+----------+-----------------------------+----------------------+ + |**equid** | ``dr`` | float | radial increment | DR | + | +-----------+----------+-----------------------------+----------------------+ + | | ``nequ`` | integer | number of equidistant radii | NEQU | + +---------------+-----------+----------+-----------------------------+----------------------+ + |**logar** | ``dr`` | float | reference radial increment | DR | + | +-----------+----------+-----------------------------+----------------------+ + | | ``nlog`` | integer | number of logarithmic radii | NLOG | + | +-----------+----------+-----------------------------+----------------------+ + | | ``rlog`` | float | largest radius | RLOG | + +---------------+-----------+----------+-----------------------------+----------------------+ + |**layer** | ``layer`` | list | layer thicknesses | H | + +---------------+-----------+----------+-----------------------------+----------------------+ + +.. container:: + :name: tb:xyz + + .. table:: ``xyz`` data keys + + +-----------+----------+-------------------------------------+----------------------+ + | **Key** | **Type** | **Description** | **TOUGH2 parameter** | + +===========+==========+=====================================+======================+ + | ``deg`` | float | angle between y-axis and horizontal | DEG | + +-----------+----------+-------------------------------------+----------------------+ + | ``del`` | float | constant grid increment | DEL | + +-----------+----------+-------------------------------------+----------------------+ + | ``deli`` | list | variable grid increments | DEL | + +-----------+----------+-------------------------------------+----------------------+ + | ``no`` | integer | number of grid increments | DR | + +-----------+----------+-------------------------------------+----------------------+ + | ``ntype`` | string | axis direction ('NX', 'NY' or 'NZ') | NTYPE | + +-----------+----------+-------------------------------------+----------------------+ + +.. container:: + :name: tb:minc + + .. table:: ``minc`` data keys + + +------------------+----------+------------------+---------------------+ + | **Key** | **Type** | **Description** | **TOUGH2 parameter**| + | | | | | + +==================+==========+==================+=====================+ + | ``dual`` | string | treatment of | DUAL | + | | | global | | + | | | matrix-matrix | | + | | | flow | | + +------------------+----------+------------------+---------------------+ + | ``num_continua`` | integer | number of | J | + | | | interacting | | + | | | continua | | + +------------------+----------+------------------+---------------------+ + | ``spacing`` | list | fracture | PAR | + | | | spacings | | + +------------------+----------+------------------+---------------------+ + | ``type`` | string | proximity | TYPE | + | | | function type | | + +------------------+----------+------------------+---------------------+ + | ``vol`` | list | volume fractions | VOL | + +------------------+----------+------------------+---------------------+ + | ``where`` | string | direction of | WHERE | + | | | volume fraction | | + | | | specification | | + +------------------+----------+------------------+---------------------+ + +---- + +.. _sec:t2data:more_options: + +``more_option`` property +^^^^^^^^^^^^^^^^^^^^^^^^ + +An array property containing additional integer parameter options, +corresponding to the **MOMOP** input block in a TOUGH2 data file (it is +not recognised by AUTOUGH2). Introduced by iTOUGH2, this is an extension +of the ``parameter.option`` property. It is of length 21 and is +populated with zeros by default. Like the ``parameter.option`` property, +values are accessed using 1-based (not zero-based) indices. + +---- + +.. _sec:t2data:multi: + +``multi`` property +^^^^^^^^^^^^^^^^^^ + +A dictionary property selecting the equation of state (EOS) module used +and setting associated parameters, corresponding to the **MULTI** input +block in a TOUGH2 or AUTOUGH2 data file. The individual keys of this +property are given in the :ref:`table ` below. + +.. container:: + :name: tb:multi + + .. table:: ``multi`` property keys + + +-----------------------------+----------+------------------+---------------------+ + | **Key** | **Type** | **Description** | **TOUGH2 parameter**| + | | | | | + +=============================+==========+==================+=====================+ + | ``eos`` | string | EOS name | NAMEOS | + | | | (AUTOUGH2 only) | | + +-----------------------------+----------+------------------+---------------------+ + | ``num_components`` | integer | number of | NK | + | | | components | | + +-----------------------------+----------+------------------+---------------------+ + | ``num_equations`` | integer | number of | NEQ | + | | | equations | | + +-----------------------------+----------+------------------+---------------------+ + | ``num_inc`` | integer | number of mass | NKIN | + | | | components in | | + | | | INCON data | | + | | | (TOUGH2 only) | | + +-----------------------------+----------+------------------+---------------------+ + | ``num_phases`` | integer | number of phases | NPH | + +-----------------------------+----------+------------------+---------------------+ + | ``num_secondary_parameters``| integer | number of | NB | + | | | secondary | | + | | | parameters | | + +-----------------------------+----------+------------------+---------------------+ + +---- + +.. _sec:t2data:noversion: + +``noversion`` property +^^^^^^^^^^^^^^^^^^^^^^ + +A Boolean property specifying whether to suppress printing of version +and date information, corresponding to the **NOVER** input block in a +TOUGH2 data file. + +---- + +.. _sec:t2data:num_generators: + +``num_generators`` property +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A read-only integer property returning the number of generators. + +---- + +.. _sec:t2data:output_times: + +``output_times`` property +^^^^^^^^^^^^^^^^^^^^^^^^^ + +A dictionary property specifying the times at which model output is +required, corresponding to the **TIMES** input block in a TOUGH2 data +file. The individual keys of this property are given in the +:ref:`table ` below. + +.. container:: + :name: tb:outputtimes + + .. table:: ``output_times`` property keys + + +------------------------+---------------+----------------+----------------+ + | **Key** | **Type** | **Description**| **TOUGH2 | + | | | | parameter** | + +========================+===============+================+================+ + | ``max_timestep`` | float | maximum time | DELAF | + | | | step | | + +------------------------+---------------+----------------+----------------+ + | ``num_times_specified``| integer | number of | ITI | + | | | times | | + | | | specified | | + +------------------------+---------------+----------------+----------------+ + | ``num_times`` | integer | total number | ITE | + | | | of times | | + +------------------------+---------------+----------------+----------------+ + | ``time`` | list of float | times at which | TIS | + | | | output is | | + | | | required | | + +------------------------+---------------+----------------+----------------+ + | ``time_increment`` | float | time increment | TINTER | + | | | after | | + | | | specified | | + | | | times | | + +------------------------+---------------+----------------+----------------+ + +---- + +.. _sec:t2data:parameter: + +``parameter`` property +^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 data files; simulation parameters + +A dictionary property specifying run-time parameters, corresponding to +the **PARAM** input block in a TOUGH2 data file. The individual keys of +this property are given in the :ref:`table ` below. + +The ``option`` parameter (MOP array in TOUGH2) is an array of 24 +integers, and has a 1-based index so that its indices are the same as +those in the TOUGH2 documentation. (In fact it is really zero-based, +like all other Python arrays, but has an extra unused +zero\ :sup:`th` element). + +.. container:: + :name: tb:parameter + + .. table:: ``parameter`` property keys + + +-------------------------+----------------+----------------+----------------+ + | **Key** | **Type** | **Description**| **TOUGH2 | + | | | | parameter** | + +=========================+================+================+================+ + | ``absolute_error`` | float | absolute | RE2 | + | | | convergence | | + | | | tolerance | | + +-------------------------+----------------+----------------+----------------+ + | ``be`` | float | enhanced | BE | + | | | vapour | | + | | | diffusion | | + +-------------------------+----------------+----------------+----------------+ + | ``const_timestep`` | float | time step | DELTEN | + | | | length | | + +-------------------------+----------------+----------------+----------------+ + | ``default_incons`` | list of float | default | DEP | + | | | initial | | + | | | conditions | | + +-------------------------+----------------+----------------+----------------+ + | ``derivative_increment``| float | numerical | DFAC | + | | | derivative | | + | | | increment | | + | | | factor | | + +-------------------------+----------------+----------------+----------------+ + | ``diff0`` | float | diffusive | DIFF0 | + | | | vapour flux | | + | | | (AUTOUGH2 | | + | | | only) | | + +-------------------------+----------------+----------------+----------------+ + | ``gravity`` | float | gravitational | GF | + | | | acceleration | | + +-------------------------+----------------+----------------+----------------+ + | ``max_duration`` | integer | maximum | MSEC | + | | | simulation | | + | | | duration | | + | | | (machine | | + | | | seconds) | | + +-------------------------+----------------+----------------+----------------+ + | ``max_iterations`` | integer | maximum number | NOITE | + | | | of iterations | | + | | | per time step | | + +-------------------------+----------------+----------------+----------------+ + | ``max_timesteps`` | integer | maximum number | MCYC | + | | | of time steps | | + +-------------------------+----------------+----------------+----------------+ + | ``max_timestep`` | float | maximum time | DELTMX | + | | | step size | | + +-------------------------+----------------+----------------+----------------+ + | ``newton_weight`` | float | Newton-Raphson | WNR | + | | | weighting | | + | | | factor | | + +-------------------------+----------------+----------------+----------------+ + | ``option`` | array(24) of | simulation | MOP | + | | integer | options | | + +-------------------------+----------------+----------------+----------------+ + | ``pivot`` | float | pivoting | U | + | | | parameter for | | + | | | linear solver | | + +-------------------------+----------------+----------------+----------------+ + | ``print_block`` | string | block name for | ELST | + | | | short printout | | + +-------------------------+----------------+----------------+----------------+ + | ``print_interval`` | integer | time step | MCYPR | + | | | interval for | | + | | | printing | | + +-------------------------+----------------+----------------+----------------+ + | ``print_level`` | integer | amount of | KDATA | + | | | printout | | + +-------------------------+----------------+----------------+----------------+ + | ``relative_error`` | float | relative | RE1 | + | | | convergence | | + | | | tolerance | | + +-------------------------+----------------+----------------+----------------+ + | ``scale`` | float | grid scale | SCALE | + | | | factor | | + +-------------------------+----------------+----------------+----------------+ + | ``texp`` | float | binary | TEXP | + | | | diffusion | | + | | | temperature | | + | | | parameter | | + +-------------------------+----------------+----------------+----------------+ + | ``timestep_reduction`` | float | time step | REDLT | + | | | reduction | | + | | | factor | | + +-------------------------+----------------+----------------+----------------+ + | ``timestep`` | list of float | specified time | DLT | + | | | step sizes | | + +-------------------------+----------------+----------------+----------------+ + | ``tstart`` | float | start time | TSTART | + | | | (seconds) | | + +-------------------------+----------------+----------------+----------------+ + | ``tstop`` | float | stop time | TIMAX | + +-------------------------+----------------+----------------+----------------+ + | ``upstream_weight`` | float | upstream | WUP | + | | | weighting | | + | | | factor | | + +-------------------------+----------------+----------------+----------------+ + +---- + +.. _sec:t2data:relative_permeability: + +``relative_permeability`` property +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A dictionary property specifying the relative permeability function +used, corresponding to the first line of the **RPCAP** input block in +the TOUGH2 data file. The individual keys of this property are given in +the :ref:`table ` below. + +.. container:: + :name: tb:relativepermeability + + .. table:: ``relative_permeability`` property keys + + +----------------+----------------+----------------+----------------+ + | **Key** | **Type** | **Description**| **TOUGH2 | + | | | | parameter** | + +================+================+================+================+ + | ``parameters`` | array (7) of | function | RP | + | | float | parameters | | + +----------------+----------------+----------------+----------------+ + | ``type`` | integer | type of | IRP | + | | | relative | | + | | | permeability | | + | | | function | | + +----------------+----------------+----------------+----------------+ + +---- + +.. _sec:t2data:selection: + +``selection`` property +^^^^^^^^^^^^^^^^^^^^^^ + +A dictionary property representing selection parameters for the +simulation (only used by some EOS modules, e.g. EOS7, EOS7R, EWASG), +corresponding to the **SELEC** block in the TOUGH2 data file. + +The dictionary contains two keys: 'integer' and 'float', the first of +which accesses a list of the integer selection parameters (the first +line of the **SELEC** block), while the second accesses a list of the +float selection parameters (the remaining lines of the **SELEC** block). + +---- + +.. _sec:t2data:short_output: + +``short_output`` property +^^^^^^^^^^^^^^^^^^^^^^^^^ + +A dictionary property representing blocks, connections and generators +for which short output is required, corresponding to the **SHORT** input +block in an AUTOUGH2 data file. + +The dictionary contains four keys: 'frequency', 'block', 'connection' +and 'generator'. The last three of these access lists of blocks, +connections and generators respectively for short output. (Note that +each of these lists contains :ref:`t2block `, +:ref:`t2connection ` or +:ref:`t2generator ` objects, rather than +names.) The 'frequency' key accesses the time step frequency (an +integer) for which short output is required. + +---- + +.. _sec:t2data:simulator: + +``simulator`` property +^^^^^^^^^^^^^^^^^^^^^^ + +A string property specifying the type of simulator, corresponding to the +**SIMUL** input block in an AUTOUGH2 data file. + +---- + +.. _sec:t2data:solver: + +``solver`` property +^^^^^^^^^^^^^^^^^^^ + +A dictionary property representing linear equation solver options, +corresponding to the **SOLVR** input block in a TOUGH2 data file. The +individual keys of this property are given in the +:ref:`table ` below. + +.. container:: + :name: tb:solver + + .. table:: ``solver`` property keys + + +----------------------------+----------+------------------+------------------+ + | **Key** | **Type** | **Description** | **TOUGH2 | + | | | | parameter** | + +============================+==========+==================+==================+ + | ``closure`` | float | convergence | CLOSUR | + | | | criterion | | + +----------------------------+----------+------------------+------------------+ + | ``relative_max_iterations``| float | relative max. | RITMAX | + | | | number of | | + | | | iterations | | + +----------------------------+----------+------------------+------------------+ + | ``type`` | integer | solver type | MATSLV | + +----------------------------+----------+------------------+------------------+ + | ``o_precond`` | string | O | OPROCS | + | | | -preconditioning | | + | | | type | | + +----------------------------+----------+------------------+------------------+ + | ``z_precond`` | string | Z | ZPROCS | + | | | -preconditioning | | + | | | type | | + +----------------------------+----------+------------------+------------------+ + +---- + +.. _sec:t2data:start: + +``start`` property +^^^^^^^^^^^^^^^^^^ + +A Boolean property specifying whether the flexible start option is used, +corresponding to the **START** input block in a TOUGH2 data file. + +---- + +.. _sec:t2data:title: + +``title`` property +^^^^^^^^^^^^^^^^^^ + +A string property containing the simulation title, corresponding to the +**TITLE** input block in a TOUGH2 data file. + +---- + +.. _sec:t2data:type: + +``type`` property +^^^^^^^^^^^^^^^^^ + +A string property specifying the simulator type ('AUTOUGH2' or +'TOUGH2'). Changing the value of this property will cause one of the +:ref:`convert_to_TOUGH2() ` or +:ref:`convert_to_AUTOUGH2() ` +methods to be executed, with default method parameters. Hence, changing +the ``type`` property to 'AUTOUGH2' causes the EOS to be set to the +default 'EW'. It is also not possible to specify TOUGH2_MP options when +setting ``type``. For more control over how the conversion is carried +out, use the conversion methods directly instead of setting ``type``. + +Functions for reading data from file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is possible to specify customized functions to control how data are +read from a TOUGH2 data file. This is done using the optional +``read_function`` parameter when a ``t2data`` object is created- in +exactly the same way it is done for a ``mulgrid`` object. For more +details, see the corresponding +:ref:`documentation ` for ``mulgrid`` objects. +By default, the read functions for ``t2data`` objects are given by the +``default_read_function`` dictionary. + +Methods +~~~~~~~ + +The main methods of a ``t2data`` object are listed in the +:ref:`table ` below. + +.. container:: + :name: tb:t2data_methods + + .. table:: Methods of a ``t2data`` object + + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + | **Method** | **Type** | **Description** | + +====================================================================+=====================================+======================+ + | :ref:`add_generator ` | – | adds a generator | + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + | :ref:`clear_generators ` | – | deletes all | + | | | generators | + | | | | + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + | :ref:`convert_to_AUTOUGH2 ` | – | converts from TOUGH2 | + | | | input to AUTOUGH2 | + | | | | + | | | | + | | | | + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + | :ref:`convert_to_TOUGH2 ` | – | converts from | + | | | AUTOUGH2 input to | + | | | TOUGH2 | + | | | | + | | | | + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + | :ref:`delete_generator ` | – | deletes a generator | + | | | | + | | | | + | | | | + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + | :ref:`delete_orphan_generators ` | – | deletes orphaned | + | | | generators | + | | | | + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + | :ref:`effective_incons ` | list or | effective initial | + | | :ref:`t2incon ` | conditions | + | | | | + | | | | + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + | :ref:`generator_index ` | integer | returns index of | + | | | generator with | + | | | specified name and | + | | | block name | + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + | :ref:`json ` | dictionary | Waiwera JSON input | + | | | | + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + | :ref:`read ` | :ref:`t2data ` | reads data file from | + | | | disk | + | | | | + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + | :ref:`rename_blocks ` | – | renames blocks | + | | | | + | | | | + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + | :ref:`run ` | – | runs a TOUGH2 | + | | | simulation | + | | | | + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + | :ref:`specific_generation ` | ``np.array`` | generation per unit | + | | | volume in each block | + | | | | + | | | | + | | | | + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + | :ref:`total_generation ` | ``np.array`` | total generation in | + | | | each block | + | | | | + | | | | + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + | :ref:`transfer_from ` | – | transfers data from | + | | | another | + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + | :ref:`write ` | – | writes to data file | + | | | on disk | + +--------------------------------------------------------------------+-------------------------------------+----------------------+ + +Details of these methods are as follows. + +---- + +.. _sec:t2data:add_generator: + +``add_generator(generator)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Adds a generator to the data file object. + +**Parameters:** + +- | **generator**: :ref:`t2generator ` + | Generator to be added to the data file object. + +---- + +.. _sec:t2data:convert_to_AUTOUGH2: + +``convert_to_AUTOUGH2(warn=True, MP=False, simulator='AUTOUGH2.2', eos='EW')`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 data files; converting + +Converts a TOUGH2 (or TOUGH2_MP) data file for use with AUTOUGH2. +Various parameter options are altered to try to make the AUTOUGH2 +simulation give similar results to the original TOUGH2 simulation. If +necessary, the ``filename`` property is changed to end in '.dat' (or +'.DAT', depending on the case of the base file name), as required by +AUTOUGH2. + +The simulator and EOS name can also be specified, as AUTOUGH2 data files +contain this information in the SIMUL and MULTI sections. + +**Parameters:** + +- | **warn**: Boolean + | If ``True``, warnings will be printed regarding TOUGH2 options used + in the original data file which are not supported in AUTOUGH2. + +- | **MP**: Boolean + | if ``True``, treats the original ``t2data`` object as a TOUGH2_MP + data file, which uses some of the parameters differently (e.g. + MOP(20)). + +- | **simulator**: string + | Simulator name, used for the leading part of the AUTOUGH2 SIMUL + data section. Possible values are 'MULKOM', 'TOUGH2', 'TOUGH2.2', + 'AUTOUGH2' and 'AUTOUGH2.2'. + +- | **eos**: string + | EOS name, used for the trailing part of the AUTOUGH2 SIMUL data + section (e.g. 'EW', 'EWC', 'EWA', 'EWAV' etc.) + +---- + +.. _sec:t2data:convert_to_TOUGH2: + +``convert_to_TOUGH2(warn=True, MP=False)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 data files; converting + +Converts an AUTOUGH2 data file for use with TOUGH2 (or compatible +simulators such as TOUGH2_MP). Various parameter options are altered to +try to make the TOUGH2 simulation give similar results to the original +AUTOUGH2 simulation. This particularly affects AUTOUGH2 options related +to backward compatibility with MULKOM. In particular, if these are used +then the heat conductivities in the ROCKS block have to be altered to +give the same results. Data blocks specific to AUTOUGH2 (e.g. SIMULATOR, +LINEQ, and SHORT) are removed, and AUTOUGH2-specific generator types are +converted to their TOUGH2 equivalents if possible, or otherwise deleted. + +**Parameters:** + +- | **warn**: Boolean + | If ``True``, warnings will be printed regarding AUTOUGH2 options + used in the original data file which are not supported in TOUGH2. + +- | **MP**: Boolean + | if ``True``, converts to a TOUGH2_MP data file, which treats some + of the parameters differently (e.g. MOP(20)). The ``filename`` + property is also changed to INFILE, as required by TOUGH2_MP. + +---- + +.. _sec:t2data:clear_generators: + +``clear_generators()`` +^^^^^^^^^^^^^^^^^^^^^^ + +Deletes all generators from the data file object. + +---- + +.. _sec:t2data:delete_generator: + +``delete_generator(blocksourcenames)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Deletes the generator with the specified block and generator (source) +name, if it exists. + +**Parameters:** + +- | **blocksourcenames**: tuple + | Tuple of block name and generator name (both strings) of the + generator to be deleted. + +---- + +.. _sec:t2data:del_orphan_geners: + +``delete_orphan_generators()`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Deletes all generators with block names that are not in the grid. + +---- + +.. _sec:t2data:effective_incons: + +``effective_incons(incons = None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns effective initial conditions, based on on the specified initial +conditions in combination with any initial conditions specified in the +``t2data`` object itself – whether as default initial conditions +specified via the :ref:`parameter ` +property, or via the :ref:`incon ` +property, or the :ref:`indom ` property (or +any combination of these). + +Any ``indom`` specifications override the defaults in the ``parameter`` +property. Values in the ``incon`` property override both the defaults +and values in ``indom``. Finally, values passed into this method via the +``incons`` parameter override any other specifications. Note that any of +these may contain incomplete specifications (i.e. values are not +specified for all blocks in the grid). + +If only default homogeneous initial conditions are in effect, then a +list of the primary variables is returned. Otherwise, a :ref:`t2incon ` +object is returned with initial conditions values for every +block. + +**Parameters:** + +- | **incons**: ``t2incon`` or ``None`` + | Initial conditions object, usually representing the contents of a + separate initial conditions file. + +---- + +.. _sec:t2data:generator_index: + +``generator_index(blocksourcenames)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns the index (in the ``generatorlist`` list) of the generator with +the specified block and generator name. + +**Parameters:** + +- | **blocksourcenames**: tuple + | Tuple of block name and generator name (both strings) of the + generator. + +---- + +.. _sec:t2data:json: + +``json(geo, mesh_filename, atmos_volume = 1.e25, incons = None, eos = None, bdy_incons = None, mesh_coords = 'xyz')`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 data files; JSON + +Returns a JSON dictionary representing the contents of the ``t2data`` +object (and associated mesh geometry), suitable for input to the +`Waiwera `_ simulator. + +Sources in the Waiwera JSON dictionary are given names based on the +corresponding TOUGH2 generator names. If the TOUGH2 model has no +duplicate generator names, these are used directly for the source +names. If there are duplicate generator names, the block names are +prepended to the generator names to form the source names. If there +are duplicate generator names within the same block, the source names +will have "_1", "_2" etc. appended to them as needed to make them +unique. + +**Parameters:** + +- | **geo**: :ref:`mulgrid ` + | Geometry object. Note that geometric meshes with column surface + elevations that do not correspond to layer elevations are not + supported in Waiwera. For meshes of this type, the column surface + elevations can be "snapped" to layer elevations using the + :ref:`snap_columns_to_nearest_layers() ` + method. In that case the + ``t2grid`` in the ``t2data`` object must be updated so it + corresponds to the snapped mesh geometry, and other parts of the + data file updated to reference the new mesh (e.g. using the + :ref:`transfer_from() ` + method). The geometry's :ref:`block_order ` + property should be set to 'dmplex', particularly if + it contains mixtures of 3- and 4-sided columns. + +- | **mesh_filename**: string + | The filename of the mesh file (e.g. ExodusII or GMSH mesh) for the + Waiwera simulation. + +- | **atmos_volume**: float + | Maximum block volume for blocks to be considered part of the + geometric grid. Blocks with volume greater than this value (or + zero) will be treated as boundary condition (e.g. atmosphere) + blocks rather than part of the simulation mesh. + +- | **incons**: :ref:`t2incon `, string, or ``None`` + | Initial conditions for the Waiwera model. If specified as a string, + this should be the filename of the Waiwera HDF5 output file for + restarting the simulation from the output of a previous run. If + ``None`` is specified, then default initial conditions will be + applied from the ``parameter`` + :ref:`property `. + +- | **eos**: string, integer or ``None`` | Equation of state used for + the simulation. For AUTOUGH2 simulations, this can generally be set + to ``None``, and the EOS will be read from the ``t2data`` + ``simulator`` or ``multi`` properties. Otherwise, it can be + specified as an integer corresponding to the EOS number (1 being + pure water, 2 being water / CO\ :math:`_2` etc.) or as a string + corresponding to the AUTOUGH2 EOS names (EOS1 being 'EW', EOS2 + being 'EWC' etc.). Note that for integer values, only EOS modules + 1, 2, 3 and 4 are supported. For AUTOUGH2 EOS names, these + correspond to 'W', 'EW', 'EWC' (or 'EWCX'), 'EWA' and 'EWAV' (or + 'EWAX'). The AUTOUGH2 passive tracer EOS modules 'EWT' and 'ETD' + are also supported (the latter supporting only constant + diffusivity, i.e. all elements of the ``diffusion`` property must + be negative and equal). For EOS4 ('EWAV'), alternative + initialization using EOS3-style primary variables via MOP(19) = 2 + is supported. + +- | **bdy_incons**: :ref:`t2incon `, or ``None`` + | TOUGH2 initial conditions from which boundary conditions are to be + derived. In many cases this parameter is not needed, because + boundary conditions are taken from the ``incons`` parameter: if the + ``incons`` parameter is specified as a ``t2incon`` object, then the + ``bdy_incons`` parameter can be set to ``None``. If, however, + ``incons`` is a string or ``None``, then it will not contain + boundary condition data, in which case boundary conditions can be + specified by passing a ``t2incon`` object as the ``bdy_incons`` + parameter; otherwise, if this is set to ``None`` then default + boundary conditions will be applied from the default initial + conditions in the ``t2data`` ``parameter`` property. Faces on which + to apply boundary conditions are identified by the presence of + connections to blocks with either zero or large volume (above the + volume specified by the ``atmos_volume`` parameter). Note that for + side boundary conditions (with horizontal connections), the + boundary blocks must have centres defined, otherwise it is not + possible to calculate the appropriate normal vector for the + boundary condition. + +- | **mesh_coords**: string + | String representing the coordinate system to be used in the Waiwera + model. 3-D Cartesian meshes are identified as 'xyz'. 2-D Cartesian + meshes may be identified as either 'xy', 'xz', or 'yz' (depending + on orientation), while 2-D radial meshes are identified as 'rz'. + +---- + +.. _sec:t2data:read: + +``read(filename, meshfilename='')`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 data files; reading + +Reads a ``t2data`` object from a TOUGH2 data file on disk. The mesh data +may optionally be read from auxiliary files, if it is not present in the +main data file. (Note that if the main data file does contain mesh +information (the 'ELEME' and 'CONNE' sections), any auxiliary mesh files +will not be read.) + +**Parameters:** + +- | **filename**: string + | Name of the TOUGH2 data file to be read. + +- | **meshfilename**: string or tuple + | Name of separate mesh file(s) to read, containing element and + connection data. If empty, then mesh data will be read from the + main data file. If a non-empty string is given, this is interpreted + as the name of a formatted text file containing 'ELEME' and 'CONNE' + data sections (as in the 'MESH' files created by TOUGH2 and + TOUGH2_MP). If a tuple of two filenames is given, these are + interpreted as the names of the two binary MESHA and MESHB files + used by TOUGH2_MP. + +Note that it is possible to create a ``t2data`` object and read its +contents in from disk files in one step, e.g.: +``dat = t2data(filename,meshfilename)``. + +---- + +.. _sec:t2data:rename_blocks: + +``rename_blocks(blockmap={}, invert=False, fix_blocknames = True)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Renames blocks in the model according to the specified block mapping +dictionary. Any block whose name is a key of the block mapping +dictionary is renamed with the corresponding dictionary value. The +blocks in the :ref:`t2grid ` object are renamed using its +own :ref:`rename_blocks() ` method. +Other ``t2data`` properties such as generators, initial conditions and +history specifications are similarly renamed. + +**Parameters:** + +- | **blockmap**: dictionary + | Block mapping dictionary, mapping strings to strings. + +- | **invert**: Boolean + | Set ``True`` to invert the block mapping dictionary, i.e. to map + its values to its keys. This can be used, for example, to rename + the blocks to correspond to a geometry created using the + :ref:`t2grid ` :ref:`rectgeo() ` + method, via the block mapping dictionary also created + by that method. + +- | **fix_blocknames**: Boolean + | Set ``True`` (the default) to 'fix' block names in the dictionary, + using the :ref:`fix_blockname() ` function. + +---- + +.. _sec:t2data:run: + +``run(save_filename='', incon_filename='', simulator='AUTOUGH2_2', silent=False, output_filename='')`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 data files; running + +Runs an AUTOUGH2 or TOUGH2 (but not TOUGH2_MP) simulation using the data +file corresponding to a ``t2data`` object. The contents of the +``t2data`` object must first have been written to disk using the +``write`` function. If the file names for the save file or initial +conditions file are not specified, they are constructed by changing the +file extension of the data file name. The name of the TOUGH2 executable +can be specified. + +For running TOUGH2 (rather than AUTOUGH2), the name of the TOUGH2 +executable must be specified via the ``simulator`` parameter. However, +the ``save_filename`` and ``incon_filename`` parameters do not need to +be specified. Initial conditions will be read from the file INCON and +final results written to SAVE. The listing file name will be the same as +the data file name, but with the extension changed to \*.listing, unless +the ``output_filename`` is specified. + +Running TOUGH2_MP is generally done via MPI rather than directly, and +the exact syntax for doing so may vary with different implementations of +MPI (OpenMPI, MPICH2 etc.) It is also necessary to specify the number of +processors to use. However it is still possible to run TOUGH2_MP from a +Python script using a system call, e.g.: + +:: + + from os import system + system("mpirun -np 16 t2eos1_mp") + +**Parameters:** + +- | **save_filename**: string + | Name of the save file to be written to disk during the simulation + (AUTOUGH2 only). Default is 'base.save' where the AUTOUGH2 data + file name is 'base.dat'. + +- | **incon_filename**: string + | Name of the initial conditions file for the simulation (AUTOUGH2 + only). Default is 'base.incon' where the AUTOUGH2 data file name is + 'base.dat'. + +- | **simulator**: string + | Name of the AUTOUGH2 or TOUGH2 executable. Default is 'AUTOUGH2_2'. + +- | **silent**: Boolean + | Set to ``True`` to suppress output to the display while running + (default is ``False``). + +- | **output_filename**: string + | Name of the output listing file for the simulation (TOUGH2 only). + Default is 'base.listing' where the base name of the TOUGH2 data + file (without file extension) is 'base'. + +---- + +.. _sec:t2data:specific_generation: + +``specific_generation(type='MASS', name='')`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns an ``np.array`` containing the total specific generation rate in +each block (i.e. generation rate per unit volume) for the specified +generator type and name. + +**Parameters:** + +- | **type**: string + | Generation type ('HEAT', 'MASS' etc.) – default is 'MASS'. + +- | **name**: string + | Regular expression to match generator names (e.g. 'SP...' (or + '^SP') will match all generators with names beginning with 'SP'.) + +---- + +.. _sec:t2data:transfer_from: + +``transfer_from(source, sourcegeo, geo, top_generator=[], bottom_generator=[], sourceinconfilename='', inconfilename='', rename_generators=False, preserve_generation_totals=False)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 data files; transferring + +Transfers data from another ``t2data`` object, and its associated +``mulgrid`` object. Parameters, rock types and rock type assignments, +and optionally initial conditions files are transferred. In general the +data for a given block in the geometry is found by identifying the +nearest block in the source geometry and transferring data from that +block. There are, however, exceptions, such as for generators that need +to remain on the surface or bottom of the model. The ``top_generator`` +and ``bottom_generator`` lists specify the 'layer' part of the generator +name for generators that should remain on the top or bottom of the +model, respectively. + +For generator types in which the ``gx`` and ``rate`` properties +represent generation rates (as opposed to other types for which these +properties are used to represent other things, e.g. productivity index +for wells on deliverability), the values of ``gx`` and ``rate`` are +scaled to account for the different volume of the block the generator +has been mapped into. If ``preserve_generation_totals`` is ``True``, and +a generator with generation rate :math:`G` is mapped into :math:`n` +blocks with volumes :math:`V_1, V_2,\ldots, V_n`, then the generation +rate for the new generator in block :math:`i` will be +:math:`G V_i/\sum_{k=1}^{n}{V_k}`. This should preserve the total +generation rate over the model. (For generator types matching the +``bottom_generator`` or ``top_generator`` specifications, the column +area instead of the block volume is used to determine the appropriate +scaling.) Note that of the columns a top or bottom generator is mapped +into, only those with centres inside the source geometry are included in +the scaling calculations. The generator types for which this scaling is +carried out are: 'AIR', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'HEAT', +'MASS', 'NACL', 'TRAC' and 'VOL'. + +If both ``sourceinconfilename`` and ``inconfilename`` are specified, a +new initial conditions file with filename ``inconfilename`` is written +to disk, with initial conditions transferred from the file +``sourceinconfilename``. + +**Parameters:** + +- | **source**: :ref:`t2data ` + | The ``t2data`` object to transfer data from. + +- | **sourcegeo**: :ref:`mulgrid ` + | The ``mulgrid`` object corresponding to ``source``. + +- | **geo**: :ref:`mulgrid ` + | The ``mulgrid`` object corresponding to the destination ``t2data`` + object. + +- | **top_generator**: list + | A list of generator 'layer' identifier strings for generators that + need to be kept at the top of the model (e.g. rain generators). + +- | **bottom_generator**: list + | A list of generator 'layer' identifier strings for generators that + need to be kept at the bottom of the model (e.g. basement heat and + mass inputs). + +- | **sourceinconfilename**: string + | Name of the (optional) initial conditions file to transfer initial + conditions data from (corresponding to ``source``). + +- | **inconfilename**: string + | Name of the (optional) initial conditions file to write, + corresponding to the destination ``t2data`` object. + +- | **rename_generators**: Boolean + | If ``False``, generators other than those at the top and bottom of + the model retain their original names. Otherwise, they will be + renamed according to their column names in the new grid. + +- | **preserve_generation_totals**: Boolean + | If ``False`` (the default), the transfer of generators will attempt + to preserve the distribution of specific generation of the original + model; otherwise, it will attempt to preserve the total generation + over the model. + +---- + +.. _sec:t2data:total_generation: + +``total_generation(type='MASS', name='')`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns an ``np.array`` containing the total generation rate in each +block for the specified generator type and name. + +**Parameters:** + +- | **type**: string + | Generation type ('HEAT', 'MASS' etc.) – default is 'MASS'. + +- | **name**: string + | Regular expression to match generator names (e.g. 'SP...' (or + '^SP') will match all generators with names beginning with 'SP'.) + +---- + +.. _sec:t2data:write: + +``write(filename='', meshfilename='', extra_precision=None, echo_extra_precision=None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 data files; writing + +Writes a ``t2data`` object to a TOUGH2 data file on disk. If the +``meshfilename`` parameter is used, mesh information can be written to +auxiliary mesh files. + +**Parameters:** + +- | **filename**: string + | Name of the TOUGH2 data file to be written. If no file name is + specified, the object's own ``filename`` property is used. + +- | **meshfilename**: string or tuple + | Name of auxiliary mesh file(s) to be written. If this is empty (the + default), the object's own ``meshfilename`` property is used. + Otherwise, if a single (non-empty) string is given, this in + interpreted as the name of a file to write formatted mesh + information to (as in the 'MESH' files produced by TOUGH2 and + TOUGH2_MP). If a tuple of two strings is given, this in interpreted + as the names of two binary files (as in the 'MESHA' and 'MESHB' + files produced by TOUGH2_MP). + +- | **extra_precision**: list or Boolean + | Controls whether to write extra precision data to auxiliary file + (AUTOUGH2 only). If set to ``True``, then all possible sections + will be written to the extra precision file. Currently the possible + extra-precision sections are the ROCKS, ELEME, CONNE, RPCAP and + GENER sections. If set to ``False`` or [], then no extra-precision + data will be written. If set to a list of section names (e.g. + ['RPCAP', 'GENER']), then only those sections will be written in + extra precision. If set to ``None`` (the default), then the value + of the data object's ``extra_precision`` property is used. + Otherwise, the value of this property is overwritten by the value + specified here. + +- | **echo_extra_precision**: Boolean or None + | Controls whether to echo all extra-precision data sections to the + main data file (AUTOUGH2 only). If ``None``, the value of the data + object's ``echo_extra_precision`` property is used. Otherwise, the + value of this property is overwritten by the value specified here. + +---- + +.. _t2generatorobjects: + +``t2generator`` objects +----------------------- + +.. index:: TOUGH2 data files; generators +.. index:: generators + +A ``t2generator`` object represents a generator in a TOUGH2 simulation +(i.e. an item in the generation table). The properties of a +``t2generator`` object are given in the +:ref:`table ` below. These correspond closely to the +parameters specified in the TOUGH2 **GENER** input block. A +``t2generator`` object has no methods. + +.. container:: + :name: tb:t2generator_properties + + .. table:: Properties of a ``t2generator`` object + + +--------------+---------------+--------------------------------+-----------------+ + | **Property** | **Type** | **Description** | **TOUGH2 | + | | | | parameter** | + +==============+===============+================================+=================+ + | ``block`` | string | name of block | EL, NE | + | | | containing the | | + | | | generator | | + +--------------+---------------+--------------------------------+-----------------+ + | ``enthalpy`` | list of float | generation enthalpies | F3 | + | | | (\|ltab\|>1, itab<>'') | | + | | | | | + | | | | | + | | | | | + | | | | | + | | | | | + +--------------+---------------+--------------------------------+-----------------+ + | ``ex`` | float | enthalpy for | EX | + | | | injection | | + +--------------+---------------+--------------------------------+-----------------+ + | ``gx`` | float | generation rate | GX | + | | | (or | | + | | | productivity | | + | | | index for | | + | | | deliverability) | | + +--------------+---------------+--------------------------------+-----------------+ + | ``hg`` | float | layer thickness | HG | + | | | for | | + | | | deliverability | | + +--------------+---------------+--------------------------------+-----------------+ + | ``fg`` | float | separator | FG | + | | | pressure/ | | + | | | injectivity | | + | | | etc. | | + +--------------+---------------+--------------------------------+-----------------+ + | ``itab`` | string | blank unless | ITAB | + | | | table of | | + | | | specific | | + | | | enthalpies | | + | | | specified | | + +--------------+---------------+--------------------------------+-----------------+ + | ``ltab`` | integer | number of | LTAB | + | | | generation | | + | | | times (or open | | + | | | layers for | | + | | | deliverability) | | + +--------------+---------------+--------------------------------+-----------------+ + | ``nadd`` | integer | successive | NADD | + | | | block increment | | + +--------------+---------------+--------------------------------+-----------------+ + | ``nads`` | integer | successive | NADS | + | | | generator | | + | | | increment | | + +--------------+---------------+--------------------------------+-----------------+ + | ``name`` | string | generator name | SL, NS | + +--------------+---------------+--------------------------------+-----------------+ + | ``nseq`` | integer | number of | NSEQ | + | | | additional | | + | | | generators | | + +--------------+---------------+--------------------------------+-----------------+ + | ``rate`` | list of float |generation rates (\|ltab\|>1) | F2 | + | | | | | + +--------------+---------------+--------------------------------+-----------------+ + | ``time`` | list of float |generation times (\|ltab\|>1) | F1 | + | | | | | + +--------------+---------------+--------------------------------+-----------------+ + | ``type`` | string |generator type (default 'MASS') | TYPE | + | | | | | + +--------------+---------------+--------------------------------+-----------------+ + +.. _example-1: + +Example +------- + +The following piece of Python script opens a MULgraph geometry file and +TOUGH2 data file, changes some TOUGH2 run-time parameters and assigns +heat generators to the blocks in the bottom layer inside a defined area, +with the specified total heat divided uniformly amongst the generators. + +:: + + geo = mulgrid('gmodel.dat') + dat = t2data('model.dat') + + dat.parameter['max_timesteps'] = 300 + dat.parameter['print_interval'] = dat.parameter['max_timesteps']/10 + dat.parameter['option'][16] = 5 # time step control + + dat.clear_generators() + totalheat = 10.e6 + layer = geo.layerlist[-1] # bottom layer + cols = [col for col in geo.columnlist if 10.e3 <= col.centre[0] <= 20.e3] + totalarea = sum([col.area for col in cols]) + q = totalheat / totalarea + + for col in cols: + blockname = geo.block_name(layer.name, col.name) + gen = t2generator(name = ' q'+col.name, block = blockname, type = 'HEAT', gx = q*col.area) + dat.add_generator(gen) + + dat.write() diff --git a/doc/source/t2grids.rst b/doc/source/t2grids.rst new file mode 100644 index 00000000..87972bdb --- /dev/null +++ b/doc/source/t2grids.rst @@ -0,0 +1,1328 @@ +:tocdepth: 3 + +.. _t2grids: + +TOUGH2 grids +============ + +.. index:: TOUGH2 grids + +.. _introduction-2: + +Introduction +------------ + +The ``t2grids`` library in PyTOUGH contains classes and routines for +manipulating TOUGH2 grids. It can be imported using the command: + +:: + + from t2grids import * + +``t2grid`` objects +------------------ + +The ``t2grids`` library defines a ``t2grid`` class, used for +representing TOUGH2 grids. This gives access via Python to the grid's +rock types, blocks, connections and other parameters. + +Normally a TOUGH2 grid is not created directly, but is either read from +a TOUGH2 data file, or constructed from a :ref:`mulgrid ` +geometry object using the :ref:`fromgeo() ` method. + +Printing a ``t2grid`` object (e.g. ``print(grid)``) displays a summary +of information about the grid: how many rock types, blocks and +connections it contains. + +.. _properties-1: + +Properties +~~~~~~~~~~ + +The main properties of a ``t2grid`` object are listed in the +:ref:`table ` below. Essentially a ``t2grid`` object +contains collections of blocks, rock types and connections, each +accessible either by name or by index. For example, block 'AB 20' in a +``t2grid`` called ``grid`` is given by ``grid.block['AB 20']``. + +Connections are slightly different from blocks or rock types, in that +they are not named individually. However, they can be accessed by the +names of the blocks connected by the connection. For example, the +connection between blocks 'aa 10' and 'ab 10' in a ``t2grid`` called +``grid`` is given by ``grid.connection['aa 10','ab 10']``. + +The ``rocktype_frequencies`` property gives information about how +frequently each rock type is used (i.e. how many blocks use that rock +type). It returns a list of tuples, the first element of each tuple +being the frequency of use, and the second element being a list of rock +type names with that frequency. The list is given in order of increasing +frequency. + +The ``rocktype_indices`` property gives an ``np.array`` containing the +index of the rocktype for each block in the grid. This can be used to +give a plot of rock types, in conjunction with the ``mulgrid`` methods +``layer_plot`` or ``slice_plot``. + +.. container:: + :name: tb:t2grid_properties + + .. table:: Properties of a ``t2grid`` object + + +---------------------------+----------------+-------------------------+ + | **Property** | **Type** | **Description** | + +===========================+================+=========================+ + | ``atmosphere_blocks`` | list | atmosphere blocks | + +---------------------------+----------------+-------------------------+ + | ``blocklist`` | list | blocks (by index) | + +---------------------------+----------------+-------------------------+ + | ``block`` | dictionary | blocks (by name) | + +---------------------------+----------------+-------------------------+ + | ``block_centres_defined`` | Boolean | whether block centres | + | | | have been calculated | + +---------------------------+----------------+-------------------------+ + | ``connectionlist`` | list | connections (by index) | + +---------------------------+----------------+-------------------------+ + | ``connection`` | dictionary | connections (by tuples | + | | | of block names) | + +---------------------------+----------------+-------------------------+ + | ``num_atmosphere_blocks`` | integer | number of atmosphere | + | | | blocks | + +---------------------------+----------------+-------------------------+ + | ``num_blocks`` | integer | number of blocks | + +---------------------------+----------------+-------------------------+ + | ``num_connections`` | integer | number of connections | + +---------------------------+----------------+-------------------------+ + | ``num_rocktypes`` | integer | number of rock types | + +---------------------------+----------------+-------------------------+ + | ``num_underground_blocks``| integer | number of | + | | | non-atmosphere blocks | + +---------------------------+----------------+-------------------------+ + | ``rocktypelist`` | list | rock types (by index) | + +---------------------------+----------------+-------------------------+ + | ``rocktype`` | dictionary | rock types (by name) | + +---------------------------+----------------+-------------------------+ + | ``rocktype_frequencies`` | list of tuples | frequencies of rock | + | | | types | + +---------------------------+----------------+-------------------------+ + | ``rocktype_indices`` | ``np.array`` | index of rock type for | + | | | each block | + +---------------------------+----------------+-------------------------+ + +.. _t2gridmethods: + +Methods +~~~~~~~ + +The main methods of a ``t2grid`` object are listed in the following +:ref:`table `. Details of these methods are given below. + +.. container:: + :name: tb:t2grid_methods + + .. table:: Methods of a ``t2grid`` object + + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | **Method** | **Type** | **Description** | + +==========================================================================+=============================+======================+ + | :ref:`+ ` | :ref:`t2grid ` | adds two grids | + | | | together | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`add_block ` | – | adds a block to the | + | | | grid | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`add_connection ` | – | adds a connection to | + | | | the grid | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`add_rocktype ` | – | adds a rock type to | + | | | the grid | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`blockmap ` | dictionary | returns block name | + | | | mapping from a | + | | | geometry | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`block_index ` | integer | returns index of a | + | | | block with a | + | | | specified name | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`calculate_block_centres ` | – | calculates | + | | | geometrical centre | + | | | of all blocks in the | + | | | grid | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`check ` | Boolean | checks grid for | + | | | errors and | + | | | optionally fixes | + | | | them | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`clean_rocktypes ` | – | deletes any unused | + | | | rock types from the | + | | | grid | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`connection_index ` | integer | returns index of a | + | | | connection with a | + | | | specified pair of | + | | | names | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`copy_connection_directions `| – | copies connection | + | | | permeability | + | | | directions from | + | | | another grid | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`delete_block ` | – | deletes a block from | + | | | the grid | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`delete_connection ` | – | deletes a connection | + | | | from the grid | + | | | | + | | | | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`delete_rocktype ` | – | deletes a rock type | + | | | from the grid | + | | | | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`demote_block ` | – | shifts a block (or | + | | | blocks) to the end | + | | | of the blocklist | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`embed ` | :ref:`t2grid ` | embeds a subgrid | + | | | inside one block of | + | | | another | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`empty ` | – | empties contents of | + | | | grid | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`flux_matrix ` | ``scipy.sparse.lil_matrix`` | constructs a sparse | + | | | matrix for | + | | | calculating | + | | | block-average flows | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`fromgeo ` | :ref:`t2grid ` | constructs a TOUGH2 | + | | | grid from a | + | | | ``mulgrid`` object | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`incons ` | :ref:`t2incon ` | constructs initial | + | | | conditions for the | + | | | grid | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`minc ` | list | creates MINC blocks | + | | | and connections | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`radial ` | :ref:`t2grid ` | constructs a radial | + | | | TOUGH2 grid | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`rectgeo ` | (:ref:`mulgrid `, | constructs a | + | | ``dict``) | ``mulgrid`` object | + | | | from a rectangular | + | | | TOUGH2 grid | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`rename_blocks ` | – | renames blocks the | + | | | grid | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`rename_rocktype ` | – | renames a rock type | + | | | in the grid | + | | | | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`reorder ` | – | reorders blocks and | + | | | connections in the | + | | | grid | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`rocktype_frequency ` | integer | frequency of use of | + | | | a particular rock | + | | | type | + | | | | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`sort_rocktypes ` | – | sorts rock type list | + | | | into alphabetical | + | | | order by name | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + | :ref:`write_vtk ` | – | writes grid to VTK | + | | | file | + | | | | + +--------------------------------------------------------------------------+-----------------------------+----------------------+ + +.. _sec:t2grid:plus: + +``+`` +^^^^^ + +Adds two grids ``a`` and ``b`` together (i.e. amalgamates them) to form +a new grid ``a+b``. If any rock types, blocks or connections exist in +both grids ``a`` and ``b``, the value from ``b`` is used, so there are +no duplicates. (Technically this is really an 'operator' rather than a +method.) + +**Parameters:** + +- | **a, b**: :ref:`t2grid ` + | The two grids to be added together. + +---- + +.. _sec:t2grid:add_block: + +``add_block(block)`` +^^^^^^^^^^^^^^^^^^^^ + +Adds a block to the grid. If another block with the same name already +exists, it is replaced. + +**Parameters:** + +- | **block**: :ref:`t2block ` + | Block to be added to the grid. + +---- + +.. _sec:t2grid:add_connection: + +``add_connection(connection)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Adds a connection to the grid. If another connection with the same +column names already exists, it is replaced. + +**Parameters:** + +- | **connection**: :ref:`t2connection ` + | Connection to be added to the grid. + +---- + +.. _sec:t2grid:add_rocktype: + +``add_rocktype(rock)`` +^^^^^^^^^^^^^^^^^^^^^^ + +Adds a rock type to the grid. If another rock type with the same name +already exists, it is replaced. + +**Parameters:** + +- | **rock**: :ref:`rocktype ` + | Rock type to be added to the grid. + +---- + +.. _sec:t2grid:block_index: + +``block_index(blockname)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns the block index (in the ``blocklist`` list) of a specified block +name. + +**Parameters:** + +- | **blockname**: string + | Name of the block. + +---- + +.. _sec:t2grid:blockmap: + +``blockmap(geo, index = None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 grids; block mappings + +Returns a mapping from the block name list of the specified geometry +object to the block names in the grid. + +**Parameters:** + +- | **geo**: :ref:`mulgrid ` + | Geometry object. + +- | **index**: list (or ``None``) + | Specifies a list of integer indices defining which blocks in the + grid to map to. If ``None``, all blocks are mapped to. + +---- + +.. _sec:t2grid:calculate_block_centres: + +``calculate_block_centres(geo)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Calculates geometrical centres of all blocks in the grid, based on the +specified geometry object ``geo``. + +**Parameters:** + +- | **geo**: :ref:`mulgrid ` + | Geometry object associated with the grid. + +---- + +.. _sec:t2grid:check: + +``check(fix=False,silent=False)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 grids; checking + +Checks a grid for errors and optionally fixes them. Errors checked for +are: blocks not connected to any other blocks, and blocks with isolated +rocktypes (not shared with any neighbouring blocks). Returns ``True`` if +no errors were found, and ``False`` otherwise. If ``fix`` is ``True``, +any identified problems will be fixed. If ``silent`` is ``True``, there +is no printout (only really useful if ``fix`` is ``True``). + +Blocks not connected to any others are fixed by deleting them. +Isolated-rocktype blocks are fixed by assigning them the most popular +rocktype of their neighbours. Blocks with large volumes +(:math:`> 10^{20}` m\ :math:`^3`) are never considered isolated (because +they often have a special rocktype, such as an atmosphere one, that +their neighbours will never share). + +**Parameters:** + +- | **fix**: Boolean + | Whether to fix any problems identified. + +- | **silent**: Boolean + | Whether to print out feedback or not. + +---- + +.. _sec:t2grid:clean_rocktypes: + +``clean_rocktypes()`` +^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 grids; cleaning rocktypes + +Deletes any rock types from the grid which are not assigned to any +block. + +---- + +.. _sec:t2grid:connection_index: + +``connection_index(blocknames)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns the connection index (in the ``connectionlist`` list) of the +connection between a specified pair of block names. + +**Parameters:** + +- | **blocknames**: tuple + | A pair of block names, each of type string. + +---- + +.. _sec:t2grid:copy_connection_directions: + +``copy_connection_directions(geo,grid)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Copies the connection permeability directions for horizontal connections +from another grid. It is assumed that both grids have the same column +structure, but may have different layer structures. + +**Parameters:** + +- | **geo**: :ref:`mulgrid ` + | Geometry object associated with the source grid. + +- | **grid**: :ref:`t2grid ` + | The source grid from which the connection permeability directions + are to be copied. + +---- + +.. _sec:t2grid:delete_block: + +``delete_block(blockname)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Deletes a block from the grid. This also deletes any connections +involving the specified block. + +**Parameters:** + +- | **blockname**: string + | Name of the block to be deleted from the grid. + +---- + +.. _sec:t2grid:delete_connection: + +``delete_connection(connectionname)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Deletes a connection from the grid. + +**Parameters:** + +- | **connectionname**: tuple (of string) + | Pair of block names identifying the connection to be deleted from + the grid. + +---- + +.. _sec:t2grid:delete_rocktype: + +``delete_rocktype(rocktypename)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Deletes a rock type from the grid. + +**Parameters:** + +- | **rocktypename**: string + | Name of the rock type to be deleted from the grid. + +---- + +.. _sec:t2grid:demote_block: + +``demote_block(blockname)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Shifts a block (or blocks) to the end of the blocklist. This can be +useful for making blocks inactive - by setting their volumes to zero or +negative, and then shifting them to the end of the list (to avoid all +blocks below them also being treated as inactive). + +**Parameters:** + +- | **blockname**: string or list of strings + | Name(s) of the block(s) to be shifted to the end of the blocklist. + +---- + +.. _sec:t2grid:embed: + +``embed(subgrid, connection)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 grids; embedding + +Returns a grid with a subgrid embedded inside one of its blocks. The +connection specifies how the two grids are to be connected: the blocks +to be connected and the connection distances, area etc. between them. + +**Parameters:** + +- | **subgrid**: :ref:`t2grid ` + | Subgrid to be embedded. + +- | **connection**: :ref:`t2connection ` + | Connection specifying how the subgrid is to be embedded, including + the connection distances and area. The first block should be the + host block, the second the connecting block in the subgrid. + +---- + +.. _sec:t2grid:empty: + +``empty()`` +^^^^^^^^^^^ + +.. index:: TOUGH2 grids; emptying + +Empties the grid of all its blocks, rock types and connections. + +---- + +.. _sec:t2grid:flux_matrix: + +``flux_matrix(geo, blockmap = {})`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 grids; flux matrices + +Takes the grid and a corresponding :ref:`mulgrid ` object, +and constructs a sparse matrix (of type ``scipy.sparse.lil_matrix``) +which can be used to convert connection flow values on the grid to +block-average fluxes (flows per unit area). Specifically, if an array of +connection flow values (one for each connection in the grid) is +multiplied by this sparse matrix, the result is a partitioned array +containing the 3-component block-average flux for each of the +(non-atmosphere) blocks. + +The method for constructing the matrix is as follows. For each block, a +distribution of flux is fitted to agree as closely as possible with the +connection flow values. This distribution is either constant or linear, +depending on how many connections the block has (linear for blocks with +at least 6 connections). Fitting the connection values results in a +small linear system to solve, which may be under- or over-determined, +depending on the number of connections and the type of flux +distribution. A pseudo-inverse matrix is calculated which will find the +least-squares solution of this system. The total matrix is formed by +assembling these matrices for each of the blocks into a global matrix. + +**Parameters:** + +- | **geo**: :ref:`mulgrid ` + | The ``mulgrid`` geometry object. + +- | **blockmap**: dictionary + | Dictionary mapping the block names in the geometry to the block + naming system used in the grid. + +---- + +.. _sec:t2grid:fromgeo: + +``fromgeo(geo)`` +^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 grids; from MULgraph geometry + +Returns a grid constructed from a ``mulgrid`` geometry object. (Any +previous contents of the grid are first emptied.) + +**Parameters:** + +- | **geo**: :ref:`mulgrid ` + | The ``mulgrid`` geometry object. + +---- + +.. _sec:t2grid:incons: + +``incons(values=(101.3e3,20.))`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 grids; initial conditions + +Returns a :ref:`t2incon ` initial conditions object for the +grid, using the supplied values. Initial conditions can be specified for +only one block, in which case they will be applied to all blocks, or for +each block, in an array. + +**Parameters:** + +- | **values**: ``tuple`` or ``np.array`` + | Initial conditions values, either a ``tuple`` of values for one + block, or an ``np.array`` with each row containing a set of values + for one block. + +---- + +.. _sec:t2grid:MINC: + +``minc(volume_fractions, spacing=50., num_fracture_planes=1, blocks=None, matrix_blockname=None, minc_rockname=None, proximity=None, atmos_volume=1.e25, incon=None, fracture_connection_distance=0.)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 grids; MINC + +Creates "Multiple Interacting Continua" (MINC) blocks and connections +in the grid, for simulating fracture flow with matrix blocks attached +to each fracture block. This has capability similar to that of the +`GMINC `_ program , or of the +MINC part of TOUGH2's :ref:`MESHMAKER ` section +(except that matrix-matrix flow is not supported). + +This function returns a rank-2 integer ``np.array`` with one row for +each MINC level, containing the indices of the blocks for that level. +For example, the first row is a list of all fracture block indices, the +second is a list of all MINC level 1 block indices, etc. This can be +useful for identifying all blocks in a given MINC level, for plotting or +other post-processing. + +For example, if the output index array from this method is +``minc_level``, and ``T`` is an array of temperatures computed over the +entire MINC grid (e.g. extracted from the element table of a listing +file), then the temperatures in MINC level ``m`` are given by: + +:: + + T[minc_level[m]] + +Note that plotting MINC results over a :ref:`mulgrid ` +geometry can be made easier (particularly for grids that have MINC +applied over only part of the domain) by using the +:ref:`minc_array() ` method to create +the solution vector to plot. + +If the ``incon`` parameter is specified as a :ref:`t2incon ` +object (from the original grid), then this method will also return a new +``t2incon`` object for the MINC grid, with values copied from the +original. + +Fracture blocks retain the same block name as their original porous +medium blocks. The naming of matrix blocks can be controlled using the +``matrix_blockname`` parameter. + +**Parameters:** + +- | **volume_fractions**: list (or ``np.array``) + | List or array of volume fractions. The first entry corresponds to + the fractures, with subsequent entries specifying the volume + fractions for each MINC level. The length of this list or array is + therefore equal to one plus the number of matrix blocks to be used. + Entries for all MINC levels must be present, but they need not sum + to 1- if they do not, they will be scaled so that the sum is 1. + (This means, for example, that entries may be specified as + percentage values.) + +- | **spacing**: float or list (or ``np.array``) + | Fracture spacing parameters. If a float value is specified, this is + applied to all sets of fracture planes (see below). If a list or + array is specified, each entry is applied to its corresponding set + of fracture planes. + +- | **num_fracture_planes**: integer + | Number of sets of fracture planes (1, 2 or 3). + +- | **blocks**: list (or ``None``) + | List of blocks or block names, specifying which blocks are to have + MINC applied. If this parameter is ``None``, all blocks are + processed (except inactive blocks). + +- | **matrix_blockname**: function (or ``None``) + | Function returning the name of a MINC matrix block (string), given + the original block name (string) and MINC level (integer > 0). If + ``None``, a default function will be used, which simply replaces + the first character of the original block name with the MINC level. + +- | **minc_rockname**: function (or ``None``) + | Function returning the MINC rocktype name, given the original + rocktype name and MINC level (:math:`\geq 0`). If ``None``, a + default function will be used, which leaves fracture blocks with + their original rocktype (the properties of which can subsequently + be edited), and for matrix blocks, simply replaces the first + character of the original rocktype name with 'X'. + +- | **proximity**: function (or ``None``) + | Proximity function, returning the total matrix volume within a + given distance (float) from the fracture faces. If ``None``, a + default function will be used, corresponding to the + ``num_fracture_planes`` parameter. + +- | **atmos_volume**: float + | Maximum block volume for blocks to be considered part of the + geometrical grid. Blocks with volume greater than this will be + assumed to be boundary condition blocks and no MINC processing will + be applied to them. + +- | **incon**: :ref:`t2incon ` (or ``None``) + | Initial conditions object for the original grid, before MINC + processing. If not ``None``, then the method returns (as well as + the block index array) a new ``t2incon`` object for the MINC grid, + with values for each block copied from the original (for all MINC + levels). + +- | **fracture_connection_distance**: float + | Connection distance between fracture and matrix blocks. Default is + zero, as in MESHMAKER, but in some situations a finite value (e.g. + :math:`10^{-10}` m) can work better. + +---- + +.. _sec:t2grid:radial: + +``radial(rblocks, zblocks, convention=0, atmos_type=2, origin=[0,0], justify='r', case=None, dimension=2, blockmap={}, chars=ascii_lowercase, spaces=True)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 grids; radial + +Returns a radial TOUGH2 grid with the specified radial and vertical +block sizes. Grid column and layer naming convention, atmosphere type +and origin can be specified. The optional ``justify`` and ``case`` +parameters control the formatting of the character part of the block +names. + +The ``dimension`` parameter sets the flow dimension for `"generalized +radial flow" `_, which can +represent flow in fractured rocks and modifies the block volumes and +areas. The default ``dimension`` = 2 corresponds to standard radial +flow. + +**Parameters:** + +- | **rblocks**, **zblocks**: list (or ``np.array``) + | Lists (or arrays) of block sizes in the *r* and *z* directions. + +- | **convention**: integer + | Naming convention for grid columns and layers - same as the + :ref:`naming convention ` for a + :ref:`mulgrid ` object. + +- | **atmos_type**: integer + | Type of atmosphere - also the same as the + :ref:`atmosphere type ` for a + :ref:`mulgrid ` object. + +- | **origin**: list (or ``np.array``) + | Origin of the grid (of length 2 or 3). The first entry is the + radial origin, i.e. the starting radius of the grid. The last entry + is the vertical origin, i.e. the vertical position of the top of + the grid. If of length 3, the middle entry is ignored. + +- | **justify**: string + | Specify 'r' for the character part of the block names (first three + characters) to be right-justified, 'l' for left-justified. + +- | **case**: string + | Specify 'l' for the character part of the block names (first three + characters) to be lower case, 'u' for upper case. Alternatively, + use the more flexible ``chars`` parameter (see below). + +- | **dimension**: float + | Dimension for 'generalized radial flow', which can take any + (possibly non-integer) value between 1 and 3. Dimension 1 + corresponds to flow in a linear 'pipe', dimension 2 corresponds to + standard radial flow in a disc-shaped reservoir and dimension 3 + corresponds to flow in a spherically symmetric reservoir. + +- | **blockmap**: dictionary + | Dictionary mapping the block names in the geometry to the block + naming system used in the grid. + +- | **chars**: string + | Specify a string of characters to be used to form the character + part of block names. For example, to use both lowercase and + uppercase characters, set ``chars`` to + ``ascii_lowercase + ascii_uppercase``, or to use uppercase letters + only, specify ``ascii_uppercase``. + +- | **spaces**: Boolean + | Specify ``False`` to disallow spaces in character part of block + names. In this case, the first element of the ``chars`` parameter + functions like a 'zero' and replaces spaces. + +Visualization of radial :math:`r-z` model grids and results can be done +in PyTOUGH by creating a 'dummy' vertical slice rectangular geometry, +using the ``mulgrid`` :ref:`rectangular() ` +method, using its :math:`x` direction for radius (and +having only one block in the :math:`y` direction - which is not used). +The :ref:`slice_plot() ` method can +then be used to plot results. + +---- + +.. _sec:t2grid:rectgeo: + +``rectgeo(origin_block=None, atmos_volume=1.e25, remove_inactive=False, convention=0, atmos_type=2, justify='r', chars=ascii_lowercase, spaces=True, layer_snap=0.1, block_order=None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: MULgraph geometry; rectangular + +Creates a :ref:`mulgrid ` geometry object from a +rectangular TOUGH2 grid. It also returns a dictionary defining the +mapping from the geometry block names to the grid block names. This +block mapping can be used when the block naming convention used by the +original TOUGH2 grid is not compatible with the layer/column based +:ref:`naming conventions ` assumed by a +``mulgrid`` geometry. + +The method works within the following assumptions: + +- the grid is in fact rectangular (results will not be predictable + otherwise) + +- block centre coordinates are present for all blocks in the grid + +- the bottom layer of blocks is complete (no missing blocks) + +The method should work on rectangular TOUGH2 grids that have been +translated and/or horizontally rotated with respect to the coordinate +axes. Grids with incomplete upper layers (e.g. representing topography) +should also be OK. + +**Parameters:** + +- | **origin_block**: string, :ref:`t2block ` + or ``None`` + | The block on the bottom layer of the geometry, at the origin of the + axes defined by permeability directions 1 and 2. If ``None``, it + will be detected. Specify it manually if the algorithm does not + detect it correctly. + +- | **atmos_volume**: float + | Block volume below which blocks are considered part of the + geometrical grid. Blocks with volume greater than or equal to this + value will be assumed to be boundary condition blocks and will not + be represented geometrically. + +- | **remove_inactive**: Boolean + | Set ``True`` to remove inactive blocks from the geometry. TOUGH2 + treats all blocks with zero or negative volume, and all subsequent + blocks in the block list, to be inactive. If this option is used, + the inactive blocks will be used to detect the surface elevations + of the columns in the geometry. Otherwise, inactive blocks will be + retained in the geometry. + +- | **convention**: integer + | :ref:`Naming convention ` for grid + columns and layers in the output geometry. + +- | **atmos_type**: integer + | :ref:`Atmosphere type ` for the + output geometry. + +- | **justify**: string + | Specify 'r' for the character part of the block names (first three + characters) to be right-justified, 'l' for left-justified. + +- | **chars**: string + | Specify a string of characters to be used to form the character + part of block names. For example, to use both lowercase and + uppercase characters, set ``chars`` to + ``ascii_lowercase + ascii_uppercase``, or to use uppercase letters + only, specify ``ascii_uppercase``. + +- | **spaces**: Boolean + | Specify ``False`` to disallow spaces in character part of block + names. In this case, the first element of the ``chars`` parameter + functions like a 'zero' and replaces spaces. + +- | **layer_snap**: float + | Smallest desired surface block thickness. Set to a positive value + to eliminate surface blocks in the geometry with very small + thicknesses (resulting from column surface elevations that are very + close to the bottom of a layer). Default value is 0.1 m. Note that + it is not recommended to use a value of zero, as spurious + small-thickness surface blocks can arise from rounding errors in + reading the data file. If this still occurs, try increasing the + snap value until they disappear. + +- | **block_order**: string or ``None`` + | Specify ``None`` or 'layer_column' for default block ordering by + layer and column, starting from the atmosphere. Specify 'dmplex' to + order blocks by geometrical type (8-node hexahedrons first followed + by 6-node wedges) as in PETSc DMPlex meshes. + +---- + +.. _sec:t2grid:rename_blocks: + +``rename_blocks(blockmap = {}, fix_blocknames = True)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Renames blocks in the grid according to the specified block mapping +dictionary. Any block whose name is a key of the block mapping +dictionary is renamed with the corresponding dictionary value. Related +properties such as connections are also renamed. + +**Parameters:** + +- | **blockmap**: dictionary + | Block mapping dictionary, mapping strings to strings. + +- | **fix_blocknames**: Boolean + | Set ``True`` (the default) to 'fix' block names in the dictionary, + using the :ref:`fix_blockname() ` function. + +---- + +.. _sec:t2grid:rename_rocktype: + +``rename_rocktype(rockname, newrockname)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Renames a rock type in the grid. An exception is raised if the specified +rocktype name does not exist, or if the new target rocktype name has +already been used. + +**Parameters:** + +- | **rockname**: string + | Name of the rock type to be renamed. + +- | **newrockname**: string + | New name for the rock type. + +---- + +.. _sec:t2grid:reorder: + +``reorder(block_names, connection_names=None, geo=None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 grids; reordering + +Reorders the blocks (and optionally connections) in the grid. + +**Parameters:** + +- | **block_names**: list of string (or ``None``) + | List specifying the names of the blocks, in their desired order. + Each block name must exist in the grid, otherwise an error will be + raised. If this parameter is ``None`` (the default), blocks are not + reordered (unless a geometry is specified instead). + +- | **connection_names**: list of string (or ``None``) + | List specifying the names of the connections, in their desired + order. Each item in the list should be a tuple of block names. The + ordering of the block names in any tuple may be reversed with + respect to the original connection naming. However an error will be + raised if any tuple of block names in the list does not exist in + the grid (in either its forward or reverse form). If this parameter + is ``None`` (the default), connections are not reordered (unless a + geometry is specified instead). + +- | **geo**: :ref:`mulgrid ` geometry (or ``None``) + | Geometry object to use for the reordering. If this is specified, + the geometry's block and connection name lists are used (and the + previous parameters are ignored). After reordering, the grid's + blocks and connections will have the same ordering as if the grid + had been created using the :ref:`fromgeo() ` + method. + +---- + +.. _sec:t2grid:rocktype_frequency: + +``rocktype_frequency(rockname)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns the frequency of use of the rock type with the specified name, +i.e. how many blocks are assigned that rock type. + +**Parameters:** + +- | **rockname**: string + | Name of the specified rock type. + +---- + +.. _sec:t2grid:sort_rocktypes: + +``sort_rocktypes()`` +^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 grids; sorting rocktypes + +Sorts the rocktype list into alphabetical order by name. + +---- + +.. _sec:t2grid:write_vtk: + +``write_vtk(geo, filename, wells=False, blockmap = {}, surface_snap=0.1)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 grids; VTK + +Writes a ``t2grid`` object to a VTK file on disk, for visualisation with +VTK, Paraview, Mayavi etc. The grid is written as an 'unstructured grid' +VTK object with data arrays defined on cells. The data arrays written, +in addition to the defaults arrays for the associated ``mulgrid`` +object, are: rock type index, porosity and permeability for each block. +A separate VTK file for the wells in the grid can optionally be written. + +**Parameters:** + +- | **geo**: :ref:`mulgrid ` + | The ``mulgrid`` geometry object associated with the grid. This is + required as the ``t2grid`` object does not contain any spatial + information, e.g. locations of block vertices. + +- | **filename**: string + | Name of the VTK file to be written. This is also required. + +- | **wells**: Boolean + | Set to ``True`` if the wells from the ``mulgrid`` object are to be + written to a separate VTK file. + +- | **blockmap**: dictionary + | Dictionary mapping the block names in the geometry to the block + naming system used in the grid. + +- | **surface_snap**: float + | Tolerance for specifying how close column surface elevations need + to be before being considered "equal" when constructing surface + nodes. + +---- + +Other objects (``rocktype``, ``t2block`` and ``t2connection``) +-------------------------------------------------------------- + +A ``t2grid`` object contains lists of other types of objects: +``rocktype``, ``t2block`` and ``t2connection``. These classes are +described below. + +.. _rocktypeobjects: + +``rocktype`` objects +~~~~~~~~~~~~~~~~~~~~ + +.. index:: TOUGH2 grids; rocktypes +.. index:: rocktypes + +A ``rocktype`` object represents a TOUGH2 rock type. The properties of a +``rocktype`` object, and their default values, are given in the +:ref:`table ` below. + +.. container:: + :name: tb:rocktype_properties + + .. table:: Properties of a ``rocktype`` object + + +--------------------------+--------------+----------------+-----------------------------------------------+ + | **Property** | **Type** | **Description**| **Default** | + | | | | | + +==========================+==============+================+===============================================+ + | ``capillarity`` | dictionary | capillarity | – | + | | | function | | + +--------------------------+--------------+----------------+-----------------------------------------------+ + | ``compressibility`` | float | compressibility| 0 m\ :sup:`2`/N | + | | | | | + | | | | | + | | | | | + +--------------------------+--------------+----------------+-----------------------------------------------+ + | ``conductivity`` | float | heat | 1.5 W/m/K | + | | | conductivity | | + | | | | | + | | | | | + +--------------------------+--------------+----------------+-----------------------------------------------+ + | ``density`` | float | rock grain | 2600 kg/m\ :sup:`3` | + | | | density | | + | | | | | + +--------------------------+--------------+----------------+-----------------------------------------------+ + | ``dry_conductivity`` | float | dry heat | wet heat | + | | | conductivity | conductivity | + +--------------------------+--------------+----------------+-----------------------------------------------+ + | ``expansivity`` | float | expansivity | 0 K\ :sup:`-1` | + | | | | | + | | | | | + +--------------------------+--------------+----------------+-----------------------------------------------+ + | ``klinkenberg`` | float | Klinkenberg | 0 Pa\ :sup:`-1` | + | | | parameter | | + | | | | | + +--------------------------+--------------+----------------+-----------------------------------------------+ + | ``nad`` | integer | number of | 0 | + | | | extra data | | + | | | lines | | + +--------------------------+--------------+----------------+-----------------------------------------------+ + | ``name`` | string | rock type name | 'dfalt' | + +--------------------------+--------------+----------------+-----------------------------------------------+ + | ``permeability`` | ``np.array`` | permeability | ``np.array``\ ([10\ :sup:`-15`]*3) m\ :sup:`2`| + | | | | | + | | | | | + | | | | | + +--------------------------+--------------+----------------+-----------------------------------------------+ + | ``porosity`` | float | porosity | 0.1 | + +--------------------------+--------------+----------------+-----------------------------------------------+ + | ``relative_permeability``| dictionary | relative | – | + | | | permeability | | + | | | function | | + +--------------------------+--------------+----------------+-----------------------------------------------+ + | ``specific_heat`` | float | rock grain | 900 J/kg/K | + | | | specific heat | | + | | | | | + | | | | | + +--------------------------+--------------+----------------+-----------------------------------------------+ + | ``tortuosity`` | float | tortuosity | 0 | + | | | factor | | + +--------------------------+--------------+----------------+-----------------------------------------------+ + | ``xkd3`` | float | used by EOS7R | 0 m\ :sup:`3`/kg | + | | | | | + | | | | | + | | | | | + +--------------------------+--------------+----------------+-----------------------------------------------+ + | ``xkd4`` | float | used by EOS7R | 0 m\ :sup:`3`/kg | + | | | | | + | | | | | + | | | | | + +--------------------------+--------------+----------------+-----------------------------------------------+ + +The main familiar properties of a rock type are referred to in a natural +way, e.g. the porosity of a rock type ``r`` is given by ``r.porosity``. +The permeability property is a 3-element ``np.array``, giving the +permeability in each of the three principal axes of the grid, so e.g. +the vertical permeability of a rock type ``r`` would normally be given +by ``r.permeability[2]`` (recall that array indices in Python are +zero-based, so that the third element has index 2). + +Some rock type properties are optional, and only need be specified when +the property ``nad`` is greater than zero. An example is the relative +permeability and capillarity functions that can be specified for a rock +type when ``nad`` :math:`\ge` 2. The way these functions are specified +is described in :ref:`TOUGH2 data files `. + +**Example:** + +:: + + r = rocktype(name = 'ignim', permeability = [10.e-15, 10.e-15, 2.e-15], specific_heat = 850) + +declares a rocktype object called ``r`` with name 'ignim', permeability +of 10 mD in the first and second directions and 2 mD in the vertical +direction, and specific heat 850 J.kg\ :math:`^{-1}`.K\ :math:`^{-1}`. + +(Note that when declaring rock types, the permeability can for +convenience be specified as a list, which will be converted internally +to an ``np.array``.) + + +.. _t2blockobjects: + +``t2block`` objects +~~~~~~~~~~~~~~~~~~~ + +.. index:: TOUGH2 grids; blocks +.. index:: blocks + +A ``t2block`` object represents a block in a TOUGH2 grid. The properties +of a ``t2block`` object are given in the +:ref:`table ` below. These reflect the specifications of a +TOUGH2 block as given in a TOUGH2 data file, with the exception of the +``atmosphere``, ``centre``, ``connection_name``, ``neighbour_name`` and +``num_connections`` properties. + +.. container:: + :name: tb:t2block_properties + + .. table:: Properties of a ``t2block`` object + + +---------------------+--------------+-----------------------+ + | **Property** | **Type** | **Description** | + +=====================+==============+=======================+ + | ``ahtx`` | float | interface area for | + | | | heat exchange (TOUGH2 | + | | | only) | + +---------------------+--------------+-----------------------+ + | ``atmosphere`` | Boolean | whether block is an | + | | | atmosphere block or | + | | | not | + +---------------------+--------------+-----------------------+ + | ``centre`` | ``np.array`` | block centre | + | | | (optional) | + +---------------------+--------------+-----------------------+ + | ``connection_name`` | set | names of connections | + | | | involving the block | + +---------------------+--------------+-----------------------+ + | ``nadd`` | integer | increment between | + | | | block numbers in | + | | | sequence | + +---------------------+--------------+-----------------------+ + | ``name`` | string | block name | + +---------------------+--------------+-----------------------+ + | ``neighbour_name`` | set | names of neighbouring | + | | | (connected) blocks | + +---------------------+--------------+-----------------------+ + | ``nseq`` | integer | number of additional | + | | | blocks in sequence | + +---------------------+--------------+-----------------------+ + | ``num_connections`` | integer | number of connections | + | | | containing the block | + +---------------------+--------------+-----------------------+ + | ``pmx`` | float | permeability modifier | + | | | (TOUGH2 only) | + +---------------------+--------------+-----------------------+ + | ``rocktype`` | ``rocktype`` | rock type | + +---------------------+--------------+-----------------------+ + | ``volume`` | float | block volume | + +---------------------+--------------+-----------------------+ + +The ``atmosphere`` property determines whether the block is to be +treated as an atmosphere block. The ``centre`` property can optionally +be used to specify the coordinates of the centre of a block. Block +centres are automatically calculated when a :ref:`t2grid ` +object is constructed from a :ref:`mulgrid ` object using +the :ref:`fromgeo ` method). The +``connection_name`` property is a set containing the names (as tuples of +strings) of all connections involving the block. + +A ``t2block`` object has no methods. + +.. _t2connectionobjects: + +``t2connection`` objects +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. index:: TOUGH2 grids; connections +.. index:: connections + +A ``t2connection`` object represents a connections between two TOUGH2 +blocks. The properties of a ``t2connnection`` object are given in the +:ref:`table ` below. These correspond to the +properties of a connection specified in a TOUGH2 data file. Note that +the ``block`` property returns :ref:`t2block ` +objects, not just the names of the blocks in the connection. Hence, for +example, the volume of the first block in a connection object ``con`` is +given simply by ``con.block[0].volume``. + +A ``t2connection`` object has no methods. + +.. container:: + :name: tb:t2connection_properties + + .. table:: Properties of a ``t2connection`` object + + +--------------+----------+----------------------------------------------+ + | **Property** | **Type** | **Description** | + +==============+==========+==============================================+ + | ``area`` | float | connection area | + +--------------+----------+----------------------------------------------+ + | ``block`` | ``list`` | two-element list of blocks | + +--------------+----------+----------------------------------------------+ + | ``dircos`` | float | gravity direction cosine | + +--------------+----------+----------------------------------------------+ + | ``direction``| integer | permeability direction (1, 2, or 3) | + +--------------+----------+----------------------------------------------+ + | ``distance`` | ``list`` | two-element list of connection distances | + +--------------+----------+----------------------------------------------+ + | ``nad1,nad2``| integer | increments in sequence numbering | + +--------------+----------+----------------------------------------------+ + | ``nseq`` | integer | number of additional connections in sequence | + +--------------+----------+----------------------------------------------+ + | ``sigma`` | float | radiant emittance factor (TOUGH2 only) | + +--------------+----------+----------------------------------------------+ + +Example +------- + +The following piece of Python script creates a rectangular 2-D slice +TOUGH2 grid with two rock types, and assigns these rock types to blocks +in the grid according to their position along the slice. + +:: + + from t2grids import * + + geo = mulgrid().rectangular([500]*20, [1000], [100]*20, atmos_type = 0, convention = 2) + geo.write('2Dgrd.dat') + grid = t2grid().fromgeo(geo) + + grid.add_rocktype(rocktype('greyw', permeability = [1.e-15]*2 + [0.1e-15])) + grid.add_rocktype(rocktype('fill ', permeability = [15.e-15]*2 + [5.e-15])) + + for blk in grid.blocklist[1:]: + if 200 <= blk.centre[0] <= 400: blk.rocktype = grid.rocktype['fill '] + else: blk.rocktype = grid.rocktype['greyw'] + +The first line just imports the required PyTOUGH library. (It is not +necessary to import the ``mulgrids`` library explicitly, because it is +used and therefore imported by the ``t2grids`` library.) + +The second block of code creates a rectangular MULgraph geometry object +with 20 columns (each 500 m wide) along the slice and 20 layers (each +100 m thick), writes this to a geometry file on disk, and creates a +TOUGH2 grid from it. + +Then the two rock types are created, ``'greyw'`` and ``'fill '``. (Note +that rock types are expected by TOUGH2 to have names 5 characters long, +so it is necessary to add spaces to shorter names.) + +The final part assigns the rock types to the blocks in the grid. The +loop starts from 1 instead of 0, so that the atmosphere block is +skipped. In this example, the blocks in the grid are assigned the +``'fill '`` rock type if they are between 200 m and 400 m along the +slice. Blocks outside this region are assigned the ``'greyw'`` rock +type. diff --git a/doc/source/t2incon.rst b/doc/source/t2incon.rst new file mode 100644 index 00000000..9bfb3eeb --- /dev/null +++ b/doc/source/t2incon.rst @@ -0,0 +1,514 @@ +:tocdepth: 3 + +.. _incons: + +TOUGH2 initial conditions +========================= + +.. index:: TOUGH2 initial conditions + +.. _introduction-4: + +Introduction +------------ + +The ``t2incons`` library in PyTOUGH contains classes and routines for +reading, editing and writing TOUGH2 initial conditions and files. It can +be imported using the command: + +:: + + from t2incons import * + +The initial conditions files used by TOUGH2 and AUTOUGH2 have the same +format. PyTOUGH also supports TOUGHREACT initial conditions files, which +have a slightly different format – permeabilities are included for each +block, and timing information at the bottom of the file is formatted +differently. + +``t2incon`` objects +------------------- + +The ``t2incons`` library defines a ``t2incon`` class, used for +representing TOUGH2 initial conditions. + +**Example:** + +:: + + inc = t2incon() + +creates an empty ``t2incon`` object called ``inc``. + +:: + + inc = t2incon(filename) + +creates a ``t2incon`` object called ``inc`` and reads its contents from +file ``filename``. + +.. _properties-3: + +Properties +~~~~~~~~~~ + +The main properties of a ``t2incon`` object are listed in the +:ref:`table ` below. Once a set of initial conditions is +loaded into a ``t2incon`` object, conditions for individual blocks can +be accessed by block name or index. For example, for a ``t2incon`` +object ``inc``, the initial conditions in block 'blockname' are given +simply by ``inc[blockname]``. This returns a :ref:`t2blockincon ` +object. Similarly, ``inc[i]`` returns the initial conditions at the block with +(zero-based) index ``i``. + +Each column in the initial conditions file can be accessed by adding an +integer (zero-based) index after the ``t2blockincon`` object, so for +example: + +:: + + t = inc['aa 20'][1] + +assigns the variable ``t`` the value of the second primary thermodynamic +variable (index 1) in block ``'AA 20'``. Initial conditions can be +edited in a similar way, for example: + +:: + + inc['aa 20'][0] = p + +assigns the value of ``p`` to the first primary variable (usually +pressure) in block ``'AA 20'``. For convenience, initial conditions for +a given block can also be specified as a simple list or tuple of values, +for example: + +:: + + inc['ab 25'] = (101.3e5,25.0) + +sets the initial conditions at block ``'ab 25'`` to the specified +values. This will work even if no initial conditions have been +previously specified for the given block. + +An ``np.array`` of the values of the variables at all blocks can be +found from the ``variable`` property. For example: + +:: + + inc.variable[:,2] + +returns an ``np.array`` of the third variable (index 2) in each block. +The ``variable`` property can also be set to a given array. Note, +however, that the whole array must be set, not just part of it. For +example, adding an offset ``P0`` to all pressures (variable 0) in the +initial conditions could be done by: + +:: + + v = inc.variable + v[:,0] += P0 + inc.variable = v + +The ``porosity`` property may be set to assign values of porosity to all +blocks. The assigned value may be an ``np.array`` with a value for each +block, or a scalar float (in which case the same value is assigned to +all blocks), or ``None`` which assigns the value in each block to +``None``. + +Similarly, for TOUGHREACT initial conditions files, the ``permeability`` +property can be used to read or assign permeabilities for all blocks. +When assigning this property, the value can be an ``np.array`` of shape +(``num_blocks``, 3), (i.e. a row for each block), or a single +``np.array`` with 3 elements, to be applied to all blocks, a single +scalar float (to assign isotropic permeabilities to all blocks) or +``None`` which assigns ``None`` to all block permeabilities. + +The ``timing`` property of a ``t2incon`` object contains the optional +timing information at the end of the file. This is a dictionary property +with keys ``'kcyc'``, ``'iter'``, ``'nm'``, ``'tstart'`` and +``'sumtim'``, corresponding to the values stored on this line. + +The ``simulator`` string property is 'TOUGH2' by default, and is set to +'TOUGHREACT' if permeabilities are detected while reading from file. +Setting this property back to 'TOUGH2' will cause the file to be written +out in TOUGH2 format (no permeabilities, and different format for timing +information) if the ``write()`` method is executed. + +.. container:: + :name: tb:t2incon_properties + + .. table:: Properties of a ``t2incon`` object + + +-------------------+--------------+---------------------------------+ + | **Property** | **Type** | **Description** | + +===================+==============+=================================+ + | ``blocklist`` | list | ordered list of block names in | + | | | the initial conditions file | + +-------------------+--------------+---------------------------------+ + | ``num_blocks`` | integer | number of blocks at which | + | | | conditions are specified | + +-------------------+--------------+---------------------------------+ + | ``num_variables`` | integer | number of thermodynamic | + | | | variables specified at each | + | | | block | + +-------------------+--------------+---------------------------------+ + | ``permeability`` | ``np.array`` | array of permeability values | + | | | specified at each block | + | | | (TOUGHREACT only) | + +-------------------+--------------+---------------------------------+ + | ``porosity`` | ``np.array`` | array of porosity values | + | | | specified at each block | + +-------------------+--------------+---------------------------------+ + | ``simulator`` | string | simulator type ('TOUGH2' or | + | | | 'TOUGHREACT') | + +-------------------+--------------+---------------------------------+ + | ``timing`` | dictionary | additional timing information | + | | | for restarting | + +-------------------+--------------+---------------------------------+ + | ``variable`` | ``np.array`` | two-dimensional array of | + | | | thermodynamic variable values | + | | | at each block | + +-------------------+--------------+---------------------------------+ + +.. _functions-for-reading-data-from-file-1: + +Functions for reading data from file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It is possible to specify customized functions to control how data are +read from a TOUGH2 initial conditions file. This is done using the +optional ``read_function`` parameter when a ``t2incon`` object is +created- in exactly the same way it is done for a ``mulgrid`` object. +For more details, see the corresponding +:ref:`documentation ` for ``mulgrid`` +objects. By default, the read functions for ``t2incon`` objects are given +by the ``fortran_read_function`` dictionary. + +Specifying the number of primary variables +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Most common TOUGH2 EOS modules have no more than four primary variables, +in which case the variables for a given block all fit on one line in the +initial conditions file. However, some EOS modules (e.g. EOS7c and +EOS7r) have more than four primary variables. For these, the variables +for a given block are specified over multiple lines in the initial +conditions file. + +In this case, it is not possible for PyTOUGH to reliably detect the +number of primary variables, as it does when there are no more than four +variables. Instead, the number of primary variables must be specified +when the ``t2incon`` object is created (or its +:ref:`read() ` method is executed). This can +be done by setting the optional integer ``num_variables`` parameter, +which defaults to ``None`` (meaning PyTOUGH will detect the number of +variables). For example: + +:: + + from t2incons import * + inc = t2incon('model.incon', num_variables = 6) + +opens initial conditions for an EOS using six primary variables. + +For writing initial conditions files with more than four primary +variables, no extra parameters need be set, as the data stored in the +``t2incon`` object determines the number of primary variables, and they +will be written out over multiple lines as required. + +Checking block names +^^^^^^^^^^^^^^^^^^^^ + +By default, when a ``t2incon`` object is read from file, the block +names are checked to make sure they are valid TOUGH2 block names (3 +characters plus 2 digits). However these checks can be skipped by +setting the optional ``check_blocknames`` parameter to ``False``. For example: + +:: + + from t2incons import * + inc = t2incon('model.incon', check_blocknames = False) + +.. _methods-1: + +Methods +~~~~~~~ + +The main methods of a ``t2incon`` object are listed in the +:ref:`table ` below. + +.. container:: + :name: tb:t2incon_methods + + .. table:: Methods of a ``t2incon`` object + + +--------------------------------------------------+----------+----------------------------+ + | **Method** | **Type** | **Description** | + +==================================================+==========+============================+ + | :ref:`add_incon ` | – | adds a set of initial | + | | | conditions for one block | + | | | | + +--------------------------------------------------+----------+----------------------------+ + | :ref:`delete_incon ` | – | deletes the initial | + | | | conditions for one block | + | | | | + +--------------------------------------------------+----------+----------------------------+ + | :ref:`empty ` | – | deletes all initial | + | | | conditions from the object | + | | | | + | | | | + +--------------------------------------------------+----------+----------------------------+ + | :ref:`insert_incon ` | – | inserts initial conditions | + | | | for one block at a | + | | | specified index | + +--------------------------------------------------+----------+----------------------------+ + | :ref:`read ` | – | reads initial conditions | + | | | from file | + | | | | + +--------------------------------------------------+----------+----------------------------+ + | :ref:`transfer_from ` | – | transfers initial | + | | | conditions from one grid | + | | | to another | + +--------------------------------------------------+----------+----------------------------+ + | :ref:`write ` | – | writes initial conditions | + | | | to file | + | | | | + +--------------------------------------------------+----------+----------------------------+ + +Details of these methods are as follows. + +---- + +.. _sec:t2incon:add_incon: + +``add_incon(incon)`` +^^^^^^^^^^^^^^^^^^^^ + +Adds a set of initial conditions for a single block. + +**Parameters:** + +- | **incon**: :ref:`t2blockincon ` + | Initial conditions for the block. + +---- + +.. _sec:t2incon:delete_incon: + +``delete_incon(blockname)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Deletes a set of initial conditions for a single block. + +**Parameters:** + +- | **blockname**: string + | Name of the block at which initial conditions are to be deleted. + +---- + +.. _sec:t2incon:empty: + +``empty()`` +^^^^^^^^^^^ + +Deletes initial conditions for all blocks. + +---- + +.. _sec:t2incon:insert_incon: + +``insert_incon(index,incon)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Inserts a set of initial conditions for a single block at the specified +index. + +**Parameters:** + +- | **index**: integer + | Index (zero-based) at which to insert the initial conditions. + +- | **incon**: :ref:`t2blockincon ` + | Initial conditions for the block. + +---- + +.. _sec:t2incon:read: + +``read(filename, num_variables = None, check_blocknames = True)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Reads initial conditions from file. + +**Parameters:** + +- | **filename**: string + | Name of the initial conditions file to be read. + +- | **num_variables**: integer or ``None`` + | If reading initial conditions files with more than four primary + variables, set to the number of primary variables. Otherwise, the + default ``None`` value can be used, in which case the number of + primary variables will be detected automatically. + +- | **check_blocknames**: Boolean + | Whether to check if block names in the file are valid TOUGH2 block + names (3 characters followed by 2 digits). + +---- + +.. _sec:t2incon:transfer_from: + +``transfer_from(sourceinc, sourcegeo, geo, mapping={}, colmapping={})`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Transfers initial conditions from another ``t2incon`` object +``sourceinc``, using the two corresponding ``mulgrid`` geometry objects +``sourcegeo`` and ``geo``, and optionally the block and column mappings +between the two grids (which are created if not specified). + +**Parameters:** + +- | **sourceinc**: :ref:`t2incon ` + | Source initial conditions object. + +- | **sourcegeo**: :ref:`mulgrid ` + | Geometry object corresponding to the source initial conditions. + +- | **geo**: :ref:`mulgrid ` + | Geometry object for the grid to be transferred to. + +- | **mapping**: dictionary + | Dictionary mapping block names from ``geo`` to ``sourcegeo``. + +- | **colmapping**: dictionary + | Dictionary mapping column names from ``geo`` to ``sourcegeo``. + +---- + +.. _sec:t2incon:write: + +``write(filename, reset=True)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Writes initial conditions to file. + +**Parameters:** + +- | **filename**: string + | Name of the initial conditions file to be written. + +- | **reset**: Boolean + | Set to ``False`` if timing information is not to be reset - e.g. if + restarting a transient simulation. + +---- + +.. _t2blockincons: + +``t2blockincon`` objects +------------------------ + +A ``t2blockincon`` object represents the initial conditions for a +particular block. The properties of a ``t2blockincon`` object are given +in the :ref:`table ` below. The ``permeability`` +property is used only by TOUGHREACT. If no values are specified for +``porosity``, ``permeability``, ``nseq`` or ``nadd``, their values are +``None``. A ``t2blockincon`` object has no methods. + +The ``variable`` property of a ``t2blockincon`` can be more easily +accessed simply by adding the required (zero-based) variable index after +the object. For example, for a ``t2blockincon`` object ``b``, the value +of the second variable is given simply by ``b[1]``. + +To create a new ``t2blockincon`` object, simply invoke the class name +with values of the desired properties, e.g.: + +:: + + binc = t2blockincon(block = 'abc10', porosity = 0.1, variable = [101.3e3, 28.]) + +.. container:: + :name: tb:t2blockincon_properties + + .. table:: Properties of a ``t2blockincon`` object + + +------------------+------------------------+------------------------+ + | **Property** | **Type** | **Description** | + +==================+========================+========================+ + | ``block`` | string | block name | + +------------------+------------------------+------------------------+ + | ``nadd`` | integer or ``None`` | optional block index | + | | | increment between | + | | | additional blocks with | + | | | the same initial | + | | | conditions | + +------------------+------------------------+------------------------+ + | ``nseq`` | integer or ``None`` | optional number of | + | | | additional blocks with | + | | | the same initial | + | | | conditions | + +------------------+------------------------+------------------------+ + | ``permeability`` | ``np.array`` or | optional permeability | + | | ``None`` | for the block | + | | | (TOUGHREACT only) | + +------------------+------------------------+------------------------+ + | ``porosity`` | float or ``None`` | optional porosity for | + | | | the block | + +------------------+------------------------+------------------------+ + | ``variable`` | list | list of thermodynamic | + | | | variable values for | + | | | the block | + +------------------+------------------------+------------------------+ + +Reading save files and converting to initial conditions +------------------------------------------------------- + +TOUGH2 writes a save file (SAVE, or \*.save for AUTOUGH2) at the end of +the simulation, which has a format almost the same as that of an initial +conditions file and can be used to start a subsequent run. A save file +generally has some extra timing information at the end which can be used +to restart a simulation at a particular time. However, in many cases, +e.g when running natural state simulations, we want to restart at the +original start time and this timing information must be discarded. + +PyTOUGH will read a save file into a ``t2incon`` object. This can then +be written to file, providing a simple way to convert save files into +incon files. By default, the timing information is discarded when +writing (it can be retained by setting the ``reset`` parameter of the +``write`` method to ``False``). For example: + +:: + + t2incon('model1.save').write('model2.incon') + +will read the save file ``'model1.save'``, convert it to initial +conditions, and write it to the initial conditions file +``'model2.incon'``. + +.. _example-2: + +Example +------- + +The following piece of Python script reads in a save file and prints out +a table of block names and temperatures for the first 10 blocks. It then +adds an extra variable to each initial condition and gives it a constant +value (giving a new column in the initial conditions file), and finally +writes out the edited initial conditions to a new file. + +Adding a new variable to each initial condition can be useful when e.g. +changing from one TOUGH2 equation of state (EOS) module to another, as +different EOS modules may have different numbers of primary +thermodynamic variables. + +:: + + from t2incons import * + inc = t2incon('model1.save') + for blk in inc[0:10]: + print('Block %5s: temperature = %5.1f' % (blk.block,blk[1])) + patm = 101.3e3 + for blk in inc: blk.variable.append(patm) + inc.write('model2.incon') + diff --git a/doc/source/t2listing.rst b/doc/source/t2listing.rst new file mode 100644 index 00000000..a05e62a4 --- /dev/null +++ b/doc/source/t2listing.rst @@ -0,0 +1,1253 @@ +:tocdepth: 3 + +.. _listingfiles: + +TOUGH2 listing files +==================== + +.. index:: TOUGH2 listing files + +.. _introduction-5: + +Introduction +------------ + +The ``t2listing`` library in PyTOUGH contains classes and routines for +reading TOUGH2 listing files. It can be imported using the command: + +:: + + from t2listing import * + +Listing files produced by AUTOUGH2, TOUGH2, TOUGH2_MP, TOUGH+ and TOUGH3 +have different formats but are all supported. The main listing files +produced by TOUGHREACT are also supported. (There is also a separate +:ref:`toughreact_tecplot ` class for handling +the additional Tecplot output files produced by TOUGHREACT.) + +``t2listing`` objects +--------------------- + +The ``t2listing`` library defines a ``t2listing`` class, used for +representing TOUGH2 listing files. + +**Example:** + +:: + + lst = t2listing() + +creates an empty ``t2listing`` object called ``lst``. + +:: + + lst = t2listing(filename) + +creates a ``t2listing`` object called ``lst`` and reads its contents +from file ``filename``. + +.. _t2listing_properties: + +Properties +~~~~~~~~~~ + +The main properties of a ``t2listing`` object are listed in the +:ref:`table ` below. + +Element, connection and generation tables +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 listing files; tables + +There are three main 'table' properties, corresponding to the +**element**, **connection** and **generation** tables in the listing +file. These are all of type :ref:`listingtable ` and +provide access to the simulation results. Not all of these tables will +necessarily be present - this depends on the settings in the data file +which produced the results. For TOUGH2 results, a fourth **primary** +table may also be present, containing primary variables and their +changes, if the KDATA parameter is set to 3. TOUGH+ results can also +contain additional element tables containing other calculated +quantities; these are named **element1**, **element2** etc. A list of +names of all available tables is given by the property. + +For example, for a ``t2listing`` object ``lst``, +``lst.element['AR210']['Temperature']`` gives the temperature at block +'AR210', at the current time. Blocks can also be identified by index +rather than name, so that ``lst.element[120]['Pressure']`` gives the +pressure at the block with (zero-based) index 120. + +These tables can also be accessed to give all results for a given block, +or for a given column in the table. For example, +``lst.element['AR210']`` returns a dictionary containing all results at +block 'AR210', referred to by the name of each table column. +``lst.element['Temperature']`` returns an ``np.array`` containing the +temperatures at all blocks in the model. (Hence, +``lst.element['Pressure'][120]`` gives the same result as +``lst.element[120]['Pressure']``.) + +The connection and generation tables work very similarly to the element +table, except that connections are referred to by tuples of block names +(rather than single block names), and generators are referred to by +tuples of block names and generator names. So for example, the mass flow +rate between blocks 'AB300' and 'AC300' might be given by +``lst.connection['AB300', 'AC300']['Mass flow']``. + +The names of the columns for each table are read directly from the +listing file, and will depend on the TOUGH2 equation of state (EOS) +being used. + +Skipping tables +^^^^^^^^^^^^^^^ + +The default behaviour is for a ``t2listing`` object to read all tables +present in the listing file. However, it is possible to skip the reading +of specified tables if required. This can be useful for speeding up +reading of large listing files where not all tables are required. For +example, sometimes the connection data are not required, but for large +models the connection table is often much bigger than the others, so +skipping it can make reading significantly faster. Data in skipped +tables are not available either via their corresponding properties or +via the :ref:`history() ` method. + +To skip tables, specify their table names (``element``, ``connection`` +etc.) in the optional ``skip_tables`` parameter when creating the +``t2listing`` object. (By default, this parameter is an empty list.) For +example, to read a listing file with name 'output.listing' into the +object ``lst`` and skip reading the connection and generation tables: + +:: + + lst = t2listing('output.listing', skip_tables = ['connection', 'generation']) + +File encoding +^^^^^^^^^^^^^ + +It is possible to specify the file encoding for the listing file using +the optional ``encoding`` parameter when creating the ``t2listing`` +object. The default for this parameter is "latin-1" encoding which +should be fine for reading in most listing files. If you encounter +exotic characters in your listing files which are not read correctly +using the default encoding you may want to try other encodings. + +Full and short output +^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 listing files; short output + +AUTOUGH2 allows the use of 'short' output, in which a specified +selection of block, connection or generator properties are printed at +time steps between normal full output. A ``t2listing`` object will read +short output results, if they are present, when producing time histories +using the :ref:`history() ` method. +However it is not possible to navigate to short output results or access +them via the ``t2listing`` table properties above. + +TOUGH2, TOUGH2_MP, TOUGHREACT, TOUGH+ and TOUGH3 do not support short +output. + +Navigating in time using ``time``, ``index`` and ``step`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 listing files; navigation + +The ``time`` property returns the time (in seconds) corresponding to the +current set of results. It is also possible to set the ``time`` property +to navigate to a specific set of full results. For example, +``lst.time=1.e9`` navigates to the set of full results with time closest +to :math:`10^9`\ s. + +The ``index`` property gives the index of the current set of results, +and can take any value between 0 and ``num_fulltimes``-1. The value of +``index`` can also be set to change to a different set of results in the +listing file (e.g. ``lst.index=12``). It can be incremented and +decremented like any other Python integer variable, e.g. +``lst.index+=1`` or ``lst.index-=2`` to go to the next set of results, +or the second to last set respectively. + +The ``step`` property gives the time step number for the current set of +results. This is the number of time steps carried out in the simulation +up to the current set of results (recall that results are not +necessarily written to the listing file at every time step). Again, its +value can be set to navigate through the results, e.g. ``lst.step=100`` +navigates to the set of full results with time step number nearest to +100. + +The ``times`` property returns an ``np.array`` of all times at which +results (including short output) are given in the listing file. It has +length equal to ``num_times``. The ``fulltimes`` property returns an +``np.array`` of times at which full results are given (not including +short output), and has length equal to ``num_fulltimes``. + +A ``t2listing`` object also has :ref:`methods ` (as +well as properties) for navigating through time. + +Listing diagnostics +^^^^^^^^^^^^^^^^^^^ + +``t2listing`` objects have two properties that provide diagnostics on +the results of the TOUGH2 run. + +The ``convergence`` property is a dictionary of the maximum absolute +differences in the element table between the second to last and last +sets of results in the listing file. This can be used to check +convergence of steady-state simulations. For example: + +:: + + lst.convergence['Temperature'] + +gives the largest absolute temperature change between the second to last +and last sets of results. + +The ``reductions`` property is a list of tuples of time step indices at +which the time step size was reduced during the simulation, and the +block name at which the maximum residual occurred prior to each +reduction. This gives an indication of problematic times and blocks +which caused time step reductions. + +.. container:: + :name: tb:t2listing_properties + + .. table:: Properties of a ``t2listing`` object + + +-------------------+-------------------------------------------+-----------------------+ + | **Property** | **Type** | **Description** | + +===================+===========================================+=======================+ + | ``connection`` | :ref:`listingtable ` | connection table for | + | | | current set of | + | | | results | + | | | | + +-------------------+-------------------------------------------+-----------------------+ + | ``convergence`` | dictionary | maximum differences | + | | | in element table | + | | | between second to | + | | | last and last sets of | + | | | results | + +-------------------+-------------------------------------------+-----------------------+ + | ``element`` | :ref:`listingtable ` | element table for | + | | | current set of | + | | | results | + +-------------------+-------------------------------------------+-----------------------+ + | ``element1`` etc. | :ref:`listingtable ` | additional element | + | | | table for current set | + | | | of results (TOUGH+ | + | | | only) | + +-------------------+-------------------------------------------+-----------------------+ + | ``filename`` | string | name of listing file | + | | | on disk | + +-------------------+-------------------------------------------+-----------------------+ + | ``fullsteps`` | ``np.array`` | array of time step | + | | | numbers (integer) for | + | | | full results | + +-------------------+-------------------------------------------+-----------------------+ + | ``fulltimes`` | ``np.array`` | array of times | + | | | (float) for full | + | | | results | + +-------------------+-------------------------------------------+-----------------------+ + | ``generation`` | :ref:`listingtable ` | generation table for | + | | | current set of | + | | | results | + | | | | + +-------------------+-------------------------------------------+-----------------------+ + | ``index`` | integer | index of current set | + | | | of results | + +-------------------+-------------------------------------------+-----------------------+ + | ``num_fulltimes`` | integer | number of sets of | + | | | full results | + +-------------------+-------------------------------------------+-----------------------+ + | ``num_times`` | integer | number of sets of all | + | | | results (full and | + | | | short) | + +-------------------+-------------------------------------------+-----------------------+ + | ``primary`` | :ref:`listingtable ` | primary variable | + | | | table for current set | + | | | of results (TOUGH2 | + | | | only) | + +-------------------+-------------------------------------------+-----------------------+ + | ``reductions`` | list | time step indices at | + | | | which time step was | + | | | reduced during the | + | | | simulation | + +-------------------+-------------------------------------------+-----------------------+ + | ``short_types`` | list of string | types of short output | + | | | present | + +-------------------+-------------------------------------------+-----------------------+ + | ``simulator`` | string | detected simulator | + | | | ('AUTOUGH2', 'TOUGH2' | + | | | etc.) | + +-------------------+-------------------------------------------+-----------------------+ + | ``step`` | integer | time step number of | + | | | current set of | + | | | results | + +-------------------+-------------------------------------------+-----------------------+ + | ``steps`` | ``np.array`` | array of time step | + | | | numbers (integer) for | + | | | all results (full and | + | | | short) | + +-------------------+-------------------------------------------+-----------------------+ + | ``table_names`` | list | names of available | + | | | tables | + +-------------------+-------------------------------------------+-----------------------+ + | ``time`` | float | time of current set | + | | | of results | + +-------------------+-------------------------------------------+-----------------------+ + | ``times`` | ``np.array`` | array of times | + | | | (float) for all | + | | | results (full and | + | | | short) | + +-------------------+-------------------------------------------+-----------------------+ + | ``title`` | string | simulation title | + +-------------------+-------------------------------------------+-----------------------+ + +.. _t2listingmethods: + +Methods +~~~~~~~ + +The main methods of a ``t2listing`` object are listed in the +:ref:`table ` below. + +.. container:: + :name: tb:t2listing_methods + + .. table:: Methods of a ``t2listing`` object + + +------------------------------------------------------------+---------------+-------------------------+ + | **Method** | **Type** | **Description** | + +============================================================+===============+=========================+ + | :ref:`add_side_recharge ` | – | adds side recharge | + | | | generators to a | + | | | ``t2data`` object | + +------------------------------------------------------------+---------------+-------------------------+ + | :ref:`close ` | – | closes listing file | + | | | | + | | | | + +------------------------------------------------------------+---------------+-------------------------+ + | :ref:`first` | – | navigates to the first | + | | | set of full results | + | | | | + +------------------------------------------------------------+---------------+-------------------------+ + | :ref:`get_difference` | dictionary | maximum differences in | + | | | element table between | + | | | two sets of results | + +------------------------------------------------------------+---------------+-------------------------+ + | :ref:`history` | list or tuple | time history for a | + | | | selection of locations | + | | | and table columns | + +------------------------------------------------------------+---------------+-------------------------+ + | :ref:`last` | – | navigates to the last | + | | | set of full results | + | | | | + +------------------------------------------------------------+---------------+-------------------------+ + | :ref:`next` | Boolean | navigates to the next | + | | | set of full results | + | | | | + +------------------------------------------------------------+---------------+-------------------------+ + | :ref:`prev` | Boolean | navigates to the | + | | | previous set of full | + | | | results | + +------------------------------------------------------------+---------------+-------------------------+ + | :ref:`write_vtk` | – | writes results to VTK | + | | | file | + | | | | + +------------------------------------------------------------+---------------+-------------------------+ + +Details of these methods are as follows. + +---- + +.. _sec:t2listing:add_side_recharge: + +``add_side_recharge(geo, dat)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Adds side recharge generators to a ``t2data`` object ``dat`` for a +production run, calculated according to the final results in the +listing. These generators represent side inflows due to pressure changes +in the blocks on the model's horizontal boundaries. Recharge generators +are given the names of their blocks- any existing generators with the +same names will be overwritten. + +**Parameters:** + +- | **geo**: :ref:`mulgrid ` + | Geometry object associated with the listing. + +- | **dat**: :ref:`t2data ` + | TOUGH2 data object for the side recharge generators to be added to. + +---- + +.. _sec:t2listing:close: + +``close()`` +^^^^^^^^^^^ + +Closes the listing file after use. + +---- + +.. _sec:t2listing:first: + +``first()`` +^^^^^^^^^^^ + +Navigates to the first set of full results in the listing file. + +---- + +.. _sec:t2listing:get_difference: + +``get_difference(indexa=None, indexb=None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns dictionary of maximum differences, and locations of difference, +of all element table properties between two sets of results. + +**Parameters:** + +- | **indexa**, **indexb**: integer or ``None`` + | Indices of results between which the maximum differences are to be + calculated. If both indexa and indexb are provided, the result is + the difference between these two result indices. If only one index + is given, the result is the difference between the given index and + the one before that. If neither are given, the result is the + difference between the last and penultimate sets of results. + +---- + +.. _sec:t2listing:history: + +``history(selection, short=True, start_datetime=None``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 listing files; time histories + +Returns a list of time histories (as ``np.arrays``) for specified +locations and table columns in the element, connection or generation +tables. For each selection, a tuple of two ``np.arrays`` is returned, +one each for times and values. Short output (AUTOUGH2 only) can be +omitted from the history results by setting the ``short`` parameter to +``False``. If the ``start_datetime`` parameter is given, times in the +output are given as datetimes rather than seconds from the start. + +**Parameters:** + +- | **selection**: list of tuples + | Selection of listing tables, locations (or indices) and table + columns to produce histories for. Each tuple contains three + elements: the listing **table type** ('e', 'c', 'p' or 'g' for + element, connection, primary or generation table respectively), the + **block/ connection/ generator name** (or index) and the **table + column name**. (If only a single tuple is given instead of a list + of tuples, just the single tuple of times and values for that + selection is returned.) For history of additional element tables in + TOUGH+ results, use 'e1', 'e2' etc. instead of 'e'. Note that, as + for listing tables, connection and generator names (or 'keys') are + specified as two-element tuples (see + :ref:`Keys for different listing table types `). + If the second element of a selection tuple is an integer, it will + be interpreted as the (zero-based) index of the block, connection + or generator in the corresponding table. + +- | **short**: Boolean + | Whether short output (AUTOUGH2 only) is to be included in the + history results - default is ``True``. + +- | **start_datetime**: datetime or ``None`` + | Datetime of the start of the simulation. If ``None`` (the default), + output times are given as seconds from the start of the simulation. + If a Python datetime is given, then output times are given as + datetimes. + +**Examples:** + +:: + + [(tt,temp), (tq,q), (tg,g)] = lst.history([('e', 'AR210', 'Temperature'), + ('c', ('AB300','AC300'), 'Mass flow'), ('g', ('BR110','SO 1'), 'Generation rate')]) + +returns a list of three tuples of ``np.arrays``, ``(tt,temp)``, +``(tq,q)`` and ``(tg,g)``, giving the times and values of temperature at +block 'AR210', mass flow at the connection between blocks 'AB300' and +'AC300', and generation rate in the generator 'SO 1' in block 'BR110' +respectively. + +:: + + from datetime import datetime + t0 = datetime(1955, 1, 1) + t,T = lst.history(('e', 'AR210', 'Temperature'), start_datetime = t0) + +returns ``T`` as an ``np.array`` of temperature values, and ``t`` as an +``np.array`` of Python datetimes, starting at 1 January 1955. + +---- + +.. _sec:t2listing:last: + +``last()`` +^^^^^^^^^^ + +Navigates to the last set of full results in the listing file. + +---- + +.. _sec:t2listing:next: + +``next()`` +^^^^^^^^^^ + +Navigates to the next set of full results in the listing file. Returns +``False`` if already at the last set of results (and ``True`` +otherwise). + +---- + +.. _sec:t2listing:prev: + +``prev()`` +^^^^^^^^^^ + +Navigates to the previous set of full results in the listing file. +Returns ``False`` if already at the first set of results (and ``True`` +otherwise). + +---- + +.. _sec:t2listing:write_vtk: + +``write_vtk(geo, filename, grid=None, indices=None, flows=False, wells=False, start_time=0, time_unit='s', flux_matrix=None, blockmap = {}, surface_snap=0.1)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: TOUGH2 listing files; VTK + +Writes a ``t2listing`` object to a set of VTK files on disk, for +visualisation with VTK, Paraview, Mayavi etc. The results in the listing +object are written as an 'unstructured grid' VTK object with data arrays +defined on cells. The data arrays written correspond to the variables +given in the columns of the element table of the ``t2listing`` object. +(For TOUGH+ results, variables from the additional element tables are +also included.) In addition, data arrays from an associated ``mulgrid`` +and (optionally) ``t2grid`` objects can be included. + +If ``flows`` is ``True`` (and a ``grid`` is specified and the listing +contains connection data), approximate block-average flux vectors at the +centre of each block are also written, for all variables in the +connection table with names ending in 'flow'. + +One \*.vtu file is produced for each time step in the ``t2listing`` +object at which full results are present, and a \*.pvd file is also +written. This is usually the file that should actually be opened in +Paraview or other software as it contains time information associated +with each \*.vtu file. + +Optionally, only a subset of the time indices present in the +``t2listing`` can be written, according to the ``indices`` parameter. A +start time and time unit for the output can optionally be specified. + +**Parameters:** + +- | **geo**: :ref:`mulgrid ` + | The ``mulgrid`` geometry object associated with the results. For + flexibility, this geometry need not be fully compatible with the + results – for example, it may contain only a subset of the blocks + for which results are present, or the blocks may be in a different + order. However, if it is not fully compatible, the writing process + will be slower, and flux vectors will not be written (even if + ``flows`` is set to ``True``). + +- | **filename**: string + | Name of the \*.pvd file to be written. Names of the individual + \*.vtu files for each time step are similar but with a time index + appended and the file extension changed. + +- | **grid**: :ref:`t2grid ` + | Name of optional ``t2grid`` object associated with the results. + +- | **indices**: list or tuple + | Optional specification of time indices to include in the output. If + set to ``None`` (the default), all time indices will be included. + +- | **flows**: Boolean + | Set to ``True`` if approximate block-centred flux vectors are to be + calculated and written, for visualising flows. Default is + ``False``. **Note**: flow vectors can only be calculated if a + **grid** is specified. + +- | **wells**: Boolean + | Set to ``True`` if a separate VTK file for the wells in the + :ref:`mulgrid ` object is to be written. Default is + ``False``. + +- | **start_time**: float + | Optional start time of the simulation, i.e. time associated with + the first set of results. Default is zero. + +- | **time_unit**: string + | Optional time unit for the output. TOUGH2 results are given at + times in seconds, but this option allows them to be converted to + other units. Options are: 's', 'h', 'd' and 'y', for seconds, + hours, days and years respectively. Default is 's'. + +- | **flux_matrix**: ``scipy.sparse.lil_matrix`` + | Sparse matrix that multiplies a vector of connection values to + produce a partition vector of 3-D block average flows at the + (underground) block centres. One of these can be produced using the + ``t2grid.flux_matrix()`` method, and a corresponding ``mulgrid`` + object. A flux matrix will be calculated internally if not + supplied. + +- | **blockmap**: dictionary + | Dictionary mapping the block names in the geometry to the block + naming system used in the listing. + +- | **surface_snap**: float + | Tolerance for specifying how close column surface elevations need + to be before being considered "equal" when constructing surface + nodes. + +---- + +.. _listingtableobjects: + +``listingtable`` objects +------------------------ + +.. index:: TOUGH2 listing files; tables + +A ``listingtable`` object represents a table of results in a TOUGH2 +listing file (whether it is an element, connection or generation table). +The column headings of the table are taken directly from the +corresponding table in the listing file. The rows of the table may be +accessed either by (zero-based) index, or by the 'key' for the table +row, which depends on the table type (see :ref:`table ` +below). + +.. container:: + :name: tb:listing_table_keys + + .. table:: Keys for different listing table types + + ============== ============================ + **Table type** **Key** + ============== ============================ + ``element`` block name + ``connection`` (block name 1, block name 2) + ``generation`` (block name, generator name) + ============== ============================ + +Hence, the value in the element table for a given block and column can +be accessed by ``lst.element[blockname][columnname]``, or by +``lst.element[blockindex][columnname]`` (for a ``t2listing`` object +``lst``). Note that for connection and generation tables, the keys are +tuples of two strings. For connection tables, the order of these two +strings (the block names) is not important; if the listing file contains +results for (block1, block2), then results for (block2, block1) can be +accessed via the corresponding ``listingtable`` object (though the +results will have the opposite sign to those in the file, as they will +represent flows in the opposite direction). + +The values for an entire row or column of the table can also be +accessed, for example ``lst.element[blockname]`` gives the row in the +table for a specified block, with the values arranged in a dictionary +which can be accessed using the column names of the table (e.g. +``lst.element['AR231']['Temperature']``). This dictionary for each row +also contains an additional ``'key'`` item which returns the key for +that row. Conversely, ``lst.element[columnname]`` gives the column in +the table for a specified column name, with the values returned in an +``np.array`` (one value for each block in the grid, for an element +table). + +``listingtable`` properties +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The properties of a ``listingtable`` object are given in the +:ref:`table ` below. The entire list of +key values for a ``listingtable`` may be accessed via the ``row_name`` +property, which contains the key value for each row. The column +headings of the table can similarly be accessed via the +``column_name`` list property. The ``num_rows`` and ``num_columns`` +properties of a ``listingtable`` object return the numbers of rows and +columns respectively. The ``num_keys`` property just returns the +number of keys used to identify each row - generally 1 for an element +table and 2 for connection and generation tables. + +.. container:: + :name: tb:listingtable_properties + + .. table:: Properties of a ``listingtable`` object + + +-----------------+------------------+--------------------------+ + | **Property** | **Type** | **Description** | + +=================+==================+==========================+ + | ``column_name`` | list | column headings | + +-----------------+------------------+--------------------------+ + | ``DataFrame`` | Pandas DataFrame | data in DataFrame format | + +-----------------+------------------+--------------------------+ + | ``num_columns`` | integer | number of columns | + +-----------------+------------------+--------------------------+ + | ``num_keys`` | integer | number of keys per row | + +-----------------+------------------+--------------------------+ + | ``num_rows`` | integer | number of rows | + +-----------------+------------------+--------------------------+ + | ``row_name`` | list | keys for each row | + +-----------------+------------------+--------------------------+ + +Adding and subtracting +~~~~~~~~~~~~~~~~~~~~~~ + +It is possible to perform addition and subtraction operations on +``listingtable`` objects. Subtraction can be useful, for example, when +comparing results from different runs. These operations can only be +carried out when the row and column names of the two tables are +identical. The resulting table will have the same row and column names +as the original tables, but will contain the element-wise sums or +differences. + +Converting to DataFrames +~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``listingtable`` object has a ``DataFrame`` property which returns +the entire table in the form of a `Pandas +`_ DataFrame object. Pandas is a Python +library for data analysis, which you will need to have installed +before you can use the ``DataFrame`` property. With Pandas you can do +advanced data analysis on your TOUGH2 results. See the Pandas +documentation for more details. + +``listingtable`` methods +~~~~~~~~~~~~~~~~~~~~~~~~ + +``listingtable`` objects have one method as described below. + +---- + +``rows_matching(pattern, index=0, match_any=False)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns a list of rows in the table with keys matching the specified +regular expression string, ``pattern``. + +For tables with multiple keys, ``pattern`` can be a list or tuple of +regular expressions. If a single string pattern is given for a +multiple-key table, the pattern is matched on the index\ :math:`^{th}` +key (and any value of the other key - unless the ``match_any`` option is +used; see below). + +If ``match_any`` is set to ``True``, rows are returned with keys +matching any of the specified patterns (instead of all of them). If this +option is used in conjunction with a single string pattern, the +specified pattern is applied to all keys. + +**Parameters:** + +- | **pattern**: string, list or tuple + | Regular expression string specifying the pattern to match. For + multiple-key tables, this can be a list or tuple of regular + expression strings. + +- | **index**: integer + | Index of the key to which the pattern is to be applied, for + multiple-key tables and when ``pattern`` is specified as a single + string. + +- | **match_any**: Boolean + | If ``False``, return only rows with keys matching *all* of their + corresponding patterns. If ``True``, return rows with keys matching + *any* of the specified patterns - and if a single string pattern is + given, apply this to all keys. + +---- + +``t2historyfile`` objects +------------------------- + +.. index:: TOUGH2 data files; FOFT +.. index:: TOUGH2 data files; COFT +.. index:: TOUGH2 data files; GOFT + +In addition to the main listing file, TOUGH2 can optionally produce +extra files containing time history data from selected blocks, +connections or generators, named ``FOFT``, ``COFT`` and ``GOFT`` files +respectively. TOUGH+ can optionally name these files +``Elem_Time_Series``, ``Conx_Time_Series`` and ``SS_Time_Series`` +instead. (AUTOUGH2 does not produce separate history files, but can +instead produce 'short output' at selected blocks, connections or +generators within the listing file itself.) + +The ``t2listing`` module contains a ``t2historyfile`` class for reading +and manipulating these history files. History files produced by TOUGH2, +TOUGH2_MP and TOUGH+ are supported, although they all have different +formats. The same class is used for FOFT, COFT and GOFT files. A history +file of any of these types can be opened using a command such as: + +:: + + hist = t2historyfile(filename) + +where ``filename`` is the name of the file. It may contain wildcards (*) +so that several files matching a pattern are read in to the same object. +This is useful for reading output from TOUGH2_MP, which creates separate +history files for each processor used in the calculation (e.g. +``FOFT_P.000``, ``FOFT_P.001``, etc.). It is assumed that all files +opened are however of the same type (FOFT, COFT or GOFT). + +Once a history file has been read in, history results for a particular +key (i.e. block, connection or generator) can be extracted. For +TOUGH2_MP, the keys are the block names for FOFT files, tuples of block +names for COFT files, and tuples of block names and source names for +GOFT files. For example: + +:: + + foft = t2historyfile('FOFT_P.*') + blockname = 'fmq20' + results = foft[blockname] + +This will return a dictionary containing an ``np.array`` for each column +in the file, indexed by the column name. For example the temperature +history at this block would be given by: + +:: + + temp = foft[blockname]['TEMPERATURE'] + +Results at a particular time can also be found: + +:: + + time = 3.156e7 + result = foft[blockname, time] + +Again, this will return a dictionary with one item for each column, but +in this case each item is just a single floating point number instead of +an array. + +For **TOUGH2** rather than TOUGH2_MP, the keys are integer indices of +blocks, connections or generators, rather than names or tuples of names. +Similarly, the column names are just integers. This is because the key +names and column names are not given in TOUGH2 history files. Aside from +these differences, they can be used in the same way as TOUGH2_MP history +files, for example: + +:: + + foft = t2historyfile('FOFT') + blkindex = 123 + temp = foft[blkindex][1] + +For **TOUGH+** connection and generator history files (``COFT`` and +``GOFT``, or ``Conx_Time_Series`` and ``SS_Time_Series``), multiple +connections and generators can be specified as usual in the TOUGH2 input +data file, but individual results for them are not written to the +history file. Instead, the results for them are summed. As a result, +there are no 'keys' as such for accessing individual results, and the +``t2historyfile`` works a little differently. An array containing the +data in each column can be accessed by specifying the column name, for +example: + +:: + + ct = t2historyfile('Conx_Time_Series') + qh = ct['HeatFlow'] + +The properties of a ``t2historyfile`` object are given in the +:ref:`table ` below. + +.. container:: + :name: tb:t2historyfile_properties + + .. table:: Properties of a ``t2historyfile`` object + + +-----------------+--------------+----------------------------------------------+ + | **Property** | **Type** | **Description** | + +=================+==============+==============================================+ + | ``column_name`` | list | column headings | + +-----------------+--------------+----------------------------------------------+ + | ``key_name`` | list | names of keys | + +-----------------+--------------+----------------------------------------------+ + | ``num_times`` | integer | number of times | + +-----------------+--------------+----------------------------------------------+ + | ``num_columns`` | integer | number of data columns | + +-----------------+--------------+----------------------------------------------+ + | ``num_rows`` | integer | total number of data (for all keys) | + +-----------------+--------------+----------------------------------------------+ + | ``simulator`` | string | detected simulator ('TOUGH2' or 'TOUGH2_MP') | + +-----------------+--------------+----------------------------------------------+ + | ``times`` | ``np.array`` | times at which results are given | + +-----------------+--------------+----------------------------------------------+ + | ``type`` | string | history type ('FOFT', 'COFT' or 'GOFT') | + +-----------------+--------------+----------------------------------------------+ + +.. _toughreact_tecplot: + +``toughreact_tecplot`` objects +------------------------------ + +.. index:: TOUGH2; TOUGHREACT + +The ``t2listing`` library also defines a ``toughreact_tecplot`` class, +used for representing the additional Tecplot output files produced by +TOUGHREACT. + +**Example:** + +:: + + tp = toughreact_tecplot(filename, blocks) + +creates a ``toughreact_tecplot`` object called ``tp`` and reads its +contents from file ``filename``. The ``blocks`` object passed in as a +second parameter specifies the block names (see +:ref:`Specifying block names `). + +Differences from ``t2listing`` objects +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``toughreact_tecplot`` object is similar to a +:ref:`t2listing ` object in many respects. Apart from the need to specify +the block names on creation (see +:ref:`Specifying block names `), the other main difference +is that unlike a ``t2listing`` object, which usually contains several +``listingtable`` objects, a ``toughreact_tecplot`` object contains only +one: the ``element`` table. Because of this, when using the ``history`` +method, tables need not be specified. + +These Tecplot files do not contain any information about time step +numbers, so ``t2listing`` properties like ``step`` and ``steps`` are not +present in a ``toughreact_tecplot`` object. There is also no ``title`` +property, as this is not present in the Tecplot file. + +There is also no 'short' output in a Tecplot file, so a +``toughreact_tecplot`` object does not have properties like +``fulltimes``, as this would just be the same as the ``times`` property. +There are also no diagnostic methods like ``convergence`` or +``reductions``. + +.. _toughreact_tecplot_blocknames: + +Specifying block names +~~~~~~~~~~~~~~~~~~~~~~ + +In the Tecplot file, results are not associated with block names, though +they appear in the same order as in the TOUGH2 data file used to +generate the results. To make results accessible by block name, a second +parameter containing the block names must be specified when a +``toughreact_tecplot`` object is created. This parameter is not +optional. It can be either: + +- a list of strings specifying the block names + +- a :ref:`mulgrid ` geometry object + +- a :ref:`t2grid ` object + +.. _properties-4: + +Properties +~~~~~~~~~~ + +The main properties of a ``toughreact_tecplot`` object are listed in +the :ref:`table ` below. For more details, see +the corresponding properties of the :ref:`t2listing ` class. + +.. container:: + :name: tb:toughreact_tecplot_properties + + .. table:: Properties of a ``toughreact_tecplot`` object + + +---------------+--------------------------------------------+-------------------------+ + | **Property** | **Type** | **Description** | + +===============+============================================+=========================+ + | ``element`` | :ref:`listingtable ` | element table for | + | | | current set of results | + | | | | + +---------------+--------------------------------------------+-------------------------+ + | ``filename`` | string | name of listing file on | + | | | disk | + +---------------+--------------------------------------------+-------------------------+ + | ``index`` | integer | index of current set of | + | | | results | + +---------------+--------------------------------------------+-------------------------+ + | ``num_times`` | integer | number of sets of | + | | | results | + +---------------+--------------------------------------------+-------------------------+ + | ``time`` | float | time of current set of | + | | | results | + +---------------+--------------------------------------------+-------------------------+ + | ``times`` | ``np.array`` | array of times (float) | + | | | for all results | + +---------------+--------------------------------------------+-------------------------+ + +.. _methods-2: + +Methods +~~~~~~~ + +The methods of a ``toughreact_tecplot`` object are listed in the +:ref:`table ` below. + +.. container:: + :name: tb:toughreact_tecplot_methods + + .. table:: Methods of a ``toughreact_tecplot`` object + + +------------------------------------------------------+---------------+-------------------------+ + | **Method** | **Type** | **Description** | + +======================================================+===============+=========================+ + | :ref:`close ` | – | closes file | + | | | | + | | | | + +------------------------------------------------------+---------------+-------------------------+ + | :ref:`first ` | – | navigates to the first | + | | | set of full results | + | | | | + +------------------------------------------------------+---------------+-------------------------+ + | :ref:`history ` | list or tuple | time history for a | + | | | selection of locations | + | | | and table columns | + +------------------------------------------------------+---------------+-------------------------+ + | :ref:`last ` | – | navigates to the last | + | | | set of full results | + | | | | + +------------------------------------------------------+---------------+-------------------------+ + | :ref:`next ` | Boolean | navigates to the next | + | | | set of full results | + | | | | + +------------------------------------------------------+---------------+-------------------------+ + | :ref:`prev` | Boolean | navigates to the | + | | | previous set of full | + | | | results | + +------------------------------------------------------+---------------+-------------------------+ + | :ref:`write_vtk ` | – | writes results to VTK | + | | | file | + | | | | + | | | | + +------------------------------------------------------+---------------+-------------------------+ + +Details of these methods are as follows. + +---- + +.. _sec:toughreact_tecplot:close: + +``close()`` +^^^^^^^^^^^ + +Closes the file after use. + +---- + +.. _sec:toughreact_tecplot:first: + +``first()`` +^^^^^^^^^^^ + +Navigates to the first set of results in the Tecplot file. + +---- + +.. _sec:toughreact_tecplot:history: + +``history(selection)`` +^^^^^^^^^^^^^^^^^^^^^^ + +Returns a list of time histories (as ``np.arrays``) for specified +locations and table columns in the element table. For each selection, a +tuple of two ``np.arrays`` is returned, one each for times and values. + +**Parameters:** + +- | **selection**: list of tuples + | Selection of locations (or indices) and table columns to produce + histories for. Each tuple contains two elements: **block name** and + **table column name**. (If only a single tuple is given instead of + a list of tuples, just the single tuple of times and values for + that selection is returned.) + +---- + +.. _sec:toughreact_tecplot:last: + +``last()`` +^^^^^^^^^^ + +Navigates to the last set of results in the Tecplot file. + +---- + +.. _sec:toughreact_tecplot:next: + +``next()`` +^^^^^^^^^^ + +Navigates to the next set of results in the Tecplot file. Returns +``False`` if already at the last set of results (and ``True`` +otherwise). + +---- + +.. _sec:toughreact_tecplot:prev: + +``prev()`` +^^^^^^^^^^ + +Navigates to the previous set of results in the Tecplot file. Returns +``False`` if already at the first set of results (and ``True`` +otherwise). + +---- + +.. _sec:toughreact_tecplot:write_vtk: + +``write_vtk(geo, filename, grid=None, indices=None, start_time=0, time_unit='s', blockmap = {}, surface_snap=0.1)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Writes a ``toughreact_tecplot`` object to a set of VTK files on disk, +for visualisation with VTK, Paraview, Mayavi etc. The results in the +element table of the Tecplot file object are written as an 'unstructured +grid' VTK object with data arrays defined on cells. The data arrays +written correspond to the variables given in the columns of the element +table of the ``toughreact_tecplot`` object. In addition, data arrays +from an associated ``mulgrid`` and (optionally) ``t2grid`` objects can +be included. + +One \*.vtu file is produced for each time step in the +``toughreact_tecplot`` object, and a \*.pvd file is also written. This +is usually the file that should actually be opened in Paraview or other +software as it contains time information associated with each \*.vtu +file. + +Optionally, only a subset of the time indices present in the +``toughreact_tecplot`` can be written, according to the ``indices`` +parameter. A start time and time unit for the output can optionally be +specified. + +**Parameters:** + +- | **geo**: :ref:`mulgrid ` + | The ``mulgrid`` geometry object associated with the results. For + flexibility, this geometry need not be fully compatible with the + results – for example, it may contain only a subset of the blocks + for which results are present, or the blocks may be in a different + order. However, if it is not fully compatible, the writing process + will be slower. + +- | **filename**: string + | Name of the \*.pvd file to be written. Names of the individual + \*.vtu files for each time step are similar but with a time index + appended and the file extension changed. + +- | **grid**: :ref:`t2grid ` + | Name of optional ``t2grid`` object associated with the results. + +- | **indices**: list or tuple + | Optional specification of time indices to include in the output. If + set to ``None`` (the default), all time indices will be included. + +- | **start_time**: float + | Optional start time of the simulation, i.e. time associated with + the first set of results. Default is zero. + +- | **time_unit**: string + | Optional time unit for the output. TOUGHREACT Tecplot results are + given at times in years, but this option allows them to be + converted to other units. Options are: 's', 'h', 'd' and 'y', for + seconds, hours, days and years respectively. Default is 's'. + +- | **blockmap**: dictionary + | Dictionary mapping the block names in the geometry to the block + naming system used in the Tecplot output. + +- | **surface_snap**: float + | Tolerance for specifying how close column surface elevations need + to be before being considered "equal" when constructing surface + nodes. + +---- + +Examples +-------- + +Slice plot of drawdown +~~~~~~~~~~~~~~~~~~~~~~ + +This script shows a vertical slice plot along the model's *x*-axis of +the difference in pressure (i.e. drawdown) between the start and end of +a simulation. + +:: + + from mulgrids import * + from t2listing import * + from copy import copy + + geo = mulgrid('gmodel.dat') + results = t2listing('model.listing') + + results.first() + p0 = copy(results.element['Pressure']) + results.last() + p1 = results.element['Pressure'] + + geo.slice_plot('x', (p1-p0)/1.e5, 'Pressure\ difference', 'bar') + +(Note: the ``copy`` command is needed, otherwise the arrays ``p0`` and +``p1`` would both contain the final values of pressure after the +``results.last()`` command.) + +Pressure-temperature diagram +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This script plots model results from a specified block on a +pressure-temperature diagram. + +:: + + from t2listing import * + import matplotlib.pyplot as plt + + lst = t2listing('model.listing') + blk = ' n 60' + [(tp,p), (tt,t)] = lst.history([('e', blk, 'Pressure'), ('e', blk, 'Temperature')]) + + plt.plot(t, p/1.e5, 'o-') + plt.xlabel('T ($\degree$C)') + plt.ylabel('P (bar)') + plt.show() + +.. _comparison_example: + +Comparing results of two models +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This script reads grids and results for two different models, a coarse +model and a fine one, and produces a comparison plot of the time history +of temperature for both models at a given point. + +:: + + from mulgrids import * + from t2listing import * + import matplotlib.pyplot as plt + + geoc, geof = mulgrid('gcoarse.dat'), mulgrid('gfine.dat') + coarse, fine = t2listing('coarse.listing'), t2listing('fine.listing') + + p = [47.e3, 0.0, -7000.0] + blkc = geoc.block_name_containing_point(p) + blkf = geof.block_name_containing_point(p) + + tc, tempc = coarse.history(('e', blkc, 'Temperature')) + tf, temp = fine.history(('e', blkf, 'Temperature')) + + plt.plot(tc, tempc, 'o-', label = 'coarse model') + plt.plot(tf, tempf, 's-', label = 'fine model') + plt.xlabel('time (s)') + plt.ylabel('Temperature ($\degree$C)') + plt.legend() + + plt.show() + diff --git a/doc/source/t2thermo.rst b/doc/source/t2thermo.rst new file mode 100644 index 00000000..8a38af2f --- /dev/null +++ b/doc/source/t2thermo.rst @@ -0,0 +1,332 @@ +:tocdepth: 3 + +.. _t2thermo: + +TOUGH2 thermodynamics +===================== + +.. index:: thermodynamics; IFC-67 + +.. _introduction-6: + +Introduction +------------ + +The ``t2thermo`` library in PyTOUGH contains a Python implementation +of the thermodynamic routines used in TOUGH2. These can be used to +calculate the thermodynamic properties of water and steam under a +range of conditions. They are based on a subset of the IFC-67 +thermodynamic formulation. + +The ``t2thermo`` library can be imported using the command: + +:: + + from t2thermo import * + +The functions available through the ``t2thermo`` library are listed in +the :ref:`table ` below. + +.. container:: + :name: tb:t2thermo_functions + + .. table:: ``t2thermo`` functions + + +------------------------------------------------------------------------+----------+----------------------------+ + | **Function** | **Type** | **Description** | + +========================================================================+==========+============================+ + | :ref:`cowat` | tuple | density and internal | + | | | energy of liquid water | + | | | | + +------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`sat` | float | saturation pressure as a | + | | | function of temperature | + | | | | + +------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`region` | integer | thermodynamic region | + | | | | + | | | | + +------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`separated_steam_fraction` | float | separated steam fraction | + | | | for given enthalpy and | + | | | separator pressure | + | | | | + +------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`supst` | tuple | density and internal | + | | | energy of dry steam | + | | | | + +------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`tsat` | float | saturation temperature as | + | | | a function of pressure | + | | | | + +------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`visw` | float | dynamic viscosity of water | + | | | | + | | | | + +------------------------------------------------------------------------+----------+----------------------------+ + | :ref:`viss` | float | dynamic viscosity of steam | + | | | | + | | | | + +------------------------------------------------------------------------+----------+----------------------------+ + +Thermodynamic functions +----------------------- + +The thermodynamic routines used in TOUGH2 provide functions for liquid +water and dry steam. These functions calculate secondary parameters from +the primary thermodynamic variables. Their names follow the subroutine +names used in the TOUGH2 code. + +---- + +.. _sec:t2thermo:cowat: + +Liquid water: ``cowat(t, p, bounds = False)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``cowat`` function returns a two-element tuple (``d``,\ ``u``) of +density (kg/m\ :math:`^3`) and internal energy (J/kg) of liquid water as +a function of temperature ``t`` (°C) and pressure ``p`` +(Pa). + +**Parameters:** + +- | **t**: float + | Temperature (°C) + +- | **p**: float + | Pressure (Pa) + +- | **bounds**: Boolean + | If ``True``, return ``None`` if the input temperature and pressure + are outside the operating range of the routine (as defined by + thermodynamic region 1 of the IFC-67 specification). + +---- + +.. _sec:t2thermo:supst: + +Dry steam: ``supst(t, p, bounds = False)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``supst`` function returns a two-element tuple (``d``,\ ``u``) of +density (kg/m\ :math:`^3`) and internal energy (J/kg) of dry steam as a +function of temperature ``t`` (°C) and pressure ``p`` +(Pa). + +**Parameters:** + +- | **t**: float + | Temperature (°C) + +- | **p**: float + | Pressure (Pa) + +- | **bounds**: Boolean + | If ``True``, return ``None`` if the input temperature and pressure + are outside the operating range of the routine (as defined by + thermodynamic region 2 of the IFC-67 specification). + +---- + +Viscosity +--------- + +.. _sec:t2thermo:visw: + +Liquid water: ``visw(t,p,ps)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``visw`` function returns the dynamic viscosity (Pa.s) of liquid +water as a function of temperature ``t`` (°C), pressure (Pa) and +saturation pressure (Pa). + +**Parameters:** + +- | **t**: float + | Temperature (°C) + +- | **p**: float + | Pressure (Pa) + +- | **ps**: float + | Saturation pressure (Pa), calculated for example using the ``sat`` + function. + +---- + +.. _sec:t2thermo:viss: + +Dry steam: ``viss(t,d)`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``viss`` function returns the dynamic viscosity (Pa.s) of dry steam +as a function of temperature ``t`` (°C) and density +``d`` (kg/m\ :math:`^3`). + +**Parameters:** + +- | **t**: float + | Temperature (°C) + +- | **d**: float + | Density (kg/m\ :math:`^3`) + +---- + +Saturation line: ``sat(t)`` and ``tsat(p)`` +------------------------------------------- + +.. _sec:t2thermo:sat: + +``sat(t, bounds = False)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``sat`` function returns the saturation pressure (Pa) at a given +temperature ``t`` (°C), for temperatures below the +critical temperature. + +**Parameters:** + +- | **t**: float + | Temperature (°C) + +- | **bounds**: Boolean + | If ``True``, return ``None`` if the input temperature is outside + the operating range of the routine (i.e. less than + 0.01 °C or greater than the critical temperature, + 374.15 °C ). + +---- + +.. _sec:t2thermo:tsat: + +``tsat(p, bounds = False)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``tsat`` function returns the saturation temperature +(°C) at a given pressure ``p`` (Pa), for pressures below +the critical pressure. + +Note that the IFC-67 formulation did not include an explicit formula for +calculating saturation temperature as a function of pressure, so here +(as in TOUGH2) this is calculated using an iterative root-finding +process on the ``sat`` function. The root-finding function is from the +``scipy`` library, so this library must be installed before the ``tsat`` +function will work. + +**Parameters:** + +- | **p**: float + | Pressure (Pa) + +- | **bounds**: Boolean + | If ``True``, return ``None`` if the input pressure is outside the + operating range of the routine (i.e. less than ``sat(0.01)`` or + greater than the critical pressure, 22.12 MPa). + +---- + +Other functions +--------------- + +Separated steam fraction +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _sec:t2thermo:separated_steam_fraction: + +``separated_steam_fraction(h, separator_pressure, separator_pressure2 = None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Returns the separated steam fraction for a given enthalpy ``h`` and +separator pressure. A second separator pressure may be specified in the +case of two-stage flash. + +**Parameters:** + +- | **h**: float + | Enthalpy (J/kg) + +- | **separator_pressure**: float + | Steam separator pressure (Pa) + +- | **separator_pressure2**: float (or ``None``) + | Second separator pressure (Pa) for two-stage flash – set to + ``None`` (the default) for single-stage. + +---- + +Determining thermodynamic region +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _sec:t2thermo:region: + +``region(t, p)`` +^^^^^^^^^^^^^^^^ + +Returns the thermodynamic region (integer, or ``None``) corresponding to +the given temperature (°C) and pressure (Pa), as defined +by the IFC-67 specification. The regions are: + +#. liquid water + +#. dry steam + +#. supercritical + +#. near-critical + +If the input temperature and/or pressure are outside the operating range +of the IFC-67 formulation, the routine will return ``None``. + +**Parameters:** + +- | **t**: float + | Temperature (°C) + +- | **Pressure**: float + | Pressure (Pa) + +---- + +.. _example-3: + +Example +------- + +The following script reads in a geometry file and writes an initial +conditions file with approximate hydrostatic conditions corresponding to +a specified vertical temperature gradient. In this case, the model has a +simple flat surface, so that each column has the same number of layers. +The ``cowat`` function is used to calculate the fluid density at each +layer, and hence the approximate vertical pressure distribution. + +:: + + from mulgrids import * + from t2thermo import * + + geo = mulgrid('gmodel.dat') + + patm, tatm = 101.325e3, 15.0 + ptblk = np.zeros((geo.num_blocks, 2)) + ptblk[:,0] = patm; ptblk[:,1] = tatm + + g = 9.8 + p, t = patm, tatm + thick = 0.0 + tgradient = 30 # deg C/km + for lay in geo.layerlist[1:]: + d = cowat(t, p)[0] + thisthick = lay.top - lay.bottom + h = 0.5 * (thick + thisthick) + p += d * g * h + t += tgradient / 1.e3 * h + thick = thisthick + for col in geo.columnlist: + blkname = geo.block_name(lay.name, col.name) + iblk = geo.block_name_index[blkname] + ptblk[iblk] = [p, t] + inc = dat.grid.incons(ptblk) + inc.write('model.incon') + diff --git a/doc/t2data.tex b/doc/t2data.tex deleted file mode 100755 index 549252f3..00000000 --- a/doc/t2data.tex +++ /dev/null @@ -1,1029 +0,0 @@ -\chapter{TOUGH2 data files} -\label{datafiles} -\index{TOUGH2 data files} - -\section{Introduction} -The \texttt{t2data} library in PyTOUGH contains classes and routines for creating, editing and saving TOUGH2 or AUTOUGH2 data files. It can be imported using the command: - -\begin{lstlisting} - from t2data import * -\end{lstlisting} - -\section{\texttt{t2data} objects} -\index{PyTOUGH!classes!\texttt{t2data}} -\index{TOUGH2 data files!creating} -\index{TOUGH2 data files!objects} - -The \texttt{t2data} library defines a \texttt{t2data} class, used for representing TOUGH2 data files. - -\textbf{Example:} - -\begin{lstlisting} -dat = t2data() -\end{lstlisting} - -creates an empty \texttt{t2data} object called \texttt{dat}. - -\begin{lstlisting} -dat = t2data(filename) -\end{lstlisting} - -creates a \texttt{t2data} object called \texttt{dat} and reads its contents from file \texttt{filename}. (It is also possible to read the mesh part of the \texttt{t2data} object from separate files - see below.) - -Because a \texttt{t2data} object contains a large number of different parameters, it is usually easier to load one from an existing TOUGH2 data file and edit it, rather than creating a new one from scratch. - -\subsection{Properties} - -The main properties of a \texttt{t2data} object are listed in Table \ref{tb:t2data_properties}. In general, each of these properties corresponds to an input block in a TOUGH2 data file. Most of these input blocks contain a number of different parameters, so that the \texttt{t2data} property corresponding to each input block is usually in the form of a dictionary, containing a number of keys representing sub-properties. - -For example, the maximum number of time steps for the simulation is controlled by \texttt{max\_timesteps} key in the \texttt{parameter} property, which for a \texttt{t2data} object called \texttt{dat} would be accessed by \texttt{dat.parameter['max\_timesteps']}. - -The details of all the \texttt{t2data} properties are given below. - -\index{TOUGH2 data files!properties} -\begin{sidewaystable} - \begin{center} - \begin{tabular}{|l|l|l|l|} - \hline - \textbf{Property} & \textbf{Type} & \textbf{Description} & \textbf{Input block}\\ - \hline - \hyperref[sec:t2data:capillarity]{\texttt{capillarity}} & dictionary & capillarity function & RELP\\ - \hyperref[sec:t2data:diffusion]{\texttt{diffusion}} & list & diffusion coefficients & DIFFU\\ - \hyperref[sec:t2data:echo_extra_precision]{\texttt{echo\_extra\_precision}} & Boolean & echoing extra precision sections to main data file (AUTOUGH2 only) & --\\ - \hyperref[sec:t2data:end_keyword]{\texttt{end\_keyword}} & string & keyword to end file & ENDCY or ENDFI\\ - \hyperref[sec:t2data:extra_precision]{\texttt{extra\_precision}} & list & data sections read from extra precision auxiliary file (AUTOUGH2 only) & --\\ - \hyperref[sec:t2data:filename]{\texttt{filename}} & string & file name on disk & --\\ - \hyperref[sec:t2data:generator]{\texttt{generator}} & dictionary & generators (by block name and generator name) & GENER\\ - \hyperref[sec:t2data:generatorlist]{\texttt{generatorlist}} & list & generators (by index) & GENER\\ - \hyperref[sec:t2data:grid]{\texttt{grid}} & \hyperref[t2grids]{\texttt{t2grid}} & model grid & ELEME, CONNE\\ - \hyperref[sec:t2data:history_block]{\texttt{history\_block}} & list & history blocks (TOUGH2 only) & FOFT\\ - \hyperref[sec:t2data:history_connection]{\texttt{history\_connection}} & list & history connections (TOUGH2 only) & COFT\\ - \hyperref[sec:t2data:history_generator]{\texttt{history\_generator}} & list & history generators (TOUGH2 only) & GOFT\\ - \hyperref[sec:t2data:incon]{\texttt{incon}} & dictionary & initial conditions & INCON\\ - \hyperref[sec:t2data:indom]{\texttt{indom}} & dictionary & rocktype-specific initial conditions & INDOM\\ - \hyperref[sec:t2data:lineq]{\texttt{lineq}} & dictionary & linear equation solver options (AUTOUGH2 only) & LINEQ\\ - \hyperref[sec:t2data:meshfilename]{\texttt{meshfilename}} & string or tuple & file name(s) on disk containing mesh data & --\\ - \hyperref[sec:t2data:meshmaker]{\texttt{meshmaker}} & list & mesh generation options & MESHM\\ - \hyperref[sec:t2data:more_options]{\texttt{more\_option}} & array of integer & additional parameter options & MOMOP\\ - \hyperref[sec:t2data:multi]{\texttt{multi}} & dictionary & EOS configuration & MULTI\\ - \hyperref[sec:t2data:noversion]{\texttt{noversion}} & Boolean & suppressing printing of version summary & NOVER\\ - \hyperref[sec:t2data:num_generators]{\texttt{num\_generators}} & integer & number of generators & --\\ - \hyperref[sec:t2data:output_times]{\texttt{output\_times}} & dictionary & times to write output & TIMES\\ - \hyperref[sec:t2data:parameter]{\texttt{parameter}} & dictionary & run-time parameters & PARAM\\ - \hyperref[sec:t2data:relative_permeability]{\texttt{relative\_permeability}} & dictionary & relative permeability function & RELP\\ - \hyperref[sec:t2data:selection]{\texttt{selection}} & dictionary & selection parameters & SELEC\\ - \hyperref[sec:t2data:short_output]{\texttt{short\_output}} & dictionary & short output (AUTOUGH2 only) & SHORT\\ - \hyperref[sec:t2data:simulator]{\texttt{simulator}} & string & simulator name (AUTOUGH2 only) & SIMUL\\ - \hyperref[sec:t2data:solver]{\texttt{solver}} & dictionary & linear equation solver options (TOUGH2 only) & SOLVR\\ - \hyperref[sec:t2data:start]{\texttt{start}} & Boolean & run initialisation option & START\\ - \hyperref[sec:t2data:title]{\texttt{title}} & string & simulation title & TITLE\\ - \hyperref[sec:t2data:type]{\texttt{type}} & string & simulator type (AUTOUGH2 or TOUGH2) & --\\ - \hline - \end{tabular} - \caption{Properties of a \texttt{t2data} object} - \label{tb:t2data_properties} - \end{center} -\end{sidewaystable} - -\begin{snugshade} -\subsubsection{\texttt{capillarity} property} -\end{snugshade} -\label{sec:t2data:capillarity} -\index{TOUGH2 data files!capillarity} - -A dictionary property specifying the capillarity function used, corresponding to the second line of the \textbf{RPCAP} input block in the TOUGH2 data file. The individual keys of this property are given in Table \ref{tb:capillarity}. - -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|l|} - \hline - \textbf{Key} & \textbf{Type} & \textbf{Description} & \textbf{TOUGH2 parameter}\\ - \hline - \texttt{parameters} & array (7) of float & function parameters & CP\\ - \texttt{type} & integer & type of capillarity function & ICP\\ - \hline - \end{tabular} - \caption{\texttt{capillarity} property keys} - \label{tb:capillarity} - \end{center} -\end{table} - -\begin{snugshade} -\subsubsection{\texttt{diffusion} property} -\end{snugshade} -\label{sec:t2data:diffusion} -\index{TOUGH2 data files!diffusion} - -A list property specifying diffusion coefficients for each mass component simulated, corresponding to the \textbf{DIFFU} input block in the TOUGH2 data file. The list has length \texttt{multi['num\_components']} (i.e. NK in TOUGH2 terminology), and each element is a list of the diffusion coefficients for each component (with length \texttt{multi['num\_phases']}, or NPH). - -\begin{snugshade} -\subsubsection{\texttt{echo\_extra\_precision} property} -\end{snugshade} -\label{sec:t2data:echo_extra_precision} -\index{TOUGH2 data files!extra precision} -\index{TOUGH2!AUTOUGH2} - -A Boolean property (AUTOUGH2 only) governing whether data written to an auxiliary extra-precision file is also echoed to the main data file. If \texttt{True}, all extra-precision data sections are echoed to the main file. - -\begin{snugshade} -\subsubsection{\texttt{end\_keyword} property} -\end{snugshade} -\label{sec:t2data:end_keyword} -\index{TOUGH2 data files!end keyword} - -A string property containing the keyword used in the data file to end the file. Normally this is `ENDCY', but `ENDFI' can also be used. - -\begin{snugshade} -\subsubsection{\texttt{extra\_precision} property} -\end{snugshade} -\label{sec:t2data:extra_precision} -\index{TOUGH2 data files!extra precision} -\index{TOUGH2!AUTOUGH2} - -A list property determining which data sections will be written to an auxiliary extra-precision file (AUTOUGH2 only). Recent versions of AUTOUGH2 support an additional data file containing some data written with extra precision. Possible extra-precision data sections are ROCKS, ELEME, CONNE, RPCAP and GENER. Typical usage of this extra-precision data is for automatic model calibration using PEST or similar software, where calculation of derivatives of model outputs with respect to model parameters requires higher precision than is possible with the standard TOUGH2 data file format. - -The \texttt{extra\_precision} parameter may be a list containing names of sections to be written in extra precision (e.g. [`RPCAP', `GENER']), or set to \texttt{False} to disable extra precision (equivalent to []), or to \texttt{True} to specify that all possible sections should be written in extra precision. - -The \hyperref[sec:t2data:read]{\texttt{read()}} method of a \texttt{t2data} object determines whether extra precision data are available by searching for an additional file with the same base name as the data file itself, but with a '.pdat' or '.PDAT' extension (depending on the case of the main data file name). If no such file exists, then no extra precision data will be read. - -\begin{snugshade} -\subsubsection{\texttt{filename} property} -\end{snugshade} -\label{sec:t2data:filename} -\index{TOUGH2 data files!filename} - -A string property containing the name of the TOUGH2 data file on disk. (This does not correspond to any parameter in the TOUGH2 data file.) - -\begin{snugshade} -\subsubsection{\texttt{generator} property} -\end{snugshade} -\label{sec:t2data:generator} -\index{TOUGH2 data files!generators} - -A dictionary property containing the generators for the simulation, accessed by tuples of block name and generator name. Each generator is an object of type \hyperref[t2generatorobjects]{\texttt{t2generator}}, which is described in section \ref{t2generatorobjects}. - -\begin{snugshade} -\subsubsection{\texttt{generatorlist} property} -\end{snugshade} -\label{sec:t2data:generatorlist} -\index{TOUGH2 data files!generators} - -A list property containing the generators for the simulation, accessed by index. - -\begin{snugshade} -\subsubsection{\texttt{grid} property} -\end{snugshade} -\label{sec:t2data:grid} -\index{TOUGH2 data files!grid} - -A \hyperref[t2grids]{\texttt{t2grid}} object (see chapter \ref{t2grids}) representing the simulation grid, corresponding to the \textbf{ELEME} and \textbf{CONNE} input blocks in a TOUGH2 data file. - -\begin{snugshade} -\subsubsection{\texttt{history\_block} property} -\end{snugshade} -\label{sec:t2data:history_block} -\index{TOUGH2 data files!history blocks} -\index{TOUGH2 data files!FOFT} - -A list property containing blocks for which time history output is required, corresponding to the \textbf{FOFT} input block in a TOUGH2 data file. If the \texttt{t2data} object contains grid data, the items in this list are \hyperref[t2blockobjects]{\texttt{t2block}} objects; otherwise, they are block names (i.e. strings). - -\begin{snugshade} -\subsubsection{\texttt{history\_connection} property} -\end{snugshade} -\label{sec:t2data:history_connection} -\index{TOUGH2 data files!history connections} -\index{TOUGH2 data files!COFT} - -A list property containing connections for which time history output is required, corresponding to the \textbf{COFT} input block in a TOUGH2 data file. If the \texttt{t2data} object contains grid data, the items in this list are \hyperref[t2connectionobjects]{\texttt{t2connection}} objects; otherwise, they are tuples of block names (i.e. tuples of strings). - -\begin{snugshade} -\subsubsection{\texttt{history\_generator} property} -\end{snugshade} -\label{sec:t2data:history_generator} -\index{TOUGH2 data files!history generators} -\index{TOUGH2 data files!GOFT} - -A list property containing blocks in which generators are defined and for which time history output is required, corresponding to the \textbf{GOFT} input block in a TOUGH2 data file. If the \texttt{t2data} object contains grid data, the items in this list are \hyperref[t2blockobjects]{\texttt{t2block}} objects; otherwise, they are block names (i.e. strings). - -\begin{snugshade} -\subsubsection{\texttt{incon} property} -\end{snugshade} -\label{sec:t2data:incon} -\index{TOUGH2 data files!initial conditions} -\index{TOUGH2 initial conditions!in data file} - -A dictionary property representing the initial conditions for the simulation, accessed by block name, corresponding to the \textbf{INCON} input block in a TOUGH2 data file. The value of each element of the dictionary is a list consisting of the porosity of the block, followed by a list of the specified initial primary thermodynamic variables in the block. If the TOUGH2 NSEQ and NADD values are used, these are stored after the thermodynamic variables. If they are not used, they can either be set to \texttt{None} or simply omitted. - -For example, to specify porosity 0.1 and initial conditions (101.3E3, 20.0) in block \texttt{'AB105'} of a \texttt{t2data} object called \texttt{dat}, set \texttt{dat.incon['AB105'] = [0.1, [101.3e3, 20.0]]}. - -To specify these same conditions but with NSEQ = 10 and NADD = 2, set \texttt{dat.incon['AB105'] = [0.1, [101.3e3, 20.0], 10, 2]}. - -Porosity can be specified as \texttt{None} if default porosity (from the rocktype) is to be used. - -\begin{snugshade} -\subsubsection{\texttt{indom} property} -\end{snugshade} -\label{sec:t2data:indom} -\index{TOUGH2 data files!initial conditions!by rocktype} - -A dictionary property representing the initial conditions for the simulation, accessed by rocktype name, corresponding to the \textbf{INDOM} input block in a TOUGH2 data file. The value of each element of the dictionary is a list consisting of the specified initial primary thermodynamic variables for the rocktype. - -\begin{snugshade} -\subsubsection{\texttt{lineq} property} -\end{snugshade} -\label{sec:t2data:lineq} -\index{TOUGH2 data files!linear equation solver!AUTOUGH2} -\index{TOUGH2!AUTOUGH2} - -A dictionary property representing linear equation solver options, corresponding to the \textbf{LINEQ} input block in an AUTOUGH2 data file. The individual keys of this property are given in Table \ref{tb:lineq}. - -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|l|} - \hline - \textbf{Key} & \textbf{Type} & \textbf{Description} & \textbf{AUTOUGH2 parameter}\\ - \hline - \texttt{epsilon} & float & solver tolerance & EPN\\ - \texttt{gauss} & integer & Gauss elimination parameter & IGAUSS\\ - \texttt{max\_iterations} & integer & max. number of iterations & MAXIT\\ - \texttt{num\_orthog} & integer & number of orthogonalisations & NORTH\\ - \texttt{type} & integer & type of solver (1 or 2) & ISOLVR\\ - \hline - \end{tabular} - \caption{\texttt{lineq} property keys} - \label{tb:lineq} - \end{center} -\end{table} - -\begin{snugshade} -\subsubsection{\texttt{meshfilename} property} -\end{snugshade} -\label{sec:t2data:meshfilename} -\index{TOUGH2 data files!mesh file} - -A string property (or tuple of strings) containing the name(s) of files on disk containing the mesh data. (This does not correspond to any parameter in the TOUGH2 data file.) Its default value is an empty string which means mesh data will be read from the main data file. - -If \texttt{meshfilename} is a single (non-empty) string, this is interpreted as the name of a formatted text file containing `ELEME' and `CONNE' sections specifying the mesh (e.g. the `MESH' file created by TOUGH2 or TOUGH2\_MP). - -If \texttt{meshfilename} is a tuple of two strings, these are interpreted as the names of two binary files containing the mesh data, e.g. the `MESHA' and `MESHB' files created by TOUGH2\_MP. - -\begin{snugshade} -\subsubsection{\texttt{meshmaker} property} -\end{snugshade} -\label{sec:t2data:meshmaker} -\index{TOUGH2 data files!mesh maker} - -A list property representing mesh generation options, corresponding to the \textbf{MESHM} input block in a TOUGH2 data file. For more detail on the use of \textbf{MESHM} data, consult the TOUGH2 users' guide \citep{tough2}. - -The \textbf{MESHM} data may contain multiple sections (e.g. creation of a rectilinear XYZ grid followed by MINC processing), so the \texttt{meshmaker} property is structured as a list of two-element tuples, each containing the type of section (\texttt{rz2d}, \texttt{xyz} or \texttt{minc}) followed by the section data itself. - -The form of the section data varies depending on the section type. For the \texttt{rz2d} type it is also structured as a list, as these types may contain variable numbers of sub-sections. (For example, data for the \texttt{rz2d} type may contain multiple \texttt{logar} sub-sections for different logarithmic radial parts of the mesh.) Each sub-section is again a two-element tuple, consisting of the sub-section type (a string) followed by a dictionary containing the data for the sub-section. - -Data for the \texttt{xyz} type are also structured as a list, with the first element containing the stand-alone \texttt{deg} parameter (a float), followed by the other sub-sections, corresponding to the \textbf{NX}, \textbf{NY} and \textbf{NZ} sub-sections in the TOUGH2 data file. The \texttt{minc} type does not have sub-sections so MINC data are not structured as a list but simply a dictionary. - -Possible sub-section types for \texttt{rz2d} data are \texttt{radii}, \texttt{equid}, \texttt{logar} and \texttt{layer}, corresponding to their (uppercase) keyword counterparts in the TOUGH2 data file. Data keys for these types are given in Table \ref{tb:rz2d}. Data keys for the \texttt{xyz} and \texttt{minc} data are given in Tables \ref{tb:xyz} and \ref{tb:minc}. - -\textbf{Example}: The easiest way to understand how the \texttt{meshmaker} property works is to read some example input data into a \texttt{t2data} object and examine the result. The \textbf{MESHM} data for the standard TOUGH2 test problem `rhbc' (`Production from a geothermal reservoir with hypersaline brine') is represented as a \texttt{t2data} \texttt{meshmaker} property as follows: - -\begin{lstlisting} -[('rz2d',[ - ('radii', {'radii': [5.0]}), - ('equid', {'dr': 2.0, 'nequ': 1}), - ('logar', {'rlog': 100.0, 'nlog': 50}), - ('logar', {'rlog': 1000.0, 'nlog': 20}), - ('equid', {'dr': 0.0, 'nequ': 1}), - ('layer', {'layer': [500.0]}) - ]) -] -\end{lstlisting} - -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|l|} - \hline - \textbf{Key} & \textbf{Type} & \textbf{Description} & \textbf{TOUGH2 parameter}\\ - \hline - \multicolumn{4}{|c|}{\textbf{radii} sub-section keys}\\ - \hline - \texttt{radii} & list & specified mesh radii & RC\\ - \hline - \multicolumn{4}{|c|}{\textbf{equid} sub-section keys}\\ - \hline - \texttt{dr} & float & radial increment & DR\\ - \texttt{nequ} & integer & number of equidistant radii & NEQU\\ - \hline - \multicolumn{4}{|c|}{\textbf{logar} sub-section keys}\\ - \hline - \texttt{dr} & float & reference radial increment & DR\\ - \texttt{nlog} & integer & number of logarithmic radii & NLOG\\ - \texttt{rlog} & float & largest radius & RLOG\\ - \hline - \multicolumn{4}{|c|}{\textbf{layer} sub-section keys}\\ - \hline - \texttt{layer} & list & layer thicknesses & H\\ - \hline - \end{tabular} - \caption{\texttt{rz2d} data keys} - \label{tb:rz2d} - \end{center} -\end{table} - -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|l|} - \hline - \textbf{Key} & \textbf{Type} & \textbf{Description} & \textbf{TOUGH2 parameter}\\ - \hline - \multicolumn{4}{|c|}{\texttt{deg} parameter}\\ - \hline - \texttt{deg} & float & angle between y-axis and horizontal & DEG\\ - \hline - \multicolumn{4}{|c|}{NX, NY and NZ keys}\\ - \hline - \texttt{del} & float & constant grid increment & DEL\\ - \texttt{deli} & list & variable grid increments & DEL\\ - \texttt{no} & integer & number of grid increments & DR\\ - \texttt{ntype} & string & axis direction (`NX', `NY' or `NZ') & NTYPE\\ - \hline - \end{tabular} - \caption{\texttt{xyz} data keys} - \label{tb:xyz} - \end{center} -\end{table} - -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|l|} - \hline - \textbf{Key} & \textbf{Type} & \textbf{Description} & \textbf{TOUGH2 parameter}\\ - \hline - \texttt{dual} & string & treatment of global matrix-matrix flow & DUAL\\ - \texttt{num\_continua} & integer & number of interacting continua & J\\ - \texttt{spacing} & list & fracture spacings & PAR\\ - \texttt{type} & string & proximity function type & TYPE\\ - \texttt{vol} & list & volume fractions & VOL\\ - \texttt{where} & string & direction of volume fraction specification & WHERE\\ - \hline - \end{tabular} - \caption{\texttt{minc} data keys} - \label{tb:minc} - \end{center} -\end{table} - -\begin{snugshade} -\subsubsection{\texttt{more\_option} property} -\end{snugshade} -\label{sec:t2data:more_options} -\index{TOUGH2 data files!simulation parameters} - -An array property containing additional integer parameter options, corresponding to the \textbf{MOMOP} input block in a TOUGH2 data file (it is not recognised by AUTOUGH2). Introduced by iTOUGH2, this is an extension of the \texttt{parameter.option} property. It is of length 21 and is populated with zeros by default. Like the \texttt{parameter.option} property, values are accessed using 1-based (not zero-based) indices. - -\begin{snugshade} -\subsubsection{\texttt{multi} property} -\end{snugshade} -\label{sec:t2data:multi} -\index{TOUGH2 data files!equation of state} - -A dictionary property selecting the equation of state (EOS) module used and setting associated parameters, corresponding to the \textbf{MULTI} input block in a TOUGH2 or AUTOUGH2 data file. The individual keys of this property are given in Table \ref{tb:multi}. - -\begin{table} - \begin{center} - \begin{tabular}{|l|l|p{37mm}|p{23mm}|} - \hline - \textbf{Key} & \textbf{Type} & \textbf{Description} & \textbf{TOUGH2 parameter}\\ - \hline - \texttt{eos} & string & EOS name (AUTOUGH2 only) & NAMEOS\\ - \texttt{num\_components} & integer & number of components & NK\\ - \texttt{num\_equations} & integer & number of equations & NEQ\\ - \texttt{num\_inc} & integer & number of mass components in INCON data (TOUGH2 only) & NKIN\\ - \texttt{num\_phases} & integer & number of phases & NPH\\ - \texttt{num\_secondary\_parameters} & integer & number of secondary parameters & NB\\ - \hline - \end{tabular} - \caption{\texttt{multi} property keys} - \label{tb:multi} - \end{center} -\end{table} - -\begin{snugshade} -\subsubsection{\texttt{noversion} property} -\end{snugshade} -\label{sec:t2data:noversion} - -A Boolean property specifying whether to suppress printing of version and date information, corresponding to the \textbf{NOVER} input block in a TOUGH2 data file. - -\begin{snugshade} -\subsubsection{\texttt{num\_generators} property} -\end{snugshade} -\label{sec:t2data:num_generators} -\index{TOUGH2 data files!generators!number of} - -A read-only integer property returning the number of generators. - -\begin{snugshade} -\subsubsection{\texttt{output\_times} property} -\end{snugshade} -\label{sec:t2data:output_times} -\index{TOUGH2 data files!output times} - -A dictionary property specifying the times at which model output is required, corresponding to the \textbf{TIMES} input block in a TOUGH2 data file. The individual keys of this property are given in Table \ref{tb:outputtimes}. - -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|p{20mm}|} - \hline - \textbf{Key} & \textbf{Type} & \textbf{Description} & \textbf{TOUGH2 parameter}\\ - \hline - \texttt{max\_timestep} & float & maximum time step & DELAF\\ - \texttt{num\_times\_specified} & integer & number of times specified & ITI\\ - \texttt{num\_times} & integer & total number of times & ITE\\ - \texttt{time} & list of float & times at which output is required & TIS\\ - \texttt{time\_increment} & float & time increment after specified times & TINTER\\ - \hline - \end{tabular} - \caption{\texttt{output\_times} property keys} - \label{tb:outputtimes} - \end{center} -\end{table} - -\begin{snugshade} -\subsubsection{\texttt{parameter} property} -\end{snugshade} -\label{sec:t2data:parameter} -\index{TOUGH2 data files!simulation parameters} - -A dictionary property specifying run-time parameters, corresponding to the \textbf{PARAM} input block in a TOUGH2 data file. The individual keys of this property are given in Table \ref{tb:parameter}. - -The \texttt{option} parameter (MOP array in TOUGH2) is an array of 24 integers, and has a 1-based index so that its indices are the same as those in the TOUGH2 documentation. (In fact it is really zero-based, like all other Python arrays, but has an extra unused zero$^{th}$ element). - -\begin{sidewaystable} - \begin{center} - \begin{tabular}{|l|l|l|l|} - \hline - \textbf{Key} & \textbf{Type} & \textbf{Description} & \textbf{TOUGH2 parameter}\\ - \hline - \texttt{absolute\_error} & float & absolute convergence tolerance & RE2\\ - \texttt{be} & float & enhanced vapour diffusion & BE\\ - \texttt{const\_timestep} & float & time step length & DELTEN\\ - \texttt{default\_incons} & list of float & default initial conditions & DEP\\ - \texttt{derivative\_increment} & float & numerical derivate increment factor & DFAC\\ - \texttt{diff0} & float & diffusive vapour flux (AUTOUGH2 only) & DIFF0\\ - \texttt{gravity} & float & gravitational acceleration & GF\\ - \texttt{max\_duration} & integer & maximum simulation duration (machine seconds) & MSEC\\ - \texttt{max\_iterations} & integer & maximum number of iterations per time step & NOITE\\ - \texttt{max\_timesteps} & integer & maximum number of time steps & MCYC\\ - \texttt{max\_timestep} & float & maximum time step size & DELTMX\\ - \texttt{newton\_weight} & float & Newton-Raphson weighting factor & WNR\\ - \texttt{option} & array(24) of integer & simulation options & MOP\\ - \texttt{pivot} & float & pivoting parameter for linear solver & U\\ - \texttt{print\_block} & string & block name for short printout & ELST\\ - \texttt{print\_interval} & integer & time step interval for printing & MCYPR\\ - \texttt{print\_level} & integer & amount of printout & KDATA\\ - \texttt{relative\_error} & float & relative convergence tolerance & RE1\\ - \texttt{scale} & float & grid scale factor & SCALE\\ - \texttt{texp} & float & binary diffusion temperature parameter & TEXP\\ - \texttt{timestep\_reduction} & float & time step reduction factor & REDLT\\ - \texttt{timestep} & list of float & specified time step sizes & DLT\\ - \texttt{tstart} & float & start time (seconds) & TSTART\\ - \texttt{tstop} & float & stop time & TIMAX\\ - \texttt{upstream\_weight} & float & upstream weighting factor & WUP\\ - \hline - \end{tabular} - \caption{\texttt{parameter} property keys} - \label{tb:parameter} - \end{center} -\end{sidewaystable} - -\begin{snugshade} -\subsubsection{\texttt{relative\_permeability} property} -\end{snugshade} -\label{sec:t2data:relative_permeability} -\index{TOUGH2 data files!relative permeability} - -A dictionary property specifying the relative permeability function used, corresponding to the first line of the \textbf{RPCAP} input block in the TOUGH2 data file. The individual keys of this property are given in Table \ref{tb:relativepermeability}. - -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|p{20mm}|} - \hline - \textbf{Key} & \textbf{Type} & \textbf{Description} & \textbf{TOUGH2 parameter}\\ - \hline - \texttt{parameters} & array (7) of float & function parameters & RP\\ - \texttt{type} & integer & type of relative permeability function & IRP\\ - \hline - \end{tabular} - \caption{\texttt{relative\_permeability} property keys} - \label{tb:relativepermeability} - \end{center} -\end{table} - -\begin{snugshade} -\subsubsection{\texttt{selection} property} -\end{snugshade} -\label{sec:t2data:selection} -\index{TOUGH2 data files!selection parameters} - -A dictionary property representing selection parameters for the simulation (only used by some EOS modules, e.g. EOS7, EOS7R, EWASG), corresponding to the \textbf{SELEC} block in the TOUGH2 data file. - -The dictionary contains two keys: `integer' and `float', the first of which accesses a list of the integer selection parameters (the first line of the \textbf{SELEC} block), while the second accesses a list of the float selection parameters (the remaining lines of the \textbf{SELEC} block). - -\begin{snugshade} -\subsubsection{\texttt{short\_output} property} -\end{snugshade} -\label{sec:t2data:short_output} -\index{TOUGH2 data files!short output} -\index{TOUGH2!AUTOUGH2} - -A dictionary property representing blocks, connections and generators for which short output is required, corresponding to the \textbf{SHORT} input block in an AUTOUGH2 data file. - -The dictionary contains four keys: `frequency', `block', `connection' and `generator'. The last three of these access lists of blocks, connections and generators respectively for short output. (Note that each of these lists contains \hyperref[t2blockobjects]{\texttt{t2block}}, \hyperref[t2connectionobjects]{\texttt{t2connection}} or \hyperref[t2generatorobjects]{\texttt{t2generator}} objects, rather than names.) The `frequency' key accesses the time step frequency (an integer) for which short output is required. - -\begin{snugshade} -\subsubsection{\texttt{simulator} property} -\end{snugshade} -\label{sec:t2data:simulator} -\index{TOUGH2 data files!simulator type} - -A string property specifying the type of simulator, corresponding to the \textbf{SIMUL} input block in an AUTOUGH2 data file. - -\begin{snugshade} -\subsubsection{\texttt{solver} property} -\end{snugshade} -\label{sec:t2data:solver} -\index{TOUGH2 data files!linear equation solver!TOUGH2} - -A dictionary property representing linear equation solver options, corresponding to the \textbf{SOLVR} input block in a TOUGH2 data file. The individual keys of this property are given in Table \ref{tb:solver}. - -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|l|} - \hline - \textbf{Key} & \textbf{Type} & \textbf{Description} & \textbf{TOUGH2 parameter}\\ - \hline - \texttt{closure} & float & convergence criterion & CLOSUR\\ - \texttt{relative\_max\_iterations} & float & relative max. number of iterations & RITMAX\\ - \texttt{type} & integer & solver type & MATSLV\\ - \texttt{o\_precond} & string & O-preconditioning type & OPROCS\\ - \texttt{z\_precond} & string & Z-preconditioning type & ZPROCS\\ - \hline - \end{tabular} - \caption{\texttt{solver} property keys} - \label{tb:solver} - \end{center} -\end{table} - -\begin{snugshade} -\subsubsection{\texttt{start} property} -\end{snugshade} -\label{sec:t2data:start} -\index{TOUGH2 data files!flexible start option} - -A Boolean property specifying whether the flexible start option is used, corresponding to the \textbf{START} input block in a TOUGH2 data file. - -\begin{snugshade} -\subsubsection{\texttt{title} property} -\end{snugshade} -\label{sec:t2data:title} -\index{TOUGH2 data files!title} - -A string property containing the simulation title, corresponding to the \textbf{TITLE} input block in a TOUGH2 data file. - -\begin{snugshade} -\subsubsection{\texttt{type} property} -\end{snugshade} -\label{sec:t2data:type} -\index{TOUGH2 data files!simulator type} -\index{TOUGH2} -\index{TOUGH2!AUTOUGH2} - -A string property specifying the simulator type (`AUTOUGH2' or `TOUGH2'). Changing the value of this property will cause one of the \hyperref[sec:t2data:convert_to_TOUGH2]{\texttt{convert\_to\_TOUGH2()}} or \hyperref[sec:t2data:convert_to_AUTOUGH2]{\texttt{convert\_to\_AUTOUGH2()}} methods to be executed, with default method parameters. Hence, changing the \texttt{type} property to `AUTOUGH2' causes the EOS to be set to the default `EW'. It is also not possible to specify TOUGH2\_MP options when setting \texttt{type}. For more control over how the conversion is carried out, use the conversion methods directly instead of setting \texttt{type}. - -\subsection{Functions for reading data from file} -\index{TOUGH2 data files!reading} - -It is possible to specify customized functions to control how data are read from a TOUGH2 data file. This is done using the optional \texttt{read\_function} parameter when a \texttt{t2data} object is created- in exactly the same way it is done for a \texttt{mulgrid} object. For more details, see the corresponding documentation for \texttt{mulgrid} objects in section \ref{mulgridreadfunctions}. By default, the read functions for \texttt{t2data} objects are given by the \texttt{default\_read\_function} dictionary. - -\subsection{Methods} - -The main methods of a \texttt{t2data} object are listed in Table \ref{tb:t2data_methods}. Details of these methods are given below. - -\index{TOUGH2 data files!methods} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|p{65mm}|} - \hline - \textbf{Method} & \textbf{Type} & \textbf{Description}\\ - \hline - \hyperref[sec:t2data:add_generator]{\texttt{add\_generator}} & -- & adds a generator\\ - \hyperref[sec:t2data:clear_generators]{\texttt{clear\_generators}} & -- & deletes all generators\\ - \hyperref[sec:t2data:convert_to_AUTOUGH2]{\texttt{convert\_to\_AUTOUGH2}} & -- & converts from TOUGH2 input to AUTOUGH2\\ - \hyperref[sec:t2data:convert_to_TOUGH2]{\texttt{convert\_to\_TOUGH2}} & -- & converts from AUTOUGH2 input to TOUGH2\\ - \hyperref[sec:t2data:delete_generator]{\texttt{delete\_generator}} & -- & deletes a generator\\ - \hyperref[sec:t2data:delete_orphan_generators]{\texttt{delete\_orphan\_generators}} & -- & deletes orphaned generators\\ - \hyperref[sec:t2data:effective_incons]{\texttt{effective\_incons}} & list or \hyperref[incons]{\texttt{t2incon}} & effective initial conditions\\ - \hyperref[sec:t2data:generator_index]{\texttt{generator\_index}} & integer & returns index of generator with specified name and block name\\ - \hyperref[sec:t2data:json]{\texttt{json}} & dictionary & Waiwera JSON input \\ - \hyperref[sec:t2data:read]{\texttt{read}} & \hyperref[datafiles]{\texttt{t2data}} & reads data file from disk\\ - \hyperref[sec:t2data:rename_blocks]{\texttt{rename\_blocks}} & -- & renames blocks\\ - \hyperref[sec:t2data:run]{\texttt{run}} & -- & runs a TOUGH2 simulation\\ - \hyperref[sec:t2data:specific_generation]{\texttt{specific\_generation}} & \texttt{np.array} & generation per unit volume in each block\\ - \hyperref[sec:t2data:total_generation]{\texttt{total\_generation}} & \texttt{np.array} & total generation in each block\\ - \hyperref[sec:t2data:transfer_from]{\texttt{transfer\_from}} & -- & transfers data from another \texttt{t2data} object\\ - \hyperref[sec:t2data:write]{\texttt{write}} & -- & writes to data file on disk\\ - \hline - \end{tabular} - \caption{Methods of a \texttt{t2data} object} - \label{tb:t2data_methods} - \end{center} -\end{table} - -\begin{snugshade} -\subsubsection{\texttt{add\_generator(\emph{generator})}} -\end{snugshade} -\label{sec:t2data:add_generator} -\index{TOUGH2 data files!adding!generators} -\index{TOUGH2 data files!generators!adding} - -Adds a generator to the data file object. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{generator}: \hyperref[t2generatorobjects]{\texttt{t2generator}}\\ - Generator to be added to the data file object. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{convert\_to\_AUTOUGH2(\emph{warn}=True, \emph{MP}=False, \emph{simulator}='AUTOUGH2.2',\ - \emph{eos}='EW')}} -\end{snugshade} -\label{sec:t2data:convert_to_AUTOUGH2} -\index{TOUGH2 data files!converting!to AUTOUGH2} -\index{TOUGH2 data files!simulator type} -\index{TOUGH2!AUTOUGH2} -\index{TOUGH2} - -Converts a TOUGH2 (or TOUGH2\_MP) data file for use with AUTOUGH2. Various parameter options are altered to try to make the AUTOUGH2 simulation give similar results to the original TOUGH2 simulation. If necessary, the \texttt{filename} property is changed to end in `.dat' (or `.DAT', depending on the case of the base file name), as required by AUTOUGH2. - -The simulator and EOS name can also be specified, as AUTOUGH2 data files contain this information in the SIMUL and MULTI sections. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{warn}: Boolean\\ - If \texttt{True}, warnings will be printed regarding TOUGH2 options used in the original data file which are not supported in AUTOUGH2. -\item \textbf{MP}: Boolean\\ - if \texttt{True}, treats the original \texttt{t2data} object as a TOUGH2\_MP data file, which uses some of the parameters differently (e.g. MOP(20)). -\item \textbf{simulator}: string\\ - Simulator name, used for the leading part of the AUTOUGH2 SIMUL data section. Possible values are `MULKOM', `TOUGH2', `TOUGH2.2', `AUTOUGH2' and `AUTOUGH2.2'. -\item \textbf{eos}: string\\ - EOS name, used for the trailing part of the AUTOUGH2 SIMUL data section (e.g. `EW', `EWC', `EWA', `EWAV' etc.) -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{convert\_to\_TOUGH2(\emph{warn}=True, \emph{MP}=False)}} -\end{snugshade} -\label{sec:t2data:convert_to_TOUGH2} -\index{TOUGH2 data files!converting!to TOUGH2} -\index{TOUGH2 data files!simulator type} -\index{TOUGH2!AUTOUGH2} -\index{TOUGH2} - -Converts an AUTOUGH2 data file for use with TOUGH2 (or compatible simulators such as TOUGH2\_MP). Various parameter options are altered to try to make the TOUGH2 simulation give similar results to the original AUTOUGH2 simulation. This particularly affects AUTOUGH2 options related to backward compatibility with MULKOM. In particular, if these are used then the heat conductivities in the ROCKS block have to be altered to give the same results. Data blocks specific to AUTOUGH2 (e.g. SIMULATOR, LINEQ, and SHORT) are removed, and AUTOUGH2-specific generator types are converted to their TOUGH2 equivalents if possible, or otherwise deleted. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{warn}: Boolean\\ - If \texttt{True}, warnings will be printed regarding AUTOUGH2 options used in the original data file which are not supported in TOUGH2. -\item \textbf{MP}: Boolean\\ - if \texttt{True}, converts to a TOUGH2\_MP data file, which treats some of the parameters differently (e.g. MOP(20)). The \texttt{filename} property is also changed to INFILE, as required by TOUGH2\_MP. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{clear\_generators()}} -\end{snugshade} -\label{sec:t2data:clear_generators} -\index{TOUGH2 data files!generators!deleting} -\index{TOUGH2 data files!deleting!generators} - -Deletes all generators from the data file object. - -\begin{snugshade} -\subsubsection{\texttt{delete\_generator(\emph{blocksourcenames})}} -\end{snugshade} -\label{sec:t2data:delete_generator} -\index{TOUGH2 data files!generators!deleting} -\index{TOUGH2 data files!deleting!generators} - -Deletes the generator with the specified block and generator (source) name, if it exists. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{blocksourcenames}: tuple\\ - Tuple of block name and generator name (both strings) of the generator to be deleted. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{delete\_orphan\_generators()}} -\end{snugshade} -\label{sec:t2data:delete_orphan_generators} -\index{TOUGH2 data files!generators!deleting} -\index{TOUGH2 data files!deleting!generators} - -Deletes all generators with block names that are not in the grid. - -\begin{snugshade} -\subsubsection{\texttt{effective\_incons(\emph{incons} = \texttt{None})}} -\end{snugshade} -\label{sec:t2data:effective_incons} -\index{TOUGH2 data files!initial conditions} - -Returns effective initial conditions, based on on the specified initial conditions in combination -with any initial conditions specified in the \texttt{t2data} object itself -- whether as -default initial conditions specified via the -\hyperref[sec:t2data:parameter]{\texttt{parameter}} property, -or via the \hyperref[sec:t2data:incon]{\texttt{incon}} property, or the -\hyperref[sec:t2data:indom]{\texttt{indom}} property (or any combination of these). - -Any \texttt{indom} specifications override the defaults in the \texttt{parameter} property. -Values in the \texttt{incon} property override both the defaults and values in \texttt{indom}. -Finally, values passed into this method via the \texttt{incons} parameter override any other -specifications. Note that any of these may contain incomplete specifications (i.e. values -are not specified for all blocks in the grid). - -If only default homogeneous initial conditions are in effect, then a list of the primary variables -is returned. Otherwise, a \hyperref[incons]{\texttt{t2incon}} object is returned with initial -conditions values for every block. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{incons}: \texttt{t2incon} or \texttt{None}\\ - Initial conditions object, usually representing the contents of a separate initial conditions file. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{generator\_index(\emph{blocksourcenames})}} -\end{snugshade} -\label{sec:t2data:generator_index} -\index{TOUGH2 data files!generators!indices of} - -Returns the index (in the \texttt{generatorlist} list) of the generator with the specified block and generator name. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{blocksourcenames}: tuple\\ - Tuple of block name and generator name (both strings) of the generator. -\end{itemize} - -\begin{snugshade} - \subsubsection{\texttt{json(\emph{geo}, \emph{mesh\_filename}, \emph{atmos\_volume} = 1.e25, - \emph{incons} = \texttt{None}, \emph{eos} = \texttt{None}, \\ - \emph{bdy\_incons} = \texttt{None}, \emph{mesh\_coords} = 'xyz')}} -\end{snugshade} -\label{sec:t2data:json} -\index{TOUGH2 data files!JSON} - -Returns a JSON dictionary representing the contents of the \texttt{t2data} object (and associated -mesh geometry), suitable for input to the Waiwera simulator (\url{http://waiwera.github.io}). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - Geometry object. Note that geometric meshes with column surface elevations that do not correspond to layer elevations are not supported in Waiwera. For meshes of this type, the column surface elevations can be ``snapped'' to layer elevations using the \hyperref[sec:mulgrid:snap_columns_to_nearest_layers]{\texttt{snap\_columns\_to\_nearest\_layers()}} method. In that case the \texttt{t2grid} in the \texttt{t2data} object must be updated so it corresponds to the snapped mesh geometry, and other parts of the data file updated to reference the new mesh (e.g. using the \hyperref[sec:t2data:transfer_from]{\texttt{transfer\_from()}} method). The geometry's \hyperref[sec:mulgrid:blockordering]{\texttt{block\_order}} property should be set to `dmplex', particularly if it contains mixtures of 3- and 4-sided columns. -\item \textbf{mesh\_filename}: string\\ - The filename of the mesh file (e.g. ExodusII or GMSH mesh) for the Waiwera simulation. -\item \textbf{atmos\_volume}: float\\ - Maximum block volume for blocks to be considered part of the geometric grid. Blocks with volume greater than this value (or zero) will be treated as boundary condition (e.g. atmosphere) blocks rather than part of the simulation mesh. -\item \textbf{incons}: \hyperref[incons]{\texttt{t2incon}}, string, or \texttt{None}\\ - Initial conditions for the Waiwera model. If specified as a string, this should be the filename of the Waiwera HDF5 output file for restarting the simulation from the output of a previous run. If \texttt{None} is specified, then default initial conditions will be applied from the \texttt{parameter} property (see \ref{sec:t2data:parameter}). -\item \textbf{eos}: string, integer or \texttt{None}\\ - Equation of state used for the simulation. For AUTOUGH2 simulations, this can generally be set to \texttt{None}, and the EOS will be read from the \texttt{t2data} \texttt{simulator} or \texttt{multi} properties. Otherwise, it can be specified as an integer corresponding to the EOS number (1 being pure water, 2 being water / CO$_2$ etc.) or as a string corresponding to the AUTOUGH2 EOS names (EOS1 being `EW', EOS2 being `EWC' etc.). Note that only EOS modules 1, 2 and 4 (i.e. `W', `EW', `EWC' and `EWAV' in terms of AUTOUGH2 EOS names) are supported. -\item \textbf{bdy\_incons}: \hyperref[incons]{\texttt{t2incon}}, or \texttt{None}\\ - TOUGH2 initial conditions from which boundary conditions are to be derived. In many cases this parameter is not needed, because boundary conditions are taken from the \texttt{incons} parameter: if the \texttt{incons} parameter is specified as a \texttt{t2incon} object, then the \texttt{bdy\_incons} parameter can be set to \texttt{None}. If, however, \texttt{incons} is a string or \texttt{None}, then it will not contain boundary condition data, in which case boundary conditions can be specified by passing a \texttt{t2incon} object as the \texttt{bdy\_incons} parameter; otherwise, if this is set to \texttt{None} then default boundary conditions will be applied from the default initial conditions in the \texttt{t2data} \texttt{parameter} property. Faces on which to apply boundary conditions are identified by the presence of connections to blocks with either zero or large volume (above the volume specified by the \texttt{atmos\_volume} parameter). Note that for side boundary conditions (with horizontal connections), the boundary blocks must have centres defined, otherwise it is not possible to calculate the appropriate normal vector for the boundary condition. -\item \textbf{mesh\_coords}: string\\ - String representing the coordinate system to be used in the Waiwera model. 3-D Cartesian meshes are identified as `xyz'. 2-D Cartesian meshes may be identified as either `xy', `xz', or `yz' (depending on orientation), while 2-D radial meshes are identified as `rz'. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{read(\emph{filename}, \emph{meshfilename}='')}} -\end{snugshade} -\label{sec:t2data:read} -\index{TOUGH2 data files!reading} - -Reads a \texttt{t2data} object from a TOUGH2 data file on disk. The mesh data may optionally be read from auxiliary files, if it is not present in the main data file. (Note that if the main data file does contain mesh information (the `ELEME' and `CONNE' sections), any auxiliary mesh files will not be read.) - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{filename}: string\\ - Name of the TOUGH2 data file to be read. -\item \textbf{meshfilename}: string or tuple\\ - Name of separate mesh file(s) to read, containing element and connection data. If empty, then mesh data will be read from the main data file. If a non-empty string is given, this is interpreted as the name of a formatted text file containing `ELEME' and `CONNE' data sections (as in the `MESH' files created by TOUGH2 and TOUGH2\_MP). If a tuple of two filenames is given, these are interpreted as the names of the two binary MESHA and MESHB files used by TOUGH2\_MP. -\end{itemize} - -Note that it is possible to create a \texttt{t2data} object and read its contents in from disk files in one step, e.g.: \texttt{dat = t2data(filename,meshfilename)}. - -\begin{snugshade} -\subsubsection{\texttt{rename\_blocks(\emph{blockmap}=\{\}, \emph{invert}=False, \emph{fix\_blocknames} = True)}} -\end{snugshade} -\label{sec:t2data:rename_blocks} -\index{TOUGH2 data files!renaming blocks} - -Renames blocks in the model according to the specified block mapping dictionary. Any block whose name is a key of the block mapping dictionary is renamed with the corresponding dictionary value. The blocks in the \hyperref[t2grids]{\texttt{t2grid}} object are renamed using its own \hyperref[sec:t2grid:rename_blocks]{\texttt{rename\_blocks()}} method. Other \texttt{t2data} properties such as generators, initial conditions and history specifications are similarly renamed. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{blockmap}: dictionary\\ - Block mapping dictionary, mapping strings to strings. -\item \textbf{invert}: Boolean\\ - Set \texttt{True} to invert the block mapping dictionary, i.e. to map its values to its keys. This can be used, for example, to rename the blocks to correspond to a geometry created using the \hyperref[t2grids]{\texttt{t2grid}} \hyperref[sec:t2grid:rectgeo]{\texttt{rectgeo()}} method, via the block mapping dictionary also created by that method. -\item \textbf{fix\_blocknames}: Boolean\\ - Set \texttt{True} (the default) to `fix' block names in the dictionary, using the \hyperref[sec:mulgrid:fix_blockname]{\texttt{fix\_blockname()}} function. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{run(\emph{save\_filename}='', \emph{incon\_filename}='', \emph{simulator}='AUTOUGH2\_2',\\ - \emph{silent}=False, \emph{output\_filename}='')}} -\end{snugshade} -\label{sec:t2data:run} -\index{TOUGH2 data files!running} - -Runs an AUTOUGH2 or TOUGH2 (but not TOUGH2\_MP) simulation using the data file corresponding to a \texttt{t2data} object. The contents of the \texttt{t2data} object must first have been written to disk using the \texttt{write} function. If the file names for the save file or initial conditions file are not specified, they are constructed by changing the file extension of the data file name. The name of the TOUGH2 executable can be specified. - -\index{TOUGH2} -For running TOUGH2 (rather than AUTOUGH2), the name of the TOUGH2 executable must be specified via the \texttt{simulator} parameter. However, the \texttt{save\_filename} and \texttt{incon\_filename} parameters do not need to be specified. Initial conditions will be read from the file INCON and final results written to SAVE. The listing file name will be the same as the data file name, but with the extension changed to *.listing, unless the \texttt{output\_filename} is specified. - -\index{TOUGH2!TOUGH2-MP} -Running TOUGH2\_MP is generally done via MPI rather than directly, and the exact syntax for doing so may vary with different implementations of MPI (OpenMPI, MPICH2 etc.) It is also necessary to specify the number of processors to use. However it is still possible to run TOUGH2\_MP from a Python script using a system call, e.g.: - -\begin{lstlisting} -from os import system -system("mpirun -np 16 t2eos1_mp") -\end{lstlisting} - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{save\_filename}: string\\ - Name of the save file to be written to disk during the simulation (AUTOUGH2 only). Default is `base.save' where the AUTOUGH2 data file name is `base.dat'. -\item \textbf{incon\_filename}: string\\ - Name of the initial conditions file for the simulation (AUTOUGH2 only). Default is `base.incon' where the AUTOUGH2 data file name is `base.dat'. -\item \textbf{simulator}: string\\ - Name of the AUTOUGH2 or TOUGH2 executable. Default is `AUTOUGH2\_2'. -\item \textbf{silent}: Boolean\\ - Set to \texttt{True} to suppress output to the display while running (default is \texttt{False}). -\item \textbf{output\_filename}: string\\ - Name of the output listing file for the simulation (TOUGH2 only). Default is `base.listing' where the base name of the TOUGH2 data file (without file extension) is `base'. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{specific\_generation(\emph{type}='MASS', \emph{name}='')}} -\end{snugshade} -\label{sec:t2data:specific_generation} -\index{TOUGH2 data files!generators!specific generation rates} - -Returns an \texttt{np.array} containing the total specific generation rate in each block (i.e. generation rate per unit volume) for the specified generator type and name. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{type}: string\\ - Generation type (`HEAT', `MASS' etc.) -- default is `MASS'. -\item \textbf{name}: string\\ - Regular expression to match generator names (e.g. `SP...' (or `\^{}SP') will match all generators with names beginning with `SP'.) -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{transfer\_from(\emph{source}, \emph{sourcegeo}, \emph{geo}, \emph{top\_generator}=[], \emph{bottom\_generator}=[],\\ - \emph{sourceinconfilename}='', \emph{inconfilename}='', \emph{rename\_generators}=False, \\ - \emph{preserve\_generation\_totals}=False)}} -\end{snugshade} -\label{sec:t2data:transfer_from} -\index{TOUGH2 data files!transferring} - -Transfers data from another \texttt{t2data} object, and its associated \texttt{mulgrid} object. Parameters, rock types and rock type assignments, and optionally initial conditions files are transferred. In general the data for a given block in the geometry is found by identifying the nearest block in the source geometry and transferring data from that block. There are, however, exceptions, such as for generators that need to remain on the surface or bottom of the model. The \texttt{top\_generator} and \texttt{bottom\_generator} lists specify the `layer' part of the generator name for generators that should remain on the top or bottom of the model, respectively. - -For generator types in which the \texttt{gx} and \texttt{rate} properties represent generation rates (as opposed to other types for which these properties are used to represent other things, e.g. productivity index for wells on deliverability), the values of \texttt{gx} and \texttt{rate} are scaled to account for the different volume of the block the generator has been mapped into. If \texttt{preserve\_generation\_totals} is \texttt{True}, and a generator with generation rate $G$ is mapped into $n$ blocks with volumes $V_1, V_2,\ldots, V_n$, then the generation rate for the new generator in block $i$ will be $G V_i/\sum_{k=1}^{n}{V_k}$. This should preserve the total generation rate over the model. (For generator types matching the \texttt{bottom\_generator} or \texttt{top\_generator} specifications, the column area instead of the block volume is used to determine the appropriate scaling.) Note that of the columns a top or bottom generator is mapped into, only those with centres inside the source geometry are included in the scaling calculations. The generator types for which this scaling is carried out are: ` AIR', `COM1', `COM2', `COM3', `COM4', `COM5', `HEAT', `MASS', `NACL', `TRAC' and ` VOL'. - -If both \texttt{sourceinconfilename} and \texttt{inconfilename} are specified, a new initial conditions file with filename \texttt{inconfilename} is written to disk, with initial conditions transferred from the file \texttt{sourceinconfilename}. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{source}: \hyperref[datafiles]{\texttt{t2data}}\\ - The \texttt{t2data} object to transfer data from. -\item \textbf{sourcegeo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - The \texttt{mulgrid} object corresponding to \texttt{source}. -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - The \texttt{mulgrid} object corresponding to the destination \texttt{t2data} object. -\item \textbf{top\_generator}: list\\ - A list of generator `layer' identifier strings for generators that need to be kept at the top of the model (e.g. rain generators). -\item \textbf{bottom\_generator}: list\\ - A list of generator `layer' identifier strings for generators that need to be kept at the bottom of the model (e.g. basement heat and mass inputs). -\item \textbf{sourceinconfilename}: string\\ - Name of the (optional) initial conditions file to transfer initial conditions data from (corresponding to \texttt{source}). -\item \textbf{inconfilename}: string\\ - Name of the (optional) initial conditions file to write, corresponding to the destination \texttt{t2data} object. -\item \textbf{rename\_generators}: Boolean\\ - If \texttt{False}, generators other than those at the top and bottom of the model retain their original names. Otherwise, they will be renamed according to their column names in the new grid. -\item \textbf{preserve\_generation\_totals}: Boolean\\ - If \texttt{False} (the default), the transfer of generators will attempt to preserve the distribution of specific generation of the original model; otherwise, it will attempt to preserve the total generation over the model. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{total\_generation(\emph{type}='MASS', \emph{name}='')}} -\end{snugshade} -\label{sec:t2data:total_generation} -\index{TOUGH2 data files!generators!total generation rates} - -Returns an \texttt{np.array} containing the total generation rate in each block for the specified generator type and name. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{type}: string\\ - Generation type (`HEAT', `MASS' etc.) -- default is `MASS'. -\item \textbf{name}: string\\ - Regular expression to match generator names (e.g. `SP...' (or `\^{}SP') will match all generators with names beginning with `SP'.) -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{write(\emph{filename}='', \emph{meshfilename}='', \emph{extra\_precision}=None,\\ - \emph{echo\_extra\_precision}=None)}} -\end{snugshade} -\label{sec:t2data:write} -\index{TOUGH2 data files!writing} -\index{TOUGH2 data files!mesh file} -\index{TOUGH2 data files!extra precision} - -Writes a \texttt{t2data} object to a TOUGH2 data file on disk. If the \texttt{meshfilename} parameter is used, mesh information can be written to auxiliary mesh files. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{filename}: string\\ - Name of the TOUGH2 data file to be written. If no file name is specified, the object's own \texttt{filename} property is used. -\item \textbf{meshfilename}: string or tuple\\ - Name of auxiliary mesh file(s) to be written. If this is empty (the default), the object's own \texttt{meshfilename} property is used. Otherwise, if a single (non-empty) string is given, this in interpreted as the name of a file to write formatted mesh information to (as in the `MESH' files produced by TOUGH2 and TOUGH2\_MP). If a tuple of two strings is given, this in interpreted as the names of two binary files (as in the `MESHA' and `MESHB' files produced by TOUGH2\_MP). -\item \textbf{extra\_precision}: list or Boolean\\ - Controls whether to write extra precision data to auxiliary file (AUTOUGH2 only). If set to \texttt{True}, then all possible sections will be written to the extra precision file. Currently the possible extra-precision sections are the ROCKS, ELEME, CONNE, RPCAP and GENER sections. If set to \texttt{False} or [], then no extra-precision data will be written. If set to a list of section names (e.g. [`RPCAP', `GENER']), then only those sections will be written in extra precision. If set to \texttt{None} (the default), then the value of the data object's \texttt{extra\_precision} property is used. Otherwise, the value of this property is overwritten by the value specified here. -\item \textbf{echo\_extra\_precision}: Boolean or None\\ - Controls whether to echo all extra-precision data sections to the main data file (AUTOUGH2 only). If \texttt{None}, the value of the data object's \texttt{echo\_extra\_precision} property is used. Otherwise, the value of this property is overwritten by the value specified here. -\end{itemize} - -\section{\texttt{t2generator} objects} -\label{t2generatorobjects} -\index{PyTOUGH!classes!\texttt{t2generator}} -\index{TOUGH2 data files!generators} - -A \texttt{t2generator} object represents a generator in a TOUGH2 simulation (i.e. an item in the generation table). The properties of a \texttt{t2generator} object are given in Table \ref{tb:t2generator_properties}. These correspond closely to the parameters specified in the TOUGH2 \textbf{GENER} input block. A \texttt{t2generator} object has no methods. - -\index{TOUGH2 data files!generators!properties} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|p{60mm}|p{20mm}|} - \hline - \textbf{Property} & \textbf{Type} & \textbf{Description} & \textbf{TOUGH2 parameter}\\ - \hline - \texttt{block} & string & name of block containing the generator & EL, NE\\ - \texttt{enthalpy} & list of float & generation enthalpies ($|$ltab$|>$1, itab$<>$`')& F3\\ - \texttt{ex} & float & enthalpy for injection & EX\\ - \texttt{gx} & float & generation rate (or productivity index for deliverability) & GX\\ - \texttt{hg} & float & layer thickness for deliverability & HG\\ - \texttt{fg} & float & separator pressure/ injectivity etc. & FG\\ - \texttt{itab} & string & blank unless table of specific enthalpies specified & ITAB\\ - \texttt{ltab} & integer & number of generation times (or open layers for deliverability) & LTAB\\ - \texttt{nadd} & integer & successive block increment & NADD\\ - \texttt{nads} & integer & successive generator increment & NADS\\ - \texttt{name} & string & generator name & SL, NS\\ - \texttt{nseq} & integer & number of additional generators & NSEQ\\ - \texttt{rate} & list of float & generation rates ($|$ltab$|>$1)& F2\\ - \texttt{time} & list of float & generation times ($|$ltab$|>$1)& F1\\ - \texttt{type} & string & generator type (default `MASS') & TYPE\\ - \hline - \end{tabular} - \caption{Properties of a \texttt{t2generator} object} - \label{tb:t2generator_properties} - \end{center} -\end{table} - -\section{Example} -\index{examples!TOUGH2 data files} - -The following piece of Python script opens a MULgraph geometry file and TOUGH2 data file, changes some TOUGH2 run-time parameters and assigns heat generators to the blocks in the bottom layer inside a defined area, with the specified total heat divided uniformly amongst the generators. - -\begin{lstlisting} -geo = mulgrid('gmodel.dat') -dat = t2data('model.dat') - -dat.parameter['max_timesteps'] = 300 -dat.parameter['print_interval'] = dat.parameter['max_timesteps']/10 -dat.parameter['option'][16] = 5 # time step control - -dat.clear_generators() -totalheat = 10.e6 -layer = geo.layerlist[-1] # bottom layer -cols = [col for col in geo.columnlist if 10.e3 <= col.centre[0] <= 20.e3] -totalarea = sum([col.area for col in cols]) -q = totalheat / totalarea - -for col in cols: - blockname = geo.block_name(layer.name, col.name) - gen = t2generator(name = ' q'+col.name, block = blockname, type = 'HEAT', gx = q*col.area) - dat.add_generator(gen) - -dat.write() - -\end{lstlisting} diff --git a/doc/t2grids.tex b/doc/t2grids.tex deleted file mode 100755 index d44ce908..00000000 --- a/doc/t2grids.tex +++ /dev/null @@ -1,794 +0,0 @@ -\chapter{TOUGH2 grids} -\label{t2grids} -\index{TOUGH2 grids} - -\section{Introduction} -The \texttt{t2grids} library in PyTOUGH contains classes and routines for manipulating TOUGH2 grids. It can be imported using the command: - -\begin{lstlisting} - from t2grids import * -\end{lstlisting} - -\section{\texttt{t2grid} objects} -\index{PyTOUGH!classes!\texttt{t2grid}} -\index{TOUGH2 grids!objects} - -The \texttt{t2grids} library defines a \texttt{t2grid} class, used for representing TOUGH2 grids. This gives access via Python to the grid's rock types, blocks, connections and other parameters. - -Normally a TOUGH2 grid is not created directly, but is either read from a TOUGH2 data file, or constructed from a \hyperref[mulgrids]{\texttt{mulgrid}} geometry object (see chapter \ref{mulgrids}) using the \hyperref[sec:t2grid:fromgeo]{\texttt{fromgeo()}} method. - -Printing a \texttt{t2grid} object (e.g. \texttt{print grid}) displays a summary of information about the grid: how many rock types, blocks and connections it contains. - -\subsection{Properties} - -The main properties of a \texttt{t2grid} object are listed in Table \ref{tb:t2grid_properties}. Essentially a \texttt{t2grid} object contains collections of blocks, rock types and connections, each accessible either by name or by index. For example, block `AB 20' in a \texttt{t2grid} called \texttt{grid} is given by \texttt{grid.block['AB 20']}. - -Connections are slightly different from blocks or rock types, in that they are not named individually. However, they can be accessed by the names of the blocks connected by the connection. For example, the connection between blocks `aa 10' and `ab 10' in a \texttt{t2grid} called \texttt{grid} is given by \texttt{grid.connection['aa 10','ab 10']}. - -The \texttt{rocktype\_frequencies} property gives information about how frequently each rock type is used (i.e. how many blocks use that rock type). It returns a list of tuples, the first element of each tuple being the frequency of use, and the second element being a list of rock type names with that frequency. The list is given in order of increasing frequency. - -The \texttt{rocktype\_indices} property gives an \texttt{np.array} containing the index of the rocktype for each block in the grid. This can be used to give a plot of rock types, in conjunction with the \texttt{mulgrid} methods \texttt{layer\_plot} or \texttt{slice\_plot}. - -\index{TOUGH2 grids!properties} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|} - \hline - \textbf{Property} & \textbf{Type} & \textbf{Description}\\ - \hline - \texttt{atmosphere\_blocks} & list & atmosphere blocks\\ - \texttt{blocklist} & list & blocks (by index)\\ - \texttt{block} & dictionary & blocks (by name)\\ - \texttt{block\_centres\_defined} & Boolean & whether block centres have been calculated\\ - \texttt{connectionlist} & list & connections (by index)\\ - \texttt{connection} & dictionary & connections (by tuples of block names)\\ - \texttt{num\_atmosphere\_blocks} & integer & number of atmosphere blocks\\ - \texttt{num\_blocks} & integer & number of blocks\\ - \texttt{num\_connections} & integer & number of connections\\ - \texttt{num\_rocktypes} & integer & number of rock types\\ - \texttt{num\_underground\_blocks} & integer & number of non-atmosphere blocks\\ - \texttt{rocktypelist} & list & rock types (by index)\\ - \texttt{rocktype} & dictionary & rock types (by name)\\ - \texttt{rocktype\_frequencies} & list of tuples & frequencies of rock types\\ - \texttt{rocktype\_indices} & \texttt{np.array} & index of rock type for each block\\ - \hline - \end{tabular} - \caption{Properties of a \texttt{t2grid} object} - \label{tb:t2grid_properties} - \end{center} -\end{table} - -\subsection{Methods} -\label{t2gridmethods} - -The main methods of a \texttt{t2grid} object are listed in Table \ref{tb:t2grid_methods}. Details of these methods are given below. - -\index{TOUGH2 grids!methods} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|p{65mm}|} - \hline - \textbf{Method} & \textbf{Type} & \textbf{Description}\\ - \hline - \hyperref[sec:t2grid:plus]{\texttt{+}} & \hyperref[t2grids]{\texttt{t2grid}} & adds two grids together\\ - \hyperref[sec:t2grid:add_block]{\texttt{add\_block}} & -- & adds a block to the grid\\ - \hyperref[sec:t2grid:add_connection]{\texttt{add\_connection}} & -- & adds a connection to the grid\\ - \hyperref[sec:t2grid:add_rocktype]{\texttt{add\_rocktype}} & -- & adds a rock type to the grid\\ - \hyperref[sec:t2grid:blockmap]{\texttt{blockmap}} & dictionary & returns block name mapping from a geometry\\ - \hyperref[sec:t2grid:block_index]{\texttt{block\_index}} & integer & returns index of a block with a specified name\\ - \hyperref[sec:t2grid:calculate_block_centres]{\texttt{calculate\_block\_centres}} & -- & calculates geometrical centre of all blocks in the grid\\ - \hyperref[sec:t2grid:check]{\texttt{check}} & Boolean & checks grid for errors and optionally fixes them\\ - \hyperref[sec:t2grid:clean_rocktypes]{\texttt{clean\_rocktypes}} & -- & deletes any unused rock types from the grid\\ - \hyperref[sec:t2grid:connection_index]{\texttt{connection\_index}} & integer & returns index of a connection with a specified pair of names\\ - \hyperref[sec:t2grid:copy_connection_directions]{\texttt{copy\_connection\_directions}} & -- & copies connection permeability directions from another grid\\ - \hyperref[sec:t2grid:delete_block]{\texttt{delete\_block}} & -- & deletes a block from the grid\\ - \hyperref[sec:t2grid:delete_connection]{\texttt{delete\_connection}} & -- & deletes a connection from the grid\\ - \hyperref[sec:t2grid:delete_rocktype]{\texttt{delete\_rocktype}} & -- & deletes a rock type from the grid\\ - \hyperref[sec:t2grid:demote_block]{\texttt{demote\_block}} & -- & shifts a block (or blocks) to the end of the blocklist\\ - \hyperref[sec:t2grid:embed]{\texttt{embed}} & \hyperref[t2grids]{\texttt{t2grid}} & embeds a subgrid inside one block of another \\ - \hyperref[sec:t2grid:empty]{\texttt{empty}} & -- & empties contents of grid\\ - \hyperref[sec:t2grid:flux_matrix]{\texttt{flux\_matrix}} & \texttt{scipy.sparse.lil\_matrix} & constructs a sparse matrix for calculating block-average flows \\ - \hyperref[sec:t2grid:fromgeo]{\texttt{fromgeo}} & \hyperref[t2grids]{\texttt{t2grid}} & constructs a TOUGH2 grid from a \texttt{mulgrid} object\\ - \hyperref[sec:t2grid:incons]{\texttt{incons}} & \hyperref[incons]{\texttt{t2incon}} & constructs initial conditions for the grid\\ - \hyperref[sec:t2grid:MINC]{\texttt{minc}} & list & creates MINC blocks and connections\\ - \hyperref[sec:t2grid:radial]{\texttt{radial}} & \hyperref[t2grids]{\texttt{t2grid}} & constructs a radial TOUGH2 grid\\ - \hyperref[sec:t2grid:rectgeo]{\texttt{rectgeo}} & (\hyperref[mulgrids]{\texttt{mulgrid}}, \texttt{dict}) & constructs a \texttt{mulgrid} object from a rectangular TOUGH2 grid\\ - \hyperref[sec:t2grid:rename_blocks]{\texttt{rename\_blocks}} & -- & renames blocks the grid\\ - \hyperref[sec:t2grid:rename_rocktype]{\texttt{rename\_rocktype}} & -- & renames a rock type in the grid\\ - \hyperref[sec:t2grid:reorder]{\texttt{reorder}} & -- & reorders blocks and connections in the grid\\ - \hyperref[sec:t2grid:rocktype_frequency]{\texttt{rocktype\_frequency}} & integer & frequency of use of a particular rock type\\ - \hyperref[sec:t2grid:sort_rocktypes]{\texttt{sort\_rocktypes}} & -- & sorts rock type list into alphabetical order by name\\ - \hyperref[sec:t2grid:write_vtk]{\texttt{write\_vtk}} & -- & writes grid to VTK file\\ - \hline - \end{tabular} - \caption{Methods of a \texttt{t2grid} object} - \label{tb:t2grid_methods} - \end{center} -\end{table} - -\begin{snugshade} -\subsubsection{\texttt{+}} -\end{snugshade} -\label{sec:t2grid:plus} -\index{TOUGH2 grids!adding together} - -Adds two grids \texttt{a} and \texttt{b} together (i.e. amalgamates them) to form a new grid \texttt{a+b}. If any rock types, blocks or connections exist in both grids \texttt{a} and \texttt{b}, the value from \texttt{b} is used, so there are no duplicates. (Technically this is really an `operator' rather than a method.) - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{a, b}: \hyperref[t2grids]{\texttt{t2grid}}\\ - The two grids to be added together. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{add\_block(\emph{block})}} -\end{snugshade} -\label{sec:t2grid:add_block} -\index{TOUGH2 grids!adding!blocks} -\index{TOUGH2 grids!blocks!adding} - -Adds a block to the grid. If another block with the same name already exists, it is replaced. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{block}: \hyperref[t2blockobjects]{\texttt{t2block}}\\ - Block to be added to the grid. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{add\_connection(\emph{connection})}} -\end{snugshade} -\label{sec:t2grid:add_connection} -\index{TOUGH2 grids!adding!connections} -\index{TOUGH2 grids!connections!adding} - -Adds a connection to the grid. If another connection with the same column names already exists, it is replaced. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{connection}: \hyperref[t2connectionobjects]{\texttt{t2connection}}\\ - Connection to be added to the grid. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{add\_rocktype(\emph{rock})}} -\end{snugshade} -\label{sec:t2grid:add_rocktype} -\index{TOUGH2 grids!adding!rocktypes} -\index{TOUGH2 grids!rocktypes!adding} - -Adds a rock type to the grid. If another rock type with the same name already exists, it is replaced. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{rock}: \hyperref[rocktypeobjects]{\texttt{rocktype}}\\ - Rock type to be added to the grid. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{block\_index(\emph{blockname})}} -\end{snugshade} -\label{sec:t2grid:block_index} -\index{TOUGH2 grids!blocks!indices} -\index{TOUGH2 grids!indices!of blocks} - -Returns the block index (in the \texttt{blocklist} list) of a specified block name. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{blockname}: string\\ - Name of the block. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{blockmap(\emph{geo}, \emph{index} = None)}} -\end{snugshade} -\label{sec:t2grid:blockmap} -\index{TOUGH2 grids!blocks!mappings} - -Returns a mapping from the block name list of the specified geometry object to the block names in the grid. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - Geometry object. -\item \textbf{index}: list (or \texttt{None})\\ - Specifies a list of integer indices defining which blocks in the grid to map to. If \texttt{None}, all blocks are mapped to. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{calculate\_block\_centres(\emph{geo})}} -\end{snugshade} -\label{sec:t2grid:calculate_block_centres} -\index{TOUGH2 grids!blocks!centres} -\index{TOUGH2 grids!centres!blocks} - -Calculates geometrical centres of all blocks in the grid, based on the specified geometry object \texttt{geo}. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - Geometry object associated with the grid. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{check(\emph{fix}=False,\emph{silent}=False)}} -\end{snugshade} -\label{sec:t2grid:check} -\index{TOUGH2 grids!checking} -\index{checking!TOUGH2 grids} - -Checks a grid for errors and optionally fixes them. Errors checked for are: blocks not connected to any other blocks, and blocks with isolated rocktypes (not shared with any neighbouring blocks). Returns \texttt{True} if no errors were found, and \texttt{False} otherwise. If \texttt{fix} is \texttt{True}, any identified problems will be fixed. If \texttt{silent} is \texttt{True}, there is no printout (only really useful if \texttt{fix} is \texttt{True}). - -Blocks not connected to any others are fixed by deleting them. Isolated-rocktype blocks are fixed by assigning them the most popular rocktype of their neighbours. Blocks with large volumes ($> 10^{20}$ m$^3$) are never considered isolated (because they often have a special rocktype, such as an atmosphere one, that their neighbours will never share). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{fix}: Boolean\\ - Whether to fix any problems identified. -\item \textbf{silent}: Boolean\\ - Whether to print out feedback or not. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{clean\_rocktypes}()} -\end{snugshade} -\label{sec:t2grid:clean_rocktypes} -\index{TOUGH2 grids!rocktypes!cleaning} - -Deletes any rock types from the grid which are not assigned to any block. - -\begin{snugshade} -\subsubsection{\texttt{connection\_index(\emph{blocknames})}} -\end{snugshade} -\label{sec:t2grid:connection_index} -\index{TOUGH2 grids!connections!indices} -\index{TOUGH2 grids!indices!of connections} - -Returns the connection index (in the \texttt{connectionlist} list) of the connection between a specified pair of block names. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{blocknames}: tuple\\ - A pair of block names, each of type string. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{copy\_connection\_directions(\emph{geo},\emph{grid})}} -\end{snugshade} -\label{sec:t2grid:copy_connection_directions} -\index{TOUGH2 grids!connections!directions} - -Copies the connection permeability directions for horizontal connections from another grid. It is assumed that both grids have the same column structure, but may have different layer structures. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - Geometry object associated with the source grid. -\item \textbf{grid}: \hyperref[t2grids]{\texttt{t2grid}}\\ - The source grid from which the connection permeability directions are to be copied. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{delete\_block(\emph{blockname})}} -\end{snugshade} -\label{sec:t2grid:delete_block} -\index{TOUGH2 grids!blocks!deleting} -\index{TOUGH2 grids!deleting!blocks} - -Deletes a block from the grid. This also deletes any connections involving the specified block. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{blockname}: string\\ - Name of the block to be deleted from the grid. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{delete\_connection(\emph{connectionname})}} -\end{snugshade} -\label{sec:t2grid:delete_connection} -\index{TOUGH2 grids!connections!deleting} -\index{TOUGH2 grids!deleting!connections} - -Deletes a connection from the grid. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{connectionname}: tuple (of string)\\ - Pair of block names identifying the connection to be deleted from the grid. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{delete\_rocktype(\emph{rocktypename})}} -\end{snugshade} -\label{sec:t2grid:delete_rocktype} -\index{TOUGH2 grids!rocktypes!deleting} -\index{TOUGH2 grids!deleting!rocktypes} - -Deletes a rock type from the grid. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{rocktypename}: string\\ - Name of the rock type to be deleted from the grid. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{demote\_block(\emph{blockname})}} -\end{snugshade} -\label{sec:t2grid:demote_block} -\index{TOUGH2 grids!blocks!demoting} - -Shifts a block (or blocks) to the end of the blocklist. This can be useful for making blocks inactive - by setting their volumes to zero or negative, and then shifting them to the end of the list (to avoid all blocks below them also being treated as inactive). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{blockname}: string or list of strings\\ - Name(s) of the block(s) to be shifted to the end of the blocklist. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{embed(\emph{subgrid}, \emph{connection})}} -\end{snugshade} -\label{sec:t2grid:embed} -\index{TOUGH2 grids!embedding} - -Returns a grid with a subgrid embedded inside one of its blocks. The connection specifies how the two grids are to be connected: the blocks to be connected and the connection distances, area etc. between them. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{subgrid}: \hyperref[t2grids]{\texttt{t2grid}}\\ - Subgrid to be embedded. -\item \textbf{connection}: \hyperref[t2connectionobjects]{\texttt{t2connection}}\\ - Connection specifying how the subgrid is to be embedded, including the connection distances and area. The first block should be the host block, the second the connecting block in the subgrid. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{empty}()} -\end{snugshade} -\label{sec:t2grid:empty} -\index{TOUGH2 grids!emptying} - -Empties the grid of all its blocks, rock types and connections. - -\begin{snugshade} -\subsubsection{\texttt{flux\_matrix(\emph{geo}, \emph{blockmap} = \{\})}} -\end{snugshade} -\label{sec:t2grid:flux_matrix} -\index{TOUGH2 grids!flux matrices} - -Takes the grid and a corresponding \hyperref[mulgrids]{\texttt{mulgrid}} object, and constructs a sparse matrix (of type \texttt{scipy.sparse.lil\_matrix}) which can be used to convert connection flow values on the grid to block-average fluxes (flows per unit area). Specifically, if an array of connection flow values (one for each connection in the grid) is multiplied by this sparse matrix, the result is a partitioned array containing the 3-component block-average flux for each of the (non-atmosphere) blocks. - -The method for constructing the matrix is as follows. For each block, a distribution of flux is fitted to agree as closely as possible with the connection flow values. This distribution is either constant or linear, depending on how many connections the block has (linear for blocks with at least 6 connections). Fitting the connection values results in a small linear system to solve, which may be under- or over-determined, depending on the number of connections and the type of flux distribution. A pseudo-inverse matrix is calculated which will find the least-squares solution of this system. The total matrix is formed by assembling these matrices for each of the blocks into a global matrix. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - The \texttt{mulgrid} geometry object (see chapter \ref{mulgrids}). -\item \textbf{blockmap}: dictionary\\ - Dictionary mapping the block names in the geometry to the block naming system used in the grid. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{fromgeo(\emph{geo})}} -\end{snugshade} -\label{sec:t2grid:fromgeo} -\index{TOUGH2 grids!creating!from MULgraph geometry} - -Returns a grid constructed from a \texttt{mulgrid} geometry object. (Any previous contents of the grid are first emptied.) - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - The \texttt{mulgrid} geometry object (see chapter \ref{mulgrids}). -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{incons(\emph{values}=(101.3e3,20.))}} -\end{snugshade} -\label{sec:t2grid:incons} -\index{TOUGH2 grids!creating!initial conditions for} -\index{TOUGH2 grids!initial conditions} -\index{TOUGH2 initial conditions!creating} - -Returns a \hyperref[incons]{\texttt{t2incon}} set of initial conditions for the grid, using the supplied values. Initial conditions can be specified for only one block, in which case they will be applied to all blocks, or for each block, in an array. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{values}: \texttt{tuple} or \texttt{np.array}\\ - Initial conditions values, either a \texttt{tuple} of values for one block, or an \texttt{np.array} with each row containing a set of values for one block. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{minc(\emph{volume\_fractions}, \emph{spacing}=50., \emph{num\_fracture\_planes}=1,\\ - \emph{blocks}=None, \emph{matrix\_blockname}=None, \emph{minc\_rockname}=None,\\ - \emph{proximity}=None, \emph{atmos\_volume}=1.e25, \emph{incon}=None,\\ - \emph{fracture\_connection\_distance}=0.)}} -\end{snugshade} -\label{sec:t2grid:MINC} -\index{TOUGH2 grids!creating!MINC} -\index{MINC!generating} - -Creates ``Multiple Interacting Continua'' (MINC) blocks and connections in the grid, for simulating fracture flow with matrix blocks attached to each fracture block. This has capability similar to that of the GMINC program \citep{GMINC}, or of the MINC part of TOUGH2's \hyperref[sec:t2data:meshmaker]{MESHMAKER} section (except that matrix-matrix flow is not supported). - -This function returns a rank-2 integer \texttt{np.array} with one row for each MINC level, containing the indices of the blocks for that level. For example, the first row is a list of all fracture block indices, the second is a list of all MINC level 1 block indices, etc. This can be useful for identifying all blocks in a given MINC level, for plotting or other post-processing. - -For example, if the output index array from this method is \texttt{minc\_level}, and \texttt{T} is an array of temperatures computed over the entire MINC grid (e.g. extracted from the element table of a listing file), then the temperatures in MINC level \texttt{m} are given by: - -\begin{lstlisting} -T[minc_level[m]] -\end{lstlisting} - -Note that plotting MINC results over a \hyperref[mulgrids]{\texttt{mulgrid}} geometry can be made easier (particularly for grids that have MINC applied over only part of the domain) by using the \hyperref[sec:mulgrid:minc_array]{\texttt{minc\_array()}} method to create the solution vector to plot. - -If the \texttt{incon} parameter is specified as a \hyperref[incons]{\texttt{t2incon}} object (from the original grid), then this method will also return a new \texttt{t2incon} object for the MINC grid, with values copied from the original. - -Fracture blocks retain the same block name as their original porous medium blocks. The naming of matrix blocks can be controlled using the \texttt{matrix\_blockname} parameter. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{volume\_fractions}: list (or \texttt{np.array})\\ - List or array of volume fractions. The first entry corresponds to the fractures, with subsequent entries specifying the volume fractions for each MINC level. The length of this list or array is therefore equal to one plus the number of matrix blocks to be used. Entries for all MINC levels must be present, but they need not sum to 1- if they do not, they will be scaled so that the sum is 1. (This means, for example, that entries may be specified as percentage values.) -\item \textbf{spacing}: float or list (or \texttt{np.array})\\ - Fracture spacing parameters. If a float value is specified, this is applied to all sets of fracture planes (see below). If a list or array is specified, each entry is applied to its corresponding set of fracture planes. -\item \textbf{num\_fracture\_planes}: integer\\ - Number of sets of fracture planes (1, 2 or 3). -\item \textbf{blocks}: list (or \texttt{None})\\ - List of blocks or block names, specifying which blocks are to have MINC applied. If this parameter is \texttt{None}, all blocks are processed (except inactive blocks). -\item \textbf{matrix\_blockname}: function (or \texttt{None})\\ - Function returning the name of a MINC matrix block (string), given the original block name (string) and MINC level (integer > 0). If \texttt{None}, a default function will be used, which simply replaces the first character of the original block name with the MINC level. -\item \textbf{minc\_rockname}: function (or \texttt{None})\\ - Function returning the MINC rocktype name, given the original rocktype name and MINC level ($\geq 0$). If \texttt{None}, a default function will be used, which leaves fracture blocks with their original rocktype (the properties of which can subsequently be edited), and for matrix blocks, simply replaces the first character of the original rocktype name with `X'. -\item \textbf{proximity}: function (or \texttt{None})\\ - Proximity function, returning the total matrix volume within a given distance (float) from the fracture faces. If \texttt{None}, a default function will be used, corresponding to the \texttt{num\_fracture\_planes} parameter. -\item \textbf{atmos\_volume}: float\\ - Maximum block volume for blocks to be considered part of the geometrical grid. Blocks with volume greater than this will be assumed to be boundary condition blocks and no MINC processing will be applied to them. -\item \textbf{incon}: \hyperref[incons]{\texttt{t2incon}} (or \texttt{None})\\ - Initial conditions object for the original grid, before MINC processing. If not \texttt{None}, then the method returns (as well as the block index array) a new \texttt{t2incon} object for the MINC grid, with values for each block copied from the original (for all MINC levels). -\item \textbf{fracture\_connection\_distance}: float\\ - Connection distance between fracture and matrix blocks. Default is zero, as in MESHMAKER, but in some situations a finite value (e.g. $10^{-10}$ m) can work better. -\end{itemize} - - -\begin{snugshade} -\subsubsection{\texttt{radial(\emph{rblocks}, \emph{zblocks}, \emph{convention}=0, \emph{atmos\_type}=2, \emph{origin}=[0,0],\\ - \emph{justify}='r', \emph{case}=None, \emph{dimension}=2, \emph{blockmap}=\{\}, \emph{chars}=ascii\_lowercase, \emph{spaces}=\texttt{True})}} -\end{snugshade} -\label{sec:t2grid:radial} -\index{TOUGH2 grids!creating!radial} - -Returns a radial TOUGH2 grid with the specified radial and vertical block sizes. Grid column and layer naming convention, atmosphere type and origin can be specified. The optional \texttt{justify} and \texttt{case} parameters control the formatting of the character part of the block names. - -The \texttt{dimension} parameter sets the flow dimension for `generalized radial flow', which can represent flow in fractured rocks and modifies the block volumes and areas (see \cite{barker_1988}). The default \texttt{dimension} = 2 corresponds to standard radial flow. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{rblocks}, \textbf{zblocks}: list (or \texttt{np.array})\\ - Lists (or arrays) of block sizes in the \emph{r} and \emph{z} directions. -\item \textbf{convention}: integer\\ - Naming convention for grid columns and layers - same as the \hyperref[geometry_format_conventions]{naming convention} for a \hyperref[mulgrids]{\texttt{mulgrid}} object. -\item \textbf{atmos\_type}: integer\\ - Type of atmosphere - also the same as the \hyperref[geometry_format_conventions]{atmosphere type} for a \hyperref[mulgrids]{\texttt{mulgrid}} object. -\item \textbf{origin}: list (or \texttt{np.array})\\ - Origin of the grid (of length 2 or 3). The first entry is the radial origin, i.e. the starting radius of the grid. The last entry is the vertical origin, i.e. the vertical position of the top of the grid. If of length 3, the middle entry is ignored. -\item \textbf{justify}: string\\ - Specify `r' for the character part of the block names (first three characters) to be right-justified, `l' for left-justified. -\item \textbf{case}: string\\ - Specify `l' for the character part of the block names (first three characters) to be lower case, `u' for upper case. Alternatively, use the more flexible \texttt{chars} parameter (see below). -\item \textbf{dimension}: float\\ - Dimension for `generalized radial flow', which can take any (possibly non-integer) value between 1 and 3. Dimension 1 corresponds to flow in a linear `pipe', dimension 2 corresponds to standard radial flow in a disc-shaped reservoir and dimension 3 corresponds to flow in a spherically symmetric reservoir. -\item \textbf{blockmap}: dictionary\\ - Dictionary mapping the block names in the geometry to the block naming system used in the grid. -\item \textbf{chars}: string\\ - Specify a string of characters to be used to form the character part of block names. For example, to use both lowercase and uppercase characters, set \texttt{chars} to \texttt{ascii\_lowercase + ascii\_uppercase}, or to use uppercase letters only, specify \texttt{ascii\_uppercase}. -\item \textbf{spaces}: Boolean\\ - Specify \texttt{False} to disallow spaces in character part of block names. In this case, the first element of the \texttt{chars} parameter functions like a `zero' and replaces spaces. -\end{itemize} - -Visualization of radial $r-z$ model grids and results can be done in PyTOUGH by creating a `dummy' vertical slice rectangular geometry, using the \texttt{mulgrid} \hyperref[sec:mulgrid:rectangular]{\texttt{rectangular()}} method, using its $x$ direction for radius (and having only one block in the $y$ direction - which is not used). The \hyperref[sec:mulgrid:slice_plot]{\texttt{slice\_plot()}} method can then be used to plot results. - -\begin{snugshade} -\subsubsection{\texttt{rectgeo(\emph{origin\_block}=None, \emph{atmos\_volume}=1.e25, \emph{remove\_inactive}=False,\\ - \emph{convention}=0, \emph{atmos\_type}=2, \emph{justify}='r', \emph{chars}=ascii\_lowercase,\\ - \emph{spaces}=\texttt{True}, \emph{layer\_snap}=0.1, \emph{block\_order}=None)}} -\end{snugshade} -\label{sec:t2grid:rectgeo} -\index{MULgraph geometry!creating!from rectangular TOUGH2 grid} -\index{TOUGH2 grids!rectangular} - -Creates a \hyperref[mulgrids]{\texttt{mulgrid}} geometry object from a rectangular TOUGH2 grid. It also returns a dictionary defining the mapping from the geometry block names to the grid block names. This block mapping can be used when the block naming convention used by the original TOUGH2 grid is not compatible with the layer/column based \hyperref[geometry_format_conventions]{naming conventions} assumed by a \texttt{mulgrid} geometry. - -The method works within the following assumptions: -\begin{itemize} - \item the grid is in fact rectangular (results will not be predictable otherwise) - \item block centre coordinates are present for all blocks in the grid - \item the bottom layer of blocks is complete (no missing blocks) -\end{itemize} - -The method should work on rectangular TOUGH2 grids that have been translated and/or horizontally rotated with respect to the coordinate axes. Grids with incomplete upper layers (e.g. representing topography) should also be OK. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{origin\_block}: string, \hyperref[t2blockobjects]{\texttt{t2block}} or \texttt{None}\\ - The block on the bottom layer of the geometry, at the origin of the axes defined by permeability directions 1 and 2. If \texttt{None}, it will be detected. Specify it manually if the algorithm does not detect it correctly. - \item \textbf{atmos\_volume}: float\\ - Block volume below which blocks are considered part of the geometrical grid. Blocks with volume greater than or equal to this value will be assumed to be boundary condition blocks and will not be represented geometrically. - \item \textbf{remove\_inactive}: Boolean\\ - Set \texttt{True} to remove inactive blocks from the geometry. TOUGH2 treats all blocks with zero or negative volume, and all subsequent blocks in the block list, to be inactive. If this option is used, the inactive blocks will be used to detect the surface elevations of the columns in the geometry. Otherwise, inactive blocks will be retained in the geometry. -\item \textbf{convention}: integer\\ - \hyperref[geometry_format_conventions]{Naming convention} for grid columns and layers in the output geometry. -\item \textbf{atmos\_type}: integer\\ - \hyperref[geometry_format_conventions]{Type} of atmosphere for the output geometry. -\item \textbf{justify}: string\\ - Specify `r' for the character part of the block names (first three characters) to be right-justified, `l' for left-justified. -\item \textbf{chars}: string\\ - Specify a string of characters to be used to form the character part of block names. For example, to use both lowercase and uppercase characters, set \texttt{chars} to \texttt{ascii\_lowercase + ascii\_uppercase}, or to use uppercase letters only, specify \texttt{ascii\_uppercase}. -\item \textbf{spaces}: Boolean\\ - Specify \texttt{False} to disallow spaces in character part of block names. In this case, the first element of the \texttt{chars} parameter functions like a `zero' and replaces spaces. -\item \textbf{layer\_snap}: float\\ - Smallest desired surface block thickness. Set to a positive value to eliminate surface blocks in the geometry with very small thicknesses (resulting from column surface elevations that are very close to the bottom of a layer). Default value is 0.1 m. Note that it is not recommended to use a value of zero, as spurious small-thickness surface blocks can arise from rounding errors in reading the data file. If this still occurs, try increasing the snap value until they disappear. -\item \textbf{block\_order}: string or \texttt{None}\\ - Specify \texttt{None} or `layer\_column' for default block ordering by layer and column, starting from the atmosphere. Specify `dmplex' to order blocks by geometrical type (8-node hexahedrons first followed by 6-node wedges) as in PETSc DMPlex meshes. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{rename\_blocks(\emph{blockmap} = \{\}, \emph{fix\_blocknames} = True)}} -\end{snugshade} -\label{sec:t2grid:rename_blocks} -\index{TOUGH2 grids!blocks!renaming} - -Renames blocks in the grid according to the specified block mapping dictionary. Any block whose name is a key of the block mapping dictionary is renamed with the corresponding dictionary value. Related properties such as connections are also renamed. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{blockmap}: dictionary\\ - Block mapping dictionary, mapping strings to strings. -\item \textbf{fix\_blocknames}: Boolean\\ - Set \texttt{True} (the default) to `fix' block names in the dictionary, using the \hyperref[sec:mulgrid:fix_blockname]{\texttt{fix\_blockname()}} function. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{rename\_rocktype(\emph{rockname}, \emph{newrockname})}} -\end{snugshade} -\label{sec:t2grid:rename_rocktype} -\index{TOUGH2 grids!rocktypes!renaming} - -Renames a rock type in the grid. An exception is raised if the specified rocktype name does not exist, or if the new target rocktype name has already been used. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{rockname}: string\\ - Name of the rock type to be renamed. -\item \textbf{newrockname}: string\\ - New name for the rock type. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{reorder(\emph{block\_names}, \emph{connection\_names}=\texttt{None}, \emph{geo}=\texttt{None})}} -\end{snugshade} -\label{sec:t2grid:reorder} -\index{TOUGH2 grids!reordering} - -Reorders the blocks (and optionally connections) in the grid. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{block\_names}: list of string (or \texttt{None})\\ - List specifying the names of the blocks, in their desired - order. Each block name must exist in the grid, otherwise an error - will be raised. If this parameter is \texttt{None} (the default), - blocks are not reordered (unless a geometry is specified - instead). -\item \textbf{connection\_names}: list of string (or \texttt{None})\\ - List specifying the names of the connections, in their desired - order. Each item in the list should be a tuple of block names. The - ordering of the block names in any tuple may be reversed with - respect to the original connection naming. However an error will be - raised if any tuple of block names in the list does not exist in the - grid (in either its forward or reverse form). If this parameter is - \texttt{None} (the default), connections are not reordered (unless a - geometry is specified instead). -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}} geometry (or \texttt{None})\\ - Geometry object to use for the reordering. If this is specified, the - geometry's block and connection name lists are used (and the - previous parameters are ignored). After reordering, the grid's - blocks and connections will have the same ordering as if the grid - had been created using the \hyperref[sec:t2grid:fromgeo]{\texttt{fromgeo()}} method. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{rocktype\_frequency(\emph{rockname})}} -\end{snugshade} -\label{sec:t2grid:rocktype_frequency} -\index{TOUGH2 grids!rocktypes!frequencies} - -Returns the frequency of use of the rock type with the specified name, i.e. how many blocks are assigned that rock type. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{rockname}: string\\ - Name of the specified rock type. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{sort\_rocktypes()}} -\end{snugshade} -\label{sec:t2grid:sort_rocktypes} -\index{TOUGH2 grids!rocktypes!sorting} - -Sorts the rocktype list into alphabetical order by name. - -\begin{snugshade} -\subsubsection{\texttt{write\_vtk(\emph{geo}, \emph{filename}, \emph{wells}=False, \emph{blockmap} = \{\}, \emph{surface\_snap}=0.1)}} -\end{snugshade} -\label{sec:t2grid:write_vtk} -\index{TOUGH2 grids!writing!VTK files} -\index{Visualization Tool Kit (VTK)} - -Writes a \texttt{t2grid} object to a VTK file on disk, for visualisation with VTK, Paraview, Mayavi etc. The grid is written as an `unstructured grid' VTK object with data arrays defined on cells. The data arrays written, in addition to the defaults arrays for the associated \texttt{mulgrid} object, are: rock type index, porosity and permeability for each block. A separate VTK file for the wells in the grid can optionally be written. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - The \texttt{mulgrid} geometry object associated with the grid. This is required as the \texttt{t2grid} object does not contain any spatial information, e.g. locations of block vertices. -\item \textbf{filename}: string\\ - Name of the VTK file to be written. This is also required. -\item \textbf{wells}: Boolean\\ - Set to \texttt{True} if the wells from the \texttt{mulgrid} object are to be written to a separate VTK file. -\item \textbf{blockmap}: dictionary\\ - Dictionary mapping the block names in the geometry to the block naming system used in the grid. -\item \textbf{surface\_snap}: float\\ - Tolerance for specifying how close column surface elevations need to be before being considered ``equal'' when constructing surface nodes. -\end{itemize} - -\section{Other objects (\texttt{rocktype}, \texttt{t2block} and \texttt{t2connection})} - -A \texttt{t2grid} object contains lists of other types of objects: \texttt{rocktype}, \texttt{t2block} and \texttt{t2connection}. These classes are described below. - -\subsection{\texttt{rocktype} objects} -\label{rocktypeobjects} -\index{PyTOUGH!classes!\texttt{rocktype}} -\index{TOUGH2 grids!rocktypes} - -A \texttt{rocktype} object represents a TOUGH2 rock type. The properties of a \texttt{rocktype} object, and their default values, are given in Table \ref{tb:rocktype_properties}. - -The main familiar properties of a rock type are referred to in a natural way, e.g. the porosity of a rock type \texttt{r} is given by \texttt{r.porosity}. The permeability property is a 3-element \texttt{np.array}, giving the permeability in each of the three principal axes of the grid, so e.g. the vertical permeability of a rock type \texttt{r} would normally be given by \texttt{r.permeability[2]} (recall that array indices in Python are zero-based, so that the third element has index 2). - -Some rock type properties are optional, and only need be specified when the property \texttt{nad} is greater than zero. An example is the relative permeability and capillarity functions that can be specified for a rock type when \texttt{nad} $\ge$ 2. The way these functions are specified is described in chapter \ref{datafiles}. - -\textbf{Example:} - -\begin{lstlisting} -r = rocktype(name = 'ignim', permeability = [10.e-15, 10.e-15, 2.e-15], specific_heat = 850) -\end{lstlisting} - -declares a rocktype object called \texttt{r} with name `ignim', permeability of 10 mD in the first and second directions and 2 mD in the vertical direction, and specific heat 850 J.kg$^{-1}$.K$^{-1}$. - -(Note that when declaring rock types, the permeability can for convenience be specified as a list, which will be converted internally to an \texttt{np.array}.) - -\index{TOUGH2 grids!rocktypes!properties} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|p{30mm}|l|} - \hline - \textbf{Property} & \textbf{Type} & \textbf{Description} & \textbf{Default}\\ - \hline - \texttt{capillarity} & dictionary & capillarity function & --\\ - \texttt{compressibility} & float & compressibility & 0 m$^2$.N$^{-1}$\\ - \texttt{conductivity} & float & heat conductivity & 1.5 W.m$^{-1}$.K$^{-1}$\\ - \texttt{density} & float & rock grain density & 2600 kg.m$^{-3}$\\ - \texttt{dry\_conductivity} & float & dry heat conductivity & wet heat conductivity\\ - \texttt{expansivity} & float & expansivity & 0 K$^{-1}$\\ - \texttt{klinkenberg} & float & Klinkenberg parameter & 0 Pa$^{-1}$\\ - \texttt{nad} & integer & number of extra data lines & 0\\ - \texttt{name} & string & rock type name & `dfalt'\\ - \texttt{permeability} & \texttt{np.array} & permeability & \texttt{np.array}([10$^{-15}$]*3) m$^2$\\ - \texttt{porosity} & float & porosity & 0.1\\ - \texttt{relative\_permeability} & dictionary & relative permeability function & --\\ - \texttt{specific\_heat} & float & rock grain specific heat & 900 J.kg$^{-1}$.K$^{-1}$\\ - \texttt{tortuosity} & float & tortuosity factor & 0\\ - \texttt{xkd3} & float & used by EOS7R & 0 m$^{3}$.kg$^{-1}$\\ - \texttt{xkd4} & float & used by EOS7R & 0 m$^{3}$.kg$^{-1}$\\ - \hline - \end{tabular} - \caption{Properties of a \texttt{rocktype} object} - \label{tb:rocktype_properties} - \end{center} -\end{table} - -\subsection{\texttt{t2block} objects} -\label{t2blockobjects} -\index{PyTOUGH!classes!\texttt{t2block}} -\index{TOUGH2 grids!blocks} - -A \texttt{t2block} object represents a block in a TOUGH2 grid. The properties of a \texttt{t2block} object are given in Table \ref{tb:t2block_properties}. These reflect the specifications of a TOUGH2 block as given in a TOUGH2 data file, with the exception of the \texttt{atmosphere}, \texttt{centre}, \texttt{connection\_name}, \texttt{neighbour\_name} and \texttt{num\_connections} properties. - -The \texttt{atmosphere} property determines whether the block is to be treated as an atmosphere block. The \texttt{centre} property can optionally be used to specify the coordinates of the centre of a block. Block centres are automatically calculated when a \hyperref[t2grids]{\texttt{t2grid}} object is constructed from a \hyperref[mulgrids]{\texttt{mulgrid}} object using the \hyperref[sec:t2grid:fromgeo]{\texttt{fromgeo}} method). The \texttt{connection\_name} property is a set containing the names (as tuples of strings) of all connections involving the block. - -A \texttt{t2block} object has no methods. - -\index{TOUGH2 grids!blocks!properties} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|l|} - \hline - \textbf{Property} & \textbf{Type} & \textbf{Description}\\ - \hline - \texttt{ahtx} & float & interface area for heat exchange (TOUGH2 only)\\ - \texttt{atmosphere} & Boolean & whether block is an atmosphere block or not\\ - \texttt{centre} & \texttt{np.array} & block centre (optional)\\ - \texttt{connection\_name} & set & names of connections involving the block\\ - \texttt{nadd} & integer & increment between block numbers in sequence\\ - \texttt{name} & string & block name\\ - \texttt{neighbour\_name} & set & names of neighbouring (connected) blocks\\ - \texttt{nseq} & integer & number of additional blocks in sequence\\ - \texttt{num\_connections} & integer & number of connections containing the block\\ - \texttt{pmx} & float & permeability modifier (TOUGH2 only)\\ - \texttt{rocktype} & \texttt{rocktype} & rock type\\ - \texttt{volume} & float & block volume\\ - \hline - \end{tabular} - \caption{Properties of a \texttt{t2block} object} - \label{tb:t2block_properties} - \end{center} -\end{table} - -\subsection{\texttt{t2connection} objects} -\label{t2connectionobjects} -\index{PyTOUGH!classes!\texttt{t2connection}} -\index{TOUGH2 grids!connections} - -A \texttt{t2connection} object represents a connections between two TOUGH2 blocks. The properties of a \texttt{t2connnection} object are given in Table \ref{tb:t2connection_properties}. These correspond to the properties of a connection specified in a TOUGH2 data file. Note that the \texttt{block} property returns \hyperref[t2blockobjects]{\texttt{t2block}} objects, not just the names of the blocks in the connection. Hence, for example, the volume of the first block in a connection object \texttt{con} is given simply by \texttt{con.block[0].volume}. - -A \texttt{t2connection} object has no methods. - -\index{TOUGH2 grids!connections!properties} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|l|} - \hline - \textbf{Property} & \textbf{Type} & \textbf{Description}\\ - \hline - \texttt{area} & float & connection area\\ - \texttt{block} & \texttt{list} & two-element list of blocks\\ - \texttt{dircos} & float & gravity direction cosine\\ - \texttt{direction} & integer & permeability direction (1, 2, or 3)\\ - \texttt{distance} & \texttt{list} & two-element list of connection distances\\ - \texttt{nad1,nad2} & integer & increments in sequence numbering\\ - \texttt{nseq} & integer & number of additional connections in sequence\\ - \texttt{sigma} & float & radiant emittance factor (TOUGH2 only)\\ - \hline - \end{tabular} - \caption{Properties of a \texttt{t2connection} object} - \label{tb:t2connection_properties} - \end{center} -\end{table} - -\section{Example} -\index{examples!TOUGH2 grids} - -The following piece of Python script creates a rectangular 2-D slice TOUGH2 grid with two rock types, and assigns these rock types to blocks in the grid according to their position along the slice. - -\begin{lstlisting} -from t2grids import * - -geo = mulgrid().rectangular([500]*20, [1000], [100]*20, atmos_type = 0, convention = 2) -geo.write('2Dgrd.dat') -grid = t2grid().fromgeo(geo) - -grid.add_rocktype(rocktype('greyw', permeability = [1.e-15]*2 + [0.1e-15])) -grid.add_rocktype(rocktype('fill ', permeability = [15.e-15]*2 + [5.e-15])) - -for blk in grid.blocklist[1:]: - if 200 <= blk.centre[0] <= 400: blk.rocktype = grid.rocktype['fill '] - else: blk.rocktype = grid.rocktype['greyw'] -\end{lstlisting} - -The first line just imports the required PyTOUGH library. (It is not necessary to import the \texttt{mulgrids} library explicitly, because it is used and therefore imported by the \texttt{t2grids} library.) - -The second block of code creates a rectangular MULgraph geometry object with 20 columns (each 500 m wide) along the slice and 20 layers (each 100 m thick), writes this to a geometry file on disk, and creates a TOUGH2 grid from it. - -Then the two rock types are created, \texttt{'greyw'} and \texttt{'fill '}. (Note that rock types are expected by TOUGH2 to have names 5 characters long, so it is necessary to add spaces to shorter names.) - -The final part assigns the rock types to the blocks in the grid. The loop starts from 1 instead of 0, so that the atmosphere block is skipped. In this example, the blocks in the grid are assigned the \texttt{'fill '} rock type if they are between 200 m and 400 m along the slice. Blocks outside this region are assigned the \texttt{'greyw'} rock type. diff --git a/doc/t2incons.tex b/doc/t2incons.tex deleted file mode 100755 index 2c30ac11..00000000 --- a/doc/t2incons.tex +++ /dev/null @@ -1,324 +0,0 @@ -\chapter{TOUGH2 initial conditions} -\label{incons} -\index{TOUGH2 initial conditions} - -\section{Introduction} -The \texttt{t2incons} library in PyTOUGH contains classes and routines for reading, editing and writing TOUGH2 initial conditions and files. It can be imported using the command: - -\begin{lstlisting} - from t2incons import * -\end{lstlisting} - -\index{TOUGH2} -\index{TOUGH2!AUTOUGH2} -\index{TOUGH2!TOUGHREACT} -The initial conditions files used by TOUGH2 and AUTOUGH2 have the same format. PyTOUGH also supports TOUGHREACT initial conditions files, which have a slightly different format -- permeabilities are included for each block, and timing information at the bottom of the file is formatted differently. - -\section{\texttt{t2incon} objects} -\index{PyTOUGH!classes!\texttt{t2incon}} -\index{TOUGH2 initial conditions!creating} -\index{TOUGH2 initial conditions!objects} - -The \texttt{t2incons} library defines a \texttt{t2incon} class, used for representing TOUGH2 initial conditions. - -\textbf{Example:} - -\begin{lstlisting} -inc = t2incon() -\end{lstlisting} - -creates an empty \texttt{t2incon} object called \texttt{inc}. - -\begin{lstlisting} -inc = t2incon(filename) -\end{lstlisting} - -creates a \texttt{t2incon} object called \texttt{inc} and reads its contents from file \texttt{filename}. - -\subsection{Properties} - -The main properties of a \texttt{t2incon} object are listed in Table \ref{tb:t2incon_properties}. Once a set of initial conditions is loaded into a \texttt{t2incon} object, conditions for individual blocks can be accessed by block name or index. For example, for a \texttt{t2incon} object \texttt{inc}, the initial conditions in block `blockname' are given simply by \texttt{inc[blockname]}. This returns a \hyperref[t2blockincons]{\texttt{t2blockincon}} object (see section \ref{t2blockincons}). Similarly, \texttt{inc[i]} returns the initial conditions at the block with (zero-based) index \texttt{i}. - -Each column in the initial conditions file can be accessed by adding an integer (zero-based) index after the \texttt{t2blockincon} object, so for example: - -\begin{lstlisting} -t = inc['aa 20'][1] -\end{lstlisting} - -assigns the variable \texttt{t} the value of the second primary thermodynamic variable (index 1) in block \texttt{'AA 20'}. Initial conditions can be edited in a similar way, for example: - -\begin{lstlisting} -inc['aa 20'][0] = p -\end{lstlisting} - -assigns the value of \texttt{p} to the first primary variable (usually pressure) in block \texttt{'AA 20'}. For convenience, initial conditions for a given block can also be specified as a simple list or tuple of values, for example: - -\begin{lstlisting} -inc['ab 25'] = (101.3e5,25.0) -\end{lstlisting} - -sets the initial conditions at block \texttt{'ab 25'} to the specified values. This will work even if no initial conditions have been previously specified for the given block. - -An \texttt{np.array} of the values of the variables at all blocks can be found from the \texttt{variable} property. For example: - -\begin{lstlisting} -inc.variable[:,2] -\end{lstlisting} - -returns an \texttt{np.array} of the third variable (index 2) in each block. The \texttt{variable} property can also be set to a given array. Note, however, that the whole array must be set, not just part of it. For example, adding an offset \texttt{P0} to all pressures (variable 0) in the initial conditions could be done by: - -\begin{lstlisting} -v = inc.variable -v[:,0] += P0 -inc.variable = v -\end{lstlisting} - -The \texttt{porosity} property may be set to assign values of porosity to all blocks. The assigned value may be an \texttt{np.array} with a value for each block, or a scalar float (in which case the same value is assigned to all blocks), or \texttt{None} which assigns the value in each block to \texttt{None}. - -\index{TOUGH2!TOUGHREACT} -Similarly, for TOUGHREACT initial conditions files, the \texttt{permeability} property can be used to read or assign permeabilities for all blocks. When assigning this property, the value can be an \texttt{np.array} of shape (\texttt{num\_blocks}, 3), (i.e. a row for each block), or a single \texttt{np.array} with 3 elements, to be applied to all blocks, a single scalar float (to assign isotropic permeabilities to all blocks) or \texttt{None} which assigns \texttt{None} to all block permeabilities. - -The \texttt{timing} property of a \texttt{t2incon} object contains the optional timing information at the end of the file. This is a dictionary property with keys \texttt{'kcyc'}, \texttt{'iter'}, \texttt{'nm'}, \texttt{'tstart'} and \texttt{'sumtim'}, corresponding to the values stored on this line. - -The \texttt{simulator} string property is `TOUGH2' by default, and is set to `TOUGHREACT' if permeabilities are detected while reading from file. Setting this property back to `TOUGH2' will cause the file to be written out in TOUGH2 format (no permeabilities, and different format for timing information) if the \texttt{write()} method is executed. - -\index{TOUGH2 initial conditions!properties} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|p{75mm}|} - \hline - \textbf{Property} & \textbf{Type} & \textbf{Description}\\ - \hline - \texttt{blocklist} & list & ordered list of block names in the initial conditions file\\ - \texttt{num\_blocks} & integer & number of blocks at which conditions are specified \\ - \texttt{num\_variables} & integer & number of thermodynamic variables specified at each block\\ - \texttt{permeability} & \texttt{np.array} & array of permeability values specified at each block (TOUGHREACT only)\\ - \texttt{porosity} & \texttt{np.array} & array of porosity values specified at each block\\ - \texttt{simulator} & string & simulator type (`TOUGH2' or `TOUGHREACT')\\ - \texttt{timing} & dictionary & additional timing information for restarting\\ - \texttt{variable} & \texttt{np.array} & two-dimensional array of thermodynamic variable values at each block\\ - \hline - \end{tabular} - \caption{Properties of a \texttt{t2incon} object} - \label{tb:t2incon_properties} - \end{center} -\end{table} - -\subsubsection{Functions for reading data from file} -\index{TOUGH2 initial conditions!reading} - -It is possible to specify customized functions to control how data are read from a TOUGH2 initial conditions file. This is done using the optional \texttt{read\_function} parameter when a \texttt{t2incon} object is created- in exactly the same way it is done for a \texttt{mulgrid} object. For more details, see the corresponding documentation for \texttt{mulgrid} objects in section \ref{mulgridreadfunctions}. By default, the read functions for \texttt{t2incon} objects are given by the \texttt{fortran\_read\_function} dictionary. - -\subsubsection{Specifying the number of primary variables} -\index{TOUGH2 initial conditions!number of primary variables} - -Most common TOUGH2 EOS modules have no more than four primary variables, in which case the variables for a given block all fit on one line in the initial conditions file. However, some EOS modules (e.g. EOS7c and EOS7r) have more than four primary variables. For these, the variables for a given block are specified over multiple lines in the initial conditions file. - -In this case, it is not possible for PyTOUGH to reliably detect the number of primary variables, as it does when there are no more than four variables. Instead, the number of primary variables must be specified when the \texttt{t2incon} object is created (or its \hyperref[sec:t2incon:read]{\texttt{read()}} method is executed). This can be done by setting the optional integer \texttt{num\_variables} parameter, which defaults to \texttt{None} (meaning PyTOUGH will detect the number of variables). For example: - -\begin{lstlisting} -from t2incons import * -inc = t2incon('model.incon', num_variables = 6) -\end{lstlisting} - -opens initial conditions for an EOS using six primary variables. - -For writing initial conditions files with more than four primary variables, no extra parameters need be set, as the data stored in the \texttt{t2incon} object determines the number of primary variables, and they will be written out over multiple lines as required. - -\subsection{Methods} - -The main methods of a \texttt{t2incon} object are listed in Table \ref{tb:t2incon_methods}. Details of these methods are given below. - -\index{TOUGH2 initial conditions!methods} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|p{65mm}|} - \hline - \textbf{Method} & \textbf{Type} & \textbf{Description}\\ - \hline - \hyperref[sec:t2incon:add_incon]{\texttt{add\_incon}} & -- & adds a set of initial conditions for one block\\ - \hyperref[sec:t2incon:delete_incon]{\texttt{delete\_incon}} & -- & deletes the initial conditions for one block\\ - \hyperref[sec:t2incon:empty]{\texttt{empty}} & -- & deletes all initial conditions from the object\\ - \hyperref[sec:t2incon:insert_incon]{\texttt{insert\_incon}} & -- & inserts initial conditions for one block at a specified index\\ - \hyperref[sec:t2incon:read]{\texttt{read}} & -- & reads initial conditions from file\\ - \hyperref[sec:t2incon:transfer_from]{\texttt{transfer\_from}} & -- & transfers initial conditions from one grid to another\\ - \hyperref[sec:t2incon:write]{\texttt{write}} & -- & writes initial conditions to file\\ - \hline - \end{tabular} - \caption{Methods of a \texttt{t2incon} object} - \label{tb:t2incon_methods} - \end{center} -\end{table} - -\begin{snugshade} -\subsubsection{\texttt{add\_incon(\emph{incon})}} -\end{snugshade} -\label{sec:t2incon:add_incon} -\index{TOUGH2 initial conditions!adding initial conditions} - -Adds a set of initial conditions for a single block. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{incon}: \hyperref[t2blockincons]{\texttt{t2blockincon}}\\ - Initial conditions for the block. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{delete\_incon(\emph{blockname})}} -\end{snugshade} -\label{sec:t2incon:delete_incon} -\index{TOUGH2 initial conditions!deleting initial conditions} - -Deletes a set of initial conditions for a single block. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{blockname}: string\\ - Name of the block at which initial conditions are to be deleted. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{empty()}} -\end{snugshade} -\label{sec:t2incon:empty} -\index{TOUGH2 initial conditions!emptying} - -Deletes initial conditions for all blocks. - -\begin{snugshade} -\subsubsection{\texttt{insert\_incon(\emph{index},\emph{incon})}} -\end{snugshade} -\label{sec:t2incon:insert_incon} -\index{TOUGH2 initial conditions!inserting initial conditions} - -Inserts a set of initial conditions for a single block at the specified index. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{index}: integer\\ - Index (zero-based) at which to insert the initial conditions. -\item \textbf{incon}: \hyperref[t2blockincons]{\texttt{t2blockincon}}\\ - Initial conditions for the block. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{read(\emph{filename}, \emph{num\_variables} = None)}} -\end{snugshade} -\label{sec:t2incon:read} -\index{TOUGH2 initial conditions!reading} - -Reads initial conditions from file. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{filename}: string\\ - Name of the initial conditions file to be read. -\item \textbf{num\_variables}: integer or \texttt{None}\\ - If reading initial conditions files with more than four primary variables, set to the number of primary variables. Otherwise, the default \texttt{None} value can be used, in which case the number of primary variables will be detected automatically. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{transfer\_from(\emph{sourceinc}, \emph{sourcegeo}, \emph{geo}, \emph{mapping=\{\}},\ - \emph{colmapping=\{\}})}} -\end{snugshade} -\label{sec:t2incon:transfer_from} -\index{TOUGH2 initial conditions!transferring} - -Transfers initial conditions from another \texttt{t2incon} object \texttt{sourceinc}, using the two corresponding \texttt{mulgrid} geometry objects \texttt{sourcegeo} and \texttt{geo}, and optionally the block and column mappings between the two grids (which are created if not specified). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{sourceinc}: \hyperref[incons]{\texttt{t2incon}}\\ - Source initial conditions object. -\item \textbf{sourcegeo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - Geometry object corresponding to the source initial conditions. -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - Geometry object for the grid to be transferred to. -\item \textbf{mapping}: dictionary\\ - Dictionary mapping block names from \texttt{geo} to \texttt{sourcegeo}. -\item \textbf{colmapping}: dictionary\\ - Dictionary mapping column names from \texttt{geo} to \texttt{sourcegeo}. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{write(\emph{filename}, \emph{reset}=True)}} -\end{snugshade} -\label{sec:t2incon:write} -\index{TOUGH2 initial conditions!writing} - -Writes initial conditions to file. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{filename}: string\\ - Name of the initial conditions file to be written. -\item \textbf{reset}: Boolean\\ - Set to \texttt{False} if timing information is not to be reset - e.g. if restarting a transient simulation. -\end{itemize} - -\section{\texttt{t2blockincon} objects} -\label{t2blockincons} -\index{PyTOUGH!classes!\texttt{t2blockincon}} -\index{TOUGH2 initial conditions!at one block} -\index{TOUGH2!TOUGHREACT} - -A \texttt{t2blockincon} object represents the initial conditions for a particular block. The properties of a \texttt{t2blockincon} object are given in Table \ref{tb:t2blockincon_properties}. The \texttt{permeability} property is used only by TOUGHREACT. If no values are specified for \texttt{porosity}, \texttt{permeability}, \texttt{nseq} or \texttt{nadd}, their values are \texttt{None}. A \texttt{t2blockincon} object has no methods. - -The \texttt{variable} property of a \texttt{t2blockincon} can be more easily accessed simply by adding the required (zero-based) variable index after the object. For example, for a \texttt{t2blockincon} object \texttt{b}, the value of the second variable is given simply by \texttt{b[1]}. - -To create a new \texttt{t2blockincon} object, simply invoke the class name with values of the desired properties, e.g.: - -\begin{lstlisting} - binc = t2blockincon(block = 'abc10', porosity = 0.1, variable = [101.3e3, 28.]) -\end{lstlisting} - -\begin{table} - \begin{center} - \begin{tabular}{|l|l|p{75mm}|} - \hline - \textbf{Property} & \textbf{Type} & \textbf{Description}\\ - \hline - \texttt{block} & string & block name\\ - \texttt{nadd} & integer or \texttt{None} & optional block index increment between additional blocks with the same initial conditions\\ - \texttt{nseq} & integer or \texttt{None} & optional number of additional blocks with the same initial conditions\\ - \texttt{permeability} & \texttt{np.array} or \texttt{None} & optional permeability for the block (TOUGHREACT only)\\ - \texttt{porosity} & float or \texttt{None} & optional porosity for the block\\ - \texttt{variable} & list & list of thermodynamic variable values for the block\\ - \hline - \end{tabular} - \caption{Properties of a \texttt{t2blockincon} object} - \label{tb:t2blockincon_properties} - \end{center} -\end{table} - -\section{Reading save files and converting to initial conditions} -\index{TOUGH2 initial conditions!converting from save files} - -TOUGH2 writes a save file (SAVE, or *.save for AUTOUGH2) at the end of the simulation, which has a format almost the same as that of an initial conditions file and can be used to start a subsequent run. A save file generally has some extra timing information at the end which can be used to restart a simulation at a particular time. However, in many cases, e.g when running natural state simulations, we want to restart at the original start time and this timing information must be discarded. - -PyTOUGH will read a save file into a \texttt{t2incon} object. This can then be written to file, providing a simple way to convert save files into incon files. By default, the timing information is discarded when writing (it can be retained by setting the \texttt{reset} parameter of the \texttt{write} method to \texttt{False}). For example: - -\begin{lstlisting} -t2incon('model1.save').write('model2.incon') -\end{lstlisting} - -will read the save file \texttt{'model1.save'}, convert it to initial conditions, and write it to the initial conditions file \texttt{'model2.incon'}. - -\section{Example} -\index{examples!initial conditions} - -The following piece of Python script reads in a save file and prints out a table of block names and temperatures for the first 10 blocks. It then adds an extra variable to each initial condition and gives it a constant value (giving a new column in the initial conditions file), and finally writes out the edited initial conditions to a new file. - -Adding a new variable to each initial condition can be useful when e.g. changing from one TOUGH2 equation of state (EOS) module to another, as different EOS modules may have different numbers of primary thermodynamic variables. - -\begin{lstlisting} -from t2incons import * -inc = t2incon('model1.save') -for blk in inc[0:10]: - print 'Block %5s: temperature = %5.1f' % (blk.block,blk[1]) -patm = 101.3e3 -for blk in inc: blk.variable.append(patm) -inc.write('model2.incon') -\end{lstlisting} - diff --git a/doc/t2listing.tex b/doc/t2listing.tex deleted file mode 100755 index 92272cae..00000000 --- a/doc/t2listing.tex +++ /dev/null @@ -1,739 +0,0 @@ -\chapter{TOUGH2 listing files} -\label{listingfiles} - -\section{Introduction} -The \texttt{t2listing} library in PyTOUGH contains classes and routines for reading TOUGH2 listing files. It can be imported using the command: - -\begin{lstlisting} - from t2listing import * -\end{lstlisting} - -\index{TOUGH2} -\index{TOUGH2!AUTOUGH2} -\index{TOUGH2!TOUGH2-MP} -\index{TOUGH2!TOUGH+} -\index{TOUGH2!TOUGHREACT} - -Listing files produced by AUTOUGH2, TOUGH2, TOUGH2\_MP, TOUGH+ and TOUGH3 have different formats but are all supported. The main listing files produced by TOUGHREACT are also supported. (There is also a separate \hyperref[toughreact_tecplot]{\texttt{toughreact\_tecplot}} class for handling the additional Tecplot output files produced by TOUGHREACT.) - -\section{\texttt{t2listing} objects} -\index{PyTOUGH!classes!\texttt{t2listing}} -\index{TOUGH2 listing files!objects} -\index{TOUGH2 listing files!creating} - -The \texttt{t2listing} library defines a \texttt{t2listing} class, used for representing TOUGH2 listing files. - -\textbf{Example:} - -\begin{lstlisting} -lst = t2listing() -\end{lstlisting} - -creates an empty \texttt{t2listing} object called \texttt{lst}. - -\begin{lstlisting} -lst = t2listing(filename) -\end{lstlisting} - -creates a \texttt{t2listing} object called \texttt{lst} and reads its contents from file \texttt{filename}. - -\subsection{Properties} -\label{t2listing_properties} -\index{TOUGH2 listing files!properties} - -The main properties of a \texttt{t2listing} object are listed in Table \ref{tb:t2listing_properties}. - -\subsubsection{Element, connection and generation tables} -\index{TOUGH2 listing files!tables} - -There are three main `table' properties, corresponding to the \textbf{element}, \textbf{connection} and \textbf{generation} tables in the listing file. These are all of type \hyperref[listingtableobjects]{\texttt{listingtable}} (see section \ref{listingtableobjects}) and provide access to the simulation results. Not all of these tables will necessarily be present - this depends on the settings in the data file which produced the results. For TOUGH2 results, a fourth \textbf{primary} table may also be present, containing primary variables and their changes, if the KDATA parameter is set to 3. \index{TOUGH2!TOUGH+} TOUGH+ results can also contain additional element tables containing other calculated quantities; these are named \textbf{element1}, \textbf{element2} etc. A list of names of all available tables is given by the \texttt{table\_names} property. - -For example, for a \texttt{t2listing} object \texttt{lst}, \texttt{lst.element['AR210']['Temperature']} gives the temperature at block `AR210', at the current time. Blocks can also be identified by index rather than name, so that \texttt{lst.element[120]['Pressure']} gives the pressure at the block with (zero-based) index 120. - -These tables can also be accessed to give all results for a given block, or for a given column in the table. For example, \texttt{lst.element['AR210']} returns a dictionary containing all results at block `AR210', referred to by the name of each table column. \texttt{lst.element['Temperature']} returns an \texttt{np.array} containing the temperatures at all blocks in the model. (Hence, \texttt{lst.element['Pressure'][120]} gives the same result as \texttt{lst.element[120]['Pressure']}.) - -The connection and generation tables work very similarly to the element table, except that connections are referred to by tuples of block names (rather than single block names), and generators are referred to by tuples of block names and generator names. So for example, the mass flow rate between blocks `AB300' and `AC300' might be given by \texttt{lst.connection['AB300', 'AC300']['Mass flow']}. - -The names of the columns for each table are read directly from the listing file, and will depend on the TOUGH2 equation of state (EOS) being used. - -\subsubsection{Skipping tables} -\index{TOUGH2 listing files!tables!skipping} - -The default behaviour is for a \texttt{t2listing} object to read all tables present in the listing file. However, it is possible to skip the reading of specified tables if required. This can be useful for speeding up reading of large listing files where not all tables are required. For example, sometimes the connection data are not required, but for large models the connection table is often much bigger than the others, so skipping it can make reading significantly faster. Data in skipped tables are not available either via their corresponding properties or via the \hyperref[sec:t2listing:history]{\texttt{history()}} method. - -To skip tables, specify their table names (\texttt{element}, \texttt{connection} etc.) in the optional \texttt{skip\_tables} parameter when creating the \texttt{t2listing} object. (By default, this parameter is an empty list.) For example, to read a listing file with name `output.listing' into the object \texttt{lst} and skip reading the connection and generation tables: - -\begin{lstlisting} -lst = t2listing('output.listing', skip_tables = ['connection', 'generation']) -\end{lstlisting} - -\subsubsection{File encoding} -\index{TOUGH2 listing files!file encoding} - -It is possible to specify the file encoding for the listing file using the optional \texttt{encoding} parameter when creating the \texttt{t2listing} object. The default for this parameter is ``latin-1'' encoding which should be fine for reading in most listing files. If you encounter exotic characters in your listing files which are not read correctly using the default encoding you may want to try other encodings. - -\subsubsection{Full and short output} -\index{TOUGH2 listing files!short output} - -AUTOUGH2 allows the use of `short' output, in which a specified selection of block, connection or generator properties are printed at time steps between normal full output. A \texttt{t2listing} object will read short output results, if they are present, when producing time histories using the \hyperref[sec:t2listing:history]{\texttt{history()}} method. However it is not possible to navigate to short output results or access them via the \texttt{t2listing} table properties above. - -TOUGH2, TOUGH2\_MP, TOUGHREACT, TOUGH+ and TOUGH3 do not support short output. - -\subsubsection{Navigating in time using \texttt{time}, \texttt{index} and \texttt{step}} -\index{TOUGH2 listing files!navigating in time} - -The \texttt{time} property returns the time (in seconds) corresponding to the current set of results. It is also possible to set the \texttt{time} property to navigate to a specific set of full results. For example, \texttt{lst.time=1.e9} navigates to the set of full results with time closest to $10^9$s. - -The \texttt{index} property gives the index of the current set of results, and can take any value between 0 and \texttt{num\_fulltimes}-1. The value of \texttt{index} can also be set to change to a different set of results in the listing file (e.g. \texttt{lst.index=12}). It can be incremented and decremented like any other Python integer variable, e.g. \texttt{lst.index+=1} or \texttt{lst.index-=2} to go to the next set of results, or the second to last set respectively. - -The \texttt{step} property gives the time step number for the current set of results. This is the number of time steps carried out in the simulation up to the current set of results (recall that results are not necessarily written to the listing file at every time step). Again, its value can be set to navigate through the results, e.g. \texttt{lst.step=100} navigates to the set of full results with time step number nearest to 100. - -The \texttt{times} property returns an \texttt{np.array} of all times at which results (including short output) are given in the listing file. It has length equal to \texttt{num\_times}. The \texttt{fulltimes} property returns an \texttt{np.array} of times at which full results are given (not including short output), and has length equal to \texttt{num\_fulltimes}. - -A \texttt{t2listing} object also has methods (as well as properties) for navigating through time (see section \ref{t2listingmethods}). - -\subsubsection{Listing diagnostics} -\index{TOUGH2 listing files!diagnostics} - -\texttt{t2listing} objects have two properties that provide diagnostics on the results of the TOUGH2 run. - -The \texttt{convergence} property is a dictionary of the maximum absolute differences in the element table between the second to last and last sets of results in the listing file. This can be used to check convergence of steady-state simulations. For example: - -\begin{lstlisting} -lst.convergence['Temperature'] -\end{lstlisting} - -gives the largest absolute temperature change between the second to last and last sets of results. - -The \texttt{reductions} property is a list of tuples of time step indices at which the time step size was reduced during the simulation, and the block name at which the maximum residual occurred prior to each reduction. This gives an indication of problematic times and blocks which caused time step reductions. - -\index{TOUGH2 listing files!properties} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|p{70mm}|} - \hline - \textbf{Property} & \textbf{Type} & \textbf{Description}\\ - \hline - \texttt{connection} & \hyperref[listingtableobjects]{\texttt{listingtable}} & connection table for current set of results\\ - \texttt{convergence} & dictionary & maximum differences in element table between second to last and last sets of results\\ - \texttt{element} & \hyperref[listingtableobjects]{\texttt{listingtable}} & element table for current set of results\\ - \texttt{element1} etc. & \hyperref[listingtableobjects]{\texttt{listingtable}} & additional element table for current set of results (TOUGH+ only)\\ - \texttt{filename} & string & name of listing file on disk\\ - \texttt{fullsteps} & \texttt{np.array} & array of time step numbers (integer) for full results\\ - \texttt{fulltimes} & \texttt{np.array} & array of times (float) for full results\\ - \texttt{generation} & \hyperref[listingtableobjects]{\texttt{listingtable}} & generation table for current set of results\\ - \texttt{index} & integer & index of current set of results\\ - \texttt{num\_fulltimes} & integer & number of sets of full results\\ - \texttt{num\_times} & integer & number of sets of all results (full and short)\\ - \texttt{primary} & \hyperref[listingtableobjects]{\texttt{listingtable}} & primary variable table for current set of results (TOUGH2 only)\\ - \texttt{reductions} & list & time step indices at which time step was reduced during the simulation\\ - \texttt{short\_types} & list of string & types of short output present\\ - \texttt{simulator} & string & detected simulator (`AUTOUGH2', `TOUGH2' etc.)\\ - \texttt{step} & integer & time step number of current set of results\\ - \texttt{steps} & \texttt{np.array} & array of time step numbers (integer) for all results (full and short)\\ - \texttt{table\_names} & list & names of available tables\\ - \texttt{time} & float & time of current set of results\\ - \texttt{times} & \texttt{np.array} & array of times (float) for all results (full and short)\\ - \texttt{title} & string & simulation title\\ - \hline - \end{tabular} - \caption{Properties of a \texttt{t2listing} object} - \label{tb:t2listing_properties} - \end{center} -\end{table} - -\subsection{Methods} -\label{t2listingmethods} - -The main methods of a \texttt{t2listing} object are listed in Table \ref{tb:t2listing_methods}. Details of these methods are given below. - -\index{TOUGH2 listing files!methods} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|p{80mm}|} - \hline - \textbf{Method} & \textbf{Type} & \textbf{Description}\\ - \hline - \hyperref[sec:t2listing:add_side_recharge]{\texttt{add\_side\_recharge}} & -- & adds side recharge generators to a \texttt{t2data} object\\ - \hyperref[sec:t2listing:close]{\texttt{close}} & -- & closes listing file\\ - \hyperref[sec:t2listing:first]{\texttt{first}} & -- & navigates to the first set of full results\\ - \hyperref[sec:t2listing:get_difference]{\texttt{get\_difference}} & dictionary & maximum differences in element table between two sets of results\\ - \hyperref[sec:t2listing:history]{\texttt{history}} & list or tuple & time history for a selection of locations and table columns\\ - \hyperref[sec:t2listing:last]{\texttt{last}} & -- & navigates to the last set of full results\\ - \hyperref[sec:t2listing:next]{\texttt{next}} & Boolean & navigates to the next set of full results\\ - \hyperref[sec:t2listing:prev]{\texttt{prev}} & Boolean & navigates to the previous set of full results\\ - \hyperref[sec:t2listing:write_vtk]{\texttt{write\_vtk}} & -- & writes results to VTK file\\ - \hline - \end{tabular} - \caption{Methods of a \texttt{t2listing} object} - \label{tb:t2listing_methods} - \end{center} -\end{table} - -\begin{snugshade} -\subsubsection{\texttt{add\_side\_recharge(\emph{geo}, \emph{dat})}} -\end{snugshade} -\label{sec:t2listing:add_side_recharge} -\index{TOUGH2 listing files!using to add side recharge} - -Adds side recharge generators to a \texttt{t2data} object \texttt{dat} for a production run, calculated according to the final results in the listing. These generators represent side inflows due to pressure changes in the blocks on the model's horizontal boundaries. Recharge generators are given the names of their blocks- any existing generators with the same names will be overwritten. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - Geometry object associated with the listing. -\item \textbf{dat}: \texttt{t2data}\\ - TOUGH2 data object for the side recharge generators to be added to. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{close()}} -\end{snugshade} -\label{sec:t2listing:close} -\index{TOUGH2 listing files!closing} - -Closes the listing file after use. - -\begin{snugshade} -\subsubsection{\texttt{first()}} -\end{snugshade} -\label{sec:t2listing:first} -\index{TOUGH2 listing files!navigating in time} - -Navigates to the first set of full results in the listing file. - -\begin{snugshade} -\subsubsection{\texttt{get\_difference(\emph{indexa}=None, \emph{indexb}=None)}} -\end{snugshade} -\label{sec:t2listing:get_difference} -\index{TOUGH2 listing files!tables!maximum differences} - -Returns dictionary of maximum differences, and locations of difference, of all element table properties between two sets of results. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{indexa}, \textbf{indexb}: integer or \texttt{None}\\ - Indices of results between which the maximum differences are to be calculated. If both indexa and indexb are provided, the result is the difference between these two result indices. If only one index is given, the result is the difference between the given index and the one before that. If neither are given, the result is the difference between the last and penultimate sets of results. -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{history(\emph{selection}, \emph{short}=True, \emph{start\_datetime}=None})} -\end{snugshade} -\label{sec:t2listing:history} -\index{TOUGH2 listing files!time histories} - -Returns a list of time histories (as \texttt{np.arrays}) for specified locations and table columns in the element, connection or generation tables. For each selection, a tuple of two \texttt{np.arrays} is returned, one each for times and values. Short output (AUTOUGH2 only) can be omitted from the history results by setting the \texttt{short} parameter to \texttt{False}. If the \texttt{start\_datetime} parameter is given, times in the output are given as datetimes rather than seconds from the start. - -\textbf{Parameters:} -\begin{itemize} - -\item \textbf{selection}: list of tuples\\ - Selection of listing tables, locations (or indices) and table columns to produce histories for. Each tuple contains three elements: the listing \textbf{table type} (`e', `c', `p' or `g' for element, connection, primary or generation table respectively), the \textbf{block/ connection/ generator name} (or index) and the \textbf{table column name}. (If only a single tuple is given instead of a list of tuples, just the single tuple of times and values for that selection is returned.) For history of additional element tables in TOUGH+ results, use `e1', `e2' etc. instead of `e'. Note that, as for listing tables, connection and generator names (or `keys') are specified as two-element tuples (see Table \ref{tb:listing_table_keys}). If the second element of a selection tuple is an integer, it will be interpreted as the (zero-based) index of the block, connection or generator in the corresponding table. - -\item \textbf{short}: Boolean\\ - Whether short output (AUTOUGH2 only) is to be included in the history results - default is \texttt{True}. - -\item \textbf{start\_datetime}: datetime or \texttt{None}\\ - Datetime of the start of the simulation. If \texttt{None} (the default), output times are given as seconds from the start of the simulation. If a Python datetime is given, then output times are given as datetimes. - -\end{itemize} - -\textbf{Examples:} - -\begin{lstlisting} -[(tt,temp), (tq,q), (tg,g)] = lst.history([('e', 'AR210', 'Temperature'), -('c', ('AB300','AC300'), 'Mass flow'), ('g', ('BR110','SO 1'), 'Generation rate')]) -\end{lstlisting} - -returns a list of three tuples of \texttt{np.arrays}, \texttt{(tt,temp)}, \texttt{(tq,q)} and \texttt{(tg,g)}, giving the times and values of temperature at block `AR210', mass flow at the connection between blocks `AB300' and `AC300', and generation rate in the generator `SO 1' in block `BR110' respectively. - -\begin{lstlisting} - from datetime import datetime - t0 = datetime(1955, 1, 1) - t,T = lst.history(('e', 'AR210', 'Temperature'), start_datetime = t0) -\end{lstlisting} - -returns \texttt{T} as an \texttt{np.array} of temperature values, and \texttt{t} as an \texttt{np.array} of Python datetimes, starting at 1 January 1955. - -\begin{snugshade} -\subsubsection{\texttt{last()}} -\end{snugshade} -\label{sec:t2listing:last} -\index{TOUGH2 listing files!navigating in time} - -Navigates to the last set of full results in the listing file. - -\begin{snugshade} -\subsubsection{\texttt{next()}} -\end{snugshade} -\label{sec:t2listing:next} -\index{TOUGH2 listing files!navigating in time} - -Navigates to the next set of full results in the listing file. Returns \texttt{False} if already at the last set of results (and \texttt{True} otherwise). - -\begin{snugshade} -\subsubsection{\texttt{prev()}} -\end{snugshade} -\label{sec:t2listing:prev} -\index{TOUGH2 listing files!navigating in time} - -Navigates to the previous set of full results in the listing file. Returns \texttt{False} if already at the first set of results (and \texttt{True} otherwise). - -\begin{snugshade} -\subsubsection{\texttt{write\_vtk(\emph{geo}, \emph{filename}, \emph{grid}=None, \emph{indices}=None, \emph{flows}=False, -\emph{wells}=False,\\ -\emph{start\_time}=0, \emph{time\_unit}='s', \emph{flux\_matrix}=None, \emph{blockmap} = \{\},\\ - \emph{surface\_snap}=0.1)}} -\end{snugshade} -\label{sec:t2listing:write_vtk} -\index{TOUGH2 listing files!writing!VTK files} -\index{Visualization Tool Kit (VTK)} - -Writes a \texttt{t2listing} object to a set of VTK files on disk, for visualisation with VTK, Paraview, Mayavi etc. The results in the listing object are written as an `unstructured grid' VTK object with data arrays defined on cells. The data arrays written correspond to the variables given in the columns of the element table of the \texttt{t2listing} object. (For TOUGH+ results, variables from the additional element tables are also included.) In addition, data arrays from an associated \texttt{mulgrid} and (optionally) \texttt{t2grid} objects can be included. - -If \texttt{flows} is \texttt{True} (and a \texttt{grid} is specified and the listing contains connection data), approximate block-average flux vectors at the centre of each block are also written, for all variables in the connection table with names ending in `flow'. - -One *.vtu file is produced for each time step in the \texttt{t2listing} object at which full results are present, and a *.pvd file is also written. This is usually the file that should actually be opened in Paraview or other software as it contains time information associated with each *.vtu file. - -Optionally, only a subset of the time indices present in the \texttt{t2listing} can be written, according to the \texttt{indices} parameter. A start time and time unit for the output can optionally be specified. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - The \texttt{mulgrid} geometry object associated with the results. For flexibility, this geometry need not be fully compatible with the results -- for example, it may contain only a subset of the blocks for which results are present, or the blocks may be in a different order. However, if it is not fully compatible, the writing process will be slower, and flux vectors will not be written (even if \texttt{flows} is set to \texttt{True}). -\item \textbf{filename}: string\\ - Name of the *.pvd file to be written. Names of the individual *.vtu files for each time step are similar but with a time index appended and the file extension changed. -\item \textbf{grid}: \hyperref[t2grids]{\texttt{t2grid}}\\ - Name of optional \texttt{t2grid} object associated with the results. -\item \textbf{indices}: list or tuple\\ - Optional specification of time indices to include in the output. If set to \texttt{None} (the default), all time indices will be included. -\item \textbf{flows}: Boolean\\ - Set to \texttt{True} if approximate block-centred flux vectors are to be calculated and written, for visualising flows. Default is \texttt{False}. \textbf{Note}: flow vectors can only be calculated if a \textbf{grid} is specified. -\item \textbf{wells}: Boolean\\ - Set to \texttt{True} if a separate VTK file for the wells in the \hyperref[mulgrids]{\texttt{mulgrid}} object is to be written. Default is \texttt{False}. -\item \textbf{start\_time}: float\\ - Optional start time of the simulation, i.e. time associated with the first set of results. Default is zero. -\item \textbf{time\_unit}: string\\ - Optional time unit for the output. TOUGH2 results are given at times in seconds, but this option allows them to be converted to other units. Options are: `s', `h', `d' and `y', for seconds, hours, days and years respectively. Default is `s'. -\item \textbf{flux\_matrix}: \texttt{scipy.sparse.lil\_matrix}\\ - Sparse matrix that multiplies a vector of connection values to produce a partition vector of 3-D block average flows at the (underground) block centres. One of these can be produced using the \texttt{t2grid.flux\_matrix()} method, and a corresponding \texttt{mulgrid} object. A flux matrix will be calculated internally if not supplied. -\item \textbf{blockmap}: dictionary\\ - Dictionary mapping the block names in the geometry to the block naming system used in the listing. -\item \textbf{surface\_snap}: float\\ - Tolerance for specifying how close column surface elevations need to be before being considered ``equal'' when constructing surface nodes. -\end{itemize} - -\section{\texttt{listingtable} objects} -\label{listingtableobjects} -\index{PyTOUGH!classes!\texttt{listingtable}} -\index{TOUGH2 listing files!tables} - -A \texttt{listingtable} object represents a table of results in a TOUGH2 listing file (whether it is an element, connection or generation table). The column headings of the table are taken directly from the corresponding table in the listing file. The rows of the table may be accessed either by (zero-based) index, or by the `key' for the table row, which depends on the table type (see Table \ref{tb:listing_table_keys}). - -\index{TOUGH2 listing files!tables!keys} -\begin{table}[h] - \begin{center} - \begin{tabular}{|l|l|} - \hline - \textbf{Table type} & \textbf{Key}\\ - \hline - \texttt{element} & block name\\ - \texttt{connection} & (block name 1, block name 2)\\ - \texttt{generation} & (block name, generator name)\\ - \hline - \end{tabular} - \caption{Keys for different listing table types} - \label{tb:listing_table_keys} - \end{center} -\end{table} - -Hence, the value in the element table for a given block and column can be accessed by \texttt{lst.element[blockname][columnname]}, or by \texttt{lst.element[blockindex][columnname]} (for a \texttt{t2listing} object \texttt{lst}). Note that for connection and generation tables, the keys are tuples of two strings. For connection tables, the order of these two strings (the block names) is not important; if the listing file contains results for (block1, block2), then results for (block2, block1) can be accessed via the corresponding \texttt{listingtable} object (though the results will have the opposite sign to those in the file, as they will represent flows in the opposite direction). - -The values for an entire row or column of the table can also be accessed, for example \texttt{lst.element[blockname]} gives the row in the table for a specified block, with the values arranged in a dictionary which can be accessed using the column names of the table (e.g. \texttt{lst.element['AR231']['Temperature']}). This dictionary for each row also contains an additional \texttt{'key'} item which returns the key for that row. Conversely, \texttt{lst.element[columnname]} gives the column in the table for a specified column name, with the values returned in an \texttt{np.array} (one value for each block in the grid, for an element table). - -\subsection{\texttt{listingtable} properties} - -The properties of a \texttt{listingtable} object are given in Table \ref{tb:listingtable_properties}. The entire list of key values for a \texttt{listingtable} may be accessed via the \texttt{row\_name} property, which contains the key value for each row. The column headings of the table can similarly be accessed via the \texttt{column\_name} list property. The \texttt{num\_rows} and \texttt{num\_columns} properties of a \texttt{listingtable} object return the numbers of rows and columns respectively. The \texttt{num\_keys} property just returns the number of keys used to identify each row - generally 1 for an element table and 2 for connection and generation tables. - -\index{TOUGH2 listing files!tables!properties} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|} - \hline - \textbf{Property} & \textbf{Type} & \textbf{Description}\\ - \hline - \texttt{column\_name} & list & column headings\\ - \texttt{DataFrame} & pandas DataFrame & data in DataFrame format\\ - \texttt{num\_columns} & integer & number of columns \\ - \texttt{num\_keys} & integer & number of keys per row \\ - \texttt{num\_rows} & integer & number of rows \\ - \texttt{row\_name} & list & keys for each row \\ - \hline - \end{tabular} - \caption{Properties of a \texttt{listingtable} object} - \label{tb:listingtable_properties} - \end{center} -\end{table} - -\subsection{Adding and subtracting} -\index{TOUGH2 listing files!tables!adding and subtracting} -It is possible to perform addition and subtraction operations on \texttt{listingtable} objects. Subtraction can be useful, for example, when comparing results from different runs. These operations can only be carried out when the row and column names of the two tables are identical. The resulting table will have the same row and column names as the original tables, but will contain the element-wise sums or differences. - -\subsection{Converting to DataFrames} - -A \texttt{listingtable} object has a \texttt{DataFrame} property which returns the entire table in the form of a \texttt{pandas} (\url{http://pandas.pydata.org/}) DataFrame object. \texttt{pandas} is a Python library for data analysis, which you will need to have installed before you can use the \texttt{DataFrame} property. With \texttt{pandas} you can do advanced data analysis on your TOUGH2 results. See the \texttt{pandas} documentation for more details. - -\subsection{\texttt{listingtable} methods} - -\texttt{listingtable} objects have one method as described below. - -\index{TOUGH2 listing files!tables!methods} -\index{TOUGH2 listing files!tables!finding rows} -\begin{snugshade} -\subsubsection{\texttt{rows\_matching(\emph{pattern}, \emph{index}=0, \emph{match\_any}=False)}} -\end{snugshade} - -Returns a list of rows in the table with keys matching the specified regular expression string, \texttt{pattern}. - -For tables with multiple keys, \texttt{pattern} can be a list or tuple of regular expressions. If a single string pattern is given for a multiple-key table, the pattern is matched on the index$^{th}$ key (and any value of the other key - unless the \texttt{match\_any} option is used; see below). - -If \texttt{match\_any} is set to \texttt{True}, rows are returned with keys matching any of the specified patterns (instead of all of them). If this option is used in conjunction with a single string pattern, the specified pattern is applied to all keys. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{pattern}: string, list or tuple\\ - Regular expression string specifying the pattern to match. For multiple-key tables, this can be a list or tuple of regular expression strings. -\item \textbf{index}: integer\\ - Index of the key to which the pattern is to be applied, for multiple-key tables and when \texttt{pattern} is specified as a single string. -\item \textbf{match\_any}: Boolean\\ - If \texttt{False}, return only rows with keys matching \emph{all} of their corresponding patterns. If \texttt{True}, return rows with keys matching \emph{any} of the specified patterns - and if a single string pattern is given, apply this to all keys. -\end{itemize} - -\section{\texttt{t2historyfile} objects} -\index{PyTOUGH!classes!\texttt{t2historyfile}} -\index{TOUGH2 history files} -\index{TOUGH2 data files!FOFT} -\index{TOUGH2 data files!COFT} -\index{TOUGH2 data files!GOFT} - -In addition to the main listing file, TOUGH2 can optionally produce extra files containing time history data from selected blocks, connections or generators, named \texttt{FOFT}, \texttt{COFT} and \texttt{GOFT} files respectively. TOUGH+ can optionally name these files \texttt{Elem\_Time\_Series}, \texttt{Conx\_Time\_Series} and \texttt{SS\_Time\_Series} instead. (AUTOUGH2 does not produce separate history files, but can instead produce `short output' at selected blocks, connections or generators within the listing file itself.) - -The \texttt{t2listing} module contains a \texttt{t2historyfile} class for reading and manipulating these history files. History files produced by TOUGH2, TOUGH2\_MP and TOUGH+ are supported, although they all have different formats. The same class is used for FOFT, COFT and GOFT files. A history file of any of these types can be opened using a command such as: - -\index{TOUGH2 history files!creating} -\begin{lstlisting} -hist = t2historyfile(filename) -\end{lstlisting} - -\index{TOUGH2!TOUGH2-MP} -where \texttt{\emph{filename}} is the name of the file. It may contain wildcards (*) so that several files matching a pattern are read in to the same object. This is useful for reading output from TOUGH2\_MP, which creates separate history files for each processor used in the calculation (e.g. \texttt{FOFT\_P.000}, \texttt{FOFT\_P.001}, etc.). It is assumed that all files opened are however of the same type (FOFT, COFT or GOFT). - -Once a history file has been read in, history results for a particular key (i.e. block, connection or generator) can be extracted. For TOUGH2\_MP, the keys are the block names for FOFT files, tuples of block names for COFT files, and tuples of block names and source names for GOFT files. For example: - -\begin{lstlisting} -foft = t2historyfile('FOFT_P.*') -blockname = 'fmq20' -results = foft[blockname] -\end{lstlisting} - -This will return a dictionary containing an \texttt{np.array} for each column in the file, indexed by the column name. For example the temperature history at this block would be given by: - -\begin{lstlisting} -temp = foft[blockname]['TEMPERATURE'] -\end{lstlisting} - -Results at a particular time can also be found: - -\begin{lstlisting} -time = 3.156e7 -result = foft[blockname, time] -\end{lstlisting} - -Again, this will return a dictionary with one item for each column, but in this case each item is just a single floating point number instead of an array. - -\index{TOUGH2} -For \textbf{TOUGH2} rather than TOUGH2\_MP, the keys are integer indices of blocks, connections or generators, rather than names or tuples of names. Similarly, the column names are just integers. This is because the key names and column names are not given in TOUGH2 history files. Aside from these differences, they can be used in the same way as TOUGH2\_MP history files, for example: - -\begin{lstlisting} -foft = t2historyfile('FOFT') -blkindex = 123 -temp = foft[blkindex][1] -\end{lstlisting} - -\index{TOUGH2!TOUGH+} -For \textbf{TOUGH+} connection and generator history files (\texttt{COFT} and \texttt{GOFT}, or \texttt{Conx\_Time\_Series} and \texttt{SS\_Time\_Series}), multiple connections and generators can be specified as usual in the TOUGH2 input data file, but individual results for them are not written to the history file. Instead, the results for them are summed. As a result, there are no `keys' as such for accessing individual results, and the \texttt{t2historyfile} works a little differently. An array containing the data in each column can be accessed by specifying the column name, for example: - -\begin{lstlisting} -ct = t2historyfile('Conx_Time_Series') -qh = ct['HeatFlow'] -\end{lstlisting} - -The properties of a \texttt{t2historyfile} object are given in Table \ref{tb:historyfile_properties}. - -\index{TOUGH2 history files!properties} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|l|} - \hline - \textbf{Property} & \textbf{Type} & \textbf{Description}\\ - \hline - \texttt{column\_name} & list & column headings\\ - \texttt{key\_name} & list & names of keys\\ - \texttt{num\_times} & integer & number of times\\ - \texttt{num\_columns} & integer & number of data columns\\ - \texttt{num\_rows} & integer & total number of data (for all keys)\\ - \texttt{simulator} & string & detected simulator (`TOUGH2' or `TOUGH2\_MP')\\ - \texttt{times} & \texttt{np.array} & times at which results are given\\ - \texttt{type} & string & history type (`FOFT', `COFT' or `GOFT')\\ - \hline - \end{tabular} - \caption{Properties of a \texttt{t2historyfile} object} - \label{tb:historyfile_properties} - \end{center} -\end{table} - -\section{\texttt{toughreact\_tecplot} objects} -\label{toughreact_tecplot} -\index{TOUGHREACT Tecplot files} -\index{PyTOUGH!classes!\texttt{toughreact\_tecplot}} -\index{TOUGH2!TOUGHREACT} - -The \texttt{t2listing} library also defines a \texttt{toughreact\_tecplot} class, used for representing the additional Tecplot output files produced by TOUGHREACT. - -\textbf{Example:} - -\begin{lstlisting} -tp = toughreact_tecplot(filename, blocks) -\end{lstlisting} - -creates a \texttt{toughreact\_tecplot} object called \texttt{tp} and reads its contents from file \texttt{filename}. The \texttt{blocks} object passed in as a second parameter specifies the block names (see \ref{toughreact_tecplot_blocknames}). - -\subsection{Differences from \texttt{t2listing} objects} - -A \texttt{toughreact\_tecplot} object is similar to a \hyperref[listingfiles]{\texttt{t2listing}} object in many respects. Apart from the need to specify the block names on creation (see \ref{toughreact_tecplot_blocknames}), the other main difference is that unlike a \texttt{t2listing} object, which usually contains several \texttt{listingtable} objects, a \texttt{toughreact\_tecplot} object contains only one: the \texttt{element} table. Because of this, when using the \texttt{history} method, tables need not be specified. - -These Tecplot files do not contain any information about time step numbers, so \texttt{t2listing} properties like \texttt{step} and \texttt{steps} are not present in a \texttt{toughreact\_tecplot} object. There is also no \texttt{title} property, as this is not present in the Tecplot file. - -There is also no `short' output in a Tecplot file, so a \texttt{toughreact\_tecplot} object does not have properties like \texttt{fulltimes}, as this would just be the same as the \texttt{times} property. There are also no diagnostic methods like \texttt{convergence} or \texttt{reductions}. - -\subsection{Specifying block names} -\label{toughreact_tecplot_blocknames} -\index{TOUGHREACT Tecplot files!block names} - -In the Tecplot file, results are not associated with block names, though they appear in the same order as in the TOUGH2 data file used to generate the results. To make results accessible by block name, a second parameter containing the block names must be specified when a \texttt{toughreact\_tecplot} object is created. This parameter is not optional. It can be either: - -\begin{itemize} -\item a list of strings specifying the block names -\item a \hyperref[mulgrids]{\texttt{mulgrid}} geometry object -\item a \hyperref[t2grids]{\texttt{t2grid}} object -\end{itemize} - -\subsection{Properties} -\index{TOUGHREACT Tecplot files!properties} - -The main properties of a \texttt{toughreact\_tecplot} object are listed in Table \ref{tb:toughreact_tecplot_properties}. For more details, see the corresponding properties of the \hyperref[t2listing_properties]{\texttt{t2listing}} class. - -\begin{table} - \begin{center} - \begin{tabular}{|l|l|p{70mm}|} - \hline - \textbf{Property} & \textbf{Type} & \textbf{Description}\\ - \hline - \texttt{element} & \hyperref[listingtableobjects]{\texttt{listingtable}} & element table for current set of results\\ - \texttt{filename} & string & name of listing file on disk\\ - \texttt{index} & integer & index of current set of results\\ - \texttt{num\_times} & integer & number of sets of results\\ - \texttt{time} & float & time of current set of results\\ - \texttt{times} & \texttt{np.array} & array of times (float) for all results\\ - \hline - \end{tabular} - \caption{Properties of a \texttt{toughreact\_tecplot} object} - \label{tb:toughreact_tecplot_properties} - \end{center} -\end{table} - -\subsection{Methods} -\index{TOUGHREACT Tecplot files!methods} - -The methods of a \texttt{toughreact\_tecplot} object are listed in Table \ref{tb:toughreact_tecplot_methods}. Details of these methods are given below. - -\begin{table} - \begin{center} - \begin{tabular}{|l|l|p{90mm}|} - \hline - \textbf{Method} & \textbf{Type} & \textbf{Description}\\ - \hline - \hyperref[sec:toughreact_tecplot:close]{\texttt{close}} & -- & closes file\\ - \hyperref[sec:toughreact_tecplot:first]{\texttt{first}} & -- & navigates to the first set of full results\\ - \hyperref[sec:toughreact_tecplot:history]{\texttt{history}} & list or tuple & time history for a selection of locations and table columns\\ - \hyperref[sec:toughreact_tecplot:last]{\texttt{last}} & -- & navigates to the last set of full results\\ - \hyperref[sec:toughreact_tecplot:next]{\texttt{next}} & Boolean & navigates to the next set of full results\\ - \hyperref[sec:toughreact_tecplot:prev]{\texttt{prev}} & Boolean & navigates to the previous set of full results\\ - \hyperref[sec:toughreact_tecplot:write_vtk]{\texttt{write\_vtk}} & -- & writes results to VTK file\\ - \hline - \end{tabular} - \caption{Methods of a \texttt{toughreact\_tecplot} object} - \label{tb:toughreact_tecplot_methods} - \end{center} -\end{table} - -\begin{snugshade} -\subsubsection{\texttt{close()}} -\end{snugshade} -\label{sec:toughreact_tecplot:close} -\index{TOUGHREACT Tecplot files!closing} - -Closes the file after use. - -\begin{snugshade} -\subsubsection{\texttt{first()}} -\end{snugshade} -\label{sec:toughreact_tecplot:first} -\index{TOUGHREACT Tecplot files!navigating in time} - -Navigates to the first set of results in the Tecplot file. - -\begin{snugshade} -\subsubsection{\texttt{history(\emph{selection})}} -\end{snugshade} -\label{sec:toughreact_tecplot:history} -\index{TOUGHREACT Tecplot files!time histories} - -Returns a list of time histories (as \texttt{np.arrays}) for specified locations and table columns in the element table. For each selection, a tuple of two \texttt{np.arrays} is returned, one each for times and values. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{selection}: list of tuples\\ - Selection of locations (or indices) and table columns to produce histories for. Each tuple contains two elements: \textbf{block name} and \textbf{table column name}. (If only a single tuple is given instead of a list of tuples, just the single tuple of times and values for that selection is returned.) -\end{itemize} - -\begin{snugshade} -\subsubsection{\texttt{last()}} -\end{snugshade} -\label{sec:toughreact_tecplot:last} -\index{TOUGHREACT Tecplot files!navigating in time} - -Navigates to the last set of results in the Tecplot file. - -\begin{snugshade} -\subsubsection{\texttt{next()}} -\end{snugshade} -\label{sec:toughreact_tecplot:next} -\index{TOUGHREACT Tecplot files!navigating in time} - -Navigates to the next set of results in the Tecplot file. Returns \texttt{False} if already at the last set of results (and \texttt{True} otherwise). - -\begin{snugshade} -\subsubsection{\texttt{prev()}} -\end{snugshade} -\label{sec:toughreact_tecplot:prev} -\index{TOUGHREACT Tecplot files!navigating in time} - -Navigates to the previous set of results in the Tecplot file. Returns \texttt{False} if already at the first set of results (and \texttt{True} otherwise). - -\begin{snugshade} -\subsubsection{\texttt{write\_vtk(\emph{geo}, \emph{filename}, \emph{grid}=None, \emph{indices}=None, \emph{start\_time}=0,\\ - \emph{time\_unit}='s', \emph{blockmap} = \{\}, \emph{surface\_snap}=0.1)}} -\end{snugshade} -\label{sec:toughreact_tecplot:write_vtk} -\index{TOUGHREACT Tecplot files!writing!VTK files} -\index{Visualization Tool Kit (VTK)} - -Writes a \texttt{toughreact\_tecplot} object to a set of VTK files on disk, for visualisation with VTK, Paraview, Mayavi etc. The results in the element table of the Tecplot file object are written as an `unstructured grid' VTK object with data arrays defined on cells. The data arrays written correspond to the variables given in the columns of the element table of the \texttt{toughreact\_tecplot} object. In addition, data arrays from an associated \texttt{mulgrid} and (optionally) \texttt{t2grid} objects can be included. - -One *.vtu file is produced for each time step in the \texttt{toughreact\_tecplot} object, and a *.pvd file is also written. This is usually the file that should actually be opened in Paraview or other software as it contains time information associated with each *.vtu file. - -Optionally, only a subset of the time indices present in the \texttt{toughreact\_tecplot} can be written, according to the \texttt{indices} parameter. A start time and time unit for the output can optionally be specified. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{geo}: \hyperref[mulgrids]{\texttt{mulgrid}}\\ - The \texttt{mulgrid} geometry object associated with the results. For flexibility, this geometry need not be fully compatible with the results -- for example, it may contain only a subset of the blocks for which results are present, or the blocks may be in a different order. However, if it is not fully compatible, the writing process will be slower. -\item \textbf{filename}: string\\ - Name of the *.pvd file to be written. Names of the individual *.vtu files for each time step are similar but with a time index appended and the file extension changed. -\item \textbf{grid}: \hyperref[t2grids]{\texttt{t2grid}}\\ - Name of optional \texttt{t2grid} object associated with the results. -\item \textbf{indices}: list or tuple\\ - Optional specification of time indices to include in the output. If set to \texttt{None} (the default), all time indices will be included. -\item \textbf{start\_time}: float\\ - Optional start time of the simulation, i.e. time associated with the first set of results. Default is zero. -\item \textbf{time\_unit}: string\\ - Optional time unit for the output. TOUGHREACT Tecplot results are given at times in years, but this option allows them to be converted to other units. Options are: `s', `h', `d' and `y', for seconds, hours, days and years respectively. Default is `s'. -\item \textbf{blockmap}: dictionary\\ - Dictionary mapping the block names in the geometry to the block naming system used in the Tecplot output. -\item \textbf{surface\_snap}: float\\ - Tolerance for specifying how close column surface elevations need to be before being considered ``equal'' when constructing surface nodes. -\end{itemize} - -\section{Examples} -\index{examples!TOUGH2 listing files} - -\subsection{Slice plot of drawdown} - -This script shows a vertical slice plot along the model's \emph{x}-axis of the difference in pressure (i.e. drawdown) between the start and end of a simulation. - -\begin{lstlisting} -from mulgrids import * -from t2listing import * -from copy import copy - -geo = mulgrid('gmodel.dat') -results = t2listing('model.listing') - -results.first() -p0 = copy(results.element['Pressure']) -results.last() -p1 = results.element['Pressure'] - -geo.slice_plot('x', (p1-p0)/1.e5, 'Pressure\ difference', 'bar') -\end{lstlisting} - -(Note: the \texttt{copy} command is needed, otherwise the arrays \texttt{p0} and \texttt{p1} would both contain the final values of pressure after the \texttt{results.last()} command.) - -\subsection{Pressure-temperature diagram} - -This script plots model results from a specified block on a pressure-temperature diagram. - -\begin{lstlisting} -from t2listing import * -import matplotlib.pyplot as plt - -lst = t2listing('model.listing') -blk = ' n 60' -[(tp,p), (tt,t)] = lst.history([('e', blk, 'Pressure'), ('e', blk, 'Temperature')]) - -plt.plot(t, p/1.e5, 'o-') -plt.xlabel('T ($\degree$C)') -plt.ylabel('P (bar)') -plt.show() -\end{lstlisting} - -\subsection{Comparing results of two models} -\label{comparison_example} - -This script reads grids and results for two different models, a coarse model and a fine one, and produces a comparison plot of the time history of temperature for both models at a given point. - -\begin{lstlisting} -from mulgrids import * -from t2listing import * -import matplotlib.pyplot as plt - -geoc, geof = mulgrid('gcoarse.dat'), mulgrid('gfine.dat') -coarse, fine = t2listing('coarse.listing'), t2listing('fine.listing') - -p = [47.e3, 0.0, -7000.0] -blkc = geoc.block_name_containing_point(p) -blkf = geof.block_name_containing_point(p) - -tc, tempc = coarse.history(('e', blkc, 'Temperature')) -tf, temp = fine.history(('e', blkf, 'Temperature')) - -plt.plot(tc, tempc, 'o-', label = 'coarse model') -plt.plot(tf, tempf, 's-', label = 'fine model') -plt.xlabel('time (s)') -plt.ylabel('Temperature ($\degree$C)') -plt.legend() - -plt.show() -\end{lstlisting} - diff --git a/doc/t2thermo.tex b/doc/t2thermo.tex deleted file mode 100755 index 0d5fb8ad..00000000 --- a/doc/t2thermo.tex +++ /dev/null @@ -1,232 +0,0 @@ -\chapter{TOUGH2 thermodynamics} -\label{t2thermo} -\index{thermodynamics!IFC-67} -\index{PyTOUGH!thermodynamics!IFC-67} - -\section{Introduction} -The \texttt{t2thermo} library in PyTOUGH contains a Python implementation of the thermodynamic routines used in TOUGH2. These can be used to calculate the thermodynamic properties of water and steam under a range of conditions. They are based on a subset of the IFC-67 thermodynamic formulation \citep{IFC_67}. - -The \texttt{t2thermo} library can be imported using the command: - -\begin{lstlisting} - from t2thermo import * -\end{lstlisting} - -The functions available through the \texttt{t2thermo} library are listed in Table \ref{tb:t2thermo_functions} and described below. - -\index{thermodynamics!IFC-67!functions} -\begin{table} - \begin{center} - \begin{tabular}{|l|l|p{65mm}|} - \hline - \textbf{Function} & \textbf{Type} & \textbf{Description}\\ - \hline - \hyperref[sec:t2thermo:cowat]{\texttt{cowat}} & tuple & density and internal energy of liquid water\\ - \hyperref[sec:t2thermo:sat]{\texttt{sat}} & float & saturation pressure as a function of temperature\\ - \hyperref[sec:t2thermo:region]{\texttt{region}} & integer & thermodynamic region\\ - \hyperref[sec:t2thermo:separated_steam_fraction]{\texttt{separated\_steam\_fraction}} & float & separated steam fraction for given enthalpy and separator pressure\\ - \hyperref[sec:t2thermo:supst]{\texttt{supst}} & tuple & density and internal energy of dry steam\\ - \hyperref[sec:t2thermo:tsat]{\texttt{tsat}} & float & saturation temperature as a function of pressure\\ - \hyperref[sec:t2thermo:visw]{\texttt{visw}} & float & dynamic viscosity of water\\ - \hyperref[sec:t2thermo:viss]{\texttt{viss}} & float & dynamic viscosity of steam\\ - \hline - \end{tabular} - \caption{\texttt{t2thermo} functions} - \label{tb:t2thermo_functions} - \end{center} -\end{table} - -\section{Thermodynamic functions} - -The thermodynamic routines used in TOUGH2 provide functions for liquid water and dry steam. These functions calculate secondary parameters from the primary thermodynamic variables. Their names follow the subroutine names used in the TOUGH2 code. - -\begin{snugshade} -\subsection{Liquid water: \texttt{cowat(\emph{t}, \emph{p}, \emph{bounds} = False)}} -\end{snugshade} -\label{sec:t2thermo:cowat} -\index{thermodynamics!IFC-67!liquid water properties} - -The \texttt{cowat} function returns a two-element tuple (\texttt{d},\texttt{u}) of density (kg/m$^3$) and internal energy (J/kg) of liquid water as a function of temperature \texttt{t} ($\degree$C) and pressure \texttt{p} (Pa). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{t}: float\\ - Temperature ($\degree$C) -\item \textbf{p}: float\\ - Pressure (Pa) -\item \textbf{bounds}: Boolean\\ - If \texttt{True}, return \texttt{None} if the input temperature and pressure are outside the operating range of the routine (as defined by thermodynamic region 1 of the IFC-67 specification). -\end{itemize} - -\begin{snugshade} -\subsection{Dry steam: \texttt{supst(\emph{t}, \emph{p}, \emph{bounds} = False)}} -\end{snugshade} -\label{sec:t2thermo:supst} -\index{thermodynamics!IFC-67!dry steam properties} - -The \texttt{supst} function returns a two-element tuple (\texttt{d},\texttt{u}) of density (kg/m$^3$) and internal energy (J/kg) of dry steam as a function of temperature \texttt{t} ($\degree$C) and pressure \texttt{p} (Pa). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{t}: float\\ - Temperature ($\degree$C) -\item \textbf{p}: float\\ - Pressure (Pa) -\item \textbf{bounds}: Boolean\\ - If \texttt{True}, return \texttt{None} if the input temperature and pressure are outside the operating range of the routine (as defined by thermodynamic region 2 of the IFC-67 specification). -\end{itemize} - -\section{Viscosity} - -\begin{snugshade} -\subsection{Liquid water: \texttt{visw(\emph{t,p,ps})}} -\end{snugshade} -\label{sec:t2thermo:visw} -\index{thermodynamics!IFC-67!viscosity} - -The \texttt{visw} function returns the dynamic viscosity (Pa.s) of liquid water as a function of temperature \texttt{t} ($\degree$C), pressure (Pa) and saturation pressure (Pa). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{t}: float\\ - Temperature ($\degree$C) -\item \textbf{p}: float\\ - Pressure (Pa) -\item \textbf{ps}: float\\ - Saturation pressure (Pa), calculated for example using the \texttt{sat} function. -\end{itemize} - -\begin{snugshade} -\subsection{Dry steam: \texttt{viss(\emph{t,d})}} -\end{snugshade} -\label{sec:t2thermo:viss} -\index{thermodynamics!IFC-67!viscosity} - -The \texttt{viss} function returns the dynamic viscosity (Pa.s) of dry steam as a function of temperature \texttt{t} ($\degree$C) and density \texttt{d} (kg/m$^3$). - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{t}: float\\ - Temperature ($\degree$C) -\item \textbf{d}: float\\ - Density (kg/m$^3$) -\end{itemize} - -\section{Saturation line: \texttt{sat(\emph{t})} and \texttt{tsat(\emph{p})}} - -\begin{snugshade} -\subsection{\texttt{sat(\emph{t}, \emph{bounds} = False)}} -\end{snugshade} -\label{sec:t2thermo:sat} -\index{thermodynamics!IFC-67!saturation curve} - -The \texttt{sat} function returns the saturation pressure (Pa) at a given temperature \texttt{t} ($\degree$C), for temperatures below the critical temperature. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{t}: float\\ - Temperature ($\degree$C) -\item \textbf{bounds}: Boolean\\ - If \texttt{True}, return \texttt{None} if the input temperature is outside the operating range of the routine (i.e. less than 0.01$\degree$C or greater than the critical temperature, 374.15$\degree$C ). -\end{itemize} - -\begin{snugshade} -\subsection{\texttt{tsat(\emph{p}, \emph{bounds} = False)}} -\end{snugshade} -\label{sec:t2thermo:tsat} -\index{thermodynamics!IFC-67!saturation curve} - -The \texttt{tsat} function returns the saturation temperature ($\degree$C) at a given pressure \texttt{p} (Pa), for pressures below the critical pressure. - -Note that the IFC-67 formulation did not include an explicit formula for calculating saturation temperature as a function of pressure, so here (as in TOUGH2) this is calculated using an iterative root-finding process on the \texttt{sat} function. The root-finding function is from the \texttt{scipy} library, so this library must be installed before the \texttt{tsat} function will work. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{p}: float\\ - Pressure (Pa) -\item \textbf{bounds}: Boolean\\ - If \texttt{True}, return \texttt{None} if the input pressure is outside the operating range of the routine (i.e. less than \texttt{sat(0.01)} or greater than the critical pressure, 22.12 MPa). -\end{itemize} - -\section{Other functions} - -\subsection{Separated steam fraction} - -\begin{snugshade} -\subsubsection{\texttt{separated\_steam\_fraction(\emph{h}, \emph{separator\_pressure}, \emph{separator\_pressure2} = None)}} -\end{snugshade} -\label{sec:t2thermo:separated_steam_fraction} -\index{thermodynamics!IFC-67!separated steam fraction} - -Returns the separated steam fraction for a given enthalpy \texttt{h} and separator pressure. A second separator pressure may be specified in the case of two-stage flash. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{h}: float\\ - Enthalpy (J/kg) -\item \textbf{separator\_pressure}: float\\ - Steam separator pressure (Pa) -\item \textbf{separator\_pressure2}: float (or \texttt{None})\\ - Second separator pressure (Pa) for two-stage flash -- set to \texttt{None} (the default) for single-stage. -\end{itemize} - -\subsection{Determining thermodynamic region} - -\begin{snugshade} -\subsubsection{\texttt{region(\emph{t}, \emph{p})}} -\end{snugshade} -\label{sec:t2thermo:region} -\index{thermodynamics!IFC-67!region} - -Returns the thermodynamic region (integer, or \texttt{None}) corresponding to the given temperature ($\degree$C) and pressure (Pa), as defined by the IFC-67 specification. The regions are: - -\begin{enumerate} - \item liquid water - \item dry steam - \item supercritical - \item near-critical -\end{enumerate} - -If the input temperature and/or pressure are outside the operating range of the IFC-67 formulation, the routine will return \texttt{None}. - -\textbf{Parameters:} -\begin{itemize} -\item \textbf{t}: float\\ - Temperature ($\degree$C) -\item \textbf{Pressure}: float\\ - Pressure (Pa) -\end{itemize} - -\section{Example} -\index{examples!thermodynamics} - -The following script reads in a geometry file and writes an initial conditions file with approximate hydrostatic conditions corresponding to a specified vertical temperature gradient. In this case, the model has a simple flat surface, so that each column has the same number of layers. The \texttt{cowat} function is used to calculate the fluid density at each layer, and hence the approximate vertical pressure distribution. - -\begin{lstlisting} -from mulgrids import * -from t2thermo import * - -geo = mulgrid('gmodel.dat') - -patm, tatm = 101.325e3, 15.0 -ptblk = np.zeros((geo.num_blocks, 2)) -ptblk[:,0] = patm; ptblk[:,1] = tatm - -g = 9.8 -p, t = patm, tatm -thick = 0.0 -tgradient = 30 # deg C/km -for lay in geo.layerlist[1:]: - d = cowat(t, p)[0] - thisthick = lay.top - lay.bottom - h = 0.5 * (thick + thisthick) - p += d * g * h - t += tgradient / 1.e3 * h - thick = thisthick - for col in geo.columnlist: - blkname = geo.block_name(lay.name, col.name) - iblk = geo.block_name_index[blkname] - ptblk[iblk] = [p, t] -inc = dat.grid.incons(ptblk) -inc.write('model.incon') -\end{lstlisting} diff --git a/doc/titlepage.tex b/doc/titlepage.tex deleted file mode 100755 index ddd8e5f1..00000000 --- a/doc/titlepage.tex +++ /dev/null @@ -1,35 +0,0 @@ -\begin{titlepage} - -\begin{center} - -\bigskip\ - -\textbf{\Huge{PyTOUGH user's guide}} - -\bigskip\ - -\includegraphics[width=0.75\textwidth]{coverpic} - -\bigskip - -\textbf{\large{Dr. Adrian Croucher\\ -Department of Engineering Science\\ -University of Auckland\\ -Auckland, New Zealand}} - -\bigskip -\bigskip -\bigskip - -\includegraphics[height=4cm]{UoA_logo} - -\bigskip -\bigskip -\bigskip -\bigskip - -\textbf{\large{Version 1.5.5\\July 2021}} - -\end{center} -\end{titlepage} - diff --git a/geometry.py b/geometry.py index a075c6b1..e761194d 100755 --- a/geometry.py +++ b/geometry.py @@ -115,18 +115,16 @@ def polygon_centroid(polygon): area *= 0.5 return c / (6. * area) + shift -def line_polygon_intersections(polygon, line, bound_line = (True,True), - indices = False): +def line_polygon_intersections(polygon, line, bound_line = (True,True)): """Returns a list of the intersection points at which a line crosses a polygon. The list is sorted by distance from the start of the line. The parameter bound_line controls whether to limit - intersections between the line's start and end points. If indices - is True, also return polygon side indices of intersections. + intersections between the line's start and end points. """ crossings = [] ref = polygon[0] l1, l2 = line[0] - ref, line[1] - ref - tol = 1.e-12 + tol = 1.e-9 ind = {} def in_unit(x): return -tol <= x <= 1.0 + tol for i, p in enumerate(polygon): @@ -144,11 +142,13 @@ def in_unit(x): return -tol <= x <= 1.0 + tol ind[c] = i except LinAlgError: continue crossings = [np.array(c) for c, i in ind.items()] - # Sort by distance from start of line: - sortindex = np.argsort([norm(c - line[0]) for c in crossings]) - if indices: return [crossings[i] for i in sortindex], \ - [ind[tuple(crossings[i])] for i in sortindex] - else: return [crossings[i] for i in sortindex] + # Remove duplicates and sort by distance from start of line: + d = np.array([norm(c - line[0]) for c in crossings]) + if len(d) > 0: d = d / max(d[-1], 1) # non-dimensionalise + d = d.round(decimals = 3) + d_unique, i_unique = np.unique(d, return_index = True) + sortindex = np.argsort(d_unique) + return [crossings[i_unique[i]] for i in sortindex] def polyline_polygon_intersections(polygon, polyline): """Returns a list of intersection points at which a polyline (list of @@ -160,6 +160,58 @@ def polyline_polygon_intersections(polygon, polyline): from itertools import chain # flatten list of lists return list(chain.from_iterable(intersections)) +def line_intersects_rectangle(rect, line): + # returns True if line intersects axis-aligned rectangle defined + # by two corners. Simplified Cohen-Sutherland algorithm, based on + # the pylineclip library. + + INSIDE, LEFT, RIGHT, LOWER, UPPER = 0, 1, 2, 4, 8 + + xmin, ymin = rect[0] + xmax, ymax = rect[1] + x1, y1 = line[0] + x2, y2 = line[1] + + def clip(xa, ya): + p = INSIDE + if xa < xmin: p |= LEFT + elif xa > xmax: p |= RIGHT + if ya < ymin: p |= LOWER + elif ya > ymax: p |= UPPER + return p + + k1 = clip(x1, y1) + k2 = clip(x2, y2) + + while (k1 | k2) != 0: + + if (k1 & k2) != 0: return False + + opt = k1 or k2 + if opt & UPPER: + x = x1 + (x2 - x1) * (ymax - y1) / (y2 - y1) + y = ymax + elif opt & LOWER: + x = x1 + (x2 - x1) * (ymin - y1) / (y2 - y1) + y = ymin + elif opt & RIGHT: + y = y1 + (y2 - y1) * (xmax - x1) / (x2 - x1) + x = xmax + elif opt & LEFT: + y = y1 + (y2 - y1) * (xmin - x1) / (x2 - x1) + x = xmin + else: + raise RuntimeError('Undefined clipping state') + + if opt == k1: + x1, y1 = x, y + k1 = clip(x1, y1) + elif opt == k2: + x2, y2 = x, y + k2 = clip(x2, y2) + + return True + def simplify_polygon(polygon, tolerance = 1.e-6): """Simplifies a polygon by deleting colinear points. The tolerance for detecting colinearity of points can optionally be diff --git a/mulgrids.py b/mulgrids.py index 9db21efe..40fb2c00 100755 --- a/mulgrids.py +++ b/mulgrids.py @@ -60,7 +60,7 @@ def fix_blockname(name): def unfix_blockname(name): """The inverse of fix_blockname().""" - return "%3s%2d" % (name[0:3], int(name[3:5])) + return "%3s%2d" % (name[0:3], int(name[3:5])) if name[3:5].isdigit() else name def fix_block_mapping(blockmap): """Fixes block names in specified block mapping.""" @@ -566,6 +566,7 @@ def __init__(self, filename = '', type = 'GENER', convention = 0, # 0: 3-char column + 2-digit layer # 1: 3-char layer + 2-digit column # 2: 2-char layer + 3-digit column + # 3: 3-char column + 2-char layer self._atmosphere_type = atmos_type # atmosphere type: # 0: single atmosphere block # 1: one atmosphere block per column @@ -587,9 +588,9 @@ def __init__(self, filename = '', type = 'GENER', convention = 0, def set_secondary_variables(self): """Sets variables dependent on naming convention and atmosphere type""" if self.atmosphere_type == 0: - self.atmosphere_column_name = ['ATM', ' 0', ' 0'][self.convention] - self.colname_length = [3, 2, 3][self.convention] - self.layername_length = [2, 3, 2][self.convention] + self.atmosphere_column_name = ['ATM', ' 0', ' 0', 'ATM'][self.convention] + self.colname_length = [3, 2, 3, 3][self.convention] + self.layername_length = [2, 3, 2, 2][self.convention] def get_convention(self): """Get naming convention""" @@ -732,7 +733,8 @@ def __repr__(self): conventionstr = [ '3 characters for column, 2 digits for layer', '3 characters for layer, 2 digits for column', - '2 characters for layer, 3 digits for column'][self.convention] + '2 characters for layer, 3 digits for column', + '3 characters for column, 2 characters for layer'][self.convention] atmstr = [ 'single atmosphere block', 'one atmosphere block over each column', @@ -867,6 +869,7 @@ def column_name(self, blockname): if self.convention == 0: return blockname[0: 3] elif self.convention == 1: return blockname[3: 5] elif self.convention == 2: return blockname[2: 5] + elif self.convention == 3: return blockname[0: 3] else: return None def layer_name(self, blockname): @@ -874,12 +877,13 @@ def layer_name(self, blockname): if self.convention == 0: return blockname[3: 5] elif self.convention == 1: return blockname[0: 3] elif self.convention == 2: return blockname[0: 2] + elif self.convention == 3: return blockname[3: 5] else: return None def node_col_name_from_number(self, num, justfn = str.rjust, chars = ascii_lowercase, spaces = True): """Returns node or column name from number.""" - if self.convention == 0: + if self.convention in [0, 3]: name = justfn(int_to_chars(num, chars = chars, spaces = spaces, length = self.colname_length), self.colname_length) else: name = str.rjust(str(num), self.colname_length) @@ -1441,7 +1445,7 @@ def block_name(self, layername, colname, blockmap = {}): """Returns block name from layer and column names, depending on the naming convention. An optional block mapping can be applied. """ - if self.convention == 0: name = colname[0:3] + layername[0:2] + if self.convention in [0, 3]: name = colname[0:3] + layername[0:2] elif self.convention == 1: name = layername[0:3] + colname[0:2] else: name = layername[0:2] + colname[0:3] blkname = fix_blockname(name) @@ -1602,7 +1606,7 @@ def add_layers(self, thicknesses, top_elevation = 0, justify = 'r', num = 0 self.clear_layers() z = top_elevation - surfacelayername = [' 0', 'atm', 'at'][self.convention] + surfacelayername = [' 0', 'atm', 'at', ' 0'][self.convention] self.add_layer(layer(surfacelayername, z, z)) for thickness in thicknesses: z -= thickness @@ -1616,45 +1620,92 @@ def add_layers(self, thicknesses, top_elevation = 0, justify = 'r', self.identify_layer_tops() def from_gmsh(self, filename, layers, convention = 0, atmos_type = 2, - top_elevation = 0, chars = ascii_lowercase, spaces = True, - block_order = None): + top_elevation = 0, justify = 'r', chars = ascii_lowercase, + spaces = True, block_order = None): """Returns a MULgraph grid constructed from a 2D gmsh grid and the specified layer structure.""" + justfn = [str.rjust, str.ljust][justify == 'l'] grid = mulgrid(type = 'GENER', convention = convention, atmos_type = atmos_type, block_order = block_order) grid.empty() + chars = uniqstring(chars) mode = 'r' if sys.version_info > (3,) else 'rU' gmsh = open(filename, mode) line = '' - chars = uniqstring(chars) - while not '$Nodes' in line: line = gmsh.readline() - num_nodes = int(gmsh.readline().strip()) - for i in range(num_nodes): + while not '$MeshFormat' in line: line = gmsh.readline() + line = gmsh.readline() + filetype = line.split(' ')[0] + gmsh.seek(0) + + def read_msh_2_2(): + line = '' + while not '$Nodes' in line: line = gmsh.readline() + num_nodes = int(gmsh.readline().strip()) + for i in range(num_nodes): + items = gmsh.readline().strip().split(' ') + name, x, y = items[0], float(items[1]), float(items[2]) + name = self.node_name_from_number(int(name), justfn, chars, spaces) + grid.add_node(node(name, np.array([x, y]))) + while not '$Elements' in line: line = gmsh.readline() + num_elements = int(gmsh.readline().strip()) + for i in range(num_elements): + items = gmsh.readline().strip().split(' ') + element_type = int(items[1]) + if element_type in [2, 3]: # triangle or quadrilateral + name = items[0] + name = self.column_name_from_number(int(name), justfn, chars, spaces) + ntags = int(items[2]) + colnodenumbers = [int(item) for item in items[3 + ntags:]] + colnodenames = [[self.node_name_from_number(nodeno, justfn, chars, spaces), + nodeno][convention in [1, 2]] for nodeno in colnodenumbers] + colnodes = [grid.node[v] for v in colnodenames] + grid.add_column(column(name, colnodes)) + + def read_msh_4_1(): + line = '' + while not '$Nodes' in line: line = gmsh.readline() items = gmsh.readline().strip().split(' ') - name, x, y = items[0], float(items[1]), float(items[2]) - name = self.node_name_from_number(int(name), chars = chars, spaces = spaces) - grid.add_node(node(name, np.array([x, y]))) - while not '$Elements' in line: line = gmsh.readline() - num_elements = int(gmsh.readline().strip()) - for i in range(num_elements): + num_entity_blocks, num_nodes = int(items[0]), int(items[1]) + for i in range(num_entity_blocks): + items = gmsh.readline().strip().split(' ') + num_block_nodes = int(items[-1]) + node_tags = [] + for inode in range(num_block_nodes): + node_tags.append(int(gmsh.readline())) + node_coords = [] + for inode in range(num_block_nodes): + items = gmsh.readline().strip().split(' ') + pos = [float(item) for item in items[:2]] + node_coords.append(pos) + for tag, pos in zip(node_tags, node_coords): + name = self.node_name_from_number(tag, justfn, chars, spaces) + grid.add_node(node(name, np.array(pos))) + while not '$Elements' in line: line = gmsh.readline() items = gmsh.readline().strip().split(' ') - element_type = int(items[1]) - if element_type in [2, 3]: # triangle or quadrilateral - name = items[0] - name = self.column_name_from_number(int(name), chars = chars, - spaces = True) - ntags = int(items[2]) - colnodenumbers = [int(item) for item in items[3 + ntags:]] - colnodenames = [[self.node_name_from_number(nodeno, - chars = chars, - spaces = spaces), - nodeno][convention > 0] for nodeno in colnodenumbers] - colnodes = [grid.node[v] for v in colnodenames] - grid.add_column(column(name, colnodes)) + num_entity_blocks, num_elements = int(items[0]), int(items[1]) + for i in range(num_entity_blocks): + items = gmsh.readline().strip().split(' ') + element_type, num_block_elements = int(items[2]), int(items[3]) + if element_type in [2, 3]: # triangle or quadrilateral + for ielt in range(num_block_elements): + items = gmsh.readline().strip().split(' ') + tag = items[0] + name = self.column_name_from_number(int(tag), justfn, chars, spaces) + colnodenumbers = [int(item) for item in items[1:]] + colnodenames = [[self.node_name_from_number(nodeno, justfn, chars, spaces), + nodeno][convention in [1, 2]] for nodeno in colnodenumbers] + colnodes = [grid.node[v] for v in colnodenames] + grid.add_column(column(name, colnodes)) + else: + for ielt in range(num_block_elements): gmsh.readline() + + if filetype == '2.2': read_msh_2_2() + elif filetype == '4.1': read_msh_4_1() + else: raise Exception('GMSH version %s not supported.' % filetype) gmsh.close() for con in grid.missing_connections: grid.add_connection(con) grid.delete_orphans() - grid.add_layers(layers, top_elevation, chars, spaces) + grid.add_layers(layers, top_elevation, justify, chars, spaces) grid.set_default_surface() grid.identify_neighbours() grid.setup_block_name_index() @@ -1952,117 +2003,41 @@ def column_track(self, line): line. """ - def furthest_intersection(poly, line): - """Returns furthest intersection point between line and poly.""" - pts, inds = line_polygon_intersections(poly, line, - bound_line = (True, False), - indices = True) - if pts: - d = np.array([np.linalg.norm(intpt - line[0]) for intpt in pts]) - i = np.argmax(d) - return pts[i], inds[i] - else: return None, None - - def find_track_start(line): - """Finds starting point for track- an arbitrary point on the line that is inside - the grid. If the start point of the line is inside the grid, that is used; - otherwise, a recursive bisection technique is used to find a point.""" - col, start_type = None, None - for endpt, name in zip(line, ['start', 'end']): - pos, col, start_type = endpt, self.column_containing_point(endpt), name - if col: break - if not col: # line ends are both outside the grid: - start_type = 'mid' - max_levels = 7 - - def find_start(line, level = 0): - midpt = 0.5 * (line[0] + line[1]) - col = self.column_containing_point(midpt) - if col: return midpt, col - else: - if level <= max_levels: - line0, line1 = [line[0], midpt], [midpt, line[1]] - pos, col = find_start(line0, level + 1) - if col: return pos, col - else: - pos, col = find_start(line1, level + 1) - if col: return pos, col - else: return None, None - else: return None, None - - pos, col = find_start(line) - return pos, col, start_type - - def next_corner_column(col, pos, more, cols): - """If the line has hit a node, determine a new column containing that node, - not already visited.""" - node_tol = 1.e-12 - nextcol = None - nearnodes = [n for n in col.node if np.linalg.norm(n.pos - pos) < node_tol] - if nearnodes: # hit a node - nearnode = nearnodes[0] - nearcols = nearnode.column - cols - if nearcols: nextcol = nearcols.pop() - else: more = False - return nextcol, more - - def next_neighbour_column(col, more, cols): - """Determine a new neighbour column not already visited.""" - nbrs = col.neighbour - cols - if nbrs: return nbrs.pop(), more - else: return None, False - - def find_track_segment(linesegment, pos, col): - """Finds track segment starting from the specified position and column.""" - track = [] - cols, more, inpos = set(), True, pos - colnbr, nextcol = col.neighbourlist, None - lined = np.linalg.norm(linesegment[1] - linesegment[0]) - while more: - cols.add(col) - outpos, ind = furthest_intersection(col.polygon, linesegment) - if outpos is not None: - d = np.linalg.norm(outpos - linesegment[0]) - if d >= lined: # gone past end of line - outpos = linesegment[1] - more = False - if np.linalg.norm(outpos - inpos) > 0.: - track.append(tuple([col, inpos, outpos])) - if more: # find next column - inpos = outpos - nextcol = colnbr[ind] - if nextcol: - if nextcol in cols: - nextcol, more = next_corner_column(col, outpos, more, cols) - if nextcol is None: - nextcol, more = next_neighbour_column(col, more, cols) - nbr_base_col = col - else: nextcol, more = next_corner_column(col, outpos, more, cols) - else: - nextcol, more = next_neighbour_column(nbr_base_col, more, cols) - col = nextcol - if col: colnbr = col.neighbourlist - else: more = False - - return track + tol = 1e-3 + def track_dist(p): return norm(p - line[0]) - def reverse_track(track): return [tuple([tk[0], tk[2], tk[1]]) for tk in track][::-1] - - pos, col, start_type = find_track_start(line) - if pos is not None and col: - if start_type == 'start': - track = find_track_segment(line, pos, col) - elif start_type == 'end': - track = find_track_segment(line[::-1], pos, col) - track = reverse_track(track) - else: - track1 = find_track_segment([pos, line[0]], pos, col) - track2 = find_track_segment([pos, line[1]], pos, col) - # remove arbitrary starting point from middle of track, and join: - midtk = tuple([track1[0][0], track1[0][2], track2[0][2]]) - track = reverse_track(track1)[:-1] + [midtk] + track2[1:] - return track - else: return [] + track, dist = [], [] + start_col, end_col = None, None + for col in self.columnlist: + bbox = col.bounding_box + if line_intersects_rectangle(bbox, line): + if start_col is None: + if col.contains_point(line[0]): + start_col = col + if end_col is None: + if col.contains_point(line[1]): + end_col = col + if col == start_col == end_col: + track.append((col, line[0], line[1])) + dist.append(0) + break + else: + poly = col.polygon + pts = line_polygon_intersections(poly, line) + if len(pts) > 0: + if col == start_col: + pts = [line[0], pts[-1]] + elif col == end_col: + pts = [pts[0], line[-1]] + din, dout = track_dist(pts[0]), track_dist(pts[-1]) + col_tol = max(col.side_lengths) * tol + if abs(dout - din) > col_tol: + track.append((col, pts[0], pts[-1])) + dist.append(din) + + sortindex = np.argsort(np.array(dist)) + track = [track[i] for i in sortindex] + return track def layer_plot_wells(self, plt, ax, layer, wells, well_names, hide_wells_outside, wellcolour, welllinewidth, wellname_bottom): @@ -2348,15 +2323,16 @@ def layer_plot(self, layer = 0, variable = None, variable_name = None, plt.ylim(plot_limits[1]) else: ax.autoscale_view() if contours is not False: - from matplotlib.mlab import griddata + from scipy.interpolate import griddata valc = np.array(vals) bds = self.bounds xgrid = np.linspace(bds[0][0], bds[1][0], contour_grid_divisions[0]) ygrid = np.linspace(bds[0][1], bds[1][1], contour_grid_divisions[1]) - valgrid = griddata(xc, yc, valc, xgrid, ygrid, interp = 'linear') + Xgrid, Ygrid = np.meshgrid(xgrid,ygrid) + valgrid = griddata((xc, yc), valc, (Xgrid, Ygrid), method = 'linear') if isinstance(contours, list): cvals = contours else: cvals = False - CS = plt.contour(xgrid, ygrid, valgrid, cvals, colors = 'k') + CS = plt.contour(Xgrid, Ygrid, valgrid, cvals, colors = 'k') if contour_label_format is not None: plt.clabel(CS, inline = 1, fmt = contour_label_format) plt.xlabel(xlabel) @@ -2644,15 +2620,16 @@ def slice_plot(self, line = None, variable = None, variable_name = None, col.norm.vmin, col.norm.vmax = tuple(colourbar_limits) ax.add_collection(col) if contours != False: - from matplotlib.mlab import griddata + from scipy.interpolate import griddata valc = np.array(vals) bds = ((np.min(xc), np.min(yc)), (np.max(xc), np.max(yc))) xgrid = np.linspace(bds[0][0], bds[1][0], contour_grid_divisions[0]) ygrid = np.linspace(bds[0][1], bds[1][1], contour_grid_divisions[1]) - valgrid = griddata(xc, yc, valc, xgrid, ygrid, interp = 'linear') + Xgrid, Ygrid = np.meshgrid(xgrid,ygrid) + valgrid = griddata((xc, yc), valc, (Xgrid, Ygrid), method = 'linear') if isinstance(contours, list): cvals = contours else: cvals = False - CS = plt.contour(xgrid, ygrid, valgrid, cvals, colors = 'k') + CS = plt.contour(Xgrid, Ygrid, valgrid, cvals, colors = 'k') if contour_label_format is not None: plt.clabel(CS, inline = 1, fmt = contour_label_format) ax.set_ylabel(ylabel) @@ -3542,6 +3519,40 @@ def write_mesh(self, filename, surface_snap = 0.1, dimension = 3, elif hasattr(meshio, 'write'): meshio.write(filename, points, cells, file_format = file_format) + def get_layermesh(self): + """Returns a layermesh mesh object representing the + geometry. Layermesh (https://github.com/acroucher/layermesh) + is a Python library for manipulating layer/column meshes. It + must be installed before this method will work. + """ + import layermesh.mesh as lm + + if self.block_order == 'dmplex': cell_type_sort = -1 + else: cell_type_sort = 0 + + m = lm.mesh(cell_type_sort = cell_type_sort) + + elevations = [self.layerlist[0].top] + \ + [lay.bottom for lay in self.layerlist[1:]] + m.set_layers(elevations) + + node_dict = {} + for i, mul_node in enumerate(self.nodelist): + lm_node = lm.node(pos = mul_node.pos, index = i) + m.add_node(lm_node) + node_dict[mul_node.name] = lm_node + + for i, mul_col in enumerate(self.columnlist): + nodes = [node_dict[mul_node.name] for mul_node in mul_col.node] + lm_col = lm.column(node = nodes, index = i) + m.add_column(lm_col) + lm_col.set_surface(m.layer, mul_col.surface) + + m.setup() + return m + + layermesh = property(get_layermesh) + def snap_columns_to_layers(self, min_thickness = 1.0, columns = []): """Snaps column surfaces to the bottom of their layers, if the surface block thickness is smaller than a given value. This can be @@ -3601,14 +3612,16 @@ def subdivide_column(self, column_name, i0, colnodelist, nodes = [centrenode if i == 'c' else col.node[col.index_plus(i0, i)] for i in colnodes] - name, colnumber = self.new_column_name(colnumber, justfn, chars) + name, colnumber = self.new_column_name(colnumber, justfn = justfn, + chars = chars, + spaces = spaces) self.add_column(column(name, nodes, surface = col.surface)) self.columnlist[-1].num_layers = col.num_layers newcolnames.append(name) self.delete_column(column_name) return newcolnames - def triangulate_column(self, column_name, replace = True, + def triangulate_column(self, column_name, chars = ascii_lowercase, spaces = True): """Replaces specified column with triangulated columns based on a new node at its centre, and returns list of new columns created. @@ -3937,8 +3950,8 @@ def transition_type(nn, sides): ((1, 2), 2, (2, 0)), ((0, 1), (1, 2), (2, 0)))}, 4: {(1, 0): ((0, (0, 1), 3), ((0, 1), 1, 2), ((0, 1), 2, 3)), - (2, 1): ((0, (0, 1), 'c'), ((0, 1), 1, 'c'), - (1, (1, 2), 'c'), ((1, 2), 2, 'c'), + (2, 1): ((0, (0, 1), 'c'), ((0, 1), 1, (1, 2), 'c'), + ((1, 2), 2, 'c'), (2, 3, 'c'), (0, 'c', 3)), (2, 2): ((0, (0, 1), (2, 3), 3), ((0, 1), 1, 2, (2, 3))), @@ -3965,7 +3978,7 @@ def transition_type(nn, sides): self.add_node(node(name, col.centre)) centrenodes[col.name] = self.nodelist[-1] for subcol in transition_column[nn][nrefined, irange]: - name, colnumber = self.new_column_name(colnumber, justfn, chars) + name, colnumber = self.new_column_name(colnumber, justfn, chars, spaces) nodes = [] for vert in subcol: if isinstance(vert, int): n = col.node[(istart + vert) % nn] @@ -4225,3 +4238,33 @@ def parse_segments(filename, bottom_layer): return geo, blockmap + def from_layermesh(self, mesh, convention = 0, atmosphere_type = 2, justify = 'r', + chars = ascii_lowercase, spaces = True, block_order = None): + """Creates a mulgrid geometry from a Layermesh mesh.""" + + justfn = [str.rjust, str.ljust][justify == 'l'] + geo = mulgrid(convention = convention, atmos_type = atmosphere_type, + block_order = block_order) + + for n in mesh.node: + name = geo.node_name_from_number(n.index + 1, justfn, chars, spaces) + newnode = node(name, n.pos) + geo.add_node(newnode) + + geo.add_layers([l.thickness for l in mesh.layer], mesh.layer[0].top, + justify, chars, spaces) + + for c in mesh.column: + name = geo.column_name_from_number(c.index + 1, justfn, chars, spaces) + colnodes = [geo.nodelist[n.index] for n in c.node] + newcol = column(name, colnodes, c.centre) + geo.add_column(newcol) + newcol.surface = c.surface + geo.set_column_num_layers(newcol) + + geo.identify_neighbours() + geo.check(fix = True, silent = True) + geo.setup_block_name_index() + geo.setup_block_connection_name_index() + + return geo diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..8d02172e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,42 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = "PyTOUGH" +version = "1.6.6" +description = "Python scripting library for TOUGH2 simulation" +readme = "README.md" +authors = [ + {name = "Adrian Croucher", email = "a.croucher@auckland.ac.nz"} +] +license = {file = "LICENSE"} +requires-python = ">=2.7" +dependencies = [ + "numpy", + "scipy", + "matplotlib" +] +classifiers = [ + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 3", + "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", + "Operating System :: OS Independent" +] + +[tool.setuptools] +py-modules = ["fixed_format_file", + "geometry", + "IAPWS97", + "mulgrids", + "t2data", + "t2grids", + "t2incons", + "t2listing", + "t2thermo" + ] + +[project.urls] +repository = "https://github.com/acroucher/PyTOUGH" + + diff --git a/setup.py b/setup.py index ff39e2d9..675ac062 100644 --- a/setup.py +++ b/setup.py @@ -1,21 +1,3 @@ -# PyTOUGH setup script -from distutils.core import setup - -setup(name = 'PyTOUGH', - version = '1.5.5', - description = 'Python scripting library for TOUGH2 simulation', - author = 'Adrian Croucher', - author_email = 'a.croucher@auckland.ac.nz', - url = 'https://github.com/acroucher/PyTOUGH', - license = 'LGPL', - py_modules = [ - 'fixed_format_file', - 'geometry', - 'IAPWS97', - 'mulgrids', - 't2data', - 't2grids', - 't2incons', - 't2listing', - 't2thermo'], - ) +from setuptools import setup +# for backwards compatibility only +setup() diff --git a/t2data.py b/t2data.py index 96d75a8f..baa1346a 100755 --- a/t2data.py +++ b/t2data.py @@ -18,21 +18,70 @@ from math import ceil import struct from os.path import splitext +from . import t2thermo -def primary_to_region_we(primary): - """Returns thermodynamic region deduced from primary variables for EOS we.""" - from .t2thermo import region - if primary[1] < 1.: return 4 - else: return region(primary[1], primary[0]) -def primary_to_region_wge(primary): - """Returns thermodynamic region deduced from primary variables for wge - (NCG) EOS (wce, wae).""" +def convert_primary_eos_1(primary): + """Returns Waiwera primary variables and thermodynamic region deduced + from primary variables for EOS 1.""" + if primary[1] < 1.5: return primary, 4 + else: return primary, t2thermo.region(primary[1], primary[0]) + +def convert_primary_eos_2_or_4(primary): + """Returns Waiwera primary variables and thermodynamic region deduced + from primary variables for EOS 2 or 4. + """ pwater = primary[0] - primary[2] - return primary_to_region_we([pwater, primary[1]]) + v, region = convert_primary_eos_1([pwater, primary[1]]) + return primary, region + +def convert_primary_eos_3(primary): + """Returns Waiwera primary variables and thermodynamic region deduced + from primary variables for EOS 3. + """ + ww, aw = 18.016, 28.96 + + def region_1_air_partial_pressure(Xa): + # air partial pressure from mass fraction in liquid phase + H = 1.e-10 + Xmol = Xa * ww / ((1. - Xa) * aw + Xa * ww) + return Xmol / H + + def region_2_air_partial_pressure(P, T, Xa, Pa): + # air partial pressure from total pressure, temperature, + # mass fraction and initial estimate, in vapour phase + from scipy.optimize import fsolve + R = 8314.56 + Tk = T + t2thermo.tc_k + def f(Pa, Tk, P, Xa): + return R * Tk * t2thermo.supst(T, P - Pa)[0] * Xa - Pa * (1. - Xa) * aw + Pa = fsolve(f, x0 = Pa, args = (Tk, P, Xa))[0] + Pa = max(Pa, 0) + return Pa + + if primary[1] < 1.5: # single-phase (P, X, T) + Xa, T = primary[1:] + Pa = region_1_air_partial_pressure(Xa) + P = primary[0] + Pw = P - Pa + if T <= 350: + Psat = t2thermo.sat(T) + region = 1 if Pw > Psat else 2 + else: + region = 2 + if region == 2: + Pa = region_2_air_partial_pressure(P, T, Xa, Pa) + variable = [P, T, Pa] + else: # two-phase (P, Sv + 10, T) + P, Sv10, T = primary[:] + Sv = Sv10 - 10. + Pw = t2thermo.sat(T) + Pa = P - Pw + variable = [P, Sv, Pa] + region = 4 + + return variable, region -primary_to_region_funcs = {'w': primary_to_region_we, 'we': primary_to_region_we, - 'wce': primary_to_region_wge, 'wae': primary_to_region_wge} waiwera_eos_num_primary = {'w': 1, 'we': 2, 'wce': 3, 'wae': 3} def trim_trailing_nones(vals): @@ -2073,10 +2122,19 @@ def mesh_json(self, geo, mesh_filename): c, s = cos(anglerad), sin(anglerad) rotation = np.array([[c, s], [-s, c]]) for blknames in geo.block_connection_name_list: - con = self.grid.connection[blknames] + if blknames in self.grid.connection: + names = blknames + con = self.grid.connection[blknames] + else: + rnames = blknames[::-1] + if rnames in self.grid.connection: + names = rnames + con = self.grid.connection[rnames] + else: + raise Exception ('Connection not found: ' + str(blknames)) blkindices = [geo.block_name_index[blkname] - - geo.num_atmosphere_blocks for blkname in blknames] - laynames = [geo.layer_name(blkname) for blkname in blknames] + geo.num_atmosphere_blocks for blkname in names] + laynames = [geo.layer_name(blkname) for blkname in names] if laynames[0] != laynames[1]: # vertical connection underground = all([blkindex >= 0 for blkindex in blkindices]) if underground and con.direction != 3: @@ -2084,7 +2142,7 @@ def mesh_json(self, geo, mesh_filename): "cells": blkindices, "permeability_direction": con.direction}) else: - colnames = [geo.column_name(blkname) for blkname in blknames] + colnames = [geo.column_name(blkname) for blkname in names] d = geo.column[colnames[1]].centre - geo.column[colnames[0]].centre d2 = np.dot(rotation, d) expected_direction = np.argmax(abs(d2)) + 1 @@ -2096,10 +2154,26 @@ def mesh_json(self, geo, mesh_filename): return jsondata def eos_json(self, eos): - """Converts TOUGH2 EOS data to Waiwera JSON dictionary.""" + """Converts TOUGH2 EOS data to Waiwera JSON dictionary. Also returns a + second dictionary with tracer data and the appropriate primary + variable conversion function for the EOS. + """ jsondata = {} - supported_eos = {'W': 'w', 'EW': 'we', 'EWC': 'wce', 'EWAV': 'wae'} + supported_eos = {'W': 'w', 'EW': 'we', 'EWC': 'wce', 'EWA': 'wae', + 'EWAV': 'wae', 'EWT': 'we', 'EWTD': 'we', + 'EWAX': 'wae', 'EWCX': 'wce'} + primary_converters = {'W': convert_primary_eos_1, + 'EW': convert_primary_eos_1, + 'EWC': convert_primary_eos_2_or_4, + 'EWA': convert_primary_eos_3, + 'EWAV': convert_primary_eos_2_or_4, + 'EWT': convert_primary_eos_1, + 'EWTD': convert_primary_eos_1, + 'EWAX': convert_primary_eos_2_or_4, + 'EWCX': convert_primary_eos_2_or_4 + } aut2eosname = '' + primary_converter = None if eos is None: if self.multi: if 'eos' in self.multi: @@ -2118,11 +2192,33 @@ def eos_json(self, eos): jsondata['eos'] = {'name': supported_eos[aut2eosname]} if jsondata['eos']['name'] == 'w': jsondata['eos']['temperature'] = self.parameter['default_incons'][1] + primary_converter = primary_converters[aut2eosname] + if aut2eosname == 'EWA': + if self.parameter['option'][19] > 0: + raise Exception ('EOS %s with MOP(19) > 0 not supported.' % \ + aut2eosname) + elif aut2eosname in ['EWAV', 'EWAX']: + if self.parameter['option'][19] == 1: + raise Exception ('EOS %s with MOP(19) = 1 not supported.' % \ + aut2eosname) + elif self.parameter['option'][19] == 2: + primary_converter = convert_primary_eos_3 else: - raise Exception ('EOS not supported:' + aut2eosname) + raise Exception ('EOS %s not supported.' % aut2eosname) else: raise Exception ('EOS not detected.') - return jsondata + if aut2eosname in ['EWT', 'EWTD']: + tracerdata = {'tracer': {'name': 'tracer', 'phase': 'liquid'}} + if aut2eosname == 'EWTD': + diffusion = np.array(self.diffusion) + if np.all(diffusion < 0) and np.allclose(diffusion, diffusion[0][0]): + D = -diffusion[0][0] + tracerdata['tracer']['diffusion'] = D + else: + raise Exception ('Unhandled diffusion type: %s' % str(self.diffusion)) + else: + tracerdata = None + return jsondata, tracerdata, primary_converter def timestepping_json(self): """Converts TOUGH2 timestepping/ iteration parameters to Waiwera JSON @@ -2144,8 +2240,11 @@ def timestepping_json(self): 'solver': {'nonlinear': {'tolerance': {'function': {'absolute': abstol, 'relative': reltol}}, 'maximum': {'iterations': maxit}}}} - if self.parameter['max_timesteps'] is not None and \ - self.parameter['max_timesteps'] >= 0: + if self.parameter['max_timesteps'] is None: + jsondata['time']['step']['maximum']['number'] = 0 + elif self.parameter['max_timesteps'] < 0: + jsondata['time']['step']['maximum']['number'] = None + else: jsondata['time']['step']['maximum']['number'] = self.parameter['max_timesteps'] if self.parameter['const_timestep'] < 0. : jsondata['time']['step'].update({'size': self.parameter['timestep'], @@ -2261,155 +2360,386 @@ def capillary_pressure_json(self): else: jsondata['capillary_pressure'] = None return jsondata - def initial_json(self, geo, incons, eos): + def initial_json(self, geo, incons, eos, primary_converter, tracer = None): """Converts initial condition specifications to Waiwera JSON dictionary.""" jsondata = {} + num_primary = waiwera_eos_num_primary[eos] + num_t2_primary = num_primary + 1 if eos == 'w' else num_primary if isinstance(incons, str): jsondata['initial'] = {'filename': incons} elif isinstance(incons, list): - num_primary = waiwera_eos_num_primary[eos] - jsondata['initial'] = {'primary': incons[:num_primary]} - if incons: - if eos in primary_to_region_funcs: - primary_to_region = primary_to_region_funcs[eos] - jsondata['initial']['region'] = primary_to_region(incons) - else: - raise Exception("Finding thermodynamic region from primary variables not yet supported for EOS:" + eos) + if len(incons) >= num_t2_primary: + primary, region = primary_converter(incons[:num_t2_primary]) + jsondata['initial'] = {'primary': primary[:num_primary], + 'region': region} + if tracer and len(incons) >= num_t2_primary + 1: + jsondata['initial']['tracer'] = incons[num_t2_primary] elif isinstance(incons, t2incon): - num_primary = waiwera_eos_num_primary[eos] - if eos in primary_to_region_funcs: - jsondata['initial'] = {'primary': [], 'region': []} - primary_to_region = primary_to_region_funcs[eos] - for blkname in geo.block_name_list[geo.num_atmosphere_blocks:]: - primary = incons[blkname].variable - jsondata['initial']['primary'].append(primary[:num_primary]) - jsondata['initial']['region'].append(primary_to_region(primary)) - if np.isclose(jsondata['initial']['primary'], - jsondata['initial']['primary'][0], rtol = 1.e-8).all(): - jsondata['initial']['primary'] = jsondata['initial']['primary'][0] - if len(set(jsondata['initial']['region'])) == 1: - jsondata['initial']['region'] = jsondata['initial']['region'][0] - else: - raise Exception("Finding thermodynamic region from primary variables not yet supported for EOS:" + eos) + jsondata['initial'] = {'primary': [], 'region': []} + if tracer: jsondata['initial']['tracer'] = [] + for blkname in geo.block_name_list[geo.num_atmosphere_blocks:]: + var = incons[blkname].variable + primary, region = primary_converter(var[:num_t2_primary]) + jsondata['initial']['primary'].append(primary[:num_primary]) + jsondata['initial']['region'].append(region) + if tracer: jsondata['initial']['tracer'].append(var[num_t2_primary]) + if np.isclose(jsondata['initial']['primary'], + jsondata['initial']['primary'][0], rtol = 1.e-8).all(): + jsondata['initial']['primary'] = jsondata['initial']['primary'][0] + if len(set(jsondata['initial']['region'])) == 1: + jsondata['initial']['region'] = jsondata['initial']['region'][0] + if tracer: + if np.isclose(jsondata['initial']['tracer'], + jsondata['initial']['tracer'][0], rtol = 1.e-8).all(): + jsondata['initial']['tracer'] = jsondata['initial']['tracer'][0] + else: + raise Exception("Inhomogeneous tracer initial conditions not yet supported.") return jsondata - def generators_json(self, geo, eosname): - """Converts TOUGH2 generator data to Waiwera JSON dictionary.""" + def generators_json(self, geo, eosname, tracer = None): + """Converts TOUGH2 generator data to Waiwera JSON dictionary, containing + data for sources and the source network.""" + jsondata = {} eos_num_equations = {'w': 1, 'we': 2, 'wce': 3, 'wae': 3} num_eqns = eos_num_equations[eosname] - unsupported_types = ['CO2 ', 'DMAK', 'FEED', 'FINJ', 'HLOS', 'IMAK', 'MAKE', - 'PINJ', 'POWR', 'RINJ', 'TMAK', 'TOST', 'VOL.', - 'WBRE', 'WFLO', 'XINJ', 'XIN2'] - mass_component = {'MASS': 1, 'MASD': 1, 'HEAT': num_eqns, - 'COM1': 1, 'COM2': 2, 'COM3': 3, 'COM4': 4, - 'COM5': 5, 'WATE': 1, 'AIR ': 2, 'TRAC': 2, 'NACL': 3} - limit_type = {'DELG': 'steam', 'DELS': 'steam', 'DELT': 'total', 'DELW': 'water'} + unsupported_types = {'CO2 ', 'FEED', 'HLOS', 'MAKE', 'POWR', + 'TOST', 'VOL.', 'WBRE', 'WFLO', 'XIN2'} + limit_type = {'DELG': 'steam', 'DMAK': 'steam', 'DELS': 'steam', + 'DELT': 'total', 'DELW': 'water', 'DMAT': 'total'} + reinjection_contributors = {'DELG', 'DELS', 'DELT', 'DELW', 'DELV', + 'DMAK', 'DMAT'} + used_names = {} + # prepend block names to generator names if generator names are not unique: + use_block_names = len(self.generator) < self.num_generators + + def unique_name(gen): + """Generates a unique generator name not already in used_names.""" + if gen.name == '': return gen.name + else: + if use_block_names: + name = '%5s%5s' % (gen.block, gen.name) + else: + name = gen.name + if name in used_names: + new_name = '%s_%d' % (name, used_names[name]) + used_names[name] += 1 + else: + new_name = name + used_names[name] = 1 + return new_name + + def separator(P): + if P is None: Psep = 0.55e6 + else: + if P > 0.: Psep = P + elif P < 0: Psep = [1.45e6, 0.55e6] + else: Psep = 0.55e6 + return {'pressure': Psep} + if self.parameter['option'][12] == 0: interp_type, averaging_type = "linear", "endpoint" elif self.parameter['option'][12] == 1: interp_type, averaging_type = "step", "endpoint" else: interp_type, averaging_type = "linear", "integrate" + + def generator_json(gen): + """Converts a single TOUGH2 generator to Waiwera JSON dictionary.""" + + mass_component = {'MASS': 1, 'MASD': 1, 'HEAT': num_eqns, + 'COM1': 1, 'COM2': 2, 'COM3': 3, 'COM4': 4, + 'COM5': 5, 'WATE': 1, 'AIR ': 2, 'TRAC': 2, 'NACL': 3} + + def specified_injection_generator_json(g, gen): + """Generators which inject at a specified rate.""" + if tracer and gen.type in ['COM2', 'TRAC']: + g['tracer'] = gen.gx + else: + g['rate'] = gen.gx + if gen.type == 'MASD': injection = False + else: + injection = gen.gx > 0. or \ + (gen.time and any([r > 0. for r in gen.rate])) + if injection: + g['component'] = mass_component[gen.type] + if gen.type != 'HEAT': g['enthalpy'] = gen.ex + else: + if gen.type == 'MASS': + g['separator'] = separator(gen.hg) + elif gen.type == 'MASD': + g['deliverability'] = {'productivity': gen.ex, + 'pressure': gen.fg, + 'threshold': gen.hg} + g['limiter'] = {'total': abs(gen.gx)} + g['separator'] = separator(gen.fg) + g['direction'] = 'production' + return g + + def delv_generator_json(g, gen): + """DELV generator type.""" + ltab = 0 if gen.ltab is None else gen.ltab + if ltab > 1: + raise Exception('DELV generator with multiple layers not supported.') + else: + g['deliverability'] = {'productivity': gen.gx, + 'pressure': gen.ex} + if gen.gx >= 0.: + g['direction'] = 'production' + g['separator'] = separator(gen.fg) + else: + g['direction'] = 'injection' + g['enthalpy'] = gen.fg + return g + + def geothermal_deliverability_generator_json(g, gen): + """Geothermal deliverability generator types - DELG etc.""" + g['deliverability'] = {'productivity': gen.gx, + 'pressure': gen.ex} + g['separator'] = separator(gen.fg) + if gen.hg is not None: + if gen.hg > 0.: + g['limiter'] = {limit_type[gen.type]: gen.hg} + elif gen.hg < 0. and gen.type in ['DELG', 'DMAK', 'DMAT']: + g['rate'] = gen.hg # initial rate for computing productivity index + del g['deliverability']['productivity'] + if gen.type == 'DELS': g['production_component'] = 2 + g['direction'] = 'production' + return g + + def recharge_generator_json(g, gen): + """Recharge generator type.""" + g['enthalpy'] = gen.ex + if (gen.hg is not None) and gen.hg != 0.: + rech = {} + g['direction'] = "both" + if gen.fg is not None: + if gen.fg < 0.: g['direction'] = "out" + elif gen.fg > 0.: g['direction'] = "in" + if gen.hg > 0.: rech['pressure'] = gen.hg + else: rech['pressure'] = 'initial' + rech['coefficient'] = gen.gx + g['recharge'] = rech + else: + g['rate'] = gen.gx + return g + + def injectivity_generator_json(g, gen): + """Generator types which inject against a pressure.""" + if gen.type == 'XINJ': g['enthalpy'] = gen.ex + g['direction'] = 'injection' + g['injectivity'] = {'pressure': gen.hg, + 'coefficient': abs(gen.fg)} + if gen.gx > 0: + g['limiter'] = {'total': gen.gx} + return g + + def table_generator_json(g, gen): + """Generators with tables of values vs. time.""" + g['interpolation'] = interp_type + g['averaging'] = averaging_type + data_table = [list(r) for r in zip(gen.time, gen.rate)] + if gen.type in ['DELG', 'DMAK', 'DMAT', 'DELT', 'DELW']: + ltab = 0 if gen.ltab is None else gen.ltab + if ltab > 0: + g['deliverability']['productivity'] = {'time': data_table} + else: + g['deliverability']['pressure'] = {'enthalpy': data_table} + elif tracer and gen.type in ['COM2', 'TRAC']: + g['tracer'] = data_table + else: + if gen.rate: g['rate'] = data_table + if gen.enthalpy: + g['enthalpy'] = [list(r) for r in zip(gen.time, gen.enthalpy)] + return g + + if gen.block in geo.block_name_index: + cell_index = geo.block_name_index[gen.block] - geo.num_atmosphere_blocks + if cell_index < 0: cell_index = None + else: + cell_index = None + g = {'name': unique_name(gen), 'cell': cell_index} + + if gen.type in mass_component: + g = specified_injection_generator_json(g, gen) + elif gen.type == 'DELV': + g = delv_generator_json(g, gen) + elif gen.type in ['DELG', 'DELS', 'DELT', 'DELW', 'DMAK', 'DMAT']: + g = geothermal_deliverability_generator_json(g, gen) + elif gen.type == 'RECH': + g = recharge_generator_json(g, gen) + elif gen.type in ['IMAK', 'XINJ']: + g = injectivity_generator_json(g, gen) + + if gen.time: + g = table_generator_json(g, gen) + return g + + def tmak_json(g, gen, itmak, makeup_inputs): + """TMAK (total makeup) group with limiter.""" + if gen.name.strip() == '': + g['name'] = 'makeup %d' % itmak + del g['cell'] + if gen.hg is None or gen.hg >= 0: + raise Exception('Unscaled TMAK not supported.') + elif gen.hg == -1: g['scaling'] = 'uniform' + else: g['scaling'] = 'progressive' + limiter = {} + if gen.gx: limiter['total'] = abs(gen.gx) + if gen.ex: limiter['steam'] = abs(gen.ex) + if limiter: g['limiter'] = limiter + g['in'] = makeup_inputs + return g + + def reinjector_output_type(gen): + """For FINJ, PINJ, RINJ and IMAK generators, returns water or steam + output type.""" + if gen.type in ['FINJ', 'PINJ', 'RINJ']: + output_type = 'water' if gen.hg > 0 else 'steam' + elif gen.type == 'IMAK': + output_type = 'water' if gen.fg > 0 else 'steam' + else: + raise Exception('Unrecognised reinjection generator type: %s' % gen.type) + return output_type + + def reinjector_output_json(g, gen): + """Returns JSON for reinjector output.""" + output = {'out': g['name']} + output['enthalpy'] = gen.ex + if gen.type == 'FINJ': + output['rate'] = gen.gx + elif gen.type in ['PINJ', 'RINJ']: + output['proportion'] = abs(gen.hg) + return output + + def has_outputs(reinjector): + """Returns true if JSON has non-empty 'water' or 'steam' properties.""" + return reinjector['water'] or reinjector['steam'] + + def prune_reinjector(reinjector): + """Deletes empty keys from reinjector.""" + for key in ['water', 'steam']: + if reinjector[key] == []: del reinjector[key] + return reinjector + + sources, groups, reinjectors = [], [], [] + makeup_inputs, group_inputs = [], [] + itmak, ireinjector = 1, 1 + reinjection = False + if self.generatorlist: - jsondata['source'] = [] for gen in self.generatorlist: + if gen.type in unsupported_types: raise Exception('Generator type ' + gen.type + ' not supported.') else: - cell_index = geo.block_name_index[gen.block] - geo.num_atmosphere_blocks - g = {'name': gen.name, 'cell': cell_index} - if gen.type in mass_component: - g['rate'] = gen.gx - if gen.gx > 0. or (gen.time and any([r > 0. for r in gen.rate])): - g['component'] = mass_component[gen.type] - if gen.type != 'HEAT': g['enthalpy'] = gen.ex - if gen.type == 'DELV': - if gen.ltab > 1: - raise Exception('DELV generator with multiple layers not supported.') - else: - g['deliverability'] = {'productivity': gen.gx, - 'pressure': gen.ex} - g['direction'] = 'production' - elif gen.type in ['DELG', 'DELS', 'DELT', 'DELW']: - g['deliverability'] = {'productivity': gen.gx, - 'pressure': gen.ex} - if gen.hg is not None: - if gen.hg > 0.: - g['limiter'] = {'type': limit_type[gen.type], 'limit': gen.hg} - if gen.type != 'DELT': - if gen.fg is not None: - if gen.fg > 0.: - g['limiter']['separator_pressure'] = gen.fg - elif gen.fg < 0.: - raise Exception('Two-stage flash separator not supported.') - elif gen.hg < 0. and gen.type == 'DELG': - g['rate'] = gen.hg # initial rate for computing productivity index - del g['deliverability']['productivity'] - if gen.type == 'DELS': g['production_component'] = 2 - g['direction'] = 'production' - elif gen.type == 'MASD': - g['deliverability'] = {'productivity': gen.ex, - 'pressure': gen.fg, - 'threshold': gen.hg} - g['direction'] = 'production' - elif gen.type == 'RECH': - g['enthalpy'] = gen.ex - if (gen.hg is not None) and gen.hg != 0.: - rech = {} - g['direction'] = "both" - if gen.fg is not None: - if gen.fg < 0.: g['direction'] = "out" - elif gen.fg > 0.: g['direction'] = "in" - if gen.hg > 0.: rech['pressure'] = gen.hg - else: rech['pressure'] = 'initial' - rech['coefficient'] = gen.gx - g['recharge'] = rech - else: - g['rate'] = gen.gx - if gen.time: - g['interpolation'] = interp_type - g['averaging'] = averaging_type - data_table = [list(r) for r in zip(gen.time, gen.rate)] - if gen.type in ['DELG', 'DELT', 'DELW']: - if gen.ltab > 0: - g['deliverability']['productivity'] = {'time': data_table} + + g = generator_json(gen) + if gen.type != 'TMAK': sources.append(g) + + if gen.type in ['DMAK', 'DMAT']: + makeup_inputs.append(g['name']) + elif gen.type in reinjection_contributors: + group_inputs.append(g['name']) + elif gen.type == 'TMAK': + tmak_subgroup = tmak_json(g, gen, itmak, makeup_inputs) + itmak += 1 + makeup_inputs = [] + groups.append(tmak_subgroup) + group_inputs.append(tmak_subgroup['name']) + elif gen.type in ['FINJ', 'PINJ', 'RINJ', 'IMAK']: + if not reinjection: + reinjection = True + if len(makeup_inputs) == 0 and len(group_inputs) == 1: + group_name = group_inputs[0] + reinjector_input_group = None else: - g['deliverability']['pressure'] = {'enthalpy': data_table} - else: - if gen.rate: g['rate'] = data_table - if gen.enthalpy: - g['enthalpy'] = [list(r) for r in zip(gen.time, gen.enthalpy)] - jsondata['source'].append(g) + group_name = 'reinjector group %d' % ireinjector + reinjector_input_group = {'name': group_name, + 'in': group_inputs + makeup_inputs} + name = 'reinjector %d' % ireinjector + reinjector = {'name': name, 'in': group_name, + 'water': [], 'steam': []} + overflow_outputs = {'water': [], 'steam': []} + if reinjection: + output_json = reinjector_output_json(g, gen) + output_type = reinjector_output_type(gen) + if gen.type == 'RINJ': + overflow_outputs[output_type].append(output_json) + else: + reinjector[output_type].append(output_json) + if gen.type in ['FINJ', 'PINJ', 'RINJ'] and gen.fg != 0: + outputs = has_outputs(reinjector) + overflow = has_outputs(overflow_outputs) + if outputs or overflow: + if reinjector_input_group: groups.append(reinjector_input_group) + reinjector = prune_reinjector(reinjector) + reinjectors.append(reinjector) + ireinjector += 1 + if overflow: + name = 'reinjector %d' % ireinjector + overflow_reinjector = {'name': name, + 'water': overflow_outputs['water'], + 'steam': overflow_outputs['steam']} + reinjectors.append(overflow_reinjector) + ireinjector += 1 + reinjector['overflow'] = overflow_reinjector['name'] + reinjection = False + makeup_inputs, group_inputs = [], [] + + if reinjection: + # end of generator list without a reinjection reset: + outputs = has_outputs(reinjector) + overflow = has_outputs(overflow_outputs) + if outputs or overflow: + if reinjector_input_group: groups.append(reinjector_input_group) + reinjectors.append(reinjector) + if overflow: + name = 'reinjector %d' % ireinjector + overflow_reinjector = {'name': name, + 'water': overflow_outputs['water'], + 'steam': overflow_outputs['steam']} + reinjector = prune_reinjector(reinjector) + reinjectors.append(overflow_reinjector) + reinjector['overflow'] = overflow_reinjector['name'] + + if sources: jsondata['source'] = sources + network = {} + if groups: network['group'] = groups + if reinjectors: network['reinject'] = reinjectors + if 'group' in network or 'reinject' in network: + jsondata['network'] = network return jsondata - def boundaries_json(self, geo, bdy_incons, atmos_volume, eos, mesh_coords): + def boundaries_json(self, geo, bdy_incons, atmos_volume, eos, primary_converter, + mesh_coords, tracer = None): """Converts Dirichlet boundary conditions to Waiwera JSON dictionary. Currently connections to boundary blocks that are not either horizontal or vertical will not be converted correctly. """ jsondata = {} vertical_tolerance = 1.e-6 - if eos in primary_to_region_funcs: - primary_to_region = primary_to_region_funcs[eos] - num_primary = waiwera_eos_num_primary[eos] - jsondata['boundaries'] = [] - for blk in self.grid.blocklist: - if not (0. < blk.volume < atmos_volume): - if isinstance(bdy_incons, t2incon): - pv = bdy_incons[blk.name].variable - else: - pv = bdy_incons - reg = primary_to_region(pv) - bc = {'primary': pv[:num_primary], 'region': reg, 'faces': []} - for conname in blk.connection_name: - nz = -self.grid.connection[conname].dircos - vertical_connection = abs(nz) > vertical_tolerance - names = list(conname) - names.remove(blk.name) - interior_blkname = names[0] - interior_blk = self.grid.block[interior_blkname] + num_primary = waiwera_eos_num_primary[eos] + num_t2_primary = num_primary + 1 if eos == 'w' else num_primary + jsondata['boundaries'] = [] + for blk in self.grid.blocklist: + if not (0. < blk.volume < atmos_volume): + if isinstance(bdy_incons, t2incon): + pv = bdy_incons[blk.name].variable + else: + pv = bdy_incons + primary, reg = primary_converter(pv[:num_t2_primary]) + bc = {'primary': primary[:num_primary], 'region': reg, 'faces': []} + if tracer: bc['tracer'] = pv[num_t2_primary] + for conname in blk.connection_name: + nz = -self.grid.connection[conname].dircos + vertical_connection = abs(nz) > vertical_tolerance + names = list(conname) + names.remove(blk.name) + interior_blkname = names[0] + interior_blk = self.grid.block[interior_blkname] + if 0. < interior_blk.volume < atmos_volume: cell_index = geo.block_name_index[interior_blkname] - geo.num_atmosphere_blocks if blk.centre is None: if vertical_connection: @@ -2429,24 +2759,29 @@ def boundaries_json(self, geo, bdy_incons, atmos_volume, eos, mesh_coords): if normal is not None: bc['faces'].append({"cells": [cell_index], "normal": list(normal)}) - normals = np.array([spec['normal'] for spec in bc['faces']]) - if np.isclose(normals, normals[0], rtol = 1.e-8).all(): - allcells = [] - for spec in bc['faces']: - allcells += spec['cells'] - bc['faces'] = {"cells": allcells, - "normal": bc['faces'][0]["normal"]} - if bc['faces']: - if isinstance(bc['faces'], list) and \ - len(bc['faces']) == 1: bc['faces'] = bc['faces'][0] - jsondata['boundaries'].append(bc) - - if jsondata['boundaries']: - # collapse down to one boundary if possible: - primaries = np.array([bc['primary'] for bc in jsondata['boundaries']]) - if np.isclose(primaries, primaries[0], rtol = 1.e-8).all(): - regions = np.array([bc['region'] for bc in jsondata['boundaries']]) - if np.isclose(regions, regions[0]).all(): + normals = np.array([spec['normal'] for spec in bc['faces']]) + if np.isclose(normals, normals[0], rtol = 1.e-8).all(): + allcells = [] + for spec in bc['faces']: + allcells += spec['cells'] + bc['faces'] = {"cells": allcells, + "normal": bc['faces'][0]["normal"]} + if bc['faces']: + if isinstance(bc['faces'], list) and \ + len(bc['faces']) == 1: bc['faces'] = bc['faces'][0] + jsondata['boundaries'].append(bc) + + if jsondata['boundaries']: + # collapse down to one boundary if possible: + primaries = np.array([bc['primary'] for bc in jsondata['boundaries']]) + if np.isclose(primaries, primaries[0], rtol = 1.e-8).all(): + regions = np.array([bc['region'] for bc in jsondata['boundaries']]) + if np.isclose(regions, regions[0]).all(): + if tracer: + tracers = np.array([bc['tracer'] for bc in jsondata['boundaries']]) + homog_tracer = np.isclose(tracers, tracers[0], rtol = 1.e-8).all() + else: homog_tracer = True + if homog_tracer: normals = [] for bc in jsondata['boundaries']: if isinstance(bc['faces'], dict): @@ -2469,8 +2804,8 @@ def boundaries_json(self, geo, bdy_incons, atmos_volume, eos, mesh_coords): jsondata['boundaries'] = [{"primary": primary, "region": region, "faces": {"normal": normal, "cells": allcells}}] - else: - raise Exception("Finding thermodynamic region from primary variables not yet supported for EOS:" + eos) + if tracer: + jsondata['boundaries'][0]['tracer'] = tracers[0] return jsondata def output_json(self): @@ -2522,7 +2857,7 @@ def output_json(self): return jsondata def json(self, geo, mesh_filename, atmos_volume = 1.e25, incons = None, - eos = None, bdy_incons = None, mesh_coords = 'xyz'): + eos = None, bdy_incons = None, mesh_coords = 'xyz'): """Returns a Waiwera JSON dictionary representing the t2data object (with associated mulgrid geometry).""" @@ -2531,7 +2866,9 @@ def json(self, geo, mesh_filename, atmos_volume = 1.e25, incons = None, jsondata['gravity'] = self.parameter['gravity'] jsondata['thermodynamics'] = 'ifc67' jsondata.update(self.mesh_json(geo, mesh_filename)) - jsondata.update(self.eos_json(eos)) + eos_data, tracer_data, primary_converter = self.eos_json(eos) + jsondata.update(eos_data) + if tracer_data: jsondata.update(tracer_data) jsondata.update(self.timestepping_json()) jsondata.update(self.output_json()) jsondata.update(self.rocks_json(geo, atmos_volume, mesh_coords)) @@ -2541,10 +2878,15 @@ def json(self, geo, mesh_filename, atmos_volume = 1.e25, incons = None, effective_incs = incons else: effective_incs = self.effective_incons(incons) - jsondata.update(self.initial_json(geo, effective_incs, jsondata['eos']['name'])) + jsondata.update(self.initial_json(geo, effective_incs, + jsondata['eos']['name'], + primary_converter, tracer_data)) if bdy_incons is None: bdy_incons = effective_incs jsondata.update(self.boundaries_json(geo, bdy_incons, atmos_volume, - jsondata['eos']['name'], mesh_coords)) - jsondata.update(self.generators_json(geo, jsondata['eos']['name'])) + jsondata['eos']['name'], + primary_converter, + mesh_coords, tracer_data)) + jsondata.update(self.generators_json(geo, jsondata['eos']['name'], + tracer_data)) return jsondata diff --git a/t2grids.py b/t2grids.py index 6d3ce3d6..5ebd6425 100755 --- a/t2grids.py +++ b/t2grids.py @@ -1038,7 +1038,6 @@ def minc(self, volume_fractions, spacing = 50., num_fracture_planes = 1, else: from scipy.optimize import bisect - from scipy.misc import derivative from numbers import Number volume_fractions = np.array(volume_fractions, dtype = float64) @@ -1106,6 +1105,9 @@ def inner_dist(x): volsum[-1] = 1. - delta xl, xr = 0., volume_fractions[1] / a[0] + def derivative(f, x, dx): + return (f(x + dx) - f(x - dx)) / (2 * dx) + for vs in volsum: xm, xr = invert_proximity(vs, xl, xr) if xm is None: diff --git a/t2incons.py b/t2incons.py index 6046b419..81961a1d 100755 --- a/t2incons.py +++ b/t2incons.py @@ -57,11 +57,12 @@ def __repr__(self): class t2incon(object): """Class for a set of initial conditions over a TOUGH2 grid.""" def __init__(self, filename = '', - read_function = fortran_read_function, num_variables = None): + read_function = fortran_read_function, num_variables = None, + check_blocknames = True): self.simulator = 'TOUGH2' self.read_function = read_function self.empty() - if filename: self.read(filename, num_variables) + if filename: self.read(filename, num_variables, check_blocknames) def __getitem__(self, key): if isinstance(key, (int, slice)): return self._blocklist[key] @@ -161,7 +162,7 @@ def delete_incon(self, block): del self._block[block] self._blocklist.remove(incon) - def read(self, filename, num_variables = None): + def read(self, filename, num_variables = None, check_blocknames = True): """Reads initial conditions from file.""" self.empty() mode = 'r' if sys.version_info > (3,) else 'rU' @@ -179,7 +180,8 @@ def read(self, filename, num_variables = None): line = padstring(line) [blkname, nseq, nadd, porosity, k1, k2, k3] = \ infile.parse_string(line, 'incon1_toughreact') - if valid_blockname(blkname): + valid = valid_blockname(blkname) if check_blocknames else True + if valid: blkname = fix_blockname(blkname) if (k1 is None or k2 is None or k3 is None): permeability = None else: diff --git a/t2listing.py b/t2listing.py index 63b660ee..21b9ca7e 100755 --- a/t2listing.py +++ b/t2listing.py @@ -305,7 +305,7 @@ def detect_simulator(self): line = self.readline().lower() ip = line.find('is a program for') if ip >= 0 and self.simulator is None: - self.simulator = line[:ip].strip().upper() + self.simulator = line[:ip].strip().split()[0].upper() if line != '' and self.simulator is None: self.skip_to_nonblank() line = self.readline() diff --git a/tests/listing/TOUGHREACT/2/case2.out b/tests/listing/TOUGHREACT/2/case2.out new file mode 100644 index 00000000..4bc48478 --- /dev/null +++ b/tests/listing/TOUGHREACT/2/case2.out @@ -0,0 +1,877 @@ + + -------- TOUGHREACT Version 3.32-OMP ------ + Lawrence Berkeley National Laboratory + February 8, 2017 + + ***** OpenMP Parallel Code Settings **** + *** maximum number of threads allotted = 1 + *** maximum number of threads available = 12 + + +--------------------------------------------------------------------------------------------------------------- + + + -- One or more of the following references are recommended to cite for publications including journal papers, + conference papers, reports using the TOUGHREACT simulator -- + + + For general approaches and methods: + ----------------------------------- + + Xu, T., and K. Pruess, Modeling multiphase non-isothermal fluid flow and reactive geochemical transport + in variably saturated fractured rocks: 1. Methodology, American Journal Science, v.301, p.16-33, 2001. + + Xu, T., E.L. Sonnenthal, N. Spycher, and K. Pruess, TOUGHREACT - A simulation program for non-isothermal + multiphase reactive geochemical transport in variably saturated geologic media: Applications to geothermal + injectivity and CO2 geological sequestration, Computers & Geosciences, v.32, p.145-165, 2006. + + Xu, T., N. Spycher, E.L. Sonnenthal, G. Zhang, L. Zheng, and K. Pruess, TOUGHREACT Version 2.0: + A simulator for subsurface reactive transport under non-isothermal multiphase flow conditions. + Computers & Geosciences, v.37, p.763-774, 2011. + + + For applications to CO2 geological sequestration: + ------------------------------------------------- + + Xu, T, J.A. Apps, and K. Pruess, Numerical simulation of CO2 disposal by mineral trapping in deep aquifers, + Applied Geochemistry, v.19, p.917-936, 2004. + + Xu, T, J. A. Apps, and K. Pruess, Mineral sequestration of carbon dioxide in a sandstone-shale system, + Chemical Geochemistry, v.217, p.295-318, 2005. + + Xu, T, J. A. Apps, K. Pruess and and H. YamamotoNumerical modeling of injection and mineral trapping of + CO2 with H2S and SO2 in a sandstone formation, Chemical Geochemistry, v.242, p.319-318, 2007. + + Aradóttir, E.S.P., E.L. Sonnenthal, G. Björnsson,& H. Jónsson, Multidimensional reactive transport modeling of CO2 mineral sequestration in basalts + at the Hellisheidi geothermal field, Iceland, International Journal of Greenhouse Gas Control, v.9, p.24-40, 2012. + + + For applications to nuclear waste geological disposal: + ------------------------------------------------------ + + Spycher N.F., E.L. Sonnenthal, and J.A. Apps, Fluid flow and reactive transport around potential nuclear + waste emplacement tunnels at Yucca Mountain, Nevada, Journal of Contaminant Hydrology, v.62-63, p.653-673, 2003. + + Dobson, P.F., T.J. Kneafsey, E.L. Sonnenthal, N.F. Spycher, and J.A. Apps, Experimental and numerical + simulation of dissolution and precipitation: Implications for fracture sealing at Yucca Mountain, Nevada, + Journal of Contaminant Hydrology. v.62-63, p.459-476, 2003. + + Sonnenthal E., A. Ito, N. Spycher, M. Yui, J. Apps, Y. Sugita, M. Conrad, and S. Kawakami, Approaches to + modeling coupled thermal, hydrological, and chemical processes in the Drift Scale Heater Test at Yucca Mountain, + International Journal of Rock Mechanics and Mining Sciences, v.42, p.698-719, 2005. + + Mukhopadhyay S., E.L. Sonnenthal, and N. Spycher, Modeling coupled thermal-hydrological-chemical processes + in the unsaturated fractured rock of Yucca Mountain, Nevada: Heterogeneity and seepage, Physics and + Chemistry of the Earth, v.31, p.626-633, 2006. + + + For applications to geothermal systems: + --------------------------------------- + + Xu, T., Y. Ontoy, P. Molling, N. Spycher, M. Parini, and K. Pruess, Reactive transport modeling of injection + well scaling and acidizing at Tiwi Field, Philippines, Geothermics, v.33, p.477-491, 2004. + + Dobson, P.F., S. Salah, N. Spycher, and E.L. Sonnenthal, Simulation of water-rock interaction + in the Yellowstone geothermal system using TOUGHREACT, Geothermics, v.33, p.493-502, 2004. + + + For applications to vadose zone reactive transport: + --------------------------------------------------- + + Xu T., E. Sonnenthal, and G.S. Bodvarsson, A reaction-transport model for calcite precipitation and evaluation + of infiltration fluxes in unsaturated fractured rock, Journal of Contaminant Hydrology, v.64, p.113-127, 2003. + + Singleton, M.J., E.L. Sonnenthal, M.E. Conrad, D.J, DePaolo, and G.W. Gee, Multiphase reactive transport + modeling of stable isotope fractionation in unsaturated zone pore water and vapor: Application to seasonal + infiltration events at the Hanford Site, WA, Vadose Zone Journal, v.3, p.775-785, 2004. + + Sonnenthal, E., T. Xu, and G. Bodvarsson, Reply to "Commentary: Assessment of past infiltration fluxes + through Yucca Mountain on the basis of the secondary mineral record - is it a viable methodology?" by + Y.V. Dublyansky and S.Z. Smirnov, Journal of Contaminant Hydrology, v.77, p.225-231, 2005. + + + For applications to biogeochemistry: + --------------------------------------- + + Xu, T., Incorporation of aqueous reaction kinetics and biodegradation into TOUGHREACT: Application + of a multi-region model to hydrobiogeochemical transport of denitrification and sulfate reduction, + Vadose Zone Journal, February issue, p.305-315, 2008 + + +************************************************************************************************************************** + + + + + + @@@@@ @@ @ @ @@@ @ @ @@ @@@ @ @ @ @ @ @ @@ @@@@@ @ @@ @ @ @@@ @ @ @ @ + @ @ @ @ @ @ @ @ @ @ @ @ @@ @@ @ @ @ @ @ @ @ @ @ @@ @ @ @ @ @ @@ @ + @ @ @ @ @ @ @@ @@@@ @ @@ @ @ @ @ @ @ @ @@@@ @ @ @ @ @ @ @ @@@ @ @ @ @ @ + @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @@ @ @ @ @ @ @@ + @ @@ @@ @@@ @ @ @@@@ @@@ @ @ @ @@ @@@@ @ @ @ @ @@ @ @ @ @ @@ @ @ + + + TOUGHREACT V3.32-OMP IS A PROGRAM FOR NONISOTHERMAL MULTIPHASE MULTICOMPONENT REACTIVE TRANSPORT IN PERMEABLE + MEDIA, DEVELOPED AT LAWRENCE BERKELEY NATIONAL LABORATORY. + + ******************************************************************************** + ******************* ******************** + ******************* TOUGHREACT V3.32-OMP (Feb 2017) ******************** + ******************* T2CG2 Solver Package ******************** + ******************* ******************** + ******************************************************************************** + + Copyright 2017 by The Regents of the University of California (subject to approval by the U.S. Department of Energy). + + NOTICE: This software was developed under funding from the U.S. Department of Energy and the U.S. Government consequently retains + certain rights as follows: the U.S. Government has been granted for itself and others acting on its behalf a paid-up, nonexclusive, + irrevocable, worldwide license in the software to reproduce, prepare derivative works, and perform publicly and display publicly. + Beginning five (5) years after the date permission to assert copyright is obtained from the U.S. Department of Energy, and subject + to any subsequent five (5) year renewals, the U.S. Government is granted for itself and others acting on its behalf a paid-up, + nonexclusive, irrevocable, worldwide license in the software to reproduce, prepare derivative works, distribute copies to the + public, perform publicly and display publicly, and to permit others to do so. + + + PARAMETERS FOR FLEXIBLE DIMENSIONING OF MAJOR ARRAYS (MAIN PROGRAM) ARE AS FOLLOWS + + MNEL = 200000 MNCON = 600000 MNEQ = 4 MNK = 3 MNPH = 3 MNB = 12 MNOGN = 2000 MGTAB = 40000 + =================================================================================================================================== + + MAXIMUM NUMBER OF VOLUME ELEMENTS (GRID BLOCKS): MNEL = 200000 + MAXIMUM NUMBER OF CONNECTIONS (INTERFACES): MNCON = 600000 + MAXIMUM LENGTH OF PRIMARY VARIABLE ARRAYS: MPRIM = 800000 + MAXIMUM NUMBER OF GENERATION ITEMS (SINKS/SOURCES): MNOGN = 2000 + MAXIMUM NUMBER OF TABULAR (TIME-DEPENDENT) GENERATION DATA: MGTAB = 40000 + LENGTH OF SECONDARY PARAMETER ARRAY: MSEC =47000000 + MAXIMUM NUMBER OF JACOBIAN MATRIX ELEMENTS: MNZ =22400000 + + LARGE LINEAR EQUATION ARRAYS: LENGTH OF IRN IS LIRN =22400000 + LENGTH OF ICN AND CO IS LICN =22400000 + + =================================================================================================================================== + + array dimensioning is made according to the needs of the conjugate gradient solvers + when using LUBAND, only a smaller-size problem can be accommodated + restriction with MA28 is: {number of elements} + 2 * {number of connections} < {MNEL + 2* MNCON}/4 + + =================================================================================================================================== + + + SUMMARY OF DISK FILES + + FILE *VERS* EXISTS --- OPEN AS AN OLD FILE + FILE *MESH* EXISTS --- OPEN AS AN OLD FILE + FILE *INCON* EXISTS --- OPEN AS AN OLD FILE + FILE *GENER* EXISTS --- OPEN AS AN OLD FILE + FILE *FIXPT* EXISTS --- OPEN AS AN OLD FILE + FILE *SAVE* EXISTS --- OPEN AS AN OLD FILE + FILE *LINEQ* EXISTS --- OPEN AS AN OLD FILE + FILE *TABLE* EXISTS --- OPEN AS AN OLD FILE + + =================================================================================================================================== + + PROBLEM TITLE: case 2 + + + + DOMAIN NO. 1 MATERIAL NAME -- dfalt + + DOMAIN NO. 2 MATERIAL NAME -- sand + + DOMAIN NO. 3 MATERIAL NAME -- atmos + + WRITE FILE *MESH* FROM INPUT DATA + + ************************************************************************************ + * EVALUATE FLOATING POINT ARITHMETIC * + ************************************************************************************ + * * + * FLOATING POINT PROCESSOR HAS APPROXIMATELY 15 SIGNIFICANT DIGITS * + * * + * DEFAULT VALUE OF INCREMENT FACTOR FOR NUMERICAL DERIVATIVES IS DFAC = 0.1490E-07 * + * DEFAULT VALUE FOR DFAC WILL BE USED * + * * + ************************************************************************************ + + +* The numbers of connections in the X-, Y- and Z-directions are 4760, 0 and 4800 respectively * +* * +********************************************************************************************************************************** + + + all NCON = 9560 connections read from file *MESH* reference known elements, and have been initialized to the data arrays + + + &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& + &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& Summary of capabilities for random permeability modification &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& + &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& + + Modification of absolute permeability on a grid block-by-grid block basis will be made when a domain "SEED " is present in data + block "ROCKS", as follows. + k ---> k' = k*m + + Here, k is the absolute permeability specified for the reservoir domain to which the grid block belongs. Parameter m is a + "permeability modifier" which can be internally generated or externally prescribed by the user on a block-by-block basis. + + When permeability modification is in effect, the strength of capillary pressure will, following Leverett (1941), automatically be + scaled as Pcap ---> Pcap' = Pcap/SQRT(m). + + User-supplied permeability modifiers have to be entered as parameter "PMX" in columns 41-50 of an ELEMEnt record. + Permeability modification options are selected through parameters in data block "ROCKS". + + &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& + Summary of available permeability modification options + (with s - random number between 0 and 1; PMX - user-supplied modifiers in data block "ELEME"): + (1) externally supplied: m = PMX - PER(2) + (2) "linear" (DROK.ne.0): m = PER(1) * s - PER(2) + (3) "logarithmic" (DROK.eq.0): m = exp(- PER(1) * s) - PER(2) + &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& + + &&&& if a domain "SEED " is present, permeability modification will be made + &&&& if no domain "SEED " is present, no permeability modification will be made + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + >>>>>>>>>>>>>>>>>>>>>>>>>>> domain = "SEED " is not present, no permeability modification will be made <<<<<<<<<<<<<<<<<<<<<<<<<< + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + Data provided in domain "SEED " are used to select the following options. + + DROK = *** random number seed for internal generation of "linear" permeability modifiers. + = 0: (default) no internal generation of "linear" permeability modifiers. + > 0: perform "linear" permeability modification; random modifiers are generated internally with DROK as seed. + + POR = *** random number seed for internal generation of "logarithmic" permeability modifiers, + = 0: (default) no internal generation of "logarithmic" permeability modifiers. + > 0: perform "logarithmic" permeability modification; random modifiers are generated internally with POR as seed. + + &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& + &&&&& note: if both DROK and POR are specified as non-zero, DROK takes precedence &&&&& + &&&&& if both DROK and POR are zero, permeability modifiers as supplied through "ELEME" data will be used &&&&& + &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& + + PER(1) = *** scale factor (optional) for internally generated permeability modifiers. + = 0: (defaults to PER(1) = 1): permeability modifiers are generated as random numbers in the interval (0, 1). + > 0: permeability modifiers are generated as random numbers in the interval (0, PER(1)). + + PER(2) = *** shift (optional) for internal or external permeability modifiers. + = 0: (default) no shift is applied to permeability modifiers. + > 0: permeability modifiers are shifted according to m' = m - PER(2). All m' < 0 are set equal to zero. + + + MESH HAS 4920 ELEMENTS ( 4920 ACTIVE) AND 9560 CONNECTIONS (INTERFACES) BETWEEN THEM + GENER HAS 0 SINKS/SOURCES + +********************************************************************************************************************************** +* * +* M A T R I X S O L V E R A N D R E L E V A N T I N F O R M A T I O N * +* * +********************************************************************************************************************************** + + The solver is determined from MOP(21) + + The solution method indicator MATSLV = 2 + MATSLV = 1: SUBROUTINE DSLUCS (DEFAULT) - Direct solver using LU decomposition + MATSLV = 2: SUBROUTINE DSLUBC - BI-CONJUGATE GRADIENT SOLVER + Incomplete LU factorization preconditioning + MATSLV = 3: SUBROUTINE DSLUCS (DEFAULT) - Lanczos-type Conjugate Gradient Squared solver + Incomplete LU factorization preconditioning + MATSLV = 4: SUBROUTINE DSLUGM - Generalized Minimum Residual Conjugate Gradient solver + Incomplete LU factorization preconditioning + MATSLV = 5: SUBROUTINE DLUSTB - STABILIZED BI-CONJUGATE GRADIENT SOLVER + Incomplete LU factorization preconditioning + MATSLV = 6: SUBROUTINE LUBAND - Direct solver using LU decomposition + + RITMAX: Maximum # of CG iterations as fraction of the total number of equations = 1.00000E-01 + (0.0 < RITMAX <= 1.0, Default = 0.1) + CLOSUR: Convergence criterion for the CG iterations = 1.00000E-06 + (1.0e-12 <= CLOSUR <= 1.0e-6, Default = 1.0e-6) + NMAXIT: Maximum # of CG iterations - not to exceed the total number of equations NELA*NEQ = 1476 + (20 < NMAXIT <= NREDM) + + + The matrix Z-preprocessing system is ZPROCS = Z1 + + ZPROCS = Z0: No Z-preprocessing; default for NEQ = 1 and for MATSLV = 6 + ZPROCS = Z1: Replacement of zeros on the main-diagonal by a small number; + default for NEQ > 1 and for 0 < MATSLV < 6 + ZPROCS = Z2: Linear combination of equations in each element to produce non-zero main diagonal entries + ZPROCS = Z3: Normalization of equations, followed by Z2 + ZPROCS = Z4: Same as in OPROCS = O4 + + + The matrix O-preprocessing system is OPROCS = O0 + + OPROCS = O0: No O-preprocessing; default for NEQ = 1 and for MATSLV = 6 + OPROCS = O1: Elimination of lower half of the main-diagonal submatrix with center pivoting + OPROCS = O2: O1+Elimination of upper half of the main-diagonal submatrix with center pivoting + OPROCS = O3: O2+Normalization - Results in unit main-diagonal submatrices + OPROCS = O4: pre-processing which results in unit main-diagonal submatrices without center pivoting + + + + END OF TOUGH2 INPUT JOB --- ELAPSED TIME = 0.3004 SECONDS + + + *********************************************************************************************************************************** + * ARRAY *MOP* ALLOWS TO GENERATE MORE PRINTOUT IN VARIOUS SUBROUTINES, AND TO MAKE SOME CALCULATIONAL CHOICES. * + *********************************************************************************************************************************** + + MOP(1) = 1 *** ALLOWS TO GENERATE A SHORT PRINTOUT FOR EACH NEWTON-RAPHSON ITERATION + = 0, 1, OR 2: GENERATE 0, 1, OR 2 LINES OF PRINTOUT + + MORE PRINTOUT IS GENERATED FOR MOP(I) > 0 IN THE FOLLOWING SUBROUTINES (THE LARGER MOP IS, THE MORE WILL BE PRINTED). + + MOP(2) = 0 *** CYCIT MOP(3) = 0 *** MULTI MOP(4) = 0 *** QU MOP(5) = 0 *** EOS MOP(6) = 0 *** LINEQ + MOP(8) = 0 *** DISF (T2DM ONLY) + + MOP(7) = 0 *** IF UNEQUAL ZERO, WILL GENERATE A PRINTOUT OF INPUT DATA + + CALCULATIONAL CHOICES OFFERED BY MOP ARE AS FOLLOWS: + + MOP(9) = 0 *** CHOOSES FLUID COMPOSITION ON WITHDRAWAL (PRODUCTION). + = 0: ACCORDING TO RELATIVE MOBILITIES. + = 1: ACCORDING TO COMPOSITION IN PRODUCING ELEMENT. + + MOP(10) = 0 *** CHOOSES INTERPOLATION FORMULA FOR DEPENDENCE OF THERMAL CONDUCTIVITY ON LIQUID SATURATION (SL). + = 0: K = KDRY + SQRT(SL)*(KWET-KDRY) + = 1: K = KDRY + SL*(KWET-KDRY) + + MOP(11) = 0 *** CHOOSES EVALUATION OF MOBILITY AND ABSOLUTE PERMEABILITY AT INTERFACES. + = 0: MOBILITIES ARE UPSTREAM WEIGHTED WITH WUP. (DEFAULT IS WUP = 1.0). PERMEABILITY IS UPSTREAM WEIGHTED. + = 1: MOBILITIES ARE AVERAGED BETWEEN ADJACENT ELEMENTS. PERMEABILITY IS UPSTREAM WEIGHTED. + = 2: MOBILITIES ARE UPSTREAM WEIGHTED WITH WUP. (DEFAULT IS WUP = 1.0). PERMEABILITY IS HARMONIC WEIGHTED. + = 3: MOBILITIES ARE AVERAGED BETWEEN ADJACENT ELEMENTS. PERMEABILITY IS HARMONIC WEIGHTED. + = 4: MOBILITY * PERMEABILITY PRODUCT IS HARMONIC WEIGHTED. + + MOP(12) = 1 *** CHOOSES PROCEDURE FOR INTERPOLATING GENERATION RATES FROM A TIME TABLE. + = 0: TRIPLE LINEAR INTERPOLATION. + = 1: "STEP FUNCTION" OPTION. + = 2: "RIGOROUS STEP FUNCTION" OPTION. + + MOP(13) = 0 *** T2DM ONLY. SPECIFIES ASSIGNMENT OF COMPONENTS OF BOUNDARY VELOCITY AND CONCENTRATION GRADIENT VECTORS. + AFFECTS ONLY THE INTERPOLATED COMPONENTS. DIRECT COMPONENTS ARE USED WHERE AVAILABLE. + = 0: VELOCITY AND GRADIENT AT BOUNDARY ARE ZERO. + = 1: VELOCITY IS ZERO; GRADIENT AT BOUNDARY IS NEAREST-NEIGHBOR. + = 2: VELOCITY IS NEAREST NEIGHBOR; GRADIENT AT BOUNDARY IS ZERO. + = 3: VELOCITY AND GRADIENT AT BOUNDARY ARE NEAREST-NEIGHBOR. + + MOP(14) = 0 *** SPECIFIES USE OF TEMPERATURE-DEPENDENT ROCK HEAT CAPACITY AND THERMAL CONDUCTIVITY + = 0: DO NOT USE TEMPERATURE-DEPENDENT ROCK HEAT CAPACITY AND THERMAL CONDUCTIVITY. + = 1: CALCULATE T-DEPENDENT HEAT CAPACITY. + = 2: CALCULATE T-DEPENDENT THERMAL CONDUCTIVITY + = 3: CALCULATE BOTH T-DEPENDENT Cp & k + + MOP(15) = 0 *** ALLOWS TO SELECT A SEMI-ANALYTICAL HEAT EXCHANGE CALCULATION WITH CONFINING BEDS. + = 0: NO SEMI-ANALYTICAL HEAT EXCHANGE + > 0: SEMI-ANALYTICAL HEAT EXCHANGE ENGAGED (WHEN A SPECIAL SUBROUTINE MODULE *QLOSS* IS PRESENT) + + MOP(16) = 4 *** PERMITS TO CHOOSE TIME STEP SELECTION OPTION + = 0: USE TIME STEPS EXPLICITLY PROVIDED AS INPUT. + > 0: INCREASE TIME STEP BY AT LEAST A FACTOR 2, IF CONVERGENCE OCCURS IN .LE. MOP(16) ITERATIONS. + + MOP(17) = 0 *** Wellbore Permeability Model + = 0: Wellbore Permeability Model Off + = 1: Wellbore Perm Model On (ISOX=5): Mean Wt + = 2: Wellbore Perm Model On (ISOX=5): Geom Mean Wt + + MOP(18) = 0 *** ALLOWS TO SELECT HANDLING OF INTERFACE DENSITY. + = 0: PERFORM UPSTREAM WEIGHTING FOR INTERFACE DENSITY. + > 0: COMPUTE INTERFACE DENSITY AS AVERAGE OF THE TWO GRID BLOCK DENSITIES. + HOWEVER, WHEN ONE OF THE TWO PHASE SATURATIONS IS ZERO, DO UPSTREAM WEIGHTING. + + MOP(19) = 0 *** is used in some EOS-modules for selecting different options + + MOP(20) = 0 *** is used in some EOS-modules for selecting different options + + MOP(21) = 2 *** PERMITS TO SELECT LINEAR EQUATION SOLVER + = 0: DEFAULTS TO MOP(21) = 3 + = 1: DEFAULTS TO MOP(21) = 3 + = 2: SUBROUTINE DSLUBC: BI-CONJUGATE GRADIENT SOLVER; PRECONDITIONER: INCOMPLETE LU FACTORIZATION + = 3: SUBROUTINE DSLUCS: BI-CONJUGATE GRADIENT SOLVER - LANCZOS TYPE; PRECONDITIONER: INCOMPLETE LU FACTORIZATION + = 4: SUBROUTINE DSLUGM: GENERALIZED MINIMUM RESIDUAL CONJUGATE GRADIENTS; PRECONDITIONER: INCOMPLETE LU FACTORIZATION + = 5: SUBROUTINE DLUSTB: Stabilized bi-conjugate gradient solver; PRECONDITIONER: INCOMPLETE LU FACTORIZATION + = 6: SUBROUTINE LUBAND: Direct solver using LU decomposition + + + MOP(22) = 0 *** T2DM ONLY. SPECIFIES METHOD OF SUMMATION OF DUPLICATE ELEMENTS IN THE JACOBIAN. *** + = 0: USE SUMDUP2. + = 1: USE SUMDUP. + = 2: + + MOP(23) = 0 *** T2DM ONLY. HANDLES EFFECTS OF NON-CONNECTED NEIGHBOR GRID BLOCKS ON DISPERSIVE FLUX IN DISF. *** + = 0: INCLUDE INFLUENCE OF NEIGHBOR GRID BLOCKS. (MORE ACCURATE JACOBIAN, SLOWER LINEAR EQUATION SOLUTION). + = 1: NEGLECT INFLUENCE OF NEIGHBOR GRID BLOCKS. (LESS ACCURATE JACOBIAN, FASTER LINEAR EQUATION SOLUTION). + + + MOP(24) = 0 *** PERMITS TO SELECT HANDLING OF MULTIPHASE DIFFUSIVE FLUXES AT INTERFACES + = 0: HARMONIC WEIGHTING OF FULLY-COUPLED EFFECTIVE MULTIPHASE DIFFUSIVITY + = 1: SEPARATE HARMONIC WEIGHTING OF GAS AND LIQUID PHASE DIFFUSIVITIES. + + *********************************************************************************************************************************** + + *********************************************************************************************************************************** + * EWASG: EQUATION OF STATE FOR MIXTURES OF WATER/SODIUM CHLORIDE/NCG WITH VAPOR PRESSURE LOWERING CAPABILITY * + *********************************************************************************************************************************** + + OPTIONS SELECTED ARE: (NK,NEQ,NPH,NB) = (3,3,3, 6) + + NK = 3 - NUMBER OF FLUID COMPONENTS + NEQ = 3 - NUMBER OF EQUATIONS PER GRID BLOCK + NPH = 3 - NUMBER OF PHASES THAT CAN BE PRESENT + NB = 6 - NUMBER OF SECONDARY PARAMETERS (OTHER THAN COMPONENT MASS FRACTIONS) + + For NB = 6, diffusion is "off", for NB = 8, diffusion is "on" + + + AVAILABLE OPTIONS: (NK,NEQ,NPH,NB)=(3,4,3,6 or 8) -WATER, NaCl, NCG; NON-ISOTHERMAL; VAR. (P, XS OR SS+10, X3 OR SG+10, T) + (3,3,3,6 or 8) -WATER, NaCl, NCG; ISOTHERMAL; VAR. (P, XS OR SS+10, X3 OR SG+10, T) + (2,3,3,6 or 8) -WATER, NaCl, NO NCG; NON-ISOTHERMAL; VAR. (P, XS OR SS+10, T) + (2,2,3,6 or 8) -WATER, NaCl, NO NCG; ISOTHERMAL; VAR. (P, XS OR SS+10, T) + + Default options are (2,3,2,6) - non-isothermal, diffusion "off" + + THE NK = 2 ("NO NCG") OPTIONS MAY ONLY BE USED FOR PROBLEMS WITH SINGLE-PHASE LIQUID CONDITIONS THROUGHOUT + + *********************************************************************************************************************************** + + + THE PRIMARY VARIABLES ARE + P - PRESSURE XSM - SALT MASS FRACTION IN LIQUID (XS) OR SOLID SATURATION (SS+10.) + X3 - NCG MASS FRACTION OR SG+10. - GAS PHASE SATURATION + 10. T - TEMPERATURE + + ****************************** ********************************************************* + * COMPONENTS * * FLUID PHASE CONDITION PRIMARY VARIABLES * + ****************************** ********************************************************* + * * * * + * # 1 - WATER * * SINGLE-PHASE GAS P, XSM, X3, T * + * * * * + * # 2 - NaCl * * SINGLE-PHASE LIQUID P, XSM, X3, T * + * * * * + * # 3 - NCG * * TWO-PHASE P, XSM, SG+10., T * + * * * * + * # 4 - HEAT * ********************************************************* + * * + ****************************** + + *********************************************************************************************************************************** + * ARRAY *IE* ALLOWS TO MAKE CHOICES AMONG DIFFERENT EOS OPTIONS * + *********************************************************************************************************************************** + + IE(1) = 1: *** NUMBER OF ADDITIONAL RECORDS READ IN DATA BLOCK SELEC. + + IE(10) = 0: *** ALLOWS TO TURN VAPOR PRESSURE LOWERING OFF/ON. + = 0: VAPOR PRESSURE LOWERING IS OFF. + = 1: VAPOR PRESSURE LOWERING IS ON. + + IE(11) = 0: *** ALLOWS CHOICE OF DEPENDENCE OF PERMEABILITY ON ACTIVE FLOW POROSITY. + = 0: PERMEABILITY DOES NOT VARY WITH FLOW POROSITY. + = 1: PERMEABILITY VARIES AS PHIF**FE(1). + = 2: SERIES FRACTURE MODEL. + = 3: SERIES TUBE MODEL. + + IE(14) = 0: *** ALLOWS CHOICE OF TREATMENT OF THERMOPHYSICAL PROPERTIES DEPENDENCE ON SALT CONTENT. + = 0: FULL DEPENDENCE (DEFAULT). + = 1: SUPPRESSION OF VAPOR PRESSURE DEPENDENCE. + = 2: SUPPRESSION OF VAPOR PRESSURE AND ENTHALPY DEPENDENCE. + = 3: SUPPRESSION OF ALL THERMOPHYSICAL PROPERTIES DEPENDENCE OTHER THAN SALT SOLUBILITY. + + IE(15) = 0: *** ALLOWS CHOICE OF CORRELATION FOR VAPOR SATURATED BRINE ENTHALPY. + = 0: MICHAELIDES (1981). + = 1: MILLER (1978). + + IE(16) = 2: *** ALLOWS CHOICE OF TYPE OF NCG. + = 1: AIR. + = 2: CO2 (DEFAULT). + = 3: CH4. + = 4: H2. + = 5: N2. + + *********************************************************************************************************************************** + + ..... NEGLECT VAPOR PRESSURE LOWERING ..... + + *********************************************************************************************************************************** + + + ********** VOLUME- AND MASS-BALANCES ********************************************************************************************* + + ********** [KCYC,ITER] = [ 0, 0] ***** THE TIME IS 0.00000E+00 SECONDS, OR 0.00000E+00 DAYS + + + PHASES PRESENT COMPONENT MASS IN PLACE (kg) + ********************************************************* ************************************************************************ + PHASES * GAS LIQUID SOLID COMPONENTS * WATER SALT CO2 HEAT + ********************************************************* ************************************************************************ + * PHASES * + VOL. (m^3) * 0.00000000E+00 0.36000000E+27 0.00000000E+00 GAS PHASE * 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 + MASS (kg) * 0.00000000E+00 0.35897432E+30 0.00000000E+00 LIQUID * 0.35897432E+30 0.00000000E+00 0.00000000E+00 0.37604715E+35 + ********************************************************* SOLID SALT * 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 + ADSORB-ROCK* 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.50232000E+35 + TOTAL * 0.35897432E+30 0.00000000E+00 0.00000000E+00 0.87836715E+35 + ************************************************************************ + + ********************************************************************************************************************************** + + + a40( 1, 1) ST = 1.000000E+00 DT = 1.000000E+00 DX1= 0.00000E+00 DX2= 0.00000E+00 T = 25.000 P = 101300. S = 0.000000E+00 + + case 2 + + OUTPUT DATA AFTER ( 1, 1)-2-TIME STEPS THE TIME IS 0.115741E-04 DAYS + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + TOTAL TIME KCYC ITER ITERC KON DX1M DX2M DX3M MAX. RES. NER KER DELTEX + 0.100000E+01 1 1 1 2 0.00000E+00 0.00000E+00 0.00000E+00 0.73212E-07 3955 1 0.10000E+01 + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + ELEM. IND. P T SG SL SS XNACL XCO2G XCO2L PCAP k-red. DL + (Pa) (deg.C) (Pa) (kg/m3) + + ah 2 1 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + ai 2 2 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + aj 2 3 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + ak 2 4 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + c 3 5 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + d 3 6 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + e 3 7 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + f 3 8 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + i 3 9 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + j 3 10 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + af 3 11 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + ag 3 12 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + ah 3 13 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + ai 3 14 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + aj 3 15 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + ak 3 16 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + al 3 17 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + am 3 18 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + an 3 19 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + a 4 20 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + b 4 21 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + c 4 22 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + d 4 23 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + e 4 24 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + f 4 25 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + g 4 26 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + h 4 27 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + i 4 28 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + j 4 29 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + k 4 30 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + ae 4 31 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + af 4 32 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + ag 4 33 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + ah 4 34 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + ai 4 35 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + aj 4 36 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + ak 4 37 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + al 4 38 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + am 4 39 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + an 4 40 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + df 4 41 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + dg 4 42 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + a 5 43 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + b 5 44 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + + ELEM. IND. P T SG SL SS XNACL XCO2G XCO2L PCAP k-red. DL + (Pa) (deg.C) (Pa) (kg/m3) + + c 5 45 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + d 5 46 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + e 5 47 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + f 5 48 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + g 5 49 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + h 5 50 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + i 5 51 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + j 5 52 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + k 5 53 0.10130E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.2 + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + a40( 2, 1) ST = 3.000000E+00 DT = 2.000000E+00 DX1= 0.00000E+00 DX2= 0.00000E+00 T = 25.000 P = 101300. S = 0.000000E+00 + a40( 3, 1) ST = 7.000000E+00 DT = 4.000000E+00 DX1= 0.00000E+00 DX2= 0.00000E+00 T = 25.000 P = 101300. S = 0.000000E+00 + a40( 4, 1) ST = 1.500000E+01 DT = 8.000000E+00 DX1= 0.00000E+00 DX2= 0.00000E+00 T = 25.000 P = 101300. S = 0.000000E+00 + a40( 5, 1) ST = 3.100000E+01 DT = 1.600000E+01 DX1= 0.00000E+00 DX2= 0.00000E+00 T = 25.000 P = 101300. S = 0.000000E+00 + a40( 6, 1) ST = 6.300000E+01 DT = 3.200000E+01 DX1= 0.00000E+00 DX2= 0.00000E+00 T = 25.000 P = 101300. S = 0.000000E+00 + a40( 7, 1) ST = 1.270000E+02 DT = 6.400000E+01 DX1= 0.00000E+00 DX2= 0.00000E+00 T = 25.000 P = 101300. S = 0.000000E+00 + a40( 8, 1) ST = 2.550000E+02 DT = 1.280000E+02 DX1= 0.00000E+00 DX2= 0.00000E+00 T = 25.000 P = 101300. S = 0.000000E+00 + ...ITERATING... AT [ 9, 1] --- DELTEX = 2.560000E+02 MAX. RES. = 1.874228E-05 AT ELEMENT a40 EQUATION 1 + do40( 9, 2) ST = 5.110000E+02 DT = 2.560000E+02 DX1= 4.82135E+04 DX2= 0.00000E+00 T = 25.000 P = 149513. S = 0.000000E+00 + ...ITERATING... AT [ 10, 1] --- DELTEX = 5.120000E+02 MAX. RES. = 3.412087E-05 AT ELEMENT dp40 EQUATION 1 + do39( 10, 2) ST = 1.023000E+03 DT = 5.120000E+02 DX1= 1.90606E+04 DX2= 0.00000E+00 T = 25.000 P = 124686. S = 0.000000E+00 + ...ITERATING... AT [ 11, 1] --- DELTEX = 1.024000E+03 MAX. RES. = 5.849454E-05 AT ELEMENT do40 EQUATION 1 + dh39( 11, 2) ST = 2.047000E+03 DT = 1.024000E+03 DX1= 5.51685E+04 DX2= 0.00000E+00 T = 25.000 P = 179846. S = 0.000000E+00 + ...ITERATING... AT [ 12, 1] --- DELTEX = 2.048000E+03 MAX. RES. = 9.344016E-05 AT ELEMENT dj40 EQUATION 1 + dn40( 12, 2) ST = 4.095000E+03 DT = 2.048000E+03 DX1= 1.98250E+05 DX2= 0.00000E+00 T = 25.000 P = 562428. S = 0.000000E+00 + ...ITERATING... AT [ 13, 1] --- DELTEX = 4.096000E+03 MAX. RES. = 1.403055E-04 AT ELEMENT e40 EQUATION 1 + o40( 13, 2) ST = 8.191000E+03 DT = 4.096000E+03 DX1= 2.86686E+05 DX2= 0.00000E+00 T = 25.000 P = 849113. S = 0.000000E+00 + ...ITERATING... AT [ 14, 1] --- DELTEX = 8.192000E+03 MAX. RES. = 2.029147E-04 AT ELEMENT dp40 EQUATION 1 + cr40( 14, 2) ST = 1.638300E+04 DT = 8.192000E+03 DX1= 4.08229E+05 DX2= 0.00000E+00 T = 25.000 P = 1257344. S = 0.000000E+00 + ...ITERATING... AT [ 15, 1] --- DELTEX = 1.638400E+04 MAX. RES. = 2.889438E-04 AT ELEMENT a40 EQUATION 1 + bi40( 15, 2) ST = 3.276700E+04 DT = 1.638400E+04 DX1= 5.78591E+05 DX2= 0.00000E+00 T = 25.000 P = 1835940. S = 0.000000E+00 + ...ITERATING... AT [ 16, 1] --- DELTEX = 3.276800E+04 MAX. RES. = 4.095334E-04 AT ELEMENT n40 EQUATION 1 + cr40( 16, 2) ST = 6.553500E+04 DT = 3.276800E+04 DX1= 8.18998E+05 DX2= 0.00000E+00 T = 25.000 P = 2654926. S = 0.000000E+00 + ...ITERATING... AT [ 17, 1] --- DELTEX = 6.553600E+04 MAX. RES. = 5.797261E-04 AT ELEMENT ay40 EQUATION 1 + dm40( 17, 2) ST = 1.310710E+05 DT = 6.553600E+04 DX1= 1.15887E+06 DX2= 0.00000E+00 T = 25.000 P = 3813795. S = 0.000000E+00 + ...ITERATING... AT [ 18, 1] --- DELTEX = 1.310720E+05 MAX. RES. = 8.203689E-04 AT ELEMENT cv40 EQUATION 1 + aq40( 18, 2) ST = 2.621430E+05 DT = 1.310720E+05 DX1= 1.63915E+06 DX2= 0.00000E+00 T = 25.000 P = 5452945. S = 0.000000E+00 + ...ITERATING... AT [ 19, 1] --- DELTEX = 2.621440E+05 MAX. RES. = 1.160508E-03 AT ELEMENT ax40 EQUATION 1 + cz40( 19, 2) ST = 5.242870E+05 DT = 2.621440E+05 DX1= 2.30328E+06 DX2= 0.00000E+00 T = 25.000 P = 7756218. S = 0.000000E+00 + ...ITERATING... AT [ 20, 1] --- DELTEX = 5.242880E+05 MAX. RES. = 1.631000E-03 AT ELEMENT da40 EQUATION 1 + ad39( 20, 2) ST = 1.048575E+06 DT = 5.242880E+05 DX1= 3.07751E+06 DX2= 0.00000E+00 T = 25.000 P = 10364091. S = 0.000000E+00 + ...ITERATING... AT [ 21, 1] --- DELTEX = 1.048576E+06 MAX. RES. = 2.185059E-03 AT ELEMENT ad40 EQUATION 1 + da39( 21, 2) ST = 2.097151E+06 DT = 1.048576E+06 DX1= 3.51832E+06 DX2= 0.00000E+00 T = 25.000 P = 13882404. S = 0.000000E+00 + ...ITERATING... AT [ 22, 1] --- DELTEX = 2.097152E+06 MAX. RES. = 2.497709E-03 AT ELEMENT bt40 EQUATION 1 + d39( 22, 2) ST = 4.194303E+06 DT = 2.097152E+06 DX1= 2.94868E+06 DX2= 0.00000E+00 T = 25.000 P = 16831086. S = 0.000000E+00 + ...ITERATING... AT [ 23, 1] --- DELTEX = 4.194304E+06 MAX. RES. = 2.094766E-03 AT ELEMENT dl40 EQUATION 1 + e39( 23, 2) ST = 8.388607E+06 DT = 4.194304E+06 DX1= 1.58165E+06 DX2= 0.00000E+00 T = 25.000 P = 18412735. S = 0.000000E+00 + ...ITERATING... AT [ 24, 1] --- DELTEX = 8.388608E+06 MAX. RES. = 1.125305E-03 AT ELEMENT cb40 EQUATION 1 + bd38( 24, 2) ST = 1.677722E+07 DT = 8.388608E+06 DX1= 4.89968E+05 DX2= 0.00000E+00 T = 25.000 P = 18412238. S = 0.000000E+00 + ...ITERATING... AT [ 25, 1] --- DELTEX = 1.475878E+07 MAX. RES. = 3.081921E-04 AT ELEMENT bm40 EQUATION 1 + bd37( 25, 2) ST = 3.153600E+07 DT = 1.475878E+07 DX1= 8.13557E+04 DX2= 0.00000E+00 T = 25.000 P = 18001761. S = 0.000000E+00 + + ********** VOLUME- AND MASS-BALANCES ********************************************************************************************* + + ********** [KCYC,ITER] = [ 25, 2] ***** THE TIME IS 3.15360E+07 SECONDS, OR 3.65000E+02 DAYS + + + PHASES PRESENT COMPONENT MASS IN PLACE (kg) + ********************************************************* ************************************************************************ + PHASES * GAS LIQUID SOLID COMPONENTS * WATER SALT CO2 HEAT + ********************************************************* ************************************************************************ + * PHASES * + VOL. (m^3) * 0.00000000E+00 0.36000000E+27 0.00000000E+00 GAS PHASE * 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 + MASS (kg) * 0.00000000E+00 0.35897432E+30 0.00000000E+00 LIQUID * 0.35897432E+30 0.00000000E+00 0.00000000E+00 0.37604715E+35 + ********************************************************* SOLID SALT * 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 + ADSORB-ROCK* 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.50232000E+35 + TOTAL * 0.35897432E+30 0.00000000E+00 0.00000000E+00 0.87836715E+35 + ************************************************************************ + + ********************************************************************************************************************************** + + + + case 2 + + OUTPUT DATA AFTER ( 25, 2)-2-TIME STEPS THE TIME IS 0.365000E+03 DAYS + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + TOTAL TIME KCYC ITER ITERC KON DX1M DX2M DX3M MAX. RES. NER KER DELTEX + 0.315360E+08 25 2 42 2 0.82153E+05 0.00000E+00 0.00000E+00 0.16590E-07 3650 1 0.14759E+08 + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + ELEM. IND. P T SG SL SS XNACL XCO2G XCO2L PCAP k-red. DL + (Pa) (deg.C) (Pa) (kg/m3) + + ah 2 1 0.83454E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.4 + ai 2 2 0.83454E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.4 + aj 2 3 0.83454E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.4 + ak 2 4 0.83454E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.4 + c 3 5 0.13235E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + d 3 6 0.13235E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + e 3 7 0.13235E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + f 3 8 0.13235E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + i 3 9 0.13235E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + j 3 10 0.13235E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + af 3 11 0.13235E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + ag 3 12 0.13235E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + ah 3 13 0.13235E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + ai 3 14 0.13235E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + aj 3 15 0.13235E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + ak 3 16 0.13235E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + al 3 17 0.13235E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + am 3 18 0.13235E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + an 3 19 0.13235E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + a 4 20 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + b 4 21 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + c 4 22 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + d 4 23 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + e 4 24 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + f 4 25 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + g 4 26 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + h 4 27 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + i 4 28 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + j 4 29 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + k 4 30 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + ae 4 31 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + af 4 32 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + ag 4 33 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + ah 4 34 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + ai 4 35 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + aj 4 36 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + ak 4 37 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + al 4 38 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + am 4 39 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + an 4 40 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + df 4 41 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + dg 4 42 0.18125E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + a 5 43 0.23016E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + b 5 44 0.23016E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + + ELEM. IND. P T SG SL SS XNACL XCO2G XCO2L PCAP k-red. DL + (Pa) (deg.C) (Pa) (kg/m3) + + c 5 45 0.23016E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + d 5 46 0.23016E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + e 5 47 0.23016E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + f 5 48 0.23016E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + g 5 49 0.23016E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + h 5 50 0.23016E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + i 5 51 0.23016E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + j 5 52 0.23016E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + k 5 53 0.23016E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + ...ITERATING... AT [ 26, 1] --- DELTEX = 2.951757E+07 MAX. RES. = 5.852333E-05 AT ELEMENT al40 EQUATION 1 + au38( 26, 2) ST = 6.105357E+07 DT = 2.951757E+07 DX1= 8.14422E+03 DX2= 0.00000E+00 T = 25.000 P = 18502129. S = 0.000000E+00 + dp40( 27, 1) ST = 6.307200E+07 DT = 2.018430E+06 DX1= 0.00000E+00 DX2= 0.00000E+00 T = 25.000 P = 19486916. S = 0.000000E+00 + + ********** VOLUME- AND MASS-BALANCES ********************************************************************************************* + + ********** [KCYC,ITER] = [ 27, 1] ***** THE TIME IS 6.30720E+07 SECONDS, OR 7.30000E+02 DAYS + + + PHASES PRESENT COMPONENT MASS IN PLACE (kg) + ********************************************************* ************************************************************************ + PHASES * GAS LIQUID SOLID COMPONENTS * WATER SALT CO2 HEAT + ********************************************************* ************************************************************************ + * PHASES * + VOL. (m^3) * 0.00000000E+00 0.36000000E+27 0.00000000E+00 GAS PHASE * 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 + MASS (kg) * 0.00000000E+00 0.35897432E+30 0.00000000E+00 LIQUID * 0.35897432E+30 0.00000000E+00 0.00000000E+00 0.37604715E+35 + ********************************************************* SOLID SALT * 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 + ADSORB-ROCK* 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.50232000E+35 + TOTAL * 0.35897432E+30 0.00000000E+00 0.00000000E+00 0.87836715E+35 + ************************************************************************ + + ********************************************************************************************************************************** + + + + case 2 + + OUTPUT DATA AFTER ( 27, 1)-2-TIME STEPS THE TIME IS 0.730000E+03 DAYS + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + TOTAL TIME KCYC ITER ITERC KON DX1M DX2M DX3M MAX. RES. NER KER DELTEX + 0.630720E+08 27 1 45 2 0.00000E+00 0.00000E+00 0.00000E+00 0.19940E-06 4074 1 0.20184E+07 + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + ELEM. IND. P T SG SL SS XNACL XCO2G XCO2L PCAP k-red. DL + (Pa) (deg.C) (Pa) (kg/m3) + + ah 2 1 0.83502E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.4 + ai 2 2 0.83502E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.4 + aj 2 3 0.83502E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.4 + ak 2 4 0.83502E+06 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.4 + c 3 5 0.13243E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + d 3 6 0.13243E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + e 3 7 0.13243E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + f 3 8 0.13243E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + i 3 9 0.13243E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + j 3 10 0.13243E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + af 3 11 0.13243E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + ag 3 12 0.13243E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + ah 3 13 0.13243E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + ai 3 14 0.13243E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + aj 3 15 0.13243E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + ak 3 16 0.13243E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + al 3 17 0.13243E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + am 3 18 0.13243E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + an 3 19 0.13243E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.6 + a 4 20 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + b 4 21 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + c 4 22 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + d 4 23 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + e 4 24 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + f 4 25 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + g 4 26 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + h 4 27 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + i 4 28 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + j 4 29 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + k 4 30 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + ae 4 31 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + af 4 32 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + ag 4 33 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + ah 4 34 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + ai 4 35 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + aj 4 36 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + ak 4 37 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + al 4 38 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + am 4 39 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + an 4 40 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + df 4 41 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + dg 4 42 0.18136E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.8 + a 5 43 0.23030E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + b 5 44 0.23030E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + + ELEM. IND. P T SG SL SS XNACL XCO2G XCO2L PCAP k-red. DL + (Pa) (deg.C) (Pa) (kg/m3) + + c 5 45 0.23030E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + d 5 46 0.23030E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + e 5 47 0.23030E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + f 5 48 0.23030E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + g 5 49 0.23030E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + h 5 50 0.23030E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + i 5 51 0.23030E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + j 5 52 0.23030E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + k 5 53 0.23030E+07 25.00 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.10000E+01 997.9 + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + + WRITE FILE *SAVE* AFTER 36 TIME STEPS --- THE TIME IS 1.892000E+08 SECONDS + + + *********************************************************************************************************************************** + * * + * SUMMARY OF PROGRAM UNITS USED * + * * + *********************************************************************************************************************************** + + UNIT VERSION DATE COMMENTS + ___________________________________________________________________________________________________________________________________ + + IO 1.0 15 APRIL 1991 OPEN FILES *VERS*, *MESH*, *INCON*, *GENER*, *SAVE*, *LINEQ*, AND *TABLE* + TOUGHREACT V3.32-OMP 08 Feb 2017 MAIN PROGRAM + INPUT 1.60 12 Apr 2016 READ ALL DATA PROVIDED THROUGH FILE *INPUT* + FLOP 2.0 12 FEB 2012 CALCULATE NUMBER OF SIGNIFICANT DIGITS FOR FLOATING POINT ARITHMETIC + RFILE 1.8 30 Nov 2015 INITIALIZE DATA FROM FILES *MESH* OR *MINC*, *GENER*, AND *INCON* + also initializes permeability modifiers and coordinate arrays + and optionally reads tables with flowing wellbore pressures + PMIN 1.0 26 September 1997 initialize block-by-block permeability modifiers + SIN 1.00 1 October 1999 initialize parameters for the solver package, and generate informative printout + CYCIT 2.3 15 December 2015 EXECUTIVE ROUTINE FOR MARCHING IN TIME + + EOS 3.1 13 November 2015 *EWASG* THERMOPHYSICAL PROPERTIES MODULE FOR H2O/NACL/NCG AIR-CO2-CH4-H2-N2 + HALITE 1.0 25 FEBRUARY 1993 EQUILIBRIUM HALITE SOLUBILITY + SATB 1.0 26 MARCH 1992 VAPOR PRESSURE OF BRINE AS FUNCTION OF T, P AND SALT MASS FRACTION + SAT 1.0 S 17 SEPTEMBER 1990 STEAM TABLE EQUATION: SATURATION PRESSURE AS FUNCTION OF TEMPERATURE (M. OS.) + HENRY 1.0 25 FEBRUARY 1993 HENRYS LAW TO CALCULATE NCG BUBBLING PRESSURE OR DISSOLVED NCG MASS FRACTION + COBRI 1.0 8 November 1999 DENSITY AND ENTHALPY OF LIQUID BRINE AS FUNCTION OF T, P AND SALT MASS FRACTION + COWAT 1.0 S 17 SEPTEMBER 1990 LIQUID WATER DENSITY AND INT. ENERGY VERSUS TEMPERATURE AND PRESSURE (M. OS.) + TCRIT 1.0 5 November 1999 CRITICAL T OF NACL SOLUTIONS AS A FUNCTION OF SALINITY + VISB 1.1 22 MARCH 1996 VISCOSITY OF BRINE AS FUNCTION OF T, P AND SALT MASS FRACTION + VISH2O 1.0 22 MARCH 1996 VISCOSITY OF LIQUID WATER OR VAPOR AS FUNCTION OF TEMPERATURE, PRESSURE AND DENSITY + SOLUT 1.1 25 MARCH 1996 HEAT OF SOLUTION OF NCG AS FUNCTION OF T AND SALT MASS FRACTION + NCG 1.1 6 MARCH 1996 SPECIFIC ENTHALPY AND DENSITY OF GASEOUS NCG + PCAP 1.1 14 April 2009 CAPILLARY PRESSURE AS FUNCTION OF SATURATION + AKREL 1.0 30 MAY 1993 PERMEABILITY REDUCTION FACTOR AS FUNCTION OF POROSITY REDUCTION + DHAL 1.0 11 MARCH 1996 HALITE DENSITY AS FUNCTION OF T AND P + HHAL 1.0 5 November 1999 HALITE SPECIFIC ENTHALPY AS FUNCTION OF T + + BALLA 1.13 ! 7 September 2001 PERFORM SUMMARY BALANCES FOR VOLUME, MASS, AND ENERGY + Get_TOUGH_HT_Variables 2.4 21 December 2015 Extract hydrological and thermal conditions from flow + TSTEP 1.0 4 MARCH 1991 ADJUST TIME STEPS TO COINCIDE WITH USER-DEFINED TARGET TIMES + MULTI 1.5 30 November 2015 ASSEMBLE ALL ACCUMULATION AND FLOW TERMS + includes capabilities for radiative heat transfer and diffusion in all phases + with local equilibrium phase partitioning between gas and liquid + allows block-by-block permeability modification + + CONVER 1.0 4 MARCH 1991 UPDATE PRIMARY VARIABLES AFTER CONVERGENCE IS ACHIEVED + LINEQ 2.00 4 October 1999 Interface for linear equation solvers T2CG2 + Can call a direct solver or a package of conjugate gradient solvers + WRIFI 1.2 30 November 2015 AT THE COMPLETION OF A TOUGH2 RUN, WRITE PRIMARY VARIABLES ON FILE *SAVE* + + + *********************************************************************************************************************************** + + + END OF TOUGHREACT V3.32-OMP SIMULATION --- ELAPSED TIME = 2.866 SEC-- CALCULATION TIME = 2.566 SEC-- DATA INPUT TIME = 0.300 SEC + diff --git a/tests/listing/TOUGHREACT/2/case2_25_element.npy b/tests/listing/TOUGHREACT/2/case2_25_element.npy new file mode 100644 index 00000000..1b09661c Binary files /dev/null and b/tests/listing/TOUGHREACT/2/case2_25_element.npy differ diff --git a/tests/mulgrid/fit_surface_result_g1.npy b/tests/mulgrid/fit_surface_result_g1.npy new file mode 100644 index 00000000..6c18dae4 Binary files /dev/null and b/tests/mulgrid/fit_surface_result_g1.npy differ diff --git a/tests/mulgrid/fit_surface_result.npy b/tests/mulgrid/fit_surface_result_g2.npy similarity index 100% rename from tests/mulgrid/fit_surface_result.npy rename to tests/mulgrid/fit_surface_result_g2.npy diff --git a/tests/mulgrid/gmsh2_2.msh b/tests/mulgrid/gmsh2_2.msh new file mode 100644 index 00000000..0267f324 --- /dev/null +++ b/tests/mulgrid/gmsh2_2.msh @@ -0,0 +1,222 @@ +$MeshFormat +2.2 0 8 +$EndMeshFormat +$Nodes +117 +1 0 0 0 +2 25 0 0 +3 50 0 0 +4 75 0 0 +5 100 0 0 +6 125 0 0 +7 150 0 0 +8 175 0 0 +9 200 0 0 +10 225 0 0 +11 250 0 0 +12 275 0 0 +13 300 0 0 +14 0 25 0 +15 25 25 0 +16 50 25 0 +17 75 25 0 +18 100 25 0 +19 125 25 0 +20 150 25 0 +21 175 25 0 +22 200 25 0 +23 225 25 0 +24 250 25 0 +25 275 25 0 +26 300 25 0 +27 0 50 0 +28 25 50 0 +29 50 50 0 +30 75 50 0 +31 100 50 0 +32 125 50 0 +33 150 50 0 +34 175 50 0 +35 200 50 0 +36 225 50 0 +37 250 50 0 +38 275 50 0 +39 300 50 0 +40 0 75 0 +41 25 75 0 +42 50 75 0 +43 75 75 0 +44 100 75 0 +45 125 75 0 +46 150 75 0 +47 175 75 0 +48 200 75 0 +49 225 75 0 +50 250 75 0 +51 275 75 0 +52 300 75 0 +53 0 100 0 +54 25 100 0 +55 50 100 0 +56 75 100 0 +57 100 100 0 +58 125 100 0 +59 150 100 0 +60 175 100 0 +61 200 100 0 +62 225 100 0 +63 250 100 0 +64 275 100 0 +65 300 100 0 +66 0 125 0 +67 25 125 0 +68 50 125 0 +69 75 125 0 +70 100 125 0 +71 125 125 0 +72 150 125 0 +73 175 125 0 +74 200 125 0 +75 225 125 0 +76 250 125 0 +77 275 125 0 +78 300 125 0 +79 0 150 0 +80 25 150 0 +81 50 150 0 +82 75 150 0 +83 100 150 0 +84 125 150 0 +85 150 150 0 +86 175 150 0 +87 200 150 0 +88 225 150 0 +89 250 150 0 +90 275 150 0 +91 300 150 0 +92 0 175 0 +93 25 175 0 +94 50 175 0 +95 75 175 0 +96 100 175 0 +97 125 175 0 +98 150 175 0 +99 175 175 0 +100 200 175 0 +101 225 175 0 +102 250 175 0 +103 275 175 0 +104 300 175 0 +105 0 200 0 +106 25 200 0 +107 50 200 0 +108 75 200 0 +109 100 200 0 +110 125 200 0 +111 150 200 0 +112 175 200 0 +113 200 200 0 +114 225 200 0 +115 250 200 0 +116 275 200 0 +117 300 200 0 +$EndNodes +$Elements +96 +1 3 2 0 0 2 15 14 1 +2 3 2 0 0 3 16 15 2 +3 3 2 0 0 4 17 16 3 +4 3 2 0 0 5 18 17 4 +5 3 2 0 0 6 19 18 5 +6 3 2 0 0 7 20 19 6 +7 3 2 0 0 8 21 20 7 +8 3 2 0 0 9 22 21 8 +9 3 2 0 0 10 23 22 9 +10 3 2 0 0 11 24 23 10 +11 3 2 0 0 12 25 24 11 +12 3 2 0 0 13 26 25 12 +13 3 2 0 0 15 28 27 14 +14 3 2 0 0 16 29 28 15 +15 3 2 0 0 17 30 29 16 +16 3 2 0 0 18 31 30 17 +17 3 2 0 0 19 32 31 18 +18 3 2 0 0 20 33 32 19 +19 3 2 0 0 21 34 33 20 +20 3 2 0 0 22 35 34 21 +21 3 2 0 0 23 36 35 22 +22 3 2 0 0 24 37 36 23 +23 3 2 0 0 25 38 37 24 +24 3 2 0 0 26 39 38 25 +25 3 2 0 0 28 41 40 27 +26 3 2 0 0 29 42 41 28 +27 3 2 0 0 30 43 42 29 +28 3 2 0 0 31 44 43 30 +29 3 2 0 0 32 45 44 31 +30 3 2 0 0 33 46 45 32 +31 3 2 0 0 34 47 46 33 +32 3 2 0 0 35 48 47 34 +33 3 2 0 0 36 49 48 35 +34 3 2 0 0 37 50 49 36 +35 3 2 0 0 38 51 50 37 +36 3 2 0 0 39 52 51 38 +37 3 2 0 0 41 54 53 40 +38 3 2 0 0 42 55 54 41 +39 3 2 0 0 43 56 55 42 +40 3 2 0 0 44 57 56 43 +41 3 2 0 0 45 58 57 44 +42 3 2 0 0 46 59 58 45 +43 3 2 0 0 47 60 59 46 +44 3 2 0 0 48 61 60 47 +45 3 2 0 0 49 62 61 48 +46 3 2 0 0 50 63 62 49 +47 3 2 0 0 51 64 63 50 +48 3 2 0 0 52 65 64 51 +49 3 2 0 0 54 67 66 53 +50 3 2 0 0 55 68 67 54 +51 3 2 0 0 56 69 68 55 +52 3 2 0 0 57 70 69 56 +53 3 2 0 0 58 71 70 57 +54 3 2 0 0 59 72 71 58 +55 3 2 0 0 60 73 72 59 +56 3 2 0 0 61 74 73 60 +57 3 2 0 0 62 75 74 61 +58 3 2 0 0 63 76 75 62 +59 3 2 0 0 64 77 76 63 +60 3 2 0 0 65 78 77 64 +61 3 2 0 0 67 80 79 66 +62 3 2 0 0 68 81 80 67 +63 3 2 0 0 69 82 81 68 +64 3 2 0 0 70 83 82 69 +65 3 2 0 0 71 84 83 70 +66 3 2 0 0 72 85 84 71 +67 3 2 0 0 73 86 85 72 +68 3 2 0 0 74 87 86 73 +69 3 2 0 0 75 88 87 74 +70 3 2 0 0 76 89 88 75 +71 3 2 0 0 77 90 89 76 +72 3 2 0 0 78 91 90 77 +73 3 2 0 0 80 93 92 79 +74 3 2 0 0 81 94 93 80 +75 3 2 0 0 82 95 94 81 +76 3 2 0 0 83 96 95 82 +77 3 2 0 0 84 97 96 83 +78 3 2 0 0 85 98 97 84 +79 3 2 0 0 86 99 98 85 +80 3 2 0 0 87 100 99 86 +81 3 2 0 0 88 101 100 87 +82 3 2 0 0 89 102 101 88 +83 3 2 0 0 90 103 102 89 +84 3 2 0 0 91 104 103 90 +85 3 2 0 0 93 106 105 92 +86 3 2 0 0 94 107 106 93 +87 3 2 0 0 95 108 107 94 +88 3 2 0 0 96 109 108 95 +89 3 2 0 0 97 110 109 96 +90 3 2 0 0 98 111 110 97 +91 3 2 0 0 99 112 111 98 +92 3 2 0 0 100 113 112 99 +93 3 2 0 0 101 114 113 100 +94 3 2 0 0 102 115 114 101 +95 3 2 0 0 103 116 115 102 +96 3 2 0 0 104 117 116 103 +$EndElements diff --git a/tests/mulgrid/gmsh4_1.msh b/tests/mulgrid/gmsh4_1.msh new file mode 100644 index 00000000..4f633e0f --- /dev/null +++ b/tests/mulgrid/gmsh4_1.msh @@ -0,0 +1,198 @@ +$MeshFormat +4.1 0 8 +$EndMeshFormat +$Entities +4 4 1 0 +1 0 0 0 0 +2 12000 0 0 0 +3 12000 16000 0 0 +4 0 16000 0 0 +1 0 0 0 12000 0 0 0 2 1 -2 +2 12000 0 0 12000 16000 0 0 2 2 -3 +3 0 16000 0 12000 16000 0 0 2 3 -4 +4 0 0 0 0 16000 0 0 2 4 -1 +1 0 0 0 12000 16000 0 0 4 1 2 3 4 +$EndEntities +$Nodes +9 48 1 48 +0 1 0 1 +1 +0 0 0 +0 2 0 1 +2 +12000 0 0 +0 3 0 1 +3 +12000 16000 0 +0 4 0 1 +4 +0 16000 0 +1 1 0 4 +5 +6 +7 +8 +2399.999999992779 0 0 +4799.999999982618 0 0 +7199.999999978812 0 0 +9599.999999985601 0 0 +1 2 0 6 +9 +10 +11 +12 +13 +14 +12000 2285.714285713262 0 +12000 4571.428571423341 0 +12000 6857.142857138471 0 +12000 9142.857142854658 0 +12000 11428.57142856977 0 +12000 13714.28571428489 0 +1 3 0 4 +15 +16 +17 +18 +9600.000000011511 16000 0 +7200.000000025368 16000 0 +4800.00000002651 16000 0 +2400.00000001706 16000 0 +1 4 0 6 +19 +20 +21 +22 +23 +24 +0 13714.28571428586 0 +0 11428.57142857173 0 +0 9142.8571428576 0 +0 6857.142857142398 0 +0 4571.428571428265 0 +0 2285.714285714133 0 +2 1 0 24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +2399.999999996247 2285.71428571396 0 +2399.999999999716 4571.428571427281 0 +2400.000000003185 6857.142857141613 0 +2400.000000006654 9142.857142857012 0 +2400.000000010123 11428.57142857134 0 +2400.000000013592 13714.28571428567 0 +4799.999999988891 2285.714285713785 0 +4799.99999999516 4571.428571426296 0 +4800.000000001428 6857.142857140829 0 +4800.0000000077 9142.857142856423 0 +4800.00000001397 11428.57142857095 0 +4800.00000002024 13714.28571428547 0 +7199.999999985464 2285.71428571361 0 +7199.999999992112 4571.428571425311 0 +7199.999999998764 6857.142857140041 0 +7200.000000005415 9142.857142855833 0 +7200.000000012064 11428.57142857056 0 +7200.000000018716 13714.28571428528 0 +9599.999999989304 2285.714285713436 0 +9599.999999993002 4571.428571424327 0 +9599.999999996708 6857.142857139258 0 +9600.000000000404 9142.857142855244 0 +9600.000000004107 11428.57142857017 0 +9600.000000007809 13714.28571428508 0 +$EndNodes +$Elements +9 63 1 63 +0 1 15 1 +1 1 +0 2 15 1 +2 2 +0 3 15 1 +3 3 +0 4 15 1 +4 4 +1 1 1 5 +5 1 5 +6 5 6 +7 6 7 +8 7 8 +9 8 2 +1 2 1 7 +10 2 9 +11 9 10 +12 10 11 +13 11 12 +14 12 13 +15 13 14 +16 14 3 +1 3 1 5 +17 3 15 +18 15 16 +19 16 17 +20 17 18 +21 18 4 +1 4 1 7 +22 4 19 +23 19 20 +24 20 21 +25 21 22 +26 22 23 +27 23 24 +28 24 1 +2 1 3 35 +29 1 5 25 24 +30 24 25 26 23 +31 23 26 27 22 +32 22 27 28 21 +33 21 28 29 20 +34 20 29 30 19 +35 19 30 18 4 +36 5 6 31 25 +37 25 31 32 26 +38 26 32 33 27 +39 27 33 34 28 +40 28 34 35 29 +41 29 35 36 30 +42 30 36 17 18 +43 6 7 37 31 +44 31 37 38 32 +45 32 38 39 33 +46 33 39 40 34 +47 34 40 41 35 +48 35 41 42 36 +49 36 42 16 17 +50 7 8 43 37 +51 37 43 44 38 +52 38 44 45 39 +53 39 45 46 40 +54 40 46 47 41 +55 41 47 48 42 +56 42 48 15 16 +57 8 2 9 43 +58 43 9 10 44 +59 44 10 11 45 +60 45 11 12 46 +61 46 12 13 47 +62 47 13 14 48 +63 48 14 3 15 +$EndElements diff --git a/tests/mulgrid/refine_areas.npy b/tests/mulgrid/refine_areas.npy index 6371856b..ca213f0b 100755 Binary files a/tests/mulgrid/refine_areas.npy and b/tests/mulgrid/refine_areas.npy differ diff --git a/tests/test_geometry.py b/tests/test_geometry.py index c3d7322f..300101dc 100644 --- a/tests/test_geometry.py +++ b/tests/test_geometry.py @@ -191,6 +191,26 @@ def test_line_projection_distance(self): d = point_line_distance(a, line) self.assertAlmostEqual(sqrt(0.5), d) + def test_line_intersects_rectangle(self): + """line_intersects_rectangle()""" + r = [np.array([1., 1.]), np.array([5., 3.])] + line = [np.array([0., 0.]), np.array([1., 1.])] + self.assertTrue(line_intersects_rectangle(r, line)) + line = r + self.assertTrue(line_intersects_rectangle(r, line)) + line = r[::-1] + self.assertTrue(line_intersects_rectangle(r, line)) + line = [np.array([1., 1.]), np.array([5., 1.])] + self.assertTrue(line_intersects_rectangle(r, line)) + line = [np.array([3., 1.]), np.array([4., 3.])] + self.assertTrue(line_intersects_rectangle(r, line)) + line = [np.array([3., 0.]), np.array([4., 6.])] + self.assertTrue(line_intersects_rectangle(r, line)) + line = [np.array([2., 2.]), np.array([4., 2.5])] + self.assertTrue(line_intersects_rectangle(r, line)) + line = [np.array([6., 6.]), np.array([8., 0.])] + self.assertFalse(line_intersects_rectangle(r, line)) + def test_vector_heading(self): """vector_heading()""" from math import atan diff --git a/tests/test_mulgrid.py b/tests/test_mulgrid.py index 9160db2f..be513790 100755 --- a/tests/test_mulgrid.py +++ b/tests/test_mulgrid.py @@ -68,6 +68,9 @@ def test_fix_unfix_blockname(self): self.assertEqual(blk, blk2) blk = 'A 200' self.assertEqual(blk, fix_blockname(blk)) + blk = 'abcde' + self.assertEqual(blk, fix_blockname(blk)) + self.assertEqual(blk, unfix_blockname(blk)) def test_valid_blockname(self): """valid_blockname() function""" @@ -202,17 +205,25 @@ def test_read_write(self): def test_fit_surface(self): """fit surface""" + d = np.load(os.path.join('mulgrid', 'fit_surface_data.npy')) tol = 1.e-3 - geo = mulgrid(os.path.join('mulgrid', 'g2.dat')) p = np.array([2.78153e6, 6.26941e6]) p2 = p + np.ones(2)*5.e3 r = [p, p2] + + geo = mulgrid(os.path.join('mulgrid', 'g1.dat')) + cols = geo.columns_in_polygon(r) + geo.fit_surface(d, alpha = 0.1, beta = 0.1, silent = True) + s = np.array([col.surface for col in cols]) + expected = np.load(os.path.join('mulgrid', 'fit_surface_result_g1.npy')) + self.assertTrue(np.allclose(s, expected, atol = tol)) + + geo = mulgrid(os.path.join('mulgrid', 'g2.dat')) cols = geo.columns_in_polygon(r) - d = np.load(os.path.join('mulgrid', 'fit_surface_data.npy')) geo.fit_surface(d, alpha = 0.1, beta = 0.1, silent = True) s = np.array([col.surface for col in cols]) - r = np.load(os.path.join('mulgrid', 'fit_surface_result.npy')) - self.assertTrue(np.allclose(s, r, atol = tol)) + expected = np.load(os.path.join('mulgrid', 'fit_surface_result_g2.npy')) + self.assertTrue(np.allclose(s, expected, atol = tol)) def test_refine(self): """refine()""" @@ -223,7 +234,7 @@ def test_refine(self): cols = [col for col in geo.columnlist if 400. < col.centre[1] < 600.] geo.refine(cols) self.assertEqual(geo.area, orig_area) - self.assertEqual(geo.num_columns, 189) + self.assertEqual(geo.num_columns, 182) self.assertEqual(geo.num_nodes, 169) areas = np.sort(np.array([col.area for col in geo.columnlist])) a = np.sort(np.load(os.path.join('mulgrid', 'refine_areas.npy'))) @@ -258,6 +269,103 @@ def test_column_track(self): except: err = True self.assertFalse(err) + # track through grid with a corner removed: + geo = mulgrid().rectangular([100]*2, [100]*2, [10]) + geo.translate([1e7, 1e7, 0]) + geo.delete_column(geo.columnlist[0].name) + geo.rotate(30) + line = [geo.columnlist[0].centre, geo.columnlist[1].centre] + t = geo.column_track(line) + self.assertEqual(len(t), 2) + if len(t) > 1: + self.assertEqual(t[0][0].name, geo.columnlist[0].name) + self.assertEqual(t[1][0].name, geo.columnlist[1].name) + + # M-grid: + geo = mulgrid().rectangular([100]*5, [100]*3, [10]) + names = [geo.columnlist[i].name for i in [1,3,6,8]] + for name in names: geo.delete_column(name) + line = [np.array([0, 50]), np.array([500, 50])] + t = geo.column_track(line) + self.assertEqual(len(t), 3) + if (len(t) == 3): + self.assertEqual(t[0][0].name, geo.columnlist[0].name) + self.assertEqual(t[1][0].name, geo.columnlist[1].name) + self.assertEqual(t[2][0].name, geo.columnlist[2].name) + + line = line[::-1] + t = geo.column_track(line) + self.assertEqual(len(t), 3) + if (len(t) == 3): + self.assertEqual(t[0][0].name, geo.columnlist[2].name) + self.assertEqual(t[1][0].name, geo.columnlist[1].name) + self.assertEqual(t[2][0].name, geo.columnlist[0].name) + + # track within a single column: + col = geo.columnlist[-1] + p = col.centre + d = np.ones(2) * 25 + line = [p - d, p + d] + t = geo.column_track(line) + self.assertEqual(len(t), 1) + if (len(t) == 1): + self.assertEqual(t[0][0].name, col.name) + + # track outside grid: + line = [np.array([-10, -10]), np.array([600, -200])] + t = geo.column_track(line) + self.assertEqual(len(t), 0) + + # track entirely within grid: + line = [np.array([380, 270]), np.array([490, 90])] + t = geo.column_track(line) + self.assertEqual(len(t), 4) + self.assertTrue(np.allclose(t[0][1], line[0])) + self.assertTrue(np.allclose(t[-1][2], line[1])) + if (len(t) == 4): + names = [ti[0].name for ti in t] + col_ind = [9, 10, 5, 2] + expected_names = [geo.columnlist[i].name for i in col_ind] + self.assertEqual(names, expected_names) + + # track leaving grid: + line = [np.array([250, 50]), np.array([300, -100])] + t = geo.column_track(line) + self.assertEqual(len(t), 1) + if (len(t) == 1): + self.assertEqual(t[0][0].name, geo.columnlist[1].name) + self.assertTrue(np.allclose(t[0][1], line[0])) + + # track entering grid: + line = [np.array([270, -50]), np.array([230, 270])] + t = geo.column_track(line) + self.assertEqual(len(t), 3) + if (len(t) == 3): + names = [ti[0].name for ti in t] + col_ind = [1, 4, 8] + expected_names = [geo.columnlist[i].name for i in col_ind] + self.assertEqual(names, expected_names) + self.assertTrue(np.allclose(t[-1][2], line[1])) + + # 5x5 grid: + geo = mulgrid().rectangular([100]*5, [100]*5, [10]) + line = [np.array([100, 100]), np.array([400, 400])] + t = geo.column_track(line) + self.assertEqual(len(t), 3) + if (len(t) == 3): + self.assertTrue(np.allclose(t[0][1], line[0])) + self.assertTrue(np.allclose(t[-1][2], line[1])) + + # contrasting grid sizes: + dx = [1] * 50 + [2] * 25 + [5] * 20 + [10] * 20 + [100] * 6 + [1000] * 2 + geo = mulgrid().rectangular(dx, [100]*5, [10]) + line = [np.array([0, 0]), np.array([3000, 0])] + t = geo.column_track(line) + self.assertEqual(len(t), 123) + if (len(t) == 123): + self.assertTrue(np.allclose(t[0][1], line[0])) + self.assertTrue(np.allclose(t[-1][2], line[1])) + def test_grid3d(self): """3D grid""" @@ -373,6 +481,59 @@ def test_amesh(self): self.assertEqual(9184, geo.num_blocks) self.assertEqual(15, geo.num_layers) + def test_gmsh(self): + infile = os.path.join('mulgrid', 'gmsh2_2.msh') + layers = [1, 2, 3] + geo = mulgrid().from_gmsh(infile, layers) + self.assertEqual(117, geo.num_nodes) + self.assertEqual(96, geo.num_columns) + self.assertEqual(96 * len(layers), geo.num_blocks) + self.assertEqual(len(layers) + 1, geo.num_layers) + + infile = os.path.join('mulgrid', 'gmsh4_1.msh') + layers = [1, 2, 3] + geo = mulgrid().from_gmsh(infile, layers) + self.assertEqual(48, geo.num_nodes) + self.assertEqual(35, geo.num_columns) + self.assertEqual(35 * len(layers), geo.num_blocks) + self.assertEqual(len(layers) + 1, geo.num_layers) + + def test_layermesh(self): + + def layermesh_case(geo): + lm = geo.layermesh + self.assertEqual(geo.num_nodes, lm.num_nodes) + self.assertEqual(geo.num_columns, lm.num_columns) + self.assertEqual(geo.num_layers - 1, lm.num_layers) + self.assertEqual(geo.num_underground_blocks, lm.num_cells) + + geo2 = geo.from_layermesh(lm, atmosphere_type = geo.atmosphere_type) + self.assertEqual(geo.num_nodes, geo2.num_nodes) + self.assertEqual(geo.num_columns, geo2.num_columns) + self.assertEqual(geo.num_layers, geo2.num_layers) + self.assertEqual(geo.num_blocks, geo2.num_blocks) + + geo = mulgrid().rectangular([100.]*10, [150.]*8, [10.]*10) + for i, col in enumerate(geo.columnlist): + col.surface = -40. + i / 80. + geo.set_column_num_layers(col) + geo.snap_columns_to_nearest_layers() + layermesh_case(geo) + + geo = mulgrid().rectangular([100.]*9, [150.]*10, [10.]*11, atmos_type = 0) + for i, col in enumerate(geo.columnlist): + col.surface = -50. + i / 70. + geo.set_column_num_layers(col) + geo.snap_columns_to_nearest_layers() + layermesh_case(geo) + + geo = mulgrid().rectangular([100.]*8, [120.]*7, [10.]*9, atmos_type = 1) + for i, col in enumerate(geo.columnlist): + col.surface = -30. + i / 60. + geo.set_column_num_layers(col) + geo.snap_columns_to_nearest_layers() + layermesh_case(geo) + def test_block_order(self): def get_block_nodes(geo): diff --git a/tests/test_t2data.py b/tests/test_t2data.py index bc2192ba..0b97b123 100755 --- a/tests/test_t2data.py +++ b/tests/test_t2data.py @@ -12,6 +12,9 @@ def column_nan_to_num(x): if dt[0].name.startswith('float'): x[key] = np.nan_to_num(x[key]) return x +default_separator_pressure = 0.55e6 +default_2_stage_separator_pressure = [1.45e6, 0.55e6] + class t2data_stats(t2data): """Variant of t2data class with extra properties for vital statistics of a t2data object- for comparisons between t2data objects that @@ -551,6 +554,7 @@ def test_json(self): dat.filename = filename_base + '.dat' dat.parameter['gravity'] = gravity dat.multi = {'eos': 'EW'} + dat.diffusion = [[-1e-6, -1e-6], [-1e-6, -1e-6]] def basic_test(): j = dat.json(geo, mesh_filename) @@ -630,15 +634,60 @@ def mesh_test(): def eos_test(): eos = None - self.assertEqual(dat.eos_json(eos)['eos'], {'name': 'we'}) + eos_data, tracer_data, pc = dat.eos_json(eos) + self.assertEqual(eos_data['eos'], {'name': 'we'}) + self.assertIsNone(tracer_data) + eos = 2 - self.assertEqual(dat.eos_json(eos)['eos'], {'name': 'wce'}) + eos_data, tracer_data, pc = dat.eos_json(eos) + self.assertEqual(eos_data['eos'], {'name': 'wce'}) + self.assertIsNone(tracer_data) + + eos = 'EWA' + eos_data, tracer_data, pc = dat.eos_json(eos) + self.assertEqual(eos_data['eos'], {'name': 'wae'}) + self.assertIsNone(tracer_data) + eos = 'EWAV' - self.assertEqual(dat.eos_json(eos)['eos'], {'name': 'wae'}) - eos = 3 + eos_data, tracer_data, pc = dat.eos_json(eos) + self.assertEqual(eos_data['eos'], {'name': 'wae'}) + self.assertIsNone(tracer_data) + + eos = 'EWT' + eos_data, tracer_data, pc = dat.eos_json(eos) + self.assertEqual(eos_data['eos'], {'name': 'we'}) + self.assertEqual(tracer_data['tracer'], + {'name': 'tracer', 'phase': 'liquid'}) + + eos = 'EWTD' + dat.diffusion = [[-1e-6, -1e-6], [-1e-6, -1e-6]] + eos_data, tracer_data, pc = dat.eos_json(eos) + self.assertEqual(eos_data['eos'], {'name': 'we'}) + self.assertEqual(tracer_data['tracer'], + {'name': 'tracer', 'phase': 'liquid', 'diffusion': 1e-6}) + + dat.diffusion = [[1e-5, 1e-6], [1e-6, 1e-5]] + with self.assertRaises(Exception): + eos_data, tracer_data, pc = dat.eos_json(eos) + + eos = 8 + with self.assertRaises(Exception): + dat.eos_json(eos) + + eos = 'EWSG' with self.assertRaises(Exception): dat.eos_json(eos) + eos = 'EWAX' + eos_data, tracer_data, pc = dat.eos_json(eos) + self.assertEqual(eos_data['eos'], {'name': 'wae'}) + self.assertIsNone(tracer_data) + + eos = 'EWCX' + eos_data, tracer_data, pc = dat.eos_json(eos) + self.assertEqual(eos_data['eos'], {'name': 'wce'}) + self.assertIsNone(tracer_data) + def output_test(): dat = t2data() @@ -744,6 +793,16 @@ def timestepping_test(): self.assertEqual(j['time']['step']['size'], start_timestep) json.dumps(j) + dat.parameter['max_timesteps'] = None + j = dat.timestepping_json() + self.assertEqual(j['time']['step']['maximum']['number'], 0) + json.dumps(j) + + dat.parameter['max_timesteps'] = -1 + j = dat.timestepping_json() + self.assertIsNone(j['time']['step']['maximum']['number']) + json.dumps(j) + dat.type = 'AUTOUGH2' reduction_autough2 = 0.2 j = dat.timestepping_json() @@ -861,13 +920,17 @@ def capillary_pressure_test(): dat.capillarity = {'type': 2, 'parameters': [0., 1., 0., 1.]} self.assertRaises(Exception, dat.capillary_pressure_json) - def primary_to_region_test(): - self.assertEqual(primary_to_region_we([2.e5, 20.]), 1) - self.assertEqual(primary_to_region_we([0.5e5, 100.]), 2) - self.assertEqual(primary_to_region_we([2.e5, 0.5]), 4) - self.assertEqual(primary_to_region_wge([2.e5, 20, 0.1e5]), 1) - self.assertEqual(primary_to_region_wge([2.e5, 100, 1.5e5]), 2) - self.assertEqual(primary_to_region_wge([1.e5, 0.1, 0.5e5]), 4) + def convert_primary_test(): + def case(p, converter, expected_v, expected_r): + v, r = converter(p) + self.assertEqual(expected_v, v) + self.assertEqual(expected_r, r) + case([2.e5, 20.], convert_primary_eos_1, [2.e5, 20.], 1) + case([0.5e5, 100.], convert_primary_eos_1, [0.5e5, 100.], 2) + case([2.e5, 0.5], convert_primary_eos_1, [2.e5, 0.5], 4) + case([2.e5, 20, 0.1e5], convert_primary_eos_2_or_4, [2.e5, 20, 0.1e5], 1) + case([2.e5, 100, 1.5e5], convert_primary_eos_2_or_4, [2.e5, 100, 1.5e5], 2) + case([1.e5, 0.1, 0.5e5], convert_primary_eos_2_or_4, [1.e5, 0.1, 0.5e5], 4) def initial_test(): @@ -876,7 +939,7 @@ def initial_test(): eos = 'w' incons = [50.e5, 20.] - j = dat.initial_json(geo, incons, eos) + j = dat.initial_json(geo, incons, eos, convert_primary_eos_1) self.assertEqual(j['initial']['primary'], incons[:1]) self.assertEqual(j['initial']['region'], 1) json.dumps(j) @@ -884,7 +947,7 @@ def initial_test(): primary1 = [2.e5, 15.] primary = [primary1 for i in range(nblks)] incons = dat.grid.incons(primary) - j = dat.initial_json(geo, incons, eos) + j = dat.initial_json(geo, incons, eos, convert_primary_eos_1) self.assertEqual(j['initial']['primary'], primary1[:1]) self.assertEqual(j['initial']['region'], 1) json.dumps(j) @@ -896,7 +959,7 @@ def initial_test(): primary[:n2] = primary1 primary[n2:] = primary2 incons = dat.grid.incons(primary) - j = dat.initial_json(geo, incons, eos) + j = dat.initial_json(geo, incons, eos, convert_primary_eos_1) self.assertEqual(len(j['initial']['primary']), nblks) self.assertEqual(j['initial']['region'], 2) self.assertTrue(all([j['initial']['primary'][i] == primary1[:1] @@ -908,24 +971,24 @@ def initial_test(): eos = 'we' incons = 'model_ns.h5' - j = dat.initial_json(geo, incons, eos) + j = dat.initial_json(geo, incons, eos, convert_primary_eos_1) self.assertEqual(j['initial']['filename'], incons) json.dumps(j) incons = [3.e5, 35.] - j = dat.initial_json(geo, incons, eos) + j = dat.initial_json(geo, incons, eos, convert_primary_eos_1) self.assertEqual(j['initial']['primary'], incons) self.assertEqual(j['initial']['region'], 1) json.dumps(j) incons = [1.e5, 130.] - j = dat.initial_json(geo, incons, eos) + j = dat.initial_json(geo, incons, eos, convert_primary_eos_1) self.assertEqual(j['initial']['primary'], incons) self.assertEqual(j['initial']['region'], 2) json.dumps(j) incons = [10.e5, 0.6] - j = dat.initial_json(geo, incons, eos) + j = dat.initial_json(geo, incons, eos, convert_primary_eos_1) self.assertEqual(j['initial']['primary'], incons) self.assertEqual(j['initial']['region'], 4) json.dumps(j) @@ -933,14 +996,14 @@ def initial_test(): eos = 'we' primary = [2.e5, 15.] incons = dat.grid.incons(primary) - j = dat.initial_json(geo, incons, eos) + j = dat.initial_json(geo, incons, eos, convert_primary_eos_1) self.assertEqual(j['initial']['primary'], primary) self.assertEqual(j['initial']['region'], 1) json.dumps(j) primary = [0.3e5, 110.] incons = dat.grid.incons(primary) - j = dat.initial_json(geo, incons, eos) + j = dat.initial_json(geo, incons, eos, convert_primary_eos_1) self.assertEqual(j['initial']['primary'], primary) self.assertEqual(j['initial']['region'], 2) json.dumps(j) @@ -952,7 +1015,7 @@ def initial_test(): primary[:n2] = primary1 primary[n2:] = primary2 incons = dat.grid.incons(primary) - j = dat.initial_json(geo, incons, eos) + j = dat.initial_json(geo, incons, eos, convert_primary_eos_1) self.assertEqual(len(j['initial']['primary']), nblks) self.assertEqual(len(j['initial']['region']), nblks) self.assertTrue(all([j['initial']['primary'][i] == primary1 @@ -965,14 +1028,62 @@ def initial_test(): for i in range(n2, nblks)])) json.dumps(j) + dat.multi['eos'] = 'EWT' + primary = [2.e5, 15., 1e-6] + incons = dat.grid.incons(primary) + j = dat.initial_json(geo, incons, 'we', convert_primary_eos_1, + {'name': 'tracer'}) + self.assertEqual(j['initial']['primary'], primary[:2]) + self.assertEqual(j['initial']['region'], 1) + self.assertEqual(j['initial']['tracer'], 1e-6) + json.dumps(j) + + eos = 'wae' # EOS3 tests + + P, X, T, Pa = 8.e5, 1e-6, 120., 6220.9968260563 + incons = [P, X, T] + j = dat.initial_json(geo, incons, eos, convert_primary_eos_3) + self.assertTrue(np.allclose(np.array(j['initial']['primary']), + np.array([P, T, Pa]), rtol = 1e-9)) + self.assertEqual(j['initial']['region'], 1) + json.dumps(j) + + P, X, T, Pa = 1.5e5, 0.1, 120., 9854.6392971362 + incons = [P, X, T] + j = dat.initial_json(geo, incons, eos, convert_primary_eos_3) + self.assertTrue(np.allclose(np.array(j['initial']['primary']), + np.array([P, T, Pa]), rtol = 1e-9)) + self.assertEqual(j['initial']['region'], 2) + json.dumps(j) + + P, Sv, Pa = 8.e5, 0.3, 1e5 + T = t2thermo.tsat(P - Pa) + incons = [P, Sv + 10., T] + j = dat.initial_json(geo, incons, eos, convert_primary_eos_3) + self.assertTrue(np.allclose(np.array(j['initial']['primary']), + np.array([P, Sv, Pa]), rtol = 1e-9)) + self.assertEqual(j['initial']['region'], 4) + json.dumps(j) + + # EOS4 test with MOP(19) = 2 (EOS3-style init) + dat.multi['eos'] = 'EWAV' + P, X, T, Pa = 8.e5, 1e-6, 120., 6220.9968260563 + incons = [P, X, T] + dat.parameter['option'][19] = 2 + j = dat.initial_json(geo, incons, eos, convert_primary_eos_3) + self.assertTrue(np.allclose(np.array(j['initial']['primary']), + np.array([P, T, Pa]), rtol = 1e-9)) + self.assertEqual(j['initial']['region'], 1) + json.dumps(j) + def generators_test(): dat.parameter['option'][12] = 0 - def generator_json(gen, eos = 'we'): + def generator_json(gen, eos = 'we', tracer = None): dat.clear_generators() dat.add_generator(gen) - j = dat.generators_json(geo, eos) + j = dat.generators_json(geo, eos, tracer) self.assertEqual(len(j['source']), 1) return j['source'][0] @@ -1007,6 +1118,36 @@ def generator_json(gen, eos = 'we'): self.assertEqual(g['component'], 2) json.dumps(g) + # COM2 tracer + gen = t2generator(name = name, block = blkname, + type = 'COM2', gx = q) + g = generator_json(gen, eos = 'we', tracer = {'name': 'foo'}) + self.assertFalse('rate' in g) + self.assertEqual(g['tracer'], q) + self.assertFalse('component' in g) + json.dumps(g) + + # TRAC + gen = t2generator(name = name, block = blkname, + type = 'TRAC', gx = q) + g = generator_json(gen, eos = 'we', tracer = {'name': 'foo'}) + self.assertFalse('rate' in g) + self.assertEqual(g['tracer'], q) + self.assertFalse('component' in g) + json.dumps(g) + + # TRAC table + t = [0., 10., 240., 350.] + q = [2.e-6, 2.5e-6, 2.9e-6, 3.1e-6] + gen = t2generator(name = name, block = blkname, + type = 'TRAC', time = t, rate = q) + g = generator_json(gen, eos = 'we', tracer = {'name': 'foo'}) + self.assertEqual(g['tracer'], [list(r) for r in zip(t, q)]) + self.assertFalse('rate' in g) + self.assertFalse('component' in g) + self.assertFalse('enthalpy' in g) + json.dumps(g) + # heat q = 1000. gen = t2generator(name = name, block = blkname, @@ -1063,6 +1204,7 @@ def generator_json(gen, eos = 'we'): self.assertEqual(g['deliverability'], {'pressure': Pwb, 'productivity': PI}) self.assertEqual(g['direction'], 'production') self.assertFalse('rate' in g) + self.assertEqual(g['separator'], {'pressure': default_separator_pressure}) json.dumps(g) # DELG @@ -1073,6 +1215,7 @@ def generator_json(gen, eos = 'we'): self.assertEqual(g['direction'], 'production') self.assertFalse('rate' in g) self.assertFalse('limiter' in g) + self.assertEqual(g['separator'], {'pressure': default_separator_pressure}) json.dumps(g) # DELG with initial rate @@ -1084,6 +1227,7 @@ def generator_json(gen, eos = 'we'): self.assertEqual(g['direction'], 'production') self.assertEqual(g['rate'], q0) self.assertFalse('limiter' in g) + self.assertEqual(g['separator'], {'pressure': default_separator_pressure}) json.dumps(g) # DELG with steam limiter @@ -1094,7 +1238,8 @@ def generator_json(gen, eos = 'we'): self.assertEqual(g['deliverability'], {'pressure': Pwb, 'productivity': PI}) self.assertEqual(g['direction'], 'production') self.assertFalse('rate' in g) - self.assertEqual(g['limiter'], {'type': 'steam', 'limit': qmax}) + self.assertEqual(g['limiter'], {'steam': qmax}) + self.assertEqual(g['separator'], {'pressure': default_separator_pressure}) json.dumps(g) # DELG with steam limiter and separator pressure @@ -1106,8 +1251,20 @@ def generator_json(gen, eos = 'we'): self.assertEqual(g['deliverability'], {'pressure': Pwb, 'productivity': PI}) self.assertEqual(g['direction'], 'production') self.assertFalse('rate' in g) - self.assertEqual(g['limiter'], - {'type': 'steam', 'limit': qmax, 'separator_pressure': psep}) + self.assertEqual(g['limiter'], {'steam': qmax}) + self.assertEqual(g['separator'], {'pressure': psep}) + json.dumps(g) + + # DELG with steam limiter and 2-stage separator + qmax = 5. + gen = t2generator(name = name, block = blkname, + type = 'DELG', gx = PI, ex = Pwb, fg = -1, hg = qmax) + g = generator_json(gen) + self.assertEqual(g['deliverability'], {'pressure': Pwb, 'productivity': PI}) + self.assertEqual(g['direction'], 'production') + self.assertFalse('rate' in g) + self.assertEqual(g['limiter'], {'steam': qmax}) + self.assertEqual(g['separator'], {'pressure': default_2_stage_separator_pressure}) json.dumps(g) # DELG with table of PI vs time @@ -1123,6 +1280,7 @@ def generator_json(gen, eos = 'we'): self.assertEqual(g['direction'], 'production') self.assertFalse('rate' in g) self.assertFalse('limiter' in g) + self.assertEqual(g['separator'], {'pressure': default_separator_pressure}) json.dumps(g) # DELG with table of cutoff pressure vs enthalpy @@ -1139,6 +1297,7 @@ def generator_json(gen, eos = 'we'): self.assertEqual(g['direction'], 'production') self.assertFalse('rate' in g) self.assertFalse('limiter' in g) + self.assertEqual(g['separator'], {'pressure': default_separator_pressure}) json.dumps(g) # DELG with table of cutoff pressure vs enthalpy and steam limiter @@ -1152,7 +1311,8 @@ def generator_json(gen, eos = 'we'): 'productivity': PI}) self.assertEqual(g['direction'], 'production') self.assertFalse('rate' in g) - self.assertEqual(g['limiter'], {'type': 'steam', 'limit': qmax}) + self.assertEqual(g['limiter'], {'steam': qmax}) + self.assertEqual(g['separator'], {'pressure': default_separator_pressure}) json.dumps(g) # DELS @@ -1178,8 +1338,8 @@ def generator_json(gen, eos = 'we'): self.assertEqual(g['deliverability'], {'pressure': Pwb, 'productivity': PI}) self.assertEqual(g['direction'], 'production') self.assertFalse('rate' in g) - self.assertEqual(g['limiter'], - {'type': 'steam', 'limit': qmax, 'separator_pressure': psep}) + self.assertEqual(g['limiter'], {'steam': qmax}) + self.assertEqual(g['separator'], {'pressure': psep}) json.dumps(g) # DELT with total flow limiter @@ -1193,7 +1353,8 @@ def generator_json(gen, eos = 'we'): self.assertEqual(g['deliverability'], {'pressure': Pwb, 'productivity': PI}) self.assertEqual(g['direction'], 'production') self.assertFalse('rate' in g) - self.assertEqual(g['limiter'], {'type': 'total', 'limit': qmax}) + self.assertEqual(g['limiter'], {'total': qmax}) + self.assertEqual(g['separator'], {'pressure': default_separator_pressure}) json.dumps(g) # DELT with negative limit specified @@ -1208,6 +1369,7 @@ def generator_json(gen, eos = 'we'): self.assertEqual(g['direction'], 'production') self.assertFalse('rate' in g) self.assertFalse('limiter' in g) + self.assertEqual(g['separator'], {'pressure': default_separator_pressure}) json.dumps(g) # DELT with table of PI vs time @@ -1223,6 +1385,7 @@ def generator_json(gen, eos = 'we'): self.assertEqual(g['direction'], 'production') self.assertFalse('rate' in g) self.assertFalse('limiter' in g) + self.assertEqual(g['separator'], {'pressure': default_separator_pressure}) json.dumps(g) # DELT with table of cutoff pressure vs enthalpy and total flow limiter @@ -1239,7 +1402,8 @@ def generator_json(gen, eos = 'we'): 'productivity': PI}) self.assertEqual(g['direction'], 'production') self.assertFalse('rate' in g) - self.assertEqual(g['limiter'], {'type': 'total', 'limit': qmax}) + self.assertEqual(g['limiter'], {'total': qmax}) + self.assertEqual(g['separator'], {'pressure': default_separator_pressure}) json.dumps(g) # DELW with liquid flow limiter @@ -1253,7 +1417,7 @@ def generator_json(gen, eos = 'we'): self.assertEqual(g['deliverability'], {'pressure': Pwb, 'productivity': PI}) self.assertEqual(g['direction'], 'production') self.assertFalse('rate' in g) - self.assertEqual(g['limiter'], {'type': 'water', 'limit': qmax}) + self.assertEqual(g['limiter'], {'water': qmax}) json.dumps(g) # DELW with negative limit specified @@ -1268,6 +1432,7 @@ def generator_json(gen, eos = 'we'): self.assertEqual(g['direction'], 'production') self.assertFalse('rate' in g) self.assertFalse('limiter' in g) + self.assertEqual(g['separator'], {'pressure': default_separator_pressure}) json.dumps(g) # DELW with table of PI vs time @@ -1283,6 +1448,7 @@ def generator_json(gen, eos = 'we'): self.assertEqual(g['direction'], 'production') self.assertFalse('rate' in g) self.assertFalse('limiter' in g) + self.assertEqual(g['separator'], {'pressure': default_separator_pressure}) json.dumps(g) # DELW with table of cutoff pressure vs enthalpy and liquid flow limiter @@ -1299,7 +1465,8 @@ def generator_json(gen, eos = 'we'): 'productivity': PI}) self.assertEqual(g['direction'], 'production') self.assertFalse('rate' in g) - self.assertEqual(g['limiter'], {'type': 'water', 'limit': qmax}) + self.assertEqual(g['limiter'], {'water': qmax}) + self.assertEqual(g['separator'], {'pressure': default_separator_pressure}) json.dumps(g) # RECH with specified mass flow (effectively same as MASS) @@ -1367,26 +1534,289 @@ def generator_json(gen, eos = 'we'): self.assertEqual(g['deliverability'], {'pressure': Pwb, 'productivity': PI, 'threshold': Pthreshold}) self.assertEqual(g['direction'], 'production') - self.assertFalse('limiter' in g) + self.assertEqual(g['limiter'], {'total': abs(q)}) + self.assertEqual(g['separator'], {'pressure': Pwb}) json.dumps(g) # MASD with mass table t = [0., 10., 240., 350., 750.] q = [-10., -12., -14., -13., -16.] + gx = 16 PI = 1.e-12 Pwb = 2.5e5 Pthreshold = 3.e5 gen = t2generator(name = name, block = blkname, type = 'MASD', ex = PI, fg = Pwb, hg = Pthreshold, - time = t, rate = q) + time = t, rate = q, gx = gx) g = generator_json(gen) self.assertEqual(g['rate'], [list(r) for r in zip(t, q)]) self.assertEqual(g['deliverability'], {'pressure': Pwb, 'productivity': PI, 'threshold': Pthreshold}) self.assertEqual(g['direction'], 'production') + self.assertEqual(g['limiter'], {'total': gx}) + self.assertEqual(g['separator'], {'pressure': Pwb}) + json.dumps(g) + + # DMAK + PI = 1.e-12 + Pwb = 2.e5 + qmax = 10. + gen = t2generator(name = name, block = blkname, + type = 'DMAK', gx = PI, ex = Pwb, hg = qmax) + g = generator_json(gen) + self.assertEqual(g['deliverability'], {'pressure': Pwb, 'productivity': PI}) + self.assertEqual(g['direction'], 'production') + self.assertFalse('rate' in g) + self.assertEqual(g['limiter'], {'steam': qmax}) + self.assertEqual(g['separator'], {'pressure': default_separator_pressure}) + json.dumps(g) + + # DMAK with 2-stage separator + gen = t2generator(name = name, block = blkname, + type = 'DMAK', gx = PI, ex = Pwb, fg = -1, hg = qmax) + g = generator_json(gen) + self.assertEqual(g['deliverability'], {'pressure': Pwb, 'productivity': PI}) + self.assertEqual(g['direction'], 'production') + self.assertFalse('rate' in g) + self.assertEqual(g['limiter'], {'steam': qmax}) + self.assertEqual(g['separator'], {'pressure': default_2_stage_separator_pressure}) + json.dumps(g) + + # DMAT + PI = 1.e-12 + Pwb = 2.e5 + qmax = 20. + psep = 65.e5 + gen = t2generator(name = name, block = blkname, + type = 'DMAT', gx = PI, ex = Pwb, hg = qmax, fg = psep) + g = generator_json(gen) + self.assertEqual(g['deliverability'], {'pressure': Pwb, 'productivity': PI}) + self.assertEqual(g['direction'], 'production') + self.assertFalse('rate' in g) + self.assertEqual(g['limiter'], {'total': qmax}) + self.assertEqual(g['separator'], {'pressure': psep}) + json.dumps(g) + + # IMAK + inj = 1.e-4 + P0 = 45.e5 + qmax = 20. + h = 4.4e5 + gen = t2generator(name = name, block = blkname, + type = 'IMAK', gx = qmax, ex = h, hg = P0, fg = inj) + g = generator_json(gen) + self.assertFalse('rate' in g) + self.assertEqual(g['injectivity'], {'coefficient': inj, 'pressure': P0}) + self.assertFalse('enthalpy' in g) # enthalpy defined in reinjector output + self.assertEqual(g['direction'], 'injection') + self.assertEqual(g['limiter'], {'total': qmax}) + json.dumps(g) + + # XINJ (with no limiter) + inj = 1.e-4 + P0 = 45.e5 + h = 4.4e5 + gen = t2generator(name = name, block = blkname, + type = 'XINJ', ex = h, hg = P0, fg = inj) + g = generator_json(gen) + self.assertFalse('rate' in g) + self.assertEqual(g['injectivity'], {'coefficient': inj, 'pressure': P0}) + self.assertEqual(g['enthalpy'], h) + self.assertEqual(g['direction'], 'injection') self.assertFalse('limiter' in g) json.dumps(g) + def network_test(): + + dat.clear_generators() + gen = t2generator(name = 'abc 1', block = ' a 1', type = 'DELG') + dat.add_generator(gen) + gen = t2generator(name = 'abc 2', block = ' a 2', type = 'DELG') + dat.add_generator(gen) + j = dat.generators_json(geo, 'we') + self.assertFalse('network' in j) + + # reset reinjection + gen = t2generator(name = 'chk 1', block = 'chk99', type = 'FINJ', + gx = 1e5, ex = 85e3, hg = 1., fg = 1.) + dat.add_generator(gen) + # add two makeup wells + gen = t2generator(name = 'foo 1', block = ' a 1', type = 'DMAK') + dat.add_generator(gen) + gen = t2generator(name = 'foo 2', block = ' a 2', type = 'DMAK') + dat.add_generator(gen) + j = dat.generators_json(geo, 'we') + self.assertTrue('network' in j) + self.assertEqual(len(j['network']['group']), 1) + grp = j['network']['group'][-1] + self.assertEqual(grp['name'], 'reinjector group 1') + self.assertEqual(grp['in'], ['abc 1', 'abc 2']) + self.assertFalse('scaling' in grp) + self.assertFalse('limiter' in grp) + + # add TMAK + gen = t2generator(block = ' a 1', type = 'TMAK', + gx = 100., hg = -1) + dat.add_generator(gen) + j = dat.generators_json(geo, 'we') + self.assertEqual(len(j['source']), 5) + self.assertEqual(len(j['network']['group']), 2) + grp = j['network']['group'][-1] + self.assertEqual(grp['name'], 'makeup 1') + self.assertEqual(grp['in'], ['foo 1', 'foo 2']) + self.assertEqual(grp['scaling'], 'uniform') + self.assertEqual(grp['limiter'], {'total': 100}) + + # add two water reinjection wells + q1, h1 = 1.1, 85.e3 + gen = t2generator(name = 'inj 1', block = ' b 1', type = 'FINJ', + gx = q1, ex = h1, hg = 1.) + dat.add_generator(gen) + f2, h2 = 0.3, 90.e3 + gen = t2generator(name = 'inj 2', block = ' b 2', type = 'PINJ', + ex = h2, hg = f2) + dat.add_generator(gen) + j = dat.generators_json(geo, 'we') + self.assertEqual(len(j['source']), 7) + self.assertEqual(len(j['network']['group']), 2) + self.assertEqual(len(j['network']['reinject']), 2) + r = j['network']['reinject'][-1] + self.assertEqual(r['name'], 'reinjector 2') + self.assertEqual(r['in'], 'makeup 1') + self.assertEqual(len(r['water']), 2) + self.assertEqual(len(r['steam']), 0) + self.assertEqual(r['water'][0], {'out': 'inj 1', 'rate': q1, 'enthalpy': h1}) + self.assertEqual(r['water'][1], {'out': 'inj 2', 'proportion': f2, 'enthalpy': h2}) + self.assertFalse('overflow' in r) + + # add two RINJ reinjection wells + h3, f3 = 82e3, 0.2 + gen = t2generator(name = 'inj 3', block = ' b 3', type = 'RINJ', + ex = h3, hg = f3) + dat.add_generator(gen) + h4, f4 = 77.e3, 0.35 + gen = t2generator(name = 'inj 4', block = ' b 4', type = 'RINJ', + ex = h4, hg = f4, fg = 1.) + dat.add_generator(gen) + j = dat.generators_json(geo, 'we') + self.assertEqual(len(j['source']), 9) + self.assertEqual(len(j['network']['group']), 2) + self.assertEqual(len(j['network']['reinject']), 3) + r = j['network']['reinject'][1] + self.assertEqual(r['overflow'], 'reinjector 3') + r = j['network']['reinject'][2] + self.assertEqual(r['name'], 'reinjector 3') + self.assertFalse('in' in r) + self.assertEqual(len(r['water']), 2) + self.assertEqual(len(r['steam']), 0) + self.assertEqual(r['water'][0], {'out': 'inj 3', 'proportion': f3, 'enthalpy': h3}) + self.assertEqual(r['water'][1], {'out': 'inj 4', 'proportion': f4, 'enthalpy': h4}) + + # add more production wells and a second TMAK + gen = t2generator(name = 'foo 3', block = ' a 3', type = 'DELG') + dat.add_generator(gen) + gen = t2generator(name = 'foo 4', block = ' a 4', type = 'DMAK') + dat.add_generator(gen) + gen = t2generator(name = 'foo 5', block = ' a 5', type = 'DELG') + dat.add_generator(gen) + gen = t2generator(name = 'foo 6', block = ' a 6', type = 'DMAT') + dat.add_generator(gen) + gen = t2generator(name = 'tmk 2', block = ' a 1', type = 'TMAK', + gx = 50., ex = 20, hg = -2) + dat.add_generator(gen) + j = dat.generators_json(geo, 'we') + self.assertEqual(len(j['network']['group']), 3) + grp = j['network']['group'][2] + self.assertEqual(grp['name'], 'tmk 2') + self.assertEqual(grp['in'], ['foo 4', 'foo 6']) + self.assertEqual(grp['scaling'], 'progressive') + self.assertEqual(grp['limiter'], {'total': 50, 'steam': 20}) + + # add three more reinjection wells including one IMAK + h5, q5 = 87e3, 1.5 + gen = t2generator(name = 'inj 5', block = ' b 5', type = 'FINJ', + gx = q5, ex = h5, hg = 1.) + dat.add_generator(gen) + q6, h6, P6, finj6 = 10., 1200.e3, 5e5, 1e-7 + gen = t2generator(name = 'inj 6', block = ' b 6', type = 'IMAK', + gx = q6, ex = h6, hg = P6, fg = -finj6) + dat.add_generator(gen) + f7, h7 = 0.1, 1400.e3 + gen = t2generator(name = 'inj 7', block = ' c 1', type = 'PINJ', + ex = h7, hg = -f7, fg = 1.) + dat.add_generator(gen) + j = dat.generators_json(geo, 'we') + q = j['source'][-2] + self.assertEqual(q['direction'], 'injection') + self.assertEqual(q['limiter'], {'total': q6}) + self.assertEqual(q['injectivity'], {'pressure': P6, 'coefficient': finj6}) + self.assertEqual(len(j['network']['group']), 4) + grp = j['network']['group'][3] + self.assertEqual(grp['name'], 'reinjector group 4') + self.assertEqual(grp['in'], ['foo 3', 'foo 5', 'tmk 2']) + self.assertFalse('scaling' in grp) + self.assertFalse('limiter' in grp) + self.assertEqual(len(j['source']), 16) + self.assertEqual(len(j['network']['reinject']), 4) + r = j['network']['reinject'][3] + self.assertEqual(r['name'], 'reinjector 4') + self.assertEqual(r['in'], 'reinjector group 4') + self.assertEqual(len(r['water']), 1) + self.assertEqual(len(r['steam']), 2) + self.assertEqual(r['water'][0], {'out': 'inj 5', 'rate': q5, 'enthalpy': h5}) + self.assertEqual(r['steam'][0], {'out': 'inj 6', 'enthalpy': h6}) + self.assertEqual(r['steam'][1], {'out': 'inj 7', 'proportion': f7, 'enthalpy': h7}) + self.assertFalse('overflow' in r) + + # two TMAKs contributing to a single reinjector + dat.clear_generators() + gen = t2generator(name = 'foo 1', block = ' a 4', type = 'DMAK') + dat.add_generator(gen) + gen = t2generator(name = 'foo 2', block = ' a 5', type = 'DMAK') + dat.add_generator(gen) + gen = t2generator(name = 'tmk 1', block = ' a 1', type = 'TMAK', + gx = 50, ex = 20, hg = -2) + dat.add_generator(gen) + gen = t2generator(name = 'foo 3', block = ' a 6', type = 'DMAK') + dat.add_generator(gen) + gen = t2generator(name = 'foo 4', block = ' a 3', type = 'DMAK') + dat.add_generator(gen) + gen = t2generator(name = 'tmk 2', block = ' a 2', type = 'TMAK', + gx = 60, ex = 30, hg = -2) + dat.add_generator(gen) + h1, q1 = 87e3, 1.5 + gen = t2generator(name = 'inj 1', block = ' b 1', type = 'FINJ', + gx = q1, ex = h1, hg = 1.) + dat.add_generator(gen) + f2, h2 = 0.3, 90.e3 + gen = t2generator(name = 'inj 2', block = ' c 1', type = 'PINJ', + ex = h2, hg = f2, fg = 1.) + dat.add_generator(gen) + j = dat.generators_json(geo, 'we') + self.assertEqual(len(j['source']), 6) + self.assertEqual(len(j['network']['group']), 3) + self.assertEqual(len(j['network']['reinject']), 1) + grp = j['network']['group'][0] + self.assertEqual(grp['name'], 'tmk 1') + self.assertEqual(grp['in'], ['foo 1', 'foo 2']) + self.assertEqual(grp['scaling'], 'progressive') + self.assertEqual(grp['limiter'], {'total': 50, 'steam': 20}) + grp = j['network']['group'][1] + self.assertEqual(grp['name'], 'tmk 2') + self.assertEqual(grp['in'], ['foo 3', 'foo 4']) + self.assertEqual(grp['scaling'], 'progressive') + self.assertEqual(grp['limiter'], {'total': 60, 'steam': 30}) + grp = j['network']['group'][2] + self.assertEqual(grp['name'], 'reinjector group 1') + self.assertEqual(grp['in'], ['tmk 1', 'tmk 2']) + self.assertFalse('scaling' in grp) + r = j['network']['reinject'][0] + self.assertEqual(r['name'], 'reinjector 1') + self.assertEqual(r['in'], 'reinjector group 1') + self.assertEqual(len(r['water']), 2) + self.assertFalse('steam' in r) + self.assertFalse('overflow' in r) + def boundaries_test(): nx, ny, nz = 2, 2, 3 @@ -1401,10 +1831,12 @@ def boundaries_test(): eos = 'w' P0, T0 = 1.e5, 15. bdy_incons = dat.grid.incons((P0, T0)) - j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, mesh_coords) + j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, + convert_primary_eos_1, mesh_coords) self.assertEqual(len(j['boundaries']), 1) self.assertEqual(j['boundaries'][0]['primary'], [P0]) self.assertEqual(j['boundaries'][0]['region'], 1) + self.assertFalse('tracer' in j['boundaries'][0]) self.assertEqual(j['boundaries'][0]['faces']['normal'], [0, 0, 1]) self.assertEqual(j['boundaries'][0]['faces']['cells'], [0, 1, 2, 3]) json.dumps(j) @@ -1413,22 +1845,43 @@ def boundaries_test(): eos = 'we' P0, T0 = 1.e5, 15. bdy_incons = dat.grid.incons((P0, T0)) - j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, mesh_coords) + j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, + convert_primary_eos_1, mesh_coords) + self.assertEqual(len(j['boundaries']), 1) + self.assertEqual(j['boundaries'][0]['primary'], [P0, T0]) + self.assertEqual(j['boundaries'][0]['region'], 1) + self.assertFalse('tracer' in j['boundaries'][0]) + self.assertEqual(j['boundaries'][0]['faces']['normal'], [0, 0, 1]) + self.assertEqual(j['boundaries'][0]['faces']['cells'], [0, 1, 2, 3]) + json.dumps(j) + + # pure water + tracer, liquid top BCs + dat.multi['eos'] = 'EWT' + eos = 'we' + P0, T0, X0 = 1.e5, 15., 1.e-6 + bdy_incons = dat.grid.incons((P0, T0, X0)) + j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, + convert_primary_eos_1, mesh_coords, + {'name': 'tracer'}) self.assertEqual(len(j['boundaries']), 1) self.assertEqual(j['boundaries'][0]['primary'], [P0, T0]) self.assertEqual(j['boundaries'][0]['region'], 1) + self.assertEqual(j['boundaries'][0]['tracer'], X0) self.assertEqual(j['boundaries'][0]['faces']['normal'], [0, 0, 1]) self.assertEqual(j['boundaries'][0]['faces']['cells'], [0, 1, 2, 3]) json.dumps(j) + dat.multi['eos'] = 'EW' # pure water, dry steam top BCs eos = 'we' P0, T0 = 0.8e5, 100. bdy_incons = dat.grid.incons((P0, T0)) - j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, mesh_coords) + j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, + convert_primary_eos_1, mesh_coords) self.assertEqual(len(j['boundaries']), 1) self.assertEqual(j['boundaries'][0]['primary'], [P0, T0]) self.assertEqual(j['boundaries'][0]['region'], 2) + self.assertFalse('tracer' in j['boundaries'][0]) self.assertEqual(j['boundaries'][0]['faces']['normal'], [0, 0, 1]) self.assertEqual(j['boundaries'][0]['faces']['cells'], [0, 1, 2, 3]) json.dumps(j) @@ -1437,10 +1890,12 @@ def boundaries_test(): eos = 'we' P0, Sv0 = 3.e5, 0.2 bdy_incons = dat.grid.incons((P0, Sv0)) - j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, mesh_coords) + j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, + convert_primary_eos_1, mesh_coords) self.assertEqual(len(j['boundaries']), 1) self.assertEqual(j['boundaries'][0]['primary'], [P0, Sv0]) self.assertEqual(j['boundaries'][0]['region'], 4) + self.assertFalse('tracer' in j['boundaries'][0]) self.assertEqual(j['boundaries'][0]['faces']['normal'], [0, 0, 1]) self.assertEqual(j['boundaries'][0]['faces']['cells'], [0, 1, 2, 3]) json.dumps(j) @@ -1449,10 +1904,12 @@ def boundaries_test(): eos = 'wae' P0, T0, Pa0 = 1.e5, 15., 0.1e5 bdy_incons = dat.grid.incons((P0, T0, Pa0)) - j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, mesh_coords) + j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, + convert_primary_eos_2_or_4, mesh_coords) self.assertEqual(len(j['boundaries']), 1) self.assertEqual(j['boundaries'][0]['primary'], [P0, T0, Pa0]) self.assertEqual(j['boundaries'][0]['region'], 1) + self.assertFalse('tracer' in j['boundaries'][0]) self.assertEqual(j['boundaries'][0]['faces']['normal'], [0, 0, 1]) self.assertEqual(j['boundaries'][0]['faces']['cells'], [0, 1, 2, 3]) json.dumps(j) @@ -1475,7 +1932,8 @@ def boundaries_test(): Pb, Tb = 4.e5, 25. for blk in dat.grid.blocklist[-geo.num_columns:]: bdy_incons[blk.name] = (Pb, Tb) - j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, mesh_coords) + j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, + convert_primary_eos_1, mesh_coords) self.assertEqual(len(j['boundaries']), 8) for ibc, bc in enumerate(j['boundaries'][:4]): self.assertEqual(bc, @@ -1508,7 +1966,8 @@ def boundaries_test(): Ps, Ts = 3.e5, 20. for blk in dat.grid.blocklist[-len(cell_indices):]: bdy_incons[blk.name] = (Ps, Ts) - j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, mesh_coords) + j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, + convert_primary_eos_1, mesh_coords) self.assertEqual(len(j['boundaries']), 12) for ibc, bc in enumerate(j['boundaries'][-4:]): cellindex = cell_indices[ibc] @@ -1523,7 +1982,8 @@ def boundaries_test(): eos = 'we' P0, T0 = 1.e5, 15. bdy_incons = dat.grid.incons((P0, T0)) - j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, mesh_coords) + j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, + convert_primary_eos_1, mesh_coords) self.assertEqual(len(j['boundaries']), 1) self.assertEqual(j['boundaries'][0]['primary'], [P0, T0]) self.assertEqual(j['boundaries'][0]['region'], 1) @@ -1546,7 +2006,8 @@ def boundaries_test(): Pb, Tb = 4.e5, 25. for blk in dat.grid.blocklist[-geo.num_columns:]: bdy_incons[blk.name] = (Pb, Tb) - j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, mesh_coords) + j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, + convert_primary_eos_1, mesh_coords) self.assertEqual(len(j['boundaries']), 8) for ibc, bc in enumerate(j['boundaries'][:4]): self.assertEqual(bc, @@ -1585,10 +2046,12 @@ def boundaries_test(): Ps, Ts = 3.e5, 20. for blk in dat.grid.blocklist[-len(cell_indices):]: bdy_incons[blk.name] = (Ps, Ts) - j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, mesh_coords) + j = dat.boundaries_json(geo, bdy_incons, atmos_volume, eos, + convert_primary_eos_1, mesh_coords) self.assertEqual(len(j['boundaries']), 1) self.assertEqual(j['boundaries'][0]['primary'], [Ps, Ts]) self.assertEqual(j['boundaries'][0]['region'], 1) + self.assertFalse('tracer' in j['boundaries'][0]) self.assertEqual(j['boundaries'][0]['faces']['normal'], [1, 0]) self.assertEqual(j['boundaries'][0]['faces']['cells'], cell_indices) json.dumps(j) @@ -1600,9 +2063,10 @@ def boundaries_test(): timestepping_test() relative_permeability_test() capillary_pressure_test() - primary_to_region_test() + convert_primary_test() initial_test() generators_test() + network_test() boundaries_test() if __name__ == '__main__': diff --git a/tests/test_t2listing.py b/tests/test_t2listing.py index 07d2098c..1a7631b5 100755 --- a/tests/test_t2listing.py +++ b/tests/test_t2listing.py @@ -736,6 +736,23 @@ def test_TOUGHREACT_1(self): self.listing.close() + def test_TOUGHREACT_2(self): + """TOUGHREACT case 2""" + + self.base = os.path.join('listing', 'TOUGHREACT', '2', 'case2') + self.listing = t2listing(self.base + '.out') + + self.assertEqual(self.listing.simulator, 'TOUGHREACT') + self.assertEqual(self.listing.num_fulltimes, 3) + + self.table_test(25, ['element']) + + self.table_spot_test(27, 'element', ' ag 3', 'P', 0.13243E+07) + self.history_test(('e', ' c 5', 'P'), ((0.100000E+01, 0.315360E+08, 0.630720E+08), + (0.10130E+06, 0.23016E+07, 0.23030E+07))) + + self.listing.close() + #-------------------------------------------------------------------------------- # TOUGH3 tests