20

I'm trying to plot the function q(t) = (4.9/ (w^2))*(cosh(w*t)-cos(w*t)) for several values of the parameter w. That is, I want to plot whole graphs of q vs t, where I plug in say w=1, w=2, w=100, or whatever to see how the plot changes as I change the parameter.

Here's a minimal working example using TikZ:

\documentclass{minimal}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
\draw[thick,->] (-2,0) -- (2,0) node[right]{$t$};
\draw[thick,->] (0,0) -- (0,5) node[above]{$q$};
\draw[blue,domain=-1:1] plot (\x,{(2.45/(3^2))*(exp(3*\x)+exp(-3*    \x)-2*cos(3*\x))});
\draw[violet,domain=-1.3:1.3] plot (\x,{(2.45/(1^2))*(exp(1*\x)+exp(-1*\x)-2*cos(1*\x))});
\draw[red,domain=-1.4:1.4] plot (\x,{(2.45/(0.1^2))*(exp(0.1*\x)+exp(-0.1*\x)-2*cos(0.1*\x))});
\end{tikzpicture}
\end{document}

And here's one using PGFplots:

\documentclass{minimal}
\usepackage{pgfplots}
\begin{document}
\begin{tikzpicture} 
\begin{axis}[xmin=-5,xmax=5,ymin=-0.5,ymax=100,no markers, grid=both]
\addplot {(2.45/(0.1^2))*(exp(0.1*x)+exp(-0.1*x)-2*cos(0.1*x))};
\addplot {(2.45/(0.5^2))*(exp(0.5*x)+exp(-0.5*x)-2*cos(0.5*x))};
\addplot {(2.45/(1^2))*(exp(1*x)+exp(-1*x)-2*cos(1*x))};
\end{axis}
\end{tikzpicture}
\end{document}

enter image description here

What I'd like to be able to do with this is

  1. plot several values of the parameter w (probably about 20) without typing out a separate \draw or \addplot line for each.
  2. color each of the different plots with a slightly different color and add a legend which will let the reader know roughly what value of the parameter each plot is based on the color.
  3. (TikZ plot only) use the range instead of the domain of the function so that every plot looks about the same -- I tried just replacing domain=-1:1 with say range=0:5 but that didn't work.

I don't have a lot of experience with making plots in LaTeX, but I'd prefer an answer using either TikZ or pgfplots as I've at least used those a couple of times before.

11

I suspect this was the desired result? Large values of \w will cause your function to blow up, and will return an error message.

enter image description here

\documentclass{standalone}
\usepackage{pgfplots}

\def\mycolone{yellow}
\def\mycoltwo{green}

\pgfplotsset{every axis legend/.append style={
at={(.5,-.2)},
anchor=north}} 

\begin{document}
\begin{tikzpicture} 
\begin{axis}[xmin=-5,xmax=5,ymin=-0.5,ymax=100,no markers, grid=both]
\foreach \w in {5,10,...,100} {
\edef\tmp{\noexpand\addplot[\mycolone!\w!\mycoltwo]}
\pgfmathparse{\w/100}
\edef\x{\pgfmathresult}
\tmp{(4.9/((\w/100)^2))*(cosh(\w*x/100)-cos(\w*x/100))};
\edef\legendentry{\noexpand\addlegendentry{$\omega =
\noexpand\pgfmathprintnumber[fixed,fixed zerofill, precision=2]{\x}$}};
       \legendentry
}
\end{axis}
\end{tikzpicture}
\end{document}
  • @1010011010 To get around the error message for large values of \w, you just need to add restrict y to domain=0: some big number (1000 seems to work well). LaTeX doesn't seem to handle numbers lower than about 0.04 well though. – user76097 Apr 13 '15 at 14:56
12

First of all, I want to thank you for asking this question; I am a basic user of LaTeX and by thinking about your question, I learned so many things. Here is the code and my solution to your problem;

enter image description here

% pdflatex
\documentclass{standalone}
    \usepackage{siunitx}
    \sisetup{round-mode=places,round-precision=1}
    %
    \usepackage{tikz}
    \usepackage{pgfplots}
    %
    \def\colora{red}
    \def\colorb{green}
