├── .github └── workflows │ └── CI.yml ├── .gitignore ├── LICENSE ├── README.md ├── codecov.yml ├── ford.md ├── fpm.toml ├── media ├── logo.png └── logo.svg ├── pyplot-fortran.code-workspace ├── src └── pyplot_module.F90 └── test ├── color_test.f90 ├── date_test.f90 └── 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 numpy matplotlib ford 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 add-apt-repository ppa:ubuntu-toolchain-r/test 50 | sudo apt-get update 51 | sudo apt-get install -y gcc-${{ matrix.gcc_v }} gfortran-${{ matrix.gcc_v }} 52 | sudo update-alternatives \ 53 | --install /usr/bin/gcc gcc /usr/bin/gcc-${{ matrix.gcc_v }} 100 \ 54 | --slave /usr/bin/gfortran gfortran /usr/bin/gfortran-${{ matrix.gcc_v }} \ 55 | --slave /usr/bin/gcov gcov /usr/bin/gcov-${{ matrix.gcc_v }} 56 | 57 | # - name: Compile 58 | # run: fpm build --profile release 59 | 60 | - name: Run tests 61 | run: fpm test --profile release --flag -coverage 62 | 63 | - name: Create coverage report 64 | run: | 65 | mkdir -p ${{ env.COV_DIR }} 66 | mv ./build/gfortran_*/*/* ${{ env.COV_DIR }} 67 | lcov --capture --initial --base-directory . --directory ${{ env.COV_DIR }} --output-file ${{ env.COV_DIR }}/coverage.base 68 | lcov --capture --base-directory . --directory ${{ env.COV_DIR }} --output-file ${{ env.COV_DIR }}/coverage.capture 69 | lcov --add-tracefile ${{ env.COV_DIR }}/coverage.base --add-tracefile ${{ env.COV_DIR }}/coverage.capture --output-file ${{ env.COV_DIR }}/coverage.info 70 | env: 71 | COV_DIR: build/coverage 72 | 73 | - name: Upload coverage report 74 | uses: codecov/codecov-action@v3 75 | with: 76 | files: build/coverage/coverage.info 77 | 78 | - name: Build documentation 79 | run: ford ./ford.md 80 | 81 | - name: Deploy Documentation 82 | if: github.ref == 'refs/heads/master' 83 | uses: JamesIves/github-pages-deploy-action@v4.4.1 84 | with: 85 | branch: gh-pages # The branch the action should deploy to. 86 | folder: doc # The folder the action should deploy. 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | lib/ 3 | doc/ 4 | build/ 5 | __pycache__ 6 | 7 | # Compiled Object files 8 | *.slo 9 | *.lo 10 | *.o 11 | *.obj 12 | 13 | # Precompiled Headers 14 | *.gch 15 | *.pch 16 | 17 | # Compiled Dynamic libraries 18 | *.so 19 | *.dylib 20 | *.dll 21 | 22 | # Fortran module files 23 | *.mod 24 | 25 | # Compiled Static libraries 26 | *.lai 27 | *.la 28 | *.a 29 | *.lib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | 36 | # Generated Test Files 37 | test/*.png 38 | test/*.py 39 | 40 | # misc 41 | .DS_Store 42 | /env -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | PYPLOT-FORTRAN: For generating plots from Fortran using Python's matplotlib.pyplot. 2 | 3 | Copyright (c) 2015-2022, Jacob Williams 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, this 13 | list of conditions and the following disclaimer in the documentation and/or 14 | other materials provided with the distribution. 15 | 16 | * The names of its contributors may not be used to endorse or promote products 17 | derived from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Pyplot-Fortran](media/logo.png) 2 | ============ 3 | 4 | A simple module for generating plots from Fortran using Python's matplotlib.pyplot. 5 | 6 | ### Status 7 | 8 | [![GitHub release](https://img.shields.io/github/release/jacobwilliams/pyplot-fortran.svg?style=plastic)](https://github.com/jacobwilliams/pyplot-fortran/releases/latest) 9 | ![Build Status](https://github.com/jacobwilliams/pyplot-fortran/actions/workflows/CI.yml/badge.svg) 10 | [![codecov](https://codecov.io/gh/jacobwilliams/pyplot-fortran/branch/master/graph/badge.svg?token=BHtd51oUTE)](https://codecov.io/gh/jacobwilliams/pyplot-fortran) 11 | 12 | ### Overview 13 | 14 | Currently, this module can be used to generate simple plots from Fortran. Eventually, it may be expanded to provide additional features and other types of plots. 15 | 16 | The way it works is simply to generate a Python script with the plotting code, which 17 | is then executed from the command line using the Fortran ```execute_command_line``` function. 18 | 19 | ### Compiling 20 | 21 | The module requires a modern Fortran compiler (it uses various Fortran 2003/2008 features such as deferred-length strings). It should work fine with the latest gfortran or ifort compilers. 22 | 23 | A `fpm.toml` file is provided for compiling pyplot-fortran with the [Fortran Package Manager](https://github.com/fortran-lang/fpm). For example, to build: 24 | 25 | ``` 26 | fpm build --profile release 27 | ``` 28 | 29 | 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: 30 | 31 | Preprocessor flag | Kind | Number of bytes 32 | ----------------- | ----- | --------------- 33 | `REAL32` | `real(kind=real32)` | 4 34 | `REAL64` | `real(kind=real64)` | 8 35 | `REAL128` | `real(kind=real128)` | 16 36 | 37 | For example, to build a single precision version of the library, use: 38 | 39 | ``` 40 | fpm build --profile release --flag "-DREAL32" 41 | ``` 42 | 43 | To run the unit tests: 44 | 45 | ``` 46 | fpm test 47 | ``` 48 | 49 | To use `pyplot-fortran` within your fpm project, add the following to your `fpm.toml` file: 50 | ```toml 51 | [dependencies] 52 | pyplot-fortran = { git="https://github.com/jacobwilliams/pyplot-fortran.git" } 53 | ``` 54 | 55 | or, to use a specific version: 56 | ```toml 57 | [dependencies] 58 | pyplot-fortran = { git="https://github.com/jacobwilliams/pyplot-fortran.git", tag = "3.3.0" } 59 | ``` 60 | 61 | To generate the documentation using [ford](https://github.com/Fortran-FOSS-Programmers/ford), run: ```ford ford.md``` 62 | 63 | ### Supported plot types 64 | 65 | * `matplotlib.pyplot.plot` -- 2D/3D plot of lines and/or markers 66 | * `matplotlib.pyplot.bar` -- bar plot 67 | * `matplotlib.pyplot.contour` -- contour plot 68 | * `matplotlib.pyplot.contourf` -- filled contour plot 69 | * `matplotlib.pyplot.imshow` -- image plot 70 | * `matplotlib.pyplot.hist` -- histogram plot 71 | * `matplotlib.pyplot.errorbar` -- errorbar plot 72 | * `mpl_toolkits.mplot3d.axes3d.Axes3D.plot_surface` -- surface plot 73 | * `mpl_toolkits.mplot3d.axes3d.Axes3D.plot_wireframe` -- 3D wireframe 74 | 75 | ### Example 76 | 77 | The following example generates a plot of the sine function: 78 | 79 | ```fortran 80 | program test 81 | 82 | use,intrinsic :: iso_fortran_env, only: wp => real64 83 | use pyplot_module 84 | 85 | implicit none 86 | 87 | real(wp),dimension(100) :: x,sx 88 | type(pyplot) :: plt 89 | integer :: i 90 | 91 | !generate some data: 92 | x = [(real(i,wp), i=0,size(x)-1)]/5.0_wp 93 | sx = sin(x) 94 | 95 | !plot it: 96 | call plt%initialize(grid=.true.,xlabel='angle (rad)',& 97 | title='Plot of $\sin(x)$',legend=.true.) 98 | call plt%add_plot(x,sx,label='$\sin(x)$',linestyle='b-o',markersize=5,linewidth=2) 99 | call plt%savefig('sinx.png', pyfile='sinx.py') 100 | 101 | end program test 102 | ``` 103 | 104 | ### Documentation 105 | 106 | * The API documentation for the current ```master``` branch can be found [here](https://jacobwilliams.github.io/pyplot-fortran/). This is generated by processing the source files with [FORD](https://github.com/Fortran-FOSS-Programmers/ford). 107 | 108 | ### See also 109 | 110 | * [Matplotlib](https://matplotlib.org) 111 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ford.md: -------------------------------------------------------------------------------- 1 | project: pyplot-fortran 2 | project_dir: src 3 | output_dir: doc 4 | media_dir: ./media 5 | project_github: https://github.com/jacobwilliams/pyplot-fortran 6 | summary: For generating plots from Fortran using Python's matplotlib.pyplot 7 | author: Jacob Williams 8 | github: https://github.com/jacobwilliams 9 | predocmark_alt: > 10 | predocmark: < 11 | docmark_alt: 12 | docmark: ! 13 | display: public 14 | protected 15 | private 16 | source: true 17 | graph: true 18 | 19 | {!README.md!} -------------------------------------------------------------------------------- /fpm.toml: -------------------------------------------------------------------------------- 1 | name = "pyplot-fortran" 2 | version = "3.1.0" 3 | author = "Jacob Williams" 4 | maintainer = "Jacob Williams" 5 | copyright = "Copyright (c) 2015-2022, Jacob Williams" 6 | license = "BSD-3" 7 | description = "A simple module for generating plots from Fortran using Python's matplotlib.pyplot." 8 | homepage = "https://github.com/jacobwilliams/pyplot-fortran" 9 | 10 | [library] 11 | source-dir = "src" 12 | 13 | [install] 14 | library = true 15 | 16 | [build] 17 | auto-executables = false 18 | auto-examples = false 19 | auto-tests = true -------------------------------------------------------------------------------- /media/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacobwilliams/pyplot-fortran/60c5690c4e8d20d3185620db44111ca75ed7ebc3/media/logo.png -------------------------------------------------------------------------------- /media/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 25 | 32 | 33 | 36 | 43 | 44 | 46 | 50 | 54 | 55 | 57 | 60 | 61 | 63 | 66 | 67 | 69 | 72 | 73 | 75 | 78 | 79 | 81 | 84 | 85 | 87 | 90 | 91 | 93 | 96 | 97 | 99 | 102 | 103 | 105 | 108 | 109 | 111 | 114 | 115 | 117 | 120 | 121 | 123 | 126 | 127 | 129 | 132 | 133 | 135 | 138 | 139 | 141 | 144 | 145 | 147 | 150 | 151 | 152 | 175 | 177 | 178 | 180 | image/svg+xml 181 | 183 | 184 | 185 | 186 | 187 | 192 | 200 | pyplot 211 | 219 | 223 | 226 | 230 | 231 | 234 | 237 | 241 | 242 | 245 | 249 | 250 | 253 | 256 | 259 | 264 | 265 | 266 | 269 | 272 | 277 | 278 | 279 | 282 | 285 | 290 | 291 | 292 | 295 | 298 | 303 | 304 | 305 | 308 | 311 | 316 | 317 | 318 | 321 | 324 | 329 | 330 | 331 | 334 | 337 | 342 | 343 | 344 | 347 | 350 | 355 | 356 | 357 | 358 | 361 | 364 | 367 | 372 | 373 | 374 | 375 | 378 | 383 | 384 | 387 | 392 | 393 | 396 | 401 | 402 | 405 | 410 | 411 | 414 | 419 | 420 | 423 | 428 | 429 | 432 | 437 | 438 | 441 | 445 | 446 | 447 | 448 | Fortran 459 | 460 | 461 | -------------------------------------------------------------------------------- /pyplot-fortran.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 | } -------------------------------------------------------------------------------- /src/pyplot_module.F90: -------------------------------------------------------------------------------- 1 | !***************************************************************************************** 2 | !> author: Jacob Williams 3 | ! date: 6/16/2017 4 | ! license: BSD 5 | ! 6 | ! For making simple x-y plots from Fortran. 7 | ! It works by generating a Python script and executing it. 8 | ! 9 | !# See also 10 | ! * Inspired by: [EasyPlot](https://pypi.python.org/pypi/EasyPlot) 11 | ! 12 | !@note The default real kind (`wp`) can be 13 | ! changed using optional preprocessor flags. 14 | ! This library was built with real kind: 15 | #ifdef REAL32 16 | ! `real(kind=real32)` [4 bytes] 17 | #elif REAL64 18 | ! `real(kind=real64)` [8 bytes] 19 | #elif REAL128 20 | ! `real(kind=real128)` [16 bytes] 21 | #else 22 | ! `real(kind=real64)` [8 bytes] 23 | #endif 24 | 25 | module pyplot_module 26 | 27 | use, intrinsic :: iso_fortran_env 28 | 29 | implicit none 30 | 31 | private 32 | 33 | #ifdef REAL32 34 | integer,parameter,public :: pyplot_wp = real32 !! real kind used by this module [4 bytes] 35 | #elif REAL64 36 | integer,parameter,public :: pyplot_wp = real64 !! real kind used by this module [8 bytes] 37 | #elif REAL128 38 | integer,parameter,public :: pyplot_wp = real128 !! real kind used by this module [16 bytes] 39 | #else 40 | integer,parameter,public :: pyplot_wp = real64 !! real kind used by this module [8 bytes] 41 | #endif 42 | 43 | integer,parameter :: wp = pyplot_wp !! local copy of `pyplot_wp` with a shorter name 44 | 45 | character(len=*), parameter :: tmp_file = 'pyplot_module_temp_1234567890.py' !! Default name of the temporary file 46 | !! (this can also be user-specified). 47 | 48 | character(len=*), parameter :: python_exe ='python' !! The python executable name. 49 | character(len=*), parameter :: int_fmt = '(I10)' !! integer format string 50 | integer, parameter :: max_int_len = 10 !! max string length for integers 51 | character(len=*), parameter :: real_fmt_default = '(E30.16)' !! default real number format string 52 | integer, parameter :: max_real_len = 60 !! max string length for reals 53 | 54 | type, public :: pyplot 55 | 56 | !! The main pyplot class. 57 | 58 | private 59 | 60 | character(len=:), allocatable :: str !! string buffer 61 | 62 | character(len=1) :: raw_str_token = ' ' !! will be 'r' if using raw strings 63 | 64 | logical :: show_legend = .false. !! show legend into plot 65 | logical :: use_numpy = .true. !! use numpy python module 66 | logical :: use_oo_api = .false. !! use OO interface of matplotlib (incopatible with showfig subroutine) 67 | logical :: mplot3d = .false. !! it is a 3d plot 68 | logical :: polar = .false. !! it is a polar plot 69 | logical :: axis_equal = .false. !! equal scale on each axis 70 | logical :: axisbelow = .true. !! axis below other chart elements 71 | logical :: tight_layout = .false. !! tight layout option 72 | logical :: usetex = .false. !! enable LaTeX 73 | 74 | character(len=:),allocatable :: xaxis_date_fmt !! date format for the x-axis. Example: `"%m/%d/%y %H:%M:%S"` 75 | character(len=:),allocatable :: yaxis_date_fmt !! date format for the y-axis. Example: `"%m/%d/%y %H:%M:%S"` 76 | 77 | character(len=:),allocatable :: real_fmt !! real number formatting 78 | 79 | contains 80 | 81 | ! public methods 82 | procedure, public :: initialize !! initialize pyplot instance 83 | 84 | procedure, public :: add_plot !! add a 2d plot to pyplot instance 85 | procedure, public :: add_errorbar !! add a 2d error bar plot to pyplot instance 86 | procedure, public :: add_3d_plot !! add a 3d plot to pyplot instance 87 | procedure, public :: add_sphere !! add a 3d sphere to pyplot instance 88 | procedure, public :: add_contour !! add a contour plot to pyplot instance 89 | procedure, public :: plot_wireframe!! add a wireframe plot to pyplot instance 90 | procedure, public :: plot_surface !! add a surface plot to pyplot instance 91 | procedure, public :: add_bar !! add a barplot to pyplot instance 92 | procedure, public :: add_imshow !! add an image plot (using `imshow`) 93 | procedure, public :: add_hist !! add a histogram plot to pyplot instance 94 | procedure, public :: savefig !! save plots of pyplot instance 95 | procedure, public :: showfig !! show plots of pyplot instance 96 | procedure, public :: destroy !! destroy pyplot instance 97 | 98 | ! private methods 99 | procedure :: execute !! execute pyplot commands 100 | procedure :: add_str !! add string to pytplot instance buffer 101 | procedure :: finish_ops !! some final ops before saving 102 | 103 | end type pyplot 104 | 105 | contains 106 | !***************************************************************************************** 107 | 108 | !***************************************************************************************** 109 | !> author: Jacob Williams 110 | ! 111 | ! Destructor. 112 | 113 | subroutine destroy(me) 114 | 115 | class(pyplot),intent(inout) :: me !! pyplot handler 116 | 117 | if (allocated(me%str)) deallocate(me%str) 118 | if (allocated(me%real_fmt)) deallocate(me%real_fmt) 119 | 120 | me%raw_str_token = ' ' 121 | 122 | end subroutine destroy 123 | !***************************************************************************************** 124 | 125 | !***************************************************************************************** 126 | !> author: Jacob Williams 127 | ! 128 | ! Add a string to the buffer. 129 | 130 | subroutine add_str(me,str) 131 | 132 | class(pyplot), intent(inout) :: me !! pyplot handler 133 | character(len=*), intent(in) :: str !! str to be added to pyplot handler buffer 134 | 135 | integer :: n_old !! current `me%str` length 136 | integer :: n_str !! length of input `str` 137 | character(len=:),allocatable :: tmp !! tmp string for building the result 138 | 139 | ! original 140 | !me%str = me%str//str//new_line(' ') 141 | 142 | if (len(str)==0) return 143 | 144 | ! the above can sometimes cause a stack overflow in the 145 | ! intel Fortran compiler, so we replace with this: 146 | if (allocated(me%str)) then 147 | n_old = len(me%str) 148 | n_str = len(str) 149 | allocate(character(len=n_old+n_str+1) :: tmp) 150 | tmp(1:n_old) = me%str 151 | tmp(n_old+1:) = str//new_line(' ') 152 | call move_alloc(tmp, me%str) 153 | else 154 | allocate(me%str, source = str//new_line(' ')) 155 | end if 156 | 157 | end subroutine add_str 158 | !***************************************************************************************** 159 | 160 | !***************************************************************************************** 161 | !> author: Jacob Williams 162 | ! 163 | ! Initialize a plot 164 | 165 | subroutine initialize(me, grid, xlabel, ylabel, zlabel, title, legend, use_numpy, figsize, & 166 | font_size, axes_labelsize, xtick_labelsize, ytick_labelsize, ztick_labelsize, & 167 | legend_fontsize, mplot3d, axis_equal, polar, real_fmt, use_oo_api, axisbelow,& 168 | tight_layout, raw_strings, usetex, xaxis_date_fmt, yaxis_date_fmt) 169 | 170 | class(pyplot), intent(inout) :: me !! pyplot handler 171 | logical, intent(in), optional :: grid !! activate grid drawing 172 | character(len=*), intent(in), optional :: xlabel !! label of x axis 173 | character(len=*), intent(in), optional :: ylabel !! label of y axis 174 | character(len=*), intent(in), optional :: zlabel !! label of z axis 175 | character(len=*), intent(in), optional :: title !! plot title 176 | logical, intent(in), optional :: legend !! plot legend 177 | logical, intent(in), optional :: use_numpy !! activate usage of numpy python module 178 | integer, dimension(2), intent(in), optional :: figsize !! dimension of the figure 179 | integer, intent(in), optional :: font_size !! font size 180 | integer, intent(in), optional :: axes_labelsize !! size of axis labels 181 | integer, intent(in), optional :: xtick_labelsize !! size of x axis tick lables 182 | integer, intent(in), optional :: ytick_labelsize !! size of y axis tick lables 183 | integer, intent(in), optional :: ztick_labelsize !! size of z axis tick lables 184 | integer, intent(in), optional :: legend_fontsize !! size of legend font 185 | logical, intent(in), optional :: mplot3d !! set true for 3d plots (cannot use with polar) 186 | logical, intent(in), optional :: axis_equal !! set true for axis = 'equal' 187 | logical, intent(in), optional :: polar !! set true for polar plots (cannot use with mplot3d) 188 | character(len=*), intent(in), optional :: real_fmt !! format string for real numbers (examples: '(E30.16)' [default], '*') 189 | logical, intent(in), optional :: use_oo_api !! avoid matplotlib's GUI by using the OO interface (cannot use with showfig) 190 | logical, intent(in), optional :: axisbelow !! to put the grid lines below the other chart elements [default is true] 191 | logical, intent(in), optional :: tight_layout !! enable tight layout [default is false] 192 | logical, intent(in), optional :: raw_strings !! if True, all strings sent to Python are treated as 193 | !! raw strings (e.g., r'str'). Default is False. 194 | logical, intent(in), optional :: usetex !! if True, enable LaTeX. (default if false) 195 | character(len=*), intent(in), optional :: xaxis_date_fmt !! if present, used to set the date format for the x-axis 196 | character(len=*), intent(in), optional :: yaxis_date_fmt !! if present, used to set the date format for the y-axis 197 | 198 | character(len=max_int_len) :: width_str !! figure width dummy string 199 | character(len=max_int_len) :: height_str !! figure height dummy string 200 | character(len=max_int_len) :: font_size_str !! font size dummy string 201 | character(len=max_int_len) :: axes_labelsize_str !! size of axis labels dummy string 202 | character(len=max_int_len) :: xtick_labelsize_str !! size of x axis tick labels dummy string 203 | character(len=max_int_len) :: ytick_labelsize_str !! size of x axis tick labels dummy string 204 | character(len=max_int_len) :: ztick_labelsize_str !! size of z axis tick labels dummy string 205 | character(len=max_int_len) :: legend_fontsize_str !! size of legend font dummy string 206 | character(len=:),allocatable :: python_fig_func !! Python's function for creating a new Figure instance 207 | 208 | character(len=*), parameter :: default_font_size_str = '10' !! the default font size for plots 209 | 210 | call me%destroy() 211 | 212 | if (present(raw_strings)) then 213 | if (raw_strings) me%raw_str_token = 'r' 214 | end if 215 | 216 | if (present(legend)) then 217 | me%show_legend = legend 218 | else 219 | me%show_legend = .false. 220 | end if 221 | if (present(use_numpy)) then 222 | me%use_numpy = use_numpy 223 | else 224 | me%use_numpy = .true. 225 | end if 226 | if (present(use_oo_api)) then 227 | me%use_oo_api = use_oo_api 228 | else 229 | me%use_oo_api = .false. 230 | end if 231 | if (present(figsize)) then 232 | call integer_to_string(figsize(1), width_str) 233 | call integer_to_string(figsize(2), height_str) 234 | end if 235 | if (present(mplot3d)) then 236 | me%mplot3d = mplot3d 237 | else 238 | me%mplot3d = .false. 239 | end if 240 | if (present(polar)) then 241 | me%polar = polar 242 | else 243 | me%polar = .false. 244 | end if 245 | if (present(axis_equal)) then 246 | me%axis_equal = axis_equal 247 | else 248 | me%axis_equal = .false. 249 | end if 250 | if (present(real_fmt)) then 251 | me%real_fmt = trim(adjustl(real_fmt)) 252 | else 253 | me%real_fmt = real_fmt_default 254 | end if 255 | if (present(tight_layout)) then 256 | me%tight_layout = tight_layout 257 | else 258 | me%tight_layout = .false. 259 | end if 260 | if (present(usetex)) then 261 | me%usetex = usetex 262 | else 263 | me%usetex = .false. 264 | end if 265 | if (present(xaxis_date_fmt)) then 266 | me%xaxis_date_fmt = xaxis_date_fmt 267 | else 268 | if (allocated(me%xaxis_date_fmt)) deallocate(me%xaxis_date_fmt) 269 | end if 270 | if (present(yaxis_date_fmt)) then 271 | me%yaxis_date_fmt = yaxis_date_fmt 272 | else 273 | if (allocated(me%yaxis_date_fmt)) deallocate(me%yaxis_date_fmt) 274 | end if 275 | 276 | call optional_int_to_string(font_size, font_size_str, default_font_size_str) 277 | call optional_int_to_string(axes_labelsize, axes_labelsize_str, default_font_size_str) 278 | call optional_int_to_string(xtick_labelsize, xtick_labelsize_str, default_font_size_str) 279 | call optional_int_to_string(ytick_labelsize, ytick_labelsize_str, default_font_size_str) 280 | call optional_int_to_string(ztick_labelsize, ztick_labelsize_str, default_font_size_str) 281 | call optional_int_to_string(legend_fontsize, legend_fontsize_str, default_font_size_str) 282 | 283 | me%str = '' 284 | 285 | call me%add_str('#!/usr/bin/env python') 286 | call me%add_str('') 287 | 288 | call me%add_str('import matplotlib') 289 | if (me%use_oo_api) then 290 | call me%add_str('from matplotlib.figure import Figure') 291 | call me%add_str('from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas') 292 | else 293 | call me%add_str('import matplotlib.pyplot as plt') 294 | endif 295 | if (me%mplot3d) call me%add_str('from mpl_toolkits.mplot3d import Axes3D') 296 | if (me%use_numpy) call me%add_str('import numpy as np') 297 | call me%add_str('') 298 | 299 | call me%add_str('matplotlib.rcParams["font.family"] = "Serif"') 300 | call me%add_str('matplotlib.rcParams["font.size"] = '//trim(font_size_str)) 301 | call me%add_str('matplotlib.rcParams["axes.labelsize"] = '//trim(axes_labelsize_str)) 302 | call me%add_str('matplotlib.rcParams["xtick.labelsize"] = '//trim(xtick_labelsize_str)) 303 | call me%add_str('matplotlib.rcParams["ytick.labelsize"] = '//trim(ytick_labelsize_str)) 304 | call me%add_str('matplotlib.rcParams["legend.fontsize"] = '//trim(legend_fontsize_str)) 305 | if (me%usetex) call me%add_str('matplotlib.rcParams["text.usetex"] = True') 306 | 307 | call me%add_str('') 308 | 309 | if (me%use_oo_api) then 310 | python_fig_func = 'Figure' 311 | else 312 | python_fig_func = 'plt.figure' 313 | endif 314 | if (present(figsize)) then !if specifying the figure size 315 | call me%add_str('fig = '//python_fig_func//'(figsize=('//trim(width_str)//','//trim(height_str)//'),facecolor="white")') 316 | else 317 | call me%add_str('fig = '//python_fig_func//'(facecolor="white")') 318 | end if 319 | 320 | if (me%mplot3d) then 321 | call me%add_str('ax = fig.add_subplot(1, 1, 1, projection=''3d'')') 322 | elseif (me%polar) then 323 | call me%add_str('ax = fig.add_subplot(1, 1, 1, projection=''polar'')') 324 | else 325 | call me%add_str('ax = fig.add_subplot(1, 1, 1)') 326 | end if 327 | 328 | if (present(grid)) then 329 | if (grid) call me%add_str('ax.grid()') 330 | end if 331 | 332 | if (present(axisbelow)) then 333 | me%axisbelow = axisbelow 334 | else 335 | me%axisbelow = .true. ! default 336 | end if 337 | if (me%axisbelow) call me%add_str('ax.set_axisbelow(True)') 338 | 339 | if (present(xlabel)) call me%add_str('ax.set_xlabel('//trim(me%raw_str_token)//'"'//trim(xlabel)//'")') 340 | if (present(ylabel)) call me%add_str('ax.set_ylabel('//trim(me%raw_str_token)//'"'//trim(ylabel)//'")') 341 | if (present(zlabel)) call me%add_str('ax.set_zlabel('//trim(me%raw_str_token)//'"'//trim(zlabel)//'")') 342 | if (present(title)) call me%add_str('ax.set_title('//trim(me%raw_str_token)//'"' //trim(title) //'")') 343 | 344 | call me%add_str('') 345 | 346 | end subroutine initialize 347 | !***************************************************************************************** 348 | 349 | !***************************************************************************************** 350 | !> author: Jacob Williams 351 | ! 352 | ! Add an x,y plot. 353 | 354 | subroutine add_plot(me, x, y, label, linestyle, markersize, linewidth, xlim, ylim, xscale, yscale, color, istat) 355 | 356 | class(pyplot), intent (inout) :: me !! pyplot handler 357 | real(wp), dimension(:), intent (in) :: x !! x values 358 | real(wp), dimension(:), intent (in) :: y !! y values 359 | character(len=*), intent (in) :: label !! plot label 360 | character(len=*), intent (in) :: linestyle !! style of the plot line 361 | integer, intent (in), optional :: markersize !! size of the plot markers 362 | integer, intent (in), optional :: linewidth !! width of the plot line 363 | real(wp),dimension(2), intent (in), optional :: xlim !! x-axis range 364 | real(wp),dimension(2), intent (in), optional :: ylim !! y-axis range 365 | character(len=*), intent (in), optional :: xscale !! example: 'linear' (default), 'log' 366 | character(len=*), intent (in), optional :: yscale !! example: 'linear' (default), 'log' 367 | real(wp),dimension(:), intent (in), optional :: color !! RGB color tuple [0-1,0-1,0-1] 368 | integer, intent (out), optional :: istat !! status output (0 means no problems) 369 | 370 | character(len=:), allocatable :: arg_str !! the arguments to pass to `plot` 371 | character(len=:), allocatable :: xstr !! x values stringified 372 | character(len=:), allocatable :: ystr !! y values stringified 373 | character(len=:), allocatable :: xlimstr !! xlim values stringified 374 | character(len=:), allocatable :: ylimstr !! ylim values stringified 375 | character(len=:), allocatable :: color_str !! color values stringified 376 | character(len=max_int_len) :: imark !! actual markers size 377 | character(len=max_int_len) :: iline !! actual line width 378 | character(len=*), parameter :: xname = 'x' !! x variable name for script 379 | character(len=*), parameter :: yname = 'y' !! y variable name for script 380 | 381 | if (allocated(me%str)) then 382 | 383 | if (present(istat)) istat = 0 384 | 385 | !axis limits (optional): 386 | if (present(xlim)) call vec_to_string(xlim, me%real_fmt, xlimstr, me%use_numpy) 387 | if (present(ylim)) call vec_to_string(ylim, me%real_fmt, ylimstr, me%use_numpy) 388 | 389 | !convert the arrays to strings: 390 | call vec_to_string(x, me%real_fmt, xstr, me%use_numpy) 391 | call vec_to_string(y, me%real_fmt, ystr, me%use_numpy) 392 | 393 | !get optional inputs (if not present, set default value): 394 | call optional_int_to_string(markersize, imark, '3') 395 | call optional_int_to_string(linewidth, iline, '3') 396 | 397 | !write the arrays: 398 | call me%add_str(trim(xname)//' = '//xstr) 399 | call me%add_str(trim(yname)//' = '//ystr) 400 | call me%add_str('') 401 | 402 | !main arguments for plot: 403 | arg_str = trim(xname)//','//& 404 | trim(yname)//','//& 405 | trim(me%raw_str_token)//'"'//trim(linestyle)//'",'//& 406 | 'linewidth='//trim(adjustl(iline))//','//& 407 | 'markersize='//trim(adjustl(imark))//','//& 408 | 'label='//trim(me%raw_str_token)//'"'//trim(label)//'"' 409 | 410 | ! optional arguments: 411 | if (present(color)) then 412 | if (size(color)<=3) then 413 | call vec_to_string(color(1:3), '*', color_str, use_numpy=.false., is_tuple=.true.) 414 | arg_str = arg_str//',color='//trim(color_str) 415 | end if 416 | end if 417 | 418 | !write the plot statement: 419 | call me%add_str('ax.plot('//arg_str//')') 420 | 421 | !axis limits: 422 | if (allocated(xlimstr)) call me%add_str('ax.set_xlim('//xlimstr//')') 423 | if (allocated(ylimstr)) call me%add_str('ax.set_ylim('//ylimstr//')') 424 | 425 | !axis scales: 426 | if (present(xscale)) call me%add_str('ax.set_xscale('//trim(me%raw_str_token)//'"'//xscale//'")') 427 | if (present(yscale)) call me%add_str('ax.set_yscale('//trim(me%raw_str_token)//'"'//yscale//'")') 428 | 429 | call me%add_str('') 430 | 431 | else 432 | if (present(istat)) istat = -1 433 | write(error_unit,'(A)') 'Error in add_plot: pyplot class not properly initialized.' 434 | end if 435 | 436 | end subroutine add_plot 437 | !***************************************************************************************** 438 | 439 | !***************************************************************************************** 440 | !> author: Jimmy Leta 441 | ! 442 | ! Add a histogram plot. 443 | 444 | subroutine add_hist(me, x, label, xlim, ylim, xscale, yscale, bins, normed, cumulative, istat) 445 | 446 | class(pyplot), intent (inout) :: me !! pyplot handler 447 | real(wp), dimension(:), intent (in) :: x !! array of data 448 | character(len=*), intent (in) :: label !! plot label 449 | real(wp),dimension(2), intent (in), optional :: xlim !! x-axis range 450 | real(wp),dimension(2), intent (in), optional :: ylim !! y-axis range 451 | character(len=*), intent (in), optional :: xscale !! example: 'linear' (default), 'log' 452 | character(len=*), intent (in), optional :: yscale !! example: 'linear' (default), 'log' 453 | integer, intent (in), optional :: bins !! number of bins 454 | logical, intent (in), optional :: normed !! boolean flag that determines whether bin counts are normalized [NO LONGER USED] 455 | logical, intent (in), optional :: cumulative !! boolean flag that determines whether histogram represents the cumulative density of dataset 456 | integer, intent (out),optional :: istat !! status output (0 means no problems) 457 | 458 | character(len=*), parameter :: xname = 'x' !! x variable name for script 459 | character(len=:), allocatable :: xstr !! x values stringified 460 | character(len=:), allocatable :: xlimstr !! xlim values stringified 461 | character(len=:), allocatable :: ylimstr !! ylim values stringified 462 | character(len=:), allocatable :: cumulativestr !! 463 | character(len=max_int_len) :: binsstr !! 464 | 465 | if (allocated(me%str)) then 466 | 467 | if (present(istat)) istat = 0 468 | 469 | !axis limits (optional): 470 | if (present(xlim)) call vec_to_string(xlim, me%real_fmt, xlimstr, me%use_numpy) 471 | if (present(ylim)) call vec_to_string(ylim, me%real_fmt, ylimstr, me%use_numpy) 472 | 473 | !convert the arrays to strings: 474 | call vec_to_string(x, me%real_fmt, xstr, me%use_numpy) 475 | 476 | !write the arrays: 477 | call me%add_str(trim(xname)//' = '//xstr) 478 | call me%add_str('') 479 | 480 | !get optional inputs (if not present, set default value): 481 | call optional_int_to_string(bins, binsstr, '10') 482 | call optional_logical_to_string(cumulative, cumulativestr, 'False') 483 | 484 | !write the plot statement: 485 | call me%add_str('ax.hist('//& 486 | trim(xname)//','//& 487 | 'label='//trim(me%raw_str_token)//'"'//trim(label)//'",'//& 488 | 'bins='//trim(binsstr)//','//& 489 | 'cumulative='//trim(cumulativestr)//')') 490 | 491 | !axis limits: 492 | if (allocated(xlimstr)) call me%add_str('ax.set_xlim('//xlimstr//')') 493 | if (allocated(ylimstr)) call me%add_str('ax.set_ylim('//ylimstr//')') 494 | 495 | !axis scales: 496 | if (present(xscale)) call me%add_str('ax.set_xscale('//trim(me%raw_str_token)//'"'//xscale//'")') 497 | if (present(yscale)) call me%add_str('ax.set_yscale('//trim(me%raw_str_token)//'"'//yscale//'")') 498 | 499 | call me%add_str('') 500 | 501 | else 502 | if (present(istat)) istat = -1 503 | write(error_unit,'(A)') 'Error in add_plot: pyplot class not properly initialized.' 504 | end if 505 | 506 | end subroutine add_hist 507 | !***************************************************************************************** 508 | 509 | !***************************************************************************************** 510 | !> author: Jacob Williams 511 | ! 512 | ! Add a contour plot. 513 | ! 514 | !@note This requires `use_numpy` to be True. 515 | 516 | subroutine add_contour(me, x, y, z, linestyle, linewidth, levels, color, & 517 | filled, cmap, colorbar, istat) 518 | 519 | class(pyplot), intent (inout) :: me !! pyplot handler 520 | real(wp),dimension(:), intent (in) :: x !! x values 521 | real(wp),dimension(:), intent (in) :: y !! y values 522 | real(wp),dimension(:,:), intent (in) :: z !! z values (a matrix) 523 | character(len=*), intent (in) :: linestyle !! style of the plot line 524 | integer, intent (in), optional :: linewidth !! width of the plot line [only used when `filled=False`] 525 | real(wp),dimension(:), intent (in), optional :: levels !! contour levels to plot 526 | character(len=*), intent (in), optional :: color !! color of the contour line 527 | logical, intent (in), optional :: filled !! use filled control (default=False) 528 | character(len=*), intent (in), optional :: cmap !! colormap if filled=True (examples: 'jet', 'bone') 529 | logical, intent (in), optional :: colorbar !! add a colorbar (default=False) 530 | integer, intent (out),optional :: istat !! status output (0 means no problems) 531 | 532 | character(len=:), allocatable :: xstr !! x values stringified 533 | character(len=:), allocatable :: ystr !! y values stringified 534 | character(len=:), allocatable :: zstr !! z values stringified 535 | character(len=:), allocatable :: levelstr !! levels vector stringified 536 | character(len=max_int_len) :: iline !! actual line width 537 | character(len=*), parameter :: xname = 'x' !! x variable name for script 538 | character(len=*), parameter :: yname = 'y' !! y variable name for script 539 | character(len=*), parameter :: zname = 'z' !! z variable name for script 540 | character(len=*), parameter :: xname_ = 'X' !! X variable name for contour 541 | character(len=*), parameter :: yname_ = 'Y' !! Y variable name for contour 542 | character(len=*), parameter :: zname_ = 'Z' !! Z variable name for contour 543 | character(len=:), allocatable :: extras !! optional stuff 544 | character(len=:), allocatable :: contourfunc !! 'contour' or 'contourf' 545 | logical :: is_filled !! if it is a filled contour plot 546 | 547 | if (allocated(me%str)) then 548 | 549 | if (present(istat)) istat = 0 550 | 551 | !convert the arrays to strings: 552 | call vec_to_string(x, me%real_fmt, xstr, me%use_numpy) 553 | call vec_to_string(y, me%real_fmt, ystr, me%use_numpy) 554 | call matrix_to_string(z, me%real_fmt, zstr, me%use_numpy) 555 | if (present(levels)) call vec_to_string(levels, me%real_fmt, levelstr, me%use_numpy) 556 | 557 | !get optional inputs (if not present, set default value): 558 | call optional_int_to_string(linewidth, iline, '3') 559 | 560 | !write the arrays: 561 | call me%add_str(trim(xname)//' = '//xstr) 562 | call me%add_str(trim(yname)//' = '//ystr) 563 | call me%add_str(trim(zname)//' = '//zstr) 564 | call me%add_str('') 565 | 566 | !convert inputs for contour plotting: 567 | call me%add_str(xname_//', '//yname_//' = np.meshgrid('//trim(xname)//', '//trim(yname)//')') 568 | call me%add_str(zname_//' = np.transpose('//zname//')') 569 | 570 | !optional arguments: 571 | extras = '' 572 | if (present(levels)) extras = extras//','//'levels='//levelstr 573 | if (present(color)) extras = extras//','//'colors='//trim(me%raw_str_token)//'"'//color//'"' 574 | if (present(linewidth)) extras = extras//','//'linewidths='//trim(adjustl(iline)) 575 | if (present(cmap)) extras = extras//','//'cmap='//trim(me%raw_str_token)//'"'//cmap//'"' 576 | 577 | !filled or regular: 578 | contourfunc = 'contour' !default 579 | is_filled = .false. 580 | if (present(filled)) then 581 | is_filled = filled 582 | if (filled) contourfunc = 'contourf' !filled contour 583 | end if 584 | 585 | !write the plot statement: 586 | call me%add_str('CS = ax.'//contourfunc//'('//xname_//','//yname_//','//zname_//','//& 587 | 'linestyles='//trim(me%raw_str_token)//'"'//trim(adjustl(linestyle))//'"'//& 588 | extras//')') 589 | 590 | if (present(colorbar)) then 591 | if (colorbar) call me%add_str('fig.colorbar(CS)') 592 | end if 593 | 594 | if (.not. is_filled) call me%add_str('ax.clabel(CS, fontsize=9, inline=1)') 595 | call me%add_str('') 596 | 597 | else 598 | if (present(istat)) istat = -1 599 | write(error_unit,'(A)') 'Error in add_plot: pyplot class not properly initialized.' 600 | end if 601 | 602 | end subroutine add_contour 603 | !***************************************************************************************** 604 | 605 | !***************************************************************************************** 606 | !> author: Jacob Williams 607 | ! 608 | ! Add a surface plot. 609 | ! 610 | !@note This requires `use_numpy` to be True. 611 | 612 | subroutine plot_surface(me, x, y, z, label, linestyle, linewidth, levels, color, & 613 | cmap, colorbar, antialiased, istat) 614 | 615 | class(pyplot), intent (inout) :: me !! pyplot handler 616 | real(wp),dimension(:), intent (in) :: x !! x values 617 | real(wp),dimension(:), intent (in) :: y !! y values 618 | real(wp),dimension(:,:), intent (in) :: z !! z values (a matrix) 619 | character(len=*), intent (in) :: label !! plot label 620 | character(len=*), intent (in) :: linestyle !! style of the plot line 621 | integer, intent (in), optional :: linewidth !! width of the plot line 622 | real(wp),dimension(:), intent (in), optional :: levels !! contour levels to plot 623 | character(len=*), intent (in), optional :: color !! Color of the surface patches 624 | character(len=*), intent (in), optional :: cmap !! colormap if filled=True (examples: 'jet', 'bone') 625 | logical, intent (in), optional :: colorbar !! add a colorbar (default=False) 626 | logical, intent (in), optional :: antialiased !! The surface is made opaque by using antialiased=False 627 | integer, intent (out), optional :: istat !! status output (0 means no problems) 628 | 629 | character(len=:), allocatable :: xstr !! x values stringified 630 | character(len=:), allocatable :: ystr !! y values stringified 631 | character(len=:), allocatable :: zstr !! z values stringified 632 | character(len=:), allocatable :: levelstr !! levels vector stringified 633 | character(len=:), allocatable :: antialiasedstr !! antialiased stringified 634 | character(len=max_int_len) :: iline !! actual line width 635 | character(len=*), parameter :: xname = 'x' !! x variable name for script 636 | character(len=*), parameter :: yname = 'y' !! y variable name for script 637 | character(len=*), parameter :: zname = 'z' !! z variable name for script 638 | character(len=*), parameter :: xname_ = 'X' !! X variable name for contour 639 | character(len=*), parameter :: yname_ = 'Y' !! Y variable name for contour 640 | character(len=*), parameter :: zname_ = 'Z' !! Z variable name for contour 641 | character(len=:), allocatable :: extras !! optional stuff 642 | 643 | if (allocated(me%str)) then 644 | 645 | if (present(istat)) istat = 0 646 | 647 | !convert the arrays to strings: 648 | call vec_to_string(x, me%real_fmt, xstr, me%use_numpy) 649 | call vec_to_string(y, me%real_fmt, ystr, me%use_numpy) 650 | call matrix_to_string(z, me%real_fmt, zstr, me%use_numpy) 651 | if (present(levels)) call vec_to_string(levels, me%real_fmt, levelstr, me%use_numpy) 652 | 653 | !get optional inputs (if not present, set default value): 654 | call optional_int_to_string(linewidth, iline, '3') 655 | call optional_logical_to_string(antialiased, antialiasedstr, 'False') 656 | 657 | !write the arrays: 658 | call me%add_str(trim(xname)//' = '//xstr) 659 | call me%add_str(trim(yname)//' = '//ystr) 660 | call me%add_str(trim(zname)//' = '//zstr) 661 | call me%add_str('') 662 | 663 | !convert inputs for contour plotting: 664 | call me%add_str(xname_//', '//yname_//' = np.meshgrid('//trim(xname)//', '//trim(yname)//')') 665 | call me%add_str(zname_//' = np.transpose('//zname//')') 666 | 667 | !optional arguments: 668 | extras = '' 669 | if (present(levels)) extras = extras//','//'levels='//levelstr 670 | if (present(color)) extras = extras//','//'colors='//trim(me%raw_str_token)//'"'//color//'"' 671 | if (present(linewidth)) extras = extras//','//'linewidths='//trim(adjustl(iline)) 672 | if (present(cmap)) extras = extras//','//'cmap='//trim(me%raw_str_token)//'"'//cmap//'"' 673 | 674 | !write the plot statement: 675 | call me%add_str('CS = ax.plot_surface'//'('//xname_//','//yname_//','//zname_//','//& 676 | 'label='//trim(me%raw_str_token)//'"'//trim(label)//'",'//& 677 | 'antialiased='//trim(antialiasedstr)//','//& 678 | 'linestyles='//trim(me%raw_str_token)//'"'//trim(adjustl(linestyle))//'"'//& 679 | extras//')') 680 | 681 | if (present(colorbar)) then 682 | if (colorbar) call me%add_str('fig.colorbar(CS)') 683 | end if 684 | 685 | call me%add_str('') 686 | 687 | else 688 | if (present(istat)) istat = -1 689 | write(error_unit,'(A)') 'Error in add_plot: pyplot class not properly initialized.' 690 | end if 691 | 692 | end subroutine plot_surface 693 | !***************************************************************************************** 694 | 695 | !***************************************************************************************** 696 | !> author: Jacob Williams 697 | ! 698 | ! Add a wireframe plot. 699 | ! 700 | !@note This requires `use_numpy` to be True. 701 | 702 | subroutine plot_wireframe(me, x, y, z, label, linestyle, linewidth, levels, color, & 703 | cmap, colorbar, antialiased, istat) 704 | 705 | class(pyplot), intent (inout) :: me !! pyplot handler 706 | real(wp),dimension(:), intent (in) :: x !! x values 707 | real(wp),dimension(:), intent (in) :: y !! y values 708 | real(wp),dimension(:,:), intent (in) :: z !! z values (a matrix) 709 | character(len=*), intent (in) :: label !! plot label 710 | character(len=*), intent (in) :: linestyle !! style of the plot line 711 | integer, intent (in), optional :: linewidth !! width of the plot line 712 | real(wp),dimension(:), intent (in), optional :: levels !! contour levels to plot 713 | character(len=*), intent (in), optional :: color !! Color of the surface patches 714 | character(len=*), intent (in), optional :: cmap !! colormap if filled=True (examples: 'jet', 'bone') 715 | logical, intent (in), optional :: colorbar !! add a colorbar (default=False) 716 | logical, intent (in), optional :: antialiased !! The surface is made opaque by using antialiased=False 717 | integer, intent (out), optional :: istat !! status output (0 means no problems) 718 | 719 | character(len=:), allocatable :: xstr !! x values stringified 720 | character(len=:), allocatable :: ystr !! y values stringified 721 | character(len=:), allocatable :: zstr !! z values stringified 722 | character(len=:), allocatable :: levelstr !! levels vector stringified 723 | character(len=:), allocatable :: antialiasedstr !! antialiased stringified 724 | character(len=max_int_len) :: iline !! actual line width 725 | character(len=*), parameter :: xname = 'x' !! x variable name for script 726 | character(len=*), parameter :: yname = 'y' !! y variable name for script 727 | character(len=*), parameter :: zname = 'z' !! z variable name for script 728 | character(len=*), parameter :: xname_ = 'X' !! X variable name for contour 729 | character(len=*), parameter :: yname_ = 'Y' !! Y variable name for contour 730 | character(len=*), parameter :: zname_ = 'Z' !! Z variable name for contour 731 | character(len=:), allocatable :: extras !! optional stuff 732 | 733 | if (allocated(me%str)) then 734 | 735 | if (present(istat)) istat = 0 736 | 737 | !convert the arrays to strings: 738 | call vec_to_string(x, me%real_fmt, xstr, me%use_numpy) 739 | call vec_to_string(y, me%real_fmt, ystr, me%use_numpy) 740 | call matrix_to_string(z, me%real_fmt, zstr, me%use_numpy) 741 | if (present(levels)) call vec_to_string(levels, me%real_fmt, levelstr, me%use_numpy) 742 | 743 | !get optional inputs (if not present, set default value): 744 | call optional_int_to_string(linewidth, iline, '3') 745 | call optional_logical_to_string(antialiased, antialiasedstr, 'False') 746 | 747 | !write the arrays: 748 | call me%add_str(trim(xname)//' = '//xstr) 749 | call me%add_str(trim(yname)//' = '//ystr) 750 | call me%add_str(trim(zname)//' = '//zstr) 751 | call me%add_str('') 752 | 753 | !convert inputs for contour plotting: 754 | call me%add_str(xname_//', '//yname_//' = np.meshgrid('//trim(xname)//', '//trim(yname)//')') 755 | call me%add_str(zname_//' = np.transpose('//zname//')') 756 | 757 | !optional arguments: 758 | extras = '' 759 | if (present(levels)) extras = extras//','//'levels='//levelstr 760 | if (present(color)) extras = extras//','//'colors='//trim(me%raw_str_token)//'"'//color//'"' 761 | if (present(linewidth)) extras = extras//','//'linewidths='//trim(adjustl(iline)) 762 | if (present(cmap)) extras = extras//','//'cmap='//trim(me%raw_str_token)//'"'//cmap//'"' 763 | 764 | !write the plot statement: 765 | call me%add_str('CS = ax.plot_wireframe'//'('//xname_//','//yname_//','//zname_//','//& 766 | 'label='//trim(me%raw_str_token)//'"'//trim(label)//'",'//& 767 | 'antialiased='//trim(antialiasedstr)//','//& 768 | 'linestyles='//trim(me%raw_str_token)//'"'//trim(adjustl(linestyle))//'"'//& 769 | extras//')') 770 | 771 | if (present(colorbar)) then 772 | if (colorbar) call me%add_str('fig.colorbar(CS)') 773 | end if 774 | 775 | call me%add_str('') 776 | 777 | else 778 | if (present(istat)) istat = -1 779 | write(error_unit,'(A)') 'Error in add_plot: pyplot class not properly initialized.' 780 | end if 781 | 782 | end subroutine plot_wireframe 783 | !***************************************************************************************** 784 | 785 | !***************************************************************************************** 786 | !> author: Jacob Williams 787 | ! 788 | ! Add a 3D x,y,z plot. 789 | ! 790 | !@note Must initialize the class with ```mplot3d=.true.``` 791 | 792 | subroutine add_3d_plot(me, x, y, z, label, linestyle, markersize, linewidth, istat) 793 | 794 | class(pyplot), intent (inout) :: me !! pyplot handler 795 | real(wp), dimension(:), intent (in) :: x !! x values 796 | real(wp), dimension(:), intent (in) :: y !! y values 797 | real(wp), dimension(:), intent (in) :: z !! z values 798 | character(len=*), intent (in) :: label !! plot label 799 | character(len=*), intent (in) :: linestyle !! style of the plot line 800 | integer, intent (in), optional :: markersize !! size of the plot markers 801 | integer, intent (in), optional :: linewidth !! width of the plot line 802 | integer, intent (out), optional :: istat !! status output (0 means no problems) 803 | 804 | character(len=:), allocatable :: xstr !! x values stringified 805 | character(len=:), allocatable :: ystr !! y values stringified 806 | character(len=:), allocatable :: zstr !! z values stringified 807 | character(len=max_int_len) :: imark !! actual markers size 808 | character(len=max_int_len) :: iline !! actual line width 809 | character(len=*), parameter :: xname = 'x' !! x variable name for script 810 | character(len=*), parameter :: yname = 'y' !! y variable name for script 811 | character(len=*), parameter :: zname = 'z' !! z variable name for script 812 | 813 | if (allocated(me%str)) then 814 | 815 | if (present(istat)) istat = 0 816 | 817 | !convert the arrays to strings: 818 | call vec_to_string(x, me%real_fmt, xstr, me%use_numpy) 819 | call vec_to_string(y, me%real_fmt, ystr, me%use_numpy) 820 | call vec_to_string(z, me%real_fmt, zstr, me%use_numpy) 821 | 822 | !get optional inputs (if not present, set default value): 823 | call optional_int_to_string(markersize, imark, '3') 824 | call optional_int_to_string(linewidth, iline, '3') 825 | 826 | !write the arrays: 827 | call me%add_str(trim(xname)//' = '//xstr) 828 | call me%add_str(trim(yname)//' = '//ystr) 829 | call me%add_str(trim(zname)//' = '//zstr) 830 | call me%add_str('') 831 | 832 | !write the plot statement: 833 | call me%add_str('ax.plot('//& 834 | trim(xname)//','//& 835 | trim(yname)//','//& 836 | trim(zname)//','//& 837 | trim(me%raw_str_token)//'"'//trim(linestyle)//'",'//& 838 | 'linewidth='//trim(adjustl(iline))//','//& 839 | 'markersize='//trim(adjustl(imark))//','//& 840 | 'label='//trim(me%raw_str_token)//'"'//trim(label)//'")') 841 | call me%add_str('') 842 | 843 | else 844 | if (present(istat)) istat = -1 845 | write(error_unit,'(A)') 'Error in add_3d_plot: pyplot class not properly initialized.' 846 | end if 847 | 848 | end subroutine add_3d_plot 849 | !***************************************************************************************** 850 | 851 | !***************************************************************************************** 852 | !> author: Jacob Williams 853 | ! 854 | ! Add a sphere to a 3D x,y,z plot. 855 | ! 856 | !@note Must initialize the class with `mplot3d=.true.` and `use_numpy=.true.`. 857 | 858 | subroutine add_sphere(me, r, xc, yc, zc, n_facets, linewidth, antialiased, color, istat) 859 | 860 | implicit none 861 | 862 | class(pyplot), intent (inout) :: me !! pyplot handler 863 | real(wp), intent (in) :: r !! radius of the sphere 864 | real(wp), intent (in) :: xc !! x value of sphere center 865 | real(wp), intent (in) :: yc !! y value of sphere center 866 | real(wp), intent (in) :: zc !! z value of sphere center 867 | integer, intent (in), optional :: n_facets !! [default is 100] 868 | integer, intent (in), optional :: linewidth !! line width 869 | logical, intent (in), optional :: antialiased !! enabled anti-aliasing 870 | character(len=*), intent (in), optional :: color !! color of the contour line 871 | integer, intent (out), optional :: istat !! status output (0 means no problems) 872 | 873 | character(len=:), allocatable :: rstr !! `r` value stringified 874 | character(len=:), allocatable :: xcstr !! `xc` value stringified 875 | character(len=:), allocatable :: ycstr !! `yc` value stringified 876 | character(len=:), allocatable :: zcstr !! `zc` value stringified 877 | character(len=*), parameter :: xname = 'x' !! `x` variable name for script 878 | character(len=*), parameter :: yname = 'y' !! `y` variable name for script 879 | character(len=*), parameter :: zname = 'z' !! `z` variable name for script 880 | 881 | character(len=max_int_len) :: linewidth_str !! `linewidth` input stringified 882 | character(len=:), allocatable :: antialiased_str !! `antialised` input stringified 883 | character(len=max_int_len) :: n_facets_str !! `n_facets` input stringified 884 | character(len=:), allocatable :: extras !! optional stuff string 885 | 886 | if (allocated(me%str)) then 887 | 888 | !get optional inputs (if not present, set default value): 889 | call optional_int_to_string(n_facets, n_facets_str, '100') 890 | extras = '' 891 | if (present(linewidth)) then 892 | call optional_int_to_string(linewidth, linewidth_str, '1') 893 | extras = extras//','//'linewidth='//linewidth_str 894 | end if 895 | if (present(antialiased)) then 896 | call optional_logical_to_string(antialiased, antialiased_str, 'False') 897 | extras = extras//','//'antialiased='//antialiased_str 898 | end if 899 | if (present(color)) then 900 | extras = extras//','//'color='//trim(me%raw_str_token)//'"'//trim(color)//'"' 901 | end if 902 | 903 | if (present(istat)) istat = 0 904 | 905 | !convert the arrays to strings: 906 | call real_to_string(r , me%real_fmt, rstr) 907 | call real_to_string(xc, me%real_fmt, xcstr) 908 | call real_to_string(yc, me%real_fmt, ycstr) 909 | call real_to_string(zc, me%real_fmt, zcstr) 910 | 911 | ! sphere code: 912 | call me%add_str('u = np.linspace(0, 2 * np.pi, '//n_facets_str//')') 913 | call me%add_str('v = np.linspace(0, np.pi, '//n_facets_str//')') 914 | call me%add_str(xname//' = '//xcstr//' + '//rstr//' * np.outer(np.cos(u), np.sin(v))') 915 | call me%add_str(yname//' = '//ycstr//' + '//rstr//' * np.outer(np.sin(u), np.sin(v))') 916 | call me%add_str(zname//' = '//zcstr//' + '//rstr//' * np.outer(np.ones(np.size(u)), np.cos(v))') 917 | call me%add_str('ax.plot_surface('//xname//', '//yname//', '//zname//extras//')') 918 | call me%add_str('') 919 | 920 | else 921 | if (present(istat)) istat = -1 922 | write(error_unit,'(A)') 'Error in add_sphere: pyplot class not properly initialized.' 923 | end if 924 | 925 | end subroutine add_sphere 926 | !***************************************************************************************** 927 | 928 | !***************************************************************************************** 929 | !> author: Jacob Williams 930 | ! 931 | ! Add a bar plot. 932 | 933 | subroutine add_bar(me, x, height, label, width, bottom, color, & 934 | yerr, align, xlim, ylim, xscale, yscale, istat) 935 | 936 | class(pyplot), intent(inout) :: me !! pyplot handler 937 | real(wp), dimension(:), intent(in) :: x !! x bar values 938 | real(wp), dimension(:), intent(in) :: height !! height bar values 939 | character(len=*), intent(in) :: label !! plot label 940 | real(wp), dimension(:), intent(in), optional :: width !! width values 941 | real(wp), dimension(:), intent(in), optional :: bottom !! bottom values 942 | character(len=*), intent(in), optional :: color !! plot color 943 | real(wp), dimension(:), intent(in), optional :: yerr !! yerr values 944 | character(len=*), intent(in), optional :: align !! default: 'center' 945 | real(wp),dimension(2), intent (in), optional :: xlim !! x-axis range 946 | real(wp),dimension(2), intent (in), optional :: ylim !! y-axis range 947 | character(len=*), intent (in), optional :: xscale !! example: 'linear' (default), 'log' 948 | character(len=*), intent (in), optional :: yscale !! example: 'linear' (default), 'log' 949 | integer, intent (out),optional :: istat !! status output (0 means no problems) 950 | 951 | character(len=:), allocatable :: xstr !! x axis values stringified 952 | character(len=:), allocatable :: ystr !! y axis values stringified 953 | character(len=:), allocatable :: xlimstr !! xlim values stringified 954 | character(len=:), allocatable :: ylimstr !! ylim values stringified 955 | character(len=:), allocatable :: wstr !! width values stringified 956 | character(len=:), allocatable :: bstr !! bottom values stringified 957 | character(len=:), allocatable :: plt_str !! plot string 958 | character(len=:), allocatable :: yerr_str !! yerr values stringified 959 | character(len=*), parameter :: xname = 'x' !! x axis name 960 | character(len=*), parameter :: yname = 'y' !! y axis name 961 | character(len=*), parameter :: wname = 'w' !! width name 962 | character(len=*), parameter :: bname = 'b' !! bottom name 963 | character(len=*), parameter :: yerrname = 'yerr' !! yerr name 964 | 965 | if (allocated(me%str)) then 966 | 967 | if (present(istat)) istat = 0 968 | 969 | !axis limits (optional): 970 | if (present(xlim)) call vec_to_string(xlim, me%real_fmt, xlimstr, me%use_numpy) 971 | if (present(ylim)) call vec_to_string(ylim, me%real_fmt, ylimstr, me%use_numpy) 972 | 973 | !convert the arrays to strings: 974 | call vec_to_string(x, me%real_fmt, xstr, me%use_numpy) 975 | call vec_to_string(height, me%real_fmt, ystr, me%use_numpy) 976 | if (present(width)) call vec_to_string(width, me%real_fmt, wstr, me%use_numpy) 977 | if (present(bottom)) call vec_to_string(bottom, me%real_fmt, bstr, me%use_numpy) 978 | if (present(yerr)) call vec_to_string(yerr, me%real_fmt, yerr_str, me%use_numpy) 979 | 980 | !write the arrays: 981 | call me%add_str(trim(xname)//' = '//xstr) 982 | call me%add_str(trim(yname)//' = '//ystr) 983 | if (present(width)) call me%add_str(trim(wname)//' = '//wstr) 984 | if (present(bottom)) call me%add_str(trim(bname)//' = '//bstr) 985 | if (present(yerr)) call me%add_str(trim(yerrname)//' = '//yerr_str) 986 | call me%add_str('') 987 | 988 | !create the plot string: 989 | plt_str = 'ax.bar('//& 990 | 'x='//trim(xname)//','//& 991 | 'height='//trim(yname)//',' 992 | if (present(yerr)) plt_str=plt_str//'yerr='//trim(yerrname)//',' 993 | if (present(width)) plt_str=plt_str//'width='//trim(wname)//',' 994 | if (present(bottom)) plt_str=plt_str//'bottom='//trim(bstr)//',' 995 | if (present(color)) plt_str=plt_str//'color='//trim(me%raw_str_token)//'"'//trim(color)//'",' 996 | if (present(align)) plt_str=plt_str//'align='//trim(me%raw_str_token)//'"'//trim(align)//'",' 997 | plt_str=plt_str//'label='//trim(me%raw_str_token)//'"'//trim(label)//'")' 998 | 999 | !write the plot statement: 1000 | call me%add_str(plt_str) 1001 | 1002 | !axis limits: 1003 | if (allocated(xlimstr)) call me%add_str('ax.set_xlim('//xlimstr//')') 1004 | if (allocated(ylimstr)) call me%add_str('ax.set_ylim('//ylimstr//')') 1005 | 1006 | !axis scales: 1007 | if (present(xscale)) call me%add_str('ax.set_xscale('//trim(me%raw_str_token)//'"'//xscale//'")') 1008 | if (present(yscale)) call me%add_str('ax.set_yscale('//trim(me%raw_str_token)//'"'//yscale//'")') 1009 | 1010 | call me%add_str('') 1011 | 1012 | else 1013 | if (present(istat)) istat = -1 1014 | write(error_unit,'(A)') 'Error in add_bar: pyplot class not properly initialized.' 1015 | end if 1016 | 1017 | end subroutine add_bar 1018 | !***************************************************************************************** 1019 | 1020 | !***************************************************************************************** 1021 | !> 1022 | ! Add an image plot using `imshow`. 1023 | ! 1024 | !### Note 1025 | ! * Based on code by Ricardo Torres, 4/2/2017. 1026 | 1027 | subroutine add_imshow(me, x, xlim, ylim, istat) 1028 | 1029 | class(pyplot), intent (inout) :: me !! pyplot handler 1030 | real(wp),dimension(:,:),intent (in) :: x !! x values 1031 | real(wp),dimension(2), intent (in), optional :: xlim !! x-axis range 1032 | real(wp),dimension(2), intent (in), optional :: ylim !! y-axis range 1033 | integer, intent (out),optional :: istat !! status output (0 means no problems) 1034 | 1035 | character(len=:), allocatable :: xstr !! x values stringified 1036 | character(len=*), parameter :: xname = 'x' !! x variable name for script 1037 | 1038 | !axis limits (optional): 1039 | character(len=:), allocatable :: xlimstr !! xlim values stringified 1040 | character(len=:), allocatable :: ylimstr !! ylim values stringified 1041 | 1042 | if (allocated(me%str)) then 1043 | 1044 | if (present(istat)) istat = 0 1045 | 1046 | if (present(xlim)) call vec_to_string(xlim, me%real_fmt, xlimstr, me%use_numpy) 1047 | if (present(ylim)) call vec_to_string(ylim, me%real_fmt, ylimstr, me%use_numpy) 1048 | 1049 | !convert the arrays to strings: 1050 | call matrix_to_string(x, me%real_fmt, xstr, me%use_numpy) 1051 | 1052 | !write the arrays: 1053 | call me%add_str(trim(xname)//' = '//xstr) 1054 | call me%add_str('') 1055 | 1056 | !write the plot statement: 1057 | call me%add_str('ax.imshow('//trim(xname)//')') 1058 | call me%add_str('') 1059 | 1060 | !axis limits: 1061 | if (allocated(xlimstr)) call me%add_str('ax.set_xlim('//xlimstr//')') 1062 | if (allocated(ylimstr)) call me%add_str('ax.set_ylim('//ylimstr//')') 1063 | 1064 | else 1065 | if (present(istat)) istat = -1 1066 | write(error_unit,'(A)') 'Error in add_imshow: pyplot class not properly initialized.' 1067 | end if 1068 | 1069 | end subroutine add_imshow 1070 | !***************************************************************************************** 1071 | 1072 | !***************************************************************************************** 1073 | !> author: Alexander Sandrock 1074 | ! 1075 | ! Add an x,y plot with errorbars. 1076 | 1077 | subroutine add_errorbar(me, x, y, label, linestyle, xerr, yerr, markersize, linewidth, xlim, ylim, xscale, yscale, color, istat) 1078 | 1079 | class(pyplot), intent (inout) :: me !! pyplot handler 1080 | real(wp), dimension(:), intent (in) :: x !! x values 1081 | real(wp), dimension(:), intent (in) :: y !! y values 1082 | character(len=*), intent (in) :: label !! plot label 1083 | character(len=*), intent (in) :: linestyle !! style of the plot line 1084 | real(wp), dimension(:), intent (in), optional :: xerr !! x errorbar sizes 1085 | real(wp), dimension(:), intent (in), optional :: yerr !! y errorbar sizes 1086 | integer, intent (in), optional :: markersize !! size of the plot markers 1087 | integer, intent (in), optional :: linewidth !! width of the plot line 1088 | real(wp),dimension(2), intent (in), optional :: xlim !! x-axis range 1089 | real(wp),dimension(2), intent (in), optional :: ylim !! y-axis range 1090 | character(len=*), intent (in), optional :: xscale !! example: 'linear' (default), 'log' 1091 | character(len=*), intent (in), optional :: yscale !! example: 'linear' (default), 'log' 1092 | real(wp),dimension(:), intent (in), optional :: color !! RGB color tuple [0-1,0-1,0-1] 1093 | integer, intent (out),optional :: istat !! status output (0 means no problems) 1094 | 1095 | character(len=:), allocatable :: arg_str !! the arguments to pass to `plot` 1096 | character(len=:), allocatable :: xstr !! x values stringified 1097 | character(len=:), allocatable :: ystr !! y values stringified 1098 | character(len=:), allocatable :: xlimstr !! xlim values stringified 1099 | character(len=:), allocatable :: ylimstr !! ylim values stringified 1100 | character(len=:), allocatable :: xerrstr !! xerr values stringified 1101 | character(len=:), allocatable :: yerrstr !! yerr values stringified 1102 | character(len=:), allocatable :: color_str !! color values stringified 1103 | character(len=max_int_len) :: imark !! actual markers size 1104 | character(len=max_int_len) :: iline !! actual line width 1105 | character(len=*), parameter :: xname = 'x' !! x variable name for script 1106 | character(len=*), parameter :: yname = 'y' !! y variable name for script 1107 | character(len=*), parameter :: xerrname = 'xerr' !! xerr variable name for script 1108 | character(len=*), parameter :: yerrname = 'yerr' !! yerr variable name for script 1109 | 1110 | if (allocated(me%str)) then 1111 | 1112 | if (present(istat)) istat = 0 1113 | 1114 | !axis limits (optional): 1115 | if (present(xlim)) call vec_to_string(xlim, me%real_fmt, xlimstr, me%use_numpy) 1116 | if (present(ylim)) call vec_to_string(ylim, me%real_fmt, ylimstr, me%use_numpy) 1117 | !errorbar sizes (optional): 1118 | if (present(xerr)) call vec_to_string(xerr, me%real_fmt, xerrstr, me%use_numpy) 1119 | if (present(yerr)) call vec_to_string(yerr, me%real_fmt, yerrstr, me%use_numpy) 1120 | 1121 | !convert the arrays to strings: 1122 | call vec_to_string(x, me%real_fmt, xstr, me%use_numpy) 1123 | call vec_to_string(y, me%real_fmt, ystr, me%use_numpy) 1124 | 1125 | !get optional inputs (if not present, set default value): 1126 | call optional_int_to_string(markersize, imark, '3') 1127 | call optional_int_to_string(linewidth, iline, '3') 1128 | 1129 | !write the arrays: 1130 | call me%add_str(trim(xname)//' = '//xstr) 1131 | call me%add_str(trim(yname)//' = '//ystr) 1132 | call me%add_str('') 1133 | if (present(xerr)) call me%add_str(trim(xerrname)//' = '//xerrstr) 1134 | if (present(yerr)) call me%add_str(trim(yerrname)//' = '//yerrstr) 1135 | if (present(xerr) .or. present(yerr)) call me%add_str('') 1136 | 1137 | !main arguments for plot: 1138 | arg_str = trim(xname)//','//& 1139 | trim(yname)//','//& 1140 | 'fmt='//trim(me%raw_str_token)//'"'//trim(linestyle)//'",'//& 1141 | 'linewidth='//trim(adjustl(iline))//','//& 1142 | 'markersize='//trim(adjustl(imark))//','//& 1143 | 'label='//trim(me%raw_str_token)//'"'//trim(label)//'"' 1144 | 1145 | ! optional arguments: 1146 | if (present(xerr)) then 1147 | arg_str = arg_str//','//'xerr='//trim(xerrname) 1148 | end if 1149 | if (present(yerr)) then 1150 | arg_str = arg_str//','//'yerr='//trim(yerrname) 1151 | end if 1152 | if (present(color)) then 1153 | if (size(color)<=3) then 1154 | call vec_to_string(color(1:3), '*', color_str, use_numpy=.false., is_tuple=.true.) 1155 | arg_str = arg_str//',color='//trim(color_str) 1156 | end if 1157 | end if 1158 | 1159 | !write the plot statement: 1160 | call me%add_str('ax.errorbar('//arg_str//')') 1161 | 1162 | !axis limits: 1163 | if (allocated(xlimstr)) call me%add_str('ax.set_xlim('//xlimstr//')') 1164 | if (allocated(ylimstr)) call me%add_str('ax.set_ylim('//ylimstr//')') 1165 | 1166 | !axis scales: 1167 | if (present(xscale)) call me%add_str('ax.set_xscale('//trim(me%raw_str_token)//'"'//xscale//'")') 1168 | if (present(yscale)) call me%add_str('ax.set_yscale('//trim(me%raw_str_token)//'"'//yscale//'")') 1169 | 1170 | call me%add_str('') 1171 | 1172 | else 1173 | if (present(istat)) istat = -1 1174 | write(error_unit,'(A)') 'Error in add_errorbar: pyplot class not properly initialized.' 1175 | end if 1176 | 1177 | end subroutine add_errorbar 1178 | !***************************************************************************************** 1179 | 1180 | !***************************************************************************************** 1181 | !> author: Jacob Williams 1182 | ! 1183 | ! Integer to string, specifying the default value if 1184 | ! the optional argument is not present. 1185 | 1186 | subroutine optional_int_to_string(int_value, string_value, default_value) 1187 | 1188 | integer, intent(in), optional :: int_value !! integer value 1189 | character(len=*), intent(out) :: string_value !! integer value stringified 1190 | character(len=*), intent(in) :: default_value !! default integer value 1191 | 1192 | if (present(int_value)) then 1193 | call integer_to_string(int_value, string_value) 1194 | else 1195 | string_value = default_value 1196 | end if 1197 | 1198 | end subroutine optional_int_to_string 1199 | !***************************************************************************************** 1200 | 1201 | !***************************************************************************************** 1202 | !> author: Jacob Williams 1203 | ! 1204 | ! Logical to string, specifying the default value if 1205 | ! the optional argument is not present. 1206 | 1207 | subroutine optional_logical_to_string(logical_value, string_value, default_value) 1208 | 1209 | logical,intent(in),optional :: logical_value 1210 | character(len=:),allocatable,intent(out) :: string_value !! integer value stringified 1211 | character(len=*),intent(in) :: default_value !! default integer value 1212 | 1213 | if (present(logical_value)) then 1214 | if (logical_value) then 1215 | string_value = 'True' 1216 | else 1217 | string_value = 'False' 1218 | end if 1219 | else 1220 | string_value = default_value 1221 | end if 1222 | 1223 | end subroutine optional_logical_to_string 1224 | !***************************************************************************************** 1225 | 1226 | !***************************************************************************************** 1227 | !> author: Jacob Williams 1228 | ! 1229 | ! Integer to string conversion. 1230 | 1231 | subroutine integer_to_string(i, s) 1232 | 1233 | integer, intent(in), optional :: i !! integer value 1234 | character(len=*), intent(out) :: s !! integer value stringified 1235 | 1236 | integer :: istat !! IO status 1237 | 1238 | write(s, int_fmt, iostat=istat) i 1239 | 1240 | if (istat/=0) then 1241 | write(error_unit,'(A)') 'Error converting integer to string' 1242 | s = '****' 1243 | else 1244 | s = adjustl(s) 1245 | end if 1246 | 1247 | end subroutine integer_to_string 1248 | !***************************************************************************************** 1249 | 1250 | !***************************************************************************************** 1251 | !> author: Jacob Williams 1252 | ! 1253 | ! Real scalar to string. 1254 | 1255 | subroutine real_to_string(v, fmt, str) 1256 | 1257 | real(wp), intent(in) :: v !! real values 1258 | character(len=*), intent(in) :: fmt !! real format string 1259 | character(len=:), allocatable, intent(out) :: str !! real values stringified 1260 | 1261 | integer :: istat !! IO status 1262 | character(len=max_real_len) :: tmp !! dummy string 1263 | 1264 | if (fmt=='*') then 1265 | write(tmp, *, iostat=istat) v 1266 | else 1267 | write(tmp, fmt, iostat=istat) v 1268 | end if 1269 | if (istat/=0) then 1270 | write(error_unit,'(A)') 'Error in real_to_string' 1271 | str = '****' 1272 | else 1273 | str = trim(adjustl(tmp)) 1274 | end if 1275 | 1276 | end subroutine real_to_string 1277 | !***************************************************************************************** 1278 | 1279 | !***************************************************************************************** 1280 | !> author: Jacob Williams 1281 | ! 1282 | ! Real vector to string. 1283 | 1284 | subroutine vec_to_string(v, fmt, str, use_numpy, is_tuple) 1285 | 1286 | real(wp), dimension(:), intent(in) :: v !! real values 1287 | character(len=*), intent(in) :: fmt !! real format string 1288 | character(len=:), allocatable, intent(out) :: str !! real values stringified 1289 | logical, intent(in) :: use_numpy !! activate numpy python module usage 1290 | logical,intent(in),optional :: is_tuple !! if true [default], use '()', if false use '[]' 1291 | 1292 | integer :: i !! counter 1293 | integer :: istat !! IO status 1294 | character(len=max_real_len) :: tmp !! dummy string 1295 | logical :: tuple 1296 | 1297 | if (present(is_tuple)) then 1298 | tuple = is_tuple 1299 | else 1300 | tuple = .false. 1301 | end if 1302 | 1303 | if (tuple) then 1304 | str = '(' 1305 | else 1306 | str = '[' 1307 | end if 1308 | 1309 | do i=1, size(v) 1310 | if (fmt=='*') then 1311 | write(tmp, *, iostat=istat) v(i) 1312 | else 1313 | write(tmp, fmt, iostat=istat) v(i) 1314 | end if 1315 | if (istat/=0) then 1316 | write(error_unit,'(A)') 'Error in vec_to_string' 1317 | str = '****' 1318 | return 1319 | end if 1320 | str = str//trim(adjustl(tmp)) 1321 | if (i author: Jacob Williams 1338 | ! 1339 | ! Real matrix (rank 2) to string. 1340 | 1341 | subroutine matrix_to_string(v, fmt, str, use_numpy) 1342 | 1343 | real(wp), dimension(:,:), intent(in) :: v !! real values 1344 | character(len=*), intent(in) :: fmt !! real format string 1345 | character(len=:), allocatable, intent(out) :: str !! real values stringified 1346 | logical, intent(in) :: use_numpy !! activate numpy python module usage 1347 | 1348 | integer :: i !! counter 1349 | character(len=:),allocatable :: tmp !! dummy string 1350 | 1351 | str = '[' 1352 | do i=1, size(v,1) !rows 1353 | call vec_to_string(v(i,:), fmt, tmp, use_numpy) !one row at a time 1354 | str = str//trim(adjustl(tmp)) 1355 | if (i author: Jacob Williams 1367 | ! date: 8/16/2015 1368 | ! 1369 | ! Write the buffer to a file, and then execute it with Python. 1370 | ! 1371 | ! If user specifies a Python file name, then the file is kept, otherwise 1372 | ! a temporary filename is used, and the file is deleted after it is used. 1373 | 1374 | subroutine execute(me, pyfile, istat, python) 1375 | 1376 | class(pyplot), intent(inout) :: me !! pytplot handler 1377 | character(len=*), intent(in), optional :: pyfile !! name of the python script to generate 1378 | integer, intent (out),optional :: istat !! status output (0 means no problems) 1379 | character(len=*), intent(in),optional :: python !! python executable to use. (by default, this is 'python') 1380 | 1381 | integer :: iunit !! IO unit 1382 | character(len=:), allocatable :: file !! file name 1383 | logical :: scratch !! if a scratch file is to be used 1384 | integer :: iostat !! open/close status code 1385 | character(len=:), allocatable :: python_ !! python executable to use 1386 | 1387 | if (allocated(me%str)) then 1388 | 1389 | if (present(istat)) istat = 0 1390 | 1391 | scratch = (.not. present(pyfile)) 1392 | 1393 | !file name to use: 1394 | if (scratch) then 1395 | file = trim(tmp_file) !use the default 1396 | else 1397 | file = trim(pyfile) !use the user-specified name 1398 | end if 1399 | 1400 | if (file == '') then 1401 | if (present(istat)) istat = -1 1402 | write(error_unit,'(A)') 'Error: filename is blank.' 1403 | return 1404 | end if 1405 | 1406 | !open the file: 1407 | open(newunit=iunit, file=file, status='REPLACE', iostat=iostat) 1408 | if (iostat/=0) then 1409 | if (present(istat)) istat = iostat 1410 | write(error_unit,'(A)') 'Error opening file: '//trim(file) 1411 | return 1412 | end if 1413 | 1414 | !write to the file: 1415 | write(iunit, '(A)') me%str 1416 | 1417 | !to ensure that the file is there for the next 1418 | !command line call, we have to close it here. 1419 | close(iunit, iostat=iostat) 1420 | if (iostat/=0) then 1421 | if (present(istat)) istat = iostat 1422 | write(error_unit,'(A)') 'Error closing file: '//trim(file) 1423 | else 1424 | 1425 | if (present(python)) then 1426 | python_ = trim(python) 1427 | else 1428 | python_ = python_exe 1429 | end if 1430 | 1431 | !run the file using python: 1432 | if (file(1:1)/='"') then 1433 | ! if not already in quotes, should enclose in quotes 1434 | call execute_command_line(python_//' "'//file//'"') 1435 | else 1436 | call execute_command_line(python_//' '//file) 1437 | end if 1438 | 1439 | if (scratch) then 1440 | !delete the file (have to reopen it because 1441 | !Fortran has no file delete function) 1442 | open(newunit=iunit, file=file, status='OLD', iostat=iostat) 1443 | if (iostat==0) close(iunit, status='DELETE', iostat=iostat) 1444 | end if 1445 | if (iostat/=0) then 1446 | if (present(istat)) istat = iostat 1447 | write(error_unit,'(A)') 'Error closing file.' 1448 | end if 1449 | 1450 | end if 1451 | 1452 | !cleanup: 1453 | if (allocated(file)) deallocate(file) 1454 | 1455 | else 1456 | if (present(istat)) istat = -1 1457 | end if 1458 | 1459 | end subroutine execute 1460 | !***************************************************************************************** 1461 | 1462 | !***************************************************************************************** 1463 | !> author: Jacob Williams 1464 | ! 1465 | ! Some final things to add before saving or showing the figure. 1466 | 1467 | subroutine finish_ops(me) 1468 | 1469 | class(pyplot),intent(inout) :: me !! pyplot handler 1470 | 1471 | if (me%show_legend) then 1472 | call me%add_str('ax.legend(loc="best")') 1473 | call me%add_str('') 1474 | end if 1475 | if (me%axis_equal) then 1476 | if (me%mplot3d) then 1477 | call me%add_str('ax.set_aspect("auto")') 1478 | call me%add_str('') 1479 | 1480 | call me%add_str('def set_axes_equal(ax):') 1481 | call me%add_str(' x_limits = ax.get_xlim3d()') 1482 | call me%add_str(' y_limits = ax.get_ylim3d()') 1483 | call me%add_str(' z_limits = ax.get_zlim3d()') 1484 | call me%add_str(' x_range = abs(x_limits[1] - x_limits[0])') 1485 | call me%add_str(' x_middle = np.mean(x_limits)') 1486 | call me%add_str(' y_range = abs(y_limits[1] - y_limits[0])') 1487 | call me%add_str(' y_middle = np.mean(y_limits)') 1488 | call me%add_str(' z_range = abs(z_limits[1] - z_limits[0])') 1489 | call me%add_str(' z_middle = np.mean(z_limits)') 1490 | call me%add_str(' plot_radius = 0.5*max([x_range, y_range, z_range])') 1491 | call me%add_str(' ax.set_xlim3d([x_middle - plot_radius, x_middle + plot_radius])') 1492 | call me%add_str(' ax.set_ylim3d([y_middle - plot_radius, y_middle + plot_radius])') 1493 | call me%add_str(' ax.set_zlim3d([z_middle - plot_radius, z_middle + plot_radius])') 1494 | call me%add_str('set_axes_equal(ax)') 1495 | 1496 | else 1497 | call me%add_str('ax.axis("equal")') 1498 | end if 1499 | call me%add_str('') 1500 | end if 1501 | if (allocated(me%xaxis_date_fmt) .or. allocated(me%yaxis_date_fmt)) then 1502 | call me%add_str('from matplotlib.dates import DateFormatter') 1503 | if (allocated(me%xaxis_date_fmt)) & 1504 | call me%add_str('ax.xaxis.set_major_formatter(DateFormatter("'//trim(me%xaxis_date_fmt)//'"))') 1505 | if (allocated(me%yaxis_date_fmt)) & 1506 | call me%add_str('ax.yaxis.set_major_formatter(DateFormatter("'//trim(me%yaxis_date_fmt)//'"))') 1507 | call me%add_str('') 1508 | end if 1509 | if (me%tight_layout) then 1510 | call me%add_str('fig.tight_layout()') 1511 | call me%add_str('') 1512 | end if 1513 | 1514 | end subroutine finish_ops 1515 | !***************************************************************************************** 1516 | 1517 | !***************************************************************************************** 1518 | !> author: Jacob Williams 1519 | ! 1520 | ! Save the figure. 1521 | ! 1522 | !### History 1523 | ! * modified: Johannes Rieke 6/16/2017 1524 | ! * modified: Jacob Williams 6/16/2017 1525 | 1526 | subroutine savefig(me, figfile, pyfile, dpi, transparent, facecolor, edgecolor, orientation, istat, python) 1527 | 1528 | class(pyplot), intent(inout) :: me !! pyplot handler 1529 | character(len=*), intent(in) :: figfile !! file name for the figure 1530 | character(len=*), intent(in), optional :: pyfile !! name of the Python script to generate 1531 | character(len=*), intent(in), optional :: dpi !! resolution of the figure for png 1532 | !! [note this is a string] 1533 | logical, intent(in), optional :: transparent !! transparent background (T/F) 1534 | character(len=*), intent(in), optional :: facecolor !! the colors of the figure rectangle 1535 | character(len=*), intent(in), optional :: edgecolor !! the colors of the figure rectangle 1536 | character(len=*), intent(in), optional :: orientation !! 'landscape' or 'portrait' 1537 | integer, intent (out), optional :: istat !! status output (0 means no problems) 1538 | character(len=*), intent(in),optional :: python !! python executable to use. (by default, this is 'python') 1539 | 1540 | character(len=:),allocatable :: tmp !! for building the `savefig` arguments. 1541 | 1542 | if (allocated(me%str)) then 1543 | 1544 | if (present(istat)) istat = 0 1545 | 1546 | !finish up the string: 1547 | call me%finish_ops() 1548 | 1549 | !build the savefig arguments: 1550 | tmp = trim(me%raw_str_token)//'"'//trim(figfile)//'"' 1551 | if (present(dpi)) tmp = tmp//', dpi='//trim(dpi) 1552 | if (present(transparent)) then 1553 | if (transparent) then 1554 | tmp = tmp//', transparent=True' 1555 | else 1556 | tmp = tmp//', transparent=False' 1557 | end if 1558 | end if 1559 | if (present(facecolor)) tmp = tmp//', facecolor="'//trim(facecolor)//'"' 1560 | if (present(edgecolor)) tmp = tmp//', edgecolor="'//trim(edgecolor)//'"' 1561 | if (present(orientation)) tmp = tmp//', orientation="'//trim(orientation)//'"' 1562 | if (me%use_oo_api) then 1563 | call me%add_str('canvas = FigureCanvas(fig)') 1564 | call me%add_str('canvas.print_figure('//tmp//')') 1565 | else 1566 | call me%add_str('plt.savefig('//tmp//')') 1567 | endif 1568 | deallocate(tmp) 1569 | 1570 | !run it: 1571 | call me%execute(pyfile, istat=istat, python=python) 1572 | 1573 | else 1574 | if (present(istat)) istat = -1 1575 | write(error_unit,'(A)') 'error in savefig: pyplot class not properly initialized.' 1576 | end if 1577 | 1578 | end subroutine savefig 1579 | !***************************************************************************************** 1580 | 1581 | !***************************************************************************************** 1582 | !> author: Johannes Rieke 1583 | ! date: 6/16/2017 1584 | ! 1585 | ! Shows the figure. 1586 | 1587 | subroutine showfig(me, pyfile, istat, python) 1588 | 1589 | class(pyplot), intent(inout) :: me !! pyplot handler 1590 | character(len=*), intent(in), optional :: pyfile !! name of the Python script to generate 1591 | integer, intent (out), optional :: istat !! status output (0 means no problems) 1592 | character(len=*), intent(in),optional :: python !! python executable to use. (by default, this is 'python') 1593 | 1594 | if (.not. allocated(me%str)) then 1595 | 1596 | if (present(istat)) istat = -1 1597 | write(error_unit,'(A)') 'error in showfig: pyplot class not properly initialized.' 1598 | 1599 | else if (me%use_oo_api) then 1600 | 1601 | if (present(istat)) istat = -2 1602 | write(error_unit,'(A)') 'error in showfig: not compatible with "use_oo_api" option' 1603 | 1604 | else 1605 | 1606 | if (present(istat)) istat = 0 1607 | 1608 | !finish up the string: 1609 | call me%finish_ops() 1610 | 1611 | !show figure: 1612 | call me%add_str('plt.show()') 1613 | 1614 | !run it: 1615 | call me%execute(pyfile, istat=istat, python=python) 1616 | 1617 | end if 1618 | 1619 | end subroutine showfig 1620 | !***************************************************************************************** 1621 | 1622 | !***************************************************************************************** 1623 | end module pyplot_module 1624 | !***************************************************************************************** 1625 | -------------------------------------------------------------------------------- /test/color_test.f90: -------------------------------------------------------------------------------- 1 | !***************************************************************************************** 2 | !> 3 | ! Color test 4 | 5 | program color_test 6 | 7 | use pyplot_module, only : pyplot, wp => pyplot_wp 8 | 9 | implicit none 10 | 11 | type(pyplot) :: plt !! pytplot handler 12 | integer :: istat 13 | real(wp), parameter :: F(3) = [0.4510d0, 0.3098d0, 0.5882d0] ! Fortran-lang color 14 | real(wp), parameter :: Y(3) = [0.9608d0, 0.8157d0, 0.0118d0] ! Yellow 15 | 16 | real(wp),dimension(3),parameter :: Ax = [1,2,3] 17 | real(wp),dimension(3),parameter :: Ay = [1,2,3] 18 | real(wp),dimension(3),parameter :: Bx = [1,2,3] 19 | real(wp),dimension(3),parameter :: By = [4,5,6] 20 | 21 | character(len=*), parameter :: testdir = "test/" 22 | 23 | call plt%initialize(figsize=[20,10],title='color test') 24 | 25 | call plt%add_plot(Ax,Ay,label='',linestyle='o',markersize=5,color=F) 26 | call plt%add_plot(Bx,By,label='',linestyle='o',markersize=5,color=Y) 27 | 28 | call plt%savefig(testdir//'color_test.png',& 29 | pyfile=testdir//'color_test.py',istat=istat) 30 | 31 | end program color_test 32 | !***************************************************************************************** 33 | -------------------------------------------------------------------------------- /test/date_test.f90: -------------------------------------------------------------------------------- 1 | !***************************************************************************************** 2 | !> 3 | ! Unit test for [[pyplot_module]]. Using the `xaxis_date_fmt` option for x-axis dates. 4 | 5 | program date_test 6 | 7 | use pyplot_module, only : pyplot, wp => pyplot_wp 8 | 9 | implicit none 10 | 11 | integer,parameter :: n = 100 12 | 13 | real(wp), dimension(:),allocatable :: x !! x values 14 | real(wp), dimension(:),allocatable :: y !! y values 15 | real(wp), dimension(:),allocatable :: sx !! sin(x) values 16 | real(wp), dimension(:),allocatable :: cx !! cos(x) values 17 | real(wp), dimension(:),allocatable :: tx !! sin(x)*cos(x) values 18 | type(pyplot) :: plt !! pytplot handler 19 | integer :: i !! counter 20 | integer :: istat !! status code 21 | 22 | character(len=*), parameter :: testdir = "test/" 23 | character(len=*), parameter :: xaxis_date_fmt = '%m/%d/%y %H:%M:%S' 24 | character(len=*), parameter :: yaxis_date_fmt = '%H:%M:%S' 25 | 26 | ! size arrays: 27 | allocate(x(n)) 28 | allocate(sx(n)) 29 | allocate(cx(n)) 30 | allocate(tx(n)) 31 | 32 | !generate some data: 33 | x = [(real(i,wp), i=0,size(x)-1)]/5.0_wp 34 | sx = 10*sin(x) 35 | cx = cos(x) 36 | tx = sx * cx 37 | 38 | !2d line plot: 39 | call plt%initialize(grid=.true.,xlabel='Calendar date',figsize=[20,10],& 40 | title='date test',legend=.true.,axis_equal=.true.,& 41 | tight_layout=.true., & 42 | xaxis_date_fmt=xaxis_date_fmt, yaxis_date_fmt=yaxis_date_fmt) 43 | call plt%add_plot(x,sx,label='$\sin (x)$',linestyle='b-o',markersize=5,linewidth=2,istat=istat) 44 | call plt%add_plot(x,cx,label='$\cos (x)$',linestyle='r-o',markersize=5,linewidth=2,istat=istat) 45 | call plt%add_plot(x,tx,label='$\sin (x) \cos (x)$',linestyle='g-o',markersize=2,linewidth=1,istat=istat) 46 | call plt%savefig(testdir//'datetest.png', pyfile=testdir//'datetest.py',& 47 | istat=istat) 48 | 49 | end program date_test 50 | !***************************************************************************************** 51 | -------------------------------------------------------------------------------- /test/test.f90: -------------------------------------------------------------------------------- 1 | !***************************************************************************************** 2 | !> author: Jacob Williams 3 | ! date: 4/14/2015 4 | ! license: BSD 5 | ! 6 | ! Unit test for [[pyplot_module]]. 7 | 8 | program test 9 | 10 | use pyplot_module, only : pyplot, wp => pyplot_wp 11 | 12 | implicit none 13 | 14 | integer,parameter :: n = 100 15 | 16 | real(wp), dimension(:),allocatable :: x !! x values 17 | real(wp), dimension(:),allocatable :: y !! y values 18 | real(wp), dimension(:),allocatable :: xerr !! error values 19 | real(wp), dimension(:),allocatable :: yerr !! error values for bar chart 20 | real(wp), dimension(:),allocatable :: sx !! sin(x) values 21 | real(wp), dimension(:),allocatable :: cx !! cos(x) values 22 | real(wp), dimension(:),allocatable :: zx !! 23 | real(wp), dimension(:),allocatable :: tx !! sin(x)*cos(x) values 24 | real(wp), dimension(:,:),allocatable :: z !! z matrix for contour plot 25 | real(wp), dimension(:,:),allocatable :: mat !! image values 26 | type(pyplot) :: plt !! pytplot handler 27 | integer :: i !! counter 28 | integer :: j !! counter 29 | real(wp) :: r2 !! temp variable 30 | integer :: istat !! status code 31 | 32 | real(wp),parameter :: pi = acos(-1.0_wp) 33 | real(wp),parameter :: deg2rad = pi/180.0_wp 34 | 35 | character(len=*), parameter :: testdir = "test/" 36 | 37 | ! size arrays: 38 | allocate(x(n)) 39 | allocate(y(n)) 40 | allocate(yerr(n)) 41 | allocate(sx(n)) 42 | allocate(cx(n)) 43 | allocate(zx(n)) 44 | allocate(tx(n)) 45 | allocate(z(n,n)) 46 | allocate(mat(n,n)) 47 | 48 | !generate some data: 49 | x = [(real(i,wp), i=0,size(x)-1)]/5.0_wp 50 | sx = sin(x) 51 | cx = cos(x) 52 | tx = sx * cx 53 | zx = 0.0_wp 54 | yerr = abs(sx*.25_wp) 55 | 56 | do i=1,n 57 | do j=1,n 58 | mat(i,j) = sin(real(i,wp)*real(j,wp)) 59 | end do 60 | end do 61 | 62 | !2d line plot: 63 | call plt%initialize(grid=.true.,xlabel='angle (rad)',figsize=[20,10],& 64 | title='plot test',legend=.true.,axis_equal=.true.,& 65 | tight_layout=.true.) 66 | call plt%add_plot(x,sx,label='$\sin (x)$',linestyle='b-o',markersize=5,linewidth=2,istat=istat) 67 | call plt%add_plot(x,cx,label='$\cos (x)$',linestyle='r-o',markersize=5,linewidth=2,istat=istat) 68 | call plt%add_plot(x,tx,label='$\sin (x) \cos (x)$',linestyle='g-o',markersize=2,linewidth=1,istat=istat) 69 | call plt%savefig(testdir//'plottest.png', pyfile=testdir//'plottest.py',& 70 | istat=istat) 71 | 72 | !bar chart: 73 | tx = 0.1_wp !for bar width 74 | call plt%initialize(grid=.true.,xlabel='angle (rad)',& 75 | title='bar test',legend=.true.,figsize=[20,10],& 76 | font_size = 20,& 77 | axes_labelsize = 20,& 78 | xtick_labelsize = 20,& 79 | ytick_labelsize = 20,& 80 | legend_fontsize = 20, raw_strings=.true. ) 81 | call plt%add_bar(x=x,height=sx,width=tx,label='$\sin (x)$',& 82 | color='r',yerr=yerr,xlim=[0.0_wp, 20.0_wp],align='center',istat=istat) 83 | call plt%savefig(testdir//'bartest.png', pyfile=testdir//'bartest.py',& 84 | istat=istat) 85 | 86 | !contour plot: 87 | x = [(real(i,wp), i=0,n-1)]/100.0_wp 88 | y = [(real(i,wp), i=0,n-1)]/100.0_wp 89 | do i=1,n 90 | do j=1,n 91 | r2 = x(i)**2 + y(j)**2 92 | z(i,j) = sin(x(i))*cos(y(j))*sin(r2)/(1.0_wp+log(r2+1.0_wp)) 93 | end do 94 | end do 95 | call plt%initialize(grid=.true.,xlabel='x angle (rad)',& 96 | ylabel='y angle (rad)',figsize=[10,10],& 97 | title='Contour plot test', real_fmt='*',& 98 | axisbelow=.false.) 99 | call plt%add_contour(x, y, z, linestyle='-', & 100 | filled=.true., cmap='bone', colorbar=.true.,& 101 | istat=istat) 102 | call plt%savefig(testdir//'contour.png',pyfile=testdir//'contour.py',istat=istat) 103 | 104 | !image plot: 105 | call plt%initialize(grid=.true.,xlabel='x',ylabel='y',figsize=[20,20],& 106 | title='imshow test',& 107 | real_fmt='(F9.3)') 108 | call plt%add_imshow(mat,xlim=[0.0_wp, 100.0_wp],ylim=[0.0_wp, 100.0_wp],istat=istat) 109 | call plt%savefig(testdir//'imshow.png', pyfile=testdir//'imshow.py',& 110 | istat=istat) 111 | 112 | !wireframe plot: 113 | call plt%initialize(grid=.true.,xlabel='x angle (rad)',& 114 | ylabel='y angle (rad)',figsize=[10,10],& 115 | title='Wireframe plot test', real_fmt='*',& 116 | axisbelow=.false.,mplot3d=.true.,use_numpy=.true.) 117 | call plt%plot_wireframe(x, y, z, label='', linestyle='-', & 118 | cmap='bone', colorbar=.true.,& 119 | istat=istat) 120 | call plt%savefig(testdir//'wireframe.png', pyfile=testdir//'wireframe.py',& 121 | istat=istat) 122 | 123 | !histogram chart: 124 | x = [0.194,0.501,-1.241,1.425,-2.217,-0.342,-0.979,0.909,0.994,0.101, & 125 | -0.131,-0.627,0.463,1.404,0.036,-2.000,0.109,1.250,-1.035,-1.115, & 126 | 0.935,0.496,1.100,0.770,-1.307,-0.693,-0.072,-1.331,-0.701, & 127 | -0.494,0.666,-0.313,-0.430,-0.654,1.638,-0.334,-0.418,0.550,-0.034, & 128 | 0.473,0.704,0.801,-0.157,0.055,-0.057,-1.049,-1.022,0.495,0.756, & 129 | 0.149,0.543,-0.813,-0.171,-0.994,-1.532,0.502,1.324,-0.593,-0.467, & 130 | 0.372,-0.904,1.255,0.931,-0.779,1.529,-0.036,0.783,0.292,-0.792, & 131 | -0.223,-0.325,0.225,-0.492,-0.941,0.065,1.300,-1.241,-1.124,-0.499, & 132 | 1.233,-0.845,-0.948,-1.060,1.103,-1.154,-0.594,0.335,-1.423,0.571, & 133 | -0.903,1.129,-0.372,-1.043,-1.327,0.147,1.056,1.068,-0.699,0.988,-0.630] 134 | 135 | call plt%initialize(grid=.true.,xlabel='x',& 136 | title='hist test',& 137 | legend=.true.,figsize=[20,10],& 138 | font_size = 20,& 139 | axes_labelsize = 20,& 140 | xtick_labelsize = 20,& 141 | ytick_labelsize = 20,& 142 | legend_fontsize = 20 ) 143 | 144 | call plt%add_hist(x=x, label='x',istat=istat) 145 | call plt%savefig(testdir//'histtest1.png', pyfile=testdir//'histtest1.py',& 146 | istat=istat) 147 | 148 | call plt%initialize(grid=.true.,xlabel='x',& 149 | title='cumulative hist test',& 150 | legend=.true.,figsize=[20,10],& 151 | font_size = 20,& 152 | axes_labelsize = 20,& 153 | xtick_labelsize = 20,& 154 | ytick_labelsize = 20,& 155 | legend_fontsize = 20 ) 156 | 157 | call plt%add_hist(x=x, label='x', bins=8, cumulative=.true.,istat=istat) 158 | call plt%savefig(testdir//'histtest2.png', & 159 | pyfile=testdir//'histtest2.py', & 160 | dpi='200', & 161 | transparent=.true.,istat=istat) 162 | 163 | !3D plot: 164 | call plt%initialize(grid=.true.,& 165 | xlabel='x (km)',ylabel='y (km)',zlabel='z (km)',& 166 | title='Orbit',& 167 | axis_equal=.true.,& 168 | mplot3d=.true.,& 169 | figsize=[20,10] ) 170 | 171 | x = [(real(i,wp), i=0,size(x)-1)]/5.0_wp 172 | sx = 7000.0_wp * cos(x * deg2rad) 173 | cx = 7000.0_wp * sin(x * deg2rad) 174 | zx = 0.0_wp 175 | call plt%add_3d_plot(sx,cx,zx,& 176 | label='orbit',linestyle='r-',& 177 | linewidth=2,istat=istat) 178 | call plt%add_sphere(6378.0_wp,0.0_wp,0.0_wp,0.0_wp,& 179 | color='Blue',n_facets=500,& 180 | antialiased=.true.,istat=istat) 181 | call plt%savefig(testdir//'orbit.png', & 182 | pyfile=testdir//'orbit.py', & 183 | dpi='200', & 184 | transparent=.true.,istat=istat) 185 | 186 | ! Errorbar plot: 187 | call plt%initialize(grid=.true.,& 188 | xlabel='x',ylabel='y',& 189 | title='Errorbar Plot Example',& 190 | figsize=[20,10] ) 191 | 192 | x = [(real(i,wp), i=0, 360, 10)] 193 | y = 10.0_wp * cos(x * deg2rad) 194 | xerr = abs(sin(x * deg2rad) * 5.0_wp) 195 | yerr = abs(sin(y * deg2rad) * 10.0_wp) 196 | 197 | call plt%add_errorbar(x, y, label='y', linestyle='.', & 198 | xerr=xerr, yerr=yerr, istat=istat) 199 | call plt%savefig(testdir//'errorbar.png', & 200 | pyfile=testdir//'errorbar.py', & 201 | dpi='200', & 202 | transparent=.true.,istat=istat, python='python') 203 | 204 | ! also test one with spaces and () in the filename 205 | call plt%savefig(testdir//'error bar (1).png', & 206 | pyfile=testdir//'error bar (1).py', & 207 | dpi='200', & 208 | transparent=.true.,istat=istat, python='python') 209 | 210 | end program test 211 | !***************************************************************************************** 212 | --------------------------------------------------------------------------------