Skip to content

Commit badee42

Browse files
committed
added additional control plot options.
fixed a bug in matrix to string conversion. added option to specify numeric format for intermediate python script.
1 parent dae6629 commit badee42

File tree

2 files changed

+83
-38
lines changed

2 files changed

+83
-38
lines changed

src/pyplot_module.f90

Lines changed: 58 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ module pyplot_module
2222
character(len=*), parameter :: tmp_file = 'pyplot_module_temp_1234567890.py' !! Default name of the temporary file
2323
!! (this can also be user-specified).
2424

25-
character(len=*), parameter :: python_exe ='python' !! The python executable name.
26-
character(len=*), parameter :: int_fmt = '(I10)' !! integer format string
27-
integer, parameter :: max_int_len = 10 !! max string length for integers
28-
character(len=*), parameter :: real_fmt = '(E30.16)' !! real number format string
29-
integer, parameter :: max_real_len = 30 !! max string length for reals
25+
character(len=*), parameter :: python_exe ='python' !! The python executable name.
26+
character(len=*), parameter :: int_fmt = '(I10)' !! integer format string
27+
integer, parameter :: max_int_len = 10 !! max string length for integers
28+
character(len=*), parameter :: real_fmt_default = '(E30.16)' !! default real number format string
29+
integer, parameter :: max_real_len = 30 !! max string length for reals
3030

3131
type, public :: pyplot
3232

@@ -42,6 +42,8 @@ module pyplot_module
4242
logical :: polar = .false. !! it is a polar plot
4343
logical :: axis_equal = .false. !! equal scale on each axis
4444

45+
character(len=:),allocatable :: real_fmt !! real number formatting
46+
4547
contains
4648

4749
! public methods
@@ -73,7 +75,8 @@ subroutine destroy(me)
7375

7476
class(pyplot),intent(inout) :: me !! pyplot handler
7577

76-
if (allocated(me%str)) deallocate(me%str)
78+
if (allocated(me%str)) deallocate(me%str)
79+
if (allocated(me%real_fmt)) deallocate(me%real_fmt)
7780

7881
end subroutine destroy
7982
!*****************************************************************************************
@@ -100,7 +103,7 @@ end subroutine add_str
100103

101104
subroutine initialize(me, grid, xlabel, ylabel, zlabel, title, legend, use_numpy, figsize, &
102105
font_size, axes_labelsize, xtick_labelsize, ytick_labelsize, ztick_labelsize, &
103-
legend_fontsize, mplot3d, axis_equal, polar)
106+
legend_fontsize, mplot3d, axis_equal, polar, real_fmt)
104107