\begin{document}
\begin{tikzpicture}
       \pgfmathdeclarefunction{q}{2}{\pgfmathparse{(4.9/(#1^2))*(cosh(#1*#2)-cos(#1*#2)}}
\begin{axis}
        [
    axis lines = center,
    grid=both,minor tick num=1,
    xlabel=$t$,ylabel=$q$,
    tick align=inside,
    every axis y label/.style={rotate=0, black, at={(0.5,1.05)},},
    every axis x label/.style={rotate=0, black, at={(1.05,0)},},
    %
    legend style={at={(0.65,1.0)},
    anchor=north west,legend columns=1},
    %
    domain=-5:5,
    smooth,
        ]
    \foreach \w in {0.10,0.20,...,0.5} {
    \pgfmathparse{200*\w}
    \xdef\x{\pgfmathresult}
    %
    \edef\tempa{\noexpand\addlegendentry{\num{\w}}}\tempa
    \edef\tempb{\noexpand\addplot [\colora!\x!\colorb]}\tempb{q(\w,x)};
    }
\end{axis}
\end{tikzpicture}
\end{document}

I have defined the function at the line

   \pgfmathdeclarefunction{q}{2}{\pgfmathparse{(4.9/(#1^2))*(cosh(#1*#2)-cos(#1*#2)}}

however, one may not define his function as above and simply type it in front of the \addplot syntax in his code. There is no difference here. The point I want to mention is that you introduced part of your function as cos(w*t). Please make sure how the variable should be in your function. Should it be in degree or radian? By checking the values of the plot as output of your MWE in your question; I can see that you did not use the degree function. But, if you have to introduce the variables in degree, please change that part to cos(deg(#1*#2)). Your plots change slightly.

Using iteration syntaxes to decrease the amount of codes:

For the easiness of drawing many plots without hesitation to have long similar codes, I tried using the iterative syntax \foreach. This part consists of a part to produce the legend, one part to draw plots and one part which focuses on the colour of the plots. In the line

\foreach \w in {0.10,0.20,...,0.5} {

you may introduce the values you need for the w as in your function. I chose the values to be 0.1, 0.2, 0.3, 0.4 and 0.5 but you can choose whatever your like. Just introduce the lowerbound, step and upperbound of your iteration. As you can see, there are problems using \addlegendentry in a loop with pgfplots. I tried a temporary technique to solve this part but if you check the alternative codes in this answer, you can see that there is no need to such temp technique for at least the legends.

Varied colouring for the set of plots:

For the purpose of varying the colour of the plots, I used the solution which is described in this answer. If you don't go through that way, pgfplots will choose nice colours for your plots too; just replace the following syntaxes and see the results.

\edef\tempb{\noexpand\addplot +[mark=none]}\tempb{q(\w,x)};

enter image description here

Problems with legends and plotting syntaxes inside an iteration:

As you wanted to plot iteratively, you should bring the legends and the plotting parts inside an iteration syntax. This causes many problems which we should think about, parts of which is described in the previous part. I will point to this problem alongside with other problem which I faced for floats and answer all these together. There were some problems with number floating as part of the legend; if one does not pay attention to it, the legend's output would be so ugly.

enter image description here

The reason for such problem is described in an answer to this problem. However, the solution for the plot in your question was different. There were many different solutions to face this problem. I chose to use the sinitx package because it was easier for me to work with as a basic user. It is partially discussed in this question and answer.

@JosephWright proposed in the chatroom that

You need to use a printing function on \w rather than just inserting \w, for example; (link)

\foreach \w in {0.1,0.2,...,1} {
    \pgfmathparse{100*\w}
    \edef\x{\pgfmathresult}
    \edef\temp{\noexpand\addplot [\colora!\x!\colorb]}\temp{q(\w,x)};
    \edef\temp{\noexpand\addlegendentry{\noexpand\pgfmathprintnumber{\w}}}\temp
}

Or you could force \w to take the 'right' values, of course I'd probably force 1 to 1.0 and tidy up a little more (link)

\foreach \w in {0.1,0.2,...,1} {
    \pgfmathparse{round(\w)}
    \pgfmathparse{100*\w}
    \edef\x{\pgfmathresult}
    \edef\temp{\noexpand\addplot [\colora!\x!\colorb]}\temp{q(\w,x)};
    \pgfkeys{/pgf/number format/.cd, fixed, precision = 1, zerofill}
    \pgfmathprintnumberto{\w}{\temp}
    \addlegendentryexpanded{\temp}
}

He also presented some other ways like (link) and (link) to plot the functions:

% pdflatex
\documentclass{standalone}
\usepackage{expl3}
\usepackage{pgfplots}
\ExplSyntaxOn
\cs_new_eq:NN \fpeval \fp_eval:n
\ExplSyntaxOff

\begin{document}
\begin{tikzpicture}
       \pgfmathdeclarefunction{q}{2}{\pgfmathparse{(4.9/(#1^2))*(cosh(#1*#2)-cos(#1*#2)}}
\begin{axis}
    \foreach \w in {10,20,...,100} {
      \edef\temp{\noexpand\addplot [red!\w]}\temp{q((\w/100),x)};
      \addlegendentryexpanded{\fpeval{\w/100}}
    }
\end{axis}
\end{tikzpicture}
\end{document}

enter image description here

As you can see, all of these two solutions are presented by the expl3 package and LaTeX3 programming.

% pdflatex
\documentclass{standalone}
\usepackage{expl3}
\usepackage{pgfplots}
\ExplSyntaxOn
\cs_new_eq:NN \fpeval \fp_eval:n
\ExplSyntaxOff

\begin{document}
\begin{tikzpicture}
       \pgfmathdeclarefunction{q}{2}{\pgfmathparse{(4.9/(#1^2))*(cosh(#1*#2)-cos(#1*#2)}}
\begin{axis}
    \foreach \w in {0.1,0.2,...,1.0} {
      \edef\temp{\noexpand\addplot [red!\fpeval{100 * \w}]}\temp{q(\w,x)};
      \addlegendentryexpanded{\fpeval{round(\w,1)}}
    }
\end{axis}
\end{tikzpicture}
\end{document}

If we want to bring the legends and addplot syntaxes, we face lots of problems. It's up to you to choose which way to handle the floats, iterations and legend problems. If you have access to the required package then follow my solution. If not, go through the other solutions.

  • 4
    Many of your problems should go away (including a lot of expansions) if you use \pgfplotsinvokeforeach – percusse Apr 13 '15 at 14:06
5

A MetaPost solution, which was a very interesting exercise to do, greatly inspired by “1010011010”'s answer.

I have used two macros of my own for this task. The one simply called function allows to enter the function expression as an argument, as it is defined, and returns its associated curve. The define_cartouche macro produces a cartouche containing the legends.

For numerical accuracy the computations have been made in floating-point numerics (double number system). The Metafun format provided the cosine and hyperbolic cosine functions.

As for the rounding problem raised by Enthusiastic Student, it has been solved here using the numprint package.

\documentclass[border=2mm]{standalone}
\usepackage[autolanguage]{numprint}
  \nprounddigits{1}
\usepackage{luamplib}
  \mplibsetformat{metafun}
  \mplibtextextlabel{enable}
  \mplibnumbersystem{double}
\begin{document}
  \begin{mplibcode}
    def define_cartouche(text cartouche)(expr pos) = 
      picture cartouche; cartouche = nullpicture;
      numeric _cnt; _cnt = 0;
      def addto_cartouche (suffix cartouche)(expr wd, str, curve_color) =
        addto cartouche also (image(draw pos - (wd,0) -- pos withcolor curve_color; 
          label.rt(str, pos) withcolor black;)
            yshifted -_cnt*\mpdim{1.1\baselineskip});
        _cnt := _cnt + 1;
      enddef;
    enddef;

    vardef function(expr xmin, xmax, xstep)(text f_x) =
      save x; x := xmin;
      (x, f_x) forever: hide(x := x + xstep) exitif x > xmax; .. (x, f_x) endfor
      if x - xstep < xmax: hide(x := xmax) .. (x, f_x) fi
    enddef;

    numeric u, v, xmin, xmax, xstep; 
    u = cm; v = .1cm;
    xmax = -xmin = 4.9; ymin = 0; ymax = 100; xstep := 0.01;

    vardef thefunction(expr w) =
      function(xmin, xmax, xstep)((4.9/(w**2)) * (cosh(w*x)-cos(w*x)))
    enddef;

    beginfig(1);
      define_cartouche(cartouche)(origin); % Cartouche initialization
      % Grid
      for i = ceiling(xmin) step 2 until floor(xmax):
        if i<>0: draw (i*u, ymin*v) -- (i*u, ymax*v) withcolor .8white;
          label.bot("$" & decimal i & "$", (i*u, 0)); fi
      endfor
      for j = ceiling(ymin) step 20 until floor(ymax):
        if j<>0: draw (xmin*u, j*v) -- (xmax*u, j*v) withcolor .8white; 
        label.llft("$" & decimal j & "$", (0, j*v)); fi
      endfor
      % Curves (and clipping)
      color curve_color;
      picture curves; curves = image(
        for w = 0.1 step 0.1 until 2.05:
          curve_color := ((w-.1)/1.9)[blue,red];
          draw thefunction(w) xyscaled (u, v) withcolor curve_color;
          addto_cartouche(cartouche)(.5cm, 
            "$\omega=\numprint{" & decimal w & "}$", curve_color);
        endfor);
      clip curves to 
        ((xmin, ymin) -- (xmax, ymin) -- (xmax, ymax) -- (xmin, ymax) -- cycle)
          xyscaled (u, v);
      draw curves;
      % Axes and cartouche
      drawarrow (xmin*u, 0) -- (xmax*u, 0); 
      drawarrow (0, ymin*v) -- (0, ymax*v);
      label.bot("$O$", origin);
      draw cartouche shifted ((xmax+1)*u, (ymax-5)*v);
    endfig;
  \end{mplibcode}
\end{document}

To be processed with LuaLaTeX. Output:

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.