Using tikz to plot for fortran

Hi all:

I realized yesterday that you can use tikz to plot for the data that generated by Fortran. That is mind-blowing for me. So I wrote a simple module that can call from Fortran and generate a standalone tex file for you:

To call the tikz subroutine, you can use the following format:

    call tikz(x, y, title, xlabel, ylabel, legend, name)

and now the legend is separated by a semicolon ; and is plotted directly on the line (rather than having a box somewhere)

Hope someone finds this helpful! And if you find any bugs, feel free to let me know :grinning:

8 Likes

Nice!. Maybe it’s interesting to call ForColormap, https://github.com/vmagnin/forcolormap, to define the color palette.

1 Like

Is it possible to generate hex number with forcolormap? I am using xcolor to define the color using the following code:

write(unitno, *) "\definecolor{" // trim(colorname(i)) // "}{HTML}{" // trim(colorvec(i)) // "}"

I tried out forcolormap and it seems like it is using RGB?

Yes, Forcolormap is just returning three integers red, green, blue.
But you can easily make the conversion to an hexadecimal string. See this code snippet:

program main
  implicit none
  integer :: red, green, blue
  character(6) :: rgb

  red = 100
  green = 0
  blue = 255

  write(rgb, '(3z2.2)') red, green, blue
  print *, rgb
end program main
$ fpm run
Project is up to date
 6400FF

That is a functionality that could easily be added later in Forcolormap. I keep note.

Maybe you might want to give the ForColor module of ForImage a try. Here you can find an example of how to convert a color to different color spaces like RGB, CMYK, HSV, HSL, XYZ, decimal and hex: forimage/example/demo_color.f90 at main · gha3mi/forimage · GitHub

1 Like

And note that ForImage is a dependency of ForColormap.

2 Likes

Is unitno a character string? If so, then this looks like a complicated way to do the simple assignnment:

unitno = "\definecolor{" // trim(colorname(i)) // "}{HTML}{" // trim(colorvec(i))

The programmer only needs the internal write if there are conversions from from other data types (integer, real, etc.) to type character. In this case, it is just concatenating some strings together to form a single string, and that is built in to the language, the internal write is unnecessary.

No, the unitno is for the external file to write that line to. So I need the write.

2 Likes

Thanks! I will try this out. What functionality do you think I should add to this child project? I am thinking about contour plots and how to implement it in tikz.

You can find inspiration in the list of plots supported by Gnuplot: Demos for gnuplot version 6.0

There are so many things you could add. So my advice would be: start with your own needs. Your motivation will be high. And people will later ask you if could add other features, or even make a Pull Request with their own work.

Maybe you should consider using the pgfplots package, which is much more user-friendly than bare-bones tikz. Besides, it includes contour plotting, 3-dimensional diagrams and more

2 Likes

Since many people are interested in your module, please consider making your gist a repository, so that people can raise issues and so I can it add it to the Graphics, Plotting, and User Interfaces section of my list of Fortran codes.

3 Likes

If you follow @Beliavsky suggestion, consider making your file .F90 instead and then change this commented module

with a C preprocessed

#if __INTEL_COMPILER
use ifport 
#endif

Does your module have a strong dependency on OpenMP? otherwise you can also use conditional compilation sentinels decorating OpenMP with !$ to enable compiling without as well

!$ omp_lib
...
thread_id = 0
!$ thread_id = omp_get_thread_num()
write(tmpChar5,'(i0)') thread_id 
unitno = 726+thread_id 

In the template I am using I did use the pgfplots:

