├── .gitignore ├── .pep8 ├── .readthedocs.yaml ├── .travis.yml ├── LICENSE.md ├── README.md ├── docs ├── Makefile ├── make.bat └── source │ ├── api │ ├── index.rst │ ├── plot.rst │ ├── rsf.rst │ └── staterelations.rst │ ├── conf.py │ ├── index.rst │ └── installationguide.rst ├── environment.yml ├── examples ├── .gitkeep ├── defining_new_state_relation.py ├── slide_hold_slide.py ├── test_examples.py ├── velocity_step_one_state.py └── velocity_step_two_state.py ├── readthedocs-pip-requirements.txt ├── rsfmodel ├── __init__.py ├── plot.py ├── rsf.py └── staterelations.py ├── setup.py └── tests ├── __init__.py ├── plot_tests.py └── rsf_tests.py /.gitignore: -------------------------------------------------------------------------------- 1 | # PyCharm 2 | .idea/ 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *,cover 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | 56 | # Sphinx documentation 57 | docs/_build/ 58 | 59 | # PyBuilder 60 | target/ 61 | 62 | .DS_Store 63 | -------------------------------------------------------------------------------- /.pep8: -------------------------------------------------------------------------------- 1 | [pep8] 2 | max-line-length = 95 3 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.11" 13 | 14 | # Build documentation in the docs/ directory with Sphinx 15 | sphinx: 16 | configuration: docs/conf.py 17 | 18 | # We recommend specifying your dependencies to enable reproducible builds: 19 | # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 20 | # python: 21 | # install: 22 | # - requirements: docs/requirements.txt -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.6" 4 | - "2.7" 5 | - "3.2" 6 | - "3.3" 7 | 8 | matrix: 9 | include: 10 | - python: 2.7 11 | env: EXAMPLES=true 12 | - python: 3.4 13 | env: EXAMPLES=true 14 | - python: 3.4 15 | env: NOSE_WITH_COVERAGE=y NOSE_COVER_PACKAGE=rsfmodel 16 | 17 | before_install: 18 | - wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh 19 | - chmod +x miniconda.sh 20 | - ./miniconda.sh -b -p $HOME/miniconda 21 | - export PATH=/home/travis/miniconda/bin:$PATH 22 | - conda update --yes conda 23 | - if [[ $TRAVIS_PYTHON_VERSION == "3.2" ]]; then 24 | conda config --add channels https://conda.binstar.org/astropy-ci-extras; 25 | fi; 26 | - conda create -n Travis --yes python=$TRAVIS_PYTHON_VERSION numpy scipy matplotlib nose 27 | - source activate Travis 28 | - if [[ $NOSE_WITH_COVERAGE == y ]]; then 29 | conda install --yes coverage; 30 | fi; 31 | 32 | install: 33 | - python setup.py install 34 | 35 | script: 36 | - if [[ $EXAMPLES == true ]]; then 37 | cd examples; 38 | echo backend:agg > matplotlibrc; 39 | MPLBACKEND='agg' python test_examples.py; 40 | else 41 | nosetests; 42 | fi 43 | 44 | after_success: 45 | - if [[ $NOSE_WITH_COVERAGE == y ]]; then 46 | pip install coveralls; 47 | coveralls; 48 | fi; 49 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 John Leeman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/jrleeman/rsfmodel.svg?branch=master)](https://travis-ci.org/jrleeman/rsfmodel) 2 | [![Coverage Status](https://coveralls.io/repos/jrleeman/rsfmodel/badge.svg?branch=master&service=github)](https://coveralls.io/github/jrleeman/rsfmodel?branch=master) 3 | [![Documentation Status](https://readthedocs.org/projects/rsfmodel/badge/?version=latest)](http://rsfmodel.readthedocs.org/en/latest/?badge=latest) 4 | 5 | # Rate and State Friction Toolkit 6 | 7 | This Python module enables modeling of frictional response to dynamic events 8 | such as velocity steps and time dependent frictional healing. The rate and 9 | state framework was laid out in Dieterich (1979), and furthered by Ruina(1983). 10 | A summary article by Chris Marone (1998) provides a solid background. 11 | 12 | ### SciPy 2016 Materials 13 | * J.R. Leeman, R. May, C. Marone, and D. Saffer. Modeling Rate-and-State Friction with Python. SciPy Scientific Programming, 2016. Talk. 14 | * [Abstract](http://johnrleeman.com/pubs/2016/SciPy2016_Abstract_Leeman.html) 15 | * [Slides](http://johnrleeman.com/pubs/2016/Leeman_SciPy_2016.pdf) 16 | * [Notebook](http://johnrleeman.com/pubs/2016/rsf_widgets_2sv_dashboard.ipynb) 17 | 18 | ### Examples of use 19 | 20 | Examples of simple single state variable and more complex multiple state-variable 21 | cases are included in the examples directory. 22 | 23 | ### Useful References 24 | - Dieterich, J. H. (1979). Modeling of rock friction: 1. Experimental results and constitutive equations. Journal of Geophysical Research: Solid Earth (1978--2012), 84(B5), 2161–2168. 25 | - Marone, C. (1998). Laboratory-derived friction laws and their application to seismic faulting. Annual Review of Earth and Planetary Sciences, 26(1), 643–696. 26 | - Ruina, A. (1983). Slip instability and state variable friction laws. Journal of Geophysical Research: Solid Earth (1978--2012), 88(B12), 10359–10370. 27 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " applehelp to make an Apple Help Book" 34 | @echo " devhelp to make HTML files and a Devhelp project" 35 | @echo " epub to make an epub" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | html: 55 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 56 | @echo 57 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 58 | 59 | dirhtml: 60 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 61 | @echo 62 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 63 | 64 | singlehtml: 65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 66 | @echo 67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 68 | 69 | pickle: 70 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 71 | @echo 72 | @echo "Build finished; now you can process the pickle files." 73 | 74 | json: 75 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 76 | @echo 77 | @echo "Build finished; now you can process the JSON files." 78 | 79 | htmlhelp: 80 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 81 | @echo 82 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 83 | ".hhp project file in $(BUILDDIR)/htmlhelp." 84 | 85 | qthelp: 86 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 87 | @echo 88 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 89 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 90 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/rsfmodel.qhcp" 91 | @echo "To view the help file:" 92 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/rsfmodel.qhc" 93 | 94 | applehelp: 95 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 96 | @echo 97 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 98 | @echo "N.B. You won't be able to view it unless you put it in" \ 99 | "~/Library/Documentation/Help or install it in your application" \ 100 | "bundle." 101 | 102 | devhelp: 103 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 104 | @echo 105 | @echo "Build finished." 106 | @echo "To view the help file:" 107 | @echo "# mkdir -p $$HOME/.local/share/devhelp/rsfmodel" 108 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/rsfmodel" 109 | @echo "# devhelp" 110 | 111 | epub: 112 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 113 | @echo 114 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 115 | 116 | latex: 117 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 118 | @echo 119 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 120 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 121 | "(use \`make latexpdf' here to do that automatically)." 122 | 123 | latexpdf: 124 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 125 | @echo "Running LaTeX files through pdflatex..." 126 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 127 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 128 | 129 | latexpdfja: 130 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 131 | @echo "Running LaTeX files through platex and dvipdfmx..." 132 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 133 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 134 | 135 | text: 136 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 137 | @echo 138 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 139 | 140 | man: 141 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 142 | @echo 143 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 144 | 145 | texinfo: 146 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 147 | @echo 148 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 149 | @echo "Run \`make' in that directory to run these through makeinfo" \ 150 | "(use \`make info' here to do that automatically)." 151 | 152 | info: 153 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 154 | @echo "Running Texinfo files through makeinfo..." 155 | make -C $(BUILDDIR)/texinfo info 156 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 157 | 158 | gettext: 159 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 160 | @echo 161 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 162 | 163 | changes: 164 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 165 | @echo 166 | @echo "The overview file is in $(BUILDDIR)/changes." 167 | 168 | linkcheck: 169 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 170 | @echo 171 | @echo "Link check complete; look for any errors in the above output " \ 172 | "or in $(BUILDDIR)/linkcheck/output.txt." 173 | 174 | doctest: 175 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 176 | @echo "Testing of doctests in the sources finished, look at the " \ 177 | "results in $(BUILDDIR)/doctest/output.txt." 178 | 179 | coverage: 180 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 181 | @echo "Testing of coverage in the sources finished, look at the " \ 182 | "results in $(BUILDDIR)/coverage/python.txt." 183 | 184 | xml: 185 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 186 | @echo 187 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 188 | 189 | pseudoxml: 190 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 191 | @echo 192 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 193 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | echo. coverage to run coverage check of the documentation if enabled 41 | goto end 42 | ) 43 | 44 | if "%1" == "clean" ( 45 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 46 | del /q /s %BUILDDIR%\* 47 | goto end 48 | ) 49 | 50 | 51 | REM Check if sphinx-build is available and fallback to Python version if any 52 | %SPHINXBUILD% 2> nul 53 | if errorlevel 9009 goto sphinx_python 54 | goto sphinx_ok 55 | 56 | :sphinx_python 57 | 58 | set SPHINXBUILD=python -m sphinx.__init__ 59 | %SPHINXBUILD% 2> nul 60 | if errorlevel 9009 ( 61 | echo. 62 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 63 | echo.installed, then set the SPHINXBUILD environment variable to point 64 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 65 | echo.may add the Sphinx directory to PATH. 66 | echo. 67 | echo.If you don't have Sphinx installed, grab it from 68 | echo.http://sphinx-doc.org/ 69 | exit /b 1 70 | ) 71 | 72 | :sphinx_ok 73 | 74 | 75 | if "%1" == "html" ( 76 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 80 | goto end 81 | ) 82 | 83 | if "%1" == "dirhtml" ( 84 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 88 | goto end 89 | ) 90 | 91 | if "%1" == "singlehtml" ( 92 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 93 | if errorlevel 1 exit /b 1 94 | echo. 95 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 96 | goto end 97 | ) 98 | 99 | if "%1" == "pickle" ( 100 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 101 | if errorlevel 1 exit /b 1 102 | echo. 103 | echo.Build finished; now you can process the pickle files. 104 | goto end 105 | ) 106 | 107 | if "%1" == "json" ( 108 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 109 | if errorlevel 1 exit /b 1 110 | echo. 111 | echo.Build finished; now you can process the JSON files. 112 | goto end 113 | ) 114 | 115 | if "%1" == "htmlhelp" ( 116 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 117 | if errorlevel 1 exit /b 1 118 | echo. 119 | echo.Build finished; now you can run HTML Help Workshop with the ^ 120 | .hhp project file in %BUILDDIR%/htmlhelp. 121 | goto end 122 | ) 123 | 124 | if "%1" == "qthelp" ( 125 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 129 | .qhcp project file in %BUILDDIR%/qthelp, like this: 130 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\rsfmodel.qhcp 131 | echo.To view the help file: 132 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\rsfmodel.ghc 133 | goto end 134 | ) 135 | 136 | if "%1" == "devhelp" ( 137 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. 141 | goto end 142 | ) 143 | 144 | if "%1" == "epub" ( 145 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 149 | goto end 150 | ) 151 | 152 | if "%1" == "latex" ( 153 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 157 | goto end 158 | ) 159 | 160 | if "%1" == "latexpdf" ( 161 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 162 | cd %BUILDDIR%/latex 163 | make all-pdf 164 | cd %~dp0 165 | echo. 166 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 167 | goto end 168 | ) 169 | 170 | if "%1" == "latexpdfja" ( 171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 172 | cd %BUILDDIR%/latex 173 | make all-pdf-ja 174 | cd %~dp0 175 | echo. 176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 177 | goto end 178 | ) 179 | 180 | if "%1" == "text" ( 181 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 182 | if errorlevel 1 exit /b 1 183 | echo. 184 | echo.Build finished. The text files are in %BUILDDIR%/text. 185 | goto end 186 | ) 187 | 188 | if "%1" == "man" ( 189 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 190 | if errorlevel 1 exit /b 1 191 | echo. 192 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 193 | goto end 194 | ) 195 | 196 | if "%1" == "texinfo" ( 197 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 198 | if errorlevel 1 exit /b 1 199 | echo. 200 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 201 | goto end 202 | ) 203 | 204 | if "%1" == "gettext" ( 205 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 206 | if errorlevel 1 exit /b 1 207 | echo. 208 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 209 | goto end 210 | ) 211 | 212 | if "%1" == "changes" ( 213 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 214 | if errorlevel 1 exit /b 1 215 | echo. 216 | echo.The overview file is in %BUILDDIR%/changes. 217 | goto end 218 | ) 219 | 220 | if "%1" == "linkcheck" ( 221 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 222 | if errorlevel 1 exit /b 1 223 | echo. 224 | echo.Link check complete; look for any errors in the above output ^ 225 | or in %BUILDDIR%/linkcheck/output.txt. 226 | goto end 227 | ) 228 | 229 | if "%1" == "doctest" ( 230 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 231 | if errorlevel 1 exit /b 1 232 | echo. 233 | echo.Testing of doctests in the sources finished, look at the ^ 234 | results in %BUILDDIR%/doctest/output.txt. 235 | goto end 236 | ) 237 | 238 | if "%1" == "coverage" ( 239 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 240 | if errorlevel 1 exit /b 1 241 | echo. 242 | echo.Testing of coverage in the sources finished, look at the ^ 243 | results in %BUILDDIR%/coverage/python.txt. 244 | goto end 245 | ) 246 | 247 | if "%1" == "xml" ( 248 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 249 | if errorlevel 1 exit /b 1 250 | echo. 251 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 252 | goto end 253 | ) 254 | 255 | if "%1" == "pseudoxml" ( 256 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 257 | if errorlevel 1 exit /b 1 258 | echo. 259 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 260 | goto end 261 | ) 262 | 263 | :end 264 | -------------------------------------------------------------------------------- /docs/source/api/index.rst: -------------------------------------------------------------------------------- 1 | .. _api-index: 2 | 3 | #################### 4 | The rsfmodel API 5 | #################### 6 | 7 | :Release: |version| 8 | :Date: |today| 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | 13 | plot 14 | rsf 15 | staterelations 16 | -------------------------------------------------------------------------------- /docs/source/api/plot.rst: -------------------------------------------------------------------------------- 1 | ******** 2 | Plotting 3 | ******** 4 | 5 | 6 | :mod:`rsfmodel.plot` 7 | ===================== 8 | 9 | .. automodule:: rsfmodel.plot 10 | :members: 11 | :undoc-members: 12 | :show-inheritance: 13 | -------------------------------------------------------------------------------- /docs/source/api/rsf.rst: -------------------------------------------------------------------------------- 1 | ************************ 2 | Rate and State Framework 3 | ************************ 4 | 5 | 6 | :mod:`rsfmodel.rsf` 7 | ===================== 8 | 9 | .. automodule:: rsfmodel.rsf 10 | :members: 11 | :undoc-members: 12 | :show-inheritance: 13 | -------------------------------------------------------------------------------- /docs/source/api/staterelations.rst: -------------------------------------------------------------------------------- 1 | *************** 2 | State Relations 3 | *************** 4 | 5 | 6 | :mod:`rsfmodel.staterelations` 7 | ===================== 8 | 9 | .. automodule:: rsfmodel.staterelations 10 | :members: 11 | :undoc-members: 12 | :show-inheritance: 13 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # rsfmodel documentation build configuration file, created by 4 | # sphinx-quickstart on Sun Aug 9 11:04:50 2015. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | import shlex 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | sys.path.insert(0, os.path.abspath('../..')) 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | #needs_sphinx = '1.0' 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be 30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 31 | # ones. 32 | extensions = ['sphinx.ext.autodoc','numpydoc','sphinx.ext.mathjax'] 33 | 34 | # Add any paths that contain templates here, relative to this directory. 35 | templates_path = ['_templates'] 36 | 37 | # The suffix(es) of source filenames. 38 | # You can specify multiple suffix as a list of string: 39 | # source_suffix = ['.rst', '.md'] 40 | source_suffix = '.rst' 41 | 42 | # The encoding of source files. 43 | #source_encoding = 'utf-8-sig' 44 | 45 | # The master toctree document. 46 | master_doc = 'index' 47 | 48 | # General information about the project. 49 | project = u'rsfmodel' 50 | copyright = u'2015, John R. Leeman' 51 | author = u'John R. Leeman' 52 | 53 | # The version info for the project you're documenting, acts as replacement for 54 | # |version| and |release|, also used in various other places throughout the 55 | # built documents. 56 | # 57 | # The short X.Y version. 58 | version = '0.0.1' 59 | # The full version, including alpha/beta/rc tags. 60 | release = '0.0.1' 61 | 62 | # The language for content autogenerated by Sphinx. Refer to documentation 63 | # for a list of supported languages. 64 | # 65 | # This is also used if you do content translation via gettext catalogs. 66 | # Usually you set "language" from the command line for these cases. 67 | language = None 68 | 69 | # There are two options for replacing |today|: either, you set today to some 70 | # non-false value, then it is used: 71 | #today = '' 72 | # Else, today_fmt is used as the format for a strftime call. 73 | #today_fmt = '%B %d, %Y' 74 | 75 | # List of patterns, relative to source directory, that match files and 76 | # directories to ignore when looking for source files. 77 | exclude_patterns = ['_build'] 78 | 79 | # The reST default role (used for this markup: `text`) to use for all 80 | # documents. 81 | #default_role = None 82 | 83 | # If true, '()' will be appended to :func: etc. cross-reference text. 84 | #add_function_parentheses = True 85 | 86 | # If true, the current module name will be prepended to all description 87 | # unit titles (such as .. function::). 88 | #add_module_names = True 89 | 90 | # If true, sectionauthor and moduleauthor directives will be shown in the 91 | # output. They are ignored by default. 92 | #show_authors = False 93 | 94 | # The name of the Pygments (syntax highlighting) style to use. 95 | pygments_style = 'sphinx' 96 | 97 | # A list of ignored prefixes for module index sorting. 98 | #modindex_common_prefix = [] 99 | 100 | # If true, keep warnings as "system message" paragraphs in the built documents. 101 | #keep_warnings = False 102 | 103 | # If true, `todo` and `todoList` produce output, else they produce nothing. 104 | todo_include_todos = False 105 | 106 | 107 | # -- Options for HTML output ---------------------------------------------- 108 | 109 | # The theme to use for HTML and HTML Help pages. See the documentation for 110 | # a list of builtin themes. 111 | html_theme = 'default' #'alabaster' 112 | 113 | # Theme options are theme-specific and customize the look and feel of a theme 114 | # further. For a list of options available for each theme, see the 115 | # documentation. 116 | #html_theme_options = {} 117 | 118 | # Add any paths that contain custom themes here, relative to this directory. 119 | #html_theme_path = [] 120 | 121 | # The name for this set of Sphinx documents. If None, it defaults to 122 | # " v documentation". 123 | #html_title = None 124 | 125 | # A shorter title for the navigation bar. Default is the same as html_title. 126 | #html_short_title = None 127 | 128 | # The name of an image file (relative to this directory) to place at the top 129 | # of the sidebar. 130 | #html_logo = None 131 | 132 | # The name of an image file (within the static path) to use as favicon of the 133 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 134 | # pixels large. 135 | #html_favicon = None 136 | 137 | # Add any paths that contain custom static files (such as style sheets) here, 138 | # relative to this directory. They are copied after the builtin static files, 139 | # so a file named "default.css" will overwrite the builtin "default.css". 140 | html_static_path = ['_static'] 141 | 142 | # Add any extra paths that contain custom files (such as robots.txt or 143 | # .htaccess) here, relative to this directory. These files are copied 144 | # directly to the root of the documentation. 145 | #html_extra_path = [] 146 | 147 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 148 | # using the given strftime format. 149 | #html_last_updated_fmt = '%b %d, %Y' 150 | 151 | # If true, SmartyPants will be used to convert quotes and dashes to 152 | # typographically correct entities. 153 | #html_use_smartypants = True 154 | 155 | # Custom sidebar templates, maps document names to template names. 156 | #html_sidebars = {} 157 | 158 | # Additional templates that should be rendered to pages, maps page names to 159 | # template names. 160 | #html_additional_pages = {} 161 | 162 | # If false, no module index is generated. 163 | #html_domain_indices = True 164 | 165 | # If false, no index is generated. 166 | #html_use_index = True 167 | 168 | # If true, the index is split into individual pages for each letter. 169 | #html_split_index = False 170 | 171 | # If true, links to the reST sources are added to the pages. 172 | #html_show_sourcelink = True 173 | 174 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 175 | #html_show_sphinx = True 176 | 177 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 178 | #html_show_copyright = True 179 | 180 | # If true, an OpenSearch description file will be output, and all pages will 181 | # contain a tag referring to it. The value of this option must be the 182 | # base URL from which the finished HTML is served. 183 | #html_use_opensearch = '' 184 | 185 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 186 | #html_file_suffix = None 187 | 188 | # Language to be used for generating the HTML full-text search index. 189 | # Sphinx supports the following languages: 190 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 191 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 192 | #html_search_language = 'en' 193 | 194 | # A dictionary with options for the search language support, empty by default. 195 | # Now only 'ja' uses this config value 196 | #html_search_options = {'type': 'default'} 197 | 198 | # The name of a javascript file (relative to the configuration directory) that 199 | # implements a search results scorer. If empty, the default will be used. 200 | #html_search_scorer = 'scorer.js' 201 | 202 | # Output file base name for HTML help builder. 203 | htmlhelp_basename = 'rsfmodeldoc' 204 | 205 | # -- Options for LaTeX output --------------------------------------------- 206 | 207 | latex_elements = { 208 | # The paper size ('letterpaper' or 'a4paper'). 209 | #'papersize': 'letterpaper', 210 | 211 | # The font size ('10pt', '11pt' or '12pt'). 212 | #'pointsize': '10pt', 213 | 214 | # Additional stuff for the LaTeX preamble. 215 | #'preamble': '', 216 | 217 | # Latex figure (float) alignment 218 | #'figure_align': 'htbp', 219 | } 220 | 221 | # Grouping the document tree into LaTeX files. List of tuples 222 | # (source start file, target name, title, 223 | # author, documentclass [howto, manual, or own class]). 224 | latex_documents = [ 225 | (master_doc, 'rsfmodel.tex', u'rsfmodel Documentation', 226 | u'John R. Leeman', 'manual'), 227 | ] 228 | 229 | # The name of an image file (relative to this directory) to place at the top of 230 | # the title page. 231 | #latex_logo = None 232 | 233 | # For "manual" documents, if this is true, then toplevel headings are parts, 234 | # not chapters. 235 | #latex_use_parts = False 236 | 237 | # If true, show page references after internal links. 238 | #latex_show_pagerefs = False 239 | 240 | # If true, show URL addresses after external links. 241 | #latex_show_urls = False 242 | 243 | # Documents to append as an appendix to all manuals. 244 | #latex_appendices = [] 245 | 246 | # If false, no module index is generated. 247 | #latex_domain_indices = True 248 | 249 | 250 | # -- Options for manual page output --------------------------------------- 251 | 252 | # One entry per manual page. List of tuples 253 | # (source start file, name, description, authors, manual section). 254 | man_pages = [ 255 | (master_doc, 'rsfmodel', u'rsfmodel Documentation', 256 | [author], 1) 257 | ] 258 | 259 | # If true, show URL addresses after external links. 260 | #man_show_urls = False 261 | 262 | 263 | # -- Options for Texinfo output ------------------------------------------- 264 | 265 | # Grouping the document tree into Texinfo files. List of tuples 266 | # (source start file, target name, title, author, 267 | # dir menu entry, description, category) 268 | texinfo_documents = [ 269 | (master_doc, 'rsfmodel', u'rsfmodel Documentation', 270 | author, 'rsfmodel', 'One line description of project.', 271 | 'Miscellaneous'), 272 | ] 273 | 274 | # Documents to append as an appendix to all manuals. 275 | #texinfo_appendices = [] 276 | 277 | # If false, no module index is generated. 278 | #texinfo_domain_indices = True 279 | 280 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 281 | #texinfo_show_urls = 'footnote' 282 | 283 | # If true, do not generate a @detailmenu in the "Top" node's menu. 284 | #texinfo_no_detailmenu = False 285 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. rsfmodel documentation master file, created by 2 | sphinx-quickstart on Sun Aug 9 11:04:50 2015. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to rsfmodel's documentation! 7 | ==================================== 8 | 9 | Here we document the API and installation of rsfmodel. 10 | 11 | Contents: 12 | 13 | .. toctree:: 14 | :maxdepth: 1 15 | 16 | installationguide 17 | api/index 18 | 19 | Indices and tables 20 | ================== 21 | 22 | * :ref:`genindex` 23 | * :ref:`modindex` 24 | * :ref:`search` 25 | -------------------------------------------------------------------------------- /docs/source/installationguide.rst: -------------------------------------------------------------------------------- 1 | ================== 2 | Installation Guide 3 | ================== 4 | 5 | ------------ 6 | Requirements 7 | ------------ 8 | 9 | rsfmodel requires the following packages: 10 | - NumPy >= 1.8.0 11 | - Matplotlib >= 1.4.0 12 | 13 | Installation Instructions for NumPy and SciPy can be found at: 14 | http://www.scipy.org/scipylib/download.html 15 | 16 | Installation Instructions for Matplotlib can be found at: 17 | http://matplotlib.org/downloads.html 18 | 19 | ------------ 20 | Installation 21 | ------------ 22 | 23 | The source code can be downloaded from `GitHub `_. 24 | In the base directory of the download, run: 25 | 26 | .. parsed-literal:: 27 | python setup.py install 28 | 29 | This will install rsfmodel into your current Python installation. 30 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | # Create full conda environment for development, including some useful tools 2 | name: rsfmodel 3 | channels: 4 | - conda-forge/label/testing 5 | - conda-forge 6 | dependencies: 7 | - python>=3.6 8 | - numpy 9 | - scipy 10 | - matplotlib 11 | - sphinx 12 | - sphinx-gallery 13 | - sphinx_rtd_theme 14 | - pytest>=2.4 15 | - pytest-cov 16 | - pytest-flake8 17 | - pytest-mpl 18 | - pytest-runner 19 | - flake8-builtins!=1.4.0 20 | - flake8-comprehensions 21 | - flake8-copyright 22 | - flake8-docstrings 23 | - flake8-import-order 24 | - flake8-mutable 25 | - flake8-pep3101 26 | - flake8-print 27 | - flake8-quotes 28 | - pep8-naming 29 | - doc8 30 | - recommonmark 31 | -------------------------------------------------------------------------------- /examples/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrleeman/rsfmodel/d04d546145f0bde34ca8ab41569788377a0d9d09/examples/.gitkeep -------------------------------------------------------------------------------- /examples/defining_new_state_relation.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from math import log 3 | from rsfmodel import rsf, plot, staterelations 4 | 5 | # This is really just the Ruina realtion, but let's pretend we invented it! 6 | # We'll inherit attributes from rsf.StateRelation, but you wouldn't have to. 7 | # It does provide velocity contribution calculation for us though! 8 | 9 | 10 | class MyStateRelation(staterelations.StateRelation): 11 | # Need to provide a steady state calcualtion method 12 | def set_steady_state(self, system): 13 | self.state = self.Dc/system.vref 14 | 15 | def evolve_state(self, system): 16 | return -1 * (system.v * self.state / self.Dc) * log(system.v * self.state / self.Dc) 17 | 18 | model = rsf.Model() 19 | 20 | # Set model initial conditions 21 | model.mu0 = 0.6 # Friction initial (at the reference velocity) 22 | model.a = 0.01 # Empirical coefficient for the direct effect 23 | model.k = 1e-3 # Normalized System stiffness (friction/micron) 24 | model.v = 1. # Initial slider velocity, generally is vlp(t=0) 25 | model.vref = 1. # Reference velocity, generally vlp(t=0) 26 | 27 | state1 = MyStateRelation() 28 | state1.b = 0.005 # Empirical coefficient for the evolution effect 29 | state1.Dc = 10. # Critical slip distance 30 | 31 | model.state_relations = [state1] # Which state relation we want to use 32 | 33 | # We want to solve for 40 seconds at 100Hz 34 | model.time = np.arange(0, 40.01, 0.01) 35 | 36 | # We want to slide at 1 um/s for 10 s, then at 10 um/s for 31 37 | lp_velocity = np.ones_like(model.time) 38 | lp_velocity[10*100:] = 10. # Velocity after 10 seconds is 10 um/s 39 | 40 | # Set the model load point velocity, must be same shape as model.model_time 41 | model.loadpoint_velocity = lp_velocity 42 | 43 | # Run the model! 44 | model.solve() 45 | 46 | # Make the phase plot 47 | plot.phasePlot(model) 48 | 49 | # Make a plot in displacement 50 | plot.dispPlot(model) 51 | 52 | # Make a plot in time 53 | plot.timePlot(model) 54 | -------------------------------------------------------------------------------- /examples/slide_hold_slide.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from rsfmodel import rsf, staterelations, plot 3 | 4 | model = rsf.Model() 5 | 6 | # Set model initial conditions 7 | model.mu0 = 0.6 # Friction initial (at the reference velocity) 8 | model.a = 0.005 # Empirical coefficient for the direct effect 9 | model.k = 1e-3 # Normalized System stiffness (friction/micron) 10 | model.v = 10. # Initial slider velocity, generally is vlp(t=0) 11 | model.vref = 10. # Reference velocity, generally vlp(t=0) 12 | 13 | state1 = staterelations.DieterichState() 14 | state1.b = 0.01 # Empirical coefficient for the evolution effect 15 | state1.Dc = 10. # Critical slip distance 16 | 17 | model.state_relations = [state1] # Which state relation we want to use 18 | 19 | model.time = np.arange(0,150.01,0.01) 20 | 21 | # We want to slide at 10 um/s for 20 s, hold for 100 s, then slide again 22 | lp_velocity = 10* np.ones_like(model.time) 23 | lp_velocity[20*100:120*100] = 0. # Velocity after 10 seconds is 10 um/s 24 | 25 | # Set the model load point velocity, must be same shape as model.model_time 26 | model.loadpoint_velocity = lp_velocity 27 | 28 | # Run the model! 29 | model.solve() 30 | 31 | # Make the phase plot 32 | plot.phasePlot(model) 33 | 34 | # Make a plot in displacement 35 | plot.dispPlot(model) 36 | 37 | # Make a plot in time 38 | plot.timePlot(model) 39 | -------------------------------------------------------------------------------- /examples/test_examples.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | import contextlib 4 | import glob 5 | import os 6 | import os.path 7 | import sys 8 | try: 9 | from StringIO import StringIO 10 | except ImportError: 11 | from io import StringIO 12 | import traceback 13 | 14 | 15 | @contextlib.contextmanager 16 | def stdoutIO(stdout=None): 17 | old = sys.stdout 18 | if stdout is None: 19 | stdout = StringIO() 20 | sys.stdout = stdout 21 | yield stdout 22 | sys.stdout = old 23 | 24 | # Loop over all scripts, read them in and execute. If *any* exception is raised, 25 | # print it out and print 'FAILED'. If any example fails to run, exit non-zero. 26 | script_dir = os.path.dirname(__file__) 27 | failed_test = False 28 | for fname in glob.glob(os.path.join(script_dir, '*.py')): 29 | if fname == os.path.basename(__file__): 30 | continue 31 | with open(fname) as pysource: 32 | print(fname, '....', sep='', end=' ') 33 | try: 34 | code = compile(pysource.read(), fname, 'exec') 35 | with stdoutIO() as s: 36 | exec(code, dict()) 37 | except Exception as e: 38 | traceback.print_exc() 39 | print("Stdout: ", s.getvalue()) 40 | print('FAILED') 41 | failed_test = True 42 | else: 43 | print('OK') 44 | 45 | sys.exit(failed_test) 46 | -------------------------------------------------------------------------------- /examples/velocity_step_one_state.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from rsfmodel import rsf, staterelations, plot 3 | 4 | model = rsf.Model() 5 | 6 | # Set model initial conditions 7 | model.mu0 = 0.6 # Friction initial (at the reference velocity) 8 | model.a = 0.005 # Empirical coefficient for the direct effect 9 | model.k = 1e-3 # Normalized System stiffness (friction/micron) 10 | model.v = 1. # Initial slider velocity, generally is vlp(t=0) 11 | model.vref = 1. # Reference velocity, generally vlp(t=0) 12 | 13 | state1 = staterelations.DieterichState() 14 | state1.b = 0.01 # Empirical coefficient for the evolution effect 15 | state1.Dc = 10. # Critical slip distance 16 | 17 | model.state_relations = [state1] # Which state relation we want to use 18 | 19 | # We want to solve for 40 seconds at 100Hz 20 | model.time = np.arange(0,40.01,0.01) 21 | 22 | # We want to slide at 1 um/s for 10 s, then at 10 um/s for 31 23 | lp_velocity = np.ones_like(model.time) 24 | lp_velocity[10*100:] = 10. # Velocity after 10 seconds is 10 um/s 25 | 26 | # Set the model load point velocity, must be same shape as model.model_time 27 | model.loadpoint_velocity = lp_velocity 28 | 29 | # Run the model! 30 | model.solve() 31 | 32 | # Make the phase plot 33 | plot.phasePlot(model) 34 | 35 | # Make a plot in displacement 36 | plot.dispPlot(model) 37 | 38 | # Make a plot in time 39 | plot.timePlot(model) 40 | -------------------------------------------------------------------------------- /examples/velocity_step_two_state.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from rsfmodel import rsf, staterelations, plot 3 | 4 | model = rsf.Model() 5 | 6 | # Set model initial conditions 7 | model.mu0 = 0.6 # Friction initial (at the reference velocity) 8 | model.a = 0.005 # Empirical coefficient for the direct effect 9 | model.k = 1e-3 # Normalized System stiffness (friction/micron) 10 | model.v = 1. # Initial slider velocity, generally is vlp(t=0) 11 | model.vref = 1. # Reference velocity, generally vlp(t=0) 12 | 13 | state1 = staterelations.DieterichState() 14 | state1.b = 0.01 # Empirical coefficient for the evolution effect 15 | state1.Dc = 10. # Critical slip distance 16 | 17 | state2 = staterelations.DieterichState() 18 | state2.b = 0.001 # Empirical coefficient for the evolution effect 19 | state2.Dc = 5. # Critical slip distance 20 | 21 | model.state_relations = [state1,state2] # Which state relation we want to use 22 | 23 | # We want to solve for 40 seconds at 100Hz 24 | model.time = np.arange(0,40.01,0.01) 25 | 26 | # We want to slide at 1 um/s for 10 s, then at 10 um/s for 31 27 | lp_velocity = np.ones_like(model.time) 28 | lp_velocity[10*100:] = 10. # Velocity after 10 seconds is 10 um/s 29 | 30 | # Set the model load point velocity, must be same shape as model.model_time 31 | model.loadpoint_velocity = lp_velocity 32 | 33 | # Run the model! 34 | model.solve() 35 | 36 | # Save the model output 37 | model.savetxt('model_output.txt') 38 | 39 | # Make the 2D phase plot 40 | plot.phasePlot(model) 41 | 42 | # Make a 3D phase plot 43 | plot.phasePlot3D(model) 44 | 45 | # Make a plot in displacement 46 | plot.dispPlot(model) 47 | 48 | # Make a plot in time 49 | plot.timePlot(model) 50 | -------------------------------------------------------------------------------- /readthedocs-pip-requirements.txt: -------------------------------------------------------------------------------- 1 | numpydoc==0.5 2 | 3 | -------------------------------------------------------------------------------- /rsfmodel/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrleeman/rsfmodel/d04d546145f0bde34ca8ab41569788377a0d9d09/rsfmodel/__init__.py -------------------------------------------------------------------------------- /rsfmodel/plot.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from mpl_toolkits.mplot3d import Axes3D 4 | 5 | def phasePlot(system, fig=None, ax1=None): 6 | """ Make a phase plot of the current model. 7 | 8 | Parameters 9 | ---------- 10 | system : model object 11 | Model object that has been solved containing results 12 | fig : figure object 13 | Figure object to plot on. New will be created if None. 14 | ax1 : axes object 15 | Axes to plot data on. New will be created if None. 16 | 17 | Returns 18 | ------- 19 | fig : figure object 20 | Figure object that contains plot 21 | ax1 : axes object 22 | Axes object that contains plot 23 | 24 | """ 25 | if fig is None: 26 | fig = plt.figure(figsize=(8, 7)) 27 | 28 | if ax1 is None: 29 | ax1 = plt.subplot(111) 30 | 31 | v_ratio = np.log(system.results.slider_velocity/system.vref) 32 | ax1.plot(v_ratio, system.results.friction, color='k', linewidth=2) 33 | 34 | ylims = ax1.get_ylim() 35 | xlims = ax1.get_xlim() 36 | 37 | # Plot lines of constant a that are in the view 38 | y_line = system.a * np.array(xlims) 39 | for mu in np.arange(0, ylims[1], 0.005): 40 | y_line_plot = y_line + mu 41 | if max(y_line_plot) > ylims[0]: 42 | ax1.plot(xlims, y_line_plot, color='k', linestyle='--') 43 | 44 | # Plot a line of rate dependence "Steady State Line" 45 | state_b_sum = 0 46 | for state in system.state_relations: 47 | state_b_sum += state.b 48 | mu_rate_dependence = system.mu0 + (system.a - state_b_sum)*np.array(xlims) 49 | ax1.plot(xlims, mu_rate_dependence, color='k', linestyle='--') 50 | 51 | ax1.set_xlabel(r'ln$\frac{V}{V_{ref}}$', fontsize=16, labelpad=20) 52 | ax1.set_ylabel(r'$\mu$', fontsize=16) 53 | ax1.set_xlim(xlims) 54 | ax1.set_ylim(ylims) 55 | plt.show() 56 | return fig, ax1 57 | 58 | 59 | def phasePlot3D(system, fig=None, ax1=None, state_variable=2): 60 | """ Make a 3D phase plot of the current model. 61 | 62 | Parameters 63 | ---------- 64 | system : model object 65 | Model object that has been solved containing results 66 | fig : figure object 67 | Figure object to plot on. New will be created if None. 68 | ax1 : axes object 69 | Axes to plot data on. New will be created if None. 70 | state_variable : int 71 | Which state variable will be plotted on the third axis. 72 | 73 | Returns 74 | ------- 75 | fig : figure object 76 | Figure object that contains plot 77 | ax1 : axes object 78 | Axes object that contains plot 79 | """ 80 | 81 | if np.shape(system.results.states)[1] < 2: 82 | raise ValueError('Must be a multi state-variable system for 3D plotting') 83 | 84 | if fig is None: 85 | fig = plt.figure(figsize=(8, 7)) 86 | 87 | if ax1 is None: 88 | ax1 = fig.gca(projection='3d') 89 | 90 | v_ratio = np.log(system.results.slider_velocity/system.vref) 91 | ax1.plot(v_ratio, system.results.states[:, state_variable-1], 92 | system.results.friction, color='k', linewidth=2) 93 | 94 | ax1.set_xlabel(r'ln$\frac{V}{V_{ref}}$', fontsize=16) 95 | ax1.set_ylabel(r'$\theta_%d$' % state_variable, fontsize=16) 96 | ax1.set_zlabel(r'$\mu$', fontsize=16) 97 | ax1.xaxis._axinfo['label']['space_factor'] = 2.5 98 | ax1.yaxis._axinfo['label']['space_factor'] = 2. 99 | ax1.zaxis._axinfo['label']['space_factor'] = 2. 100 | plt.show() 101 | return fig, ax1 102 | 103 | 104 | def dispPlot(system): 105 | """ Make a standard plot with displacement as the x variable 106 | 107 | Parameters 108 | ---------- 109 | system : model object 110 | Model object that has been solved containing results 111 | 112 | Returns 113 | ------- 114 | fig : figure object 115 | Figure object that contains plot 116 | ax1 : axes object 117 | Axes object that contains plot 118 | ax2 : axes object 119 | Axes object that contains plot 120 | ax3 : axes object 121 | Axes object that contains plot 122 | ax4 : axes object 123 | Axes object that contains plot 124 | """ 125 | 126 | fig = plt.figure(figsize=(12, 9)) 127 | ax1 = plt.subplot(411) 128 | ax2 = plt.subplot(412, sharex=ax1) 129 | ax3 = plt.subplot(413, sharex=ax1) 130 | ax4 = plt.subplot(414, sharex=ax1) 131 | ax1.plot(system.results.loadpoint_displacement, system.results.friction, color='k') 132 | ax2.plot(system.results.loadpoint_displacement, system.results.states, color='k') 133 | ax3.plot(system.results.loadpoint_displacement, system.results.slider_velocity, color='k') 134 | ax4.plot(system.results.loadpoint_displacement, system.loadpoint_velocity, color='k') 135 | ax1.set_ylabel('Friction') 136 | ax2.set_ylabel('State') 137 | ax3.set_ylabel('Slider Velocity') 138 | ax4.set_ylabel('Loadpoint Velocity') 139 | ax4.set_xlabel('Displacement') 140 | plt.show() 141 | return fig, ax1, ax2, ax3, ax4 142 | 143 | def timePlot(system): 144 | """ Make a standard plot with time as the x variable 145 | 146 | Parameters 147 | ---------- 148 | system : model object 149 | Model object that has been solved containing results 150 | 151 | Returns 152 | ------- 153 | fig : figure object 154 | Figure object that contains plot 155 | ax1 : axes object 156 | Axes object that contains plot 157 | ax2 : axes object 158 | Axes object that contains plot 159 | ax3 : axes object 160 | Axes object that contains plot 161 | ax4 : axes object 162 | Axes object that contains plot 163 | """ 164 | fig = plt.figure(figsize=(12, 9)) 165 | ax1 = plt.subplot(411) 166 | ax2 = plt.subplot(412, sharex=ax1) 167 | ax3 = plt.subplot(413, sharex=ax1) 168 | ax4 = plt.subplot(414, sharex=ax1) 169 | ax1.plot(system.results.time, system.results.friction, color='k') 170 | ax2.plot(system.results.time, system.results.states, color='k') 171 | ax3.plot(system.results.time, system.results.slider_velocity, color='k') 172 | ax4.plot(system.results.time, system.loadpoint_velocity, color='k') 173 | ax1.set_ylabel('Friction') 174 | ax2.set_ylabel('State') 175 | ax3.set_ylabel('Slider Velocity') 176 | ax4.set_ylabel('Loadpoint Velocity') 177 | ax4.set_xlabel('Time') 178 | plt.show() 179 | return fig, ax1, ax2, ax3, ax4 180 | -------------------------------------------------------------------------------- /rsfmodel/rsf.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy import integrate 3 | from math import exp 4 | from collections import namedtuple 5 | import warnings 6 | 7 | 8 | class IncompleteModelError(Exception): 9 | """ 10 | Special error case for trying to run the model with inadequate information. 11 | """ 12 | pass 13 | 14 | 15 | class LoadingSystem(object): 16 | """ 17 | Contains attributes relating to the external loading system. 18 | 19 | Attributes 20 | ---------- 21 | k : float 22 | System stiffness in units of fricton/displacement. 23 | time : list 24 | List of time values at which the system will be solved. 25 | loadpoint_velocity : list 26 | List of the imposed loadpoint velocities at the corresponding time 27 | value. Must be same length as time. 28 | v : float 29 | Slider velocity 30 | mu : float 31 | The current friciton value of the system. 32 | """ 33 | def __init__(self): 34 | self.k = None 35 | self.time = None # List of times we want answers at 36 | self.loadpoint_velocity = None # Matching list of velocities 37 | 38 | def velocity_evolution(self): 39 | v_contribution = 0 40 | for state in self.state_relations: 41 | v_contribution += state.velocity_component(self) 42 | self.v = self.vref * exp((self.mu - self.mu0 - v_contribution) / self.a) 43 | 44 | def friction_evolution(self, loadpoint_vel): 45 | return self.k * (loadpoint_vel - self.v) 46 | 47 | 48 | class Model(LoadingSystem): 49 | """ 50 | Houses the model coefficients and does the integration. 51 | 52 | Attributes 53 | ---------- 54 | mu0 : float 55 | Reference friction value at vref. 56 | a : float 57 | Rate and state constitutive parameter. 58 | vref : float 59 | System reference velocity at which the reference friction is measured. 60 | state_relations : list 61 | List of state relations to be used when calculating the model. 62 | results : namedtuple 63 | Stores all model outputs. 64 | """ 65 | def __init__(self): 66 | LoadingSystem.__init__(self) 67 | self.mu0 = 0.6 68 | self.a = None 69 | self.vref = None 70 | self.state_relations = [] 71 | self.results = namedtuple("results", ["time", "loadpoint_displacement", 72 | "slider_velocity", "friction", 73 | "states", "slider_displacement"]) 74 | 75 | 76 | def savetxt(self, fname, line_ending='\n'): 77 | """ Save the output of the model to a csv file. 78 | """ 79 | with open(fname, 'w') as f: 80 | # Model Properties 81 | f.write(f'mu0 = {self.mu0}{line_ending}') 82 | f.write(f'a = {self.a}{line_ending}') 83 | f.write(f'vref = {self.vref}{line_ending}') 84 | f.write(f'k = {self.k}{line_ending}') 85 | for i, state in enumerate(self.state_relations): 86 | f.write(f'State {i + 1}{line_ending}') 87 | f.write(str(state)) 88 | 89 | # Header 90 | f.write('Time,Loadpoint_Displacement,Slider_Velocity,Friction,') 91 | f.write(f'Slider_Displacement') 92 | for i, state in enumerate(self.state_relations): 93 | f.write(f',State_{i + 1}') 94 | f.write(line_ending) 95 | 96 | # Data 97 | i = 0 98 | for row in zip(self.results.time, 99 | self.results.loadpoint_displacement, 100 | self.results.slider_velocity, 101 | self.results.friction, 102 | self.results.slider_displacement): 103 | t, lp_disp, s_vel, fric, s_disp = row 104 | f.write(f'{t},{lp_disp},{s_vel},{fric},{s_disp}') 105 | for state in self.state_relations: 106 | f.write(f',{state.state[i]}') 107 | f.write(f'{line_ending}') 108 | i += 1 109 | 110 | 111 | def _integrationStep(self, w, t, system): 112 | """ Do the calculation for a time-step 113 | 114 | Parameters 115 | ---------- 116 | w : list 117 | Current values of integration variables. Friction first, state variables following. 118 | t : float 119 | Time at which integration is occurring 120 | system : model object 121 | Model that is being solved 122 | 123 | Returns 124 | ------- 125 | step_results : list 126 | Results of the integration step. dmu/dt first, followed by dtheta/dt for state variables. 127 | """ 128 | 129 | system.mu = w[0] 130 | for i, state_variable in enumerate(system.state_relations): 131 | state_variable.state = w[i+1] 132 | 133 | system.velocity_evolution() 134 | 135 | # Find the loadpoint_velocity corresponding to the most recent time 136 | # <= the current time. 137 | loadpoint_vel = system.loadpoint_velocity[system.time <= t][-1] 138 | 139 | self.dmu_dt = system.friction_evolution(loadpoint_vel) 140 | step_results = [self.dmu_dt] 141 | 142 | for state_variable in system.state_relations: 143 | dtheta_dt = state_variable.evolve_state(self) 144 | step_results.append(dtheta_dt) 145 | 146 | return step_results 147 | 148 | def readyCheck(self): 149 | """ 150 | Determines if all necessary parameters are set to run the model. 151 | Will raise appropriate error as necessary. 152 | """ 153 | if self.a is None: 154 | raise IncompleteModelError('Parameter a is None') 155 | elif self.vref is None: 156 | raise IncompleteModelError('Parameter vref is None') 157 | elif self.state_relations == []: 158 | raise IncompleteModelError('No state relations in state_relations') 159 | elif self.k is None: 160 | raise IncompleteModelError('Parameter k is None') 161 | elif self.time is None: 162 | raise IncompleteModelError('Parameter time is None') 163 | elif self.loadpoint_velocity is None: 164 | raise IncompleteModelError('Parameter loadpoint_velocity is not set') 165 | 166 | for state_relation in self.state_relations: 167 | if state_relation.b is None: 168 | raise IncompleteModelError('Parameter b is None') 169 | elif state_relation.Dc is None: 170 | raise IncompleteModelError('Parameter Dc is None') 171 | 172 | if len(self.time) != len(self.loadpoint_velocity): 173 | raise IncompleteModelError('Time and loadpoint_velocity lengths do not match') 174 | 175 | def _get_critical_times(self, threshold): 176 | """ 177 | Calculates accelearation and thresholds based on that to find areas 178 | that are likely problematic to integrate. 179 | 180 | Parameters 181 | ---------- 182 | threshold : float 183 | When the absolute value of acceleration exceeds this value, the 184 | time is marked as "critical" for integration. 185 | 186 | Returns 187 | ------- 188 | critical_times : list 189 | List of time values at which integration care should be taken. 190 | """ 191 | velocity_gradient = np.gradient(self.loadpoint_velocity) 192 | time_gradient = np.gradient(self.time) 193 | acceleration = velocity_gradient / time_gradient 194 | critical_times = self.time[np.abs(acceleration) > threshold] 195 | return critical_times 196 | 197 | def solve(self, threshold=2, **kwargs): 198 | """ 199 | Runs the integrator to actually solve the model and returns a 200 | named tuple of results. 201 | 202 | Parameters 203 | ---------- 204 | threshold : float 205 | Threshold used to determine when integration care should be taken. This threshold is 206 | in terms of maximum load-point acceleration before time step is marked. 207 | 208 | Returns 209 | ------- 210 | results : named tuple 211 | Results of the model 212 | """ 213 | odeint_kwargs = dict(rtol=1e-12, atol=1e-12, mxstep=5000) 214 | odeint_kwargs.update(kwargs) 215 | 216 | # Make sure we have everything set before we try to run 217 | self.readyCheck() 218 | 219 | # Initial conditions at t = 0 220 | w0 = [self.mu0] 221 | for state_variable in self.state_relations: 222 | state_variable.set_steady_state(self) 223 | w0.append(state_variable.state) 224 | 225 | # Find any critial time points we need to let the integrator know about 226 | self.critical_times = self._get_critical_times(threshold) 227 | 228 | # Solve it 229 | wsol, self.solver_info = integrate.odeint(self._integrationStep, w0, self.time, 230 | full_output=True, tcrit=self.critical_times, 231 | args=(self,), **odeint_kwargs) 232 | 233 | self.results.friction = wsol[:, 0] 234 | self.results.states = wsol[:, 1:] 235 | self.results.time = self.time 236 | 237 | # Calculate slider velocity after we have solved everything 238 | velocity_contribution = 0 239 | for i, state_variable in enumerate(self.state_relations): 240 | state_variable.state = wsol[:, i+1] 241 | velocity_contribution += state_variable.velocity_component(self) 242 | 243 | self.results.slider_velocity = self.vref * np.exp( 244 | (self.results.friction - self.mu0 - 245 | velocity_contribution) / self.a) 246 | 247 | # Calculate displacement from velocity and dt 248 | self.results.loadpoint_displacement = \ 249 | self._calculateDiscreteDisplacement(self.loadpoint_velocity) 250 | 251 | # Calculate the slider displacement 252 | self.results.slider_displacement = \ 253 | self._calculateContinuousDisplacement(self.results.slider_velocity) 254 | 255 | # Check slider displacement for accumulated error and warn 256 | if not self._check_slider_displacement(): 257 | warnings.warn("Slider displacement differs from prediction by over " 258 | "1%. Smaller requested time resolution should be used " 259 | "If you intend to use the slider displacment output.") 260 | 261 | return self.results 262 | 263 | def _check_slider_displacement(self, tol=0.01): 264 | """ 265 | Checks that the slider displacement total is within a given tolerance 266 | of the prediction from steady-state theory. Defaults to 1%. 267 | 268 | Parameters 269 | ---------- 270 | tol : float 271 | Maximum error tolerated before returns False. 272 | 273 | Returns 274 | ------- 275 | Is Within Tolerance : boolean 276 | False if out of tolerance, true if in tolerance. 277 | """ 278 | a_minus_b = self.a 279 | for state_relation in self.state_relations: 280 | a_minus_b -= state_relation.b 281 | 282 | dmu = a_minus_b * np.log(self.results.slider_velocity[-1]/self.vref) 283 | dx = -dmu/self.k 284 | 285 | predicted_slider_displacement = self.results.loadpoint_displacement[-1] + dx 286 | actual_slider_diaplacement = self.results.slider_displacement[-1] 287 | 288 | difference = np.abs(predicted_slider_displacement - actual_slider_diaplacement)/\ 289 | predicted_slider_displacement 290 | 291 | if difference > tol: 292 | return False 293 | else: 294 | return True 295 | 296 | def _calculateContinuousDisplacement(self, velocity): 297 | """ 298 | Calculate the displacement of the slider by integrating the velocity. 299 | 300 | Parameters 301 | ---------- 302 | velocity : list 303 | List of velocity values. 304 | 305 | Returns 306 | ------- 307 | displacement : ndarray 308 | List of displacement values. 309 | """ 310 | return integrate.cumtrapz(velocity, self.time, initial=0) 311 | 312 | def _calculateDiscreteDisplacement(self, velocity): 313 | """ 314 | Calculate displacement in a discrete way that returns an equal size 315 | result. 316 | 317 | Parameters 318 | ---------- 319 | velocity : list 320 | List of velocity values. 321 | 322 | Returns 323 | ------- 324 | displacement : ndarray 325 | List of displacement values. 326 | """ 327 | dt = np.ediff1d(self.results.time) 328 | displacement = np.cumsum(velocity[:-1] * dt) 329 | displacement = np.insert(displacement, 0, 0) 330 | return displacement 331 | -------------------------------------------------------------------------------- /rsfmodel/staterelations.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from math import log 3 | 4 | class StateRelation(object): 5 | """ 6 | Abstract state relation object that contains the generally used atributes 7 | in state relations (b,Dc). 8 | 9 | Attributes 10 | ---------- 11 | b : float 12 | Rate and state empirical parameter b. 13 | Dc : float 14 | Critical slip distance. 15 | state : float 16 | State variable. 17 | """ 18 | def __init__(self): 19 | self.b = None 20 | self.Dc = None 21 | self.state = None 22 | 23 | def velocity_component(self, system): 24 | """ 25 | General velocity contribution from a given state variable 26 | 27 | Notes 28 | ----- 29 | .. math:: 30 | V_\\text{contribution} = b \\text{ln}\\left(\\frac{V_0 \\theta}{D_c}\\right) 31 | """ 32 | return self.b * np.log(system.vref * self.state / self.Dc) 33 | 34 | 35 | class DieterichState(StateRelation): 36 | """ 37 | The slowness or Dieterich state relation as proposed by [#Dieterich1979]_. 38 | 39 | Notes 40 | ----- 41 | .. math:: 42 | \\frac{d\\theta}{dt} = 1 - \\frac{V_\\text{slider} \\theta}{D_c} 43 | 44 | .. [#Dieterich1979] Dieterich, J. "Modeling of rock friction: 1. Experimental 45 | results and constitutive equations." Journal of Geophysical 46 | Research: Solid Earth (19782012) 84.B5 (1979): 2161-2168. 47 | """ 48 | def __str__(self): 49 | s = 'Dieterich State Relation\n' 50 | s += f'b = {self.b}\n' 51 | s += f'Dc = {self.Dc}\n' 52 | return s 53 | 54 | def set_steady_state(self, system): 55 | self.state = self.Dc/system.vref 56 | 57 | def evolve_state(self, system): 58 | return 1. - system.v * self.state / self.Dc 59 | 60 | 61 | class RuinaState(StateRelation): 62 | """ 63 | The slip or Ruina state relation as proposed by [#Ruina1983]_. 64 | 65 | Notes 66 | ----- 67 | .. math:: 68 | \\frac{d\theta}{dt} = -\\frac{V_\\text{slider} \\theta}{D_c} 69 | \\text{ln}\left(\\frac{V_\\text{slider} \\theta}{D_c}\\right) 70 | 71 | .. [#Ruina1983] Ruina, Andy. "Slip instability and state variable friction laws." 72 | J. geophys. Res 88.10 (1983): 359-10. 73 | """ 74 | def __str__(self): 75 | s = 'Ruina State Relation\n' 76 | s += f'b = {self.b}\n' 77 | s += f'Dc = {self.Dc}\n' 78 | return s 79 | 80 | def set_steady_state(self, system): 81 | self.state = self.Dc/system.vref 82 | 83 | def evolve_state(self, system): 84 | return -1 * (system.v * self.state / self.Dc) * log(system.v * self.state / self.Dc) 85 | 86 | 87 | class PrzState(StateRelation): 88 | """ 89 | The PRZ state relation as proposed by [#PRZ1995]_: 90 | 91 | Notes 92 | ----- 93 | .. math:: 94 | \\frac{d\\theta}{dt} = 1 - \left(\\frac{V_\\text{slider} \\theta}{2D_c}\\right) ^2 95 | 96 | .. [#PRZ1995] Perrin, G., Rice, J., and Zheng, G. 97 | "Self-healing slip pulse on a frictional surface." 98 | Journal of the Mechanics and Physics of Solids 43.9 (1995): 1461-1495. 99 | """ 100 | def __str__(self): 101 | s = 'PRZ State Relation\n' 102 | s += f'b = {self.b}\n' 103 | s += f'Dc = {self.Dc}\n' 104 | return s 105 | 106 | def set_steady_state(self, system): 107 | self.state = 2 * self.Dc / system.v 108 | self.prz_vref = system.vref/(2*self.Dc) 109 | 110 | def evolve_state(self, system): 111 | return 1. - (system.v * self.state / (2 * self.Dc))**2 112 | 113 | def velocity_component(self, system): 114 | """ 115 | Perrin-Rice velocity contribution 116 | 117 | .. math:: 118 | V_\\text{contribution} = b \\text{ln}\left(V_{\\text{prz}0} \\theta\\right) 119 | """ 120 | return self.b * np.log(self.prz_vref * self.state) 121 | 122 | 123 | class NagataState(StateRelation): 124 | """ 125 | The Nagata state relation as proposed by [#Nagata2012]_: 126 | 127 | Notes 128 | ----- 129 | .. math:: 130 | \\frac{d\\theta}{dt} = 1 - \\frac{V_\\text{slider} \\theta}{D_c} 131 | - \\frac{c}{b}\\theta\\frac{d\mu}{dt} 132 | 133 | .. [#Nagata2012] Nagata, K., Nakatani, M., Yoshida, S., "A revised rate-and-state 134 | -dependent friction law obtained by constraining constitutive and 135 | evolution laws separately with laboratory data," Journal of Geophysical 136 | Research: Solid Earth, vol 117, 2012. 137 | """ 138 | def __str__(self): 139 | s = 'Nagata State Relation\n' 140 | s += f'b = {self.b}\n' 141 | s += f'c = {self.c}\n' 142 | s += f'Dc = {self.Dc}\n' 143 | return s 144 | 145 | def __init__(self): 146 | StateRelation.__init__(self) 147 | self.c = None 148 | 149 | def set_steady_state(self, system): 150 | self.state = self.Dc / system.vref 151 | 152 | def evolve_state(self, system): 153 | return 1. - (system.v * self.state / self.Dc) - \ 154 | (self.c / self.b * self.state * system.dmu_dt) 155 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | try: 2 | from setuptools import setup 3 | except ImportError: 4 | from distutils.core import setup 5 | 6 | config = { 7 | 'description': 'rsfmodel', 8 | 'author': 'John R. Leeman, Ryan May', 9 | 'url': 'http://github.com/jrleeman/rsfmodel', 10 | 'download_url': 'http://github.com/jrleeman/rsfmodel', 11 | 'author_email': 'kd5wxb@gmail.com', 12 | 'version': '0.2', 13 | 'install_requires': ['nose'], 14 | 'packages': ['rsfmodel'], 15 | 'scripts': [], 16 | 'name': 'rsfmodel' 17 | } 18 | 19 | setup(**config) 20 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrleeman/rsfmodel/d04d546145f0bde34ca8ab41569788377a0d9d09/tests/__init__.py -------------------------------------------------------------------------------- /tests/plot_tests.py: -------------------------------------------------------------------------------- 1 | from nose.tools import * 2 | import matplotlib 3 | matplotlib.use('agg') 4 | from rsfmodel import rsf, staterelations, plot 5 | from rsfmodel.rsf import IncompleteModelError 6 | import numpy as np 7 | 8 | class TestDeiterichOneStateVar(object): 9 | 10 | def setup(self): 11 | self.model = rsf.Model() 12 | self.model.mu0 = 0.6 13 | self.model.a = 0.005 14 | self.model.k = 1e-3 15 | self.model.v = 1. 16 | self.model.vref = 1. 17 | state1 = staterelations.DieterichState() 18 | state1.b = 0.01 19 | state1.Dc = 10. 20 | self.model.state_relations = [state1] 21 | self.model.time = np.arange(0, 40.01, 1.) 22 | lp_velocity = np.ones_like(self.model.time) 23 | lp_velocity[10*1:] = 10. 24 | self.model.loadpoint_velocity = lp_velocity 25 | self.model.solve() 26 | 27 | def test_phaseplot(self): 28 | plot.phasePlot(self.model) 29 | 30 | @raises(ValueError) 31 | def test_phaseplot3D(self): 32 | plot.phasePlot3D(self.model) 33 | 34 | def test_dispplot(self): 35 | plot.dispPlot(self.model) 36 | 37 | def test_timeplot(self): 38 | plot.timePlot(self.model) 39 | 40 | 41 | class TestDeiterichTwoStateVar(object): 42 | 43 | def setup(self): 44 | self.model = rsf.Model() 45 | self.model.mu0 = 0.6 46 | self.model.a = 0.012 47 | self.model.k = 3e-3 48 | self.model.v = 1. 49 | self.model.vref = 1. 50 | state1 = staterelations.DieterichState() 51 | state1.b = 0.0185 52 | state1.Dc = 5. 53 | 54 | state2 = staterelations.DieterichState() 55 | state2.b = 0.0088 56 | state2.Dc = 50. 57 | self.model.state_relations = [state1, state2] 58 | self.model.time = np.arange(0, 40.01, 1.) 59 | lp_velocity = np.ones_like(self.model.time) 60 | lp_velocity[10*1:] = 10. 61 | self.model.loadpoint_velocity = lp_velocity 62 | self.model.solve() 63 | 64 | def test_phaseplot(self): 65 | plot.phasePlot(self.model) 66 | 67 | def test_phaseplot3D(self): 68 | plot.phasePlot3D(self.model) 69 | 70 | def test_dispplot(self): 71 | plot.dispPlot(self.model) 72 | 73 | def test_timeplot(self): 74 | plot.timePlot(self.model) 75 | 76 | def test_friction(self): 77 | truth = np.array( 78 | [0.6, 0.6, 0.6, 0.6, 79 | 0.6, 0.6, 0.6, 0.6, 80 | 0.6, 0.6, 0.6, 0.61777816, 81 | 0.56719338, 0.57926411, 0.58134415, 0.5741353, 82 | 0.57495495, 0.57393578, 0.57211002, 0.5714098, 83 | 0.57046462, 0.56953958, 0.56884951, 0.56819716, 84 | 0.56762505, 0.56715188, 0.56674236, 0.5663961, 85 | 0.56610841, 0.56586767, 0.565668, 0.56550353, 86 | 0.56536809, 0.56525698, 0.56516614, 0.56509194, 87 | 0.56503146, 0.56498223, 0.5649422, 0.56490967, 88 | 0.56488327]) 89 | 90 | np.testing.assert_almost_equal(self.model.results.friction, truth, 8) 91 | 92 | def test_state1(self): 93 | truth = np.array( 94 | [5., 5., 5., 5., 95 | 5., 5., 5., 5., 96 | 5., 5., 5., 2.80081321, 97 | 0.26257732, 0.67749062, 0.5128508, 0.43041648, 98 | 0.506089, 0.47944593, 0.47554365, 0.48734539, 99 | 0.4843392, 0.48564166, 0.48855901, 0.48933082, 100 | 0.49072826, 0.4922033, 0.49326055, 0.49430172, 101 | 0.4952396, 0.49601255, 0.49668913, 0.4972666, 102 | 0.49774673, 0.49815004, 0.49848567, 0.49876233, 103 | 0.4989905, 0.4991779, 0.4993312, 0.49945649, 104 | 0.49955867]) 105 | 106 | np.testing.assert_almost_equal(self.model.results.states[:, 0], 107 | truth, 8) 108 | 109 | def test_state2(self): 110 | truth = np.array( 111 | [50., 50., 50., 50., 112 | 50., 50., 50., 50., 113 | 50., 50., 50., 47.03349418, 114 | 28.26143675, 26.02288006, 22.50536043, 18.45103796, 115 | 16.09903009, 13.99322929, 12.21984746, 10.8626045, 116 | 9.74109978, 8.83011044, 8.10068484, 7.50795792, 117 | 7.02833114, 6.64119123, 6.32770427, 6.07410906, 118 | 5.86906354, 5.70314217, 5.56890919, 5.46032285, 119 | 5.37246346, 5.30137737, 5.243863, 5.1973259, 120 | 5.1596709, 5.12920263, 5.1045488, 5.08459968, 121 | 5.06845739]) 122 | 123 | np.testing.assert_almost_equal(self.model.results.states[:, 1], 124 | truth, 8) 125 | 126 | def test_slider_velocity(self): 127 | truth = np.array( 128 | [1., 1., 1., 1., 129 | 1., 1., 1., 1., 130 | 1., 1., 1., 11.24366669, 131 | 9.27431239, 6.24880306, 12.6979352, 10.55410214, 132 | 9.72918973, 10.76583201, 10.34192569, 10.2410124, 133 | 10.3510415, 10.25600829, 10.22005129, 10.20908552, 134 | 10.17177535, 10.1462882, 10.126265, 10.10491673, 135 | 10.08761385, 10.07313912, 10.0603132, 10.04968798, 136 | 10.04085724, 10.03343832, 10.02732652, 10.02229955, 137 | 10.01816004, 10.01477376, 10.01200822, 10.00975089, 138 | 10.00791305]) 139 | 140 | np.testing.assert_almost_equal(self.model.results.slider_velocity, 141 | truth, 8) 142 | 143 | def test_time(self): 144 | truth = np.array( 145 | [0., 1., 2., 3., 146 | 4., 5., 6., 7., 147 | 8., 9., 10., 11., 148 | 12., 13., 14., 15., 149 | 16., 17., 18., 19., 150 | 20., 21., 22., 23., 151 | 24., 25., 26., 27., 152 | 28., 29., 30., 31., 153 | 32., 33., 34., 35., 154 | 36., 37., 38., 39., 155 | 40.]) 156 | 157 | np.testing.assert_almost_equal(self.model.results.time, truth, 8) 158 | 159 | def test_loadpoint_displacement(self): 160 | truth = np.array( 161 | [0., 1., 2., 3., 162 | 4., 5., 6., 7., 163 | 8., 9., 10., 20., 164 | 30., 40., 50., 60., 165 | 70., 80., 90., 100., 166 | 110., 120., 130., 140., 167 | 150., 160., 170., 180., 168 | 190., 200., 210., 220., 169 | 230., 240., 250., 260., 170 | 270., 280., 290., 300., 171 | 310.]) 172 | 173 | np.testing.assert_almost_equal(self.model.results. 174 | loadpoint_displacement, truth, 8) 175 | -------------------------------------------------------------------------------- /tests/rsf_tests.py: -------------------------------------------------------------------------------- 1 | from nose.tools import * 2 | from rsfmodel import rsf, staterelations 3 | from rsfmodel.rsf import IncompleteModelError 4 | import numpy as np 5 | 6 | 7 | class TestDeiterichOneStateVar(object): 8 | 9 | def setup(self): 10 | self.model = rsf.Model() 11 | self.model.mu0 = 0.6 12 | self.model.a = 0.005 13 | self.model.k = 1e-3 14 | self.model.v = 1. 15 | self.model.vref = 1. 16 | state1 = staterelations.DieterichState() 17 | state1.b = 0.01 18 | state1.Dc = 10. 19 | self.model.state_relations = [state1] 20 | self.model.time = np.arange(0, 40.01, 1.) 21 | lp_velocity = np.ones_like(self.model.time) 22 | lp_velocity[10*1:] = 10. 23 | self.model.loadpoint_velocity = lp_velocity 24 | self.model.solve() 25 | 26 | def test_friction(self): 27 | truth = np.array( 28 | [0.6, 0.6, 0.6, 0.6, 29 | 0.6, 0.6, 0.6, 0.6, 30 | 0.6, 0.6, 0.6, 0.60720485, 31 | 0.60186637, 0.5840091, 0.58563096, 0.58899543, 32 | 0.58998186, 0.58879751, 0.58803412, 0.58825653, 33 | 0.58858313, 0.58860319, 0.58848625, 0.58844431, 34 | 0.58847472, 0.58849913, 0.58849522, 0.58848506, 35 | 0.58848348, 0.58848674, 0.5884883, 0.58848756, 36 | 0.58848677, 0.5884868, 0.58848711, 0.58848718, 37 | 0.5884871, 0.58848704, 0.58848706, 0.58848708, 38 | 0.58848708]) 39 | 40 | np.testing.assert_almost_equal(self.model.results.friction, truth, 8) 41 | 42 | def test_state(self): 43 | truth = np.array( 44 | [10., 10., 10., 10., 45 | 10., 10., 10., 10., 46 | 10., 10., 10., 8.39993716, 47 | 2.2355129, 0.54248237, 0.94936884, 1.21163659, 48 | 1.13491241, 0.96889358, 0.94896281, 0.99777122, 49 | 1.01876822, 1.00674821, 0.99515136, 0.9961297, 50 | 1.00063816, 1.00161995, 1.00024914, 0.9994787, 51 | 0.99975352, 1.00011844, 1.00012598, 0.99999394, 52 | 0.99995185, 0.9999879, 1.00001411, 1.00000865, 53 | 0.99999738, 0.99999604, 0.99999978, 1.00000139, 54 | 1.00000049]) 55 | 56 | np.testing.assert_almost_equal(self.model.results.states, 57 | truth.reshape((41, 1)), 8) 58 | 59 | def test_slider_velocity(self): 60 | truth = np.array( 61 | [1., 1., 1., 1., 62 | 1., 1., 1., 1., 63 | 1., 1., 1., 5.98760938, 64 | 29.06408439, 13.87639287, 6.26688086, 7.54067754, 65 | 10.46913416, 11.33475717, 10.14281987, 9.59208664, 66 | 9.82182937, 10.0981912, 10.09601357, 9.99203098, 67 | 9.9626088, 9.99173502, 10.01132361, 10.00641151, 68 | 9.99774235, 9.99697087, 9.99992839, 10.00109331, 69 | 10.00035175, 9.99970252, 9.99978205, 10.00004456, 70 | 10.00009344, 10.00001142, 9.99996902, 9.99998658, 71 | 10.0000074]) 72 | 73 | np.testing.assert_almost_equal(self.model.results.slider_velocity, 74 | truth, 8) 75 | 76 | def test_time(self): 77 | truth = np.array( 78 | [0., 1., 2., 3., 79 | 4., 5., 6., 7., 80 | 8., 9., 10., 11., 81 | 12., 13., 14., 15., 82 | 16., 17., 18., 19., 83 | 20., 21., 22., 23., 84 | 24., 25., 26., 27., 85 | 28., 29., 30., 31., 86 | 32., 33., 34., 35., 87 | 36., 37., 38., 39., 88 | 40.]) 89 | 90 | np.testing.assert_almost_equal(self.model.results.time, truth, 8) 91 | 92 | def test_loadpoint_displacement(self): 93 | truth = np.array( 94 | [0., 1., 2., 3., 95 | 4., 5., 6., 7., 96 | 8., 9., 10., 20., 97 | 30., 40., 50., 60., 98 | 70., 80., 90., 100., 99 | 110., 120., 130., 140., 100 | 150., 160., 170., 180., 101 | 190., 200., 210., 220., 102 | 230., 240., 250., 260., 103 | 270., 280., 290., 300., 104 | 310.]) 105 | 106 | np.testing.assert_almost_equal(self.model.results. 107 | loadpoint_displacement, truth, 8) 108 | 109 | 110 | class TestRuinaOneStateVar(object): 111 | 112 | def setup(self): 113 | self.model = rsf.Model() 114 | self.model.mu0 = 0.6 115 | self.model.a = 0.01 116 | self.model.k = 1e-3 117 | self.model.v = 1. 118 | self.model.vref = 1. 119 | state1 = staterelations.RuinaState() 120 | state1.b = 0.005 121 | state1.Dc = 10. 122 | self.model.state_relations = [state1] 123 | self.model.time = np.arange(0, 40.01, 1.) 124 | lp_velocity = np.ones_like(self.model.time) 125 | lp_velocity[10*1:] = 10. 126 | self.model.loadpoint_velocity = lp_velocity 127 | self.model.solve() 128 | 129 | def test_friction(self): 130 | truth = np.array( 131 | [0.6, 0.6, 0.6, 0.6, 132 | 0.6, 0.6, 0.6, 0.6, 133 | 0.6, 0.6, 0.6, 0.60839299, 134 | 0.6146288, 0.61663335, 0.61451424, 0.61239234, 135 | 0.61152559, 0.61133979, 0.61138186, 0.61145348, 136 | 0.61149781, 0.61151494, 0.61151781, 0.61151612, 137 | 0.61151421, 0.61151317, 0.61151282, 0.61151279, 138 | 0.61151285, 0.6115129, 0.61151292, 0.61151293, 139 | 0.61151293, 0.61151293, 0.61151293, 0.61151293, 140 | 0.61151293, 0.61151293, 0.61151293, 0.61151293, 141 | 0.61151293]) 142 | 143 | np.testing.assert_almost_equal(self.model.results.friction, truth, 8) 144 | 145 | def test_state(self): 146 | truth = np.array( 147 | [10., 10., 10., 10., 148 | 10., 10., 10., 10., 149 | 10., 10., 10., 9.26098513, 150 | 6.15593237, 2.47810827, 1.1310728, 0.91285565, 151 | 0.92248181, 0.96185653, 0.98881543, 1.00027401, 152 | 1.00273927, 1.00198602, 1.00086923, 1.00020489, 153 | 0.99995887, 0.99992364, 0.99995225, 0.99998144, 154 | 0.99999682, 1.00000177, 1.00000203, 1.00000112, 155 | 1.00000038, 1.00000003, 0.99999994, 0.99999995, 156 | 0.99999997, 0.99999999, 1., 1., 1.]) 157 | 158 | np.testing.assert_almost_equal(self.model.results.states, 159 | truth.reshape((41, 1)), 8) 160 | 161 | def test_slider_velocity(self): 162 | truth = np.array( 163 | [1., 1., 1., 1., 164 | 1., 1., 1., 1., 165 | 1., 1., 1., 2.40532918, 166 | 5.50394141, 10.60027612, 12.69404325, 11.42856179, 167 | 10.42488359, 10.02133885, 9.92544961, 9.93937282, 168 | 9.97125078, 9.99210081, 10.00053876, 10.00216595, 169 | 10.00149419, 10.00063054, 10.00013668, 9.99996121, 170 | 9.99994058, 9.99996436, 9.99998669, 9.99999803, 171 | 10.0000015, 10.00000156, 10.00000083, 10.00000027, 172 | 10.00000002, 9.99999995, 9.99999996, 9.99999998, 173 | 9.99999999]) 174 | 175 | np.testing.assert_almost_equal(self.model.results.slider_velocity, 176 | truth, 8) 177 | 178 | def test_time(self): 179 | truth = np.array( 180 | [0., 1., 2., 3., 181 | 4., 5., 6., 7., 182 | 8., 9., 10., 11., 183 | 12., 13., 14., 15., 184 | 16., 17., 18., 19., 185 | 20., 21., 22., 23., 186 | 24., 25., 26., 27., 187 | 28., 29., 30., 31., 188 | 32., 33., 34., 35., 189 | 36., 37., 38., 39., 190 | 40.]) 191 | 192 | np.testing.assert_almost_equal(self.model.results.time, truth, 8) 193 | 194 | def test_loadpoint_displacement(self): 195 | truth = np.array( 196 | [0., 1., 2., 3., 197 | 4., 5., 6., 7., 198 | 8., 9., 10., 20., 199 | 30., 40., 50., 60., 200 | 70., 80., 90., 100., 201 | 110., 120., 130., 140., 202 | 150., 160., 170., 180., 203 | 190., 200., 210., 220., 204 | 230., 240., 250., 260., 205 | 270., 280., 290., 300., 206 | 310.]) 207 | 208 | np.testing.assert_almost_equal(self.model.results. 209 | loadpoint_displacement, truth, 8) 210 | 211 | 212 | class TestPerrinOneStateVar(object): 213 | 214 | def setup(self): 215 | self.model = rsf.Model() 216 | self.model.mu0 = 0.6 217 | self.model.a = 0.01 218 | self.model.k = 1e-3 219 | self.model.v = 1. 220 | self.model.vref = 1. 221 | state1 = staterelations.PrzState() 222 | state1.b = 0.005 223 | state1.Dc = 10. 224 | self.model.state_relations = [state1] 225 | self.model.time = np.arange(0, 40.01, 1.) 226 | lp_velocity = np.ones_like(self.model.time) 227 | lp_velocity[10*1:] = 10. 228 | self.model.loadpoint_velocity = lp_velocity 229 | self.model.solve() 230 | 231 | def test_friction(self): 232 | truth = np.array( 233 | [0.6, 0.6, 0.6, 0.6, 234 | 0.6, 0.6, 0.6, 0.6, 235 | 0.6, 0.6, 0.6, 0.60839207, 236 | 0.61456074, 0.61610039, 0.61386731, 0.61210792, 237 | 0.61145825, 0.61134801, 0.61140235, 0.61146702, 238 | 0.61150337, 0.61151606, 0.6115174, 0.61151556, 239 | 0.61151389, 0.61151306, 0.61151281, 0.61151281, 240 | 0.61151286, 0.61151291, 0.61151292, 0.61151293, 241 | 0.61151293, 0.61151293, 0.61151293, 0.61151293, 242 | 0.61151293, 0.61151293, 0.61151293, 0.61151293, 243 | 0.61151293]) 244 | 245 | np.testing.assert_almost_equal(self.model.results.friction, truth, 8) 246 | 247 | def test_state(self): 248 | truth = np.array( 249 | [20., 20., 20., 20., 250 | 20., 20., 20., 20., 251 | 20., 20., 20., 18.43306111, 252 | 11.27594869, 4.02202969, 2.08589836, 1.82377319, 253 | 1.86690475, 1.93990872, 1.98492677, 2.00233846, 254 | 2.0051588, 2.00332919, 2.00133051, 2.00024915, 255 | 1.99988898, 1.99986167, 1.99992165, 1.99997245, 256 | 1.99999695, 2.00000387, 2.00000357, 2.0000018, 257 | 2.00000054, 2., 1.99999988, 1.99999991, 258 | 1.99999996, 1.99999999, 2., 2., 259 | 2.]) 260 | 261 | np.testing.assert_almost_equal(self.model.results.states, 262 | truth.reshape((41, 1)), 8) 263 | 264 | def test_slider_velocity(self): 265 | truth = np.array( 266 | [1., 1., 1., 1., 267 | 1., 1., 1., 1., 268 | 1., 1., 1., 2.41090004, 269 | 5.71219864, 11.15638292, 12.3913343, 11.11398288, 270 | 10.29388702, 9.98762095, 9.92751362, 9.94838881, 271 | 9.97758477, 9.99481799, 10.00114731, 10.00201236, 272 | 10.00124394, 10.00047829, 10.00007902, 9.99995219, 273 | 9.99994673, 9.99997095, 9.99999023, 9.9999992, 274 | 10.00000158, 10.00000136, 10.00000066, 10.00000019, 275 | 9.99999999, 9.99999995, 9.99999997, 9.99999999, 276 | 10.]) 277 | 278 | np.testing.assert_almost_equal(self.model.results.slider_velocity, 279 | truth, 8) 280 | 281 | def test_time(self): 282 | truth = np.array( 283 | [0., 1., 2., 3., 284 | 4., 5., 6., 7., 285 | 8., 9., 10., 11., 286 | 12., 13., 14., 15., 287 | 16., 17., 18., 19., 288 | 20., 21., 22., 23., 289 | 24., 25., 26., 27., 290 | 28., 29., 30., 31., 291 | 32., 33., 34., 35., 292 | 36., 37., 38., 39., 293 | 40.]) 294 | 295 | np.testing.assert_almost_equal(self.model.results.time, truth, 8) 296 | 297 | def test_loadpoint_displacement(self): 298 | truth = np.array( 299 | [0., 1., 2., 3., 300 | 4., 5., 6., 7., 301 | 8., 9., 10., 20., 302 | 30., 40., 50., 60., 303 | 70., 80., 90., 100., 304 | 110., 120., 130., 140., 305 | 150., 160., 170., 180., 306 | 190., 200., 210., 220., 307 | 230., 240., 250., 260., 308 | 270., 280., 290., 300., 309 | 310.]) 310 | 311 | np.testing.assert_almost_equal(self.model.results. 312 | loadpoint_displacement, truth, 8) 313 | 314 | 315 | class TestDeiterichTwoStateVar(object): 316 | 317 | def setup(self): 318 | self.model = rsf.Model() 319 | self.model.mu0 = 0.6 320 | self.model.a = 0.012 321 | self.model.k = 3e-3 322 | self.model.v = 1. 323 | self.model.vref = 1. 324 | state1 = staterelations.DieterichState() 325 | state1.b = 0.0185 326 | state1.Dc = 5. 327 | 328 | state2 = staterelations.DieterichState() 329 | state2.b = 0.0088 330 | state2.Dc = 50. 331 | self.model.state_relations = [state1, state2] 332 | self.model.time = np.arange(0, 40.01, 1.) 333 | lp_velocity = np.ones_like(self.model.time) 334 | lp_velocity[10*1:] = 10. 335 | self.model.loadpoint_velocity = lp_velocity 336 | self.model.solve() 337 | 338 | def test_friction(self): 339 | truth = np.array( 340 | [0.6, 0.6, 0.6, 0.6, 341 | 0.6, 0.6, 0.6, 0.6, 342 | 0.6, 0.6, 0.6, 0.61777816, 343 | 0.56719338, 0.57926411, 0.58134415, 0.5741353, 344 | 0.57495495, 0.57393578, 0.57211002, 0.5714098, 345 | 0.57046462, 0.56953958, 0.56884951, 0.56819716, 346 | 0.56762505, 0.56715188, 0.56674236, 0.5663961, 347 | 0.56610841, 0.56586767, 0.565668, 0.56550353, 348 | 0.56536809, 0.56525698, 0.56516614, 0.56509194, 349 | 0.56503146, 0.56498223, 0.5649422, 0.56490967, 350 | 0.56488327]) 351 | 352 | np.testing.assert_almost_equal(self.model.results.friction, truth, 8) 353 | 354 | def test_state1(self): 355 | truth = np.array( 356 | [5., 5., 5., 5., 357 | 5., 5., 5., 5., 358 | 5., 5., 5., 2.80081321, 359 | 0.26257732, 0.67749062, 0.5128508, 0.43041648, 360 | 0.506089, 0.47944593, 0.47554365, 0.48734539, 361 | 0.4843392, 0.48564166, 0.48855901, 0.48933082, 362 | 0.49072826, 0.4922033, 0.49326055, 0.49430172, 363 | 0.4952396, 0.49601255, 0.49668913, 0.4972666, 364 | 0.49774673, 0.49815004, 0.49848567, 0.49876233, 365 | 0.4989905, 0.4991779, 0.4993312, 0.49945649, 366 | 0.49955867]) 367 | 368 | np.testing.assert_almost_equal(self.model.results.states[:, 0], 369 | truth, 8) 370 | 371 | def test_state2(self): 372 | truth = np.array( 373 | [50., 50., 50., 50., 374 | 50., 50., 50., 50., 375 | 50., 50., 50., 47.03349418, 376 | 28.26143675, 26.02288006, 22.50536043, 18.45103796, 377 | 16.09903009, 13.99322929, 12.21984746, 10.8626045, 378 | 9.74109978, 8.83011044, 8.10068484, 7.50795792, 379 | 7.02833114, 6.64119123, 6.32770427, 6.07410906, 380 | 5.86906354, 5.70314217, 5.56890919, 5.46032285, 381 | 5.37246346, 5.30137737, 5.243863, 5.1973259, 382 | 5.1596709, 5.12920263, 5.1045488, 5.08459968, 383 | 5.06845739]) 384 | 385 | np.testing.assert_almost_equal(self.model.results.states[:, 1], 386 | truth, 8) 387 | 388 | def test_slider_velocity(self): 389 | truth = np.array( 390 | [1., 1., 1., 1., 391 | 1., 1., 1., 1., 392 | 1., 1., 1., 11.24366669, 393 | 9.27431239, 6.24880306, 12.6979352, 10.55410214, 394 | 9.72918973, 10.76583201, 10.34192569, 10.2410124, 395 | 10.3510415, 10.25600829, 10.22005129, 10.20908552, 396 | 10.17177535, 10.1462882, 10.126265, 10.10491673, 397 | 10.08761385, 10.07313912, 10.0603132, 10.04968798, 398 | 10.04085724, 10.03343832, 10.02732652, 10.02229955, 399 | 10.01816004, 10.01477376, 10.01200822, 10.00975089, 400 | 10.00791305]) 401 | 402 | np.testing.assert_almost_equal(self.model.results.slider_velocity, 403 | truth, 8) 404 | 405 | def test_time(self): 406 | truth = np.array( 407 | [0., 1., 2., 3., 408 | 4., 5., 6., 7., 409 | 8., 9., 10., 11., 410 | 12., 13., 14., 15., 411 | 16., 17., 18., 19., 412 | 20., 21., 22., 23., 413 | 24., 25., 26., 27., 414 | 28., 29., 30., 31., 415 | 32., 33., 34., 35., 416 | 36., 37., 38., 39., 417 | 40.]) 418 | 419 | np.testing.assert_almost_equal(self.model.results.time, truth, 8) 420 | 421 | def test_loadpoint_displacement(self): 422 | truth = np.array( 423 | [0., 1., 2., 3., 424 | 4., 5., 6., 7., 425 | 8., 9., 10., 20., 426 | 30., 40., 50., 60., 427 | 70., 80., 90., 100., 428 | 110., 120., 130., 140., 429 | 150., 160., 170., 180., 430 | 190., 200., 210., 220., 431 | 230., 240., 250., 260., 432 | 270., 280., 290., 300., 433 | 310.]) 434 | 435 | np.testing.assert_almost_equal(self.model.results. 436 | loadpoint_displacement, truth, 8) 437 | 438 | 439 | class TestRuinaTwoStateVar(object): 440 | 441 | def setup(self): 442 | self.model = rsf.Model() 443 | self.model.mu0 = 0.6 444 | self.model.a = 0.012 445 | self.model.k = 8e-3 446 | self.model.v = 1. 447 | self.model.vref = 1. 448 | state1 = staterelations.RuinaState() 449 | state1.b = 0.0185 450 | state1.Dc = 5. 451 | 452 | state2 = staterelations.RuinaState() 453 | state2.b = 0.0088 454 | state2.Dc = 50. 455 | self.model.state_relations = [state1, state2] 456 | self.model.time = np.arange(0, 40.01, 1.) 457 | lp_velocity = np.ones_like(self.model.time) 458 | lp_velocity[10*1:] = 10. 459 | self.model.loadpoint_velocity = lp_velocity 460 | self.model.solve() 461 | 462 | def test_friction(self): 463 | truth = np.array( 464 | [0.6, 0.6, 0.6, 0.6, 465 | 0.6, 0.6, 0.6, 0.6, 466 | 0.6, 0.6, 0.6, 0.57317487, 467 | 0.57620828, 0.57423257, 0.57241419, 0.57098114, 468 | 0.56981792, 0.56887496, 0.56810967, 0.56748807, 469 | 0.56698283, 0.56657196, 0.56623767, 0.56596561, 470 | 0.56574412, 0.56556376, 0.56541686, 0.5652972, 471 | 0.56519972, 0.56512029, 0.56505557, 0.56500283, 472 | 0.56495985, 0.56492482, 0.56489628, 0.56487301, 473 | 0.56485404, 0.56483859, 0.56482599, 0.56481572, 474 | 0.56480735]) 475 | 476 | np.testing.assert_almost_equal(self.model.results.friction, truth, 8) 477 | 478 | def test_state1(self): 479 | truth = np.array( 480 | [5., 5., 5., 5., 481 | 5., 5., 5., 5., 482 | 5., 5., 5., 0.34503501, 483 | 0.48718318, 0.48688665, 0.48906973, 0.49116621, 484 | 0.49281743, 0.49415943, 0.49524843, 0.49613296, 485 | 0.4968519, 0.49743656, 0.49791223, 0.49829936, 486 | 0.49861453, 0.49887118, 0.4990802, 0.49925047, 487 | 0.49938918, 0.4995022, 0.49959429, 0.49966934, 488 | 0.49973049, 0.49978034, 0.49982096, 0.49985406, 489 | 0.49988105, 0.49990304, 0.49992097, 0.49993558, 490 | 0.49994749]) 491 | 492 | np.testing.assert_almost_equal(self.model.results.states[:, 0], 493 | truth, 8) 494 | 495 | def test_state2(self): 496 | truth = np.array( 497 | [50., 50., 50., 50., 498 | 50., 50., 50., 50., 499 | 50., 50., 50., 25.46871646, 500 | 19.28091149, 14.94589606, 12.15504322, 10.28679987, 501 | 8.98658279, 8.05413768, 7.36902545, 6.85569405, 502 | 6.46493829, 6.16365308, 5.92892636, 5.74450566, 503 | 5.59861356, 5.48255637, 5.38981385, 5.31542877, 504 | 5.2555883, 5.20733084, 5.1683369, 5.13677719, 505 | 5.11120066, 5.09045071, 5.07360177, 5.05991069, 506 | 5.04877914, 5.03972433, 5.03235598, 5.0263581, 507 | 5.02147453]) 508 | 509 | np.testing.assert_almost_equal(self.model.results.states[:, 1], 510 | truth, 8) 511 | 512 | def test_slider_velocity(self): 513 | truth = np.array( 514 | [1., 1., 1., 1., 515 | 1., 1., 1., 1., 516 | 1., 1., 1., 10.81546891, 517 | 10.03428877, 10.2683735, 10.19823177, 10.16123603, 518 | 10.13064517, 10.10597718, 10.08604871, 10.06991893, 519 | 10.05684658, 10.04624063, 10.03762818, 10.03062956, 520 | 10.02493909, 10.02031007, 10.01654309, 10.01347665, 521 | 10.01097985, 10.00894645, 10.00729017, 10.00594089, 522 | 10.00484157, 10.00394584, 10.00321593, 10.00262111, 523 | 10.00213635, 10.00174128, 10.00141929, 10.00115686, 524 | 10.00094295]) 525 | 526 | np.testing.assert_almost_equal(self.model.results. 527 | slider_velocity, truth, 8) 528 | 529 | def test_time(self): 530 | truth = np.array( 531 | [0., 1., 2., 3., 532 | 4., 5., 6., 7., 533 | 8., 9., 10., 11., 534 | 12., 13., 14., 15., 535 | 16., 17., 18., 19., 536 | 20., 21., 22., 23., 537 | 24., 25., 26., 27., 538 | 28., 29., 30., 31., 539 | 32., 33., 34., 35., 540 | 36., 37., 38., 39., 541 | 40.]) 542 | 543 | np.testing.assert_almost_equal(self.model.results.time, truth, 8) 544 | 545 | def test_loadpoint_displacement(self): 546 | truth = np.array( 547 | [0., 1., 2., 3., 548 | 4., 5., 6., 7., 549 | 8., 9., 10., 20., 550 | 30., 40., 50., 60., 551 | 70., 80., 90., 100., 552 | 110., 120., 130., 140., 553 | 150., 160., 170., 180., 554 | 190., 200., 210., 220., 555 | 230., 240., 250., 260., 556 | 270., 280., 290., 300., 557 | 310.]) 558 | 559 | np.testing.assert_almost_equal(self.model.results. 560 | loadpoint_displacement, truth, 8) 561 | 562 | 563 | class TestPRZOneStateVar(object): 564 | 565 | def setup(self): 566 | self.model = rsf.Model() 567 | self.model.mu0 = 0.6 568 | self.model.a = 0.011 569 | self.model.k = 3e-3 570 | self.model.v = 1. 571 | self.model.vref = 1. 572 | state1 = staterelations.PrzState() 573 | state1.b = 0.01 574 | state1.Dc = 5. 575 | self.model.state_relations = [state1] 576 | self.model.time = np.arange(0, 40.01, 1) 577 | lp_velocity = np.ones_like(self.model.time) 578 | lp_velocity[10*1:] = 10. 579 | self.model.loadpoint_velocity = lp_velocity 580 | self.model.solve() 581 | 582 | def test_friction(self): 583 | truth = np.array( 584 | [0.6, 0.6, 0.6, 0.6, 585 | 0.6, 0.6, 0.6, 0.6, 586 | 0.6, 0.6, 0.6, 0.61505335, 587 | 0.60008169, 0.60202942, 0.60245015, 0.60230027, 588 | 0.60229482, 0.60230363, 0.60230288, 0.60230249, 589 | 0.60230258, 0.60230259, 0.60230258, 0.60230258, 590 | 0.60230259, 0.60230259, 0.60230259, 0.60230259, 591 | 0.60230259, 0.60230259, 0.60230259, 0.60230259, 592 | 0.60230259, 0.60230259, 0.60230259, 0.60230259, 593 | 0.60230259, 0.60230259, 0.60230259, 0.60230259, 594 | 0.60230259]) 595 | 596 | np.testing.assert_almost_equal(self.model.results.friction, truth, 8) 597 | 598 | def test_state(self): 599 | truth = np.array( 600 | [10., 10., 10., 10., 601 | 10., 10., 10., 10., 602 | 10., 10., 10., 2.09747593, 603 | 0.81845159, 1.02623769, 1.00892332, 0.99754925, 604 | 0.99981141, 1.00015612, 0.99999181, 0.99999246, 605 | 1.00000134, 1.00000025, 0.9999999, 1., 606 | 1.00000001, 1., 1., 1., 607 | 1., 1., 1., 1., 608 | 1., 1., 1., 1., 609 | 1., 1., 1., 1., 610 | 1.]) 611 | 612 | np.testing.assert_almost_equal(self.model.results.states, 613 | truth.reshape((41, 1)), 8) 614 | 615 | def test_slider_velocity(self): 616 | truth = np.array( 617 | [1., 1., 1., 1., 618 | 1., 1., 1., 1., 619 | 1., 1., 1., 16.25418209, 620 | 9.80422815, 9.52773797, 10.05352858, 10.02022142, 621 | 9.99465394, 9.99953164, 10.00034701, 9.99998443, 622 | 9.99998292, 10.00000287, 10.00000059, 9.99999977, 623 | 9.99999999, 10.00000001, 10., 10., 624 | 10., 10., 10., 10., 625 | 10., 10., 10., 10., 626 | 10., 10., 10., 10., 627 | 10.]) 628 | 629 | np.testing.assert_almost_equal(self.model.results. 630 | slider_velocity, truth, 8) 631 | 632 | def test_time(self): 633 | truth = np.array( 634 | [0., 1., 2., 3., 635 | 4., 5., 6., 7., 636 | 8., 9., 10., 11., 637 | 12., 13., 14., 15., 638 | 16., 17., 18., 19., 639 | 20., 21., 22., 23., 640 | 24., 25., 26., 27., 641 | 28., 29., 30., 31., 642 | 32., 33., 34., 35., 643 | 36., 37., 38., 39., 644 | 40.]) 645 | 646 | np.testing.assert_almost_equal(self.model.results.time, truth, 8) 647 | 648 | def test_loadpoint_displacement(self): 649 | truth = np.array( 650 | [0., 1., 2., 3., 651 | 4., 5., 6., 7., 652 | 8., 9., 10., 20., 653 | 30., 40., 50., 60., 654 | 70., 80., 90., 100., 655 | 110., 120., 130., 140., 656 | 150., 160., 170., 180., 657 | 190., 200., 210., 220., 658 | 230., 240., 250., 260., 659 | 270., 280., 290., 300., 660 | 310.]) 661 | 662 | np.testing.assert_almost_equal(self.model.results. 663 | loadpoint_displacement, truth, 8) 664 | 665 | 666 | class TestPRZTwoStateVar(object): 667 | 668 | def setup(self): 669 | self.model = rsf.Model() 670 | self.model.mu0 = 0.6 671 | self.model.a = 0.02 672 | self.model.k = 3e-3 673 | self.model.v = 1. 674 | self.model.vref = 1. 675 | state1 = staterelations.PrzState() 676 | state1.b = 0.01 677 | state1.Dc = 5. 678 | 679 | state2 = staterelations.PrzState() 680 | state2.b = 0.005 681 | state2.Dc = 3. 682 | self.model.state_relations = [state1, state2] 683 | self.model.time = np.arange(0, 40.01, 1.) 684 | lp_velocity = np.ones_like(self.model.time) 685 | lp_velocity[10*1:] = 10. 686 | self.model.loadpoint_velocity = lp_velocity 687 | self.model.solve() 688 | 689 | def test_friction(self): 690 | truth = np.array( 691 | [0.6, 0.6, 0.6, 0.6, 692 | 0.6, 0.6, 0.6, 0.6, 693 | 0.6, 0.6, 0.6, 0.62332749, 694 | 0.61662392, 0.60925037, 0.61102238, 0.61175384, 695 | 0.61157669, 0.61148672, 0.611505, 0.61151574, 696 | 0.6115139, 0.61151263, 0.61151281, 0.61151296, 697 | 0.61151294, 0.61151292, 0.61151292, 0.61151293, 698 | 0.61151293, 0.61151293, 0.61151293, 0.61151293, 699 | 0.61151293, 0.61151293, 0.61151293, 0.61151293, 700 | 0.61151293, 0.61151293, 0.61151293, 0.61151293, 701 | 0.61151293]) 702 | 703 | np.testing.assert_almost_equal(self.model.results.friction, truth, 8) 704 | 705 | def test_state1(self): 706 | truth = np.array( 707 | [10., 10., 10., 10., 708 | 10., 10., 10., 10., 709 | 10., 10., 10., 6.84431098, 710 | 0.72378553, 0.86994234, 1.03299015, 1.01865937, 711 | 0.99670972, 0.99782031, 1.00032473, 1.00025833, 712 | 0.99996907, 0.99996963, 1.00000281, 1.00000355, 713 | 0.99999976, 0.99999959, 1.00000002, 1.00000005, 714 | 1., 0.99999999, 1., 1., 715 | 1., 1., 1., 1., 716 | 1., 1., 1., 1., 717 | 1.]) 718 | 719 | np.testing.assert_almost_equal(self.model.results.states[:, 0], 720 | truth, 8) 721 | 722 | def test_state2(self): 723 | truth = np.array( 724 | [6., 6., 6., 6., 725 | 6., 6., 6., 6., 726 | 6., 6., 6., 3.4500867, 727 | 0.36317378, 0.55303551, 0.63530577, 0.60831617, 728 | 0.59630691, 0.59898905, 0.60040086, 0.60012611, 729 | 0.59995714, 0.5999845, 0.60000454, 0.60000189, 730 | 0.59999953, 0.59999977, 0.60000005, 0.60000003, 731 | 0.6, 0.6, 0.6, 0.6, 732 | 0.6, 0.6, 0.6, 0.6, 733 | 0.6, 0.6, 0.6, 0.6, 734 | 0.6]) 735 | 736 | np.testing.assert_almost_equal(self.model.results.states[:, 1], 737 | truth, 8) 738 | 739 | def test_slider_velocity(self): 740 | truth = np.array( 741 | [1., 1., 1., 1., 742 | 1., 1., 1., 1., 743 | 1., 1., 1., 4.45620908, 744 | 17.20632436, 9.77177682, 9.46437404, 9.99361157, 745 | 10.06399985, 10.00202131, 9.99274721, 9.99958814, 746 | 10.000822, 10.0000672, 9.99990739, 9.99999004, 747 | 10.00001038, 10.00000139, 9.99999884, 9.99999981, 748 | 10.00000013, 10.00000002, 9.99999999, 10., 749 | 10., 10., 10., 10., 750 | 10., 10., 10., 10., 751 | 10.]) 752 | 753 | np.testing.assert_almost_equal(self.model.results. 754 | slider_velocity, truth, 8) 755 | 756 | def test_time(self): 757 | truth = np.array( 758 | [0., 1., 2., 3., 759 | 4., 5., 6., 7., 760 | 8., 9., 10., 11., 761 | 12., 13., 14., 15., 762 | 16., 17., 18., 19., 763 | 20., 21., 22., 23., 764 | 24., 25., 26., 27., 765 | 28., 29., 30., 31., 766 | 32., 33., 34., 35., 767 | 36., 37., 38., 39., 768 | 40.]) 769 | 770 | np.testing.assert_almost_equal(self.model.results.time, truth, 8) 771 | 772 | def test_loadpoint_displacement(self): 773 | truth = np.array( 774 | [0., 1., 2., 3., 775 | 4., 5., 6., 7., 776 | 8., 9., 10., 20., 777 | 30., 40., 50., 60., 778 | 70., 80., 90., 100., 779 | 110., 120., 130., 140., 780 | 150., 160., 170., 180., 781 | 190., 200., 210., 220., 782 | 230., 240., 250., 260., 783 | 270., 280., 290., 300., 784 | 310.]) 785 | 786 | np.testing.assert_almost_equal(self.model.results. 787 | loadpoint_displacement, truth, 8) 788 | 789 | 790 | class TestRuinaTwoStateVarMissing(object): 791 | 792 | def setup(self): 793 | self.model = rsf.Model() 794 | self.model.mu0 = 0.6 795 | self.model.a = 0.012 796 | self.model.k = 8e-3 797 | self.model.v = 1. 798 | self.model.vref = 1. 799 | state1 = staterelations.RuinaState() 800 | state1.b = 0.0185 801 | state1.Dc = 5. 802 | 803 | state2 = staterelations.RuinaState() 804 | state2.b = 0.0088 805 | state2.Dc = 50. 806 | self.model.state_relations = [state1, state2] 807 | self.model.time = np.arange(0, 40.01, 1.) 808 | lp_velocity = np.ones_like(self.model.time) 809 | lp_velocity[10*1:] = 10. 810 | self.model.loadpoint_velocity = lp_velocity 811 | self.model.solve() 812 | 813 | @raises(IncompleteModelError) 814 | def test_a_missing(self): 815 | self.model.a = None 816 | self.model.solve() 817 | 818 | @raises(IncompleteModelError) 819 | def test_vref_missing(self): 820 | self.model.vref = None 821 | self.model.solve() 822 | 823 | @raises(Exception) 824 | def test_state_realtions_missing(self): 825 | self.model.state_relations = [] 826 | self.model.solve() 827 | 828 | @raises(IncompleteModelError) 829 | def test_k_missing(self): 830 | self.model.k = None 831 | self.model.solve() 832 | 833 | @raises(IncompleteModelError) 834 | def test_time_missing(self): 835 | self.model.time = None 836 | self.model.solve() 837 | 838 | @raises(IncompleteModelError) 839 | def test_lp_velocity_missing(self): 840 | self.model.loadpoint_velocity = None 841 | self.model.solve() 842 | 843 | @raises(IncompleteModelError) 844 | def test_state1_b_missing(self): 845 | state1 = staterelations.RuinaState() 846 | state1.Dc = 5. 847 | 848 | state2 = staterelations.RuinaState() 849 | state2.b = 0.0088 850 | state2.Dc = 50. 851 | self.model.state_relations = [state1, state2] 852 | self.model.solve() 853 | 854 | @raises(IncompleteModelError) 855 | def test_state2_b_missing(self): 856 | state1 = staterelations.RuinaState() 857 | state1.b = 0.0185 858 | state1.Dc = 5. 859 | 860 | state2 = staterelations.RuinaState() 861 | state2.Dc = 50. 862 | self.model.state_relations = [state1, state2] 863 | self.model.solve() 864 | 865 | @raises(IncompleteModelError) 866 | def test_state1_Dc_missing(self): 867 | state1 = staterelations.RuinaState() 868 | state1.b = 0.0185 869 | 870 | state2 = staterelations.RuinaState() 871 | state2.b = 0.0088 872 | state2.Dc = 50. 873 | self.model.state_relations = [state1, state2] 874 | self.model.solve() 875 | 876 | @raises(IncompleteModelError) 877 | def test_state2Dcb_missing(self): 878 | state1 = staterelations.RuinaState() 879 | state1.b = 0.0185 880 | state1.Dc = 5. 881 | 882 | state2 = staterelations.RuinaState() 883 | state2.b = 0.0088 884 | self.model.state_relations = [state1, state2] 885 | self.model.solve() 886 | 887 | @raises(IncompleteModelError) 888 | def test_time_velocity_length_mismatch(self): 889 | self.model.time = np.arange(0, 40.01, 0.1) 890 | self.model.solve() 891 | 892 | 893 | class TestNagataOneStateVar(object): 894 | 895 | def setup(self): 896 | self.model = rsf.Model() 897 | self.model.mu0 = 0.6 898 | self.model.a = 0.027 899 | self.model.k = 0.01 900 | self.model.v = 1. 901 | self.model.vref = 1. 902 | state1 = staterelations.NagataState() 903 | state1.b = 0.029 904 | state1.Dc = 3.33 905 | state1.c = 2. 906 | self.model.state_relations = [state1] 907 | self.model.time = np.arange(0, 40.01, 1) 908 | lp_velocity = np.ones_like(self.model.time) 909 | lp_velocity[10*1:] = 10. 910 | self.model.loadpoint_velocity = lp_velocity 911 | self.model.solve(hmax=0.01) 912 | 913 | def test_friction(self): 914 | truth = np.array( 915 | [0.6, 0.6, 0.6, 0.6, 916 | 0.6, 0.6, 0.6, 0.6, 917 | 0.6, 0.6, 0.6, 0.60938215, 918 | 0.60034494, 0.59703287, 0.59592421, 0.59556459, 919 | 0.59544913, 0.59541219, 0.59540038, 0.5953966, 920 | 0.5953954, 0.59539501, 0.59539489, 0.59539485, 921 | 0.59539484, 0.59539483, 0.59539483, 0.59539483, 922 | 0.59539483, 0.59539483, 0.59539483, 0.59539483, 923 | 0.59539483, 0.59539483, 0.59539483, 0.59539483, 924 | 0.59539483, 0.59539483, 0.59539483, 0.59539483, 925 | 0.59539483]) 926 | 927 | np.testing.assert_almost_equal(self.model.results.friction, truth, 8) 928 | 929 | def test_state(self): 930 | truth = np.array( 931 | [3.33, 3.33, 3.33, 3.33, 932 | 3.33, 3.33, 3.33, 3.33, 933 | 3.33, 3.33, 3.33, 0.47875177, 934 | 0.37623256, 0.34642714, 0.33724867, 0.33435315, 935 | 0.33343188, 0.33313794, 0.33304407, 0.33301408, 936 | 0.3330045, 0.33300144, 0.33300046, 0.33300015, 937 | 0.33300005, 0.33300001, 0.333, 0.333, 938 | 0.333, 0.333, 0.333, 0.333, 939 | 0.333, 0.333, 0.333, 0.333, 940 | 0.333, 0.333, 0.333, 0.333, 941 | 0.333]) 942 | 943 | np.testing.assert_almost_equal(self.model.results.states, 944 | truth.reshape((41, 1)), 8) 945 | 946 | def test_slider_velocity(self): 947 | truth = np.array( 948 | [1., 1., 1., 1., 949 | 1., 1., 1., 1., 950 | 1., 1., 1., 11.36686532, 951 | 10.53618749, 10.18376884, 10.06007654, 10.01933637, 952 | 10.00619248, 10.00197996, 10.00063274, 10.00020217, 953 | 10.00006459, 10.00002064, 10.00000659, 10.00000211, 954 | 10.00000067, 10.00000022, 10.00000007, 10.00000002, 955 | 10.00000001, 10., 10., 10., 956 | 10., 10., 10., 10., 957 | 10., 10., 10., 10., 958 | 10.]) 959 | 960 | np.testing.assert_almost_equal(self.model.results.slider_velocity, 961 | truth, 8) 962 | 963 | def test_time(self): 964 | truth = np.array( 965 | [0., 1., 2., 3., 966 | 4., 5., 6., 7., 967 | 8., 9., 10., 11., 968 | 12., 13., 14., 15., 969 | 16., 17., 18., 19., 970 | 20., 21., 22., 23., 971 | 24., 25., 26., 27., 972 | 28., 29., 30., 31., 973 | 32., 33., 34., 35., 974 | 36., 37., 38., 39., 975 | 40.]) 976 | 977 | np.testing.assert_almost_equal(self.model.results.time, truth, 8) 978 | 979 | def test_loadpoint_displacement(self): 980 | truth = np.array( 981 | [0., 1., 2., 3., 982 | 4., 5., 6., 7., 983 | 8., 9., 10., 20., 984 | 30., 40., 50., 60., 985 | 70., 80., 90., 100., 986 | 110., 120., 130., 140., 987 | 150., 160., 170., 180., 988 | 190., 200., 210., 220., 989 | 230., 240., 250., 260., 990 | 270., 280., 290., 300., 991 | 310.]) 992 | 993 | np.testing.assert_almost_equal(self.model.results. 994 | loadpoint_displacement, truth, 8) 995 | --------------------------------------------------------------------------------