├── .github
└── workflows
│ └── CI.yml
├── .gitignore
├── LICENSE
├── README.md
├── codecov.yml
├── dop853.code-workspace
├── ford.md
├── fpm.toml
├── media
├── dop853_backward.png
├── dop853_forward.png
├── logo.png
└── logo.svg
├── src
└── dop853_module.F90
└── test
├── dop853_example_original.f90
└── dop853_test.f90
/.github/workflows/CI.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on: [push]
3 | jobs:
4 |
5 | Build:
6 | runs-on: ${{ matrix.os }}
7 | permissions:
8 | contents: write
9 | strategy:
10 | fail-fast: false
11 | matrix:
12 | os: [ubuntu-latest]
13 | gcc_v: [10] # Version of GFortran we want to use.
14 | python-version: [3.9]
15 | env:
16 | FC: gfortran-${{ matrix.gcc_v }}
17 | GCC_V: ${{ matrix.gcc_v }}
18 |
19 | steps:
20 | - name: Checkout code
21 | uses: actions/checkout@v3
22 | with:
23 | submodules: recursive
24 |
25 | - name: Install Python
26 | uses: actions/setup-python@v4 # Use pip to install latest CMake, & FORD/Jin2For, etc.
27 | with:
28 | python-version: ${{ matrix.python-version }}
29 |
30 | - name: Setup Graphviz
31 | uses: ts-graphviz/setup-graphviz@v1
32 |
33 | - name: Setup Fortran Package Manager
34 | uses: fortran-lang/setup-fpm@v5
35 | with:
36 | github-token: ${{ secrets.GITHUB_TOKEN }}
37 |
38 | - name: Install Python dependencies
39 | if: contains( matrix.os, 'ubuntu')
40 | run: |
41 | python -m pip install --upgrade pip
42 | pip install ford numpy matplotlib
43 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
44 |
45 | - name: Install GFortran Linux
46 | if: contains( matrix.os, 'ubuntu')
47 | run: |
48 | sudo apt-get install lcov
49 | sudo update-alternatives \
50 | --install /usr/bin/gcc gcc /usr/bin/gcc-${{ matrix.gcc_v }} 100 \
51 | --slave /usr/bin/gfortran gfortran /usr/bin/gfortran-${{ matrix.gcc_v }} \
52 | --slave /usr/bin/gcov gcov /usr/bin/gcov-${{ matrix.gcc_v }}
53 |
54 | # - name: Compile
55 | # run: fpm build --profile release
56 |
57 | - name: Run tests
58 | run: fpm test --profile debug --flag -coverage
59 |
60 | - name: Create coverage report
61 | run: |
62 | mkdir -p ${{ env.COV_DIR }}
63 | lcov --capture --initial --base-directory . --directory build/gfortran_*/ --output-file ${{ env.COV_DIR }}/coverage.base
64 | lcov --capture --base-directory . --directory build/gfortran_*/ --output-file ${{ env.COV_DIR }}/coverage.capture
65 | lcov --add-tracefile ${{ env.COV_DIR }}/coverage.base --add-tracefile ${{ env.COV_DIR }}/coverage.capture --output-file ${{ env.COV_DIR }}/coverage.info
66 | env:
67 | COV_DIR: build/coverage
68 |
69 | - name: Upload coverage report
70 | uses: codecov/codecov-action@v3
71 | with:
72 | files: build/coverage/coverage.info
73 |
74 | - name: Build documentation
75 | run: ford ./ford.md
76 |
77 | - name: Deploy Documentation
78 | if: github.ref == 'refs/heads/master'
79 | uses: JamesIves/github-pages-deploy-action@v4.4.1
80 | with:
81 | branch: gh-pages # The branch the action should deploy to.
82 | folder: doc # The folder the action should deploy.
83 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /bin
2 | /lib
3 | /doc
4 | /build
5 |
6 | # Compiled Object files
7 | *.slo
8 | *.lo
9 | *.o
10 | *.obj
11 |
12 | # Precompiled Headers
13 | *.gch
14 | *.pch
15 |
16 | # Compiled Dynamic libraries
17 | *.so
18 | *.dylib
19 | *.dll
20 |
21 | # Fortran module files
22 | *.mod
23 |
24 | # Compiled Static libraries
25 | *.lai
26 | *.la
27 | *.a
28 | *.lib
29 |
30 | # Executables
31 | *.exe
32 | *.out
33 | *.app
34 |
35 | # mac
36 | .DS_Store
37 |
38 | # misc
39 | /*.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Modern Fortran Edition of the DOP853 ODE Solver
2 | https://github.com/jacobwilliams/dop853
3 |
4 | Copyright (c) 2015-2022, Jacob Williams
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification,
8 | are permitted provided that the following conditions are met:
9 |
10 | * Redistributions of source code must retain the above copyright notice, this
11 | list of conditions and the following disclaimer.
12 |
13 | * Redistributions in binary form must reproduce the above copyright notice, this
14 | list of conditions and the following disclaimer in the documentation and/or
15 | other materials provided with the distribution.
16 |
17 | * The names of its contributors may not be used to endorse or promote products
18 | derived from this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | --------------------------------------------------------------------------------
32 | Original DOP853 License
33 | --------------------------------------------------------------------------------
34 |
35 | Copyright (c) 2004, Ernst Hairer
36 |
37 | Redistribution and use in source and binary forms, with or without
38 | modification, are permitted provided that the following conditions are
39 | met:
40 |
41 | - Redistributions of source code must retain the above copyright
42 | notice, this list of conditions and the following disclaimer.
43 |
44 | - Redistributions in binary form must reproduce the above copyright
45 | notice, this list of conditions and the following disclaimer in the
46 | documentation and/or other materials provided with the distribution.
47 |
48 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS �AS
49 | IS� AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
50 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
51 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
52 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
53 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
54 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
55 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
56 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
57 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
58 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | ============
3 |
4 | This is a modern Fortran (2003/2008) implementation of Hairer's DOP853 ODE solver. The original FORTRAN 77 code has been extensively refactored, and is now object-oriented and thread-safe, with an easy-to-use class interface. DOP853 is an explicit Runge-Kutta method of order 8(5,3) due to Dormand & Prince (with stepsize control and dense output).
5 |
6 | This project is hosted on [GitHub](https://github.com/jacobwilliams/dop853).
7 |
8 | ## Status
9 |
10 | [](https://github.com/jacobwilliams/dop853/releases/latest)
11 | [](https://github.com/jacobwilliams/dop853/actions)
12 | [](https://codecov.io/gh/jacobwilliams/dop853)
13 | [](https://github.com/jacobwilliams/dop853/commits/master)
14 |
15 | ## Example
16 |
17 | Basic use of the solver is shown here. The main methods in the `dop853_class` are `initialize()` and `integrate()`.
18 |
19 | ```fortran
20 | program dop853_example
21 |
22 | use dop853_module, wp => dop853_wp
23 | use iso_fortran_env, only: output_unit
24 |
25 | implicit none
26 |
27 | integer,parameter :: n = 2 !! dimension of the system
28 | real(wp),parameter :: tol = 1.0e-12_wp !! integration tolerance
29 | real(wp),parameter :: x0 = 0.0_wp !! initial x value
30 | real(wp),parameter :: xf = 100.0_wp !! endpoint of integration
31 | real(wp),dimension(n),parameter :: y0 = [0.0_wp,0.1_wp] !! initial y value
32 |
33 | type(dop853_class) :: prop
34 | real(wp),dimension(n) :: y
35 | real(wp),dimension(1) :: rtol,atol
36 | real(wp) :: x
37 | integer :: idid
38 | logical :: status_ok
39 |
40 | x = x0 ! initial conditions
41 | y = y0 !
42 | rtol = tol ! set tolerances
43 | atol = tol !
44 |
45 | !initialize the integrator:
46 | call prop%initialize(fcn=fvpol,n=n,status_ok=status_ok)
47 | if (.not. status_ok) error stop 'initialization error'
48 |
49 | !now, perform the integration:
50 | call prop%integrate(x,y,xf,rtol,atol,iout=0,idid=idid)
51 |
52 | !print solution:
53 | write (output_unit,'(1X,A,F6.2,A,2E18.10)') &
54 | 'x =',x ,' y =',y(1),y(2)
55 |
56 | contains
57 |
58 | subroutine fvpol(me,x,y,f)
59 | !! Right-hand side of van der Pol's equation
60 |
61 | implicit none
62 |
63 | class(dop853_class),intent(inout) :: me
64 | real(wp),intent(in) :: x
65 | real(wp),dimension(:),intent(in) :: y
66 | real(wp),dimension(:),intent(out) :: f
67 |
68 | real(wp),parameter :: mu = 0.2_wp
69 |
70 | f(1) = y(2)
71 | f(2) = mu*(1.0_wp-y(1)**2)*y(2) - y(1)
72 |
73 | end subroutine fvpol
74 |
75 | end program dop853_example
76 | ```
77 |
78 | The result is:
79 |
80 | ```
81 | x =100.00 y = -0.1360372426E+01 0.1325538438E+01
82 | ```
83 |
84 | For dense output, see the example in the `src/tests` directory.
85 |
86 | ## Building DOP853
87 |
88 | A [Fortran Package Manager](https://github.com/fortran-lang/fpm) manifest file is included, so that the library and tests cases can be compiled with FPM. For example:
89 |
90 | ```
91 | fpm build --profile release
92 | fpm test --profile release
93 | ```
94 |
95 | To use `dop853` within your FPM project, add the following to your `fpm.toml` file:
96 | ```toml
97 | [dependencies]
98 | dop853 = { git="https://github.com/jacobwilliams/dop853.git" }
99 | ```
100 |
101 | By default, the library is built with double precision (`real64`) real values. Explicitly specifying the real kind can be done using the following processor flags:
102 |
103 | Preprocessor flag | Kind | Number of bytes
104 | ----------------- | ----- | ---------------
105 | `REAL32` | `real(kind=real32)` | 4
106 | `REAL64` | `real(kind=real64)` | 8
107 | `REAL128` | `real(kind=real128)` | 16
108 |
109 | For example, to build a single precision version of the library, use:
110 |
111 | ```
112 | fpm build --profile release --flag "-DREAL32"
113 | ```
114 |
115 | To generate the documentation using [FORD](https://github.com/Fortran-FOSS-Programmers/ford), run:
116 |
117 | ```
118 | ford dop853.md
119 | ```
120 |
121 | ## 3rd Party Dependencies
122 |
123 | The unit tests require [pyplot-fortran](https://github.com/jacobwilliams/pyplot-fortran) which will be automatically downloaded by FPM.
124 |
125 | ## Documentation
126 |
127 | The latest API documentation for the `master` branch can be found [here](https://jacobwilliams.github.io/dop853/). This is generated by processing the source files with [FORD](https://github.com/Fortran-FOSS-Programmers/ford).
128 |
129 | ## References
130 |
131 | 1. E. Hairer, S.P. Norsett and G. Wanner, "[Solving ordinary
132 | Differential Equations I. Nonstiff Problems](http://www.unige.ch/~hairer/books.html)", 2nd edition.
133 | Springer Series in Computational Mathematics,
134 | Springer-Verlag (1993).
135 | 2. Ernst Hairer's website: [Fortran and Matlab Codes](http://www.unige.ch/~hairer/software.html)
136 |
137 | ## License
138 |
139 | * [Original license for Hairer's codes](http://www.unige.ch/~hairer/prog/licence.txt).
140 | * The updates are released under a [similar BSD-style license](https://raw.githubusercontent.com/jacobwilliams/dop853/master/LICENSE).
141 |
142 | ## See also
143 | * [numbalsoda](https://github.com/Nicholaswogan/numbalsoda) Python wrapper to this code.
144 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | comment:
2 | layout: header, changes, diff, sunburst
3 | coverage:
4 | ignore:
5 | - test
6 | - doc
7 | status:
8 | patch:
9 | default:
10 | target: 20%
11 | project:
12 | default:
13 | target: 70%
14 |
--------------------------------------------------------------------------------
/dop853.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": "."
5 | }
6 | ],
7 | "settings": {
8 | "files.trimTrailingWhitespace": true,
9 | "editor.insertSpaces": true,
10 | "editor.tabSize": 4,
11 | "editor.trimAutoWhitespace": true
12 | }
13 | }
--------------------------------------------------------------------------------
/ford.md:
--------------------------------------------------------------------------------
1 | project: dop853
2 | project_dir: ./src
3 | output_dir: ./doc
4 | media_dir: ./media
5 | project_github: https://github.com/jacobwilliams/dop853
6 | summary: Modern Fortran Implementation of the DOP853 ODE Solver
7 | author: Jacob Williams
8 | github: https://github.com/jacobwilliams
9 | predocmark_alt: >
10 | predocmark: <
11 | docmark_alt:
12 | docmark: !
13 | display: public
14 | private
15 | protected
16 | source: true
17 | graph: true
18 | extra_mods: pyplot_module:https://github.com/jacobwilliams/pyplot-fortran
19 | iso_fortran_env:https://gcc.gnu.org/onlinedocs/gfortran/ISO_005fFORTRAN_005fENV.html
20 |
21 | {!README.md!}
--------------------------------------------------------------------------------
/fpm.toml:
--------------------------------------------------------------------------------
1 | name = "dop853"
2 | author = "Jacob Williams"
3 | maintainer = "Jacob Williams"
4 | copyright = "Copyright (c) 2015-2022, Jacob Williams"
5 | license = "BSD"
6 | description = "Modern Fortran Edition of Hairer's DOP853 ODE Solver"
7 | homepage = "https://github.com/jacobwilliams/dop853"
8 | keywords = ["runge-kutta"]
9 |
10 | [dev-dependencies]
11 | pyplot-fortran= { git = "https://github.com/jacobwilliams/pyplot-fortran", rev = "3.2.0" }
12 |
13 | [library]
14 | source-dir = "src"
15 |
16 | [install]
17 | library = true
18 |
19 | [build]
20 | auto-executables = false
21 | auto-examples = false
22 | auto-tests = true
23 |
24 |
--------------------------------------------------------------------------------
/media/dop853_backward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobwilliams/dop853/ccb2f3e8d0947d247e76d3eabd8a0433b608e94a/media/dop853_backward.png
--------------------------------------------------------------------------------
/media/dop853_forward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobwilliams/dop853/ccb2f3e8d0947d247e76d3eabd8a0433b608e94a/media/dop853_forward.png
--------------------------------------------------------------------------------
/media/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacobwilliams/dop853/ccb2f3e8d0947d247e76d3eabd8a0433b608e94a/media/logo.png
--------------------------------------------------------------------------------
/media/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
117 |
--------------------------------------------------------------------------------
/src/dop853_module.F90:
--------------------------------------------------------------------------------
1 | !*****************************************************************************************
2 | !> author: Jacob Williams
3 | !
4 | ! Modern Fortran Edition of the DOP853 ODE Solver.
5 | !
6 | !### See also
7 | ! * [DOP853.f](http://www.unige.ch/~hairer/prog/nonstiff/dop853.f) -- The original code
8 | !
9 | !### History
10 | ! * Jacob Williams : December 2015 : Created module from the DOP853 Fortran 77 code.
11 | ! * Development continues at [GitHub](https://github.com/jacobwilliams/dop853).
12 | !
13 | !@note The default real kind (`wp`) can be
14 | ! changed using optional preprocessor flags.
15 | ! This library was built with real kind:
16 | #ifdef REAL32
17 | ! `real(kind=real32)` [4 bytes]
18 | #elif REAL64
19 | ! `real(kind=real64)` [8 bytes]
20 | #elif REAL128
21 | ! `real(kind=real128)` [16 bytes]
22 | #else
23 | ! `real(kind=real64)` [8 bytes]
24 | #endif
25 |
26 |
27 | module dop853_module
28 |
29 | use, intrinsic :: iso_fortran_env
30 |
31 | implicit none
32 |
33 | private
34 |
35 | #ifdef REAL32
36 | integer,parameter,public :: dop853_wp = real32 !! real kind used by this module [4 bytes]
37 | #elif REAL64
38 | integer,parameter,public :: dop853_wp = real64 !! real kind used by this module [8 bytes]
39 | #elif REAL128
40 | integer,parameter,public :: dop853_wp = real128 !! real kind used by this module [16 bytes]
41 | #else
42 | integer,parameter,public :: dop853_wp = real64 !! real kind used by this module [8 bytes]
43 | #endif
44 |
45 | integer,parameter :: wp = dop853_wp !! local copy of `dop853_wp` with a shorter name
46 | real(wp),parameter :: uround = epsilon(1.0_wp) !! machine \( \epsilon \)
47 |
48 | !integration constants (formerly in dp86co):
49 | real(wp),parameter :: c2 = 0.526001519587677318785587544488e-01_wp
50 | real(wp),parameter :: c3 = 0.789002279381515978178381316732e-01_wp
51 | real(wp),parameter :: c4 = 0.118350341907227396726757197510_wp
52 | real(wp),parameter :: c5 = 0.281649658092772603273242802490_wp
53 | real(wp),parameter :: c6 = 0.333333333333333333333333333333_wp
54 | real(wp),parameter :: c7 = 0.25_wp
55 | real(wp),parameter :: c8 = 0.307692307692307692307692307692_wp
56 | real(wp),parameter :: c9 = 0.651282051282051282051282051282_wp
57 | real(wp),parameter :: c10 = 0.6_wp
58 | real(wp),parameter :: c11 = 0.857142857142857142857142857142_wp
59 | real(wp),parameter :: c14 = 0.1_wp
60 | real(wp),parameter :: c15 = 0.2_wp
61 | real(wp),parameter :: c16 = 0.777777777777777777777777777778_wp
62 | real(wp),parameter :: b1 = 5.42937341165687622380535766363e-2_wp
63 | real(wp),parameter :: b6 = 4.45031289275240888144113950566_wp
64 | real(wp),parameter :: b7 = 1.89151789931450038304281599044_wp
65 | real(wp),parameter :: b8 = -5.8012039600105847814672114227_wp
66 | real(wp),parameter :: b9 = 3.1116436695781989440891606237e-1_wp
67 | real(wp),parameter :: b10 = -1.52160949662516078556178806805e-1_wp
68 | real(wp),parameter :: b11 = 2.01365400804030348374776537501e-1_wp
69 | real(wp),parameter :: b12 = 4.47106157277725905176885569043e-2_wp
70 | real(wp),parameter :: bhh1 = 0.244094488188976377952755905512_wp
71 | real(wp),parameter :: bhh2 = 0.733846688281611857341361741547_wp
72 | real(wp),parameter :: bhh3 = 0.220588235294117647058823529412e-1_wp
73 | real(wp),parameter :: er1 = 0.1312004499419488073250102996e-01_wp
74 | real(wp),parameter :: er6 = -0.1225156446376204440720569753e+01_wp
75 | real(wp),parameter :: er7 = -0.4957589496572501915214079952_wp
76 | real(wp),parameter :: er8 = 0.1664377182454986536961530415e+01_wp
77 | real(wp),parameter :: er9 = -0.3503288487499736816886487290_wp
78 | real(wp),parameter :: er10 = 0.3341791187130174790297318841_wp
79 | real(wp),parameter :: er11 = 0.8192320648511571246570742613e-01_wp
80 | real(wp),parameter :: er12 = -0.2235530786388629525884427845e-01_wp
81 | real(wp),parameter :: a21 = 5.26001519587677318785587544488e-2_wp
82 | real(wp),parameter :: a31 = 1.97250569845378994544595329183e-2_wp
83 | real(wp),parameter :: a32 = 5.91751709536136983633785987549e-2_wp
84 | real(wp),parameter :: a41 = 2.95875854768068491816892993775e-2_wp
85 | real(wp),parameter :: a43 = 8.87627564304205475450678981324e-2_wp
86 | real(wp),parameter :: a51 = 2.41365134159266685502369798665e-1_wp
87 | real(wp),parameter :: a53 = -8.84549479328286085344864962717e-1_wp
88 | real(wp),parameter :: a54 = 9.24834003261792003115737966543e-1_wp
89 | real(wp),parameter :: a61 = 3.7037037037037037037037037037e-2_wp
90 | real(wp),parameter :: a64 = 1.70828608729473871279604482173e-1_wp
91 | real(wp),parameter :: a65 = 1.25467687566822425016691814123e-1_wp
92 | real(wp),parameter :: a71 = 3.7109375e-2_wp
93 | real(wp),parameter :: a74 = 1.70252211019544039314978060272e-1_wp
94 | real(wp),parameter :: a75 = 6.02165389804559606850219397283e-2_wp
95 | real(wp),parameter :: a76 = -1.7578125e-2_wp
96 | real(wp),parameter :: a81 = 3.70920001185047927108779319836e-2_wp
97 | real(wp),parameter :: a84 = 1.70383925712239993810214054705e-1_wp
98 | real(wp),parameter :: a85 = 1.07262030446373284651809199168e-1_wp
99 | real(wp),parameter :: a86 = -1.53194377486244017527936158236e-2_wp
100 | real(wp),parameter :: a87 = 8.27378916381402288758473766002e-3_wp
101 | real(wp),parameter :: a91 = 6.24110958716075717114429577812e-1_wp
102 | real(wp),parameter :: a94 = -3.36089262944694129406857109825_wp
103 | real(wp),parameter :: a95 = -8.68219346841726006818189891453e-1_wp
104 | real(wp),parameter :: a96 = 2.75920996994467083049415600797e+1_wp
105 | real(wp),parameter :: a97 = 2.01540675504778934086186788979e+1_wp
106 | real(wp),parameter :: a98 = -4.34898841810699588477366255144e+1_wp
107 | real(wp),parameter :: a101 = 4.77662536438264365890433908527e-1_wp
108 | real(wp),parameter :: a104 = -2.48811461997166764192642586468_wp
109 | real(wp),parameter :: a105 = -5.90290826836842996371446475743e-1_wp
110 | real(wp),parameter :: a106 = 2.12300514481811942347288949897e+1_wp
111 | real(wp),parameter :: a107 = 1.52792336328824235832596922938e+1_wp
112 | real(wp),parameter :: a108 = -3.32882109689848629194453265587e+1_wp
113 | real(wp),parameter :: a109 = -2.03312017085086261358222928593e-2_wp
114 | real(wp),parameter :: a111 = -9.3714243008598732571704021658e-1_wp
115 | real(wp),parameter :: a114 = 5.18637242884406370830023853209_wp
116 | real(wp),parameter :: a115 = 1.09143734899672957818500254654_wp
117 | real(wp),parameter :: a116 = -8.14978701074692612513997267357_wp
118 | real(wp),parameter :: a117 = -1.85200656599969598641566180701e+1_wp
119 | real(wp),parameter :: a118 = 2.27394870993505042818970056734e+1_wp
120 | real(wp),parameter :: a119 = 2.49360555267965238987089396762_wp
121 | real(wp),parameter :: a1110 = -3.0467644718982195003823669022_wp
122 | real(wp),parameter :: a121 = 2.27331014751653820792359768449_wp
123 | real(wp),parameter :: a124 = -1.05344954667372501984066689879e+1_wp
124 | real(wp),parameter :: a125 = -2.00087205822486249909675718444_wp
125 | real(wp),parameter :: a126 = -1.79589318631187989172765950534e+1_wp
126 | real(wp),parameter :: a127 = 2.79488845294199600508499808837e+1_wp
127 | real(wp),parameter :: a128 = -2.85899827713502369474065508674_wp
128 | real(wp),parameter :: a129 = -8.87285693353062954433549289258_wp
129 | real(wp),parameter :: a1210 = 1.23605671757943030647266201528e+1_wp
130 | real(wp),parameter :: a1211 = 6.43392746015763530355970484046e-1_wp
131 | real(wp),parameter :: a141 = 5.61675022830479523392909219681e-2_wp
132 | real(wp),parameter :: a147 = 2.53500210216624811088794765333e-1_wp
133 | real(wp),parameter :: a148 = -2.46239037470802489917441475441e-1_wp
134 | real(wp),parameter :: a149 = -1.24191423263816360469010140626e-1_wp
135 | real(wp),parameter :: a1410 = 1.5329179827876569731206322685e-1_wp
136 | real(wp),parameter :: a1411 = 8.20105229563468988491666602057e-3_wp
137 | real(wp),parameter :: a1412 = 7.56789766054569976138603589584e-3_wp
138 | real(wp),parameter :: a1413 = -8.298e-3_wp
139 | real(wp),parameter :: a151 = 3.18346481635021405060768473261e-2_wp
140 | real(wp),parameter :: a156 = 2.83009096723667755288322961402e-2_wp
141 | real(wp),parameter :: a157 = 5.35419883074385676223797384372e-2_wp
142 | real(wp),parameter :: a158 = -5.49237485713909884646569340306e-2_wp
143 | real(wp),parameter :: a1511 = -1.08347328697249322858509316994e-4_wp
144 | real(wp),parameter :: a1512 = 3.82571090835658412954920192323e-4_wp
145 | real(wp),parameter :: a1513 = -3.40465008687404560802977114492e-4_wp
146 | real(wp),parameter :: a1514 = 1.41312443674632500278074618366e-1_wp
147 | real(wp),parameter :: a161 = -4.28896301583791923408573538692e-1_wp
148 | real(wp),parameter :: a166 = -4.69762141536116384314449447206_wp
149 | real(wp),parameter :: a167 = 7.68342119606259904184240953878_wp
150 | real(wp),parameter :: a168 = 4.06898981839711007970213554331_wp
151 | real(wp),parameter :: a169 = 3.56727187455281109270669543021e-1_wp
152 | real(wp),parameter :: a1613 = -1.39902416515901462129418009734e-3_wp
153 | real(wp),parameter :: a1614 = 2.9475147891527723389556272149_wp
154 | real(wp),parameter :: a1615 = -9.15095847217987001081870187138_wp
155 | real(wp),parameter :: d41 = -0.84289382761090128651353491142e+01_wp
156 | real(wp),parameter :: d46 = 0.56671495351937776962531783590_wp
157 | real(wp),parameter :: d47 = -0.30689499459498916912797304727e+01_wp
158 | real(wp),parameter :: d48 = 0.23846676565120698287728149680e+01_wp
159 | real(wp),parameter :: d49 = 0.21170345824450282767155149946e+01_wp
160 | real(wp),parameter :: d410 = -0.87139158377797299206789907490_wp
161 | real(wp),parameter :: d411 = 0.22404374302607882758541771650e+01_wp
162 | real(wp),parameter :: d412 = 0.63157877876946881815570249290_wp
163 | real(wp),parameter :: d413 = -0.88990336451333310820698117400e-01_wp
164 | real(wp),parameter :: d414 = 0.18148505520854727256656404962e+02_wp
165 | real(wp),parameter :: d415 = -0.91946323924783554000451984436e+01_wp
166 | real(wp),parameter :: d416 = -0.44360363875948939664310572000e+01_wp
167 | real(wp),parameter :: d51 = 0.10427508642579134603413151009e+02_wp
168 | real(wp),parameter :: d56 = 0.24228349177525818288430175319e+03_wp
169 | real(wp),parameter :: d57 = 0.16520045171727028198505394887e+03_wp
170 | real(wp),parameter :: d58 = -0.37454675472269020279518312152e+03_wp
171 | real(wp),parameter :: d59 = -0.22113666853125306036270938578e+02_wp
172 | real(wp),parameter :: d510 = 0.77334326684722638389603898808e+01_wp
173 | real(wp),parameter :: d511 = -0.30674084731089398182061213626e+02_wp
174 | real(wp),parameter :: d512 = -0.93321305264302278729567221706e+01_wp
175 | real(wp),parameter :: d513 = 0.15697238121770843886131091075e+02_wp
176 | real(wp),parameter :: d514 = -0.31139403219565177677282850411e+02_wp
177 | real(wp),parameter :: d515 = -0.93529243588444783865713862664e+01_wp
178 | real(wp),parameter :: d516 = 0.35816841486394083752465898540e+02_wp
179 | real(wp),parameter :: d61 = 0.19985053242002433820987653617e+02_wp
180 | real(wp),parameter :: d66 = -0.38703730874935176555105901742e+03_wp
181 | real(wp),parameter :: d67 = -0.18917813819516756882830838328e+03_wp
182 | real(wp),parameter :: d68 = 0.52780815920542364900561016686e+03_wp
183 | real(wp),parameter :: d69 = -0.11573902539959630126141871134e+02_wp
184 | real(wp),parameter :: d610 = 0.68812326946963000169666922661e+01_wp
185 | real(wp),parameter :: d611 = -0.10006050966910838403183860980e+01_wp
186 | real(wp),parameter :: d612 = 0.77771377980534432092869265740_wp
187 | real(wp),parameter :: d613 = -0.27782057523535084065932004339e+01_wp
188 | real(wp),parameter :: d614 = -0.60196695231264120758267380846e+02_wp
189 | real(wp),parameter :: d615 = 0.84320405506677161018159903784e+02_wp
190 | real(wp),parameter :: d616 = 0.11992291136182789328035130030e+02_wp
191 | real(wp),parameter :: d71 = -0.25693933462703749003312586129e+02_wp
192 | real(wp),parameter :: d76 = -0.15418974869023643374053993627e+03_wp
193 | real(wp),parameter :: d77 = -0.23152937917604549567536039109e+03_wp
194 | real(wp),parameter :: d78 = 0.35763911791061412378285349910e+03_wp
195 | real(wp),parameter :: d79 = 0.93405324183624310003907691704e+02_wp
196 | real(wp),parameter :: d710 = -0.37458323136451633156875139351e+02_wp
197 | real(wp),parameter :: d711 = 0.10409964950896230045147246184e+03_wp
198 | real(wp),parameter :: d712 = 0.29840293426660503123344363579e+02_wp
199 | real(wp),parameter :: d713 = -0.43533456590011143754432175058e+02_wp
200 | real(wp),parameter :: d714 = 0.96324553959188282948394950600e+02_wp
201 | real(wp),parameter :: d715 = -0.39177261675615439165231486172e+02_wp
202 | real(wp),parameter :: d716 = -0.14972683625798562581422125276e+03_wp
203 |
204 | type,public :: dop853_class
205 |
206 | private
207 |
208 | !internal variables:
209 | integer :: n = 0 !! the dimension of the system
210 | integer :: nfcn = 0 !! number of function evaluations
211 | integer :: nstep = 0 !! number of computed steps
212 | integer :: naccpt = 0 !! number of accepted steps
213 | integer :: nrejct = 0 !! number of rejected steps (due to error test),
214 | !! (step rejections in the first step are not counted)
215 | integer :: nrdens = 0 !! number of components, for which dense output
216 | !! is required. for `0 < nrdens < n` the components
217 | !! (for which dense output is required) have to be
218 | !! specified in `icomp(1),...,icomp(nrdens)`.
219 | real(wp) :: h = 0.0_wp !! predicted step size of the last accepted step
220 |
221 | !input paramters:
222 | ! these parameters allow
223 | ! to adapt the code to the problem and to the needs of
224 | ! the user. set them on class initialization.
225 |
226 | integer :: iprint = output_unit !! switch for printing error messages
227 | !! if `iprint==0` no messages are being printed
228 | !! if `iprint/=0` messages are printed with
229 | !! `write (iprint,*)` ...
230 |
231 | integer :: nmax = 100000 !! the maximal number of allowed steps.
232 | integer :: nstiff = 1000 !! test for stiffness is activated after step number
233 | !! `j*nstiff` (`j` integer), provided `nstiff>0`.
234 | !! for negative `nstiff` the stiffness test is
235 | !! never activated.
236 | real(wp) :: hinitial = 0.0_wp !! initial step size, for `hinitial=0` an initial guess
237 | !! is computed with help of the function [[hinit]].
238 | real(wp) :: hmax = 0.0_wp !! maximal step size, defaults to `xend-x` if `hmax=0`.
239 | real(wp) :: safe = 0.9_wp !! safety factor in step size prediction
240 | real(wp) :: fac1 = 0.333_wp !! parameter for step size selection.
241 | !! the new step size is chosen subject to the restriction
242 | !! `fac1 <= hnew/hold <= fac2`
243 | real(wp) :: fac2 = 6.0_wp !! parameter for step size selection.
244 | !! the new step size is chosen subject to the restriction
245 | !! `fac1 <= hnew/hold <= fac2`
246 | real(wp) :: beta = 0.0_wp !! is the `beta` for stabilized step size control
247 | !! (see section iv.2). positive values of beta ( <= 0.04 )
248 | !! make the step size control more stable.
249 |
250 | integer,dimension(:),allocatable :: icomp !! `dimension(nrdens)`
251 | !! the components for which dense output is required
252 | real(wp),dimension(:),allocatable :: cont !! `dimension(8*nrdens)`
253 |
254 | integer :: iout = 0 !! copy of `iout` input to [[dop853]]
255 |
256 | !formerly in the condo8 common block:
257 | real(wp) :: xold = 0.0_wp
258 | real(wp) :: hout = 0.0_wp
259 |
260 | !user-defined procedures:
261 | procedure(deriv_func),pointer :: fcn => null() !! subroutine computing the value of `f(x,y)`
262 | procedure(solout_func),pointer :: solout => null() !! subroutine providing the
263 | !! numerical solution during integration.
264 | !! if `iout>=1`, it is called during integration.
265 |
266 | contains
267 |
268 | private
269 |
270 | procedure,public :: initialize => set_parameters !! initialization routine.
271 | procedure,public :: integrate => dop853 !! main integration routine.
272 | procedure,public :: destroy => destroy_dop853 !! destructor.
273 | procedure,public :: info => get_dop853_info !! to get info after a run.
274 |
275 | procedure :: dp86co
276 | procedure :: hinit
277 | procedure,public :: contd8 !! can be called in user's [[solout_func]] for dense output.
278 |
279 | end type dop853_class
280 |
281 | abstract interface
282 |
283 | subroutine deriv_func(me,x,y,f)
284 | !! subroutine computing the value of \( dy/dx = f(x,y) \)
285 | import :: wp,dop853_class
286 | implicit none
287 | class(dop853_class),intent(inout) :: me
288 | real(wp),intent(in) :: x !! independent variable \(x\)
289 | real(wp),dimension(:),intent(in) :: y !! state vector \( y(x) \) [size n]
290 | real(wp),dimension(:),intent(out) :: f !! derivative vector \( f(x,y) = dy/dx \) [size n]
291 | end subroutine deriv_func
292 |
293 | subroutine solout_func(me,nr,xold,x,y,irtrn,xout)
294 | !! `solout` furnishes the solution `y` at the `nr`-th
295 | !! grid-point `x` (thereby the initial value is
296 | !! the first grid-point).
297 | import :: wp,dop853_class
298 | implicit none
299 | class(dop853_class),intent(inout) :: me
300 | integer,intent(in) :: nr !! grid point (0,1,...)
301 | real(wp),intent(in) :: xold !! the preceding grid point
302 | real(wp),intent(in) :: x !! current grid point
303 | real(wp),dimension(:),intent(in) :: y !! state vector \( y(x) \) [size n]
304 | integer,intent(inout) :: irtrn !! serves to interrupt the integration. if
305 | !! `irtrn` is set `<0`, [[dop853]] will return to
306 | !! the calling program. if the numerical solution
307 | !! is altered in `solout`, set `irtrn = 2`.
308 | real(wp),intent(out) :: xout !! `xout` can be used for efficient intermediate output
309 | !! if one puts `iout=3`. when `nr=1` define the first
310 | !! output point `xout` in `solout`. the subroutine
311 | !! `solout` will be called only when `xout` is in the
312 | !! interval `[xold,x]`; during this call
313 | !! a new value for `xout` can be defined, etc.
314 | end subroutine solout_func
315 |
316 | end interface
317 |
318 | contains
319 | !*****************************************************************************************
320 |
321 | !*****************************************************************************************
322 | !>
323 | ! Get info from a [[dop853_class]].
324 |
325 | subroutine get_dop853_info(me,n,nfcn,nstep,naccpt,nrejct,h,iout)
326 |
327 | implicit none
328 |
329 | class(dop853_class),intent(in) :: me
330 | integer,intent(out),optional :: n !! dimension of the system
331 | integer,intent(out),optional :: nfcn !! number of function evaluations
332 | integer,intent(out),optional :: nstep !! number of computed steps
333 | integer,intent(out),optional :: naccpt !! number of accepted steps
334 | integer,intent(out),optional :: nrejct !! number of rejected steps (due to error test),
335 | !! (step rejections in the first step are not counted)
336 | real(wp),intent(out),optional :: h !! predicted step size of the last accepted step
337 | integer,intent(out),optional :: iout !! `iout` flag passed into [[dop853]], used to
338 | !! specify how `solout` is called during integration.
339 |
340 | if (present(n )) n = me%n
341 | if (present(nfcn )) nfcn = me%nfcn
342 | if (present(nstep )) nstep = me%nstep
343 | if (present(naccpt)) naccpt = me%naccpt
344 | if (present(nrejct)) nrejct = me%nrejct
345 | if (present(h)) h = me%h
346 | if (present(iout)) iout = me%iout
347 |
348 | end subroutine get_dop853_info
349 | !*****************************************************************************************
350 |
351 | !*****************************************************************************************
352 | !>
353 | ! Destructor for [[dop853_class]].
354 |
355 | subroutine destroy_dop853(me)
356 |
357 | implicit none
358 |
359 | class(dop853_class),intent(out) :: me
360 |
361 | end subroutine destroy_dop853
362 | !*****************************************************************************************
363 |
364 | !*****************************************************************************************
365 | !>
366 | ! Set the optional inputs for [[dop853]].
367 | !
368 | !@note In the original code, these were part of the `work` and `iwork` arrays.
369 |
370 | subroutine set_parameters(me,n,fcn,solout,iprint,nstiff,nmax,hinitial,&
371 | hmax,safe,fac1,fac2,beta,icomp,status_ok)
372 |
373 | implicit none
374 |
375 | class(dop853_class),intent(inout) :: me
376 | integer,intent(in) :: n !! the dimension of the system (size of \(y\) and \(y'\) vectors)
377 | procedure(deriv_func) :: fcn !! subroutine computing the value of \( y' = f(x,y) \)
378 | procedure(solout_func),optional :: solout !! subroutine providing the
379 | !! numerical solution during integration.
380 | !! if `iout>=1`, it is called during integration.
381 | !! supply a dummy subroutine if `iout=0`.
382 | integer,intent(in),optional :: iprint !! switch for printing error messages
383 | !! if `iprint==0` no messages are being printed
384 | !! if `iprint/=0` messages are printed with
385 | !! `write (iprint,*)` ...
386 | integer,intent(in),optional :: nstiff !! test for stiffness is activated after step number
387 | !! `j*nstiff` (`j` integer), provided `nstiff>0`.
388 | !! for negative `nstiff` the stiffness test is
389 | !! never activated.
390 | integer,intent(in),optional :: nmax !! the maximal number of allowed steps.
391 | real(wp),intent(in),optional :: hinitial !! initial step size, for `hinitial=0` an initial guess
392 | !! is computed with help of the function [[hinit]].
393 | real(wp),intent(in),optional :: hmax !! maximal step size, defaults to `xend-x` if `hmax=0`.
394 | real(wp),intent(in),optional :: safe !! safety factor in step size prediction
395 | real(wp),intent(in),optional :: fac1 !! parameter for step size selection.
396 | !! the new step size is chosen subject to the restriction
397 | !! `fac1 <= hnew/hold <= fac2`
398 | real(wp),intent(in),optional :: fac2 !! parameter for step size selection.
399 | !! the new step size is chosen subject to the restriction
400 | !! `fac1 <= hnew/hold <= fac2`
401 | real(wp),intent(in),optional :: beta !! is the `beta` for stabilized step size control
402 | !! (see section iv.2). positive values of `beta` ( <= 0.04 )
403 | !! make the step size control more stable.
404 | integer,dimension(:),intent(in),optional :: icomp !! the components for which dense output is required (size from 0 to `n`).
405 | logical,intent(out) :: status_ok !! will be false for invalid inputs.
406 |
407 | call me%destroy()
408 |
409 | status_ok = .true.
410 |
411 | !required inputs:
412 | me%n = n
413 | me%fcn => fcn
414 |
415 | !optional inputs:
416 |
417 | if (present(solout)) me%solout => solout
418 |
419 | if (present(iprint)) me%iprint = iprint
420 | if (present(nstiff)) me%nstiff = nstiff
421 | if (present(hinitial)) me%hinitial = hinitial
422 | if (present(hmax)) me%hmax = hmax
423 | if (present(fac1)) me%fac1 = fac1
424 | if (present(fac2)) me%fac2 = fac2
425 |
426 | if (present(nmax)) then
427 | if ( nmax<=0 ) then
428 | if ( me%iprint/=0 ) &
429 | write (me%iprint,*) ' wrong input nmax=', nmax
430 | status_ok = .false.
431 | else
432 | me%nmax = nmax
433 | end if
434 | end if
435 |
436 | if (present(safe)) then
437 | if ( safe>=1.0_wp .or. safe<=1.0e-4_wp ) then
438 | if ( me%iprint/=0 ) &
439 | write (me%iprint,*) ' curious input for safety factor safe:', &
440 | safe
441 | status_ok = .false.
442 | else
443 | me%safe = safe
444 | end if
445 | end if
446 |
447 | if (present(beta)) then
448 | if ( beta<=0.0_wp ) then
449 | me%beta = 0.0_wp
450 | else
451 | if ( beta>0.2_wp ) then
452 | if ( me%iprint/=0 ) write (me%iprint,*) &
453 | ' curious input for beta: ', beta
454 | status_ok = .false.
455 | else
456 | me%beta = beta
457 | end if
458 | end if
459 | end if
460 |
461 | if (present(icomp)) then
462 | me%nrdens = size(icomp)
463 | !check validity of icomp array:
464 | if (size(icomp)<=me%n .and. all(icomp>0 .and. icomp<=me%n)) then
465 | allocate(me%icomp(me%nrdens)); me%icomp = icomp
466 | allocate(me%cont(8*me%nrdens)); me%cont = 0.0_wp
467 | else
468 | if ( me%iprint/=0 ) write (me%iprint,*) &
469 | ' invalid icomp array: ',icomp
470 | status_ok = .false.
471 | end if
472 | end if
473 |
474 | end subroutine set_parameters
475 | !*****************************************************************************************
476 |
477 | !*****************************************************************************************
478 | !>
479 | ! Numerical solution of a system of first order
480 | ! ordinary differential equations \( y'=f(x,y) \).
481 | ! This is an explicit Runge-Kutta method of order 8(5,3)
482 | ! due to Dormand & Prince (with stepsize control and
483 | ! dense output).
484 | !
485 | !### Authors
486 | ! * E. Hairer and G. Wanner
487 | ! Universite de Geneve, Dept. De Mathematiques
488 | ! ch-1211 geneve 24, switzerland
489 | ! e-mail: ernst.hairer@unige.ch
490 | ! gerhard.wanner@unige.ch
491 | ! * Version of October 11, 2009
492 | ! (new option `iout=3` for sparse dense output)
493 | ! * Jacob Williams, Dec 2015: significant refactoring into modern Fortran.
494 | !
495 | !### Reference
496 | ! * E. Hairer, S.P. Norsett and G. Wanner, [Solving Ordinary
497 | ! Differential Equations I. Nonstiff Problems. 2nd Edition](http://www.unige.ch/~hairer/books.html).
498 | ! Springer Series in Computational Mathematics, Springer-Verlag (1993)
499 |
500 | subroutine dop853(me,x,y,xend,rtol,atol,iout,idid)
501 |
502 | implicit none
503 |
504 | class(dop853_class),intent(inout) :: me
505 | real(wp),intent(inout) :: x !! *input:* initial value of independent variable.
506 | !! *output:* `x` for which the solution has been computed
507 | !! (after successful return `x=xend`).
508 | real(wp),dimension(:),intent(inout) :: y !! *input:* initial values for `y`. [size n]
509 | !!
510 | !! *output:* numerical solution at `x`.
511 | real(wp),intent(in) :: xend !! final x-value (`xend`-`x` may be positive or negative)
512 | real(wp),dimension(:),intent(in) :: rtol !! relative error tolerance. `rtol` and `atol`
513 | !! can be both scalars or else both vectors of length `n`.
514 | real(wp),dimension(:),intent(in) :: atol !! absolute error tolerance. `rtol` and `atol`
515 | !! can be both scalars or else both vectors of length `n`.
516 | !! `atol` should be strictly positive (possibly very small)
517 | integer,intent(in) :: iout !! switch for calling the subroutine `solout`:
518 | !!
519 | !! * `iout=0`: subroutine is never called
520 | !! * `iout=1`: subroutine is called after every successful step
521 | !! * `iout=2`: dense output is performed after every successful step
522 | !! * `iout=3`: dense output is performed in steps defined by the user
523 | !! (see `xout` above)
524 | integer,intent(out) :: idid !! reports on successfulness upon return:
525 | !!
526 | !! * `idid=1` computation successful,
527 | !! * `idid=2` comput. successful (interrupted by [[solout]]),
528 | !! * `idid=-1` input is not consistent,
529 | !! * `idid=-2` larger `nmax` is needed,
530 | !! * `idid=-3` step size becomes too small.
531 | !! * `idid=-4` problem is probably stiff (interrupted).
532 |
533 | real(wp) :: beta,fac1,fac2,hmax,safe
534 | integer :: i,ieco,iprint,istore,nrdens,nstiff,nmax
535 | logical :: arret
536 | integer :: itol !! switch for `rtol` and `atol`:
537 | !!
538 | !! * `itol=0`: both `rtol` and `atol` are scalars.
539 | !! the code keeps, roughly, the local error of
540 | !! `y(i)` below `rtol*abs(y(i))+atol`.
541 | !!
542 | !! * `itol=1`: both `rtol` and `atol` are vectors.
543 | !! the code keeps the local error of `y(i)` below
544 | !! `rtol(i)*abs(y(i))+atol(i)`.
545 |
546 | iprint = me%iprint
547 | me%iout = iout
548 | arret = .false.
549 |
550 | !check procedures:
551 | if (.not. associated(me%fcn)) then
552 | if ( iprint/=0 ) &
553 | write (iprint,*) &
554 | 'Error in dop853: procedure FCN is not associated.'
555 | idid = -1
556 | return
557 | end if
558 |
559 | if (iout/=0 .and. .not. associated(me%solout)) then
560 | if ( iprint/=0 ) &
561 | write (iprint,*) &
562 | 'Error in dop853: procedure SOLOUT must be associated if IOUT/=0.'
563 | idid = -1
564 | return
565 | end if
566 |
567 | !scalar or vector tolerances:
568 | if (size(rtol)==1 .and. size(atol)==1) then
569 | itol = 0
570 | elseif (size(rtol)==me%n .and. size(atol)==me%n) then
571 | itol = 1
572 | else
573 | if ( iprint/=0 ) &
574 | write (iprint,*) &
575 | 'Error in dop853: improper dimensions for rtol and/or atol.'
576 | idid = -1
577 | return
578 | end if
579 |
580 | ! setting the parameters
581 | me%nfcn = 0
582 | me%nstep = 0
583 | me%naccpt = 0
584 | me%nrejct = 0
585 |
586 | nmax = me%nmax
587 | nrdens = me%nrdens !number of dense output components
588 |
589 | ! nstiff parameter for stiffness detection
590 | if ( me%nstiff<=0 ) then
591 | nstiff = nmax + 10 !no stiffness check
592 | else
593 | nstiff = me%nstiff
594 | end if
595 |
596 | if ( nrdens<0 .or. me%nrdens>me%n ) then
597 | if ( iprint/=0 ) write (iprint,*) ' curious input nrdens=' , nrdens
598 | arret = .true.
599 | else
600 | if ( nrdens>0 .and. iout<2 .and. iprint/=0 ) &
601 | write (iprint,*) ' warning: set iout=2 or iout=3 for dense output '
602 | end if
603 |
604 | if (size(y)/=me%n) then
605 | if ( iprint/=0 ) &
606 | write (iprint,*) ' error: y must have n elements: size(y)= ',size(y)
607 | arret = .true.
608 | end if
609 |
610 | safe = me%safe
611 | fac1 = me%fac1
612 | fac2 = me%fac2
613 | beta = me%beta
614 |
615 | if ( me%hmax==0.0_wp ) then
616 | hmax = xend - x
617 | else
618 | hmax = me%hmax
619 | end if
620 |
621 | me%h = me%hinitial ! initial step size
622 |
623 | ! when a fail has occurred, we return with idid=-1
624 | if ( arret ) then
625 |
626 | idid = -1
627 |
628 | else
629 |
630 | ! call to core integrator
631 | call me%dp86co(x,y,xend,hmax,me%h,rtol,atol,itol,iprint, &
632 | iout,idid,nmax,nstiff,safe,beta,fac1,fac2, &
633 | me%nfcn,me%nstep,me%naccpt,me%nrejct)
634 |
635 | end if
636 |
637 | end subroutine dop853
638 | !*****************************************************************************************
639 |
640 | !*****************************************************************************************
641 | !>
642 | ! Core integrator for [[dop853]].
643 | ! parameters same as in [[dop853]] with workspace added.
644 |
645 | subroutine dp86co(me,x,y,xend,hmax,h,rtol,atol,itol,iprint, &
646 | iout,idid,nmax,nstiff,safe, &
647 | beta,fac1,fac2, &
648 | nfcn,nstep,naccpt,nrejct)
649 |
650 | implicit none
651 |
652 | class(dop853_class),intent(inout) :: me
653 | real(wp),intent(inout) :: x
654 | real(wp),dimension(:),intent(inout) :: y
655 | real(wp),intent(in) :: xend
656 | real(wp),intent(inout) :: hmax
657 | real(wp),intent(inout) :: h
658 | real(wp),dimension(:),intent(in) :: rtol
659 | real(wp),dimension(:),intent(in) :: atol
660 | integer,intent(in) :: itol
661 | integer,intent(in) :: iprint
662 | integer,intent(in) :: iout
663 | integer,intent(out) :: idid
664 | integer,intent(in) :: nmax
665 | integer,intent(in) :: nstiff
666 | real(wp),intent(in) :: safe
667 | real(wp),intent(in) :: beta
668 | real(wp),intent(in) :: fac1
669 | real(wp),intent(in) :: fac2
670 | integer,intent(inout) :: nfcn
671 | integer,intent(inout) :: nstep
672 | integer,intent(inout) :: naccpt
673 | integer,intent(inout) :: nrejct
674 |
675 | real(wp),dimension(me%n) :: y1,k1,k2,k3,k4,k5,k6,k7,k8,k9,k10
676 | real(wp) :: atoli,bspl,deno,err,err2,erri,expo1,fac,fac11,&
677 | facc1,facc2,facold,hlamb,hnew,posneg,rtoli,&
678 | sk,stden,stnum,xout,xph,ydiff
679 | integer :: i,iasti,iord,irtrn,j,nonsti,nrd
680 | logical :: reject,last,event,abort
681 |
682 | ! initializations
683 | nrd = me%nrdens
684 | facold = 1.0e-4_wp
685 | expo1 = 1.0_wp/8.0_wp - beta*0.2_wp
686 | facc1 = 1.0_wp/fac1
687 | facc2 = 1.0_wp/fac2
688 | posneg = sign(1.0_wp,xend-x)
689 | irtrn = 0 ! these were not initialized
690 | nonsti = 0 ! in the original code
691 | xout = 0.0_wp !
692 |
693 | ! initial preparations
694 | atoli = atol(1)
695 | rtoli = rtol(1)
696 | last = .false.
697 | hlamb = 0.0_wp
698 | iasti = 0
699 | call me%fcn(x,y,k1)
700 | hmax = abs(hmax)
701 | iord = 8
702 | if ( h==0.0_wp ) then
703 | h = me%hinit(x,y,posneg,k1,iord,hmax,atol,rtol,itol)
704 | end if
705 | nfcn = nfcn + 2
706 | reject = .false.
707 | me%xold = x
708 | if ( iout/=0 ) then
709 | irtrn = 1
710 | me%hout = 1.0_wp
711 | call me%solout(naccpt+1,me%xold,x,y,irtrn,xout)
712 | abort = ( irtrn<0 )
713 | else
714 | abort = .false.
715 | end if
716 |
717 | if (.not. abort) then
718 | do
719 | ! basic integration step
720 | if ( nstep>nmax ) then
721 | if ( iprint/=0 ) &
722 | write (iprint,'(A,E18.4)') ' exit of dop853 at x=', x
723 | if ( iprint/=0 ) &
724 | write (iprint,*) ' more than nmax =' , nmax , 'steps are needed'
725 | idid = -2
726 | return
727 | elseif ( 0.1_wp*abs(h)<=abs(x)*uround ) then
728 | if ( iprint/=0 ) &
729 | write (iprint,'(A,E18.4)') ' exit of dop853 at x=', x
730 | if ( iprint/=0 ) &
731 | write (iprint,*) ' step size too small, h=' , h
732 | idid = -3
733 | return
734 | else
735 | if ( (x+1.01_wp*h-xend)*posneg>0.0_wp ) then
736 | h = xend - x
737 | last = .true.
738 | end if
739 | nstep = nstep + 1
740 | ! the twelve stages
741 | if ( irtrn>=2 ) call me%fcn(x,y,k1)
742 | y1 = y + h*a21*k1
743 | call me%fcn(x+c2*h,y1,k2)
744 | y1 = y + h*(a31*k1+a32*k2)
745 | call me%fcn(x+c3*h,y1,k3)
746 | y1 = y + h*(a41*k1+a43*k3)
747 | call me%fcn(x+c4*h,y1,k4)
748 | y1 = y + h*(a51*k1+a53*k3+a54*k4)
749 | call me%fcn(x+c5*h,y1,k5)
750 | y1 = y + h*(a61*k1+a64*k4+a65*k5)
751 | call me%fcn(x+c6*h,y1,k6)
752 | y1 = y + h*(a71*k1+a74*k4+a75*k5+a76*k6)
753 | call me%fcn(x+c7*h,y1,k7)
754 | y1 = y + h*(a81*k1+a84*k4+a85*k5+a86*k6+a87*k7)
755 | call me%fcn(x+c8*h,y1,k8)
756 | y1 = y + h*(a91*k1+a94*k4+a95*k5+a96*k6+a97*k7+a98*k8)
757 | call me%fcn(x+c9*h,y1,k9)
758 | y1 = y + h*(a101*k1+a104*k4+a105*k5+a106*k6+a107*k7+&
759 | a108*k8+a109*k9)
760 | call me%fcn(x+c10*h,y1,k10)
761 | y1 = y + h*(a111*k1+a114*k4+a115*k5+a116*k6+a117*k7+&
762 | a118*k8+a119*k9+a1110*k10)
763 | call me%fcn(x+c11*h,y1,k2)
764 | xph = x + h
765 | y1 = y + h*(a121*k1+a124*k4+a125*k5+a126*k6+a127*k7+&
766 | a128*k8+a129*k9+a1210*k10+a1211*k2)
767 | call me%fcn(xph,y1,k3)
768 | nfcn = nfcn + 11
769 | k4 = b1*k1+b6*k6+b7*k7+b8*k8+b9*k9+b10*k10+b11*k2+b12*k3
770 | k5 = y + h*k4
771 | ! error estimation
772 | err = 0.0_wp
773 | err2 = 0.0_wp
774 | if ( itol==0 ) then
775 | do i = 1 , me%n
776 | sk = atoli + rtoli*max(abs(y(i)),abs(k5(i)))
777 | erri = k4(i) - bhh1*k1(i) - bhh2*k9(i) - bhh3*k3(i)
778 | err2 = err2 + (erri/sk)**2
779 | erri = er1*k1(i) + er6*k6(i) + er7*k7(i) + er8*k8(i) &
780 | + er9*k9(i) + er10*k10(i) + er11*k2(i) &
781 | + er12*k3(i)
782 | err = err + (erri/sk)**2
783 | end do
784 | else
785 | do i = 1 , me%n
786 | sk = atol(i) + rtol(i)*max(abs(y(i)),abs(k5(i)))
787 | erri = k4(i) - bhh1*k1(i) - bhh2*k9(i) - bhh3*k3(i)
788 | err2 = err2 + (erri/sk)**2
789 | erri = er1*k1(i) + er6*k6(i) + er7*k7(i) + er8*k8(i) &
790 | + er9*k9(i) + er10*k10(i) + er11*k2(i) &
791 | + er12*k3(i)
792 | err = err + (erri/sk)**2
793 | end do
794 | end if
795 | deno = err + 0.01_wp*err2
796 | if ( deno<=0.0_wp ) deno = 1.0_wp
797 | err = abs(h)*err*sqrt(1.0_wp/(me%n*deno))
798 | ! computation of hnew
799 | fac11 = err**expo1
800 | ! lund-stabilization
801 | fac = fac11/facold**beta
802 | ! we require fac1 <= hnew/h <= fac2
803 | fac = max(facc2,min(facc1,fac/safe))
804 | hnew = h/fac
805 | if ( err<=1.0_wp ) then
806 | ! step is accepted
807 | facold = max(err,1.0e-4_wp)
808 | naccpt = naccpt + 1
809 | call me%fcn(xph,k5,k4)
810 | nfcn = nfcn + 1
811 | ! stiffness detection
812 | if ( mod(naccpt,nstiff)==0 .or. iasti>0 ) then
813 | stnum = 0.0_wp
814 | stden = 0.0_wp
815 | do i = 1 , me%n
816 | stnum = stnum + (k4(i)-k3(i))**2
817 | stden = stden + (k5(i)-y1(i))**2
818 | end do
819 | if ( stden>0.0_wp ) hlamb = abs(h)*sqrt(stnum/stden)
820 | if ( hlamb>6.1_wp ) then
821 | nonsti = 0
822 | iasti = iasti + 1
823 | if ( iasti==15 ) then
824 | if ( iprint/=0 ) &
825 | write (iprint,*) &
826 | ' the problem seems to become stiff at x = ', x
827 | if ( iprint==0 ) then
828 | idid = -4 ! fail exit
829 | return
830 | end if
831 | end if
832 | else
833 | nonsti = nonsti + 1
834 | if ( nonsti==6 ) iasti = 0
835 | end if
836 | end if
837 | ! final preparation for dense output
838 | event = (iout==3) .and. (xout<=xph)
839 | if ( iout==2 .or. event ) then
840 | ! save the first function evaluations
841 | do j = 1 , nrd
842 | i = me%icomp(j)
843 | me%cont(j) = y(i)
844 | ydiff = k5(i) - y(i)
845 | me%cont(j+nrd) = ydiff
846 | bspl = h*k1(i) - ydiff
847 | me%cont(j+nrd*2) = bspl
848 | me%cont(j+nrd*3) = ydiff - h*k4(i) - bspl
849 | me%cont(j+nrd*4) = d41*k1(i)+d46*k6(i)+d47*k7(i)+&
850 | d48*k8(i)+d49*k9(i)+d410*k10(i)+&
851 | d411*k2(i)+d412*k3(i)
852 | me%cont(j+nrd*5) = d51*k1(i)+d56*k6(i)+d57*k7(i)+&
853 | d58*k8(i)+d59*k9(i)+d510*k10(i)+&
854 | d511*k2(i)+d512*k3(i)
855 | me%cont(j+nrd*6) = d61*k1(i)+d66*k6(i)+d67*k7(i)+&
856 | d68*k8(i)+d69*k9(i)+d610*k10(i)+&
857 | d611*k2(i)+d612*k3(i)
858 | me%cont(j+nrd*7) = d71*k1(i)+d76*k6(i)+d77*k7(i)+&
859 | d78*k8(i)+d79*k9(i)+d710*k10(i)+&
860 | d711*k2(i)+d712*k3(i)
861 | end do
862 | ! the next three function evaluations
863 | y1 = y + h*(a141*k1+a147*k7+a148*k8+a149*k9+&
864 | a1410*k10+a1411*k2+a1412*k3+a1413*k4)
865 | call me%fcn(x+c14*h,y1,k10)
866 | y1 = y + h*(a151*k1+a156*k6+a157*k7+a158*k8+&
867 | a1511*k2+a1512*k3+a1513*k4+a1514*k10)
868 | call me%fcn(x+c15*h,y1,k2)
869 | y1 = y + h*(a161*k1+a166*k6+a167*k7+a168*k8+a169*k9+&
870 | a1613*k4+a1614*k10+a1615*k2)
871 | call me%fcn(x+c16*h,y1,k3)
872 | nfcn = nfcn + 3
873 | ! final preparation
874 | do j = 1 , nrd
875 | i = me%icomp(j)
876 | me%cont(j+nrd*4) = h*(me%cont(j+nrd*4)+d413*k4(i)+&
877 | d414*k10(i)+d415*k2(i)+d416*k3(i))
878 | me%cont(j+nrd*5) = h*(me%cont(j+nrd*5)+d513*k4(i)+&
879 | d514*k10(i)+d515*k2(i)+d516*k3(i))
880 | me%cont(j+nrd*6) = h*(me%cont(j+nrd*6)+d613*k4(i)+&
881 | d614*k10(i)+d615*k2(i)+d616*k3(i))
882 | me%cont(j+nrd*7) = h*(me%cont(j+nrd*7)+d713*k4(i)+&
883 | d714*k10(i)+d715*k2(i)+d716*k3(i))
884 | end do
885 | me%hout = h
886 | end if
887 | k1 = k4
888 | y = k5
889 | me%xold = x
890 | x = xph
891 | if ( iout==1 .or. iout==2 .or. event ) then
892 | call me%solout(naccpt+1,me%xold,x,y,irtrn,xout)
893 | if ( irtrn<0 ) exit !abort
894 | end if
895 | ! normal exit
896 | if ( last ) then
897 | h = hnew
898 | idid = 1
899 | return
900 | end if
901 | if ( abs(hnew)>hmax ) hnew = posneg*hmax
902 | if ( reject ) hnew = posneg*min(abs(hnew),abs(h))
903 | reject = .false.
904 | else
905 | ! step is rejected
906 | hnew = h/min(facc1,fac11/safe)
907 | reject = .true.
908 | if ( naccpt>=1 ) nrejct = nrejct + 1
909 | last = .false.
910 | end if
911 | h = hnew
912 | end if
913 | end do
914 | end if
915 |
916 | if ( iprint/=0 ) write (iprint,'(A,E18.4)') ' exit of dop853 at x=', x
917 | idid = 2
918 |
919 | end subroutine dp86co
920 | !*****************************************************************************************
921 |
922 | !*****************************************************************************************
923 | !>
924 | ! computation of an initial step size guess
925 |
926 | function hinit(me,x,y,posneg,f0,iord,hmax,atol,rtol,itol)
927 |
928 | implicit none
929 |
930 | class(dop853_class),intent(inout) :: me
931 | real(wp),intent(in) :: x
932 | real(wp),dimension(:),intent(in) :: y !! dimension(n)
933 | real(wp),intent(in) :: posneg
934 | real(wp),dimension(:),intent(in) :: f0 !! dimension(n)
935 | integer,intent(in) :: iord
936 | real(wp),intent(in) :: hmax
937 | real(wp),dimension(:),intent(in) :: atol
938 | real(wp),dimension(:),intent(in) :: rtol
939 | integer,intent(in) :: itol
940 |
941 | real(wp) :: atoli,der12,der2,dnf,dny,h,h1,hinit,rtoli,sk
942 | integer :: i
943 | real(wp),dimension(me%n) :: f1,y1
944 |
945 | ! compute a first guess for explicit euler as
946 | ! h = 0.01 * norm (y0) / norm (f0)
947 | ! the increment for explicit euler is small
948 | ! compared to the solution
949 | dnf = 0.0_wp
950 | dny = 0.0_wp
951 | atoli = atol(1)
952 | rtoli = rtol(1)
953 | if ( itol==0 ) then
954 | do i = 1 , me%n
955 | sk = atoli + rtoli*abs(y(i))
956 | dnf = dnf + (f0(i)/sk)**2
957 | dny = dny + (y(i)/sk)**2
958 | end do
959 | else
960 | do i = 1 , me%n
961 | sk = atol(i) + rtol(i)*abs(y(i))
962 | dnf = dnf + (f0(i)/sk)**2
963 | dny = dny + (y(i)/sk)**2
964 | end do
965 | end if
966 | if ( dnf<=1.0e-10_wp .or. dny<=1.0e-10_wp ) then
967 | h = 1.0e-6_wp
968 | else
969 | h = sqrt(dny/dnf)*0.01_wp
970 | end if
971 | h = min(h,hmax)
972 | h = sign(h,posneg)
973 | ! perform an explicit euler step
974 | do i = 1 , me%n
975 | y1(i) = y(i) + h*f0(i)
976 | end do
977 | call me%fcn(x+h,y1,f1)
978 | ! estimate the second derivative of the solution
979 | der2 = 0.0_wp
980 | if ( itol==0 ) then
981 | do i = 1 , me%n
982 | sk = atoli + rtoli*abs(y(i))
983 | der2 = der2 + ((f1(i)-f0(i))/sk)**2
984 | end do
985 | else
986 | do i = 1 , me%n
987 | sk = atol(i) + rtol(i)*abs(y(i))
988 | der2 = der2 + ((f1(i)-f0(i))/sk)**2
989 | end do
990 | end if
991 | der2 = sqrt(der2)/h
992 | ! step size is computed such that
993 | ! h**iord * max ( norm (f0), norm (der2)) = 0.01
994 | der12 = max(abs(der2),sqrt(dnf))
995 | if ( der12<=1.0e-15_wp ) then
996 | h1 = max(1.0e-6_wp,abs(h)*1.0e-3_wp)
997 | else
998 | h1 = (0.01_wp/der12)**(1.0_wp/iord)
999 | end if
1000 |
1001 | h = min(100.0_wp*abs(h),h1,hmax)
1002 | hinit = sign(h,posneg)
1003 |
1004 | end function hinit
1005 | !*****************************************************************************************
1006 |
1007 | !*****************************************************************************************
1008 | !>
1009 | ! this function can be used for continuous output in connection
1010 | ! with the output-subroutine for [[dop853]]. it provides an
1011 | ! approximation to the `ii`-th component of the solution at `x`.
1012 |
1013 | function contd8(me,ii,x) result(y)
1014 |
1015 | implicit none
1016 |
1017 | class(dop853_class),intent(in) :: me
1018 | integer,intent(in) :: ii
1019 | real(wp),intent(in) :: x
1020 | real(wp) :: y
1021 |
1022 | real(wp) :: conpar, s, s1
1023 | integer :: i,j,nd,ierr
1024 |
1025 | ! compute place of ii-th component
1026 | i = 0
1027 | do j = 1, me%nrdens
1028 | if ( me%icomp(j)==ii ) i = j
1029 | end do
1030 | if ( i==0 ) then
1031 | !always report this message, since it is an invalid use of the code.
1032 | if (me%iprint==0) then
1033 | ierr = error_unit
1034 | else
1035 | ierr = me%iprint
1036 | end if
1037 | write (ierr,*) &
1038 | ' Error in contd8: no dense output available for component:', ii
1039 | y = 0.0_wp
1040 | else
1041 | nd = me%nrdens
1042 | s = (x-me%xold)/me%hout
1043 | s1 = 1.0_wp - s
1044 | conpar = me%cont(i+nd*4) + &
1045 | s*(me%cont(i+nd*5)+ &
1046 | s1*(me%cont(i+nd*6)+s*me%cont(i+nd*7)))
1047 | y = me%cont(i) + &
1048 | s*(me%cont(i+nd)+ &
1049 | s1*(me%cont(i+nd*2)+&
1050 | s*(me%cont(i+nd*3)+s1*conpar)))
1051 | end if
1052 |
1053 | end function contd8
1054 | !*****************************************************************************************
1055 |
1056 | !*****************************************************************************************
1057 | end module dop853_module
1058 | !*****************************************************************************************
1059 |
--------------------------------------------------------------------------------
/test/dop853_example_original.f90:
--------------------------------------------------------------------------------
1 | !*****************************************************************************************
2 | !>
3 | ! Original Hairer test case.
4 |
5 | program dop853_example_original
6 |
7 | use dop853_module, wp => dop853_wp
8 | use iso_fortran_env, only: output_unit
9 |
10 | implicit none
11 |
12 | integer,parameter :: n = 2 !! dimension of the system
13 | real(wp),parameter :: tol = 1.0e-9_wp !! integration tolerance
14 | real(wp),parameter :: x0 = 0.0_wp !! initial `x` value
15 | real(wp),parameter :: xf = 2.0_wp !! endpoint of integration
16 | real(wp),dimension(n),parameter :: y0 = [2.0_wp,0.0_wp] !! initial `y` value
17 |
18 | type(dop853_class) :: prop
19 | real(wp),dimension(n) :: y
20 | real(wp),dimension(1) :: rtol,atol
21 | real(wp) :: x
22 | integer :: idid
23 | logical :: status_ok
24 |
25 | x = x0
26 | y = y0
27 | rtol = tol ! set tolerances
28 | atol = tol !
29 | call prop%initialize( fcn = fvpol, &
30 | nstiff = 1, &
31 | n = n, &
32 | status_ok = status_ok )
33 | if (.not. status_ok) error stop 'initialization error'
34 |
35 | call prop%integrate(x,y,xf,rtol,atol,iout=0,idid=idid)
36 |
37 | write (output_unit,'(1X,A,F6.2,A,2E18.10)') &
38 | 'x =',x ,' y =',y(1),y(2)
39 |
40 | contains
41 |
42 | subroutine fvpol(me,x,y,f)
43 |
44 | !! Right-hand side of van der Pol's equation
45 |
46 | implicit none
47 |
48 | class(dop853_class),intent(inout) :: me
49 | real(wp),intent(in) :: x
50 | real(wp),dimension(:),intent(in) :: y
51 | real(wp),dimension(:),intent(out) :: f
52 |
53 | real(wp),parameter :: eps = 1.0e-3_wp
54 |
55 | f(1) = y(2)
56 | f(2) = ((1-y(1)**2)*y(2)-y(1))/eps
57 |
58 | end subroutine fvpol
59 |
60 | end program dop853_example_original
61 |
--------------------------------------------------------------------------------
/test/dop853_test.f90:
--------------------------------------------------------------------------------
1 | !*****************************************************************************************
2 | !>
3 | ! Driver for [[dop853]] on van der Pol's equation.
4 | !
5 | !### See also
6 | ! * Based on [dr_dop853.f](http://www.unige.ch/~hairer/prog/nonstiff/dr_dop853.f)
7 | !
8 | !### Results
9 | ! 
10 | ! 
11 | !
12 | !@note This requires [pyplot-fortran](https://github.com/jacobwilliams/pyplot-fortran).
13 |
14 | program dop853_test
15 |
16 | use dop853_module, wp => dop853_wp
17 | use iso_fortran_env, only: output_unit
18 | use pyplot_module
19 |
20 | implicit none
21 |
22 | integer,parameter :: n = 2 !! dimension of the system
23 | integer,dimension(n),parameter :: icomp = [1,2] !! indices of `y` where we need dense output
24 | integer,parameter :: iout = 3 !! output routine (and dense output) is used during integration
25 | real(wp),parameter :: tol = 1.0e-12_wp !! required (relative) tolerance
26 | real(wp),parameter :: x0 = 0.0_wp !! initial `x` value
27 | real(wp),parameter :: xf = 100.0_wp !! endpoint of integration
28 | real(wp),dimension(n),parameter :: y0 = [0.0_wp,0.1_wp] !! initial `y` value
29 | real(wp),parameter :: dx = 0.01_wp !! time step for dense output
30 | real(wp),dimension(2),parameter :: xylim = [-3.0_wp,3.0_wp] !! plot axis limits
31 |
32 | logical,parameter :: make_plots = .true. !! use pyplot to generate plots.
33 |
34 | type(dop853_class) :: prop
35 | real(wp),dimension(n) :: y
36 | real(wp),dimension(1) :: rtol,atol
37 | real(wp) :: x,xend
38 | integer :: i,idid,j,nfcn,nstep,naccpt,nrejct
39 | logical :: status_ok
40 | type(pyplot) :: plt
41 | real(wp),dimension(:),allocatable :: t_vec, y_vec, yp_vec
42 | integer :: istat !! pyplot-fortran status flag
43 |
44 | x = x0
45 | y = y0
46 | xend = xf
47 | rtol = tol
48 | atol = tol
49 | call prop%initialize( fcn = fvpol, &
50 | n = n, &
51 | solout = solout, &
52 | icomp = icomp, &
53 | nstiff = 1, &
54 | status_ok = status_ok )
55 | !all other parameters use defaults
56 |
57 | if (status_ok) then
58 |
59 | call prop%integrate(x,y,xend,rtol,atol,iout,idid)
60 | call prop%info(nfcn,nstep,naccpt,nrejct)
61 |
62 | if (make_plots) then
63 | t_vec = [t_vec,x] !last point
64 | y_vec = [y_vec,y(1)]
65 | yp_vec = [yp_vec,y(2)]
66 | end if
67 |
68 | ! print final solution
69 | write (output_unit,'(1X,A,F6.2,A,2E18.10)') &
70 | 'x =',x ,' y =',y(1),y(2)
71 |
72 | ! print statistics
73 | write (output_unit,'(A,D8.2)') ' tol=',tol
74 | write (output_unit,'(A,I5,A,I5,A,I4,A,I3)') &
75 | ' fcn=',nfcn,' step=',nstep,' accpt=',naccpt,' rejct=',nrejct
76 |
77 | ! plot:
78 | if (make_plots) then
79 | call plt%initialize(grid=.true.,xlabel='y(x)',&
80 | ylabel='y''(x)',&
81 | title='van der Pol''s Equation ($\mu = 0.2$)',legend=.true.)
82 | call plt%add_plot(y_vec,yp_vec,label='Forward',&
83 | linestyle='r-',linewidth=2,&
84 | xlim=xylim, ylim=xylim,istat=istat)
85 | call plt%savefig('dop853_forward.png',istat=istat)
86 | call plt%destroy()
87 |
88 | deallocate(t_vec )
89 | deallocate(y_vec )
90 | deallocate(yp_vec)
91 | end if
92 |
93 | !--------------------------------------------------
94 |
95 | write(*,*) ''
96 | write(*,*) 'backwards test'
97 | write(*,*) ''
98 | call prop%destroy()
99 | call prop%initialize(fcn=fvpol,n=n,solout=solout2,status_ok = status_ok)
100 | call prop%integrate(x,y,x0,rtol,atol,iout=1,idid=idid)
101 |
102 | write(*,*) ''
103 | write(*,*) 'error:', norm2(y-y0)
104 | write(*,*) ''
105 |
106 | ! plot:
107 | if (make_plots) then
108 | call plt%initialize(grid=.true.,xlabel='y(x)',&
109 | ylabel='y''(x)',&
110 | title='van der Pol''s Equation ($\mu = 0.2$)',legend=.true.)
111 |
112 | call plt%add_plot(y_vec,yp_vec,label='Backward',&
113 | linestyle='r-',linewidth=2,&
114 | xlim=xylim, ylim=xylim,istat=istat)
115 |
116 | call plt%savefig('dop853_backward.png',istat=istat)
117 | call plt%destroy()
118 | end if
119 |
120 | else
121 | write(output_unit,'(A)') 'error calling INITIALIZE.'
122 | end if
123 |
124 | contains
125 | !*****************************************************************************************
126 |
127 | !*******************************************************************************
128 | !>
129 | ! Prints solution at equidistant output-points
130 | ! by using [[contd8]], the continuous collocation solution.
131 | ! This is for an `iout=3` case.
132 | !
133 | !@note This routine uses Fortran 2008 LHS automatic allocations.
134 |
135 | subroutine solout(me,nr,xold,x,y,irtrn,xout)
136 |
137 | implicit none
138 |
139 | class(dop853_class),intent(inout) :: me
140 | integer,intent(in) :: nr
141 | real(wp),intent(in) :: xold
142 | real(wp),intent(in) :: x
143 | real(wp),dimension(:),intent(in) :: y
144 | integer,intent(inout) :: irtrn
145 | real(wp),intent(out) :: xout !! the point where we want the next output reported
146 |
147 | if ( nr==1 ) then
148 | write (output_unit,'(1X,A,F6.2,A,2E18.10,A,I4)') &
149 | 'x =',x,&
150 | ' y =',y(1),y(2),&
151 | ' nstep =',nr - 1
152 | xout = dx
153 | else
154 | do
155 | if ( x