3

I would be glad if you could help me with a problem in Tikz. I want to draw a diagram for a paper about epidemiological systems (as illustrated below) and have two issues (the more important one regarding arrows).

  1. I want to draw 3 arrows on the graph which point to the left to show the movement (which basically starts at S = 1). I tried to implement this based on this answer (Add arrows to a smooth tikz function), but I can't get 3 arrows and I have no idea how to change the direction.

  2. I need a lot of samples (2000) to draw the graph completely. If I use fewer samples, the graph just stops. Do have an idea how to improve this?

Thanks for your support.

\documentclass[tikz,border=2mm]{standalone}
    \usepackage{pgfplots}
    \usetikzlibrary{arrows.meta,positioning}
    \usetikzlibrary{decorations.markings}
    \tikzset{
        set arrow inside/.code={\pgfqkeys{/tikz/arrow inside}{#1}},
        set arrow inside={end/.initial=>, opt/.initial=},
        /pgf/decoration/Mark/.style={
            mark/.expanded=at position #1 with
            {
                \noexpand\arrow[\pgfkeysvalueof{/tikz/arrow inside/opt}]{\pgfkeysvalueof{/tikz/arrow inside/end}}
            }
        },
        arrow inside/.style 2 args={
            set arrow inside={#1},
            post*emphasized text*action={
                decorate,decoration={
                    markings,Mark/.list={#2}
                }
            }
        },
    }

\begin{document}
\begin{tikzpicture}[>=latex]
        \begin{axis}[
            axis x line=center,
            axis y line=center,
            xmin=0, xmax=1.1, 
            ymin=0, ymax=1.1,
            xtick={0,1},
            ytick={0,1},
            hide obscured x ticks=false,
            extra x ticks={0.33},
            extra x tick labels={$\frac{\gamma}{\beta}$},
            extra y ticks={0.3},
            extra y tick labels={$I_{\text{max}}$},
            xlabel=$S$, ylabel=$I$,
            xlabel style={right},
            ylabel style={above},
            scale=1.2
            ]

            \draw[dashed] (axis cs: 0.33,1) -- (axis cs: 0.33,0) node[pos=1, below] {$\frac{\gamma}{\beta}$};
            \draw[dashed] (axis cs: 0,0.3) -- (axis cs: 1,0.3);

            \addplot[smooth] {1-x};
            \addplot[samples=2000, red, smooth] {1 + (1/3) * ln(x) - x} [arrow inside={end=stealth,opt={red,scale=2}}{0.25,0.50.75}];
            %\addplot[samples=1500, green, smooth] {1 + (1/6) * ln(x) - x} [arrow inside={end=stealth,opt={green,scale=1.5}}{0.25,0.50.75}];
            \node[label={45:{$(S^*,I_{\text{max}})$}}, circle, fill, inner sep=1pt] at (axis cs: 0.33,0.3) {};
        \end{axis}
    \end{tikzpicture}
\end{document}

enter image description here

2

Welcome! As for 2, I added a domain to avoid invalid points in which logarithms of negative numbers or zero get taken, and, as for 1, fixed the arrow positions. Unfortunately for smooth plots decorations.markings easily gives rise to dimension too large errors, so one has to remove smooth, but given the still moderately large number of samples the result looks still good and is hardly distinguishable from the smooth version.

\documentclass[tikz,border=2mm]{standalone}
\usepackage{amsmath}
\usepackage{pgfplots}
\pgfplotsset{compat=1.17}
\usetikzlibrary{arrows.meta,positioning}
\usetikzlibrary{decorations.markings}
\tikzset{
    set arrow inside/.code={\pgfqkeys{/tikz/arrow inside}{#1}},
    set arrow inside={end/.initial=>, opt/.initial=},
    /pgf/decoration/Mark/.style={
        mark/.expanded=at position #1 with
        {
            \noexpand\arrow[\pgfkeysvalueof{/tikz/arrow inside/opt}]{\pgfkeysvalueof{/tikz/arrow inside/end}}
        }
    },
    arrow inside/.style 2 args={
        set arrow inside={#1},
        postaction={
            decorate,decoration={
                markings,Mark/.list={#2}
            }
        }
    },
}

\begin{document}
\begin{tikzpicture}[>=latex]
        \begin{axis}[
            axis x line=center,
            axis y line=center,
            xmin=0, xmax=1.1, 
            ymin=0, ymax=1.1,
            xtick={0,1},
            ytick={0,1},
            hide obscured x ticks=false,
            extra x ticks={0.33},
            extra x tick labels={$\frac{\gamma}{\beta}$},
            extra y ticks={0.3},
            extra y tick labels={$I_{\text{max}}$},
            xlabel=$S$, ylabel=$I$,
            xlabel style={right},
            ylabel style={above},
            scale=1.2
            ]

            \draw[dashed] (axis cs: 0.33,1) -- (axis cs: 0.33,0) node[pos=1, below] {$\frac{\gamma}{\beta}$};
            \draw[dashed] (axis cs: 0,0.3) -- (axis cs: 1,0.3);

            \addplot[smooth] {1-x};
            \addplot[samples=101, red,domain=0.05:1,
                arrow inside={end=stealth,opt={red,scale=2}}{0.25,0.5,0.75}] 
                {1 + (1/3) * ln(x) - x} 
            ;
            \addplot[samples=101, green!70!black, domain=0.002:1,
                arrow inside={end=stealth,opt={green!70!black,scale=1.5}}{0.25,0.5,0.75}] {1 + (1/6) * ln(x) - x} ;
            \node[label={45:{$(S^*,I_{\text{max}})$}}, circle, fill, inner sep=1pt]
             at (axis cs: 0.33,0.3) {};
        \end{axis}
    \end{tikzpicture}
\end{document}

enter image description here

One can avoid the dimension too large errors and use smooth plots if one tells TikZ to use fpu for computing reciprocals.

\documentclass[tikz,border=2mm]{standalone}
\usepackage{amsmath}
\usepackage{pgfplots}
\pgfplotsset{compat=1.17}
\usetikzlibrary{arrows.meta,positioning}
\usetikzlibrary{decorations.markings}
\tikzset{
    set arrow inside/.code={\pgfqkeys{/tikz/arrow inside}{#1}},
    set arrow inside={end/.initial=>, opt/.initial=},
    /pgf/decoration/Mark/.style={
        mark/.expanded=at position #1 with
        {
            \noexpand\arrow[\pgfkeysvalueof{/tikz/arrow inside/opt}]{\pgfkeysvalueof{/tikz/arrow inside/end}}
        }
    },
    arrow inside/.style 2 args={
        set arrow inside={#1},
        postaction={
            decorate,decoration={
                markings,Mark/.list={#2}
            }
        }
    },
}

\makeatletter
\tikzset{use fpu reciprocal/.code={%
\def\pgfmathreciprocal@##1{%
    \begingroup
    \pgfkeys{/pgf/fpu=true,/pgf/fpu/output format=fixed}%
    \pgfmathparse{1/##1}%
    \pgfmath@smuggleone\pgfmathresult
    \endgroup
}}}%
\makeatother


\begin{document}
\begin{tikzpicture}[>=latex]
        \begin{axis}[
            axis x line=center,
            axis y line=center,
            xmin=0, xmax=1.1, 
            ymin=0, ymax=1.1,
            xtick={0,1},
            ytick={0,1},
            hide obscured x ticks=false,
            extra x ticks={0.33},
            extra x tick labels={$\frac{\gamma}{\beta}$},
            extra y ticks={0.3},
            extra y tick labels={$I_{\text{max}}$},
            xlabel=$S$, ylabel=$I$,
            xlabel style={right},
            ylabel style={above},
            scale=1.2
            ]

            \draw[dashed] (axis cs: 0.33,1) -- (axis cs: 0.33,0) node[pos=1, below] {$\frac{\gamma}{\beta}$};
            \draw[dashed] (axis cs: 0,0.3) -- (axis cs: 1,0.3);

            \addplot[smooth] {1-x};
            \begin{scope}[use fpu reciprocal,>=stealth]
            \addplot[samples=101, red,domain=0.05:1,smooth,
                arrow inside={end={<},opt={red,scale=2}}{0.25,0.5,0.75}] 
                {1 + (1/3) * ln(x) - x} 
            ;
            \addplot[samples=101, green!70!black, domain=0.002:1,smooth,
                arrow inside={end={<},opt={green!70!black,scale=1.5}}{0.25,0.5,0.75}] {1 + (1/6) * ln(x) - x} ;
            \end{scope} 
            \node[label={45:{$(S^*,I_{\text{max}})$}}, circle, fill, inner sep=1pt]
             at (axis cs: 0.33,0.3) {};
        \end{axis}
    \end{tikzpicture}
\end{document}

enter image description here

| improve this answer | |
  • Thank you a lot, that was very helpful. I just edit a detail in my question (because I erroneously said it begins at S=0, but indeed it should begin at S=1. Is there a possibility that the arrows point in the other direction (i.e. from left to right)? Thanks again. – thomjan Apr 23 at 17:40
  • 1
    @thomjan You can either add a reversed key (see pgfmanual v3.1.5 p. 198) or just use the < symbol. I added reversed arrows to the second code but the same construction will work for the first one (provided you drop smooth there, of course). – user194703 Apr 23 at 17:47
  • To be clear, in the first example you can use something like \addplot[samples=101, red,domain=0.05:1, arrow inside={end={Stealth[reversed,red,scale=1.5]}}{0.25,0.5,0.75}] {1 + (1/3) * ln(x) - x};. – user194703 Apr 23 at 17:52
  • Thanks mate for the advice and the reference, it works now! – thomjan Apr 23 at 18:27

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.