The following chuck of fortran code generate the templates:

    open(unitno, file=trim(fpath) // trim(tikz),status='unknown')
       write(unitno, *) "\documentclass[tikz]{standalone}"

       write(unitno, *) "\usepackage{tikz}"
       write(unitno, *) "\usepackage{pgfplots}"
       write(unitno, *) "\usetikzlibrary{decorations}"
       write(unitno, *) "\usetikzlibrary{decorations.pathreplacing, intersections, fillbetween}"
       write(unitno, *) "\usetikzlibrary{calc,positioning}"
       write(unitno, *) "\pgfplotsset{compat=newest, scale only axis, width = 13cm, height = 6cm}"
       write(unitno, *) "\pgfplotsset{sciclean/.style={axis lines=left,"
       write(unitno, *) "        axis x line shift=0.5em,"
       write(unitno, *) "        axis y line shift=0.5em,"
       write(unitno, *) "        axis line style={-,very thin},"
       write(unitno, *) "        axis background/.style={draw,ultra thin,gray},"
       write(unitno, *) "        tick align=outside,"
       write(unitno, *) "        xtick distance=" // num2str(xticsdist) // ","
       write(unitno, *) "        ytick distance=" // num2str(yticsdist) // ","
       write(unitno, *) "        major tick length=2pt}}"
       write(unitno, *) ""
       write(unitno, *) "% Create fake \onslide and other commands for standalone picture"
       write(unitno, *) "\usepackage{xparse}"
       write(unitno, *) "\NewDocumentCommand{\onslide}{s t+ d<>}{}"
       write(unitno, *) "\NewDocumentCommand{\only}{d<>}{}"
       write(unitno, *) "\NewDocumentCommand{\uncover}{d<>}{}"
       write(unitno, *) "\NewDocumentCommand{\visible}{d<>}{}"
       write(unitno, *) "\NewDocumentCommand{\invisible}{d<>}{}"
       write(unitno, *) ""
       write(unitno, *) "\makeatletter"
       write(unitno, *) "\tikzset{"
       write(unitno, *) "Sloped/.code = {"
       write(unitno, *) "\iftikz@fullytransformed% tikz.code.tex"
       write(unitno, *) "    \tikzset{sloped}"
       write(unitno, *) "\else"
       write(unitno, *) "    \pgfgettransformentries{\mya}{\myb}{\myc}{\myd}{\mys}{\myt}%"
       write(unitno, *) "    \tikzset{sloped, transform shape, rotate = {atan2(\myb,\mya)}}%"
       write(unitno, *) "\fi"
       write(unitno, *) "}"
       write(unitno, *) "}"
       write(unitno, *) "\makeatother"
       write(unitno, *) ""
       write(unitno, *) "% ---------------------------------------------------------------------"
       write(unitno, *) "% Coordinate extraction"
       write(unitno, *) "% #1: node name"
       write(unitno, *) "% #2: output macro name: x coordinate"
       write(unitno, *) "% #3: output macro name: y coordinate"
       write(unitno, *) "\newcommand{\Getxycoords}[3]{%"
       write(unitno, *) "    \pgfplotsextra{%"
       write(unitno, *) "        % using `\pgfplotspointgetcoordinates' stores the (axis)"
       write(unitno, *) "        % coordinates in `data point' which then can be called by"
       write(unitno, *) "        % `\pgfkeysvalueof' or `\pgfkeysgetvalue'"
       write(unitno, *) "        \pgfplotspointgetcoordinates{(#1)}%"
       write(unitno, *) "        % `\global' (a TeX macro and not a TikZ/PGFPlots one) allows to"
       write(unitno, *) "        % store the values globally"
       write(unitno, *) "         \global\pgfkeysgetvalue{/data point/x}{#2}%"
       write(unitno, *) "         \global\pgfkeysgetvalue{/data point/y}{#3}%"
       write(unitno, *) "     }%"
       write(unitno, *) "}"
       write(unitno, *) "% ---------------------------------------------------------------------"
       write(unitno, *) ""
       write(unitno, *) "\begin{document}"
       write(unitno, *) ""
       write(unitno, *) "\begin{tikzpicture}"
       write(unitno, *) ""
       write(unitno, *) ""
       write(unitno, *) "\begin{axis}["
       write(unitno, *) "    sciclean,"
       write(unitno, *) "    xlabel = {" // xlb // "},"
       write(unitno, *) "    ylabel = {" // ylb // "},"
       write(unitno, *) "    xmin = " // num2str(xmin) // ","
       write(unitno, *) "    xmax = " // num2str(xmax) // ","
       write(unitno, *) "    ymin = " // num2str(ymin) // ","
       write(unitno, *) "    ymax = " // num2str(ymax) // ","
       write(unitno, *) "    legend cell align = left,"
       write(unitno, *) "    legend pos = south east,"
       write(unitno, *) "    title = {" // title // "}]"
       write(unitno, *) ""
       write(unitno, *) ""
       call plotting(unitno, nj, palette, cnames, fpath, dat, legend)
       write(unitno, *) ""
       write(unitno, *) ""
       write(unitno, *) "\end{axis}"
       write(unitno, *) ""
       write(unitno, *) ""
       write(unitno, *) "\end{tikzpicture}"
       write(unitno, *) ""
       write(unitno, *) "\end{document}"
    close(unitno)
3 Likes

I guess this might be a newbie question: what is C preprocessed?

I try to add the #if chuck into the code and somehow it is not compiled. The error is

<ERROR> *cmd_run* Targets error: Unable to find source for module dependency: "ifport" used by "././src/tikz_module

I am using gfortran to compile it, and I guess it tries to load ifport and failed.

I have used ifport because this code is adapted from Grey Gorden’s mod_plot.f90 for gnuplot:

And I am actually not sure why he needs ifport for ifort compiler.

I think your suggestion for removing needs to compile with openmp is great :grinning:

1 Like

Did you change the file suffix to capital F (.F90 instead of .f90)? Otherwise you have to add the flag -cpp with gfortran or -fpp with ifort to enable preprocessing the file

1 Like

Basically, the defacto mechanism in Fortran to enable conditional compilation. Which is borrowed from C.

1 Like

Hello @Beliavsky , this is my github page for tikz-fortran:

Thank you!

3 Likes