105108
class(pyplot), intent(inout) :: me !! pyplot handler
106109
logical, intent(in), optional :: grid !! activate grid drawing
@@ -120,6 +123,7 @@ subroutine initialize(me, grid, xlabel, ylabel, zlabel, title, legend, use_numpy
120123
logical, intent(in), optional :: mplot3d !! set true for 3d plots (cannot use with polar)
121124
logical, intent(in), optional :: axis_equal !! set true for axis = 'equal'
122125
logical, intent(in), optional :: polar !! set true for polar plots (cannot use with mplot3d)
126+
character(len=*), intent(in), optional :: real_fmt !! format string for real numbers (examples: '(E30.16)' [default], '*')
123127

124128
character(len=max_int_len) :: width_str !! figure width dummy string
125129
character(len=max_int_len) :: height_str !! figure height dummy string
@@ -129,6 +133,7 @@ subroutine initialize(me, grid, xlabel, ylabel, zlabel, title, legend, use_numpy
129133
character(len=max_int_len) :: ytick_labelsize_str !! size of x axis tick labels dummy string
130134
character(len=max_int_len) :: ztick_labelsize_str !! size of z axis tick labels dummy string
131135
character(len=max_int_len) :: legend_fontsize_str !! size of legend font dummy string
136+
132137
character(len=*), parameter :: default_font_size_str = '10' !! the default font size for plots
133138

134139
call me%destroy()
@@ -162,6 +167,11 @@ subroutine initialize(me, grid, xlabel, ylabel, zlabel, title, legend, use_numpy
162167
else
163168
me%axis_equal = .false.
164169
end if
170+
if (present(real_fmt)) then
171+
me%real_fmt = trim(adjustl(real_fmt))
172+
else
173+
me%real_fmt = real_fmt_default
174+
end if
165175

166176
call optional_int_to_string(font_size, font_size_str, default_font_size_str)
167177
call optional_int_to_string(axes_labelsize, axes_labelsize_str, default_font_size_str)
@@ -249,12 +259,12 @@ subroutine add_plot(me, x, y, label, linestyle, markersize, linewidth, xlim, yli
249259
if (allocated(me%str)) then
250260

251261
!axis limits (optional):
252-
if (present(xlim)) call vec_to_string(xlim, xlimstr, me%use_numpy)
253-
if (present(ylim)) call vec_to_string(ylim, ylimstr, me%use_numpy)
262+
if (present(xlim)) call vec_to_string(xlim, me%real_fmt, xlimstr, me%use_numpy)
263+
if (present(ylim)) call vec_to_string(ylim, me%real_fmt, ylimstr, me%use_numpy)
254264

255265
!convert the arrays to strings:
256-
call vec_to_string(x, xstr, me%use_numpy)
257-
call vec_to_string(y, ystr, me%use_numpy)
266+
call vec_to_string(x, me%real_fmt, xstr, me%use_numpy)
267+
call vec_to_string(y, me%real_fmt, ystr, me%use_numpy)
258268

259269
!get optional inputs (if not present, set default value):
260270
call optional_int_to_string(markersize, imark, '3')
@@ -298,7 +308,7 @@ end subroutine add_plot
298308
!
299309
!@note This requires `use_numpy` to be True.
300310

301-
subroutine add_contour(me, x, y, z, label, linestyle, linewidth, levels, color)
311+
subroutine add_contour(me, x, y, z, label, linestyle, linewidth, levels, color, filled, cmap)
302312

303313
class(pyplot), intent (inout) :: me !! pyplot handler
304314
real(wp),dimension(:), intent (in) :: x !! x values
@@ -309,6 +319,8 @@ subroutine add_contour(me, x, y, z, label, linestyle, linewidth, levels, color)
309319
integer, intent (in), optional :: linewidth !! width of the plot line
310320
real(wp),dimension(:), intent (in), optional :: levels !! contour levels to plot
311321
character(len=*), intent (in), optional :: color !! color of the contour line
322+
logical, intent (in), optional :: filled !! use filled control (default=False)
323+
character(len=*), intent (in), optional :: cmap !! colormap if filled=True (examples: 'jet', 'bone')
312324

313325
character(len=:), allocatable :: xstr !! x values strinfied
314326
character(len=:), allocatable :: ystr !! y values strinfied
@@ -322,14 +334,15 @@ subroutine add_contour(me, x, y, z, label, linestyle, linewidth, levels, color)
322334
character(len=*), parameter :: yname_ = 'Y' !! Y variable name for contour
323335
character(len=*), parameter :: zname_ = 'Z' !! Z variable name for contour
324336
character(len=:), allocatable :: extras !! optional stuff
337+
character(len=:), allocatable :: contourfunc !! 'contour' or 'contourf'
325338

326339
if (allocated(me%str)) then
327340

328341
!convert the arrays to strings:
329-
call vec_to_string(x, xstr, me%use_numpy)
330-
call vec_to_string(y, ystr, me%use_numpy)
331-
call matrix_to_string(z, zstr, me%use_numpy)
332-
if (present(levels)) call vec_to_string(levels, levelstr, me%use_numpy)
342+
call vec_to_string(x, me%real_fmt, xstr, me%use_numpy)
343+
call vec_to_string(y, me%real_fmt, ystr, me%use_numpy)
344+
call matrix_to_string(z, me%real_fmt, zstr, me%use_numpy)
345+
if (present(levels)) call vec_to_string(levels, me%real_fmt, levelstr, me%use_numpy)
333346

334347
!get optional inputs (if not present, set default value):
335348
call optional_int_to_string(linewidth, iline, '3')
@@ -349,9 +362,16 @@ subroutine add_contour(me, x, y, z, label, linestyle, linewidth, levels, color)
349362
if (present(levels)) extras = extras//','//'levels='//levelstr
350363
if (present(color)) extras = extras//','//'colors="'//color//'"'
351364
if (present(linewidth)) extras = extras//','//'linewidths='//trim(adjustl(iline))
365+
if (present(cmap)) extras = extras//','//'cmap="'//cmap//'"'
366+
367+
!filled or regular:
368+
contourfunc = 'contour' !default
369+
if (present(filled)) then
370+
if (filled) contourfunc = 'contourf' !filled contour
371+
end if
352372

353373
!write the plot statement:
354-
call me%add_str('CS = ax.contour('//xname_//','//yname_//','//zname_//','//&
374+
call me%add_str('CS = ax.'//contourfunc//'('//xname_//','//yname_//','//zname_//','//&
355375
'label="'//trim(label)//'",'//&
356376
'linestyles="'//trim(adjustl(linestyle))//'"'//&
357377
extras//')')
@@ -396,9 +416,9 @@ subroutine add_3d_plot(me, x, y, z, label, linestyle, markersize, linewidth)
396416
if (allocated(me%str)) then
397417

398418
!convert the arrays to strings:
399-
call vec_to_string(x, xstr, me%use_numpy)
400-
call vec_to_string(y, ystr, me%use_numpy)
401-
call vec_to_string(z, zstr, me%use_numpy)
419+
call vec_to_string(x, me%real_fmt, xstr, me%use_numpy)
420+
call vec_to_string(y, me%real_fmt, ystr, me%use_numpy)
421+
call vec_to_string(z, me%real_fmt, zstr, me%use_numpy)
402422

403423
!get optional inputs (if not present, set default value):
404424
call optional_int_to_string(markersize, imark, '3')
@@ -466,15 +486,15 @@ subroutine add_bar(me, left, height, label, width, bottom, color, yerr, align, x
466486
if (allocated(me%str)) then
467487

468488
!axis limits (optional):
469-
if (present(xlim)) call vec_to_string(xlim, xlimstr, me%use_numpy)
470-
if (present(ylim)) call vec_to_string(ylim, ylimstr, me%use_numpy)
489+
if (present(xlim)) call vec_to_string(xlim, me%real_fmt, xlimstr, me%use_numpy)
490+
if (present(ylim)) call vec_to_string(ylim, me%real_fmt, ylimstr, me%use_numpy)
471491

472492
!convert the arrays to strings:
473-
call vec_to_string(left, xstr, me%use_numpy)
474-
call vec_to_string(height, ystr, me%use_numpy)
475-
if (present(width)) call vec_to_string(width, wstr, me%use_numpy)
476-
if (present(bottom)) call vec_to_string(bottom, bstr, me%use_numpy)
477-
if (present(yerr)) call vec_to_string(yerr, yerr_str, me%use_numpy)
493+
call vec_to_string(left, me%real_fmt, xstr, me%use_numpy)
494+
call vec_to_string(height, me%real_fmt, ystr, me%use_numpy)
495+
if (present(width)) call vec_to_string(width, me%real_fmt, wstr, me%use_numpy)
496+
if (present(bottom)) call vec_to_string(bottom, me%real_fmt, bstr, me%use_numpy)
497+
if (present(yerr)) call vec_to_string(yerr, me%real_fmt, yerr_str, me%use_numpy)
478498

479499
!write the arrays:
480500
call me%add_str(trim(xname)//' = '//xstr)
@@ -562,9 +582,10 @@ end subroutine integer_to_string
562582
!
563583
! Real vector to string.
564584

565-
subroutine vec_to_string(v, str, use_numpy)
585+
subroutine vec_to_string(v, fmt, str, use_numpy)
566586

567587
real(wp), dimension(:), intent(in) :: v !! real values
588+
character(len=*), intent(in) :: fmt !! real format string
568589
character(len=:), allocatable, intent(out) :: str !! real values stringified
569590
logical, intent(in) :: use_numpy !! activate numpy python module usage
570591

@@ -574,7 +595,11 @@ subroutine vec_to_string(v, str, use_numpy)
574595

575596
str = '['
576597
do i=1, size(v)
577-
write(tmp, real_fmt, iostat=istat) v(i)
598+
if (fmt=='*') then
599+
write(tmp, *, iostat=istat) v(i)
600+
else
601+
write(tmp, fmt, iostat=istat) v(i)
602+
end if
578603
if (istat/=0) error stop 'Error in vec_to_string'
579604
str = str//trim(adjustl(tmp))
580605
if (i<size(v)) str = str // ','
@@ -592,9 +617,10 @@ end subroutine vec_to_string
592617
!
593618
! Real matrix (rank 2) to string.
594619

595-
subroutine matrix_to_string(v, str, use_numpy)
620+
subroutine matrix_to_string(v, fmt, str, use_numpy)
596621

597622
real(wp), dimension(:,:), intent(in) :: v !! real values
623+
character(len=*), intent(in) :: fmt !! real format string
598624
character(len=:), allocatable, intent(out) :: str !! real values stringified
599625
logical, intent(in) :: use_numpy !! activate numpy python module usage
600626

@@ -603,9 +629,9 @@ subroutine matrix_to_string(v, str, use_numpy)
603629

604630
str = '['
605631
do i=1, size(v,1) !rows
606-
call vec_to_string(v(i,:), tmp, use_numpy) !one row at a time
632+
call vec_to_string(v(i,:), fmt, tmp, use_numpy) !one row at a time
607633
str = str//trim(adjustl(tmp))
608-
if (i<size(v)) str = str // ','
634+
if (i<size(v,1)) str = str // ','
609635
end do
610636
str = str // ']'
611637

src/tests/test.f90

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,21 @@ program test
1212

1313
implicit none
1414

15-
real(wp), dimension(100) :: x !! x values
16-
real(wp), dimension(100) :: yerr !! error values for bar chart
17-
real(wp), dimension(100) :: sx !! sin(x) values
18-
real(wp), dimension(100) :: cx !! cos(x) values
19-
real(wp), dimension(100) :: tx !! sin(x)*cos(x) values
15+
integer,parameter :: n = 100
16+
17+
real(wp), dimension(n) :: x !! x values
18+
real(wp), dimension(n) :: y !! y values
19+
real(wp), dimension(n) :: yerr !! error values for bar chart
20+
real(wp), dimension(n) :: sx !! sin(x) values
21+
real(wp), dimension(n) :: cx !! cos(x) values
22+
real(wp), dimension(n) :: tx !! sin(x)*cos(x) values
23+
real(wp), dimension(n,n) :: z !! z matrix for contour plot
2024
type(pyplot) :: plt !! pytplot handler
2125
integer :: i !! counter
26+
integer :: j !! counter
27+
real(wp) :: r2 !! temp variable
2228

2329
!generate some data:
24-
x = [(real(i,wp), i=0,size(x)-1)]/5.0_wp
2530
sx = sin(x)
2631
cx = cos(x)
2732
tx = sx * cx
@@ -48,5 +53,19 @@ program test
4853
color='r',yerr=yerr,xlim=[0.0_wp, 20.0_wp],align='center')
4954
call plt%savefig('bartest.png', pyfile='bartest.py')
5055

56+
!contour plot:
57+
x = [(real(i,wp), i=0,n-1)]/100.0_wp
58+
y = [(real(i,wp), i=0,n-1)]/100.0_wp
59+
do i=1,n
60+
do j=1,n
61+
r2 = x(i)**2 + y(j)**2
62+
z(i,j) = sin(x(i))*cos(y(j))*sin(r2)/(1.0_wp+log(r2+1.0_wp))
63+
end do
64+
end do
65+
call plt%initialize(grid=.true.,xlabel='x angle (rad)',ylabel='y angle (rad)',figsize=[10,10],&
66+
title='Contour plot test', real_fmt='*')
67+
call plt%add_contour(x, y, z, label='contour', linestyle='-', linewidth=2, filled=.true., cmap='bone')
68+
call plt%savefig('contour.png',pyfile='contour.py')
69+
5170
end program test
5271
!*****************************************************************************************

0 commit comments

Comments
 (0)