PGFplots explicit colors for plot using wave

I'm trying to plot the ramification/Riemann surface of the square root in the complex plane (e.g. Wikipedia). To encode the imaginary part of the square root, I try to provide specific values for the color using wave format for color input.

I use polar coordinates (x cos(y), x sin(y)) and the real part of the square root should be sqrt(x) cos(y/2), while the imaginary part for the color is sqrt(x) sin(y/2).

Consider the following code:

\documentclass{article}
\usepackage{pgfplots}

\begin{document}
\begin{tikzpicture}
\begin{axis}
surf,
domain=0:4,
domain y=-pi:3*pi,
mesh/color input=explicit mathparse,
point meta={symbolic={wave=363+(2+sqrt(x)*sin(deg(y/2)))/4*(814-363)}}
]
(x,y,0); % This works just fine
% ({x*cos(deg(y))}, {x*sin(deg(y))}, {sqrt(x)*cos(deg(y/2))}); % This breaks!
\end{axis}
\end{tikzpicture}

\end{document}


Plotting a horizontal plane yields no problems. But using the actual plot, I obtain the following error message:

Package pgfplots Error: Sorry, the color component value nan (no. 0) is out of range. The allowed range is 0 <= value <= 1. The error occured near point meta 'wave=363+(2+sqrt(x)*sin(deg(y/2)))/4*(814-363)' of coord no 6 (2Y9.99975739e-1],2Y3.141592654e0],0Y0.0e0])'.


This also happens at other coordinates.

Edit: I boiled it down to the first argument, the cos(deg(y)) seems to be the problem.

Edit: How it should look like:

This image is taken from Wikipedia. Note the color distribution. For example, yellow, green and cyan appear two times, but blue and red only once. More precisely, the two yellow parts are not precisely on top of another, but reflected along the real line.

Edit: Just writing what I wrote as a comment: Let alone this code, which is very similar to the example in the official documentation, p. 151, fails with some modifications.

\documentclass{article}
\usepackage{pgfplots}

\begin{document}
\begin{tikzpicture}
\begin{axis}
surf,
domain=0:1,
domain y=0:1,
mesh/color input=explicit mathparse,
point meta={symbolic={wave=363+x*(814-363)}}
]
(x,y,0); % This works just fine
% (y,x,0); % This breaks!
\end{axis}
\end{tikzpicture}

\end{document}

• Use cos(y). It works. Jul 13, 2023 at 8:09
• @RaffaeleSantoro This is not a solution. I have to use deg(y) since TikZ/the PGF engine expects degrees, not radians. Converting everything to degrees (i.e. domain y=-180:3*180) yields the same problem. Jul 13, 2023 at 8:39
• There is something going on with the "point meta" function that makes it hir a NaN at some point. If I plot it, I obtain i.stack.imgur.com/dbEWc.png, which is quite strange... Jul 13, 2023 at 9:30
• ...and it is triggered by some value of z. Very strange. I think that the wave= thing is not working here, try point meta={symbolic={wave=363+x/4*(814-363)}} Jul 13, 2023 at 9:34
• @Rmano But this is also not a solution, since the color is quite important if it should encode the imaginary part of sqrt(z) for z a complex number. See my edit. Jul 13, 2023 at 9:48

I'm not familiar with defining colormaps but your graphing issues can be solved by using an accurate engine to calculate the points. There are always choices (Asymptote, lua, etc). I'm recommending SAGE for several reasons. First, it's a CAS (computer algebra system) so it's the highest class of mathematical software for giving accurate calculations. Second, as a CAS you have access to built in capabilities (derivatives,matrices,graph theory,differential equations, etc) that other programs don't. Third, you get access to Python which allows you circumvent programming in LaTeX. Fourth, a SAGE cell server (or Sagemath app) can help you tinker easily from a tablet. Here is a solution using sagetex and the jet colormap:

\documentclass[border=5pt]{standalone}
\usepackage{pgfplots}
\usepackage{sagetex}
\pgfplotsset{compat=1.15}
\begin{document}
\begin{sagesilent}
var('x,y')
xcoords = [i for i in srange(0,4,.2)]
ycoords = [i for i in srange(-3.14,9.52,.2)]

