From beb87d1f368eeeab675b65e5b6efc64a3a506507 Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Tue, 25 Feb 2014 11:20:27 -0800 Subject: [PATCH 01/32] initial commit --- .travis.yml | 25 +++ LICENSE | 20 +++ README.md | 21 +++ docs/Makefile | 153 +++++++++++++++++++ docs/api_Richards.rst | 38 +++++ docs/conf.py | 244 ++++++++++++++++++++++++++++++ docs/index.rst | 45 ++++++ docs/make.bat | 190 +++++++++++++++++++++++ docs/simpeg-logo.png | Bin 0 -> 23545 bytes requirements.txt | 4 + setup.py | 8 + simpegFLOW/Richards/__init__.py | 1 + simpegFLOW/Tests/__init__.py | 12 ++ simpegFLOW/Tests/test_Richards.py | 56 +++++++ simpegFLOW/__init__.py | 1 + 15 files changed, 818 insertions(+) create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 docs/Makefile create mode 100644 docs/api_Richards.rst create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 docs/make.bat create mode 100644 docs/simpeg-logo.png create mode 100644 requirements.txt create mode 100644 setup.py create mode 100644 simpegFLOW/Richards/__init__.py create mode 100644 simpegFLOW/Tests/__init__.py create mode 100644 simpegFLOW/Tests/test_Richards.py create mode 100644 simpegFLOW/__init__.py diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..1272ce31 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,25 @@ +language: python +python: + - "2.7" +virtualenv: + system_site_packages: true +before_install: + - sudo apt-get install -qq gcc gfortran libblas-dev liblapack-dev python-numpy python-scipy python-matplotlib python-pip + - sudo pip install scipy --upgrade + - sudo pip install numpy --upgrade + - cd ../ + - git clone https://github.com/simpeg/simpeg.git + - cd simpeg/SimPEG/ + - python setup.py + - cd ../../ + - echo export PYTHONPATH=$PYTHONPATH:/home/travis/build/simpeg/simpeg >> .bashrc + - source .bashrc + - cd simpegflow +# command to install dependencies +install: "pip install -r requirements.txt --use-mirrors" +# command to run tests +script: nosetests -v + +notifications: + email: + - rowanc1@gmail.com diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..f94a23fd --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013-2014 SimPEG Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..947fadb6 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +simpegflow +********** + +A groundwater forward modelling and inversion package for SimPEG. + + + +Documentation: +[http://simpegflow.readthedocs.org/en/latest/](http://simpegflow.readthedocs.org/en/latest/) + +Code: +[https://github.com/simpeg/simpegflow](https://github.com/simpeg/simpegflow) + +Tests: +[https://travis-ci.org/simpeg/simpegflow](https://travis-ci.org/simpeg/simpegflow) + +Build Status: +[![Build Status](https://travis-ci.org/simpeg/simpegflow.png)](https://travis-ci.org/simpeg/simpegflow) + +Bugs & Issues: +[https://github.com/simpeg/simpegflow/issues](https://github.com/simpeg/simpegflow/issues) diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..bee80244 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/SimPEG.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/SimPEG.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/SimPEG" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SimPEG" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/docs/api_Richards.rst b/docs/api_Richards.rst new file mode 100644 index 00000000..084ae86e --- /dev/null +++ b/docs/api_Richards.rst @@ -0,0 +1,38 @@ +.. _api_Richards: + +There are two different forms of Richards equation that differ +on how they deal with the non-linearity in the time-stepping term. + +The most fundamental form, referred to as the +'mixed'-form of Richards Equation [Celia et al., 1990] + +.. math:: + + \\frac{\partial \\theta(\psi)}{\partial t} - \\nabla \cdot k(\psi) \\nabla \psi - \\frac{\partial k(\psi)}{\partial z} = 0 + \quad \psi \in \Omega + +where theta is water content, and psi is pressure head. +This formulation of Richards equation is called the +'mixed'-form because the equation is parameterized in psi +but the time-stepping is in terms of theta. + +As noted in [Celia et al., 1990] the 'head'-based form of Richards +equation can be written in the continuous form as: + +.. math:: + + \\frac{\partial \\theta}{\partial \psi}\\frac{\partial \psi}{\partial t} - \\nabla \cdot k(\psi) \\nabla \psi - \\frac{\partial k(\psi)}{\partial z} = 0 + \quad \psi \in \Omega + +However, it can be shown that this does not conserve mass in the discrete formulation. + + + +Richards +======== + +.. automodule:: simpegFLOW.Richards + :show-inheritance: + :members: + :undoc-members: + :inherited-members: diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..86aacce4 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,244 @@ +# -*- coding: utf-8 -*- +# +# SimPEG documentation build configuration file, created by +# sphinx-quickstart on Fri Aug 30 18:42:44 2013. +# +# 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. + +import sys, os + +# 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. +#sys.path.insert(0, os.path.abspath('.')) + +sys.path.append('../') + +# -- 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.todo', 'sphinx.ext.mathjax', 'sphinx.ext.viewcode', 'sphinx.ext.autodoc', 'matplotlib.sphinxext.plot_directive'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'SimPEG' +copyright = u'2013, SimPEG Developers' + +# 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 = '0.0.1' +# The full version, including alpha/beta/rc tags. +release = '0.0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- 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 = 'default' + +# 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 themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# 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'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'SimPEGdoc' + + +# -- 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': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'SimPEG.tex', u'SimPEG Documentation', + u'Rowan Cockett', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'simpeg', u'SimPEG Documentation', + [u'Rowan Cockett'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- 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 = [ + ('index', 'SimPEG', u'SimPEG Documentation', + u'Rowan Cockett', 'SimPEG', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..b2966f24 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,45 @@ +.. image:: simpeg-logo.png + :width: 300 px + :alt: SimPEG + :align: center + +SimPEG (Simulation and Parameter Estimation in Geophysics) is a python +package for simulation and gradient based parameter estimation in the +context of geoscience applications. + +simpegFLOW uses SimPEG as the framework for the forward and inverse +problems in groundwater flow. + + +Richards +******** + +.. toctree:: + :maxdepth: 2 + + api_Richards + + +Testing SimPEG +============== + +* Master Branch + .. image:: https://travis-ci.org/simpeg/simpegflow.png?branch=master + :target: https://travis-ci.org/simpeg/simpegflow + :alt: Master Branch + :align: center + +* Develop Branch + .. image:: https://travis-ci.org/simpeg/simpegflow.png?branch=develop + :target: https://travis-ci.org/simpeg/simpegflow + :alt: Develop Branch + :align: center + + +Project Index & Search +====================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..2ac3df69 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,190 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\SimPEG.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\SimPEG.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/docs/simpeg-logo.png b/docs/simpeg-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a0ec740f135721f5cf7d1c430ae2908c20ed4547 GIT binary patch literal 23545 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
>hg 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 Date: Tue, 25 Feb 2014 11:35:13 -0800 Subject: [PATCH 02/32] Initial commit of richards equation code. Forward working. Inverse untested. --- docs/api_Richards.rst | 8 +- simpegFLOW/Richards/BaseRichards.py | 256 +++++++++++++++++++++++ simpegFLOW/Richards/RichardsProblem.py | 278 +++++++++++++++++++++++++ simpegFLOW/Richards/__init__.py | 3 +- simpegFLOW/Tests/test_Richards.py | 213 ++++++++++++++++++- 5 files changed, 752 insertions(+), 6 deletions(-) create mode 100644 simpegFLOW/Richards/BaseRichards.py create mode 100644 simpegFLOW/Richards/RichardsProblem.py diff --git a/docs/api_Richards.rst b/docs/api_Richards.rst index 084ae86e..9bb787e3 100644 --- a/docs/api_Richards.rst +++ b/docs/api_Richards.rst @@ -1,5 +1,9 @@ .. _api_Richards: + +Richards Equation +***************** + There are two different forms of Richards equation that differ on how they deal with the non-linearity in the time-stepping term. @@ -8,7 +12,7 @@ The most fundamental form, referred to as the .. math:: - \\frac{\partial \\theta(\psi)}{\partial t} - \\nabla \cdot k(\psi) \\nabla \psi - \\frac{\partial k(\psi)}{\partial z} = 0 + \frac{\partial \theta(\psi)}{\partial t} - \nabla \cdot k(\psi) \nabla \psi - \frac{\partial k(\psi)}{\partial z} = 0 \quad \psi \in \Omega where theta is water content, and psi is pressure head. @@ -21,7 +25,7 @@ equation can be written in the continuous form as: .. math:: - \\frac{\partial \\theta}{\partial \psi}\\frac{\partial \psi}{\partial t} - \\nabla \cdot k(\psi) \\nabla \psi - \\frac{\partial k(\psi)}{\partial z} = 0 + \frac{\partial \theta}{\partial \psi}\frac{\partial \psi}{\partial t} - \nabla \cdot k(\psi) \nabla \psi - \frac{\partial k(\psi)}{\partial z} = 0 \quad \psi \in \Omega However, it can be shown that this does not conserve mass in the discrete formulation. diff --git a/simpegFLOW/Richards/BaseRichards.py b/simpegFLOW/Richards/BaseRichards.py new file mode 100644 index 00000000..04359512 --- /dev/null +++ b/simpegFLOW/Richards/BaseRichards.py @@ -0,0 +1,256 @@ +from SimPEG import Model, Utils, np + + +class RichardsModel(object): + """docstring for RichardsModel""" + + mesh = None #: SimPEG mesh + + @property + def thetaModel(self): + """Model for moisture content""" + return self._thetaModel + + @property + def kModel(self): + """Model for hydraulic conductivity""" + return self._kModel + + def __init__(self, mesh, thetaModel, kModel): + self.mesh = mesh + assert isinstance(thetaModel, Model.BaseNonLinearModel) + assert isinstance(kModel, Model.BaseNonLinearModel) + + self._thetaModel = thetaModel + self._kModel = kModel + + def theta(self, u, m): + return self.thetaModel.transform(u, m) + + def thetaDerivM(self, u, m): + return self.thetaModel.transformDerivM(u, m) + + def thetaDerivU(self, u, m): + return self.thetaModel.transformDerivU(u, m) + + def k(self, u, m): + return self.kModel.transform(u, m) + + def kDerivM(self, u, m): + return self.kModel.transformDerivM(u, m) + + def kDerivU(self, u, m): + return self.kModel.transformDerivU(u, m) + + +class BaseHaverkamp_theta(Model.BaseNonLinearModel): + + theta_s = 0.430 + theta_r = 0.078 + alpha = 0.036 + beta = 3.960 + + def __init__(self, mesh, **kwargs): + Model.BaseNonLinearModel.__init__(self, mesh) + Utils.setKwargs(self, **kwargs) + + def setModel(self, m): + self._currentModel = m + + def transform(self, u, m): + self.setModel(m) + f = (self.alpha*(self.theta_s - self.theta_r )/ + (self.alpha + abs(u)**self.beta) + self.theta_r) + f[u >= 0] = self.theta_s + return f + + def transformDerivM(self, u, m): + self.setModel(m) + + def transformDerivU(self, u, m): + self.setModel(m) + g = (self.alpha*((self.theta_s - self.theta_r)/ + (self.alpha + abs(u)**self.beta)**2) + *(-self.beta*abs(u)**(self.beta-1)*np.sign(u))) + g[u >= 0] = 0 + g = Utils.sdiag(g) + return g + + +class BaseHaverkamp_k(Model.BaseNonLinearModel): + + A = 1.175e+06 + gamma = 4.74 + Ks = np.log(24.96) + + def __init__(self, mesh, **kwargs): + Model.BaseNonLinearModel.__init__(self, mesh) + Utils.setKwargs(self, **kwargs) + + def setModel(self, m): + self._currentModel = m + #TODO: Fix me! + self.Ks = m + + def transform(self, u, m): + self.setModel(m) + f = np.exp(self.Ks)*self.A/(self.A+abs(u)**self.gamma) + if type(self.Ks) is np.ndarray and self.Ks.size > 1: + f[u >= 0] = np.exp(self.Ks[u >= 0]) + else: + f[u >= 0] = np.exp(self.Ks) + return f + + def transformDerivM(self, u, m): + self.setModel(m) + #A + # dA = np.exp(self.Ks)/(self.A+abs(u)**self.gamma) - np.exp(self.Ks)*self.A/(self.A+abs(u)**self.gamma)**2 + #gamma + # dgamma = -(self.A*np.exp(self.Ks)*np.log(abs(u))*abs(u)**self.gamma)/(self.A + abs(u)**self.gamma)**2 + + # This assumes that the the model is Ks + return Utils.sdiag(self.transform(u, m)) + + def transformDerivU(self, u, m): + self.setModel(m) + g = -(np.exp(self.Ks)*self.A*self.gamma*abs(u)**(self.gamma-1)*np.sign(u))/((self.A+abs(u)**self.gamma)**2) + g[u >= 0] = 0 + g = Utils.sdiag(g) + return g + + + +# class Haverkamp(object): +# """docstring for Haverkamp""" + +# empiricalModelName = "VanGenuchten" + +# theta_s = 0.430 +# theta_r = 0.078 +# alpha = 0.036 +# beta = 3.960 +# A = 1.175e+06 +# gamma = 4.74 +# Ks = np.log(24.96) + +# def __init__(self, **kwargs): +# Utils.setKwargs(self, **kwargs) + +# def setModel(self, m): +# self.Ks = m + +# def moistureContent(self, h): +# f = (self.alpha*(self.theta_s - self.theta_r )/ +# (self.alpha + abs(h)**self.beta) + self.theta_r) +# f[h > 0] = self.theta_s +# return f + +# def moistureContentDeriv(self, h): +# g = (self.alpha*((self.theta_s - self.theta_r)/ +# (self.alpha + abs(h)**self.beta)**2) +# *(-self.beta*abs(h)**(self.beta-1)*np.sign(h))); +# g[h >= 0] = 0 +# g = Utils.sdiag(g) +# return g + +# def hydraulicConductivity(self, h): +# f = np.exp(self.Ks)*self.A/(self.A+abs(h)**self.gamma) +# if type(self.Ks) is np.ndarray and self.Ks.size > 1: +# f[h >= 0] = np.exp(self.Ks[h >= 0]) +# else: +# f[h >= 0] = np.exp(self.Ks) +# return f + +# def hydraulicConductivityModelDeriv(self, h): +# #A +# # dA = np.exp(self.Ks)/(self.A+abs(h)**self.gamma) - np.exp(self.Ks)*self.A/(self.A+abs(h)**self.gamma)**2; +# #gamma +# # dgamma = -(self.A*np.exp(self.Ks)*np.log(abs(h))*abs(h)**self.gamma)/(self.A + abs(h)**self.gamma)**2; +# return Utils.sdiag(self.hydraulicConductivity(h)) # This assumes that the the model is Ks + +# def hydraulicConductivityDeriv(self, h): +# g = -(np.exp(self.Ks)*self.A*self.gamma*abs(h)**(self.gamma-1)*np.sign(h))/((self.A+abs(h)**self.gamma)**2) +# g[h >= 0] = 0 +# g = Utils.sdiag(g) +# return g + + +# class VanGenuchten(object): +# """ + +# .. math:: + +# \\theta(h) = \\frac{\\alpha (\\theta_s - \\theta_r)}{\\alpha + |h|^\\beta} + \\theta_r + +# Where parameters alpha, beta, gamma, A are constants in the media; +# theta_r and theta_s are the residual and saturated moisture +# contents; and K_s is the saturated hydraulic conductivity. + +# Celia1990 + +# """ + +# empiricalModelName = "VanGenuchten" + +# theta_s = 0.430 +# theta_r = 0.078 +# alpha = 0.036 +# n = 1.560 +# beta = 3.960 +# I = 0.500 +# Ks = np.log(24.96) + +# def __init__(self, **kwargs): +# Utils.setKwargs(self, **kwargs) + +# def setModel(self, m): +# self.Ks = m + +# def moistureContent(self, h): +# m = 1 - 1.0/self.n; +# f = (( self.theta_s - self.theta_r )/ +# ((1+abs(self.alpha*h)**self.n)**m) + self.theta_r) +# f[h > 0] = self.theta_s +# return f + +# def moistureContentDeriv(self, h): +# g = -self.alpha*self.n*abs(self.alpha*h)**(self.n - 1)*np.sign(self.alpha*h)*(1./self.n - 1)*(self.theta_r - self.theta_s)*(abs(self.alpha*h)**self.n + 1)**(1./self.n - 2) +# g[h > 0] = 0 +# g = Utils.sdiag(g) +# return g + +# def hydraulicConductivity(self, h): +# alpha = self.alpha +# I = self.I +# n = self.n +# Ks = self.Ks +# m = 1.0 - 1.0/n + +# theta_e = 1.0/((1.0+abs(alpha*h)**n)**m) +# f = np.exp(Ks)*theta_e**I* ( ( 1.0 - ( 1.0 - theta_e**(1.0/m) )**m )**2 ) +# if type(self.Ks) is np.ndarray and self.Ks.size > 1: +# f[h >= 0] = np.exp(self.Ks[h >= 0]) +# else: +# f[h >= 0] = np.exp(self.Ks) +# return f + +# def hydraulicConductivityModelDeriv(self, h): +# #alpha +# # dA = I*h*n*np.exp(Ks)*abs(alpha*h)**(n - 1)*np.sign(alpha*h)*(1.0/n - 1)*((abs(alpha*h)**n + 1)**(1.0/n - 1))**(I - 1)*((1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)**2*(abs(alpha*h)**n + 1)**(1.0/n - 2) - (2*h*n*np.exp(Ks)*abs(alpha*h)**(n - 1)*np.sign(alpha*h)*(1.0/n - 1)*((abs(alpha*h)**n + 1)**(1.0/n - 1))**I*((1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)*(abs(alpha*h)**n + 1)**(1.0/n - 2))/(((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1) + 1)*(1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1.0/n)); +# #n +# # dn = 2*np.exp(Ks)*((np.log(1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))*(1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n))/n**2 + ((1.0/n - 1)*(((np.log(abs(alpha*h)**n + 1)*(abs(alpha*h)**n + 1)**(1.0/n - 1))/n**2 - abs(alpha*h)**n*np.log(abs(alpha*h))*(1.0/n - 1)*(abs(alpha*h)**n + 1)**(1.0/n - 2))/((1.0/n - 1)*((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1) + 1)) - np.log((abs(alpha*h)**n + 1)**(1.0/n - 1))/(n**2*(1.0/n - 1)**2*((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))))/(1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1.0/n))*((abs(alpha*h)**n + 1)**(1.0/n - 1))**I*((1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1) - I*np.exp(Ks)*((np.log(abs(alpha*h)**n + 1)*(abs(alpha*h)**n + 1)**(1.0/n - 1))/n**2 - abs(alpha*h)**n*np.log(abs(alpha*h))*(1.0/n - 1)*(abs(alpha*h)**n + 1)**(1.0/n - 2))*((abs(alpha*h)**n + 1)**(1.0/n - 1))**(I - 1)*((1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)**2; +# #I +# # dI = np.exp(Ks)*np.log((abs(alpha*h)**n + 1)**(1.0/n - 1))*((abs(alpha*h)**n + 1)**(1.0/n - 1))**I*((1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)**2; +# return Utils.sdiag(self.hydraulicConductivity(h)) # This assumes that the the model is Ks + +# def hydraulicConductivityDeriv(self, h): +# alpha = self.alpha +# I = self.I +# n = self.n +# Ks = self.Ks +# m = 1.0 - 1.0/n + +# g = I*alpha*n*np.exp(Ks)*abs(alpha*h)**(n - 1.0)*np.sign(alpha*h)*(1.0/n - 1.0)*((abs(alpha*h)**n + 1)**(1.0/n - 1))**(I - 1)*((1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)**2*(abs(alpha*h)**n + 1)**(1.0/n - 2) - (2*alpha*n*np.exp(Ks)*abs(alpha*h)**(n - 1)*np.sign(alpha*h)*(1.0/n - 1)*((abs(alpha*h)**n + 1)**(1.0/n - 1))**I*((1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)*(abs(alpha*h)**n + 1)**(1.0/n - 2))/(((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1) + 1)*(1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1.0/n)) +# g[h >= 0] = 0 +# g = Utils.sdiag(g) +# return g diff --git a/simpegFLOW/Richards/RichardsProblem.py b/simpegFLOW/Richards/RichardsProblem.py new file mode 100644 index 00000000..870d857b --- /dev/null +++ b/simpegFLOW/Richards/RichardsProblem.py @@ -0,0 +1,278 @@ +from SimPEG import * +from BaseRichards import RichardsModel + +class RichardsData(Data.BaseData): + """docstring for RichardsData""" + + P = None + + def __init__(self, **kwargs): + Data.BaseData.__init__(self, **kwargs) + + @property + def dataType(self): + """Choose how your data is collected, must be 'saturation' or 'pressureHead'.""" + return getattr(self, '_dataType', 'pressureHead') + @dataType.setter + def dataType(self, value): + assert value in ['saturation','pressureHead'], "dataType must be 'saturation' or 'pressureHead'." + self._dataType = value + + def projectFields(self, u): + u = np.concatenate(u[1:]) + if self.dataType == 'saturation': + #TODO: Fix this: + u = self.prob.model.theta(MODEL, u) + return self.P*u + + +class RichardsProblem(Problem.BaseProblem): + """docstring for RichardsProblem""" + + timeEnd = None + boundaryConditions = None + initialConditions = None + + dataPair = RichardsData + modelPair = RichardsModel + + def __init__(self, mesh, model, **kwargs): + self.doNewton = False # This also sets the rootFinder algorithm. + Problem.BaseProblem.__init__(self, mesh, model, **kwargs) + + @property + def timeStep(self): + """The time between steps.""" + return getattr(self, '_timeStep', None) + @timeStep.setter + def timeStep(self, value): + self._timeStep = float(value) # Because integers suck. + + @property + def numIts(self): + """The number of iterations in the time domain problem.""" + return int(self.timeEnd/self.timeStep) + + @property + def method(self): + """Method must be either 'mixed' or 'head'. See notes in Celia et al., 1990.""" + return getattr(self, '_method', 'mixed') + @method.setter + def method(self, value): + assert value in ['mixed','head'], "method must be 'mixed' or 'head'." + self._method = value + + @property + def doNewton(self): + """Do a Newton iteration. If False, a Picard iteration will be completed.""" + return self._doNewton + @doNewton.setter + def doNewton(self, value): + value = bool(value) + self.rootFinder = Optimization.NewtonRoot(doLS=value) + self._doNewton = value + + def fields(self, m): + Hs = range(self.numIts+1) + Hs[0] = self.initialConditions + for ii in range(self.numIts): + Hs[ii+1] = self.rootFinder.root(lambda hn1m, return_g=True: self.getResidual(m, Hs[ii], hn1m, return_g=return_g), Hs[ii]) + return Hs + + def diagsJacobian(self, m, hn, hn1): + + DIV = self.mesh.faceDiv + GRAD = self.mesh.cellGrad + BC = self.mesh.cellGradBC + AV = self.mesh.aveCC2F + if self.mesh.dim == 1: + Dz = self.mesh.faceDivx + elif self.mesh.dim == 2: + Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.nFv[0]), self.mesh.faceDivy),format='csr') + elif self.mesh.dim == 3: + Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.nFv[0]+self.mesh.nFv[1]), self.mesh.faceDivz),format='csr') + + bc = self.boundaryConditions + dt = self.timeStep + + dT = self.model.thetaDerivU(hn, m) + dT1 = self.model.thetaDerivU(hn1, m) + K1 = self.model.k(hn1, m) + dK1 = self.model.kDerivU(hn1, m) + dKa1 = self.model.kDerivM(hn1, m) + + # Compute part of the derivative of: + # + # DIV*diag(GRAD*hn1+BC*bc)*(AV*(1.0/K))^-1 + + DdiagGh1 = DIV*Utils.sdiag(GRAD*hn1+BC*bc) + diagAVk2_AVdiagK2 = Utils.sdiag((AV*(1./K1))**(-2)) * AV*Utils.sdiag(K1**(-2)) + + # The matrix that we are computing has the form: + # + # - - - - - - + # | Adiag | | h1 | | b1 | + # | Asub Adiag | | h2 | | b2 | + # | Asub Adiag | | h3 | = | b3 | + # | ... ... | | .. | | .. | + # | Asub Adiag | | hn | | bn | + # - - - - - - + + Asub = (-1.0/dt)*dT + + Adiag = ( + (1.0/dt)*dT1 + -DdiagGh1*diagAVk2_AVdiagK2*dK1 + -DIV*Utils.sdiag(1./(AV*(1./K1)))*GRAD + -Dz*diagAVk2_AVdiagK2*dK1 + ) + + B = DdiagGh1*diagAVk2_AVdiagK2*dKa1 + Dz*diagAVk2_AVdiagK2*dKa1 + + return Asub, Adiag, B + + def getResidual(self, m, hn, h, return_g=True): + """ + Where h is the proposed value for the next time iterate (h_{n+1}) + """ + DIV = self.mesh.faceDiv + GRAD = self.mesh.cellGrad + BC = self.mesh.cellGradBC + AV = self.mesh.aveCC2F + if self.mesh.dim == 1: + Dz = self.mesh.faceDivx + elif self.mesh.dim == 2: + Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.nFv[0]), self.mesh.faceDivy),format='csr') + elif self.mesh.dim == 3: + Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.nFv[0]+self.mesh.nFv[1]), self.mesh.faceDivz),format='csr') + + bc = self.boundaryConditions + dt = self.timeStep + + T = self.model.theta(h, m) + dT = self.model.thetaDerivU(h, m) + Tn = self.model.theta(hn, m) + K = self.model.k(h, m) + dK = self.model.kDerivU(h, m) + + aveK = 1./(AV*(1./K)); + + RHS = DIV*Utils.sdiag(aveK)*(GRAD*h+BC*bc) + Dz*aveK + if self.method == 'mixed': + r = (T-Tn)/dt - RHS + elif self.method == 'head': + r = dT*(h - hn)/dt - RHS + + if not return_g: return r + + J = dT/dt - DIV*Utils.sdiag(aveK)*GRAD + if self.doNewton: + DDharmAve = Utils.sdiag(aveK**2)*AV*Utils.sdiag(K**(-2)) * dK + J = J - DIV*Utils.sdiag(GRAD*h + BC*bc)*DDharmAve - Dz*DDharmAve + + return r, J + + def fullJ(self, m, u=None): + if u is None: + u = self.field(m) + Hs = u + nn = len(Hs)-1 + Asubs, Adiags, Bs = range(nn), range(nn), range(nn) + for ii in range(nn): + Asubs[ii], Adiags[ii], Bs[ii] = self.diagsJacobian(m, Hs[ii],Hs[ii+1]) + Ad = sp.block_diag(Adiags) + zRight = Utils.spzeros((len(Asubs)-1)*Asubs[0].shape[0],Adiags[0].shape[1]) + zTop = Utils.spzeros(Adiags[0].shape[0], len(Adiags)*Adiags[0].shape[1]) + As = sp.vstack((zTop,sp.hstack((sp.block_diag(Asubs[1:]),zRight)))) + A = As + Ad + B = np.array(sp.vstack(Bs).todense()) + + Ainv = Solver(A) + J = Ainv.solve(B) + return J + + + def Jvec(self, m, v, u=None): + if u is None: + u = self.field(m) + Hs = u + JvC = range(len(Hs)-1) # Cell to hold each row of the long vector. + + # This is done via forward substitution. + temp, Adiag, B = self.diagsJacobian(m, Hs[0],Hs[1]) + Adiaginv = Solver(Adiag) + JvC[0] = Adiaginv.solve(B*v) + + # M = @(x) tril(Adiag)\(diag(Adiag).*(triu(Adiag)\x)); + # JvC{1} = bicgstab(Adiag,(B*v),tolbcg,500,M); + + for ii in range(1,len(Hs)-1): + Asub, Adiag, B = self.diagsJacobian(m, Hs[ii],Hs[ii+1]) + Adiaginv = Solver(Adiag) + JvC[ii] = Adiaginv.solve(B*v - Asub*JvC[ii-1]) + + if self.dataType == 'pressureHead': + Jv = self.P*np.concatenate(JvC) + elif self.dataType == 'saturation': + dT = self.model.thetaDerivU(np.concatenate(Hs[1:]), m) + Jv = self.P*dT*np.concatenate(JvC) + + return Jv + + def Jtvec(self, m, v, u=None): + if u is None: + u = self.field(m) + Hs = u + + if self.dataType == 'pressureHead': + PTv = self.P.T*v; + elif self.dataType == 'saturation': + dT = self.model.thetaDerivU(np.concatenate(Hs[1:]), m) + PTv = dT.T*self.P.T*v + + # This is done via backward substitution. + minus = 0 + BJtv = 0 + for ii in range(len(Hs)-1,0,-1): + Asub, Adiag, B = self.diagsJacobian(m, Hs[ii-1], Hs[ii]) + #select the correct part of v + vpart = range((ii-1)*Adiag.shape[0], (ii)*Adiag.shape[0]) + AdiaginvT = Solver(Adiag.T) + JTvC = AdiaginvT.solve(PTv[vpart] - minus) + minus = Asub.T*JTvC # this is now the super diagonal. + BJtv = BJtv + B.T*JTvC + + return BJtv + + + +if __name__ == '__main__': + from SimPEG import * + import Richards + import matplotlib.pyplot as plt + M = Mesh.TensorMesh([np.ones(40)]) + Ks = 9.4400e-03 + E = Richards.Haverkamp(Ks=np.log(Ks), A=1.1750e+06, gamma=4.74, alpha=1.6110e+06, theta_s=0.287, theta_r=0.075, beta=3.96) + bc = np.array([-61.5,-20.7]) + h = np.zeros(M.nC) + bc[0] + + # data = R + prob = Richards.RichardsProblem(M,E, timeStep=10, timeEnd=100, boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') + + q = sp.csr_matrix((np.ones(4),(np.arange(4),np.array([20, 30, 35, 38]))), shape=(4,M.nCx)) + P = sp.kron(sp.identity(prob.numIts),q) + + prob.dataType = 'pressureHead' + mTrue = np.ones(M.nC)*np.log(Ks) + stdev = 0.01 # The standard deviation for the noise + data = prob.createSyntheticData(mTrue,std=stdev, P=P) + p = plt.plot(data.dobs.reshape((-1,4))) + plt.show() + # opt = Optimization.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) + # reg = Regularization.Tikhonov(model) + # inv = Inversion.BaseInversion(prob, reg, opt, beta0=1e4) + # derChk = lambda m: [inv.dataObj(m), inv.dataObjDeriv(m)] + # print inv.dataObj(mTrue*0+np.log(1e-5)) + # print inv.dataObj(mTrue) + # tests.checkDerivative(derChk, mTrue, plotIt=False) + diff --git a/simpegFLOW/Richards/__init__.py b/simpegFLOW/Richards/__init__.py index 85657732..bf9832f7 100644 --- a/simpegFLOW/Richards/__init__.py +++ b/simpegFLOW/Richards/__init__.py @@ -1 +1,2 @@ -#blank! +from BaseRichards import * +from RichardsProblem import * diff --git a/simpegFLOW/Tests/test_Richards.py b/simpegFLOW/Tests/test_Richards.py index 72c1cbf5..08befb01 100644 --- a/simpegFLOW/Tests/test_Richards.py +++ b/simpegFLOW/Tests/test_Richards.py @@ -2,12 +2,11 @@ import unittest from SimPEG import * from SimPEG.Tests.TestUtils import OrderTest, checkDerivative from scipy.sparse.linalg import dsolve -import simpegFLOW.Richards +import simpegFLOW.Richards as Richards TOL = 1E-8 -class EmpiricalRelations(unittest.TestCase): - +class TestModels(unittest.TestCase): def test_BaseHaverkamp_Theta(self): mesh = Mesh.TensorMesh([50]) @@ -52,5 +51,213 @@ class EmpiricalRelations(unittest.TestCase): # passed = checkDerivative(wrapper, np.random.randn(n), plotIt=False) # self.assertTrue(passed,True) + # def test_VanGenuchten_moistureContent(self): + # print 'VanGenuchten_moistureContent' + # vanG = Richards.VanGenuchten() + # def wrapper(x): + # return vanG.moistureContent(x), vanG.moistureContentDeriv(x) + # passed = checkDerivative(wrapper, np.random.randn(50), plotIt=False) + # self.assertTrue(passed,True) + + # def test_VanGenuchten_hydraulicConductivity(self): + # print 'VanGenuchten_hydraulicConductivity' + # hav = Richards.VanGenuchten() + # def wrapper(x): + # return hav.hydraulicConductivity(x), hav.hydraulicConductivityDeriv(x) + # passed = checkDerivative(wrapper, np.random.randn(50), plotIt=False) + # self.assertTrue(passed,True) + + # def test_VanGenuchten_hydraulicConductivity_FullKs(self): + # print 'VanGenuchten_hydraulicConductivity_FullKs' + # n = 50 + # hav = Richards.VanGenuchten(Ks=np.random.rand(n)) + # def wrapper(x): + # return hav.hydraulicConductivity(x), hav.hydraulicConductivityDeriv(x) + # passed = checkDerivative(wrapper, np.random.randn(n), plotIt=False) + # self.assertTrue(passed,True) + + # def test_Haverkamp_moistureContent(self): + # print 'Haverkamp_moistureContent' + # hav = Richards.Haverkamp() + # def wrapper(x): + # return hav.moistureContent(x), hav.moistureContentDeriv(x) + # passed = checkDerivative(wrapper, np.random.randn(50), plotIt=False) + # self.assertTrue(passed,True) + + # def test_Haverkamp_hydraulicConductivity(self): + # print 'Haverkamp_hydraulicConductivity' + # hav = Richards.Haverkamp() + # def wrapper(x): + # return hav.hydraulicConductivity(x), hav.hydraulicConductivityDeriv(x) + # passed = checkDerivative(wrapper, np.random.randn(50), plotIt=False) + # self.assertTrue(passed,True) + + # def test_Haverkamp_hydraulicConductivity_FullKs(self): + # print 'Haverkamp_hydraulicConductivity_FullKs' + # n = 50 + # hav = Richards.Haverkamp(Ks=np.random.rand(n)) + # def wrapper(x): + # return hav.hydraulicConductivity(x), hav.hydraulicConductivityDeriv(x) + # passed = checkDerivative(wrapper, np.random.randn(n), plotIt=False) + # self.assertTrue(passed,True) + + +# class RichardsTests1D(unittest.TestCase): + +# def setUp(self): +# M = Mesh.TensorMesh([np.ones(20)]) +# M.setCellGradBC('dirichlet') + +# Ks = 9.4400e-03 +# E = Richards.Haverkamp(Ks=np.log(Ks), A=1.1750e+06, gamma=4.74, alpha=1.6110e+06, theta_s=0.287, theta_r=0.075, beta=3.96) + +# bc = np.array([-61.5,-20.7]) +# h = np.zeros(M.nC) + bc[0] +# prob = Richards.RichardsProblem(M,E, timeStep=60, timeEnd=180, boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') + +# q = sp.csr_matrix((np.ones(3),(np.arange(3),np.array([5,10,15]))),shape=(3,M.nC)) +# P = sp.kron(sp.identity(prob.numIts),q) +# prob.P = P + +# self.h0 = h +# self.M = M +# self.Ks = Ks +# self.prob = prob + +# def test_Richards_getResidual_Newton(self): +# self.prob.doNewton = True +# passed = checkDerivative(lambda hn1: self.prob.getResidual(self.h0,hn1), self.h0, plotIt=False) +# self.assertTrue(passed,True) + +# def test_Richards_getResidual_Picard(self): +# self.prob.doNewton = False +# passed = checkDerivative(lambda hn1: self.prob.getResidual(self.h0,hn1), self.h0, plotIt=False, expectedOrder=1) +# self.assertTrue(passed,True) + +# def test_Adjoint_PressureHead(self): +# self.prob.dataType = 'pressureHead' +# Ks = self.Ks +# v = np.random.rand(self.prob.P.shape[0]) +# z = np.random.rand(self.M.nC) +# Hs = self.prob.field(np.log(Ks)) +# vJz = v.dot(self.prob.J(np.log(Ks),z,u=Hs)) +# zJv = z.dot(self.prob.Jt(np.log(Ks),v,u=Hs)) +# tol = TOL*(10**int(np.log10(zJv))) +# passed = np.abs(vJz - zJv) < tol +# print 'Richards Adjoint Test - PressureHead' +# print '%4.4e === %4.4e, diff=%4.4e < %4.e'%(vJz, zJv,np.abs(vJz - zJv),tol) +# self.assertTrue(passed,True) + + +# def test_Adjoint_Saturation(self): +# self.prob.dataType = 'saturation' +# Ks = self.Ks +# v = np.random.rand(self.prob.P.shape[0]) +# z = np.random.rand(self.M.nC) +# Hs = self.prob.field(np.log(Ks)) +# vJz = v.dot(self.prob.J(np.log(Ks),z,u=Hs)) +# zJv = z.dot(self.prob.Jt(np.log(Ks),v,u=Hs)) +# tol = TOL*(10**int(np.log10(zJv))) +# passed = np.abs(vJz - zJv) < tol +# print 'Richards Adjoint Test - Saturation' +# print '%4.4e === %4.4e, diff=%4.4e < %4.e'%(vJz, zJv,np.abs(vJz - zJv),tol) +# self.assertTrue(passed,True) + +# def test_Sensitivity(self): +# self.prob.dataType = 'pressureHead' +# mTrue = np.ones(self.M.nC)*np.log(self.Ks) +# stdev = 0.01 # The standard deviation for the noise +# dobs = self.prob.createSyntheticData(mTrue,std=stdev)[0] +# self.prob.dobs = dobs +# self.prob.std = dobs*0 + stdev +# Hs = self.prob.field(mTrue) +# opt = inverse.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) +# reg = regularization.Regularization(self.M) +# inv = inverse.Inversion(self.prob, reg, opt, beta0=1e4) +# derChk = lambda m: [inv.dataObj(m), inv.dataObjDeriv(m)] +# print 'Testing Richards Derivative' +# passed = checkDerivative(derChk, mTrue, num=5, plotIt=False) +# self.assertTrue(passed,True) + + + +# class RichardsTests2D(object): + +# def setUp(self): +# M = mesh.TensorMesh([np.ones(8),np.ones(30)]) +# Ks = 9.4400e-03 +# E = Richards.Haverkamp(Ks=np.log(Ks), A=1.1750e+06, gamma=4.74, alpha=1.6110e+06, theta_s=0.287, theta_r=0.075, beta=3.96) + +# bc = np.array([-61.5,-20.7]) +# bc = np.r_[np.zeros(M.nCy*2),np.ones(M.nCx)*bc[0],np.ones(M.nCx)*bc[1]] +# h = np.zeros(M.nC) + bc[0] +# prob = Richards.RichardsProblem(M,E, timeStep=60, timeEnd=180, boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') + + +# XY = utils.ndgrid(np.array([5,7.]),np.array([5,15,25.])) +# q = M.getInterpolationMat(XY,'CC') +# P = sp.kron(sp.identity(prob.numIts),q) +# prob.P = P + +# self.h0 = h +# self.M = M +# self.Ks = Ks +# self.prob = prob + +# def test_Richards_getResidual_Newton(self): +# self.prob.doNewton = True +# passed = checkDerivative(lambda hn1: self.prob.getResidual(self.h0,hn1), self.h0, plotIt=False) +# self.assertTrue(passed,True) + +# def test_Richards_getResidual_Picard(self): +# self.prob.doNewton = False +# passed = checkDerivative(lambda hn1: self.prob.getResidual(self.h0,hn1), self.h0, plotIt=False, expectedOrder=1) +# self.assertTrue(passed,True) + +# def test_Adjoint_PressureHead(self): +# self.prob.dataType = 'pressureHead' +# Ks = self.Ks +# v = np.random.rand(self.prob.P.shape[0]) +# z = np.random.rand(self.M.nC) +# Hs = self.prob.field(np.log(Ks)) +# vJz = v.dot(self.prob.J(np.log(Ks),z,u=Hs)) +# zJv = z.dot(self.prob.Jt(np.log(Ks),v,u=Hs)) +# tol = TOL*(10**int(np.log10(zJv))) +# passed = np.abs(vJz - zJv) < tol +# print 'Richards Adjoint Test - PressureHead' +# print '%4.4e === %4.4e, diff=%4.4e < %4.e'%(vJz, zJv,np.abs(vJz - zJv),tol) +# self.assertTrue(passed,True) + + +# def test_Adjoint_Saturation(self): +# self.prob.dataType = 'saturation' +# Ks = self.Ks +# v = np.random.rand(self.prob.P.shape[0]) +# z = np.random.rand(self.M.nC) +# Hs = self.prob.field(np.log(Ks)) +# vJz = v.dot(self.prob.J(np.log(Ks),z,u=Hs)) +# zJv = z.dot(self.prob.Jt(np.log(Ks),v,u=Hs)) +# tol = TOL #*(10**int(np.log10(zJv))) +# passed = np.abs(vJz - zJv) < tol +# print 'Richards Adjoint Test - Saturation' +# print '%4.4e === %4.4e, diff=%4.4e < %4.e'%(vJz, zJv,np.abs(vJz - zJv),tol) +# self.assertTrue(passed,True) + +# def test_Sensitivity(self): +# self.prob.dataType = 'pressureHead' +# mTrue = np.ones(self.M.nC)*np.log(self.Ks) +# stdev = 0.01 # The standard deviation for the noise +# dobs = self.prob.createSyntheticData(mTrue,std=stdev)[0] +# self.prob.dobs = dobs +# self.prob.std = dobs*0 + stdev +# Hs = self.prob.field(mTrue) +# opt = inverse.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) +# reg = regularization.Regularization(self.M) +# inv = inverse.Inversion(self.prob, reg, opt, beta0=1e4) +# derChk = lambda m: [inv.dataObj(m), inv.dataObjDeriv(m)] +# print 'Testing Richards Derivative' +# passed = checkDerivative(derChk, mTrue, num=5, plotIt=False) +# self.assertTrue(passed,True) + if __name__ == '__main__': unittest.main() From 0084a278b59a6dde1d85275d64e832f054fe7d7d Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Tue, 25 Feb 2014 16:54:29 -0800 Subject: [PATCH 03/32] derivative tests --- docs/api_Richards.rst | 6 +- .../richards/comparisonToCeiliaEtAl1990.py | 45 +++++ .../{BaseRichards.py => Empirical.py} | 59 ++++++- simpegFLOW/Richards/RichardsProblem.py | 116 ++++++------- simpegFLOW/Richards/__init__.py | 2 +- simpegFLOW/Tests/test_Richards.py | 157 ++++++++++-------- 6 files changed, 245 insertions(+), 140 deletions(-) create mode 100644 docs/examples/richards/comparisonToCeiliaEtAl1990.py rename simpegFLOW/Richards/{BaseRichards.py => Empirical.py} (83%) diff --git a/docs/api_Richards.rst b/docs/api_Richards.rst index 9bb787e3..d1d51809 100644 --- a/docs/api_Richards.rst +++ b/docs/api_Richards.rst @@ -31,12 +31,14 @@ equation can be written in the continuous form as: However, it can be shown that this does not conserve mass in the discrete formulation. +Here we reproduce the results from Ceilia et al. (1990): + +.. plot:: examples/richards/comparisonToCeiliaEtAl1990.py Richards ======== -.. automodule:: simpegFLOW.Richards +.. automodule:: simpegFLOW.Richards.Empirical :show-inheritance: :members: :undoc-members: - :inherited-members: diff --git a/docs/examples/richards/comparisonToCeiliaEtAl1990.py b/docs/examples/richards/comparisonToCeiliaEtAl1990.py new file mode 100644 index 00000000..22abb2f2 --- /dev/null +++ b/docs/examples/richards/comparisonToCeiliaEtAl1990.py @@ -0,0 +1,45 @@ +from SimPEG import * +from simpegFLOW import Richards +import matplotlib.pyplot as plt + +M = Mesh.TensorMesh([np.ones(40)]) +M.setCellGradBC('dirichlet') +params = Richards.Empirical.HaverkampParams().celia1990 +model = Richards.Empirical.Haverkamp(M, **params) + +bc = np.array([-61.5,-20.7]) +h = np.zeros(M.nC) + bc[0] + +def getFields(timeStep,method): + prob = Richards.RichardsProblem(M,model, timeStep=timeStep, timeEnd=360, + boundaryConditions=bc, initialConditions=h, + doNewton=False, method=method) + return prob.fields(params['Ks']) + +Hs_M10 = getFields(10, 'mixed') +Hs_M30 = getFields(30, 'mixed') +Hs_M120= getFields(120,'mixed') +Hs_H10 = getFields(10, 'head') +Hs_H30 = getFields(30, 'head') +Hs_H120= getFields(120,'head') + +plt.figure(figsize=(13,5)) +plt.subplot(121) +plt.plot(40-M.gridCC, Hs_M10[-1],'b-') +plt.plot(40-M.gridCC, Hs_M30[-1],'r-') +plt.plot(40-M.gridCC, Hs_M120[-1],'k-') +plt.ylim([-70,-10]) +plt.title('Mixed Method') +plt.xlabel('Depth, cm') +plt.ylabel('Pressure Head, cm') +plt.legend(('$\Delta t$ = 10 sec','$\Delta t$ = 30 sec','$\Delta t$ = 120 sec')) +plt.subplot(122) +plt.plot(40-M.gridCC, Hs_H10[-1],'b-') +plt.plot(40-M.gridCC, Hs_H30[-1],'r-') +plt.plot(40-M.gridCC, Hs_H120[-1],'k-') +plt.ylim([-70,-10]) +plt.title('Head-Based Method') +plt.xlabel('Depth, cm') +plt.ylabel('Pressure Head, cm') +plt.legend(('$\Delta t$ = 10 sec','$\Delta t$ = 30 sec','$\Delta t$ = 120 sec')) +plt.show() diff --git a/simpegFLOW/Richards/BaseRichards.py b/simpegFLOW/Richards/Empirical.py similarity index 83% rename from simpegFLOW/Richards/BaseRichards.py rename to simpegFLOW/Richards/Empirical.py index 04359512..0688eff5 100644 --- a/simpegFLOW/Richards/BaseRichards.py +++ b/simpegFLOW/Richards/Empirical.py @@ -43,7 +43,42 @@ class RichardsModel(object): return self.kModel.transformDerivU(u, m) -class BaseHaverkamp_theta(Model.BaseNonLinearModel): +def _ModelProperty(name, model, doc=None, default=None): + + def fget(self): + if getattr(self, model, None) is not None: + MOD = getattr(self, model) + return getattr(MOD, name, default) + return default + + def fset(self, value): + if getattr(self, model, None) is not None: + MOD = getattr(self, model) + setattr(MOD, name, value) + + return property(fget, fset=fset, doc=doc) + + +class HaverkampParams(object): + """Holds some default parameterizations for the Haverkamp model.""" + def __init__(self): pass + @property + def celia1990(self): + """ + Parameters used in: + + Celia, Michael A., Efthimios T. Bouloutas, and Rebecca L. Zarba. + "A general mass-conservative numerical solution for the unsaturated flow equation." + Water Resources Research 26.7 (1990): 1483-1496. + + """ + return {'alpha':1.611e+06, 'beta':3.96, + 'theta_r':0.075, 'theta_s':0.287, + 'Ks':np.log(9.44e-03), 'A':1.175e+06, + 'gamma':4.74} + + +class _haverkamp_theta(Model.BaseNonLinearModel): theta_s = 0.430 theta_r = 0.078 @@ -77,7 +112,7 @@ class BaseHaverkamp_theta(Model.BaseNonLinearModel): return g -class BaseHaverkamp_k(Model.BaseNonLinearModel): +class _haverkamp_k(Model.BaseNonLinearModel): A = 1.175e+06 gamma = 4.74 @@ -118,6 +153,26 @@ class BaseHaverkamp_k(Model.BaseNonLinearModel): g = Utils.sdiag(g) return g +class Haverkamp(RichardsModel): + """Haverkamp Model""" + + alpha = _ModelProperty('alpha', 'thetaModel', default=1.6110e+06) + beta = _ModelProperty('beta', 'thetaModel', default=3.96) + theta_r = _ModelProperty('theta_r', 'thetaModel', default=0.075) + theta_s = _ModelProperty('theta_s', 'thetaModel', default=0.287) + + Ks = _ModelProperty('Ks', 'kModel', default=np.log(24.96)) + A = _ModelProperty('A', 'kModel', default=1.1750e+06) + gamma = _ModelProperty('gamma', 'kModel', default=4.74) + + def __init__(self, mesh, **kwargs): + RichardsModel.__init__(self, mesh, + _haverkamp_theta(mesh), + _haverkamp_k(mesh)) + Utils.setKwargs(self, **kwargs) + + + # class Haverkamp(object): diff --git a/simpegFLOW/Richards/RichardsProblem.py b/simpegFLOW/Richards/RichardsProblem.py index 870d857b..5016f2f6 100644 --- a/simpegFLOW/Richards/RichardsProblem.py +++ b/simpegFLOW/Richards/RichardsProblem.py @@ -18,13 +18,40 @@ class RichardsData(Data.BaseData): assert value in ['saturation','pressureHead'], "dataType must be 'saturation' or 'pressureHead'." self._dataType = value - def projectFields(self, u): - u = np.concatenate(u[1:]) + @Utils.count + @Utils.requires('prob') + def dpred(self, m, u=None): + """ + Create the projected data from a model. + The field, u, (if provided) will be used for the predicted data + instead of recalculating the fields (which may be expensive!). + + .. math:: + d_\\text{pred} = P(u(m)) + + Where P is a projection of the fields onto the data space. + """ + if u is None: u = self.prob.fields(m) + return Utils.mkvc(self.projectFields(u, m)) + + def projectFields(self, U, m): + + u = np.concatenate(U[1:]) + if self.dataType == 'saturation': - #TODO: Fix this: - u = self.prob.model.theta(MODEL, u) + u = self.prob.model.theta(u, m) return self.P*u + def projectFieldsDerivU(self, U, m): + + u = np.concatenate(U[1:]) + + if self.dataType == 'pressureHead': + return self.P + elif self.dataType == 'saturation': + dT = self.model.thetaDerivU(u, m) + return self.P*dT + class RichardsProblem(Problem.BaseProblem): """docstring for RichardsProblem""" @@ -73,11 +100,11 @@ class RichardsProblem(Problem.BaseProblem): self._doNewton = value def fields(self, m): - Hs = range(self.numIts+1) - Hs[0] = self.initialConditions + u = range(self.numIts+1) + u[0] = self.initialConditions for ii in range(self.numIts): - Hs[ii+1] = self.rootFinder.root(lambda hn1m, return_g=True: self.getResidual(m, Hs[ii], hn1m, return_g=return_g), Hs[ii]) - return Hs + u[ii+1] = self.rootFinder.root(lambda hn1m, return_g=True: self.getResidual(m, u[ii], hn1m, return_g=return_g), u[ii]) + return u def diagsJacobian(self, m, hn, hn1): @@ -172,14 +199,14 @@ class RichardsProblem(Problem.BaseProblem): return r, J - def fullJ(self, m, u=None): + def Jfull(self, m, u=None): if u is None: u = self.field(m) - Hs = u - nn = len(Hs)-1 + + nn = len(u)-1 Asubs, Adiags, Bs = range(nn), range(nn), range(nn) for ii in range(nn): - Asubs[ii], Adiags[ii], Bs[ii] = self.diagsJacobian(m, Hs[ii],Hs[ii+1]) + Asubs[ii], Adiags[ii], Bs[ii] = self.diagsJacobian(m, u[ii],u[ii+1]) Ad = sp.block_diag(Adiags) zRight = Utils.spzeros((len(Asubs)-1)*Asubs[0].shape[0],Adiags[0].shape[1]) zTop = Utils.spzeros(Adiags[0].shape[0], len(Adiags)*Adiags[0].shape[1]) @@ -195,46 +222,38 @@ class RichardsProblem(Problem.BaseProblem): def Jvec(self, m, v, u=None): if u is None: u = self.field(m) - Hs = u - JvC = range(len(Hs)-1) # Cell to hold each row of the long vector. + + JvC = range(len(u)-1) # Cell to hold each row of the long vector. # This is done via forward substitution. - temp, Adiag, B = self.diagsJacobian(m, Hs[0],Hs[1]) + temp, Adiag, B = self.diagsJacobian(m, u[0],u[1]) Adiaginv = Solver(Adiag) JvC[0] = Adiaginv.solve(B*v) # M = @(x) tril(Adiag)\(diag(Adiag).*(triu(Adiag)\x)); # JvC{1} = bicgstab(Adiag,(B*v),tolbcg,500,M); - for ii in range(1,len(Hs)-1): - Asub, Adiag, B = self.diagsJacobian(m, Hs[ii],Hs[ii+1]) + for ii in range(1,len(u)-1): + Asub, Adiag, B = self.diagsJacobian(m, u[ii],u[ii+1]) Adiaginv = Solver(Adiag) JvC[ii] = Adiaginv.solve(B*v - Asub*JvC[ii-1]) - if self.dataType == 'pressureHead': - Jv = self.P*np.concatenate(JvC) - elif self.dataType == 'saturation': - dT = self.model.thetaDerivU(np.concatenate(Hs[1:]), m) - Jv = self.P*dT*np.concatenate(JvC) - - return Jv + P = self.data.projectFieldsDerivU(u, m) + return P * np.concatenate(JvC) def Jtvec(self, m, v, u=None): if u is None: u = self.field(m) - Hs = u - if self.dataType == 'pressureHead': - PTv = self.P.T*v; - elif self.dataType == 'saturation': - dT = self.model.thetaDerivU(np.concatenate(Hs[1:]), m) - PTv = dT.T*self.P.T*v + + P = self.data.projectFieldsDerivU(u, m) + PTv = P.T*v # This is done via backward substitution. minus = 0 BJtv = 0 - for ii in range(len(Hs)-1,0,-1): - Asub, Adiag, B = self.diagsJacobian(m, Hs[ii-1], Hs[ii]) + for ii in range(len(u)-1,0,-1): + Asub, Adiag, B = self.diagsJacobian(m, u[ii-1], u[ii]) #select the correct part of v vpart = range((ii-1)*Adiag.shape[0], (ii)*Adiag.shape[0]) AdiaginvT = Solver(Adiag.T) @@ -243,36 +262,3 @@ class RichardsProblem(Problem.BaseProblem): BJtv = BJtv + B.T*JTvC return BJtv - - - -if __name__ == '__main__': - from SimPEG import * - import Richards - import matplotlib.pyplot as plt - M = Mesh.TensorMesh([np.ones(40)]) - Ks = 9.4400e-03 - E = Richards.Haverkamp(Ks=np.log(Ks), A=1.1750e+06, gamma=4.74, alpha=1.6110e+06, theta_s=0.287, theta_r=0.075, beta=3.96) - bc = np.array([-61.5,-20.7]) - h = np.zeros(M.nC) + bc[0] - - # data = R - prob = Richards.RichardsProblem(M,E, timeStep=10, timeEnd=100, boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') - - q = sp.csr_matrix((np.ones(4),(np.arange(4),np.array([20, 30, 35, 38]))), shape=(4,M.nCx)) - P = sp.kron(sp.identity(prob.numIts),q) - - prob.dataType = 'pressureHead' - mTrue = np.ones(M.nC)*np.log(Ks) - stdev = 0.01 # The standard deviation for the noise - data = prob.createSyntheticData(mTrue,std=stdev, P=P) - p = plt.plot(data.dobs.reshape((-1,4))) - plt.show() - # opt = Optimization.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) - # reg = Regularization.Tikhonov(model) - # inv = Inversion.BaseInversion(prob, reg, opt, beta0=1e4) - # derChk = lambda m: [inv.dataObj(m), inv.dataObjDeriv(m)] - # print inv.dataObj(mTrue*0+np.log(1e-5)) - # print inv.dataObj(mTrue) - # tests.checkDerivative(derChk, mTrue, plotIt=False) - diff --git a/simpegFLOW/Richards/__init__.py b/simpegFLOW/Richards/__init__.py index bf9832f7..6e25d292 100644 --- a/simpegFLOW/Richards/__init__.py +++ b/simpegFLOW/Richards/__init__.py @@ -1,2 +1,2 @@ -from BaseRichards import * +import Empirical from RichardsProblem import * diff --git a/simpegFLOW/Tests/test_Richards.py b/simpegFLOW/Tests/test_Richards.py index 08befb01..6dcde6c5 100644 --- a/simpegFLOW/Tests/test_Richards.py +++ b/simpegFLOW/Tests/test_Richards.py @@ -2,7 +2,7 @@ import unittest from SimPEG import * from SimPEG.Tests.TestUtils import OrderTest, checkDerivative from scipy.sparse.linalg import dsolve -import simpegFLOW.Richards as Richards +from simpegFLOW import Richards TOL = 1E-8 @@ -10,7 +10,7 @@ class TestModels(unittest.TestCase): def test_BaseHaverkamp_Theta(self): mesh = Mesh.TensorMesh([50]) - hav = Richards.BaseHaverkamp_theta(mesh) + hav = Richards.Empirical._haverkamp_theta(mesh) m = np.random.randn(50) def wrapper(u): return hav.transform(u, m), hav.transformDerivU(u, m) @@ -20,14 +20,14 @@ class TestModels(unittest.TestCase): def test_BaseHaverkamp_k(self): mesh = Mesh.TensorMesh([50]) - hav = Richards.BaseHaverkamp_k(mesh) + hav = Richards.Empirical._haverkamp_k(mesh) m = np.random.randn(50) def wrapper(u): return hav.transform(u, m), hav.transformDerivU(u, m) passed = checkDerivative(wrapper, np.random.randn(50), plotIt=False) self.assertTrue(passed,True) - hav = Richards.BaseHaverkamp_k(mesh) + hav = Richards.Empirical._haverkamp_k(mesh) u = np.random.randn(50) def wrapper(m): return hav.transform(u, m), hav.transformDerivM(u, m) @@ -102,82 +102,99 @@ class TestModels(unittest.TestCase): # self.assertTrue(passed,True) -# class RichardsTests1D(unittest.TestCase): +class RichardsTests1D(unittest.TestCase): -# def setUp(self): -# M = Mesh.TensorMesh([np.ones(20)]) -# M.setCellGradBC('dirichlet') + def setUp(self): + M = Mesh.TensorMesh([np.ones(20)]) + M.setCellGradBC('dirichlet') -# Ks = 9.4400e-03 -# E = Richards.Haverkamp(Ks=np.log(Ks), A=1.1750e+06, gamma=4.74, alpha=1.6110e+06, theta_s=0.287, theta_r=0.075, beta=3.96) + params = Richards.Empirical.HaverkampParams().celia1990 + model = Richards.Empirical.Haverkamp(M, **params) -# bc = np.array([-61.5,-20.7]) -# h = np.zeros(M.nC) + bc[0] -# prob = Richards.RichardsProblem(M,E, timeStep=60, timeEnd=180, boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') + bc = np.array([-61.5,-20.7]) + h = np.zeros(M.nC) + bc[0] -# q = sp.csr_matrix((np.ones(3),(np.arange(3),np.array([5,10,15]))),shape=(3,M.nC)) -# P = sp.kron(sp.identity(prob.numIts),q) -# prob.P = P + prob = Richards.RichardsProblem(M,model, timeStep=60, timeEnd=180, + boundaryConditions=bc, initialConditions=h, + doNewton=False, method='mixed') -# self.h0 = h -# self.M = M -# self.Ks = Ks -# self.prob = prob + q = sp.csr_matrix((np.ones(3),(np.arange(3),np.array([5,10,15]))),shape=(3,M.nC)) + P = sp.kron(sp.identity(prob.numIts),q) + data = Richards.RichardsData(P=P) -# def test_Richards_getResidual_Newton(self): -# self.prob.doNewton = True -# passed = checkDerivative(lambda hn1: self.prob.getResidual(self.h0,hn1), self.h0, plotIt=False) -# self.assertTrue(passed,True) + prob.pair(data) -# def test_Richards_getResidual_Picard(self): -# self.prob.doNewton = False -# passed = checkDerivative(lambda hn1: self.prob.getResidual(self.h0,hn1), self.h0, plotIt=False, expectedOrder=1) -# self.assertTrue(passed,True) + self.h0 = h + self.M = M + self.Ks = params['Ks'] + self.prob = prob + self.data = data -# def test_Adjoint_PressureHead(self): -# self.prob.dataType = 'pressureHead' -# Ks = self.Ks -# v = np.random.rand(self.prob.P.shape[0]) -# z = np.random.rand(self.M.nC) -# Hs = self.prob.field(np.log(Ks)) -# vJz = v.dot(self.prob.J(np.log(Ks),z,u=Hs)) -# zJv = z.dot(self.prob.Jt(np.log(Ks),v,u=Hs)) -# tol = TOL*(10**int(np.log10(zJv))) -# passed = np.abs(vJz - zJv) < tol -# print 'Richards Adjoint Test - PressureHead' -# print '%4.4e === %4.4e, diff=%4.4e < %4.e'%(vJz, zJv,np.abs(vJz - zJv),tol) -# self.assertTrue(passed,True) + def test_Richards_getResidual_Newton(self): + self.prob.doNewton = True + m = self.Ks + passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0,hn1), self.h0, plotIt=False) + self.assertTrue(passed,True) + def test_Richards_getResidual_Picard(self): + self.prob.doNewton = False + m = self.Ks + passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0,hn1), self.h0, plotIt=False, expectedOrder=1) + self.assertTrue(passed,True) -# def test_Adjoint_Saturation(self): -# self.prob.dataType = 'saturation' -# Ks = self.Ks -# v = np.random.rand(self.prob.P.shape[0]) -# z = np.random.rand(self.M.nC) -# Hs = self.prob.field(np.log(Ks)) -# vJz = v.dot(self.prob.J(np.log(Ks),z,u=Hs)) -# zJv = z.dot(self.prob.Jt(np.log(Ks),v,u=Hs)) -# tol = TOL*(10**int(np.log10(zJv))) -# passed = np.abs(vJz - zJv) < tol -# print 'Richards Adjoint Test - Saturation' -# print '%4.4e === %4.4e, diff=%4.4e < %4.e'%(vJz, zJv,np.abs(vJz - zJv),tol) -# self.assertTrue(passed,True) + def test_Adjoint_PressureHead(self): + self.prob.dataType = 'pressureHead' + v = np.random.rand(self.data.P.shape[0]) + z = np.random.rand(self.M.nC) + Hs = self.prob.fields(self.Ks) + vJz = v.dot(self.prob.Jvec(self.Ks,z,u=Hs)) + zJv = z.dot(self.prob.Jtvec(self.Ks,v,u=Hs)) + tol = TOL*(10**int(np.log10(zJv))) + passed = np.abs(vJz - zJv) < tol + print 'Richards Adjoint Test - PressureHead' + print '%4.4e === %4.4e, diff=%4.4e < %4.e'%(vJz, zJv,np.abs(vJz - zJv),tol) + self.assertTrue(passed,True) -# def test_Sensitivity(self): -# self.prob.dataType = 'pressureHead' -# mTrue = np.ones(self.M.nC)*np.log(self.Ks) -# stdev = 0.01 # The standard deviation for the noise -# dobs = self.prob.createSyntheticData(mTrue,std=stdev)[0] -# self.prob.dobs = dobs -# self.prob.std = dobs*0 + stdev -# Hs = self.prob.field(mTrue) -# opt = inverse.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) -# reg = regularization.Regularization(self.M) -# inv = inverse.Inversion(self.prob, reg, opt, beta0=1e4) -# derChk = lambda m: [inv.dataObj(m), inv.dataObjDeriv(m)] -# print 'Testing Richards Derivative' -# passed = checkDerivative(derChk, mTrue, num=5, plotIt=False) -# self.assertTrue(passed,True) + def test_Adjoint_Saturation(self): + self.prob.dataType = 'saturation' + v = np.random.rand(self.data.P.shape[0]) + z = np.random.rand(self.M.nC) + Hs = self.prob.fields(self.Ks) + vJz = v.dot(self.prob.Jvec(self.Ks,z,u=Hs)) + zJv = z.dot(self.prob.Jtvec(self.Ks,v,u=Hs)) + tol = TOL*(10**int(np.log10(zJv))) + passed = np.abs(vJz - zJv) < tol + print 'Richards Adjoint Test - Saturation' + print '%4.4e === %4.4e, diff=%4.4e < %4.e'%(vJz, zJv,np.abs(vJz - zJv),tol) + self.assertTrue(passed,True) + + def test_SensitivityPressureHead(self): + self.prob.dataType = 'pressureHead' + self.prob.unpair() + mTrue = np.ones(self.M.nC)*self.Ks + stdev = 0.01 # The standard deviation for the noise + data = self.prob.createSyntheticData(mTrue, std=stdev, P=self.data.P) + opt = Optimization.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) + reg = Regularization.Tikhonov(Model.BaseModel(self.M)) + obj = ObjFunction.BaseObjFunction(data, reg) + derChk = lambda m: [obj.dataObj(m), obj.dataObjDeriv(m)] + print 'Testing Richards Derivative - Pressure Head' + passed = checkDerivative(derChk, mTrue, num=5, plotIt=False) + self.assertTrue(passed,True) + + def test_SensitivitySaturation(self): + self.prob.unpair() + self.prob.dataType = 'saturation' + mTrue = np.ones(self.M.nC)*self.Ks + stdev = 0.01 # The standard deviation for the noise + data = self.prob.createSyntheticData(mTrue, std=stdev, P=self.data.P) + opt = Optimization.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) + reg = Regularization.Tikhonov(Model.BaseModel(self.M)) + obj = ObjFunction.BaseObjFunction(data, reg) + derChk = lambda m: [obj.dataObj(m), obj.dataObjDeriv(m)] + print 'Testing Richards Derivative - Saturation' + passed = checkDerivative(derChk, mTrue, num=5, plotIt=False) + self.assertTrue(passed,True) @@ -245,7 +262,7 @@ class TestModels(unittest.TestCase): # def test_Sensitivity(self): # self.prob.dataType = 'pressureHead' -# mTrue = np.ones(self.M.nC)*np.log(self.Ks) +# mTrue = np.ones(self.M.nC)*self.Ks # stdev = 0.01 # The standard deviation for the noise # dobs = self.prob.createSyntheticData(mTrue,std=stdev)[0] # self.prob.dobs = dobs From 1016def784f958b7f7e6dd1fdab72cd3f7f60526 Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Tue, 25 Feb 2014 17:24:11 -0800 Subject: [PATCH 04/32] minor updates --- simpegFLOW/Richards/RichardsProblem.py | 36 ++++++++++++++------------ 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/simpegFLOW/Richards/RichardsProblem.py b/simpegFLOW/Richards/RichardsProblem.py index 5016f2f6..caf0ffc8 100644 --- a/simpegFLOW/Richards/RichardsProblem.py +++ b/simpegFLOW/Richards/RichardsProblem.py @@ -34,6 +34,7 @@ class RichardsData(Data.BaseData): if u is None: u = self.prob.fields(m) return Utils.mkvc(self.projectFields(u, m)) + @Utils.requires('prob') def projectFields(self, U, m): u = np.concatenate(U[1:]) @@ -42,13 +43,18 @@ class RichardsData(Data.BaseData): u = self.prob.model.theta(u, m) return self.P*u - def projectFieldsDerivU(self, U, m): + @Utils.requires('prob') + def projectFieldsDeriv(self, U, m): + """The Derivative with respect to the fields.""" u = np.concatenate(U[1:]) if self.dataType == 'pressureHead': return self.P elif self.dataType == 'saturation': + #TODO: if m is a parameter in the theta + # distribution, we may need to do + # some more chain rule here. dT = self.model.thetaDerivU(u, m) return self.P*dT @@ -64,7 +70,6 @@ class RichardsProblem(Problem.BaseProblem): modelPair = RichardsModel def __init__(self, mesh, model, **kwargs): - self.doNewton = False # This also sets the rootFinder algorithm. Problem.BaseProblem.__init__(self, mesh, model, **kwargs) @property @@ -89,15 +94,16 @@ class RichardsProblem(Problem.BaseProblem): assert value in ['mixed','head'], "method must be 'mixed' or 'head'." self._method = value + # Setting doNewton will clear the rootFinder, which will be reinitialized when called + doNewton = Utils.dependentProperty('_doNewton', False, ['_rootFinder'], + "Do a Newton iteration. If False, a Picard iteration will be completed.") + @property - def doNewton(self): - """Do a Newton iteration. If False, a Picard iteration will be completed.""" - return self._doNewton - @doNewton.setter - def doNewton(self, value): - value = bool(value) - self.rootFinder = Optimization.NewtonRoot(doLS=value) - self._doNewton = value + def rootFinder(self): + """Root-finding Algorithm""" + if getattr(self, '_rootFinder', None) is None: + self._rootFinder = Optimization.NewtonRoot(doLS=self.doNewton) + return self._rootFinder def fields(self, m): u = range(self.numIts+1) @@ -126,7 +132,7 @@ class RichardsProblem(Problem.BaseProblem): dT1 = self.model.thetaDerivU(hn1, m) K1 = self.model.k(hn1, m) dK1 = self.model.kDerivU(hn1, m) - dKa1 = self.model.kDerivM(hn1, m) + dKm1 = self.model.kDerivM(hn1, m) # Compute part of the derivative of: # @@ -154,7 +160,7 @@ class RichardsProblem(Problem.BaseProblem): -Dz*diagAVk2_AVdiagK2*dK1 ) - B = DdiagGh1*diagAVk2_AVdiagK2*dKa1 + Dz*diagAVk2_AVdiagK2*dKa1 + B = DdiagGh1*diagAVk2_AVdiagK2*dKm1 + Dz*diagAVk2_AVdiagK2*dKm1 return Asub, Adiag, B @@ -218,7 +224,6 @@ class RichardsProblem(Problem.BaseProblem): J = Ainv.solve(B) return J - def Jvec(self, m, v, u=None): if u is None: u = self.field(m) @@ -238,15 +243,14 @@ class RichardsProblem(Problem.BaseProblem): Adiaginv = Solver(Adiag) JvC[ii] = Adiaginv.solve(B*v - Asub*JvC[ii-1]) - P = self.data.projectFieldsDerivU(u, m) + P = self.data.projectFieldsDeriv(u, m) return P * np.concatenate(JvC) def Jtvec(self, m, v, u=None): if u is None: u = self.field(m) - - P = self.data.projectFieldsDerivU(u, m) + P = self.data.projectFieldsDeriv(u, m) PTv = P.T*v # This is done via backward substitution. From 7a5fde8a3eac998db6159c7c7587b93f16f218b2 Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Thu, 27 Feb 2014 09:44:23 -0800 Subject: [PATCH 05/32] Minor changes to problem --- docs/api_Richards.rst | 4 ++-- .../examples/richards/comparisonToCeiliaEtAl1990.py | 12 ++++++------ simpegFLOW/Richards/RichardsProblem.py | 13 +++++++------ 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/docs/api_Richards.rst b/docs/api_Richards.rst index d1d51809..b0a69b3b 100644 --- a/docs/api_Richards.rst +++ b/docs/api_Richards.rst @@ -35,8 +35,8 @@ Here we reproduce the results from Ceilia et al. (1990): .. plot:: examples/richards/comparisonToCeiliaEtAl1990.py -Richards -======== +Richards API +============ .. automodule:: simpegFLOW.Richards.Empirical :show-inheritance: diff --git a/docs/examples/richards/comparisonToCeiliaEtAl1990.py b/docs/examples/richards/comparisonToCeiliaEtAl1990.py index 22abb2f2..1d3c8451 100644 --- a/docs/examples/richards/comparisonToCeiliaEtAl1990.py +++ b/docs/examples/richards/comparisonToCeiliaEtAl1990.py @@ -16,12 +16,12 @@ def getFields(timeStep,method): doNewton=False, method=method) return prob.fields(params['Ks']) -Hs_M10 = getFields(10, 'mixed') -Hs_M30 = getFields(30, 'mixed') -Hs_M120= getFields(120,'mixed') -Hs_H10 = getFields(10, 'head') -Hs_H30 = getFields(30, 'head') -Hs_H120= getFields(120,'head') +Hs_M10 = getFields(10., 'mixed') +Hs_M30 = getFields(30., 'mixed') +Hs_M120= getFields(120.,'mixed') +Hs_H10 = getFields(10., 'head') +Hs_H30 = getFields(30., 'head') +Hs_H120= getFields(120.,'head') plt.figure(figsize=(13,5)) plt.subplot(121) diff --git a/simpegFLOW/Richards/RichardsProblem.py b/simpegFLOW/Richards/RichardsProblem.py index caf0ffc8..2a37308b 100644 --- a/simpegFLOW/Richards/RichardsProblem.py +++ b/simpegFLOW/Richards/RichardsProblem.py @@ -1,5 +1,5 @@ from SimPEG import * -from BaseRichards import RichardsModel +from Empirical import RichardsModel class RichardsData(Data.BaseData): """docstring for RichardsData""" @@ -66,7 +66,7 @@ class RichardsProblem(Problem.BaseProblem): boundaryConditions = None initialConditions = None - dataPair = RichardsData + dataPair = RichardsData modelPair = RichardsModel def __init__(self, mesh, model, **kwargs): @@ -117,7 +117,7 @@ class RichardsProblem(Problem.BaseProblem): DIV = self.mesh.faceDiv GRAD = self.mesh.cellGrad BC = self.mesh.cellGradBC - AV = self.mesh.aveCC2F + AV = self.mesh.aveF2CC.T if self.mesh.dim == 1: Dz = self.mesh.faceDivx elif self.mesh.dim == 2: @@ -171,7 +171,7 @@ class RichardsProblem(Problem.BaseProblem): DIV = self.mesh.faceDiv GRAD = self.mesh.cellGrad BC = self.mesh.cellGradBC - AV = self.mesh.aveCC2F + AV = self.mesh.aveF2CC.T if self.mesh.dim == 1: Dz = self.mesh.faceDivx elif self.mesh.dim == 2: @@ -188,7 +188,7 @@ class RichardsProblem(Problem.BaseProblem): K = self.model.k(h, m) dK = self.model.kDerivU(h, m) - aveK = 1./(AV*(1./K)); + aveK = 1./(AV*(1./K)) RHS = DIV*Utils.sdiag(aveK)*(GRAD*h+BC*bc) + Dz*aveK if self.method == 'mixed': @@ -221,7 +221,8 @@ class RichardsProblem(Problem.BaseProblem): B = np.array(sp.vstack(Bs).todense()) Ainv = Solver(A) - J = Ainv.solve(B) + P = self.data.projectFieldsDeriv(u, m) + J = P * Ainv.solve(B) return J def Jvec(self, m, v, u=None): From 9fb83ad727a8223e69f6d19cc5924063fd5d814c Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Thu, 27 Feb 2014 09:51:02 -0800 Subject: [PATCH 06/32] try updating rtfd --- docs/api_Richards.rst | 4 ++-- docs/examples/richards/comparisonToCeiliaEtAl1990.py | 1 + setup.py | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/api_Richards.rst b/docs/api_Richards.rst index b0a69b3b..d1d51809 100644 --- a/docs/api_Richards.rst +++ b/docs/api_Richards.rst @@ -35,8 +35,8 @@ Here we reproduce the results from Ceilia et al. (1990): .. plot:: examples/richards/comparisonToCeiliaEtAl1990.py -Richards API -============ +Richards +======== .. automodule:: simpegFLOW.Richards.Empirical :show-inheritance: diff --git a/docs/examples/richards/comparisonToCeiliaEtAl1990.py b/docs/examples/richards/comparisonToCeiliaEtAl1990.py index 1d3c8451..917d1286 100644 --- a/docs/examples/richards/comparisonToCeiliaEtAl1990.py +++ b/docs/examples/richards/comparisonToCeiliaEtAl1990.py @@ -10,6 +10,7 @@ model = Richards.Empirical.Haverkamp(M, **params) bc = np.array([-61.5,-20.7]) h = np.zeros(M.nC) + bc[0] + def getFields(timeStep,method): prob = Richards.RichardsProblem(M,model, timeStep=timeStep, timeEnd=360, boundaryConditions=bc, initialConditions=h, diff --git a/setup.py b/setup.py index 95fb3b72..9881e138 100644 --- a/setup.py +++ b/setup.py @@ -6,3 +6,4 @@ except ImportError, e: os.system('mv simpeg/SimPEG temp') os.system('rm -rf simpeg') os.system('mv temp SimPEG') + os.system('echo export PYTHONPATH=$PYTHONPATH:'+os.path.abspath('.')+' >> ~/.bashrc') From e5105065a512cdfc49fd42d58e0c59f18a37b100 Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Thu, 27 Feb 2014 12:18:22 -0800 Subject: [PATCH 07/32] testing setup.py --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9881e138..68400e8e 100644 --- a/setup.py +++ b/setup.py @@ -6,4 +6,6 @@ except ImportError, e: os.system('mv simpeg/SimPEG temp') os.system('rm -rf simpeg') os.system('mv temp SimPEG') - os.system('echo export PYTHONPATH=$PYTHONPATH:'+os.path.abspath('.')+' >> ~/.bashrc') + # os.system('mkdir simp') + print os.path.abspath('.') + # os.system('cp -r SimPEG simpegflow/docs/examples/SimPEG') From 0e9b1371e32759cfcc63eeff31e402af6ec0c5f2 Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Thu, 27 Feb 2014 12:23:36 -0800 Subject: [PATCH 08/32] more setup.py stuff. --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 68400e8e..6e97a6d4 100644 --- a/setup.py +++ b/setup.py @@ -7,5 +7,7 @@ except ImportError, e: os.system('rm -rf simpeg') os.system('mv temp SimPEG') # os.system('mkdir simp') - print os.path.abspath('.') # os.system('cp -r SimPEG simpegflow/docs/examples/SimPEG') + +print os.path.abspath('.') +print os.system('ls') From d8128c2976b7bf6c52c574e5530f9a83690dd766 Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Thu, 27 Feb 2014 12:31:59 -0800 Subject: [PATCH 09/32] setup.py work.. --- docs/api_Richards.rst | 2 +- ...aEtAl1990.py => richards_comparisonToCeiliaEtAl1990.py} | 0 setup.py | 7 ++----- 3 files changed, 3 insertions(+), 6 deletions(-) rename docs/examples/{richards/comparisonToCeiliaEtAl1990.py => richards_comparisonToCeiliaEtAl1990.py} (100%) diff --git a/docs/api_Richards.rst b/docs/api_Richards.rst index d1d51809..ecaf8c83 100644 --- a/docs/api_Richards.rst +++ b/docs/api_Richards.rst @@ -33,7 +33,7 @@ However, it can be shown that this does not conserve mass in the discrete formul Here we reproduce the results from Ceilia et al. (1990): -.. plot:: examples/richards/comparisonToCeiliaEtAl1990.py +.. plot:: examples/richards_comparisonToCeiliaEtAl1990.py Richards ======== diff --git a/docs/examples/richards/comparisonToCeiliaEtAl1990.py b/docs/examples/richards_comparisonToCeiliaEtAl1990.py similarity index 100% rename from docs/examples/richards/comparisonToCeiliaEtAl1990.py rename to docs/examples/richards_comparisonToCeiliaEtAl1990.py diff --git a/setup.py b/setup.py index 6e97a6d4..a48ca0ee 100644 --- a/setup.py +++ b/setup.py @@ -6,8 +6,5 @@ except ImportError, e: os.system('mv simpeg/SimPEG temp') os.system('rm -rf simpeg') os.system('mv temp SimPEG') - # os.system('mkdir simp') - # os.system('cp -r SimPEG simpegflow/docs/examples/SimPEG') - -print os.path.abspath('.') -print os.system('ls') + os.system('mkdir docs/examples/SimPEG') + os.system('cp -r SimPEG simpegflow/docs/examples/SimPEG') From ce9111efa1e079276fe16e0cfd0295ca7f58f44f Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Thu, 27 Feb 2014 12:40:46 -0800 Subject: [PATCH 10/32] move to inline plotting? --- docs/api_Richards.rst | 50 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/docs/api_Richards.rst b/docs/api_Richards.rst index ecaf8c83..d2442188 100644 --- a/docs/api_Richards.rst +++ b/docs/api_Richards.rst @@ -33,7 +33,55 @@ However, it can be shown that this does not conserve mass in the discrete formul Here we reproduce the results from Ceilia et al. (1990): -.. plot:: examples/richards_comparisonToCeiliaEtAl1990.py +.. plot:: + + from SimPEG import * + from simpegFLOW import Richards + import matplotlib.pyplot as plt + + M = Mesh.TensorMesh([np.ones(40)]) + M.setCellGradBC('dirichlet') + params = Richards.Empirical.HaverkampParams().celia1990 + model = Richards.Empirical.Haverkamp(M, **params) + + bc = np.array([-61.5,-20.7]) + h = np.zeros(M.nC) + bc[0] + + + def getFields(timeStep,method): + prob = Richards.RichardsProblem(M,model, timeStep=timeStep, timeEnd=360, + boundaryConditions=bc, initialConditions=h, + doNewton=False, method=method) + return prob.fields(params['Ks']) + + Hs_M10 = getFields(10., 'mixed') + Hs_M30 = getFields(30., 'mixed') + Hs_M120= getFields(120.,'mixed') + Hs_H10 = getFields(10., 'head') + Hs_H30 = getFields(30., 'head') + Hs_H120= getFields(120.,'head') + + plt.figure(figsize=(13,5)) + plt.subplot(121) + plt.plot(40-M.gridCC, Hs_M10[-1],'b-') + plt.plot(40-M.gridCC, Hs_M30[-1],'r-') + plt.plot(40-M.gridCC, Hs_M120[-1],'k-') + plt.ylim([-70,-10]) + plt.title('Mixed Method') + plt.xlabel('Depth, cm') + plt.ylabel('Pressure Head, cm') + plt.legend(('$\Delta t$ = 10 sec','$\Delta t$ = 30 sec','$\Delta t$ = 120 sec')) + plt.subplot(122) + plt.plot(40-M.gridCC, Hs_H10[-1],'b-') + plt.plot(40-M.gridCC, Hs_H30[-1],'r-') + plt.plot(40-M.gridCC, Hs_H120[-1],'k-') + plt.ylim([-70,-10]) + plt.title('Head-Based Method') + plt.xlabel('Depth, cm') + plt.ylabel('Pressure Head, cm') + plt.legend(('$\Delta t$ = 10 sec','$\Delta t$ = 30 sec','$\Delta t$ = 120 sec')) + plt.show() + Richards ======== From 4b7323b8f510c5463c05eb9eb9a1462a73af6219 Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Thu, 27 Feb 2014 13:34:13 -0800 Subject: [PATCH 11/32] setup.py --- docs/api_Richards.rst | 50 +------------------------------------------ setup.py | 12 ++++++----- 2 files changed, 8 insertions(+), 54 deletions(-) diff --git a/docs/api_Richards.rst b/docs/api_Richards.rst index d2442188..ecaf8c83 100644 --- a/docs/api_Richards.rst +++ b/docs/api_Richards.rst @@ -33,55 +33,7 @@ However, it can be shown that this does not conserve mass in the discrete formul Here we reproduce the results from Ceilia et al. (1990): -.. plot:: - - from SimPEG import * - from simpegFLOW import Richards - import matplotlib.pyplot as plt - - M = Mesh.TensorMesh([np.ones(40)]) - M.setCellGradBC('dirichlet') - params = Richards.Empirical.HaverkampParams().celia1990 - model = Richards.Empirical.Haverkamp(M, **params) - - bc = np.array([-61.5,-20.7]) - h = np.zeros(M.nC) + bc[0] - - - def getFields(timeStep,method): - prob = Richards.RichardsProblem(M,model, timeStep=timeStep, timeEnd=360, - boundaryConditions=bc, initialConditions=h, - doNewton=False, method=method) - return prob.fields(params['Ks']) - - Hs_M10 = getFields(10., 'mixed') - Hs_M30 = getFields(30., 'mixed') - Hs_M120= getFields(120.,'mixed') - Hs_H10 = getFields(10., 'head') - Hs_H30 = getFields(30., 'head') - Hs_H120= getFields(120.,'head') - - plt.figure(figsize=(13,5)) - plt.subplot(121) - plt.plot(40-M.gridCC, Hs_M10[-1],'b-') - plt.plot(40-M.gridCC, Hs_M30[-1],'r-') - plt.plot(40-M.gridCC, Hs_M120[-1],'k-') - plt.ylim([-70,-10]) - plt.title('Mixed Method') - plt.xlabel('Depth, cm') - plt.ylabel('Pressure Head, cm') - plt.legend(('$\Delta t$ = 10 sec','$\Delta t$ = 30 sec','$\Delta t$ = 120 sec')) - plt.subplot(122) - plt.plot(40-M.gridCC, Hs_H10[-1],'b-') - plt.plot(40-M.gridCC, Hs_H30[-1],'r-') - plt.plot(40-M.gridCC, Hs_H120[-1],'k-') - plt.ylim([-70,-10]) - plt.title('Head-Based Method') - plt.xlabel('Depth, cm') - plt.ylabel('Pressure Head, cm') - plt.legend(('$\Delta t$ = 10 sec','$\Delta t$ = 30 sec','$\Delta t$ = 120 sec')) - plt.show() - +.. plot:: examples/richards_comparisonToCeiliaEtAl1990.py Richards ======== diff --git a/setup.py b/setup.py index a48ca0ee..0485bd51 100644 --- a/setup.py +++ b/setup.py @@ -3,8 +3,10 @@ try: import SimPEG except ImportError, e: os.system('git clone https://github.com/simpeg/simpeg.git') - os.system('mv simpeg/SimPEG temp') - os.system('rm -rf simpeg') - os.system('mv temp SimPEG') - os.system('mkdir docs/examples/SimPEG') - os.system('cp -r SimPEG simpegflow/docs/examples/SimPEG') + os.system('python simpeg/setup.py install') + + # os.system('mv simpeg/SimPEG temp') + # os.system('rm -rf simpeg') + # os.system('mv temp SimPEG') + # os.system('mkdir docs/examples/SimPEG') + # os.system('cp -r SimPEG simpegflow/docs/examples/SimPEG') From 8919861e91e37eebf98dc9a2377752263800f7ba Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Thu, 27 Feb 2014 13:50:18 -0800 Subject: [PATCH 12/32] try again! figure out where to put things... --- docs/api_Richards.rst | 4 ++-- docs/examples/richards_comparisonToCeiliaEtAl1990.py | 4 ++++ setup.py | 10 +++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/api_Richards.rst b/docs/api_Richards.rst index ecaf8c83..1a773d68 100644 --- a/docs/api_Richards.rst +++ b/docs/api_Richards.rst @@ -35,8 +35,8 @@ Here we reproduce the results from Ceilia et al. (1990): .. plot:: examples/richards_comparisonToCeiliaEtAl1990.py -Richards -======== +Richards API +============ .. automodule:: simpegFLOW.Richards.Empirical :show-inheritance: diff --git a/docs/examples/richards_comparisonToCeiliaEtAl1990.py b/docs/examples/richards_comparisonToCeiliaEtAl1990.py index 917d1286..3f3d907d 100644 --- a/docs/examples/richards_comparisonToCeiliaEtAl1990.py +++ b/docs/examples/richards_comparisonToCeiliaEtAl1990.py @@ -2,6 +2,10 @@ from SimPEG import * from simpegFLOW import Richards import matplotlib.pyplot as plt +import os +os.system('pwd') +os.system('ls') + M = Mesh.TensorMesh([np.ones(40)]) M.setCellGradBC('dirichlet') params = Richards.Empirical.HaverkampParams().celia1990 diff --git a/setup.py b/setup.py index 0485bd51..4bb0cdde 100644 --- a/setup.py +++ b/setup.py @@ -5,8 +5,8 @@ except ImportError, e: os.system('git clone https://github.com/simpeg/simpeg.git') os.system('python simpeg/setup.py install') - # os.system('mv simpeg/SimPEG temp') - # os.system('rm -rf simpeg') - # os.system('mv temp SimPEG') - # os.system('mkdir docs/examples/SimPEG') - # os.system('cp -r SimPEG simpegflow/docs/examples/SimPEG') + os.system('mv simpeg/SimPEG temp') + os.system('rm -rf simpeg') + os.system('mv temp SimPEG') + os.system('mkdir docs/examples/SimPEG') + os.system('cp -r SimPEG simpegflow/docs/examples/SimPEG') From 752696a5d56d584688fd9684eb61b8a0ac7048ac Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Thu, 27 Feb 2014 15:20:35 -0800 Subject: [PATCH 13/32] added a few things! --- docs/api_Richards.rst | 4 ++-- docs/conf.py | 1 + docs/examples/richards_comparisonToCeiliaEtAl1990.py | 7 ++++--- setup.py | 2 -- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/api_Richards.rst b/docs/api_Richards.rst index 1a773d68..ecaf8c83 100644 --- a/docs/api_Richards.rst +++ b/docs/api_Richards.rst @@ -35,8 +35,8 @@ Here we reproduce the results from Ceilia et al. (1990): .. plot:: examples/richards_comparisonToCeiliaEtAl1990.py -Richards API -============ +Richards +======== .. automodule:: simpegFLOW.Richards.Empirical :show-inheritance: diff --git a/docs/conf.py b/docs/conf.py index 86aacce4..70c40605 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,6 +19,7 @@ import sys, os #sys.path.insert(0, os.path.abspath('.')) sys.path.append('../') +sys.path.append('../../') # -- General configuration ----------------------------------------------------- diff --git a/docs/examples/richards_comparisonToCeiliaEtAl1990.py b/docs/examples/richards_comparisonToCeiliaEtAl1990.py index 3f3d907d..c449638a 100644 --- a/docs/examples/richards_comparisonToCeiliaEtAl1990.py +++ b/docs/examples/richards_comparisonToCeiliaEtAl1990.py @@ -1,10 +1,11 @@ +import sys +sys.path.append('../') +sys.path.append('../../') + from SimPEG import * from simpegFLOW import Richards import matplotlib.pyplot as plt -import os -os.system('pwd') -os.system('ls') M = Mesh.TensorMesh([np.ones(40)]) M.setCellGradBC('dirichlet') diff --git a/setup.py b/setup.py index 4bb0cdde..06dca86b 100644 --- a/setup.py +++ b/setup.py @@ -8,5 +8,3 @@ except ImportError, e: os.system('mv simpeg/SimPEG temp') os.system('rm -rf simpeg') os.system('mv temp SimPEG') - os.system('mkdir docs/examples/SimPEG') - os.system('cp -r SimPEG simpegflow/docs/examples/SimPEG') From 98385e5f28e1ac89b2f00508a7e956fc1a7f981c Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Fri, 7 Mar 2014 14:14:45 -0800 Subject: [PATCH 14/32] Changed Data --> Survey --- docs/conf.py | 1 - .../richards_comparisonToCeiliaEtAl1990.py | 3 +-- simpegFLOW/Richards/RichardsProblem.py | 18 +++++++-------- simpegFLOW/Tests/test_Richards.py | 22 +++++++++---------- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 70c40605..86aacce4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,7 +19,6 @@ import sys, os #sys.path.insert(0, os.path.abspath('.')) sys.path.append('../') -sys.path.append('../../') # -- General configuration ----------------------------------------------------- diff --git a/docs/examples/richards_comparisonToCeiliaEtAl1990.py b/docs/examples/richards_comparisonToCeiliaEtAl1990.py index c449638a..385263f8 100644 --- a/docs/examples/richards_comparisonToCeiliaEtAl1990.py +++ b/docs/examples/richards_comparisonToCeiliaEtAl1990.py @@ -1,5 +1,4 @@ import sys -sys.path.append('../') sys.path.append('../../') from SimPEG import * @@ -17,7 +16,7 @@ h = np.zeros(M.nC) + bc[0] def getFields(timeStep,method): - prob = Richards.RichardsProblem(M,model, timeStep=timeStep, timeEnd=360, + prob = Richards.RichardsProblem(model, timeStep=timeStep, timeEnd=360, boundaryConditions=bc, initialConditions=h, doNewton=False, method=method) return prob.fields(params['Ks']) diff --git a/simpegFLOW/Richards/RichardsProblem.py b/simpegFLOW/Richards/RichardsProblem.py index 2a37308b..b77b52d5 100644 --- a/simpegFLOW/Richards/RichardsProblem.py +++ b/simpegFLOW/Richards/RichardsProblem.py @@ -1,13 +1,13 @@ from SimPEG import * from Empirical import RichardsModel -class RichardsData(Data.BaseData): - """docstring for RichardsData""" +class RichardsSurvey(Survey.BaseSurvey): + """docstring for RichardsSurvey""" P = None def __init__(self, **kwargs): - Data.BaseData.__init__(self, **kwargs) + Survey.BaseSurvey.__init__(self, **kwargs) @property def dataType(self): @@ -66,11 +66,11 @@ class RichardsProblem(Problem.BaseProblem): boundaryConditions = None initialConditions = None - dataPair = RichardsData + surveyPair = RichardsSurvey modelPair = RichardsModel - def __init__(self, mesh, model, **kwargs): - Problem.BaseProblem.__init__(self, mesh, model, **kwargs) + def __init__(self, model, **kwargs): + Problem.BaseProblem.__init__(self, model, **kwargs) @property def timeStep(self): @@ -221,7 +221,7 @@ class RichardsProblem(Problem.BaseProblem): B = np.array(sp.vstack(Bs).todense()) Ainv = Solver(A) - P = self.data.projectFieldsDeriv(u, m) + P = self.survey.projectFieldsDeriv(u, m) J = P * Ainv.solve(B) return J @@ -244,14 +244,14 @@ class RichardsProblem(Problem.BaseProblem): Adiaginv = Solver(Adiag) JvC[ii] = Adiaginv.solve(B*v - Asub*JvC[ii-1]) - P = self.data.projectFieldsDeriv(u, m) + P = self.survey.projectFieldsDeriv(u, m) return P * np.concatenate(JvC) def Jtvec(self, m, v, u=None): if u is None: u = self.field(m) - P = self.data.projectFieldsDeriv(u, m) + P = self.survey.projectFieldsDeriv(u, m) PTv = P.T*v # This is done via backward substitution. diff --git a/simpegFLOW/Tests/test_Richards.py b/simpegFLOW/Tests/test_Richards.py index 6dcde6c5..84c31c2e 100644 --- a/simpegFLOW/Tests/test_Richards.py +++ b/simpegFLOW/Tests/test_Richards.py @@ -114,21 +114,21 @@ class RichardsTests1D(unittest.TestCase): bc = np.array([-61.5,-20.7]) h = np.zeros(M.nC) + bc[0] - prob = Richards.RichardsProblem(M,model, timeStep=60, timeEnd=180, + prob = Richards.RichardsProblem(model, timeStep=60, timeEnd=180, boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') q = sp.csr_matrix((np.ones(3),(np.arange(3),np.array([5,10,15]))),shape=(3,M.nC)) P = sp.kron(sp.identity(prob.numIts),q) - data = Richards.RichardsData(P=P) + survey = Richards.RichardsSurvey(P=P) - prob.pair(data) + prob.pair(survey) self.h0 = h self.M = M self.Ks = params['Ks'] self.prob = prob - self.data = data + self.survey = survey def test_Richards_getResidual_Newton(self): self.prob.doNewton = True @@ -144,7 +144,7 @@ class RichardsTests1D(unittest.TestCase): def test_Adjoint_PressureHead(self): self.prob.dataType = 'pressureHead' - v = np.random.rand(self.data.P.shape[0]) + v = np.random.rand(self.survey.P.shape[0]) z = np.random.rand(self.M.nC) Hs = self.prob.fields(self.Ks) vJz = v.dot(self.prob.Jvec(self.Ks,z,u=Hs)) @@ -157,7 +157,7 @@ class RichardsTests1D(unittest.TestCase): def test_Adjoint_Saturation(self): self.prob.dataType = 'saturation' - v = np.random.rand(self.data.P.shape[0]) + v = np.random.rand(self.survey.P.shape[0]) z = np.random.rand(self.M.nC) Hs = self.prob.fields(self.Ks) vJz = v.dot(self.prob.Jvec(self.Ks,z,u=Hs)) @@ -173,10 +173,10 @@ class RichardsTests1D(unittest.TestCase): self.prob.unpair() mTrue = np.ones(self.M.nC)*self.Ks stdev = 0.01 # The standard deviation for the noise - data = self.prob.createSyntheticData(mTrue, std=stdev, P=self.data.P) + survey = self.prob.createSyntheticSurvey(mTrue, std=stdev, P=self.survey.P) opt = Optimization.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) reg = Regularization.Tikhonov(Model.BaseModel(self.M)) - obj = ObjFunction.BaseObjFunction(data, reg) + obj = ObjFunction.BaseObjFunction(survey, reg) derChk = lambda m: [obj.dataObj(m), obj.dataObjDeriv(m)] print 'Testing Richards Derivative - Pressure Head' passed = checkDerivative(derChk, mTrue, num=5, plotIt=False) @@ -187,10 +187,10 @@ class RichardsTests1D(unittest.TestCase): self.prob.dataType = 'saturation' mTrue = np.ones(self.M.nC)*self.Ks stdev = 0.01 # The standard deviation for the noise - data = self.prob.createSyntheticData(mTrue, std=stdev, P=self.data.P) + survey = self.prob.createSyntheticSurvey(mTrue, std=stdev, P=self.survey.P) opt = Optimization.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) reg = Regularization.Tikhonov(Model.BaseModel(self.M)) - obj = ObjFunction.BaseObjFunction(data, reg) + obj = ObjFunction.BaseObjFunction(survey, reg) derChk = lambda m: [obj.dataObj(m), obj.dataObjDeriv(m)] print 'Testing Richards Derivative - Saturation' passed = checkDerivative(derChk, mTrue, num=5, plotIt=False) @@ -264,7 +264,7 @@ class RichardsTests1D(unittest.TestCase): # self.prob.dataType = 'pressureHead' # mTrue = np.ones(self.M.nC)*self.Ks # stdev = 0.01 # The standard deviation for the noise -# dobs = self.prob.createSyntheticData(mTrue,std=stdev)[0] +# dobs = self.prob.createSyntheticSurvey(mTrue,std=stdev)[0] # self.prob.dobs = dobs # self.prob.std = dobs*0 + stdev # Hs = self.prob.field(mTrue) From 9252ed7980d5bde45b5afe3f99fbea1f451f1d7b Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Wed, 9 Apr 2014 10:34:47 -0600 Subject: [PATCH 15/32] VanG empirical relations --- simpegFLOW/Richards/Empirical.py | 448 ++++++++++++++++++------- simpegFLOW/Richards/RichardsProblem.py | 21 +- simpegFLOW/Tests/test_Richards.py | 24 ++ 3 files changed, 357 insertions(+), 136 deletions(-) diff --git a/simpegFLOW/Richards/Empirical.py b/simpegFLOW/Richards/Empirical.py index 0688eff5..8565c05c 100644 --- a/simpegFLOW/Richards/Empirical.py +++ b/simpegFLOW/Richards/Empirical.py @@ -1,4 +1,4 @@ -from SimPEG import Model, Utils, np +from SimPEG import Mesh, Model, Utils, np class RichardsModel(object): @@ -42,19 +42,32 @@ class RichardsModel(object): def kDerivU(self, u, m): return self.kModel.transformDerivU(u, m) + def plot(self, m): + import matplotlib.pyplot as plt -def _ModelProperty(name, model, doc=None, default=None): + m = m[0] + h = np.linspace(-100, 20, 1000) + ax = plt.subplot(121) + ax.plot(self.theta(h, m), h) + ax = plt.subplot(122) + ax.semilogx(self.k(h, m), h) + + + +def _ModelProperty(name, models, doc=None, default=None): def fget(self): + model = models[0] if getattr(self, model, None) is not None: MOD = getattr(self, model) return getattr(MOD, name, default) return default def fset(self, value): - if getattr(self, model, None) is not None: - MOD = getattr(self, model) - setattr(MOD, name, value) + for model in models: + if getattr(self, model, None) is not None: + MOD = getattr(self, model) + setattr(MOD, name, value) return property(fget, fset=fset, doc=doc) @@ -156,14 +169,14 @@ class _haverkamp_k(Model.BaseNonLinearModel): class Haverkamp(RichardsModel): """Haverkamp Model""" - alpha = _ModelProperty('alpha', 'thetaModel', default=1.6110e+06) - beta = _ModelProperty('beta', 'thetaModel', default=3.96) - theta_r = _ModelProperty('theta_r', 'thetaModel', default=0.075) - theta_s = _ModelProperty('theta_s', 'thetaModel', default=0.287) + alpha = _ModelProperty('alpha', ['thetaModel'], default=1.6110e+06) + beta = _ModelProperty('beta', ['thetaModel'], default=3.96) + theta_r = _ModelProperty('theta_r', ['thetaModel'], default=0.075) + theta_s = _ModelProperty('theta_s', ['thetaModel'], default=0.287) - Ks = _ModelProperty('Ks', 'kModel', default=np.log(24.96)) - A = _ModelProperty('A', 'kModel', default=1.1750e+06) - gamma = _ModelProperty('gamma', 'kModel', default=4.74) + Ks = _ModelProperty('Ks', ['kModel'], default=np.log(24.96)) + A = _ModelProperty('A', ['kModel'], default=1.1750e+06) + gamma = _ModelProperty('gamma', ['kModel'], default=4.74) def __init__(self, mesh, **kwargs): RichardsModel.__init__(self, mesh, @@ -174,138 +187,319 @@ class Haverkamp(RichardsModel): +class _vangenuchten_theta(Model.BaseNonLinearModel): -# class Haverkamp(object): -# """docstring for Haverkamp""" + theta_s = 0.430 + theta_r = 0.078 + alpha = 0.036 + n = 1.560 -# empiricalModelName = "VanGenuchten" + def __init__(self, mesh, **kwargs): + Model.BaseNonLinearModel.__init__(self, mesh) + Utils.setKwargs(self, **kwargs) -# theta_s = 0.430 -# theta_r = 0.078 -# alpha = 0.036 -# beta = 3.960 -# A = 1.175e+06 -# gamma = 4.74 -# Ks = np.log(24.96) + def setModel(self, m): + self._currentModel = m -# def __init__(self, **kwargs): -# Utils.setKwargs(self, **kwargs) + def transform(self, u, m): + self.setModel(m) + m = 1 - 1.0/self.n + f = (( self.theta_s - self.theta_r )/ + ((1+abs(self.alpha*u)**self.n)**m) + self.theta_r) + f[u > 0] = self.theta_s + return f -# def setModel(self, m): -# self.Ks = m + def transformDerivM(self, u, m): + self.setModel(m) -# def moistureContent(self, h): -# f = (self.alpha*(self.theta_s - self.theta_r )/ -# (self.alpha + abs(h)**self.beta) + self.theta_r) -# f[h > 0] = self.theta_s -# return f - -# def moistureContentDeriv(self, h): -# g = (self.alpha*((self.theta_s - self.theta_r)/ -# (self.alpha + abs(h)**self.beta)**2) -# *(-self.beta*abs(h)**(self.beta-1)*np.sign(h))); -# g[h >= 0] = 0 -# g = Utils.sdiag(g) -# return g - -# def hydraulicConductivity(self, h): -# f = np.exp(self.Ks)*self.A/(self.A+abs(h)**self.gamma) -# if type(self.Ks) is np.ndarray and self.Ks.size > 1: -# f[h >= 0] = np.exp(self.Ks[h >= 0]) -# else: -# f[h >= 0] = np.exp(self.Ks) -# return f - -# def hydraulicConductivityModelDeriv(self, h): -# #A -# # dA = np.exp(self.Ks)/(self.A+abs(h)**self.gamma) - np.exp(self.Ks)*self.A/(self.A+abs(h)**self.gamma)**2; -# #gamma -# # dgamma = -(self.A*np.exp(self.Ks)*np.log(abs(h))*abs(h)**self.gamma)/(self.A + abs(h)**self.gamma)**2; -# return Utils.sdiag(self.hydraulicConductivity(h)) # This assumes that the the model is Ks - -# def hydraulicConductivityDeriv(self, h): -# g = -(np.exp(self.Ks)*self.A*self.gamma*abs(h)**(self.gamma-1)*np.sign(h))/((self.A+abs(h)**self.gamma)**2) -# g[h >= 0] = 0 -# g = Utils.sdiag(g) -# return g + def transformDerivU(self, u, m): + g = -self.alpha*self.n*abs(self.alpha*u)**(self.n - 1)*np.sign(self.alpha*u)*(1./self.n - 1)*(self.theta_r - self.theta_s)*(abs(self.alpha*u)**self.n + 1)**(1./self.n - 2) + g[u >= 0] = 0 + g = Utils.sdiag(g) + return g -# class VanGenuchten(object): -# """ +class _vangenuchten_k(Model.BaseNonLinearModel): -# .. math:: + I = 0.500 + alpha = 0.036 + n = 1.560 + Ks = np.log(24.96) -# \\theta(h) = \\frac{\\alpha (\\theta_s - \\theta_r)}{\\alpha + |h|^\\beta} + \\theta_r + def __init__(self, mesh, **kwargs): + Model.BaseNonLinearModel.__init__(self, mesh) + Utils.setKwargs(self, **kwargs) -# Where parameters alpha, beta, gamma, A are constants in the media; -# theta_r and theta_s are the residual and saturated moisture -# contents; and K_s is the saturated hydraulic conductivity. + def setModel(self, m): + self._currentModel = m + #TODO: Fix me! + self.Ks = m -# Celia1990 + def transform(self, u, m): + self.setModel(m) -# """ + alpha = self.alpha + I = self.I + n = self.n + Ks = self.Ks + m = 1.0 - 1.0/n -# empiricalModelName = "VanGenuchten" + theta_e = 1.0/((1.0+abs(alpha*u)**n)**m) + f = np.exp(Ks)*theta_e**I* ( ( 1.0 - ( 1.0 - theta_e**(1.0/m) )**m )**2 ) + if type(self.Ks) is np.ndarray and self.Ks.size > 1: + f[u >= 0] = np.exp(self.Ks[u >= 0]) + else: + f[u >= 0] = np.exp(self.Ks) + return f -# theta_s = 0.430 -# theta_r = 0.078 -# alpha = 0.036 -# n = 1.560 -# beta = 3.960 -# I = 0.500 -# Ks = np.log(24.96) - -# def __init__(self, **kwargs): -# Utils.setKwargs(self, **kwargs) - -# def setModel(self, m): -# self.Ks = m - -# def moistureContent(self, h): -# m = 1 - 1.0/self.n; -# f = (( self.theta_s - self.theta_r )/ -# ((1+abs(self.alpha*h)**self.n)**m) + self.theta_r) -# f[h > 0] = self.theta_s -# return f - -# def moistureContentDeriv(self, h): -# g = -self.alpha*self.n*abs(self.alpha*h)**(self.n - 1)*np.sign(self.alpha*h)*(1./self.n - 1)*(self.theta_r - self.theta_s)*(abs(self.alpha*h)**self.n + 1)**(1./self.n - 2) -# g[h > 0] = 0 -# g = Utils.sdiag(g) -# return g - -# def hydraulicConductivity(self, h): -# alpha = self.alpha -# I = self.I -# n = self.n -# Ks = self.Ks -# m = 1.0 - 1.0/n - -# theta_e = 1.0/((1.0+abs(alpha*h)**n)**m) -# f = np.exp(Ks)*theta_e**I* ( ( 1.0 - ( 1.0 - theta_e**(1.0/m) )**m )**2 ) -# if type(self.Ks) is np.ndarray and self.Ks.size > 1: -# f[h >= 0] = np.exp(self.Ks[h >= 0]) -# else: -# f[h >= 0] = np.exp(self.Ks) -# return f - -# def hydraulicConductivityModelDeriv(self, h): + def transformDerivM(self, u, m): + self.setModel(m) # #alpha -# # dA = I*h*n*np.exp(Ks)*abs(alpha*h)**(n - 1)*np.sign(alpha*h)*(1.0/n - 1)*((abs(alpha*h)**n + 1)**(1.0/n - 1))**(I - 1)*((1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)**2*(abs(alpha*h)**n + 1)**(1.0/n - 2) - (2*h*n*np.exp(Ks)*abs(alpha*h)**(n - 1)*np.sign(alpha*h)*(1.0/n - 1)*((abs(alpha*h)**n + 1)**(1.0/n - 1))**I*((1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)*(abs(alpha*h)**n + 1)**(1.0/n - 2))/(((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1) + 1)*(1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1.0/n)); +# # dA = I*u*n*np.exp(Ks)*abs(alpha*u)**(n - 1)*np.sign(alpha*u)*(1.0/n - 1)*((abs(alpha*u)**n + 1)**(1.0/n - 1))**(I - 1)*((1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)**2*(abs(alpha*u)**n + 1)**(1.0/n - 2) - (2*u*n*np.exp(Ks)*abs(alpha*u)**(n - 1)*np.sign(alpha*u)*(1.0/n - 1)*((abs(alpha*u)**n + 1)**(1.0/n - 1))**I*((1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)*(abs(alpha*u)**n + 1)**(1.0/n - 2))/(((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1) + 1)*(1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1.0/n)); # #n -# # dn = 2*np.exp(Ks)*((np.log(1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))*(1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n))/n**2 + ((1.0/n - 1)*(((np.log(abs(alpha*h)**n + 1)*(abs(alpha*h)**n + 1)**(1.0/n - 1))/n**2 - abs(alpha*h)**n*np.log(abs(alpha*h))*(1.0/n - 1)*(abs(alpha*h)**n + 1)**(1.0/n - 2))/((1.0/n - 1)*((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1) + 1)) - np.log((abs(alpha*h)**n + 1)**(1.0/n - 1))/(n**2*(1.0/n - 1)**2*((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))))/(1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1.0/n))*((abs(alpha*h)**n + 1)**(1.0/n - 1))**I*((1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1) - I*np.exp(Ks)*((np.log(abs(alpha*h)**n + 1)*(abs(alpha*h)**n + 1)**(1.0/n - 1))/n**2 - abs(alpha*h)**n*np.log(abs(alpha*h))*(1.0/n - 1)*(abs(alpha*h)**n + 1)**(1.0/n - 2))*((abs(alpha*h)**n + 1)**(1.0/n - 1))**(I - 1)*((1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)**2; +# # dn = 2*np.exp(Ks)*((np.log(1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))*(1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n))/n**2 + ((1.0/n - 1)*(((np.log(abs(alpha*u)**n + 1)*(abs(alpha*u)**n + 1)**(1.0/n - 1))/n**2 - abs(alpha*u)**n*np.log(abs(alpha*u))*(1.0/n - 1)*(abs(alpha*u)**n + 1)**(1.0/n - 2))/((1.0/n - 1)*((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1) + 1)) - np.log((abs(alpha*u)**n + 1)**(1.0/n - 1))/(n**2*(1.0/n - 1)**2*((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))))/(1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1.0/n))*((abs(alpha*u)**n + 1)**(1.0/n - 1))**I*((1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1) - I*np.exp(Ks)*((np.log(abs(alpha*u)**n + 1)*(abs(alpha*u)**n + 1)**(1.0/n - 1))/n**2 - abs(alpha*u)**n*np.log(abs(alpha*u))*(1.0/n - 1)*(abs(alpha*u)**n + 1)**(1.0/n - 2))*((abs(alpha*u)**n + 1)**(1.0/n - 1))**(I - 1)*((1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)**2; # #I -# # dI = np.exp(Ks)*np.log((abs(alpha*h)**n + 1)**(1.0/n - 1))*((abs(alpha*h)**n + 1)**(1.0/n - 1))**I*((1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)**2; -# return Utils.sdiag(self.hydraulicConductivity(h)) # This assumes that the the model is Ks +# # dI = np.exp(Ks)*np.log((abs(alpha*u)**n + 1)**(1.0/n - 1))*((abs(alpha*u)**n + 1)**(1.0/n - 1))**I*((1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)**2; + return Utils.sdiag(self.transform(u, m)) # This assumes that the the model is Ks -# def hydraulicConductivityDeriv(self, h): -# alpha = self.alpha -# I = self.I -# n = self.n -# Ks = self.Ks -# m = 1.0 - 1.0/n + def transformDerivU(self, u, m): + self.setModel(m) + alpha = self.alpha + I = self.I + n = self.n + Ks = self.Ks + m = 1.0 - 1.0/n -# g = I*alpha*n*np.exp(Ks)*abs(alpha*h)**(n - 1.0)*np.sign(alpha*h)*(1.0/n - 1.0)*((abs(alpha*h)**n + 1)**(1.0/n - 1))**(I - 1)*((1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)**2*(abs(alpha*h)**n + 1)**(1.0/n - 2) - (2*alpha*n*np.exp(Ks)*abs(alpha*h)**(n - 1)*np.sign(alpha*h)*(1.0/n - 1)*((abs(alpha*h)**n + 1)**(1.0/n - 1))**I*((1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)*(abs(alpha*h)**n + 1)**(1.0/n - 2))/(((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1) + 1)*(1 - 1.0/((abs(alpha*h)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1.0/n)) -# g[h >= 0] = 0 -# g = Utils.sdiag(g) -# return g + g = I*alpha*n*np.exp(Ks)*abs(alpha*u)**(n - 1.0)*np.sign(alpha*u)*(1.0/n - 1.0)*((abs(alpha*u)**n + 1)**(1.0/n - 1))**(I - 1)*((1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)**2*(abs(alpha*u)**n + 1)**(1.0/n - 2) - (2*alpha*n*np.exp(Ks)*abs(alpha*u)**(n - 1)*np.sign(alpha*u)*(1.0/n - 1)*((abs(alpha*u)**n + 1)**(1.0/n - 1))**I*((1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)*(abs(alpha*u)**n + 1)**(1.0/n - 2))/(((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1) + 1)*(1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1.0/n)) + g[u >= 0] = 0 + g = Utils.sdiag(g) + return g + +class VanGenuchten(RichardsModel): + """vanGenuchten Model""" + + theta_r = _ModelProperty('theta_r', ['thetaModel'], default=0.075) + theta_s = _ModelProperty('theta_s', ['thetaModel'], default=0.287) + + alpha = _ModelProperty('alpha', ['thetaModel', 'kModel'], default=0.036) + n = _ModelProperty('n', ['thetaModel', 'kModel'], default=1.560) + + Ks = _ModelProperty('Ks', ['kModel'], default=np.log(24.96)) + I = _ModelProperty('I', ['kModel'], default=0.500) + + def __init__(self, mesh, **kwargs): + RichardsModel.__init__(self, mesh, + _haverkamp_theta(mesh), + _haverkamp_k(mesh)) + Utils.setKwargs(self, **kwargs) + + +class vanGenuchtenParams(object): + """ + The RETC code for quantifying the hydraulic functions of unsaturated soils, + Van Genuchten, M Th, Leij, F J, Yates, S R + + Table 3: Average values for selected soil water retention and hydraulic + conductivity parameters for 11 major soil textural groups + according to Rawls et al. [1982] + + """ + def __init__(self): pass + @property + def sand(self): + return {"theta_r": 0.020, "theta_s": 0.417, "alpha": 0.138*100., "n": 1.592, "Ks": 504.0/100./24./60./60.} + @property + def loamySand(self): + return {"theta_r": 0.035, "theta_s": 0.401, "alpha": 0.115*100., "n": 1.474, "Ks": 146.6/100./24./60./60.} + @property + def sandyLoam(self): + return {"theta_r": 0.041, "theta_s": 0.412, "alpha": 0.068*100., "n": 1.322, "Ks": 62.16/100./24./60./60.} + @property + def loam(self): + return {"theta_r": 0.027, "theta_s": 0.434, "alpha": 0.090*100., "n": 1.220, "Ks": 16.32/100./24./60./60.} + @property + def siltLoam(self): + return {"theta_r": 0.015, "theta_s": 0.486, "alpha": 0.048*100., "n": 1.211, "Ks": 31.68/100./24./60./60.} + @property + def sandyClayLoam(self): + return {"theta_r": 0.068, "theta_s": 0.330, "alpha": 0.036*100., "n": 1.250, "Ks": 10.32/100./24./60./60.} + @property + def clayLoam(self): + return {"theta_r": 0.075, "theta_s": 0.390, "alpha": 0.039*100., "n": 1.194, "Ks": 5.52/100./24./60./60.} + @property + def siltyClayLoam(self): + return {"theta_r": 0.040, "theta_s": 0.432, "alpha": 0.031*100., "n": 1.151, "Ks": 3.60/100./24./60./60.} + @property + def sandyClay(self): + return {"theta_r": 0.109, "theta_s": 0.321, "alpha": 0.034*100., "n": 1.168, "Ks": 2.88/100./24./60./60.} + @property + def siltyClay(self): + return {"theta_r": 0.056, "theta_s": 0.423, "alpha": 0.029*100., "n": 1.127, "Ks": 2.16/100./24./60./60.} + @property + def clay(self): + return {"theta_r": 0.090, "theta_s": 0.385, "alpha": 0.027*100., "n": 1.131, "Ks": 1.44/100./24./60./60.} + + + # From: INDIRECT METHODS FOR ESTIMATING THE HYDRAULIC PROPERTIES OF UNSATURATED SOILS + # @property + # def siltLoamGE3(self): + # """Soil Index: 3310""" + # return {"theta_r": 0.139, "theta_s": 0.394, "alpha": 0.00414, "n": 2.15} + # @property + # def yoloLightClayK_WC(self): + # """Soil Index: None""" + # return {"theta_r": 0.205, "theta_s": 0.499, "alpha": 0.02793, "n": 1.71} + # @property + # def yoloLightClayK_H(self): + # """Soil Index: None""" + # return {"theta_r": 0.205, "theta_s": 0.499, "alpha": 0.02793, "n": 1.71} + # @property + # def hygieneSandstone(self): + # """Soil Index: 4130""" + # return {"theta_r": 0.000, "theta_s": 0.256, "alpha": 0.00562, "n": 3.27} + # @property + # def lambcrgClay(self): + # """Soil Index: 1003""" + # return {"theta_r": 0.000, "theta_s": 0.502, "alpha": 0.140, "n": 1.93} + # @property + # def beitNetofaClaySoil(self): + # """Soil Index: 1006""" + # return {"theta_r": 0.000, "theta_s": 0.447, "alpha": 0.00156, "n": 1.17} + # @property + # def shiohotSiltyClay(self): + # """Soil Index: 1101""" + # return {"theta_r": 0.000, "theta_s": 0.456, "alpha": 183, "n":1.17} + # @property + # def siltColumbia(self): + # """Soil Index: 2001""" + # return {"theta_r": 0.146, "theta_s": 0.397, "alpha": 0.0145, "n": 1.85} + # @property + # def siltMontCenis(self): + # """Soil Index: 2002""" + # return {"theta_r": 0.000, "theta_s": 0.425, "alpha": 0.0103, "n": 1.34} + # @property + # def slateDust(self): + # """Soil Index: 2004""" + # return {"theta_r": 0.000, "theta_s": 0.498, "alpha": 0.00981, "n": 6.75} + # @property + # def weldSiltyClayLoam(self): + # """Soil Index: 3001""" + # return {"theta_r": 0.159, "theta_s": 0.496, "alpha": 0.0136, "n": 5.45} + # @property + # def rideauClayLoam_Wetting(self): + # """Soil Index: 3101a""" + # return {"theta_r": 0.279, "theta_s": 0.419, "alpha": 0.0661, "n": 1.89} + # @property + # def rideauClayLoam_Drying(self): + # """Soil Index: 3101b""" + # return {"theta_r": 0.290, "theta_s": 0.419, "alpha": 0.0177, "n": 3.18} + # @property + # def caribouSiltLoam_Drying(self): + # """Soil Index: 3301a""" + # return {"theta_r": 0.000, "theta_s": 0.451, "alpha": 0.00845, "n": 1.29} + # @property + # def caribouSiltLoam_Wetting(self): + # """Soil Index: 3301b""" + # return {"theta_r": 0.000, "theta_s": 0.450, "alpha": 0.140, "n": 1.09} + # @property + # def grenvilleSiltLoam_Wetting(self): + # """Soil Index: 3302a""" + # return {"theta_r": 0.013, "theta_s": 0523, "alpha": 0.0630, "n": 1.24} + # @property + # def grenvilleSiltLoam_Drying(self): + # """Soil Index: 3302c""" + # return {"theta_r": 0.000, "theta_s": 0.488, "alpha": 0.0112, "n": 1.23} + # @property + # def touchetSiltLoam(self): + # """Soil Index: 3304""" + # return {"theta_r": 0.183, "theta_s": 0.498, "alpha": 0.0104, "n": 5.78} + # @property + # def gilatLoam(self): + # """Soil Index: 3402a""" + # return {"theta_r": 0.000, "theta_s": 0.454, "alpha": 0.0291, "n": 1.47} + # @property + # def pachapaLoam(self): + # """Soil Index: 3403""" + # return {"theta_r": 0.000, "theta_s": 0.472, "alpha": 0.00829, "n": 1.62} + # @property + # def adelantoLoam(self): + # """Soil Index: 3404""" + # return {"theta_r": 0.000, "theta_s": 0.444, "alpha": 0.00710, "n": 1.26} + # @property + # def indioLoam(self): + # """Soil Index: 3405a""" + # return {"theta_r": 0.000, "theta_s": 0.507, "alpha": 0.00847, "n": 1.60} + # @property + # def guclphLoam(self): + # """Soil Index: 3407a""" + # return {"theta_r": 0.000, "theta_s": 0.563, "alpha": 0.0275, "n": 1.27} + # @property + # def guclphLoam(self): + # """Soil Index: 3407b""" + # return {"theta_r": 0.236, "theta_s": 0.435, "alpha": 0.0271, "n": 262} + # @property + # def rubiconSandyLoam(self): + # """Soil Index: 3501a""" + # return {"theta_r": 0.000, "theta_s": 0.393, "alpha": 0.00972, "n": 2.18} + # @property + # def rubiconSandyLoam(self): + # """Soil Index: 350lb""" + # return {"theta_r": 0.000, "theta_s": 0.433, "alpha": 0.147, "n": 1.28} + # @property + # def pachapaFmeSandyClay(self): + # """Soil Index: 3503a""" + # return {"theta_r": 0.000, "theta_s": 0.340, "alpha": 0.0194, "n": 1.45} + # @property + # def gilatSandyLoam(self): + # """Soil Index: 3504""" + # return {"theta_r": 0.000, "theta_s": 0.432, "alpha": 0.0103, "n": 1.48} + # @property + # def plainfieldSand_210to250(self): + # """Soil Index: 4101a""" + # return {"theta_r": 0.000, "theta_s": 0.351, "alpha": 0.0236, "n": 12.30} + # @property + # def plainfieldSand_210to250(self): + # """Soil Index: 4101b""" + # return {"theta_r": 0.000, "theta_s": 0.312, "alpha": 0.0387, "n": 4.48} + # @property + # def plainfieldSand_177to210(self): + # """Soil Index: 4102a""" + # return {"theta_r": 0.000, "theta_s": 0.361, "alpha": 0.0207, "n": 10.0} + # @property + # def plainfieldSand_177to210(self): + # """Soil Index: 4102b""" + # return {"theta_r": 0.022, "theta_s": 0.309, "alpha": 0.0328, "n": 6.23} + # @property + # def plainfieldSand_149to177(self): + # """Soil Index: 4103a""" + # return {"theta_r": 0.000, "theta_s": 0.387, "alpha": 0.0173, "n": 7.80} + # @property + # def plainfieldSand_149to177(self): + # """Soil Index: 4103b""" + # return {"theta_r": 0.025, "theta_s": 0.321, "alpha": 0.0272, "n": 6.69} + # @property + # def plainfieldSand_l25to149(self): + # """Soil Index: 4104a""" + # return {"theta_r": 0.000, "theta_s": 03770, "alpha": 0.0145, "n": 10.60} + # @property + # def plainfieldSand_125to149(self): + # """Soil Index: 4104b""" + # return {"theta_r": 0.000, "theta_s": 0.342, "alpha": 0.0230, "n": 5.18} + + +if __name__ == '__main__': + import matplotlib.pyplot as plt + M = Mesh.TensorMesh([10]) + VGparams = vanGenuchtenParams() + leg = [] + for p in dir(VGparams): + if p[0] == '_': continue + leg += [p] + params = getattr(VGparams, p) + model = VanGenuchten(M, **params) + ks = np.log(np.r_[params['Ks']]) + model.plot(ks) + + plt.legend(leg) + + plt.show() diff --git a/simpegFLOW/Richards/RichardsProblem.py b/simpegFLOW/Richards/RichardsProblem.py index b77b52d5..7ea788f6 100644 --- a/simpegFLOW/Richards/RichardsProblem.py +++ b/simpegFLOW/Richards/RichardsProblem.py @@ -69,6 +69,9 @@ class RichardsProblem(Problem.BaseProblem): surveyPair = RichardsSurvey modelPair = RichardsModel + Solver = Solver + solverOpts = {} + def __init__(self, model, **kwargs): Problem.BaseProblem.__init__(self, model, **kwargs) @@ -102,7 +105,7 @@ class RichardsProblem(Problem.BaseProblem): def rootFinder(self): """Root-finding Algorithm""" if getattr(self, '_rootFinder', None) is None: - self._rootFinder = Optimization.NewtonRoot(doLS=self.doNewton) + self._rootFinder = Optimization.NewtonRoot(doLS=self.doNewton, Solver=self.Solver) return self._rootFinder def fields(self, m): @@ -121,9 +124,9 @@ class RichardsProblem(Problem.BaseProblem): if self.mesh.dim == 1: Dz = self.mesh.faceDivx elif self.mesh.dim == 2: - Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.nFv[0]), self.mesh.faceDivy),format='csr') + Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.vnF[0]), self.mesh.faceDivy),format='csr') elif self.mesh.dim == 3: - Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.nFv[0]+self.mesh.nFv[1]), self.mesh.faceDivz),format='csr') + Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.vnF[0]+self.mesh.vnF[1]), self.mesh.faceDivz),format='csr') bc = self.boundaryConditions dt = self.timeStep @@ -175,9 +178,9 @@ class RichardsProblem(Problem.BaseProblem): if self.mesh.dim == 1: Dz = self.mesh.faceDivx elif self.mesh.dim == 2: - Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.nFv[0]), self.mesh.faceDivy),format='csr') + Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.vnF[0]), self.mesh.faceDivy),format='csr') elif self.mesh.dim == 3: - Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.nFv[0]+self.mesh.nFv[1]), self.mesh.faceDivz),format='csr') + Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.vnF[0]+self.mesh.vnF[1]), self.mesh.faceDivz),format='csr') bc = self.boundaryConditions dt = self.timeStep @@ -220,7 +223,7 @@ class RichardsProblem(Problem.BaseProblem): A = As + Ad B = np.array(sp.vstack(Bs).todense()) - Ainv = Solver(A) + Ainv = self.Solver(A, **self.solverOpts) P = self.survey.projectFieldsDeriv(u, m) J = P * Ainv.solve(B) return J @@ -233,7 +236,7 @@ class RichardsProblem(Problem.BaseProblem): # This is done via forward substitution. temp, Adiag, B = self.diagsJacobian(m, u[0],u[1]) - Adiaginv = Solver(Adiag) + Adiaginv = self.Solver(Adiag, **self.solverOpts) JvC[0] = Adiaginv.solve(B*v) # M = @(x) tril(Adiag)\(diag(Adiag).*(triu(Adiag)\x)); @@ -241,7 +244,7 @@ class RichardsProblem(Problem.BaseProblem): for ii in range(1,len(u)-1): Asub, Adiag, B = self.diagsJacobian(m, u[ii],u[ii+1]) - Adiaginv = Solver(Adiag) + Adiaginv = self.Solver(Adiag, **self.solverOpts) JvC[ii] = Adiaginv.solve(B*v - Asub*JvC[ii-1]) P = self.survey.projectFieldsDeriv(u, m) @@ -261,7 +264,7 @@ class RichardsProblem(Problem.BaseProblem): Asub, Adiag, B = self.diagsJacobian(m, u[ii-1], u[ii]) #select the correct part of v vpart = range((ii-1)*Adiag.shape[0], (ii)*Adiag.shape[0]) - AdiaginvT = Solver(Adiag.T) + AdiaginvT = self.Solver(Adiag.T, **self.solverOpts) JTvC = AdiaginvT.solve(PTv[vpart] - minus) minus = Asub.T*JTvC # this is now the super diagonal. BJtv = BJtv + B.T*JTvC diff --git a/simpegFLOW/Tests/test_Richards.py b/simpegFLOW/Tests/test_Richards.py index 84c31c2e..399e3e94 100644 --- a/simpegFLOW/Tests/test_Richards.py +++ b/simpegFLOW/Tests/test_Richards.py @@ -17,6 +17,14 @@ class TestModels(unittest.TestCase): passed = checkDerivative(wrapper, np.random.randn(50), plotIt=False) self.assertTrue(passed,True) + def test_vangenuchten_theta(self): + mesh = Mesh.TensorMesh([50]) + hav = Richards.Empirical._vangenuchten_theta(mesh) + m = np.random.randn(50) + def wrapper(u): + return hav.transform(u, m), hav.transformDerivU(u, m) + passed = checkDerivative(wrapper, np.random.randn(50), plotIt=False) + self.assertTrue(passed,True) def test_BaseHaverkamp_k(self): mesh = Mesh.TensorMesh([50]) @@ -34,6 +42,22 @@ class TestModels(unittest.TestCase): passed = checkDerivative(wrapper, np.random.randn(50), plotIt=False) self.assertTrue(passed,True) + def test_vangenuchten_k(self): + mesh = Mesh.TensorMesh([50]) + hav = Richards.Empirical._vangenuchten_k(mesh) + m = np.random.randn(50) + def wrapper(u): + return hav.transform(u, m), hav.transformDerivU(u, m) + passed = checkDerivative(wrapper, np.random.randn(50), plotIt=False) + self.assertTrue(passed,True) + + hav = Richards.Empirical._vangenuchten_k(mesh) + u = np.random.randn(50) + def wrapper(m): + return hav.transform(u, m), hav.transformDerivM(u, m) + passed = checkDerivative(wrapper, np.random.randn(50), plotIt=False) + self.assertTrue(passed,True) + # def test_Haverkamp_hydraulicConductivity(self): # print 'Haverkamp_hydraulicConductivity' # hav = Richards.Haverkamp() From 4b569924f4306a8fed5548f4fd49dfafcef4e47b Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Mon, 14 Apr 2014 09:31:16 -0700 Subject: [PATCH 16/32] Empirical function updates. --- simpegFLOW/Richards/Empirical.py | 25 ++++++++++++++++--------- simpegFLOW/Richards/RichardsProblem.py | 5 ++++- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/simpegFLOW/Richards/Empirical.py b/simpegFLOW/Richards/Empirical.py index 8565c05c..0ca994e7 100644 --- a/simpegFLOW/Richards/Empirical.py +++ b/simpegFLOW/Richards/Empirical.py @@ -109,7 +109,10 @@ class _haverkamp_theta(Model.BaseNonLinearModel): self.setModel(m) f = (self.alpha*(self.theta_s - self.theta_r )/ (self.alpha + abs(u)**self.beta) + self.theta_r) - f[u >= 0] = self.theta_s + if Utils.isScalar(self.theta_s): + f[u >= 0] = self.theta_s + else: + f[u >= 0] = self.theta_s[u >= 0] return f def transformDerivM(self, u, m): @@ -143,10 +146,10 @@ class _haverkamp_k(Model.BaseNonLinearModel): def transform(self, u, m): self.setModel(m) f = np.exp(self.Ks)*self.A/(self.A+abs(u)**self.gamma) - if type(self.Ks) is np.ndarray and self.Ks.size > 1: - f[u >= 0] = np.exp(self.Ks[u >= 0]) - else: + if Utils.isScalar(self.Ks): f[u >= 0] = np.exp(self.Ks) + else: + f[u >= 0] = np.exp(self.Ks[u >= 0]) return f def transformDerivM(self, u, m): @@ -206,7 +209,11 @@ class _vangenuchten_theta(Model.BaseNonLinearModel): m = 1 - 1.0/self.n f = (( self.theta_s - self.theta_r )/ ((1+abs(self.alpha*u)**self.n)**m) + self.theta_r) - f[u > 0] = self.theta_s + if Utils.isScalar(self.theta_s): + f[u >= 0] = self.theta_s + else: + f[u >= 0] = self.theta_s[u >= 0] + return f def transformDerivM(self, u, m): @@ -246,10 +253,10 @@ class _vangenuchten_k(Model.BaseNonLinearModel): theta_e = 1.0/((1.0+abs(alpha*u)**n)**m) f = np.exp(Ks)*theta_e**I* ( ( 1.0 - ( 1.0 - theta_e**(1.0/m) )**m )**2 ) - if type(self.Ks) is np.ndarray and self.Ks.size > 1: - f[u >= 0] = np.exp(self.Ks[u >= 0]) - else: + if Utils.isScalar(self.Ks): f[u >= 0] = np.exp(self.Ks) + else: + f[u >= 0] = np.exp(self.Ks[u >= 0]) return f def transformDerivM(self, u, m): @@ -294,7 +301,7 @@ class VanGenuchten(RichardsModel): Utils.setKwargs(self, **kwargs) -class vanGenuchtenParams(object): +class VanGenuchtenParams(object): """ The RETC code for quantifying the hydraulic functions of unsaturated soils, Van Genuchten, M Th, Leij, F J, Yates, S R diff --git a/simpegFLOW/Richards/RichardsProblem.py b/simpegFLOW/Richards/RichardsProblem.py index 7ea788f6..81ca8b20 100644 --- a/simpegFLOW/Richards/RichardsProblem.py +++ b/simpegFLOW/Richards/RichardsProblem.py @@ -101,11 +101,14 @@ class RichardsProblem(Problem.BaseProblem): doNewton = Utils.dependentProperty('_doNewton', False, ['_rootFinder'], "Do a Newton iteration. If False, a Picard iteration will be completed.") + maxIterRootFinder = Utils.dependentProperty('_maxIterRootFinder', 30, ['_rootFinder'], + "Maximum iterations for rootFinder iteration.") + @property def rootFinder(self): """Root-finding Algorithm""" if getattr(self, '_rootFinder', None) is None: - self._rootFinder = Optimization.NewtonRoot(doLS=self.doNewton, Solver=self.Solver) + self._rootFinder = Optimization.NewtonRoot(doLS=self.doNewton, maxIter=self.maxIterRootFinder, Solver=self.Solver) return self._rootFinder def fields(self, m): From 8f108d488d2d4f130f275992f7b591e8bda41545 Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Tue, 15 Apr 2014 15:43:16 -0700 Subject: [PATCH 17/32] simper model-->mapping and docs updates. --- README.md | 2 +- .../richards_comparisonToCeiliaEtAl1990.py | 4 +- docs/index.rst | 8 +--- simpegFLOW/Richards/Empirical.py | 39 ++++++++++--------- simpegFLOW/Richards/RichardsProblem.py | 30 +++++++------- simpegFLOW/Tests/test_Richards.py | 8 ++-- 6 files changed, 44 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 947fadb6..f70bf828 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Tests: [https://travis-ci.org/simpeg/simpegflow](https://travis-ci.org/simpeg/simpegflow) Build Status: -[![Build Status](https://travis-ci.org/simpeg/simpegflow.png)](https://travis-ci.org/simpeg/simpegflow) +[![Build Status](https://travis-ci.org/simpeg/simpegflow.svg?branch=master)](https://travis-ci.org/simpeg/simpegflow) Bugs & Issues: [https://github.com/simpeg/simpegflow/issues](https://github.com/simpeg/simpegflow/issues) diff --git a/docs/examples/richards_comparisonToCeiliaEtAl1990.py b/docs/examples/richards_comparisonToCeiliaEtAl1990.py index 385263f8..949031bd 100644 --- a/docs/examples/richards_comparisonToCeiliaEtAl1990.py +++ b/docs/examples/richards_comparisonToCeiliaEtAl1990.py @@ -9,14 +9,14 @@ import matplotlib.pyplot as plt M = Mesh.TensorMesh([np.ones(40)]) M.setCellGradBC('dirichlet') params = Richards.Empirical.HaverkampParams().celia1990 -model = Richards.Empirical.Haverkamp(M, **params) +E = Richards.Empirical.Haverkamp(M, **params) bc = np.array([-61.5,-20.7]) h = np.zeros(M.nC) + bc[0] def getFields(timeStep,method): - prob = Richards.RichardsProblem(model, timeStep=timeStep, timeEnd=360, + prob = Richards.RichardsProblem(M, mapping=E, timeStep=timeStep, timeEnd=360, boundaryConditions=bc, initialConditions=h, doNewton=False, method=method) return prob.fields(params['Ks']) diff --git a/docs/index.rst b/docs/index.rst index b2966f24..2fa5b251 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -24,17 +24,11 @@ Testing SimPEG ============== * Master Branch - .. image:: https://travis-ci.org/simpeg/simpegflow.png?branch=master + .. image:: https://travis-ci.org/simpeg/simpegflow.svg?branch=master :target: https://travis-ci.org/simpeg/simpegflow :alt: Master Branch :align: center -* Develop Branch - .. image:: https://travis-ci.org/simpeg/simpegflow.png?branch=develop - :target: https://travis-ci.org/simpeg/simpegflow - :alt: Develop Branch - :align: center - Project Index & Search ====================== diff --git a/simpegFLOW/Richards/Empirical.py b/simpegFLOW/Richards/Empirical.py index 0ca994e7..8b28f455 100644 --- a/simpegFLOW/Richards/Empirical.py +++ b/simpegFLOW/Richards/Empirical.py @@ -1,8 +1,8 @@ -from SimPEG import Mesh, Model, Utils, np +from SimPEG import Mesh, Maps, Utils, np -class RichardsModel(object): - """docstring for RichardsModel""" +class RichardsMap(object): + """docstring for RichardsMap""" mesh = None #: SimPEG mesh @@ -18,8 +18,8 @@ class RichardsModel(object): def __init__(self, mesh, thetaModel, kModel): self.mesh = mesh - assert isinstance(thetaModel, Model.BaseNonLinearModel) - assert isinstance(kModel, Model.BaseNonLinearModel) + assert isinstance(thetaModel, Maps.NonLinearMap) + assert isinstance(kModel, Maps.NonLinearMap) self._thetaModel = thetaModel self._kModel = kModel @@ -52,6 +52,9 @@ class RichardsModel(object): ax = plt.subplot(122) ax.semilogx(self.k(h, m), h) + def _assertMatchesPair(self, pair): + assert isinstance(self, pair), "Mapping object must be an instance of a %s class."%(pair.__name__) + def _ModelProperty(name, models, doc=None, default=None): @@ -91,7 +94,7 @@ class HaverkampParams(object): 'gamma':4.74} -class _haverkamp_theta(Model.BaseNonLinearModel): +class _haverkamp_theta(Maps.NonLinearMap): theta_s = 0.430 theta_r = 0.078 @@ -99,7 +102,7 @@ class _haverkamp_theta(Model.BaseNonLinearModel): beta = 3.960 def __init__(self, mesh, **kwargs): - Model.BaseNonLinearModel.__init__(self, mesh) + Maps.NonLinearMap.__init__(self, mesh) Utils.setKwargs(self, **kwargs) def setModel(self, m): @@ -128,14 +131,14 @@ class _haverkamp_theta(Model.BaseNonLinearModel): return g -class _haverkamp_k(Model.BaseNonLinearModel): +class _haverkamp_k(Maps.NonLinearMap): A = 1.175e+06 gamma = 4.74 Ks = np.log(24.96) def __init__(self, mesh, **kwargs): - Model.BaseNonLinearModel.__init__(self, mesh) + Maps.NonLinearMap.__init__(self, mesh) Utils.setKwargs(self, **kwargs) def setModel(self, m): @@ -169,7 +172,7 @@ class _haverkamp_k(Model.BaseNonLinearModel): g = Utils.sdiag(g) return g -class Haverkamp(RichardsModel): +class Haverkamp(RichardsMap): """Haverkamp Model""" alpha = _ModelProperty('alpha', ['thetaModel'], default=1.6110e+06) @@ -182,7 +185,7 @@ class Haverkamp(RichardsModel): gamma = _ModelProperty('gamma', ['kModel'], default=4.74) def __init__(self, mesh, **kwargs): - RichardsModel.__init__(self, mesh, + RichardsMap.__init__(self, mesh, _haverkamp_theta(mesh), _haverkamp_k(mesh)) Utils.setKwargs(self, **kwargs) @@ -190,7 +193,7 @@ class Haverkamp(RichardsModel): -class _vangenuchten_theta(Model.BaseNonLinearModel): +class _vangenuchten_theta(Maps.NonLinearMap): theta_s = 0.430 theta_r = 0.078 @@ -198,7 +201,7 @@ class _vangenuchten_theta(Model.BaseNonLinearModel): n = 1.560 def __init__(self, mesh, **kwargs): - Model.BaseNonLinearModel.__init__(self, mesh) + Maps.NonLinearMap.__init__(self, mesh) Utils.setKwargs(self, **kwargs) def setModel(self, m): @@ -226,7 +229,7 @@ class _vangenuchten_theta(Model.BaseNonLinearModel): return g -class _vangenuchten_k(Model.BaseNonLinearModel): +class _vangenuchten_k(Maps.NonLinearMap): I = 0.500 alpha = 0.036 @@ -234,7 +237,7 @@ class _vangenuchten_k(Model.BaseNonLinearModel): Ks = np.log(24.96) def __init__(self, mesh, **kwargs): - Model.BaseNonLinearModel.__init__(self, mesh) + Maps.NonLinearMap.__init__(self, mesh) Utils.setKwargs(self, **kwargs) def setModel(self, m): @@ -282,7 +285,7 @@ class _vangenuchten_k(Model.BaseNonLinearModel): g = Utils.sdiag(g) return g -class VanGenuchten(RichardsModel): +class VanGenuchten(RichardsMap): """vanGenuchten Model""" theta_r = _ModelProperty('theta_r', ['thetaModel'], default=0.075) @@ -295,7 +298,7 @@ class VanGenuchten(RichardsModel): I = _ModelProperty('I', ['kModel'], default=0.500) def __init__(self, mesh, **kwargs): - RichardsModel.__init__(self, mesh, + RichardsMap.__init__(self, mesh, _haverkamp_theta(mesh), _haverkamp_k(mesh)) Utils.setKwargs(self, **kwargs) @@ -497,7 +500,7 @@ class VanGenuchtenParams(object): if __name__ == '__main__': import matplotlib.pyplot as plt M = Mesh.TensorMesh([10]) - VGparams = vanGenuchtenParams() + VGparams = VanGenuchtenParams() leg = [] for p in dir(VGparams): if p[0] == '_': continue diff --git a/simpegFLOW/Richards/RichardsProblem.py b/simpegFLOW/Richards/RichardsProblem.py index 81ca8b20..58a6f0fb 100644 --- a/simpegFLOW/Richards/RichardsProblem.py +++ b/simpegFLOW/Richards/RichardsProblem.py @@ -1,5 +1,5 @@ from SimPEG import * -from Empirical import RichardsModel +from Empirical import RichardsMap class RichardsSurvey(Survey.BaseSurvey): """docstring for RichardsSurvey""" @@ -55,7 +55,7 @@ class RichardsSurvey(Survey.BaseSurvey): #TODO: if m is a parameter in the theta # distribution, we may need to do # some more chain rule here. - dT = self.model.thetaDerivU(u, m) + dT = self.mapping.thetaDerivU(u, m) return self.P*dT @@ -67,13 +67,13 @@ class RichardsProblem(Problem.BaseProblem): initialConditions = None surveyPair = RichardsSurvey - modelPair = RichardsModel + mapPair = RichardsMap Solver = Solver solverOpts = {} - def __init__(self, model, **kwargs): - Problem.BaseProblem.__init__(self, model, **kwargs) + def __init__(self, mesh, mapping=None, **kwargs): + Problem.BaseProblem.__init__(self, mesh, mapping=mapping, **kwargs) @property def timeStep(self): @@ -134,11 +134,11 @@ class RichardsProblem(Problem.BaseProblem): bc = self.boundaryConditions dt = self.timeStep - dT = self.model.thetaDerivU(hn, m) - dT1 = self.model.thetaDerivU(hn1, m) - K1 = self.model.k(hn1, m) - dK1 = self.model.kDerivU(hn1, m) - dKm1 = self.model.kDerivM(hn1, m) + dT = self.mapping.thetaDerivU(hn, m) + dT1 = self.mapping.thetaDerivU(hn1, m) + K1 = self.mapping.k(hn1, m) + dK1 = self.mapping.kDerivU(hn1, m) + dKm1 = self.mapping.kDerivM(hn1, m) # Compute part of the derivative of: # @@ -188,11 +188,11 @@ class RichardsProblem(Problem.BaseProblem): bc = self.boundaryConditions dt = self.timeStep - T = self.model.theta(h, m) - dT = self.model.thetaDerivU(h, m) - Tn = self.model.theta(hn, m) - K = self.model.k(h, m) - dK = self.model.kDerivU(h, m) + T = self.mapping.theta(h, m) + dT = self.mapping.thetaDerivU(h, m) + Tn = self.mapping.theta(hn, m) + K = self.mapping.k(h, m) + dK = self.mapping.kDerivU(h, m) aveK = 1./(AV*(1./K)) diff --git a/simpegFLOW/Tests/test_Richards.py b/simpegFLOW/Tests/test_Richards.py index 399e3e94..10dcf5e7 100644 --- a/simpegFLOW/Tests/test_Richards.py +++ b/simpegFLOW/Tests/test_Richards.py @@ -133,12 +133,12 @@ class RichardsTests1D(unittest.TestCase): M.setCellGradBC('dirichlet') params = Richards.Empirical.HaverkampParams().celia1990 - model = Richards.Empirical.Haverkamp(M, **params) + E = Richards.Empirical.Haverkamp(M, **params) bc = np.array([-61.5,-20.7]) h = np.zeros(M.nC) + bc[0] - prob = Richards.RichardsProblem(model, timeStep=60, timeEnd=180, + prob = Richards.RichardsProblem(M, mapping=E, timeStep=60, timeEnd=180, boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') @@ -199,7 +199,7 @@ class RichardsTests1D(unittest.TestCase): stdev = 0.01 # The standard deviation for the noise survey = self.prob.createSyntheticSurvey(mTrue, std=stdev, P=self.survey.P) opt = Optimization.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) - reg = Regularization.Tikhonov(Model.BaseModel(self.M)) + reg = Regularization.Tikhonov(self.M) obj = ObjFunction.BaseObjFunction(survey, reg) derChk = lambda m: [obj.dataObj(m), obj.dataObjDeriv(m)] print 'Testing Richards Derivative - Pressure Head' @@ -213,7 +213,7 @@ class RichardsTests1D(unittest.TestCase): stdev = 0.01 # The standard deviation for the noise survey = self.prob.createSyntheticSurvey(mTrue, std=stdev, P=self.survey.P) opt = Optimization.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) - reg = Regularization.Tikhonov(Model.BaseModel(self.M)) + reg = Regularization.Tikhonov(self.M) obj = ObjFunction.BaseObjFunction(survey, reg) derChk = lambda m: [obj.dataObj(m), obj.dataObjDeriv(m)] print 'Testing Richards Derivative - Saturation' From 2831b66ea9a8b4f5b7f7b5f1e451e0efd04187c9 Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Sun, 20 Apr 2014 11:33:59 -0700 Subject: [PATCH 18/32] Variable Time Stepping --- simpegFLOW/Richards/RichardsProblem.py | 36 +++++++------------------- simpegFLOW/Tests/test_Richards.py | 8 +++--- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/simpegFLOW/Richards/RichardsProblem.py b/simpegFLOW/Richards/RichardsProblem.py index 58a6f0fb..43485aeb 100644 --- a/simpegFLOW/Richards/RichardsProblem.py +++ b/simpegFLOW/Richards/RichardsProblem.py @@ -59,10 +59,9 @@ class RichardsSurvey(Survey.BaseSurvey): return self.P*dT -class RichardsProblem(Problem.BaseProblem): +class RichardsProblem(Problem.BaseTimeProblem): """docstring for RichardsProblem""" - timeEnd = None boundaryConditions = None initialConditions = None @@ -73,20 +72,7 @@ class RichardsProblem(Problem.BaseProblem): solverOpts = {} def __init__(self, mesh, mapping=None, **kwargs): - Problem.BaseProblem.__init__(self, mesh, mapping=mapping, **kwargs) - - @property - def timeStep(self): - """The time between steps.""" - return getattr(self, '_timeStep', None) - @timeStep.setter - def timeStep(self, value): - self._timeStep = float(value) # Because integers suck. - - @property - def numIts(self): - """The number of iterations in the time domain problem.""" - return int(self.timeEnd/self.timeStep) + Problem.BaseTimeProblem.__init__(self, mesh, mapping=mapping, **kwargs) @property def method(self): @@ -112,13 +98,13 @@ class RichardsProblem(Problem.BaseProblem): return self._rootFinder def fields(self, m): - u = range(self.numIts+1) + u = range(self.nT+1) u[0] = self.initialConditions - for ii in range(self.numIts): - u[ii+1] = self.rootFinder.root(lambda hn1m, return_g=True: self.getResidual(m, u[ii], hn1m, return_g=return_g), u[ii]) + for ii, dt in enumerate(self.timeSteps): + u[ii+1] = self.rootFinder.root(lambda hn1m, return_g=True: self.getResidual(m, u[ii], hn1m, dt, return_g=return_g), u[ii]) return u - def diagsJacobian(self, m, hn, hn1): + def diagsJacobian(self, m, hn, hn1, dt): DIV = self.mesh.faceDiv GRAD = self.mesh.cellGrad @@ -132,7 +118,6 @@ class RichardsProblem(Problem.BaseProblem): Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.vnF[0]+self.mesh.vnF[1]), self.mesh.faceDivz),format='csr') bc = self.boundaryConditions - dt = self.timeStep dT = self.mapping.thetaDerivU(hn, m) dT1 = self.mapping.thetaDerivU(hn1, m) @@ -170,7 +155,7 @@ class RichardsProblem(Problem.BaseProblem): return Asub, Adiag, B - def getResidual(self, m, hn, h, return_g=True): + def getResidual(self, m, hn, h, dt, return_g=True): """ Where h is the proposed value for the next time iterate (h_{n+1}) """ @@ -186,7 +171,6 @@ class RichardsProblem(Problem.BaseProblem): Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.vnF[0]+self.mesh.vnF[1]), self.mesh.faceDivz),format='csr') bc = self.boundaryConditions - dt = self.timeStep T = self.mapping.theta(h, m) dT = self.mapping.thetaDerivU(h, m) @@ -238,7 +222,7 @@ class RichardsProblem(Problem.BaseProblem): JvC = range(len(u)-1) # Cell to hold each row of the long vector. # This is done via forward substitution. - temp, Adiag, B = self.diagsJacobian(m, u[0],u[1]) + temp, Adiag, B = self.diagsJacobian(m, u[0], u[1], self.timeSteps[0]) Adiaginv = self.Solver(Adiag, **self.solverOpts) JvC[0] = Adiaginv.solve(B*v) @@ -246,7 +230,7 @@ class RichardsProblem(Problem.BaseProblem): # JvC{1} = bicgstab(Adiag,(B*v),tolbcg,500,M); for ii in range(1,len(u)-1): - Asub, Adiag, B = self.diagsJacobian(m, u[ii],u[ii+1]) + Asub, Adiag, B = self.diagsJacobian(m, u[ii], u[ii+1], self.timeSteps[ii]) Adiaginv = self.Solver(Adiag, **self.solverOpts) JvC[ii] = Adiaginv.solve(B*v - Asub*JvC[ii-1]) @@ -264,7 +248,7 @@ class RichardsProblem(Problem.BaseProblem): minus = 0 BJtv = 0 for ii in range(len(u)-1,0,-1): - Asub, Adiag, B = self.diagsJacobian(m, u[ii-1], u[ii]) + Asub, Adiag, B = self.diagsJacobian(m, u[ii-1], u[ii], self.timeSteps[ii-1]) #select the correct part of v vpart = range((ii-1)*Adiag.shape[0], (ii)*Adiag.shape[0]) AdiaginvT = self.Solver(Adiag.T, **self.solverOpts) diff --git a/simpegFLOW/Tests/test_Richards.py b/simpegFLOW/Tests/test_Richards.py index 10dcf5e7..5387a18e 100644 --- a/simpegFLOW/Tests/test_Richards.py +++ b/simpegFLOW/Tests/test_Richards.py @@ -138,12 +138,12 @@ class RichardsTests1D(unittest.TestCase): bc = np.array([-61.5,-20.7]) h = np.zeros(M.nC) + bc[0] - prob = Richards.RichardsProblem(M, mapping=E, timeStep=60, timeEnd=180, + prob = Richards.RichardsProblem(M, mapping=E, timeSteps=[(40,3),(60,3)], boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') q = sp.csr_matrix((np.ones(3),(np.arange(3),np.array([5,10,15]))),shape=(3,M.nC)) - P = sp.kron(sp.identity(prob.numIts),q) + P = sp.kron(sp.identity(prob.nT),q) survey = Richards.RichardsSurvey(P=P) prob.pair(survey) @@ -157,13 +157,13 @@ class RichardsTests1D(unittest.TestCase): def test_Richards_getResidual_Newton(self): self.prob.doNewton = True m = self.Ks - passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0,hn1), self.h0, plotIt=False) + passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0,hn1, self.prob.timeSteps[0]), self.h0, plotIt=False) self.assertTrue(passed,True) def test_Richards_getResidual_Picard(self): self.prob.doNewton = False m = self.Ks - passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0,hn1), self.h0, plotIt=False, expectedOrder=1) + passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0,hn1, self.prob.timeSteps[0]), self.h0, plotIt=False, expectedOrder=1) self.assertTrue(passed,True) def test_Adjoint_PressureHead(self): From 96e129aae240cf8939ce129fe77519c351636e21 Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Sun, 20 Apr 2014 13:12:14 -0700 Subject: [PATCH 19/32] Time projections --- simpegFLOW/Richards/RichardsProblem.py | 83 ++++++++++------ simpegFLOW/Tests/test_Richards.py | 128 +++---------------------- 2 files changed, 67 insertions(+), 144 deletions(-) diff --git a/simpegFLOW/Richards/RichardsProblem.py b/simpegFLOW/Richards/RichardsProblem.py index 43485aeb..30715c55 100644 --- a/simpegFLOW/Richards/RichardsProblem.py +++ b/simpegFLOW/Richards/RichardsProblem.py @@ -1,22 +1,44 @@ from SimPEG import * from Empirical import RichardsMap + +class RichardsRx(Survey.BaseTimeRx): + """Richards Receiver Object""" + + knownRxTypes = ['saturation','pressureHead'] + + def projectFields(self, u, m, mapping, mesh, timeMesh): + + if self.rxType == 'saturation': + u = mapping.theta(u, m) + + return self.getP(mesh, timeMesh) * u + + def projectFieldsDeriv(self, u, m, mapping, mesh, timeMesh): + + P = self.getP(mesh, timeMesh) + if self.rxType == 'pressureHead': + return P + elif self.rxType == 'saturation': + #TODO: if m is a parameter in the theta + # distribution, we may need to do + # some more chain rule here. + dT = mapping.thetaDerivU(u, m) + return P*dT + + class RichardsSurvey(Survey.BaseSurvey): """docstring for RichardsSurvey""" - P = None + rxList = None - def __init__(self, **kwargs): + def __init__(self, rxList, **kwargs): + self.rxList = rxList Survey.BaseSurvey.__init__(self, **kwargs) @property - def dataType(self): - """Choose how your data is collected, must be 'saturation' or 'pressureHead'.""" - return getattr(self, '_dataType', 'pressureHead') - @dataType.setter - def dataType(self, value): - assert value in ['saturation','pressureHead'], "dataType must be 'saturation' or 'pressureHead'." - self._dataType = value + def nD(self): + return np.array([rx.nD for rx in self.rxList]).sum() @Utils.count @Utils.requires('prob') @@ -27,7 +49,7 @@ class RichardsSurvey(Survey.BaseSurvey): instead of recalculating the fields (which may be expensive!). .. math:: - d_\\text{pred} = P(u(m)) + d_\\text{pred} = P(u(m), m) Where P is a projection of the fields onto the data space. """ @@ -37,27 +59,31 @@ class RichardsSurvey(Survey.BaseSurvey): @Utils.requires('prob') def projectFields(self, U, m): - u = np.concatenate(U[1:]) + u = np.concatenate(U) - if self.dataType == 'saturation': - u = self.prob.model.theta(u, m) - return self.P*u + Ds = range(len(self.rxList)) + for ii, rx in enumerate(self.rxList): + Ds[ii] = rx.projectFields(u, m, + self.prob.mapping, + self.prob.mesh, + self.prob.timeMesh) + + return np.concatenate(Ds) @Utils.requires('prob') def projectFieldsDeriv(self, U, m): """The Derivative with respect to the fields.""" - u = np.concatenate(U[1:]) + u = np.concatenate(U) - if self.dataType == 'pressureHead': - return self.P - elif self.dataType == 'saturation': - #TODO: if m is a parameter in the theta - # distribution, we may need to do - # some more chain rule here. - dT = self.mapping.thetaDerivU(u, m) - return self.P*dT + Ds = range(len(self.rxList)) + for ii, rx in enumerate(self.rxList): + Ds[ii] = rx.projectFieldsDeriv(u, m, + self.prob.mapping, + self.prob.mesh, + self.prob.timeMesh) + return sp.vstack(Ds) class RichardsProblem(Problem.BaseTimeProblem): """docstring for RichardsProblem""" @@ -197,7 +223,7 @@ class RichardsProblem(Problem.BaseTimeProblem): def Jfull(self, m, u=None): if u is None: - u = self.field(m) + u = self.fields(m) nn = len(u)-1 Asubs, Adiags, Bs = range(nn), range(nn), range(nn) @@ -217,7 +243,7 @@ class RichardsProblem(Problem.BaseTimeProblem): def Jvec(self, m, v, u=None): if u is None: - u = self.field(m) + u = self.fields(m) JvC = range(len(u)-1) # Cell to hold each row of the long vector. @@ -226,16 +252,13 @@ class RichardsProblem(Problem.BaseTimeProblem): Adiaginv = self.Solver(Adiag, **self.solverOpts) JvC[0] = Adiaginv.solve(B*v) - # M = @(x) tril(Adiag)\(diag(Adiag).*(triu(Adiag)\x)); - # JvC{1} = bicgstab(Adiag,(B*v),tolbcg,500,M); - for ii in range(1,len(u)-1): Asub, Adiag, B = self.diagsJacobian(m, u[ii], u[ii+1], self.timeSteps[ii]) Adiaginv = self.Solver(Adiag, **self.solverOpts) JvC[ii] = Adiaginv.solve(B*v - Asub*JvC[ii-1]) P = self.survey.projectFieldsDeriv(u, m) - return P * np.concatenate(JvC) + return P * np.concatenate([np.zeros(self.mesh.nC)] + JvC) def Jtvec(self, m, v, u=None): if u is None: @@ -250,7 +273,7 @@ class RichardsProblem(Problem.BaseTimeProblem): for ii in range(len(u)-1,0,-1): Asub, Adiag, B = self.diagsJacobian(m, u[ii-1], u[ii], self.timeSteps[ii-1]) #select the correct part of v - vpart = range((ii-1)*Adiag.shape[0], (ii)*Adiag.shape[0]) + vpart = range((ii)*Adiag.shape[0], (ii+1)*Adiag.shape[0]) AdiaginvT = self.Solver(Adiag.T, **self.solverOpts) JTvC = AdiaginvT.solve(PTv[vpart] - minus) minus = Asub.T*JTvC # this is now the super diagonal. diff --git a/simpegFLOW/Tests/test_Richards.py b/simpegFLOW/Tests/test_Richards.py index 5387a18e..bcda57bf 100644 --- a/simpegFLOW/Tests/test_Richards.py +++ b/simpegFLOW/Tests/test_Richards.py @@ -58,72 +58,6 @@ class TestModels(unittest.TestCase): passed = checkDerivative(wrapper, np.random.randn(50), plotIt=False) self.assertTrue(passed,True) - # def test_Haverkamp_hydraulicConductivity(self): - # print 'Haverkamp_hydraulicConductivity' - # hav = Richards.Haverkamp() - # def wrapper(x): - # return hav.hydraulicConductivity(x), hav.hydraulicConductivityDeriv(x) - # passed = checkDerivative(wrapper, np.random.randn(50), plotIt=False) - # self.assertTrue(passed,True) - - # def test_Haverkamp_hydraulicConductivity_FullKs(self): - # print 'Haverkamp_hydraulicConductivity_FullKs' - # n = 50 - # hav = Richards.Haverkamp(Ks=np.random.rand(n)) - # def wrapper(x): - # return hav.hydraulicConductivity(x), hav.hydraulicConductivityDeriv(x) - # passed = checkDerivative(wrapper, np.random.randn(n), plotIt=False) - # self.assertTrue(passed,True) - - # def test_VanGenuchten_moistureContent(self): - # print 'VanGenuchten_moistureContent' - # vanG = Richards.VanGenuchten() - # def wrapper(x): - # return vanG.moistureContent(x), vanG.moistureContentDeriv(x) - # passed = checkDerivative(wrapper, np.random.randn(50), plotIt=False) - # self.assertTrue(passed,True) - - # def test_VanGenuchten_hydraulicConductivity(self): - # print 'VanGenuchten_hydraulicConductivity' - # hav = Richards.VanGenuchten() - # def wrapper(x): - # return hav.hydraulicConductivity(x), hav.hydraulicConductivityDeriv(x) - # passed = checkDerivative(wrapper, np.random.randn(50), plotIt=False) - # self.assertTrue(passed,True) - - # def test_VanGenuchten_hydraulicConductivity_FullKs(self): - # print 'VanGenuchten_hydraulicConductivity_FullKs' - # n = 50 - # hav = Richards.VanGenuchten(Ks=np.random.rand(n)) - # def wrapper(x): - # return hav.hydraulicConductivity(x), hav.hydraulicConductivityDeriv(x) - # passed = checkDerivative(wrapper, np.random.randn(n), plotIt=False) - # self.assertTrue(passed,True) - - # def test_Haverkamp_moistureContent(self): - # print 'Haverkamp_moistureContent' - # hav = Richards.Haverkamp() - # def wrapper(x): - # return hav.moistureContent(x), hav.moistureContentDeriv(x) - # passed = checkDerivative(wrapper, np.random.randn(50), plotIt=False) - # self.assertTrue(passed,True) - - # def test_Haverkamp_hydraulicConductivity(self): - # print 'Haverkamp_hydraulicConductivity' - # hav = Richards.Haverkamp() - # def wrapper(x): - # return hav.hydraulicConductivity(x), hav.hydraulicConductivityDeriv(x) - # passed = checkDerivative(wrapper, np.random.randn(50), plotIt=False) - # self.assertTrue(passed,True) - - # def test_Haverkamp_hydraulicConductivity_FullKs(self): - # print 'Haverkamp_hydraulicConductivity_FullKs' - # n = 50 - # hav = Richards.Haverkamp(Ks=np.random.rand(n)) - # def wrapper(x): - # return hav.hydraulicConductivity(x), hav.hydraulicConductivityDeriv(x) - # passed = checkDerivative(wrapper, np.random.randn(n), plotIt=False) - # self.assertTrue(passed,True) class RichardsTests1D(unittest.TestCase): @@ -142,9 +76,11 @@ class RichardsTests1D(unittest.TestCase): boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') - q = sp.csr_matrix((np.ones(3),(np.arange(3),np.array([5,10,15]))),shape=(3,M.nC)) - P = sp.kron(sp.identity(prob.nT),q) - survey = Richards.RichardsSurvey(P=P) + locs = np.r_[5.,10,15] + times = prob.times[3:5] + rxSat = Richards.RichardsRx(locs, times, 'saturation') + rxPre = Richards.RichardsRx(locs, times, 'pressureHead') + survey = Richards.RichardsSurvey([rxSat, rxPre]) prob.pair(survey) @@ -157,18 +93,17 @@ class RichardsTests1D(unittest.TestCase): def test_Richards_getResidual_Newton(self): self.prob.doNewton = True m = self.Ks - passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0,hn1, self.prob.timeSteps[0]), self.h0, plotIt=False) + passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0, hn1, self.prob.timeSteps[0]), self.h0, plotIt=False) self.assertTrue(passed,True) def test_Richards_getResidual_Picard(self): self.prob.doNewton = False m = self.Ks - passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0,hn1, self.prob.timeSteps[0]), self.h0, plotIt=False, expectedOrder=1) + passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0, hn1, self.prob.timeSteps[0]), self.h0, plotIt=False, expectedOrder=1) self.assertTrue(passed,True) - def test_Adjoint_PressureHead(self): - self.prob.dataType = 'pressureHead' - v = np.random.rand(self.survey.P.shape[0]) + def test_Adjoint(self): + v = np.random.rand(self.survey.nD) z = np.random.rand(self.M.nC) Hs = self.prob.fields(self.Ks) vJz = v.dot(self.prob.Jvec(self.Ks,z,u=Hs)) @@ -179,48 +114,13 @@ class RichardsTests1D(unittest.TestCase): print '%4.4e === %4.4e, diff=%4.4e < %4.e'%(vJz, zJv,np.abs(vJz - zJv),tol) self.assertTrue(passed,True) - def test_Adjoint_Saturation(self): - self.prob.dataType = 'saturation' - v = np.random.rand(self.survey.P.shape[0]) - z = np.random.rand(self.M.nC) - Hs = self.prob.fields(self.Ks) - vJz = v.dot(self.prob.Jvec(self.Ks,z,u=Hs)) - zJv = z.dot(self.prob.Jtvec(self.Ks,v,u=Hs)) - tol = TOL*(10**int(np.log10(zJv))) - passed = np.abs(vJz - zJv) < tol - print 'Richards Adjoint Test - Saturation' - print '%4.4e === %4.4e, diff=%4.4e < %4.e'%(vJz, zJv,np.abs(vJz - zJv),tol) + def test_Sensitivity(self): + mTrue = self.Ks*np.ones(self.M.nC) + derChk = lambda m: [self.survey.dpred(m), lambda v: self.prob.Jvec(m, v)] + print 'Testing Richards Derivative' + passed = checkDerivative(derChk, mTrue, num=4, plotIt=False) self.assertTrue(passed,True) - def test_SensitivityPressureHead(self): - self.prob.dataType = 'pressureHead' - self.prob.unpair() - mTrue = np.ones(self.M.nC)*self.Ks - stdev = 0.01 # The standard deviation for the noise - survey = self.prob.createSyntheticSurvey(mTrue, std=stdev, P=self.survey.P) - opt = Optimization.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) - reg = Regularization.Tikhonov(self.M) - obj = ObjFunction.BaseObjFunction(survey, reg) - derChk = lambda m: [obj.dataObj(m), obj.dataObjDeriv(m)] - print 'Testing Richards Derivative - Pressure Head' - passed = checkDerivative(derChk, mTrue, num=5, plotIt=False) - self.assertTrue(passed,True) - - def test_SensitivitySaturation(self): - self.prob.unpair() - self.prob.dataType = 'saturation' - mTrue = np.ones(self.M.nC)*self.Ks - stdev = 0.01 # The standard deviation for the noise - survey = self.prob.createSyntheticSurvey(mTrue, std=stdev, P=self.survey.P) - opt = Optimization.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) - reg = Regularization.Tikhonov(self.M) - obj = ObjFunction.BaseObjFunction(survey, reg) - derChk = lambda m: [obj.dataObj(m), obj.dataObjDeriv(m)] - print 'Testing Richards Derivative - Saturation' - passed = checkDerivative(derChk, mTrue, num=5, plotIt=False) - self.assertTrue(passed,True) - - # class RichardsTests2D(object): From 26079333bbbbb96ad12e8dd1d6e600d9ee70e3f8 Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Sun, 18 May 2014 23:18:24 -0700 Subject: [PATCH 20/32] simpeg updates --- simpegFLOW/Richards/Empirical.py | 83 ++++++++++++++++++++++---- simpegFLOW/Richards/RichardsProblem.py | 65 +++++++++++--------- simpegFLOW/Tests/test_Richards.py | 13 +++- 3 files changed, 121 insertions(+), 40 deletions(-) diff --git a/simpegFLOW/Richards/Empirical.py b/simpegFLOW/Richards/Empirical.py index 8b28f455..c3e5066e 100644 --- a/simpegFLOW/Richards/Empirical.py +++ b/simpegFLOW/Richards/Empirical.py @@ -1,6 +1,69 @@ from SimPEG import Mesh, Maps, Utils, np +class NonLinearMap(object): + """ + SimPEG NonLinearMap + + """ + + __metaclass__ = Utils.SimPEGMetaClass + + counter = None #: A SimPEG.Utils.Counter object + mesh = None #: A SimPEG Mesh + + def __init__(self, mesh): + self.mesh = mesh + + def _transform(self, u, m): + """ + :param numpy.array u: fields + :param numpy.array m: model + :rtype: numpy.array + :return: transformed model + + The *transform* changes the model into the physical property. + + """ + return m + + def derivU(self, u, m): + """ + :param numpy.array u: fields + :param numpy.array m: model + :rtype: scipy.csr_matrix + :return: derivative of transformed model + + The *transform* changes the model into the physical property. + The *transformDerivU* provides the derivative of the *transform* with respect to the fields. + """ + raise NotImplementedError('The transformDerivU is not implemented.') + + + def derivM(self, u, m): + """ + :param numpy.array u: fields + :param numpy.array m: model + :rtype: scipy.csr_matrix + :return: derivative of transformed model + + The *transform* changes the model into the physical property. + The *transformDerivU* provides the derivative of the *transform* with respect to the model. + """ + raise NotImplementedError('The transformDerivM is not implemented.') + + @property + def nP(self): + """Number of parameters in the model.""" + return self.mesh.nC + + def example(self): + raise NotImplementedError('The example is not implemented.') + + def test(self, m=None): + raise NotImplementedError('The test is not implemented.') + + class RichardsMap(object): """docstring for RichardsMap""" @@ -18,8 +81,8 @@ class RichardsMap(object): def __init__(self, mesh, thetaModel, kModel): self.mesh = mesh - assert isinstance(thetaModel, Maps.NonLinearMap) - assert isinstance(kModel, Maps.NonLinearMap) + assert isinstance(thetaModel, NonLinearMap) + assert isinstance(kModel, NonLinearMap) self._thetaModel = thetaModel self._kModel = kModel @@ -94,7 +157,7 @@ class HaverkampParams(object): 'gamma':4.74} -class _haverkamp_theta(Maps.NonLinearMap): +class _haverkamp_theta(NonLinearMap): theta_s = 0.430 theta_r = 0.078 @@ -102,7 +165,7 @@ class _haverkamp_theta(Maps.NonLinearMap): beta = 3.960 def __init__(self, mesh, **kwargs): - Maps.NonLinearMap.__init__(self, mesh) + NonLinearMap.__init__(self, mesh) Utils.setKwargs(self, **kwargs) def setModel(self, m): @@ -131,14 +194,14 @@ class _haverkamp_theta(Maps.NonLinearMap): return g -class _haverkamp_k(Maps.NonLinearMap): +class _haverkamp_k(NonLinearMap): A = 1.175e+06 gamma = 4.74 Ks = np.log(24.96) def __init__(self, mesh, **kwargs): - Maps.NonLinearMap.__init__(self, mesh) + NonLinearMap.__init__(self, mesh) Utils.setKwargs(self, **kwargs) def setModel(self, m): @@ -193,7 +256,7 @@ class Haverkamp(RichardsMap): -class _vangenuchten_theta(Maps.NonLinearMap): +class _vangenuchten_theta(NonLinearMap): theta_s = 0.430 theta_r = 0.078 @@ -201,7 +264,7 @@ class _vangenuchten_theta(Maps.NonLinearMap): n = 1.560 def __init__(self, mesh, **kwargs): - Maps.NonLinearMap.__init__(self, mesh) + NonLinearMap.__init__(self, mesh) Utils.setKwargs(self, **kwargs) def setModel(self, m): @@ -229,7 +292,7 @@ class _vangenuchten_theta(Maps.NonLinearMap): return g -class _vangenuchten_k(Maps.NonLinearMap): +class _vangenuchten_k(NonLinearMap): I = 0.500 alpha = 0.036 @@ -237,7 +300,7 @@ class _vangenuchten_k(Maps.NonLinearMap): Ks = np.log(24.96) def __init__(self, mesh, **kwargs): - Maps.NonLinearMap.__init__(self, mesh) + NonLinearMap.__init__(self, mesh) Utils.setKwargs(self, **kwargs) def setModel(self, m): diff --git a/simpegFLOW/Richards/RichardsProblem.py b/simpegFLOW/Richards/RichardsProblem.py index 30715c55..9c2ecffb 100644 --- a/simpegFLOW/Richards/RichardsProblem.py +++ b/simpegFLOW/Richards/RichardsProblem.py @@ -7,14 +7,16 @@ class RichardsRx(Survey.BaseTimeRx): knownRxTypes = ['saturation','pressureHead'] - def projectFields(self, u, m, mapping, mesh, timeMesh): + def projectFields(self, U, m, mapping, mesh, timeMesh): - if self.rxType == 'saturation': - u = mapping.theta(u, m) + if self.rxType == 'pressureHead': + u = np.concatenate(U) + elif self.rxType == 'saturation': + u = np.concatenate([mapping.theta(ui, m) for ui in U]) return self.getP(mesh, timeMesh) * u - def projectFieldsDeriv(self, u, m, mapping, mesh, timeMesh): + def projectFieldsDeriv(self, U, m, mapping, mesh, timeMesh): P = self.getP(mesh, timeMesh) if self.rxType == 'pressureHead': @@ -23,7 +25,7 @@ class RichardsRx(Survey.BaseTimeRx): #TODO: if m is a parameter in the theta # distribution, we may need to do # some more chain rule here. - dT = mapping.thetaDerivU(u, m) + dT = sp.block_diag([mapping.thetaDerivU(ui, m) for ui in U]) return P*dT @@ -58,12 +60,9 @@ class RichardsSurvey(Survey.BaseSurvey): @Utils.requires('prob') def projectFields(self, U, m): - - u = np.concatenate(U) - Ds = range(len(self.rxList)) for ii, rx in enumerate(self.rxList): - Ds[ii] = rx.projectFields(u, m, + Ds[ii] = rx.projectFields(U, m, self.prob.mapping, self.prob.mesh, self.prob.timeMesh) @@ -73,12 +72,9 @@ class RichardsSurvey(Survey.BaseSurvey): @Utils.requires('prob') def projectFieldsDeriv(self, U, m): """The Derivative with respect to the fields.""" - - u = np.concatenate(U) - Ds = range(len(self.rxList)) for ii, rx in enumerate(self.rxList): - Ds[ii] = rx.projectFieldsDeriv(u, m, + Ds[ii] = rx.projectFieldsDeriv(U, m, self.prob.mapping, self.prob.mesh, self.prob.timeMesh) @@ -100,6 +96,14 @@ class RichardsProblem(Problem.BaseTimeProblem): def __init__(self, mesh, mapping=None, **kwargs): Problem.BaseTimeProblem.__init__(self, mesh, mapping=mapping, **kwargs) + def getBoundaryConditions(self, ii): + if type(self.boundaryConditions) is np.ndarray: + return self.boundaryConditions + + time = self.timeMesh.vectorCCx[ii] + + return self.boundaryConditions(time) + @property def method(self): """Method must be either 'mixed' or 'head'. See notes in Celia et al., 1990.""" @@ -127,10 +131,11 @@ class RichardsProblem(Problem.BaseTimeProblem): u = range(self.nT+1) u[0] = self.initialConditions for ii, dt in enumerate(self.timeSteps): - u[ii+1] = self.rootFinder.root(lambda hn1m, return_g=True: self.getResidual(m, u[ii], hn1m, dt, return_g=return_g), u[ii]) + bc = self.getBoundaryConditions(ii) + u[ii+1] = self.rootFinder.root(lambda hn1m, return_g=True: self.getResidual(m, u[ii], hn1m, dt, bc, return_g=return_g), u[ii]) return u - def diagsJacobian(self, m, hn, hn1, dt): + def diagsJacobian(self, m, hn, hn1, dt, bc): DIV = self.mesh.faceDiv GRAD = self.mesh.cellGrad @@ -143,8 +148,6 @@ class RichardsProblem(Problem.BaseTimeProblem): elif self.mesh.dim == 3: Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.vnF[0]+self.mesh.vnF[1]), self.mesh.faceDivz),format='csr') - bc = self.boundaryConditions - dT = self.mapping.thetaDerivU(hn, m) dT1 = self.mapping.thetaDerivU(hn1, m) K1 = self.mapping.k(hn1, m) @@ -181,7 +184,7 @@ class RichardsProblem(Problem.BaseTimeProblem): return Asub, Adiag, B - def getResidual(self, m, hn, h, dt, return_g=True): + def getResidual(self, m, hn, h, dt, bc, return_g=True): """ Where h is the proposed value for the next time iterate (h_{n+1}) """ @@ -196,8 +199,6 @@ class RichardsProblem(Problem.BaseTimeProblem): elif self.mesh.dim == 3: Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.vnF[0]+self.mesh.vnF[1]), self.mesh.faceDivz),format='csr') - bc = self.boundaryConditions - T = self.mapping.theta(h, m) dT = self.mapping.thetaDerivU(h, m) Tn = self.mapping.theta(hn, m) @@ -228,7 +229,9 @@ class RichardsProblem(Problem.BaseTimeProblem): nn = len(u)-1 Asubs, Adiags, Bs = range(nn), range(nn), range(nn) for ii in range(nn): - Asubs[ii], Adiags[ii], Bs[ii] = self.diagsJacobian(m, u[ii],u[ii+1]) + dt = self.timeSteps[ii] + bc = self.getBoundaryConditions(ii) + Asubs[ii], Adiags[ii], Bs[ii] = self.diagsJacobian(m, u[ii], u[ii+1], dt, bc) Ad = sp.block_diag(Adiags) zRight = Utils.spzeros((len(Asubs)-1)*Asubs[0].shape[0],Adiags[0].shape[1]) zTop = Utils.spzeros(Adiags[0].shape[0], len(Adiags)*Adiags[0].shape[1]) @@ -238,7 +241,10 @@ class RichardsProblem(Problem.BaseTimeProblem): Ainv = self.Solver(A, **self.solverOpts) P = self.survey.projectFieldsDeriv(u, m) - J = P * Ainv.solve(B) + AinvB = Ainv * B + z = np.zeros((self.mesh.nC, B.shape[1])) + zAinvB = np.vstack((z, AinvB)) + J = P * zAinvB return J def Jvec(self, m, v, u=None): @@ -248,14 +254,16 @@ class RichardsProblem(Problem.BaseTimeProblem): JvC = range(len(u)-1) # Cell to hold each row of the long vector. # This is done via forward substitution. - temp, Adiag, B = self.diagsJacobian(m, u[0], u[1], self.timeSteps[0]) + bc = self.getBoundaryConditions(0) + temp, Adiag, B = self.diagsJacobian(m, u[0], u[1], self.timeSteps[0], bc) Adiaginv = self.Solver(Adiag, **self.solverOpts) - JvC[0] = Adiaginv.solve(B*v) + JvC[0] = Adiaginv * (B*v) for ii in range(1,len(u)-1): - Asub, Adiag, B = self.diagsJacobian(m, u[ii], u[ii+1], self.timeSteps[ii]) + bc = self.getBoundaryConditions(ii) + Asub, Adiag, B = self.diagsJacobian(m, u[ii], u[ii+1], self.timeSteps[ii], bc) Adiaginv = self.Solver(Adiag, **self.solverOpts) - JvC[ii] = Adiaginv.solve(B*v - Asub*JvC[ii-1]) + JvC[ii] = Adiaginv * (B*v - Asub*JvC[ii-1]) P = self.survey.projectFieldsDeriv(u, m) return P * np.concatenate([np.zeros(self.mesh.nC)] + JvC) @@ -271,11 +279,12 @@ class RichardsProblem(Problem.BaseTimeProblem): minus = 0 BJtv = 0 for ii in range(len(u)-1,0,-1): - Asub, Adiag, B = self.diagsJacobian(m, u[ii-1], u[ii], self.timeSteps[ii-1]) + bc = self.getBoundaryConditions(ii-1) + Asub, Adiag, B = self.diagsJacobian(m, u[ii-1], u[ii], self.timeSteps[ii-1], bc) #select the correct part of v vpart = range((ii)*Adiag.shape[0], (ii+1)*Adiag.shape[0]) AdiaginvT = self.Solver(Adiag.T, **self.solverOpts) - JTvC = AdiaginvT.solve(PTv[vpart] - minus) + JTvC = AdiaginvT * (PTv[vpart] - minus) minus = Asub.T*JTvC # this is now the super diagonal. BJtv = BJtv + B.T*JTvC diff --git a/simpegFLOW/Tests/test_Richards.py b/simpegFLOW/Tests/test_Richards.py index bcda57bf..ab2cbc1a 100644 --- a/simpegFLOW/Tests/test_Richards.py +++ b/simpegFLOW/Tests/test_Richards.py @@ -93,13 +93,13 @@ class RichardsTests1D(unittest.TestCase): def test_Richards_getResidual_Newton(self): self.prob.doNewton = True m = self.Ks - passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0, hn1, self.prob.timeSteps[0]), self.h0, plotIt=False) + passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0, hn1, self.prob.timeSteps[0], self.prob.boundaryConditions), self.h0, plotIt=False) self.assertTrue(passed,True) def test_Richards_getResidual_Picard(self): self.prob.doNewton = False m = self.Ks - passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0, hn1, self.prob.timeSteps[0]), self.h0, plotIt=False, expectedOrder=1) + passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0, hn1, self.prob.timeSteps[0], self.prob.boundaryConditions), self.h0, plotIt=False, expectedOrder=1) self.assertTrue(passed,True) def test_Adjoint(self): @@ -122,6 +122,15 @@ class RichardsTests1D(unittest.TestCase): self.assertTrue(passed,True) + def test_Sensitivity_full(self): + mTrue = self.Ks*np.ones(self.M.nC) + J = self.prob.Jfull(mTrue) + derChk = lambda m: [self.survey.dpred(m), J] + print 'Testing Richards Derivative' + passed = checkDerivative(derChk, mTrue, num=4, plotIt=False) + self.assertTrue(passed,True) + + # class RichardsTests2D(object): # def setUp(self): From 75fb7a871994daf8226ab3a74a1e17ab74dd7fa2 Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Mon, 19 May 2014 11:30:32 -0700 Subject: [PATCH 21/32] Update travis! --- .travis.yml | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1272ce31..b2c71ae8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,24 +1,35 @@ language: python python: - - "2.7" -virtualenv: - system_site_packages: true + - 2.7 + +# Setup anaconda before_install: - - sudo apt-get install -qq gcc gfortran libblas-dev liblapack-dev python-numpy python-scipy python-matplotlib python-pip - - sudo pip install scipy --upgrade - - sudo pip install numpy --upgrade - - cd ../ + - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then wget http://repo.continuum.io/miniconda/Miniconda-3.3.0-Linux-x86_64.sh -O miniconda.sh; else wget http://repo.continuum.io/miniconda/Miniconda3-3.3.0-Linux-x86_64.sh -O miniconda.sh; fi + - chmod +x miniconda.sh + - ./miniconda.sh -b + - export PATH=/home/travis/anaconda/bin:/home/travis/miniconda/bin:$PATH + - conda update --yes conda + # The next couple lines fix a crash with multiprocessing on Travis and are not specific to using Miniconda + - sudo rm -rf /dev/shm + - sudo ln -s /run/shm /dev/shm + +# Install packages +install: + - conda install --yes pip python=$TRAVIS_PYTHON_VERSION numpy scipy matplotlib cython + - pip install nose-cov python-coveralls + # Remove this when SimPEG is on pip - git clone https://github.com/simpeg/simpeg.git - - cd simpeg/SimPEG/ - - python setup.py - - cd ../../ - - echo export PYTHONPATH=$PYTHONPATH:/home/travis/build/simpeg/simpeg >> .bashrc - - source .bashrc - - cd simpegflow -# command to install dependencies -install: "pip install -r requirements.txt --use-mirrors" -# command to run tests -script: nosetests -v + - cd simpeg/ + - python setup.py install + - cd ../ + +# Run test +script: + - nosetests --with-cov --cov simpegEM --cov-config .coveragerc + +# Calculate coverage +after_success: + - coveralls --config_file .coveragerc notifications: email: From 0718c2c50ade9167a363000d0dda09935788ab17 Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Mon, 19 May 2014 11:40:30 -0700 Subject: [PATCH 22/32] coverage updates --- .coveragerc | 9 +++++++++ .travis.yml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..efe71f36 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,9 @@ +[run] +source = simpegFLOW +omit = + */python?.?/* + */lib-python/?.?/*.py + */lib_pypy/_*.py + */site-packages/ordereddict.py + */site-packages/nose/* + */unittest2/* diff --git a/.travis.yml b/.travis.yml index b2c71ae8..bfe89717 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ install: # Run test script: - - nosetests --with-cov --cov simpegEM --cov-config .coveragerc + - nosetests --with-cov --cov simpegFLOW --cov-config .coveragerc # Calculate coverage after_success: From 23141c682feed5239e1c6bc3d144c58d05020dbd Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Thu, 3 Jul 2014 07:50:25 -0700 Subject: [PATCH 23/32] update example to work with new problem formulation in simpeg --- docs/examples/richards_comparisonToCeiliaEtAl1990.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/examples/richards_comparisonToCeiliaEtAl1990.py b/docs/examples/richards_comparisonToCeiliaEtAl1990.py index 949031bd..ae7f5d1d 100644 --- a/docs/examples/richards_comparisonToCeiliaEtAl1990.py +++ b/docs/examples/richards_comparisonToCeiliaEtAl1990.py @@ -16,7 +16,8 @@ h = np.zeros(M.nC) + bc[0] def getFields(timeStep,method): - prob = Richards.RichardsProblem(M, mapping=E, timeStep=timeStep, timeEnd=360, + timeSteps = np.ones(360/timeStep)*timeStep + prob = Richards.RichardsProblem(M, mapping=E, timeSteps=timeSteps, boundaryConditions=bc, initialConditions=h, doNewton=False, method=method) return prob.fields(params['Ks']) From f747d724c9942b99133865dd6e0df95395575c88 Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Thu, 3 Jul 2014 09:56:05 -0700 Subject: [PATCH 24/32] Make working in log conductivity a bit more explicit. Should be fixed in the actual implementation, and use SimPEG maps instead of forcing the user to work in weird units. --- docs/examples/richards_comparisonToCeiliaEtAl1990.py | 1 + simpegFLOW/Richards/Empirical.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/examples/richards_comparisonToCeiliaEtAl1990.py b/docs/examples/richards_comparisonToCeiliaEtAl1990.py index ae7f5d1d..95f58cd8 100644 --- a/docs/examples/richards_comparisonToCeiliaEtAl1990.py +++ b/docs/examples/richards_comparisonToCeiliaEtAl1990.py @@ -9,6 +9,7 @@ import matplotlib.pyplot as plt M = Mesh.TensorMesh([np.ones(40)]) M.setCellGradBC('dirichlet') params = Richards.Empirical.HaverkampParams().celia1990 +params['Ks'] = np.log(params['Ks']) E = Richards.Empirical.Haverkamp(M, **params) bc = np.array([-61.5,-20.7]) diff --git a/simpegFLOW/Richards/Empirical.py b/simpegFLOW/Richards/Empirical.py index c3e5066e..a28f58af 100644 --- a/simpegFLOW/Richards/Empirical.py +++ b/simpegFLOW/Richards/Empirical.py @@ -153,7 +153,7 @@ class HaverkampParams(object): """ return {'alpha':1.611e+06, 'beta':3.96, 'theta_r':0.075, 'theta_s':0.287, - 'Ks':np.log(9.44e-03), 'A':1.175e+06, + 'Ks':9.44e-03, 'A':1.175e+06, 'gamma':4.74} @@ -243,7 +243,7 @@ class Haverkamp(RichardsMap): theta_r = _ModelProperty('theta_r', ['thetaModel'], default=0.075) theta_s = _ModelProperty('theta_s', ['thetaModel'], default=0.287) - Ks = _ModelProperty('Ks', ['kModel'], default=np.log(24.96)) + Ks = _ModelProperty('Ks', ['kModel'], default=24.96) A = _ModelProperty('A', ['kModel'], default=1.1750e+06) gamma = _ModelProperty('gamma', ['kModel'], default=4.74) @@ -357,7 +357,7 @@ class VanGenuchten(RichardsMap): alpha = _ModelProperty('alpha', ['thetaModel', 'kModel'], default=0.036) n = _ModelProperty('n', ['thetaModel', 'kModel'], default=1.560) - Ks = _ModelProperty('Ks', ['kModel'], default=np.log(24.96)) + Ks = _ModelProperty('Ks', ['kModel'], default=24.96) I = _ModelProperty('I', ['kModel'], default=0.500) def __init__(self, mesh, **kwargs): From 076005a7e94fd74337a900dffc730898b902a27d Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Thu, 3 Jul 2014 10:03:42 -0700 Subject: [PATCH 25/32] fix tests for now. --- simpegFLOW/Richards/Empirical.py | 4 ++-- simpegFLOW/Tests/test_Richards.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/simpegFLOW/Richards/Empirical.py b/simpegFLOW/Richards/Empirical.py index a28f58af..0e45ed57 100644 --- a/simpegFLOW/Richards/Empirical.py +++ b/simpegFLOW/Richards/Empirical.py @@ -243,7 +243,7 @@ class Haverkamp(RichardsMap): theta_r = _ModelProperty('theta_r', ['thetaModel'], default=0.075) theta_s = _ModelProperty('theta_s', ['thetaModel'], default=0.287) - Ks = _ModelProperty('Ks', ['kModel'], default=24.96) + Ks = _ModelProperty('Ks', ['kModel'], default=np.log(24.96)) A = _ModelProperty('A', ['kModel'], default=1.1750e+06) gamma = _ModelProperty('gamma', ['kModel'], default=4.74) @@ -357,7 +357,7 @@ class VanGenuchten(RichardsMap): alpha = _ModelProperty('alpha', ['thetaModel', 'kModel'], default=0.036) n = _ModelProperty('n', ['thetaModel', 'kModel'], default=1.560) - Ks = _ModelProperty('Ks', ['kModel'], default=24.96) + Ks = _ModelProperty('Ks', ['kModel'], default=np.log(24.96)) I = _ModelProperty('I', ['kModel'], default=0.500) def __init__(self, mesh, **kwargs): diff --git a/simpegFLOW/Tests/test_Richards.py b/simpegFLOW/Tests/test_Richards.py index ab2cbc1a..f8ec6358 100644 --- a/simpegFLOW/Tests/test_Richards.py +++ b/simpegFLOW/Tests/test_Richards.py @@ -67,6 +67,7 @@ class RichardsTests1D(unittest.TestCase): M.setCellGradBC('dirichlet') params = Richards.Empirical.HaverkampParams().celia1990 + params['Ks'] = np.log(params['Ks']) E = Richards.Empirical.Haverkamp(M, **params) bc = np.array([-61.5,-20.7]) From 6166c2b14b74e382bcaaa0434eb10ebf5b62fdb7 Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Tue, 15 Jul 2014 07:56:06 -0700 Subject: [PATCH 26/32] Make boundary conditions be passed the current head value. --- simpegFLOW/Richards/RichardsProblem.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/simpegFLOW/Richards/RichardsProblem.py b/simpegFLOW/Richards/RichardsProblem.py index 9c2ecffb..da4bc5c7 100644 --- a/simpegFLOW/Richards/RichardsProblem.py +++ b/simpegFLOW/Richards/RichardsProblem.py @@ -96,13 +96,13 @@ class RichardsProblem(Problem.BaseTimeProblem): def __init__(self, mesh, mapping=None, **kwargs): Problem.BaseTimeProblem.__init__(self, mesh, mapping=mapping, **kwargs) - def getBoundaryConditions(self, ii): + def getBoundaryConditions(self, ii, u_ii): if type(self.boundaryConditions) is np.ndarray: return self.boundaryConditions time = self.timeMesh.vectorCCx[ii] - return self.boundaryConditions(time) + return self.boundaryConditions(time, u_ii) @property def method(self): @@ -131,7 +131,7 @@ class RichardsProblem(Problem.BaseTimeProblem): u = range(self.nT+1) u[0] = self.initialConditions for ii, dt in enumerate(self.timeSteps): - bc = self.getBoundaryConditions(ii) + bc = self.getBoundaryConditions(ii, u[ii]) u[ii+1] = self.rootFinder.root(lambda hn1m, return_g=True: self.getResidual(m, u[ii], hn1m, dt, bc, return_g=return_g), u[ii]) return u @@ -230,7 +230,7 @@ class RichardsProblem(Problem.BaseTimeProblem): Asubs, Adiags, Bs = range(nn), range(nn), range(nn) for ii in range(nn): dt = self.timeSteps[ii] - bc = self.getBoundaryConditions(ii) + bc = self.getBoundaryConditions(ii, u[ii]) Asubs[ii], Adiags[ii], Bs[ii] = self.diagsJacobian(m, u[ii], u[ii+1], dt, bc) Ad = sp.block_diag(Adiags) zRight = Utils.spzeros((len(Asubs)-1)*Asubs[0].shape[0],Adiags[0].shape[1]) @@ -254,13 +254,13 @@ class RichardsProblem(Problem.BaseTimeProblem): JvC = range(len(u)-1) # Cell to hold each row of the long vector. # This is done via forward substitution. - bc = self.getBoundaryConditions(0) + bc = self.getBoundaryConditions(0, u[0]) temp, Adiag, B = self.diagsJacobian(m, u[0], u[1], self.timeSteps[0], bc) Adiaginv = self.Solver(Adiag, **self.solverOpts) JvC[0] = Adiaginv * (B*v) for ii in range(1,len(u)-1): - bc = self.getBoundaryConditions(ii) + bc = self.getBoundaryConditions(ii, u[ii]) Asub, Adiag, B = self.diagsJacobian(m, u[ii], u[ii+1], self.timeSteps[ii], bc) Adiaginv = self.Solver(Adiag, **self.solverOpts) JvC[ii] = Adiaginv * (B*v - Asub*JvC[ii-1]) @@ -279,7 +279,7 @@ class RichardsProblem(Problem.BaseTimeProblem): minus = 0 BJtv = 0 for ii in range(len(u)-1,0,-1): - bc = self.getBoundaryConditions(ii-1) + bc = self.getBoundaryConditions(ii-1, u[ii-1]) Asub, Adiag, B = self.diagsJacobian(m, u[ii-1], u[ii], self.timeSteps[ii-1], bc) #select the correct part of v vpart = range((ii)*Adiag.shape[0], (ii+1)*Adiag.shape[0]) From 9067590c65c9e135d7e0eebdcb8545a91883976a Mon Sep 17 00:00:00 2001 From: Rowan Cockett Date: Tue, 18 Nov 2014 08:24:51 -0800 Subject: [PATCH 27/32] VanG model bug. --- simpegFLOW/Richards/Empirical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/simpegFLOW/Richards/Empirical.py b/simpegFLOW/Richards/Empirical.py index 0e45ed57..1bb163b5 100644 --- a/simpegFLOW/Richards/Empirical.py +++ b/simpegFLOW/Richards/Empirical.py @@ -362,8 +362,8 @@ class VanGenuchten(RichardsMap): def __init__(self, mesh, **kwargs): RichardsMap.__init__(self, mesh, - _haverkamp_theta(mesh), - _haverkamp_k(mesh)) + _vangenuchten_theta(mesh), + _vangenuchten_k(mesh)) Utils.setKwargs(self, **kwargs) From b2d57e8892a0e8a473c19106df680e9cc0349d28 Mon Sep 17 00:00:00 2001 From: Rowan Cockett Date: Thu, 29 Jan 2015 14:02:26 -0800 Subject: [PATCH 28/32] updates to 2D testing --- .gitignore | 1 + simpegFLOW/Tests/test_Richards.py | 128 ++++++++++++++---------------- 2 files changed, 61 insertions(+), 68 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0d20b648 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/simpegFLOW/Tests/test_Richards.py b/simpegFLOW/Tests/test_Richards.py index f8ec6358..0e65ac5e 100644 --- a/simpegFLOW/Tests/test_Richards.py +++ b/simpegFLOW/Tests/test_Richards.py @@ -109,7 +109,7 @@ class RichardsTests1D(unittest.TestCase): Hs = self.prob.fields(self.Ks) vJz = v.dot(self.prob.Jvec(self.Ks,z,u=Hs)) zJv = z.dot(self.prob.Jtvec(self.Ks,v,u=Hs)) - tol = TOL*(10**int(np.log10(zJv))) + tol = TOL*(10**int(np.log10(np.abs(zJv)))) passed = np.abs(vJz - zJv) < tol print 'Richards Adjoint Test - PressureHead' print '%4.4e === %4.4e, diff=%4.4e < %4.e'%(vJz, zJv,np.abs(vJz - zJv),tol) @@ -127,88 +127,80 @@ class RichardsTests1D(unittest.TestCase): mTrue = self.Ks*np.ones(self.M.nC) J = self.prob.Jfull(mTrue) derChk = lambda m: [self.survey.dpred(m), J] - print 'Testing Richards Derivative' + print 'Testing Richards Derivative FULL' passed = checkDerivative(derChk, mTrue, num=4, plotIt=False) self.assertTrue(passed,True) -# class RichardsTests2D(object): +class RichardsTests2D(unittest.TestCase): -# def setUp(self): -# M = mesh.TensorMesh([np.ones(8),np.ones(30)]) -# Ks = 9.4400e-03 -# E = Richards.Haverkamp(Ks=np.log(Ks), A=1.1750e+06, gamma=4.74, alpha=1.6110e+06, theta_s=0.287, theta_r=0.075, beta=3.96) + def setUp(self): + M = Mesh.TensorMesh([np.ones(8),np.ones(30)]) -# bc = np.array([-61.5,-20.7]) -# bc = np.r_[np.zeros(M.nCy*2),np.ones(M.nCx)*bc[0],np.ones(M.nCx)*bc[1]] -# h = np.zeros(M.nC) + bc[0] -# prob = Richards.RichardsProblem(M,E, timeStep=60, timeEnd=180, boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') + M.setCellGradBC(['neumann','dirichlet']) + params = Richards.Empirical.HaverkampParams().celia1990 + params['Ks'] = np.log(params['Ks']) + E = Richards.Empirical.Haverkamp(M, **params) -# XY = utils.ndgrid(np.array([5,7.]),np.array([5,15,25.])) -# q = M.getInterpolationMat(XY,'CC') -# P = sp.kron(sp.identity(prob.numIts),q) -# prob.P = P + bc = np.array([-61.5,-20.7]) + bc = np.r_[np.zeros(M.nCy*2),np.ones(M.nCx)*bc[0],np.ones(M.nCx)*bc[1]] + h = np.zeros(M.nC) + bc[0] + prob = Richards.RichardsProblem(M,E, timeSteps=[(40,3),(60,3)], boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') -# self.h0 = h -# self.M = M -# self.Ks = Ks -# self.prob = prob + locs = Utils.ndgrid(np.array([5,7.]),np.array([5,15,25.])) + times = prob.times[3:5] + rxSat = Richards.RichardsRx(locs, times, 'saturation') + rxPre = Richards.RichardsRx(locs, times, 'pressureHead') + survey = Richards.RichardsSurvey([rxSat, rxPre]) -# def test_Richards_getResidual_Newton(self): -# self.prob.doNewton = True -# passed = checkDerivative(lambda hn1: self.prob.getResidual(self.h0,hn1), self.h0, plotIt=False) -# self.assertTrue(passed,True) + prob.pair(survey) -# def test_Richards_getResidual_Picard(self): -# self.prob.doNewton = False -# passed = checkDerivative(lambda hn1: self.prob.getResidual(self.h0,hn1), self.h0, plotIt=False, expectedOrder=1) -# self.assertTrue(passed,True) + self.h0 = h + self.M = M + self.Ks = params['Ks'] + self.prob = prob + self.survey = survey -# def test_Adjoint_PressureHead(self): -# self.prob.dataType = 'pressureHead' -# Ks = self.Ks -# v = np.random.rand(self.prob.P.shape[0]) -# z = np.random.rand(self.M.nC) -# Hs = self.prob.field(np.log(Ks)) -# vJz = v.dot(self.prob.J(np.log(Ks),z,u=Hs)) -# zJv = z.dot(self.prob.Jt(np.log(Ks),v,u=Hs)) -# tol = TOL*(10**int(np.log10(zJv))) -# passed = np.abs(vJz - zJv) < tol -# print 'Richards Adjoint Test - PressureHead' -# print '%4.4e === %4.4e, diff=%4.4e < %4.e'%(vJz, zJv,np.abs(vJz - zJv),tol) -# self.assertTrue(passed,True) + def test_Richards_getResidual_Newton(self): + self.prob.doNewton = True + m = self.Ks + passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0, hn1, self.prob.timeSteps[0], self.prob.boundaryConditions), self.h0, plotIt=False) + self.assertTrue(passed,True) + def test_Richards_getResidual_Picard(self): + self.prob.doNewton = False + m = self.Ks + passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0, hn1, self.prob.timeSteps[0], self.prob.boundaryConditions), self.h0, plotIt=False, expectedOrder=1) + self.assertTrue(passed,True) -# def test_Adjoint_Saturation(self): -# self.prob.dataType = 'saturation' -# Ks = self.Ks -# v = np.random.rand(self.prob.P.shape[0]) -# z = np.random.rand(self.M.nC) -# Hs = self.prob.field(np.log(Ks)) -# vJz = v.dot(self.prob.J(np.log(Ks),z,u=Hs)) -# zJv = z.dot(self.prob.Jt(np.log(Ks),v,u=Hs)) -# tol = TOL #*(10**int(np.log10(zJv))) -# passed = np.abs(vJz - zJv) < tol -# print 'Richards Adjoint Test - Saturation' -# print '%4.4e === %4.4e, diff=%4.4e < %4.e'%(vJz, zJv,np.abs(vJz - zJv),tol) -# self.assertTrue(passed,True) + def test_Adjoint(self): + v = np.random.rand(self.survey.nD) + z = np.random.rand(self.M.nC) + Hs = self.prob.fields(self.Ks) + vJz = v.dot(self.prob.Jvec(self.Ks,z,u=Hs)) + zJv = z.dot(self.prob.Jtvec(self.Ks,v,u=Hs)) + tol = TOL*(10**int(np.log10(np.abs(zJv)))) + passed = np.abs(vJz - zJv) < tol + print '2D: Richards Adjoint Test - PressureHead' + print '%4.4e === %4.4e, diff=%4.4e < %4.e'%(vJz, zJv,np.abs(vJz - zJv),tol) + self.assertTrue(passed,True) + + def test_Sensitivity(self): + mTrue = self.Ks*np.ones(self.M.nC) + derChk = lambda m: [self.survey.dpred(m), lambda v: self.prob.Jvec(m, v)] + print '2D: Testing Richards Derivative' + passed = checkDerivative(derChk, mTrue, num=4, plotIt=False) + self.assertTrue(passed,True) + + def test_Sensitivity_full(self): + mTrue = self.Ks*np.ones(self.M.nC) + J = self.prob.Jfull(mTrue) + derChk = lambda m: [self.survey.dpred(m), J] + print '2D: Testing Richards Derivative FULL' + passed = checkDerivative(derChk, mTrue, num=4, plotIt=False) + self.assertTrue(passed,True) -# def test_Sensitivity(self): -# self.prob.dataType = 'pressureHead' -# mTrue = np.ones(self.M.nC)*self.Ks -# stdev = 0.01 # The standard deviation for the noise -# dobs = self.prob.createSyntheticSurvey(mTrue,std=stdev)[0] -# self.prob.dobs = dobs -# self.prob.std = dobs*0 + stdev -# Hs = self.prob.field(mTrue) -# opt = inverse.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) -# reg = regularization.Regularization(self.M) -# inv = inverse.Inversion(self.prob, reg, opt, beta0=1e4) -# derChk = lambda m: [inv.dataObj(m), inv.dataObjDeriv(m)] -# print 'Testing Richards Derivative' -# passed = checkDerivative(derChk, mTrue, num=5, plotIt=False) -# self.assertTrue(passed,True) if __name__ == '__main__': unittest.main() From 5c675a7992c586d76ddf76cb14f9185c83641e12 Mon Sep 17 00:00:00 2001 From: Rowan Cockett Date: Thu, 29 Jan 2015 14:58:01 -0800 Subject: [PATCH 29/32] update the tests to test the 3D version of the equations. --- simpegFLOW/Tests/test_Richards.py | 80 +++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/simpegFLOW/Tests/test_Richards.py b/simpegFLOW/Tests/test_Richards.py index 0e65ac5e..fd69adf4 100644 --- a/simpegFLOW/Tests/test_Richards.py +++ b/simpegFLOW/Tests/test_Richards.py @@ -3,6 +3,12 @@ from SimPEG import * from SimPEG.Tests.TestUtils import OrderTest, checkDerivative from scipy.sparse.linalg import dsolve from simpegFLOW import Richards +try: + from pymatsolver import MumpsSolver + Solver = MumpsSolver +except Exception, e: + pass + TOL = 1E-8 @@ -76,6 +82,7 @@ class RichardsTests1D(unittest.TestCase): prob = Richards.RichardsProblem(M, mapping=E, timeSteps=[(40,3),(60,3)], boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') + prob.Solver = Solver locs = np.r_[5.,10,15] times = prob.times[3:5] @@ -147,6 +154,7 @@ class RichardsTests2D(unittest.TestCase): bc = np.r_[np.zeros(M.nCy*2),np.ones(M.nCx)*bc[0],np.ones(M.nCx)*bc[1]] h = np.zeros(M.nC) + bc[0] prob = Richards.RichardsProblem(M,E, timeSteps=[(40,3),(60,3)], boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') + prob.Solver = Solver locs = Utils.ndgrid(np.array([5,7.]),np.array([5,15,25.])) times = prob.times[3:5] @@ -202,5 +210,77 @@ class RichardsTests2D(unittest.TestCase): self.assertTrue(passed,True) + +class RichardsTests3D(unittest.TestCase): + + def setUp(self): + M = Mesh.TensorMesh([np.ones(8),np.ones(20),np.ones(10)]) + + M.setCellGradBC(['neumann','neumann','dirichlet']) + + params = Richards.Empirical.HaverkampParams().celia1990 + params['Ks'] = np.log(params['Ks']) + E = Richards.Empirical.Haverkamp(M, **params) + + bc = np.array([-61.5,-20.7]) + bc = np.r_[np.zeros(M.nCy*M.nCz*2),np.zeros(M.nCx*M.nCz*2),np.ones(M.nCx*M.nCy)*bc[0],np.ones(M.nCx*M.nCy)*bc[1]] + h = np.zeros(M.nC) + bc[0] + prob = Richards.RichardsProblem(M,E, timeSteps=[(40,3),(60,3)], boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') + prob.Solver = Solver + + locs = Utils.ndgrid(np.r_[5,7.],np.r_[5,15.],np.r_[6,8.]) + times = prob.times[3:5] + rxSat = Richards.RichardsRx(locs, times, 'saturation') + rxPre = Richards.RichardsRx(locs, times, 'pressureHead') + survey = Richards.RichardsSurvey([rxSat, rxPre]) + + prob.pair(survey) + + self.h0 = h + self.M = M + self.Ks = params['Ks'] + self.prob = prob + self.survey = survey + + def test_Richards_getResidual_Newton(self): + self.prob.doNewton = True + m = self.Ks + passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0, hn1, self.prob.timeSteps[0], self.prob.boundaryConditions), self.h0, plotIt=False) + self.assertTrue(passed,True) + + def test_Richards_getResidual_Picard(self): + self.prob.doNewton = False + m = self.Ks + passed = checkDerivative(lambda hn1: self.prob.getResidual(m, self.h0, hn1, self.prob.timeSteps[0], self.prob.boundaryConditions), self.h0, plotIt=False, expectedOrder=1) + self.assertTrue(passed,True) + + def test_Adjoint(self): + v = np.random.rand(self.survey.nD) + z = np.random.rand(self.M.nC) + Hs = self.prob.fields(self.Ks) + vJz = v.dot(self.prob.Jvec(self.Ks,z,u=Hs)) + zJv = z.dot(self.prob.Jtvec(self.Ks,v,u=Hs)) + tol = TOL*(10**int(np.log10(np.abs(zJv)))) + passed = np.abs(vJz - zJv) < tol + print '3D: Richards Adjoint Test - PressureHead' + print '%4.4e === %4.4e, diff=%4.4e < %4.e'%(vJz, zJv,np.abs(vJz - zJv),tol) + self.assertTrue(passed,True) + + def test_Sensitivity(self): + mTrue = self.Ks*np.ones(self.M.nC) + derChk = lambda m: [self.survey.dpred(m), lambda v: self.prob.Jvec(m, v)] + print '3D: Testing Richards Derivative' + passed = checkDerivative(derChk, mTrue, num=4, plotIt=False) + self.assertTrue(passed,True) + + # def test_Sensitivity_full(self): + # mTrue = self.Ks*np.ones(self.M.nC) + # J = self.prob.Jfull(mTrue) + # derChk = lambda m: [self.survey.dpred(m), J] + # print '3D: Testing Richards Derivative FULL' + # passed = checkDerivative(derChk, mTrue, num=4, plotIt=False) + # self.assertTrue(passed,True) + + if __name__ == '__main__': unittest.main() From e99d6dc208e243a4de62c6e4e0f6f979e3737349 Mon Sep 17 00:00:00 2001 From: Rowan Cockett Date: Tue, 24 Feb 2015 17:16:47 -0500 Subject: [PATCH 30/32] update miniconda --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bfe89717..3f0dcf22 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ python: # Setup anaconda before_install: - - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then wget http://repo.continuum.io/miniconda/Miniconda-3.3.0-Linux-x86_64.sh -O miniconda.sh; else wget http://repo.continuum.io/miniconda/Miniconda3-3.3.0-Linux-x86_64.sh -O miniconda.sh; fi + - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then wget http://repo.continuum.io/miniconda/Miniconda-3.8.3-Linux-x86_64.sh -O miniconda.sh; else wget http://repo.continuum.io/miniconda/Miniconda3-3.8.3-Linux-x86_64.sh -O miniconda.sh; fi - chmod +x miniconda.sh - ./miniconda.sh -b - export PATH=/home/travis/anaconda/bin:/home/travis/miniconda/bin:$PATH From 26a84c7bfca14294f0d4012007b481b03b4f42ce Mon Sep 17 00:00:00 2001 From: Rowan Cockett Date: Fri, 5 Jun 2015 13:35:52 -0700 Subject: [PATCH 31/32] updates to problem debugging abilities --- simpegFLOW/Richards/RichardsProblem.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/simpegFLOW/Richards/RichardsProblem.py b/simpegFLOW/Richards/RichardsProblem.py index da4bc5c7..2a2bf1e1 100644 --- a/simpegFLOW/Richards/RichardsProblem.py +++ b/simpegFLOW/Richards/RichardsProblem.py @@ -1,5 +1,6 @@ from SimPEG import * from Empirical import RichardsMap +import time class RichardsRx(Survey.BaseTimeRx): @@ -119,22 +120,28 @@ class RichardsProblem(Problem.BaseTimeProblem): maxIterRootFinder = Utils.dependentProperty('_maxIterRootFinder', 30, ['_rootFinder'], "Maximum iterations for rootFinder iteration.") + tolRootFinder = Utils.dependentProperty('_tolRootFinder', 1e-4, ['_rootFinder'], + "Maximum iterations for rootFinder iteration.") @property def rootFinder(self): """Root-finding Algorithm""" if getattr(self, '_rootFinder', None) is None: - self._rootFinder = Optimization.NewtonRoot(doLS=self.doNewton, maxIter=self.maxIterRootFinder, Solver=self.Solver) + self._rootFinder = Optimization.NewtonRoot(doLS=self.doNewton, maxIter=self.maxIterRootFinder, tol=self.tolRootFinder, Solver=self.Solver) return self._rootFinder + @Utils.timeIt def fields(self, m): + tic = time.time() u = range(self.nT+1) u[0] = self.initialConditions for ii, dt in enumerate(self.timeSteps): bc = self.getBoundaryConditions(ii, u[ii]) u[ii+1] = self.rootFinder.root(lambda hn1m, return_g=True: self.getResidual(m, u[ii], hn1m, dt, bc, return_g=return_g), u[ii]) + print "Solving Fields (%4d/%d - %3.1f%% Done) %d Iterations, %4.2f seconds"%(ii, self.nT, 100.0*ii/self.nT, self.rootFinder.iter, time.time() - tic) return u + @Utils.timeIt def diagsJacobian(self, m, hn, hn1, dt, bc): DIV = self.mesh.faceDiv @@ -184,6 +191,7 @@ class RichardsProblem(Problem.BaseTimeProblem): return Asub, Adiag, B + @Utils.timeIt def getResidual(self, m, hn, h, dt, bc, return_g=True): """ Where h is the proposed value for the next time iterate (h_{n+1}) @@ -222,6 +230,7 @@ class RichardsProblem(Problem.BaseTimeProblem): return r, J + @Utils.timeIt def Jfull(self, m, u=None): if u is None: u = self.fields(m) @@ -247,6 +256,7 @@ class RichardsProblem(Problem.BaseTimeProblem): J = P * zAinvB return J + @Utils.timeIt def Jvec(self, m, v, u=None): if u is None: u = self.fields(m) @@ -268,6 +278,7 @@ class RichardsProblem(Problem.BaseTimeProblem): P = self.survey.projectFieldsDeriv(u, m) return P * np.concatenate([np.zeros(self.mesh.nC)] + JvC) + @Utils.timeIt def Jtvec(self, m, v, u=None): if u is None: u = self.field(m) From 4113a7438bb47c95305b9bb57462d62c6929280e Mon Sep 17 00:00:00 2001 From: Rowan Cockett Date: Fri, 5 Jun 2015 16:29:15 -0700 Subject: [PATCH 32/32] updates to tolerances in the derivative check. --- simpegFLOW/Richards/RichardsProblem.py | 4 +++- simpegFLOW/Tests/test_Richards.py | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/simpegFLOW/Richards/RichardsProblem.py b/simpegFLOW/Richards/RichardsProblem.py index 2a2bf1e1..7c61c221 100644 --- a/simpegFLOW/Richards/RichardsProblem.py +++ b/simpegFLOW/Richards/RichardsProblem.py @@ -91,6 +91,8 @@ class RichardsProblem(Problem.BaseTimeProblem): surveyPair = RichardsSurvey mapPair = RichardsMap + debug=True + Solver = Solver solverOpts = {} @@ -138,7 +140,7 @@ class RichardsProblem(Problem.BaseTimeProblem): for ii, dt in enumerate(self.timeSteps): bc = self.getBoundaryConditions(ii, u[ii]) u[ii+1] = self.rootFinder.root(lambda hn1m, return_g=True: self.getResidual(m, u[ii], hn1m, dt, bc, return_g=return_g), u[ii]) - print "Solving Fields (%4d/%d - %3.1f%% Done) %d Iterations, %4.2f seconds"%(ii, self.nT, 100.0*ii/self.nT, self.rootFinder.iter, time.time() - tic) + if self.debug: print "Solving Fields (%4d/%d - %3.1f%% Done) %d Iterations, %4.2f seconds"%(ii+1, self.nT, 100.0*(ii+1)/self.nT, self.rootFinder.iter, time.time() - tic) return u @Utils.timeIt diff --git a/simpegFLOW/Tests/test_Richards.py b/simpegFLOW/Tests/test_Richards.py index fd69adf4..d82127ce 100644 --- a/simpegFLOW/Tests/test_Richards.py +++ b/simpegFLOW/Tests/test_Richards.py @@ -79,7 +79,7 @@ class RichardsTests1D(unittest.TestCase): bc = np.array([-61.5,-20.7]) h = np.zeros(M.nC) + bc[0] - prob = Richards.RichardsProblem(M, mapping=E, timeSteps=[(40,3),(60,3)], + prob = Richards.RichardsProblem(M, mapping=E, timeSteps=[(40,3),(60,3)], tolRootFinder=1e-6, debug=False, boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') prob.Solver = Solver @@ -153,7 +153,7 @@ class RichardsTests2D(unittest.TestCase): bc = np.array([-61.5,-20.7]) bc = np.r_[np.zeros(M.nCy*2),np.ones(M.nCx)*bc[0],np.ones(M.nCx)*bc[1]] h = np.zeros(M.nC) + bc[0] - prob = Richards.RichardsProblem(M,E, timeSteps=[(40,3),(60,3)], boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') + prob = Richards.RichardsProblem(M,E, timeSteps=[(40,3),(60,3)], boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed', tolRootFinder=1e-6, debug=False) prob.Solver = Solver locs = Utils.ndgrid(np.array([5,7.]),np.array([5,15,25.])) @@ -225,7 +225,7 @@ class RichardsTests3D(unittest.TestCase): bc = np.array([-61.5,-20.7]) bc = np.r_[np.zeros(M.nCy*M.nCz*2),np.zeros(M.nCx*M.nCz*2),np.ones(M.nCx*M.nCy)*bc[0],np.ones(M.nCx*M.nCy)*bc[1]] h = np.zeros(M.nC) + bc[0] - prob = Richards.RichardsProblem(M,E, timeSteps=[(40,3),(60,3)], boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed') + prob = Richards.RichardsProblem(M,E, timeSteps=[(40,3),(60,3)], boundaryConditions=bc, initialConditions=h, doNewton=False, method='mixed', tolRootFinder=1e-6, debug=False) prob.Solver = Solver locs = Utils.ndgrid(np.r_[5,7.],np.r_[5,15.],np.r_[6,8.])