diff --git a/.ci/get_sundials.sh b/.ci/get_sundials.sh index 1c6a4e54..e37228e2 100755 --- a/.ci/get_sundials.sh +++ b/.ci/get_sundials.sh @@ -1,4 +1,4 @@ -#!/bin/bash -eu +#!/bin/bash -u # # Usage: # @@ -6,37 +6,59 @@ # $ ./get_sundials.sh 2.7.0 /opt/sun-2.7.0 -DLAPACK_ENABLE:BOOL=OFF # -VERSION=$1 -PREFIX=$2 -if [ -d "$PREFIX" ]; then 2>&1 echo "Directory already exists: $PREFIX"; exit 1; fi -if [[ $VERSION == "3.1.1" ]]; then - SUNDIALS_FNAME="sundials-3.1.1.tar.gz" - SUNDIALS_MD5="e63f4de0be5be97f750b30b0fa11ef34" -elif [[ $VERSION == "3.1.2" ]]; then +function quiet_unless_fail { + # suppresses function output unless exit status is != 0 + OUTPUT_FILE=$(tempfile) + #/bin/rm --force /tmp/suppress.out 2>/dev/null + EXECMD=${1+"$@"} + $EXECMD > ${OUTPUT_FILE} 2>&1 + EXIT_CODE=$? + if [ ${EXIT_CODE} -ne 0 ]; then + cat ${OUTPUT_FILE} + echo "The following command exited with exit status ${EXIT_CODE}: ${EXECMD}" + /bin/rm ${OUTPUT_FILE} + exit $? + fi + /bin/rm ${OUTPUT_FILE} +} + +VERSION="$1" +PREFIX="$2" +if [ -d "$PREFIX" ]; then >&2 echo "Directory already exists: $PREFIX"; exit 1; fi +if [[ "$VERSION" == "2.7.0" ]]; then + SUNDIALS_FNAME="sundials-2.7.0.tar.gz" + SUNDIALS_MD5="c304631b9bc82877d7b0e9f4d4fd94d3" + SUNDIALS_SHA256="d39fcac7175d701398e4eb209f7e92a5b30a78358d4a0c0fcc23db23c11ba104" +elif [[ "$VERSION" == "3.1.2" ]]; then SUNDIALS_FNAME="sundials-3.1.2.tar.gz" SUNDIALS_MD5="63304dafc935c94a0ad37832085384bc" -elif [[ $VERSION == "3.2.0" ]]; then - SUNDIALS_FNAME="sundials-3.2.0.tar.gz" - SUNDIALS_MD5="669e05565d3294478848ed497ac35a6e" -elif [[ $VERSION == "3.2.1" ]]; then + SUNDIALS_SHA256="a8985bb1e851d90e24260450667b134bc13d71f5c6effc9e1d7183bd874fe116" +elif [[ "$VERSION" == "3.2.1" ]]; then SUNDIALS_FNAME="sundials-3.2.1.tar.gz" SUNDIALS_MD5="65c42e4fec7d1f4f4bcd670f9bbe31c0" -elif [[ $VERSION == "2.7.0" ]]; then - SUNDIALS_FNAME="sundials-2.7.0.tar.gz" - SUNDIALS_MD5="c304631b9bc82877d7b0e9f4d4fd94d3" + SUNDIALS_SHA256="47d94d977ab2382cdcdd02f72a25ebd4ba8ca2634bbb2f191fe1636e71c86808" +elif [[ "$VERSION" == "4.0.2" ]]; then + SUNDIALS_FNAME="sundials-4.0.2.tar.gz" + SUNDIALS_MD5="2d840ed467ca491a3c1fe4ce67d2a99a" + SUNDIALS_SHA256="6656d6938aed9142e61a001b1ed9f4ee4f7eaf003613bf5a887e98a85904d375" +elif [[ "$VERSION" == "4.1.0" ]]; then + SUNDIALS_FNAME="sundials-4.1.0.tar.gz" + SUNDIALS_MD5="f25bb0bc109ac3db0aaae13eadce559c" + SUNDIALS_SHA256="280de1c27b2360170a6f46cb3799b2aee9dff3bddbafc8b08c291a47ab258aa5" else - 2>&1 echo "Unknown sundials version $VERSION" + >&2 echo "Unknown sundials version \"$VERSION\"" fi SUNDIALS_URLS=(\ "http://hera.physchem.kth.se/~repo/${SUNDIALS_MD5}/${SUNDIALS_FNAME}" \ + "http://davycrockett.mooo.com:49090/~repo/${SUNDIALS_SHA256}/${SUNDIALS_FNAME}" \ "http://computation.llnl.gov/projects/sundials/download/${SUNDIALS_FNAME}" \ ) TIMEOUT=60 # 60 seconds for URL in "${SUNDIALS_URLS[@]}"; do if echo $SUNDIALS_MD5 $SUNDIALS_FNAME | md5sum -c --; then - echo "Found ${SUNDIALS_FNAME} with matching checksum, using this file." + echo "Found ${SUNDIALS_FNAME} with matching checksum, using that file." else echo "Downloading ${URL}..." timeout $TIMEOUT wget --quiet --tries=2 --timeout=$TIMEOUT $URL -O $SUNDIALS_FNAME || continue @@ -45,18 +67,28 @@ for URL in "${SUNDIALS_URLS[@]}"; do tar xzf $SUNDIALS_FNAME mkdir sundials_build cd sundials_build - cmake -DCMAKE_INSTALL_PREFIX:PATH=$PREFIX \ - -DCMAKE_BUILD_TYPE:STRING="Release" \ - -DBUILD_SHARED_LIBS:BOOL=ON \ - -DBUILD_STATIC_LIBS:BOOL=OFF \ - -DEXAMPLES_ENABLE_C:BOOL=OFF \ - -DEXAMPLES_INSTALL:BOOL=OFF \ - -DOPENMP_ENABLE:BOOL=OFF \ - "${@:3}" ../sundials-${VERSION}/ - make -j 2 >/dev/null 2>&1 - make install + ( set -x; \ + cmake -DCMAKE_INSTALL_PREFIX:PATH="$PREFIX" \ + -DCMAKE_BUILD_TYPE:STRING="Release" \ + -DBUILD_SHARED_LIBS:BOOL=ON \ + -DBUILD_STATIC_LIBS:BOOL=OFF \ + -DEXAMPLES_ENABLE_C:BOOL=OFF \ + -DEXAMPLES_INSTALL:BOOL=OFF \ + -DOPENMP_ENABLE:BOOL=OFF \ + "${@:3}" "../sundials-$VERSION/" + ) + if [[ $? -ne 0 ]]; then + >&2 echo "Cmake configuration failed." + exit 1 + fi + quiet_unless_fail make VERBOSE=1 -j 1 + if [ $? -ne 0 ]; then + >&2 echo "Building of sundials \"$VERSION\" failed." + exit 1 + fi + quiet_unless_fail make install if [ $? -ne 0 ]; then - 2>&1 echo "Build/install of sundials-${VERSION} failed." + >&2 echo "Install of sundials \"$VERSION\" failed." exit 1 fi cd .. diff --git a/.ci/run_ci.sh b/.ci/run_ci.sh index cf19b3fe..f4e8984a 100755 --- a/.ci/run_ci.sh +++ b/.ci/run_ci.sh @@ -17,11 +17,12 @@ export PYTHONHASHSEED=$(python3 -c "import random; print(random.randint(1,2**32- PYTHON="python3 -R" ./scripts/run_tests.sh --cov $PKG_NAME --cov-report html ./scripts/render_notebooks.sh -(cd $PKG_NAME/tests; jupyter nbconvert --debug --to=html --ExecutePreprocessor.enabled=True --ExecutePreprocessor.timeout=360 *.ipynb) +(cd $PKG_NAME/tests; jupyter nbconvert --debug --to=html --ExecutePreprocessor.enabled=True --ExecutePreprocessor.timeout=600 *.ipynb) ./scripts/generate_docs.sh # Test package without any 3rd party libraries that are in extras_require: python3 -m pip install virtualenv python3 -m virtualenv venv +git archive -o dist/$PKG_NAME-head.zip HEAD # test pip installable zip (symlinks break) set +u -(source ./venv/bin/activate; python3 -m pip install pytest .; python3 -m pytest $PKG_NAME) +(source ./venv/bin/activate; cd dist/; python3 -m pip install pytest $PKG_NAME-head.zip; python3 -m pytest --pyargs $PKG_NAME) diff --git a/.ci/test_py2.sh b/.ci/test_py2.sh index 7e4e9ad6..d51d758f 100755 --- a/.ci/test_py2.sh +++ b/.ci/test_py2.sh @@ -13,4 +13,3 @@ python2 -m pip install --user virtualenv python2 -m virtualenv /tmp/test_py2 sed -i -E -e "/python_requires/d" setup.py bash -c "source /tmp/test_py2/bin/activate; pip install pytest '.[all]' && pytest -rs --pyargs ${PKG_NAME}" - diff --git a/.drone.yml b/.drone.yml index 6a0731ed..20be3655 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,55 +1,103 @@ clone: - depth: 2 - recursive: true - submodule_override: - external/anyode: git://github.com/bjodah/anyode.git -cache: - mount: - - /drone/sund-3.2.1-klu - - /drone/sund-3.2.1-nolapack-noklu-extended -build: - image: bjodah/bjodahimg18dev:v1.4 - environment: - - OMP_NUM_THREADS=1 - - ANYODE_NUM_THREADS=2 - - CPLUS_INCLUDE_PATH=/usr/include/suitesparse - - LIBRARY_PATH=/usr/lib/x86_64-linux-gnu - - LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu - commands: - - git fetch -tq - - if [ ! -d /drone/sund-3.2.1-klu ]; then .ci/get_sundials.sh 3.2.1 /drone/sund-3.2.1-klu -DLAPACK_ENABLE:BOOL=ON -DSUNDIALS_INDEX_SIZE=32 -DKLU_ENABLE:BOOL=ON -DKLU_INCLUDE_DIR=/usr/include/suitesparse -DKLU_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu; fi - - if [ ! -d /drone/sund-3.2.1-nolapack-noklu-extended ]; then .ci/get_sundials.sh 3.2.1 /drone/sund-3.2.1-nolapack-noklu-extended -DLAPACK_ENABLE:BOOL=OFF -DSUNDIALS_INDEX_SIZE=32 -DSUNDIALS_PRECISION:STRING="extended" -DKLU_ENABLE:BOOL=OFF; fi - # Need to install pycvodes from source for extended precision/int64 test, since - # that requires no LAPACK, whereas the pypi version installs with LAPACK - - git clone --recurse-submodules https://github.com/bjodah/pycvodes.git /drone/pycvodes - - cd /drone/pycvodes - - LIBRARY_PATH=/drone/sund-3.2.1-nolapack-noklu-extended/lib:$LIBRARY_PATH CPLUS_INCLUDE_PATH=/drone/sund-3.2.1-nolapack-noklu-extended/include:$CPLUS_INCLUDE_PATH LD_LIBRARY_PATH=/drone/sund-3.2.1-nolapack-noklu-extended/lib:$LD_LIBRARY_PATH PYCVODES_LAPACK=0 PYCVODES_NO_KLU=1 python3 setup.py install - - cd - - - .ci/run_ci.sh pyodesys /drone/sund-3.2.1-nolapack-noklu-extended - - rm -r /usr/local/lib/python*/dist-packages/pycvodes* - # Need to remove cache so that new sundials/pyodesys doesn't try to use the old - # compiled native code wrappers - - rm -r /root/.cache/*pyodesys* - # "Normal" (double, int) tests - - .ci/run_ci.sh pyodesys /drone/sund-3.2.1-klu - - ./scripts/prepare_deploy.sh - - .ci/test_py2.sh pyodesys /drone/sund-3.2.1-klu - - rm -r ~/.cache/python*pyodesys*/ - - git clean -fd - - # PATH=/opt/miniconda3/bin:$PATH conda config --add channels bjodah # sym, pyodesys, pyneqsys - - # PATH=/opt/miniconda3/bin:$PATH conda upgrade --quiet conda-build - - # PATH=/opt/miniconda3/bin:$PATH conda build --output-folder "deploy/public_html/branches/${CI_BRANCH}" conda-recipe - - bash -c '[[ $(python3 setup.py --version) =~ ^[0-9]+.* ]]' - - ./scripts/grep-for-merge-blocking-token.sh - - ./scripts/grep-for-binary-data.sh + default: + image: plugins/git + recursive: true + submodule_override: + external/anyode: git://github.com/bjodah/anyode.git + +pipeline: + restore-cache: + image: drillster/drone-volume-cache + restore: true + mount: + - ./ci_cache/sund-3.2.1-klu + - ./ci_cache/sund-4.1.0-nolapack-noklu-extended + - ./ci_cache/conda_packages + - ./ci_cache/pip_cache + volumes: + - /tmp/cache:/cache + ttl: 90 # liftetime in days + + test-sund-3.2.1-klu: + group: testing + image: bjodah/bjodahimg18:v1.5 + environment: + - OMP_NUM_THREADS=1 + - ANYODE_NUM_THREADS=2 + - CPLUS_INCLUDE_PATH=/usr/include/suitesparse + - LIBRARY_PATH=/usr/lib/x86_64-linux-gnu + - LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu + commands: + - export SUNDBASE=$(pwd)/ci_cache/sund-3.2.1-klu + - if [ ! -d $SUNDBASE ]; then .ci/get_sundials.sh 3.2.1 $SUNDBASE -DLAPACK_ENABLE:BOOL=ON -DSUNDIALS_INDEX_SIZE=32 -DKLU_ENABLE:BOOL=ON -DKLU_INCLUDE_DIR=/usr/include/suitesparse -DKLU_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu; fi + - mkdir -p $HOME/.config/pip/; bash -c 'echo -e "[global]\nno-cache-dir = false\ndownload-cache = $(pwd)/ci_cache/pip_cache" >$HOME/.config/pip/pip.conf' + - bash -c "ulimit -v 2048000; .ci/run_ci.sh pyodesys $SUNDBASE" + - ./scripts/prepare_deploy.sh + - bash -c '[[ $(python3 setup.py --version) =~ ^[0-9]+.* ]]' + - ./scripts/grep-for-merge-blocking-token.sh + - ./scripts/grep-for-binary-data.sh + + test-sund-4.1.0-nolapack-noklu-extended: + group: testing + image: bjodah/bjodahimg18:v1.5 + environment: + - OMP_NUM_THREADS=1 + - ANYODE_NUM_THREADS=2 + - CPLUS_INCLUDE_PATH=/usr/include/suitesparse + - LIBRARY_PATH=/usr/lib/x86_64-linux-gnu + - LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu + commands: + - export SUNDBASE=$(pwd)/ci_cache/sund-4.1.0-nolapack-noklu-extended + - if [ ! -d $SUNDBASE ]; then .ci/get_sundials.sh 4.1.0 $SUNDBASE -DLAPACK_ENABLE:BOOL=OFF -DSUNDIALS_INDEX_SIZE=32 -DSUNDIALS_PRECISION:STRING="extended" -DKLU_ENABLE:BOOL=OFF; fi + # Need to install pycvodes from source for extended precision/int64 test, since + # that requires no LAPACK, whereas the pypi version installs with LAPACK + - git clone --recurse-submodules https://github.com/bjodah/pycvodes.git /tmp/pycvodes + - cd /tmp/pycvodes + - LIBRARY_PATH=$SUNDBASE/lib:$LIBRARY_PATH CPATH=$SUNDBASE/include:$CPATH LD_LIBRARY_PATH=$SUNDBASE/lib:$LD_LIBRARY_PATH PYCVODES_LAPACK=0 PYCVODES_NO_KLU=1 python3 setup.py install + - cd - + - mkdir -p $HOME/.config/pip/; bash -c 'echo -e "[global]\nno-cache-dir = false\ndownload-cache = $(pwd)/ci_cache/pip_cache" >$HOME/.config/pip/pip.conf' + - bash -c "ulimit -v 2048000; .ci/run_ci.sh pyodesys $SUNDBASE" + + test-py2: + image: bjodah/bjodahimg18:v1.5 + environment: + - OMP_NUM_THREADS=1 + - ANYODE_NUM_THREADS=2 + - CPLUS_INCLUDE_PATH=/usr/include/suitesparse + - LIBRARY_PATH=/usr/lib/x86_64-linux-gnu + - LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu + commands: + - export SUNDBASE=$(pwd)/ci_cache/sund-3.2.1-klu + - bash -c "ulimit -v 2048000; .ci/test_py2.sh pyodesys $SUNDBASE" + + test-conda-recipe: + group: testing + image: bjodah/bjodahimg18dev:v1.5 + commands: + - export CONDA_PKGS_DIRS=$(pwd)/ci_cache/conda_packages + - PATH=/opt/miniconda3/bin:$PATH conda update conda-build + - PATH=/opt/miniconda3/bin:$PATH conda build --output-folder "deploy/public_html/branches/${CI_BRANCH}" conda-recipe deploy: - rsync: - host: hera.physchem.kth.se + rebuild-cache: + image: drillster/drone-volume-cache + rebuild: true + mount: + - ./ci_cache/sund-3.2.1-klu + - ./ci_cache/sund-4.1.0-nolapack-noklu-extended + - ./ci_cache/conda_packages + - ./ci_cache/pip_cache + volumes: + - /tmp/cache:/cache + + deploy: + image: drillster/drone-rsync + when: + event: [push] + hosts: [ "hera.physchem.kth.se" ] port: 22 user: pyodesys - source: deploy/ - target: ~ - recursive: true - delete: false + secrets: [ rsync_key ] # secret only set from event "push" not "pull_request" + source: ./deploy/public_html + target: ~/ diff --git a/.gitignore b/.gitignore index dd1f3750..f7533533 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ gks.svg .ipython/ .jupyter/* !.jupyter/jupyter_notebook_config.py +tmp/ +.gdb* diff --git a/README.rst b/README.rst index c7bf49b7..1be847d5 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,8 @@ pyodesys ======== -.. image:: http://hera.physchem.kth.se:9090/api/badges/bjodah/pyodesys/status.svg - :target: http://hera.physchem.kth.se:9090/bjodah/pyodesys +.. image:: http://hera.physchem.kth.se:8090/api/badges/bjodah/pyodesys/status.svg + :target: http://hera.physchem.kth.se:8090/bjodah/pyodesys :alt: Build status on Drone .. image:: https://circleci.com/gh/bjodah/pyodesys.svg?style=svg :target: https://circleci.com/gh/bjodah/pyodesys @@ -10,7 +10,7 @@ pyodesys .. image:: https://img.shields.io/pypi/v/pyodesys.svg :target: https://pypi.python.org/pypi/pyodesys :alt: PyPI version -.. image:: https://img.shields.io/badge/python-2.7,3.5,3.6-blue.svg +.. image:: https://img.shields.io/badge/python-3.6,3.7-blue.svg :target: https://www.python.org/ :alt: Python version .. image:: https://img.shields.io/pypi/l/pyodesys.svg diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml index a4eed350..ee87f1ae 100644 --- a/conda-recipe/meta.yaml +++ b/conda-recipe/meta.yaml @@ -1,9 +1,5 @@ {% set name = "pyodesys" %} -{% set version = GIT_DESCRIBE_TAG | replace("v", "") %} -{% set number = GIT_DESCRIBE_NUMBER | int %} -{% if number > 0 %} - {% set version = version + ".post" + GIT_DESCRIBE_NUMBER %} -{% endif %} +{% set version = "0.13.0.dev0+git" %} package: name: {{ name|lower }} @@ -33,7 +29,7 @@ requirements: - boost - pyodeint >=0.10.1 - pygslodeiv2 >=0.9.1 - - pycvodes >=0.11.9 + - pycvodes >=0.11.12 - pycompilation - pycodeexport >=0.1.2 - cython diff --git a/pyodesys/native/sources/_cvode_wrapper.pyx b/pyodesys/native/sources/_cvode_wrapper.pyx index 5920a488..337cec40 100644 --- a/pyodesys/native/sources/_cvode_wrapper.pyx +++ b/pyodesys/native/sources/_cvode_wrapper.pyx @@ -3,6 +3,7 @@ # distutils: language = c++ # distutils: extra_compile_args = -std=c++11 -fopenmp # distutils: extra_link_args = -fopenmp +# cython: language_level=3str from collections import Iterable @@ -54,8 +55,6 @@ ctypedef fused floating: ctypedef OdeSys[realtype, indextype] CvodesOdeSys -from odesys_util cimport adaptive_return - def _as_dict(unordered_map[string, int] nfo, unordered_map[string, double] nfo_dbl, @@ -93,7 +92,8 @@ def integrate_adaptive(floating [:, ::1] y0, bool record_order=False, bool record_fpe=False, realtype get_dx_max_factor=0.0, bool error_outside_bounds=False, realtype max_invariant_violation=0.0, special_settings=None, - bool autonomous_exprs=False, int nprealloc=500, vector[realtype] constraints=[]): + bool autonomous_exprs=False, int nprealloc=500, bool ew_ele=False, + vector[realtype] constraints=[]): cdef: realtype ** xyout = malloc(y0.shape[0]*sizeof(realtype *)) realtype [:,::1] xyout_view @@ -118,7 +118,12 @@ def integrate_adaptive(floating [:, ::1] y0, vector[realtype] atol_vec vector[realtype] special_settings_vec int idx, yi, tidx = 0 - realtype *** ew_ele = NULL + realtype ** ew_ele_arr = malloc(y0.shape[0]*sizeof(realtype*)) + cnp.npy_intp ew_ele_dims[3] + realtype [:,:,::1] ew_ele_view + + ew_ele_dims[1] = 2 + ew_ele_dims[2] = y0.shape[1] if np.isnan(y0).any(): raise ValueError("NaN found in y0") @@ -177,6 +182,8 @@ def integrate_adaptive(floating [:, ::1] y0, xyout[idx][0] = x0[idx] for yi in range(y0.shape[1]): xyout[idx][yi+1] = y0[idx, yi] + if ew_ele: + ew_ele_arr[idx] = malloc(2*nprealloc*y0.shape[1]*sizeof(realtype)) try: result = multi_adaptive[CvodesOdeSys]( @@ -185,7 +192,7 @@ def integrate_adaptive(floating [:, ::1] y0, &_dx0[0], &_dx_min[0], &_dx_max[0], with_jacobian, iter_type_from_name(_iter_t), linear_solver_from_name(linear_solver.lower().encode('UTF-8')), maxl, eps_lin, nderiv, return_on_root, autorestart, return_on_error, with_jtimes, - tidx, ew_ele, constraints + tidx, ew_ele_arr if ew_ele else NULL, constraints ) xout, yout = [], [] for idx in range(y0.shape[0]): @@ -210,6 +217,11 @@ def integrate_adaptive(floating [:, ::1] y0, systems[idx].current_info.nfo_vecint, root_indices[idx], root_out=None, mode='adaptive', success=success)) + if ew_ele: + ew_ele_dims[0] = dims[0] + ew_ele_view = ew_ele_arr[idx] + nfos[-1]['ew_ele'] = np.asarray(ew_ele_view, dtype=dtype) + free(ew_ele_arr[idx]) del systems[idx] finally: @@ -217,6 +229,7 @@ def integrate_adaptive(floating [:, ::1] y0, # memory of xyout[i] is freed by xyout_arr taking ownership # but memory of xyout itself must be freed here free(xyout) + free(ew_ele_arr) return xout, yout, nfos @@ -237,7 +250,7 @@ def integrate_predefined(floating [:, ::1] y0, bool record_order=False, bool record_fpe=False, realtype get_dx_max_factor=0.0, bool error_outside_bounds=False, realtype max_invariant_violation=0.0, special_settings=None, - bool autonomous_exprs=False, vector[realtype] constraints=[]): + bool autonomous_exprs=False, ew_ele=False, vector[realtype] constraints=[]): cdef: vector[CvodesOdeSys *] systems list nfos = [] @@ -258,7 +271,13 @@ def integrate_predefined(floating [:, ::1] y0, cnp.ndarray[realtype, ndim=2, mode='c'] params_arr = np.asarray(params, dtype=dtype) vector[realtype] atol_vec vector[realtype] special_settings_vec - realtype **ew_ele = NULL + realtype ** ew_ele_arr = malloc(y0.shape[0]*sizeof(realtype*)) + realtype [:,:,::1] ew_ele_view + cnp.npy_intp ew_ele_dims[3] + ew_ele_dims[0] = xout.shape[1] + ew_ele_dims[1] = 2 + ew_ele_dims[2] = y0.shape[1] + if np.isnan(y0).any(): raise ValueError("NaN found in y0") @@ -310,6 +329,8 @@ def integrate_predefined(floating [:, ::1] y0, systems[idx].record_jac_xvals = record_jac_xvals systems[idx].record_order = record_order systems[idx].record_fpe = record_fpe + if ew_ele: + ew_ele_arr[idx] = malloc(xout.shape[1]*2*y0.shape[1]*sizeof(realtype)) yout_arr = np.empty((y0.shape[0], xout.shape[1], y0.shape[1]), dtype=dtype) @@ -318,7 +339,7 @@ def integrate_predefined(floating [:, ::1] y0, xout_arr.data, yout_arr.data, mxsteps, &_dx0[0], &_dx_min[0], &_dx_max[0], with_jacobian, iter_type_from_name(_iter_t), linear_solver_from_name(linear_solver.lower().encode('UTF-8')), - maxl, eps_lin, nderiv, autorestart, return_on_error, with_jtimes, ew_ele, constraints) + maxl, eps_lin, nderiv, autorestart, return_on_error, with_jtimes, ew_ele_arr if ew_ele else NULL, constraints) for idx in range(y0.shape[0]): nreached = result[idx].first @@ -330,6 +351,12 @@ def integrate_predefined(floating [:, ::1] y0, root_indices=result[idx].second.first, root_out=result[idx].second.second, mode='predefined', success=success, nreached=nreached)) + if ew_ele: + ew_ele_view = ew_ele_arr[idx] + nfos[-1]['ew_ele'] = np.asarray(ew_ele_view, dtype=dtype) + free(ew_ele_arr[idx]) + del systems[idx] + free((ew_ele_arr)) return yout_arr, nfos diff --git a/pyodesys/native/tests/_tests.py b/pyodesys/native/tests/_tests.py index e5cc63aa..d3e980cd 100644 --- a/pyodesys/native/tests/_tests.py +++ b/pyodesys/native/tests/_tests.py @@ -187,12 +187,12 @@ def _test_PartiallySolved_symmetric_native_multi(NativeSys, multiple=False, forg def _test_multiple_adaptive(NativeSys, **kwargs): native = NativeSys.from_callback(sine, 2, 1) - _test_integrate_multiple_adaptive(native, integrator='native', **kwargs) + return _test_integrate_multiple_adaptive(native, integrator='native', **kwargs) def _test_multiple_predefined(NativeSys, **kwargs): native = NativeSys.from_callback(decay, 2, 1) - _test_integrate_multiple_predefined(native, integrator='native', **kwargs) + return _test_integrate_multiple_predefined(native, integrator='native', **kwargs) def _test_multiple_adaptive_chained(MySys, kw, **kwargs): diff --git a/pyodesys/native/tests/test_cvode.py b/pyodesys/native/tests/test_cvode.py index b315285e..4e06ca93 100644 --- a/pyodesys/native/tests/test_cvode.py +++ b/pyodesys/native/tests/test_cvode.py @@ -271,3 +271,13 @@ def test_render_native_cse_regression(): @requires('sym', 'pycvodes') def test_constraints(): _test_multiple_predefined(NativeSys, atol=1e-10, rtol=1e-10, constraints=[1.0, 1.0]) + + +@pytest.mark.slow +@requires('sym', 'pycvodes') +def test_ew_ele(): + for tst in [_test_multiple_predefined, _test_multiple_adaptive]: + results = tst(NativeSys, atol=1e-10, rtol=1e-10, ew_ele=True, nsteps=1400) + for res in results: + ee = res.info['ew_ele'] + assert ee.ndim == 3 and ee.shape[1] == 2 diff --git a/pyodesys/plotting.py b/pyodesys/plotting.py index 6cbb4638..91b5e6ae 100644 --- a/pyodesys/plotting.py +++ b/pyodesys/plotting.py @@ -28,7 +28,8 @@ def plot_result(x, y, indices=None, plot_kwargs_cb=None, ax=None, m=('o', 'v', '8', 's', 'p', 'x', '+', 'd', 's'), m_lim=-1, lines=None, interpolate=None, interp_from_deriv=None, names=None, latex_names=None, xlabel=None, ylabel=None, - xscale=None, yscale=None, legend=False, yerr=None, labels=None, tex_lbl_fmt='$%s$'): + xscale=None, yscale=None, legend=False, yerr=None, labels=None, tex_lbl_fmt='$%s$', + fig_kw=None, xlim=None, ylim=None): """ Plot the depepndent variables vs. the independent variable @@ -76,7 +77,7 @@ def plot_result(x, y, indices=None, plot_kwargs_cb=None, ax=None, import matplotlib.pyplot as plt if ax is None: - ax = plt.subplot(1, 1, 1) + _fig, ax = plt.subplots(1, 1, **(fig_kw or {})) if plot_kwargs_cb is None: def plot_kwargs_cb(idx, lines=False, markers=False, labels=None): @@ -191,6 +192,11 @@ def plot_kwargs_cb(idx, lines=False, markers=False, labels=None): pass else: ax.legend(**legend) + + if xlim: + ax.set_xlim(xlim) + if ylim: + ax.set_ylim(ylim) return ax diff --git a/pyodesys/tests/test_core.py b/pyodesys/tests/test_core.py index 8ede8d7b..fca39690 100644 --- a/pyodesys/tests/test_core.py +++ b/pyodesys/tests/test_core.py @@ -190,6 +190,7 @@ def _test_integrate_multiple_predefined(odes, **kwargs): assert np.allclose(yout[:, 0], ref) assert np.allclose(yout[:, 1], _y0[idx, 0] - ref + _y0[idx, 1]) assert info['nfev'] > 0 + return results @requires('scipy') @@ -293,6 +294,7 @@ def _ref(A, k, t): assert np.allclose(yout[:, 0], ref[0], atol=1e-5, rtol=1e-5) assert np.allclose(yout[:, 1], ref[1], atol=1e-5, rtol=1e-5) assert info['nfev'] > 0 + return results @requires('scipy') diff --git a/scripts/render_notebooks.sh b/scripts/render_notebooks.sh index 23c9bbca..c5b9fcfe 100755 --- a/scripts/render_notebooks.sh +++ b/scripts/render_notebooks.sh @@ -10,6 +10,6 @@ for ipynb in *.ipynb; do if [[ $PREC != "double" && $ipynb == "_robertson.ipynb" ]]; then continue fi - jupyter nbconvert --debug --to=html --ExecutePreprocessor.enabled=True --ExecutePreprocessor.timeout=420 $ipynb + jupyter nbconvert --debug --to=html --ExecutePreprocessor.enabled=True --ExecutePreprocessor.timeout=900 $ipynb done ../scripts/render_index.sh *.html diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh index e607060b..95f832e3 100755 --- a/scripts/run_tests.sh +++ b/scripts/run_tests.sh @@ -3,6 +3,6 @@ # $ ./scripts/run_tests.sh # or # $ ./scripts/run_tests.sh --cov pycvodes --cov-report html -${PYTHON:-python3} -m pytest -ra --slow --veryslow --doctest-modules --pep8 --flakes $@ +${PYTHON:-python3} -m pytest -ra --slow --veryslow --doctest-modules --pep8 --flakes -x --verbose $@ ${PYTHON:-python3} -m doctest README.rst rstcheck README.rst diff --git a/setup.py b/setup.py index 95d40624..e3f15554 100755 --- a/setup.py +++ b/setup.py @@ -78,7 +78,7 @@ def _path_under_setup(*args): _author, _author_email = open(_path_under_setup('AUTHORS'), 'rt').readline().split('<') extras_req = { - 'integrators': ['pyodeint>=0.10.1', 'pycvodes>=0.11.9', 'pygslodeiv2>=0.9.1'], + 'integrators': ['pyodeint>=0.10.1', 'pycvodes>=0.11.12', 'pygslodeiv2>=0.9.1'], 'native': ['pycompilation>=0.4.3', 'pycodeexport>=0.1.2', 'appdirs'], 'docs': ['Sphinx', 'sphinx_rtd_theme', 'numpydoc'], 'testing': ['pytest', 'pytest-cov', 'pytest-flakes', 'pytest-pep8', 'rstcheck']