19

I am learning lualatex because of its abilities to use lua and make it easier for more sophisticated documents.

I am using this document for running some introductory examples. https://www.unirioja.es/cu/jvarona/downloads/numerical-methods-luatex.pdf

I have two questions.

  1. Is it possible to call external Lua libraries downloaded from the web from Lua code being used in Luatex?

  2. Can we write long chunks of lua code in a different file than inside the TeX file itself? Consider the Lorentez attractor examples at the end, which compiles and works well on my machine. But the lua code to produce the attractor is rather long. Is there anyway for me to write this lua code in an external file instead? The following gives me a latex compilation error where I attempted to use \input{scrap.lua}

\documentclass{article} \usepackage{luacode} \usepackage{pgfplots} \usepackage{tikz} \begin{luacode*} \input{scrap.lua} \end{luacode*} \newcommand\addLUADEDplot[3][]{% \directlua{print_LorAttrWithEulerMethod(#2,#3,[[#1]])}% } \begin{document} \begin{tikzpicture} \begin{axis} % SYNTAX: Solution of the Lorenz system % with step h=0.02 sampled at 1000 points. \addLUADEDplot[color=red,smooth]{0.02}{1000}; \addLUADEDplot[color=green,smooth]{0.02}{1000}; \addLUADEDplot[color=blue,smooth]{0.02}{1000}; \addLUADEDplot[color=cyan,smooth]{0.02}{1000}; \addLUADEDplot[color=magenta,smooth]{0.02}{1000}; \addLUADEDplot[color=yellow,smooth]{0.02}{1000}; \end{axis} \end{tikzpicture} \end{document} 

The external file scrap.lua looks like

-- Differential equation of the Lorenz attractor function f(x,y,z) local sigma = 3 local rho = 26.5 local beta = 1 return {sigma*(y-x), -x*z + rho*x - y, x*y - beta*z} end -- Code to write PGFplots data as coordinates function print_LorAttrWithEulerMethod(h,npoints,option) -- The initial point (x0,y0,z0) local x0 = 0.0 local y0 = 1.0 local z0 = 0.0 -- we add a random number between -0.25 and 0.25 local x = x0 + (math.random()-0.5)/2 local y = y0 + (math.random()-0.5)/2 local z = z0 + (math.random()-0.5)/2 if option~=[[]] then tex.sprint("\\addplot3["..option.."] coordinates{") else tex.sprint("\\addplot3 coordinates{") end -- we dismiss the first 100 points to go into the attractor for i=1, 100 do m = f(x,y,z) x = x + h * m[1] y = y + h * m[2] z = z + h * m[3] end for i=1, npoints do m = f(x,y,z) x = x + h * m[1] y = y + h * m[2] z = z + h * m[3] tex.sprint("("..x..","..y..","..z..")") end tex.sprint("}") end 

The error I get is

ex))) ! LuaTeX error [\directlua]:1: unexpected symbol near '\'. \luacode@dbg@exec ...code@maybe@printdbg {#1} #1 } l.8 \end{luacode*}

 ? 
2
  • Between \begin{luacode*} and \end{luacode*} you have to write Lua code and \input{scrap.lua} is not valid Lua. Hence you get this cryptic error. Commented Dec 10, 2018 at 0:40
  • 1
    This might be obvious for the regular luatex/lualatex user, but you can specify the path of the file you want to load within the require command. I just have a bunch of personal functions and load them in my preamble with \directlua{require('./pathFromCurrentFolder/LuaCommands.lua')}. Commented Aug 14, 2022 at 4:44

1 Answer 1

23

The best way to do this is to make your external file into a module. That means, you make sure that all variables are local and in the end you return a table which exports all user-accessible functions. For example f(x,y,z) is not going to be used outside, so I won't export it. As you can see I can also export functions under a different name.

-- Differential equation of the Lorenz attractor local function f(x,y,z) local sigma = 3 local rho = 26.5 local beta = 1 return {sigma*(y-x), -x*z + rho*x - y, x*y - beta*z} end -- Code to write PGFplots data as coordinates local function print_LorAttrWithEulerMethod(h,npoints,option) -- The initial point (x0,y0,z0) local x0 = 0.0 local y0 = 1.0 local z0 = 0.0 -- we add a random number between -0.25 and 0.25 local x = x0 + (math.random()-0.5)/2 local y = y0 + (math.random()-0.5)/2 local z = z0 + (math.random()-0.5)/2 if option~=[[]] then tex.sprint("\\addplot3["..option.."] coordinates{") else tex.sprint("\\addplot3 coordinates{") end -- we dismiss the first 100 points to go into the attractor for i=1, 100 do local m = f(x,y,z) x = x + h * m[1] y = y + h * m[2] z = z + h * m[3] end for i=1, npoints do local m = f(x,y,z) x = x + h * m[1] y = y + h * m[2] z = z + h * m[3] tex.sprint("("..x..","..y..","..z..")") end tex.sprint("}") end return { LorenzAttractor = print_LorAttrWithEulerMethod } 

An advantage of having the Lua code in a separate file is that you do not have to worry about catcodes. Therefore you don't need the luacode package at all. The module you created in scrap.lua can be loaded like any regular Lua module. Usually you would load it like

local scrap = require("scrap") 

but that won't work in LuaTeX because the scrap variable would only be local to the \directlua chunk it is mentioned in and can hence not be used in other chunks. Therefore you have to make a global variable scrap to encapsulate the module

\directlua{scrap = require("scrap")} 

The full document would read

\documentclass{article} \usepackage{pgfplots} \usepackage{tikz} \directlua{scrap = require("scrap")} \newcommand\addLUADEDplot[3][]{% \directlua{scrap.LorenzAttractor(#2,#3,[[#1]])}% } \begin{document} \begin{tikzpicture} \begin{axis} % SYNTAX: Solution of the Lorenz system % with step h=0.02 sampled at 1000 points. \addLUADEDplot[color=red,smooth]{0.02}{1000}; \addLUADEDplot[color=green,smooth]{0.02}{1000}; \addLUADEDplot[color=blue,smooth]{0.02}{1000}; \addLUADEDplot[color=cyan,smooth]{0.02}{1000}; \addLUADEDplot[color=magenta,smooth]{0.02}{1000}; \addLUADEDplot[color=yellow,smooth]{0.02}{1000}; \end{axis} \end{tikzpicture} \end{document} 

enter image description here

3
  • Wow! Nice answer! Exactly what I was looking for. Regarding question 1, I will be able to use external Lua libraries from scrap.lua right? I am particularly interested in calling https://github.com/gvvaughan/lyaml to read yaml files which contain some experimental data Commented Dec 10, 2018 at 0:56
  • @smilingbuddha Yes, however lyaml has to be placed such that LuaTeX can find it. Don't worry there is a package to help you: luapackageloader. Commented Dec 10, 2018 at 1:00
  • In order to avoid potential name collisions, you can remove the declaration of the global scratch variable and replace scratch.LorentzAttractor by require("scratch").LorentzAttractor. The require function will read scratch.lua only once (dofile would read it at each call). Depending on your own coding style, you can use local scratch = require("scratch"), just before scratch.LorentzAttractor in the same \directlua chunck. Finally, you can still choose a different global name: smilingbouddha_scratch would be more qualified. Commented May 4, 2022 at 7:48

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.