output = ""
output += r"\begin{tikzpicture}[scale=1.0]"
output += r"\begin{axis}[view={-10}{-40},xmin=%d, xmax=%d, ymin=%d, ymax=%d]"%(-4,4,-4,4)
# the length of ycoords is the number of y values
for y in ycoords:
for x in xcoords:
output += r"(%f, %f, %f) "%(x*cos(y),x*sin(y),sqrt(x)*cos(y/2))

output += r"};"
output += r"\end{axis}"
output += r"\end{tikzpicture}"
\end{sagesilent}
\sagestr{output}
\end{document}


The result in Cocalc is:

I've set opacity=0.5, you might want to experiment with that value.

The line xcoords = [i for i in srange(0,4,.2)] creates a list in Python of my xvalues as they go from 0, .2, .4, to 3.8 and the yvalues are created by SAGE, which gives the accuracy we expect. The fact that sagetex requires 3 steps in compiling is behind creating my code as a string with the help of Python. The for-loop puts the coordinates into the string which is eventually put into the document using \sagestr{output}.

SAGE is not part of a LaTeX distribution. The easiest way to experiment with it is by getting a free Cocalc account. It's also possible to download SAGE to your computer and get it to work with your LaTeX distribution but that is a much longer and more difficult process. Search this site for more examples of how the sagetex package can be used.

EDIT: I think the problem with your approach is that x isn't what we want it to be. We think x is nonnegative because you defined x to be between 0 and 4 but the value of x, according to the plotting program is x*cos(y). I noticed that if I use sqrt(abs(x)) the program runs fine:

If I remove the absolute value then it won't compile at all:

Note the error message telling me the value needs to be between 0 and 1. When I check my data file, it shows lots of x-values that are negative:

It appears to me that wave=363+(2+sqrt(x)*sin(deg(y/2)))/4*(814-363) is calculating the square root of your x-value: {x*cos(deg(y))} rather than the x you defined to be between 0 and 4. Since {x*cos(deg(y))} is negative, square root is giving an imaginary number, which isn't in [0,1]. I don't see how to make the wave colormap work in this case.

• Thanks for your reply, but this is not my desired answer, since the colormap is wrong. As I explained, the color should encode the imaginary part of the function output, namely sqrt(x) * sin(y/2). If I would use any predefined colormap like jet, everything compiles without a problem in PGFPlots. Jul 23, 2023 at 10:46
• @Gargantuar I think I found the issue: x is not what we think it is. I've added an edit to my answer above with pics and the data file.
– DJP
Jul 23, 2023 at 23:12
• I accepted it as an answer since it explains why my problem cannot be solved in pgfplots. Aug 24, 2023 at 18:49

Not a satisfying answer, since this won't resolve the bug, but I got my hands dirty today with asymptote, and I'm quite happy (although asymptote does not always render 3d graphics as vector graphics very well).

\documentclass[convert]{standalone}

\usepackage{asymptote}

\begin{document}

\begin{asy}
settings.prc = false;
settings.render = 0;
unitsize(1cm);
fontsize(11pt);
size(5cm);

import graph3;
import palette;

currentprojection=orthographic(-0.8,-1.1,0.8);
int angles = 60;

triple f(pair z) { // using polar coordinates
return (z.x * cos(z.y), z.x * sin(z.y), sqrt(z.x) * cos(z.y/2));
}

triple c(pair z) { // using polar coordinates
return (z.x * cos(z.y), z.x * sin(z.y), sqrt(z.x) * sin(z.y/2));
}

surface s = surface(f, (0,-pi), (1,3pi), nu=radii, nv=angles, Spline);
pen[][] p = palette(surface(c, (0,-pi), (1,3pi), nu=radii, nv=angles).map(zpart), Wheel());
draw(s, mean(p), meshpen=black+linewidth(0.2pt));

xaxis3(Bounds(Min, Min), -1, 1, OutTicks(2,2));
yaxis3(Bounds(Min, Min), -1, 1, OutTicks(2,2));
zaxis3(Bounds(Min, Max), -1, 1, OutTicks(2,2));
\end{asy}

\end{document}
`