{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 2. Shell and other kinds of programming\n", "\n", "
Edward Sternin
September, 2025
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2.1 Shell programming: advanced parts of the bash tutorial" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you go through the tutorial, copy the example commands into your jupyter notebook and test them to make sure you understand how they work. Feel free to do that more than once, changing the given examples as you wish. An example from the \"Using Shell Arrays\" section of the tutorial would look like this:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "NAME[0]=\"Zara\"\n", "NAME[1]=\"Qadir\"\n", "NAME[2]=\"Mahnaz\"\n", "NAME[3]=\"Ayan\"\n", "NAME[4]=\"Daisy\"\n", "echo \"First Method: ${NAME[*]}\"\n", "echo \"Second Method: ${NAME[@]}\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also save the commands into a shell script and execute it as a \"command\" in your own filespace. Each time, be sure to start with a clean slate of a working directory, and clearly indicate near the beginning of the notebook where that working directory is. Among other benefits, it makes it easy for somebody else to run your notebook, from scratch. The following cells should be near the top of any jupyter notebook. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "WORKDIR=~/5P10/Lab2\n", "# if the directory exists, remove it and all its contents\n", "if [ -d $WORKDIR ]; then\n", " rm -rf $WORKDIR\n", "fi\n", "# and re-create it as a blank work directory\n", "mkdir -p $WORKDIR" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# now tell jupyter where it work\n", "%cd ~/5P10/Lab2\n", "%pwd" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%file list.sh\n", "#!/bin/bash\n", "\n", "NAME[0]=\"Zara\"\n", "NAME[1]=\"Qadir\"\n", "NAME[2]=\"Mahnaz\"\n", "NAME[3]=\"Ayan\"\n", "NAME[4]=\"Daisy\"\n", "echo \"First Method: ${NAME[*]}\"\n", "echo \"Second Method: ${NAME[@]}\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "ls -la\n", "chmod 755 list.sh\n", "ls -la\n", "./list.sh" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "echo \"Hello, world!\"\n", "\n", "### These are all of the bash environment variables (LOTS OF LINES!):\n", "#set\n", "\n", "### These are the variables that have \"TRIUMF\" in their name:\n", "set | grep ^PATH\n", "\n", "### Select a specific variable and parse the NAME=VALUE for VALUE:\n", "set | grep ^PATH | cut -d= -f2\n", "\n", "### instead of typing it, assign it to a variable:\n", "DIRS=`set | grep ^PATH | cut -d= -f2`\n", "\n", "echo \"PATH is \\\"$DIRS\\\", look for executables there\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "DIRS=`set | grep ^PATH | cut -f2 -d=`\n", "\n", "echo -e \"\\nMethod 1\\n--------\\n\"\n", "i=0\n", "while [ true ]\n", "do\n", " ((i++))\n", " d=`echo $DIRS | cut -f$i -d:`\n", " if [ \"$d\" == \"\" ]; then \n", " break\n", " else\n", " echo \"PATH contains directory $d \"\n", " fi\n", "done\n", "\n", "# another tool is sed=Stream EDitor\n", "echo -e \"\\nMethod 2\\n--------\\n\"\n", "for v in $DIRS\n", "do\n", " #replace all : with newline characters, to print each on its own line\n", " tokens=($(echo $v | sed 's/:/\\\\n\\\\t/g'))\n", " echo -e \"Directories specified in the environment variable PATH are:\\n\\t${tokens[@]}\"\n", "done\n", "\n", "# a slightly more efficient way, convert into an array of values\n", "echo -e \"\\nMethod 3\\n--------\\n\"\n", "IFS=: read -a value <<< $DIRS\n", "# \n", "printf 'PATH contains directory %s\\n' \"${value[@]}\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2.2 Writing and compiling programs\n", "\n", "Let's try a very simple C program. \"Running\" a program requires that you first \n", "\n", "Usually, the compiler can take care of all of these steps at once." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%file hello.c\n", "#include \n", "#include \n", "\n", "int main() {\n", " double f;\n", " \n", " f=4.*atan(1.);\n", " printf(\"Hello, world! Pi=%.15f\\n\",f);\n", " \n", " return 0; /* a non-zero return reports an error to OS */\n", " }" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "ls -l\n", "cc hello.c\n", "ls -l\n", "./a.out\n", "rm a.out" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The above command cc (that stands for \"C Compiler\") has many switches. Without them, it defaults to a full compile-and-link cycle and places the executable into an executable called a.out, which can get confusing if there is more than one program. This is better:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "cc -o hello hello.c\n", "./hello\n", "ls -l" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The link stage of the cc command found all the libraries it needed, including the system library that provided the atan() function." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Homework" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use bash commands ar, nm, grep, find, cut, and bash loops as needed to find which of the \\*.a or \\*.so files in /usr/lib64 and/or in /lib and/or /lib64 and all of their subdirectories contains a subroutine to calculate arctan(). Ignore symbolic link files, just use the actual library file, to remove duplication." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Solution" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "# executables in this directory become personal \"commands\"\n", "if [ ! -d ~/bin ]; then\n", " mkdir ~/bin\n", "fi" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "hide-input" ] }, "outputs": [], "source": [ "%%file ~/bin/search_libs\n", "#!/bin/sh\n", "\n", "# could make it into a parameter\n", "LIBDIR=/usr/lib64\n", "\n", "# check input line for parameters, at least one is mandatory\n", "if [ $# -lt 1 ]; then\n", " echo \" usage: $0 subroutine_name\"\n", " exit 1\n", "fi\n", "\n", "# if more than one, warn the user we are ignoring the extra input\n", "if [ $# -gt 1 ]; then\n", " echo \" $0 warning: extra input \\\"$2 ...\\\" ignored\"\n", "fi\n", "\n", "FILES=`find -P $LIBDIR \\( -name \"*.a\" -o -name \"*.so\" \\)`\n", "\n", "for f in $FILES\n", "do\n", " # figure out the file type, apply appropriate command\n", " ELF=`file $f | grep \"shared object\"`\n", " AR=`file $f | grep \"ar archive\"`\n", " \n", " # if both of the above strings are empty, this is not a file for us \n", " if [ \"$ELF\" != \"\" ]; then\n", " nm -D --defined-only --print-file $f | grep \"$1\\$\"\n", " elif [ \"$AR\" != \"\" ]; then\n", " FOUND=`ar -t $f | grep \"$1\"`\n", " if [ ! -z \"$FOUND\" ]; then # another way of checking if string is empty\n", " echo \"$f:$FOUND\"\n", " fi\n", " fi\n", "done\n", " \n", "exit 0" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "chmod 755 ~/bin/search_libs" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash \n", "search_libs atan" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We need to link to the libm\\*.so library to get the atan() function, and to libc\\*.so to initiate the running of a main(). It so happens that these are on the default list, so specifying any libraries is not necessary. However, the following should fail to link, and should produce a few errors:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "cc -nodefaultlibs -o hello hello.c" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash \n", "search_libs start_main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To fix the errors, we need to tell the linker where to look. The compiler/linker switch -lxxx forces it to look for a file libxxx.so.\\* in the usual directories. The directory to look in can be also set, using the -L/some_directory/subdir switch. Strictly speaking, in our case we should use -L/lib64 -lm-2.28 but \n", "\n", "so this (simplified) version without specific version numbers should be sufficient: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "ls -la /usr/lib64/libm.so*" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "cc -nodefaultlibs -o hello hello.c -lm -lc\n", "./hello" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2.3 Using make" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The make command treats spaces and TAB characters differently than the jupyter notebook. The following javascript commands should reconfigure jupyter to treat TABS as characters, not replace them with 4 spaces, as it does by default. This is necessary because the syntax of Makefiles mandates the use of TABs." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%javascript\n", "IPython.tab_as_tab_everywhere = function(use_tabs) {\n", " if (use_tabs === undefined) {\n", " use_tabs = true; \n", " }\n", " // apply setting to all current CodeMirror instances\n", " IPython.notebook.get_cells().map(\n", " function(c) { return c.code_mirror.options.indentWithTabs=use_tabs; }\n", " );\n", " // make sure new CodeMirror instances created in the future also use this setting\n", " CodeMirror.defaults.indentWithTabs=use_tabs;\n", " };\n", "IPython.tab_as_tab_everywhere()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%file Makefile\n", "hello: hello.c\n", "\tcc -o hello hello.c" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "rm -f hello\n", "make\n", "./hello" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This was super simple, and for such a simple \"project\" of only one source-code file probably unnecessary, but we can very quickly make this Makefile more robust and demonstrate some of the more advanced features of make. In larger projects, makefiles save a lot of time.\n", "\n", "For details, consult this makefile tutorial." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%file Makefile\n", "\n", "OBJ = hello.o\n", "\n", "# these are pre-defined, but we can also change the default behaviour, if we have multiple compilers\n", "CC = gcc\n", "#CC = icc\n", "CFLAGS = -O\n", "LIBS = -lm\n", "\n", "# special abbreviations: $@ is the file to be made; $? is the changed dependent(s).\n", "\n", "hello: $(OBJ)\n", "#\t@echo -n \" 2.link: \"\n", "\t$(CC) $(CFLAGS) -o $@ $(OBJ) $(LIBS)\n", "\n", "# this is the default implicit rule to convert xxx.c into xxx.o \n", ".c.o:\n", "#\t@echo -n \" 1.compile: \"\n", "\t$(CC) $(CFLAGS) -c $<\n", "\n", "clean:\n", "#\t@echo -n \" 0.cleanup: \"\n", "\trm -f hello a.out *.o *~ core" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "make clean; make\n", "./hello" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2.4 Gaussian packet evolution: a C project" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "More homework! This is a week-long project which we will begin today and continue next week.\n", "\n", "The goal is to graphically illustrate what happens to a Gaussian wave packet (we will make it up by adding a few individual waves) impacting on a barrier. We want to observe how some of the packet is reflected, and some undergoes quantum-mechanical tunneling, so both a reflected and a transmitted packet emerges after the impact. We will run the program repeatedly, changing the number of wave making up the packet, the number of points to plot, the initial energy of the incoming packet, etc. \n", "\n", "The physics of this tunnelling process is briefly discussed in these notes from a QM course (PDF).\n", "\n", "We need to understand the physics to create a meaningful computation, but at the same time, every programming project also has generic needs: interacting with a user, reading data, printing out the results that can be redirected to a plotting program, etc. We can actually set up a skeleton of a project even before we get into the details of the numeric code. Let's:\n", "\n", "

\n", "The second part of the homework is to try to figure out what is going on in an old Fortran program that does the same thing. We do not know either Fortran or C syntax, but hopefully the computational content, i.e. the algorithmic core, will be self-explanatory. The task is to \"port\", or convert this old Fortran code to C, and learn some basic C programming in the process.\n", " \n", "To begin with, let's make sure the old Fortan code still works and produces meaningful results." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "rm -rf packet\n", "mkdir packet" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%cd packet" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%file packet.f\n", "C packet.f -- propagation of a Gaussian wave packet through a barrier in 1D\n", "C\tx = coordinate (in units of a, the barrier is from -a to +a)\n", "C\tP = wavefunction (not normalized)\n", "C\tE = energy of the wavefunction (in units of V, the barrier height)\n", "C\n", "C\tWritten by:\tE.Sternin\n", "C\tCompleted:\t30.03.93\n", "C=======================================================================\t\n", "\treal*4\t\tx(601),x0,p0,sp,p_now,p_step,Pi,A0,t\n", "\tcomplex*8\tP(601),I,A\n", "\tinteger\t\tj,Nargs,Nwaves\n", "\tcharacter*80 \tbuffer\n", "\n", "\tNwaves = 20\t! add up (2*Nwaves+1) eigenwaves to form a packet\n", "\tx0 = -25.\t! start the packet far away from the barrier\n", "\tp0 = 0.5\t! keep incident energy low, E=0.5**2 < 1 (units of V)\n", "\tsp = 0.1\t! p0 +/- 4 sigma_p > 0,\tall eigenwaves move to the right\n", "\t \n", "\tNargs = iargc() \t! arguments supplied on the command line ?\n", "\tif (Nargs .lt. 1) then \n", "\t write(*,'(A,$)') ' Error: need time t: '\n", "\t read(*,*) t\n", "\telse\n", "\t call getarg(1,buffer)\n", "\t read(buffer,*) t\n", "\tend if\n", "\n", "\tI = cmplx(0.,1.)\t\t!complex i\n", "\tPi = 2.*acos(0.)\n", "\tA0 = 1./sqrt( sqrt(2.*Pi) * sp ) /float (2*Nwaves+1)\n", "\n", "\tdo j = 1,601\n", "\t x(j) = -75 + (j-1)*0.25\n", "\t P(j) = cmplx(0.,0.)\n", "\tend do\n", "\n", "\tp_step = 4.*sp/float(Nwaves)\t! calculate over +/- 4*sigma_p\n", "\tdo j = -Nwaves,Nwaves\t\t! in Nwaves steps\n", "\t p_now = p0 + j * p_step \n", "\t E = p_now**2\n", "\t A = A0*exp( - ( (p_now-p0)/(2*sp) )**2 - I*(p_now*x0 + E*t) )\n", "\t call add_wave(x,P,A,E)\n", "\tend do\n", "\n", "C..output the saved values as is, without normalization\n", " 10\tdo j=1,\t601\n", "\t write(*,*) x(j),abs(P(j))**2,real(P(j)),aimag(P(j))\n", "\tend do\n", "\tend\n", "\n", "C=======================================================================\t\n", "\tsubroutine add_wave(x,P,A,E)\n", "C..adds the specified wave to the the supplied complex storage array P\n", "C the values of Psi are only calculated at points given in real array x \n", "\treal*4 \t\tx(601),E\n", "\tcomplex*8\tP(601),I,A,k1,K2,C1,b,c,d,f\n", "\n", "\tI = cmplx(0.,1.)\t!complex i\n", "\n", "\tk1 = sqrt(cmplx(E,0.))\n", "\tK2 = sqrt(cmplx(E-1.,0.))\n", "\n", "\tC1 = -2*k1*K2-k1**2-2*k1*K2*exp(4*I*K2)+exp(4*I*K2)*k1**2\n", " *\t-K2**2+K2**2*exp(4*I*K2)\n", "\n", "\tb = (k1**2-K2**2)/ C1 *(exp(2*I*(2*K2-k1))-exp(-2*I*k1))\n", "\tc = -2*k1*(K2+k1)/ C1 *exp(I*(K2-k1))\n", "\td = (-2*K2+2*k1)*k1/ C1 *exp(I*(-k1+3*K2))\n", "\tf = -4*k1*K2/ C1 *exp(2*I*(K2-k1))\n", "\n", "\tdo j = 1,296\t\t! left of the barrier, x < -1\n", "\t P(j) = P(j) + A * ( exp(I*k1*x(j)) + b*exp(-I*k1*x(j)) )\n", "\tend do\n", "\tdo j=297,304\t\t! inside the barrier, -1 < x < 1\n", "\t P(j) = P(j) + A * ( c*exp(I*K2*x(j)) + d*exp(-I*K2*x(j)) )\n", "\tend do\n", "\tdo j=305,601\t\t! to the right of the barrier, x > 1\n", "\t P(j) = P(j) + A * f*exp(I*k1*x(j))\n", "\tend do\n", "\t \n", " \treturn\n", "\tend" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "gfortran -o packet packet.f\n", "./packet 40" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Perhaps it's better to just see things graphically. gnuplot is a very good, universally available plotting program, and with just a few keystrokes we can generate good-looking graphs." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%file Vo.dat\n", "-75\t0\n", "-1\t0\n", "-1\t1\n", "1\t1\n", "1\t0\n", "75\t0" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "gnuplot --persist\n", "plot 'Vo.dat' with lines" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "gnuplot --persist\n", "set style data line\n", "plot [-80:80] [-0.1:1.1] 'Vo.dat' title \"energy barrier V\",'<./packet 0' title \"packet at time = 0\",'<./packet 20' title \"packet at time = 20\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can even create an \"animation\" of sorts, by using a macro file." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%file packet.gnu\n", "set style data line\n", "set yrange [0:2]\n", "set xrange [-75:75]\n", "set xlabel \"x/a\"\n", "set ylabel \"Psi^2\"\n", "set title \"Scattering of a Gaussian wave packet (41 waves), E=V/4\"\n", "plot '<./packet 0' t \"time = 0\",'Vo.dat' t \"V(x)\"\n", "pause 2 \"initially, the packet is at x = -25 a\"\n", "plot '<./packet 5' t \"time = 5\",'Vo.dat' t \"V(x)\" \n", "pause 2 \"as time goes on...\"\n", "plot '<./packet 10' t \"time = 10\",'Vo.dat' t \"V(x)\" \n", "pause 2 \"...the packet approaches the barrier\"\n", "plot '<./packet 15' t \"time = 15\",'Vo.dat' t \"V(x)\" \n", "pause 2 \"the interference is observed\"\n", "plot '<./packet 20' t \"time = 20\",'Vo.dat' t \"V(x)\" \n", "pause 2 \"the interference is observed\"\n", "plot '<./packet 25' t \"time = 25\",'Vo.dat' t \"V(x)\" \n", "pause 2 \"the interference is observed\"\n", "plot '<./packet 30' t \"time = 30\",'Vo.dat' t \"V(x)\" \n", "pause 2 \"the interference is observed\"\n", "plot '<./packet 35' t \"time = 35\",'Vo.dat' t \"V(x)\" \n", "pause 2 \"transmitted and reflected packets form\"\n", "plot '<./packet 40' t \"time = 40\",'Vo.dat' t \"V(x)\" \n", "pause 2 \"transmitted and reflected packets form\"\n", "plot '<./packet 45' t \"time = 45\",'Vo.dat' t \"V(x)\" \n", "pause 2 \"and move away from the barrier\"\n", "plot '<./packet 50' t \"time = 50\",'Vo.dat' t \"V(x)\" \n", "pause 2 \"and move away from the barrier\"\n", "plot '<./packet 55' t \"time = 55\",'Vo.dat' t \"V(x)\" \n", "pause 2 \"note how they spread out with time\"\n", "plot '<./packet 60' t \"time = 60\",'Vo.dat' t \"V(x)\" \n", "pause 2 \"note how they spread out with time\"\n", "plot '<./packet 65' t \"time = 65\",'Vo.dat' t \"V(x)\" \n", "pause 2 \"note how they spread out with time\"\n", "plot '<./packet 70' t \"time = 70\",'Vo.dat' t \"V(x)\" \n", "pause 2 \"note how they spread out with time\"\n", "plot '<./packet 80' t \"time = 80\",'Vo.dat' t \"V(x)\" \n", "pause 2\n", "plot '<./packet 90' t \"time = 90\",'Vo.dat' t \"V(x)\"\n", "pause 2" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "gnuplot\n", "load 'packet.gnu'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another way of using gnuplot is to load it right into the notebook itself." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%load_ext gnuplot_kernel" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%gnuplot inline pngcairo font \"Arial,16\" size 640,480" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%gnuplot\n", "set style data line\n", "set yrange [0:2]\n", "set xrange [-75:75]\n", "set xlabel \"x/a\"\n", "set ylabel \"Psi^2\"\n", "set title \"Scattering of a Gaussian wave packet (41 waves), E=V/4\"\n", "plot '<./packet 0' t \"time = 0\",'<./packet 40' t \"time = 40\",'Vo.dat' t \"V(x)\",\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2.5 Porting Fortran to C\n", "\n", "Now that we have a skeleton of a C program working, let's examine the old Fortran code to see how it works and what its output looks like. We may not know the Fortran syntax, but we should be able to figure out what the code does computationally. After all, FORTRAN = \"FORmula TRANslation\", by design.\n", "\n", "Let us begin by taking a skeleton C program that demonstrates the framework of how a C program interacts with the user.\n", "\n", "You can make changes, recompile, and re-run. If you continue working in this jupyter notebook, the next few cells will get executed repeatedly. You can also open a separate editor window to modify the packet.c file and use the make to re-build the code after every change." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%file packet.c\n", "/*\n", " * packet.c\n", " * packet - generate a Gaussian wavepacket impacting on an energy barrier.\n", " *\n", " * Completed: January.2018 (c) E.Sternin\n", " * Revisions: \n", " *\n", " */\n", "\n", "#ifndef VERSION /* date-derived in Makefile */\n", "#define VERSION \"2018.01\" /* default, that's when we first wrote the program */\n", "#endif\n", "\n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "\n", "#define MAX_STR 256\n", "\n", "/* Global variables */\n", "\n", "static char whoami[MAX_STR] ; /* argv[0] will end up here */\n", "static int verbosity; /* Show detailed information */\n", "static char options[] = \"Vvhp:t:\"; /* command-line options, : means takes a value */\n", "static char help_msg[] = \"\\\n", "%s []\\n\\\n", "\\t-V report version information\\n\\\n", "\\t-v increase verbosity, may combine several\\n\\\n", "\\t-p # number of points for the packet\\n\\\n", "\\t-t # time since the beginning\\n\\\n", "\\t-h help message\\n\\\n", "\\n\\\n", "e.g.\\tpacket -v -p 601 -t 20\\n\" \n", ";\n", "\n", "/*************************************************service routines************/\n", "\n", "void __attribute__((noreturn)) die(char *msg, ...) {\n", " va_list args;\n", " va_start(args, msg);\n", " vfprintf(stderr, msg, args);\n", " fputc('\\n', stderr);\n", " exit(1);\n", " }\n", "\n", "/***************************************************************** main *********/\n", "int main(int argc, char **argv) {\n", " int i,p;\n", " double t;\n", "\n", "/*\n", " * default values, may get changed by the command-line options\n", " */\n", " verbosity = 0; /* 0 = quiet by default, 1 = info, 2 = debug */\t\n", " p = 25; /* default to 25 points in the packet */\n", " t = 0;\n", "\n", " strncpy(whoami, argv[0], MAX_STR);\n", "\n", " while ((i = getopt(argc, argv, options)) != -1)\n", " switch (i) {\n", " case 'V':\n", " printf(\" %s: version %s\\n\",whoami,VERSION);\n", " break;\n", " case 'v':\n", " verbosity++;\n", " if (verbosity > 1) printf(\" %s: verbosity level set to %d\\n\",whoami,verbosity);\n", " break;\n", " case 'h':\n", " die(help_msg,whoami);\n", " break;\n", " case 'p':\n", " if ( ((p = atoi(optarg)) > 10000) || p < 10 )\n", " die(\" %s: -p %d is not a valid number of points (10..10000)\\n\",whoami,p);\n", " if (verbosity > 1) printf(\" %s: Number of points = %d\\n\",whoami,p);\n", " break;\n", " case 't':\n", " if ( ((t = atof(optarg)) < 0) )\n", " die(\" %s: -t %d is not a valid time\\n\",whoami,t);\n", " if (verbosity > 1) printf(\" %s: time = %f\\n\",whoami,t);\n", " break;\n", " default:\n", " if (verbosity > 0) die(\" try %s -h\\n\",whoami);\t/* otherwise, die quietly */\n", " return 0;\n", " }\n", "\n", "/*\n", " * when we get here, we parsed all user input, and are ready to calculate things\n", " */\n", "\n", " for (i=0; i < p; i++) { \n", " if (verbosity > 0) printf(\"%d\\t%f\\n\",i,i*t);\n", " }\n", "\n", " return 0;\n", " }\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "cc -DVERSION=\"\\\"`date '+%Y-%b-%d-%H:%M'`\\\"\" -o packet packet.c\n", "./packet -vvvV -p10" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%file Makefile\n", "\n", "OBJ = packet.o\n", "\n", "# these are pre-defined, but we can also change the default behaviour, if we have multiple compilers\n", "CC = cc\n", "#CC = icc\n", "CFLAGS = -O -DVERSION=\"\\\"`date '+%Y-%b-%d-%H:%M'`\\\"\" \n", "LIBS = -lm\n", "\n", "# special abbreviations: $@ is the file to be made; $? is the changed dependent(s).\n", "\n", "packet: $(OBJ)\n", "\t$(CC) $(CFLAGS) -o $@ $(OBJ) $(LIBS)\n", "\n", "# this is the default implicit rule to convert xxx.c into xxx.o \n", ".c.o:\n", "\t$(CC) $(CFLAGS) -c $<\n", "\n", "clean:\n", "\trm -f packet *.o *~ core" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "%%bash\n", "make clean; make\n", "./packet -Vvvv -p 11 -t 25" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Homework\n", "\n", "Analyze the numeric parts of the Fortran code and insert the equivalent C code into the above \n", "skeleton packet.c. At the moment, the numerical time loop simply prints out the time value.\n", "The details are in the add_wave() part of the Fortran code." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "hide_input": false, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.3" }, "toc": { "nav_menu": {}, "number_sections": false, "sideBar": true, "skip_h1_title": false, "toc_cell": false, "toc_position": {}, "toc_section_display": "block", "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 2 }