├── images ├── .gitkeep ├── euro-cc.jpg ├── epcc_logo.jpg └── Archer2_logo.png ├── notebooks ├── mpi4py │ ├── .gitignore │ ├── example5.py │ ├── example1.py │ ├── example5a.py │ ├── example4.py │ ├── example2.py │ ├── example6.py │ ├── example3.py │ └── mpi4py.ipynb ├── numpy │ ├── darts.png │ ├── example.py │ └── darts.py ├── images │ ├── epcc_logo.png │ ├── hpe_logo.png │ ├── ukri_logo.png │ ├── archer2_logo.png │ ├── logos │ │ ├── crest_bw.pdf │ │ ├── crest_rb.pdf │ │ ├── epccvert.pdf │ │ ├── eucrest.pdf │ │ ├── nucrest.pdf │ │ ├── bullet_red.pdf │ │ ├── crest_col.pdf │ │ ├── logo_black.pdf │ │ ├── logo_colour.pdf │ │ ├── logo_white.pdf │ │ ├── bullet_black.pdf │ │ ├── epccvert.eps │ │ ├── bullet_red.eps │ │ ├── bullet_black.eps │ │ └── logo_black.eps │ └── reusematerial.png ├── trafficmpi │ ├── trafficlib.py │ ├── traffic.py │ └── trafficmpi.ipynb ├── trafficnumpy │ ├── trafficlib.py │ ├── traffic.py │ └── trafficnumpy.ipynb └── traffic │ ├── trafficlib.py │ ├── traffic.py │ └── traffic.ipynb ├── code ├── C-MPI │ ├── uni.h │ ├── traffic.h │ ├── archer2.job │ ├── Makefile │ ├── trafficlib.c │ ├── traffic.c │ └── uni.c ├── C-OMP │ ├── uni.h │ ├── traffic.h │ ├── Makefile │ ├── archer2.job │ ├── trafficlib.c │ ├── traffic.c │ └── uni.c ├── C-SER │ ├── uni.h │ ├── traffic.h │ ├── Makefile │ ├── trafficlib.c │ ├── traffic.c │ └── uni.c ├── P-SER-NP │ ├── archer2.job │ ├── trafficlib.py │ └── traffic.py ├── P-SER │ ├── archer2.job │ ├── trafficlib.py │ └── traffic.py ├── F-MPI │ ├── archer2.job │ ├── Makefile │ ├── unirand.f90 │ ├── trafficlib.f90 │ └── traffic.f90 ├── P-MPI │ ├── archer2.job │ ├── trafficlib.py │ └── traffic.py ├── F-OMP │ ├── archer2.job │ ├── Makefile │ ├── trafficlib.f90 │ ├── unirand.f90 │ └── traffic.f90 └── F-SER │ ├── Makefile │ ├── traffic.f90 │ ├── unirand.f90 │ └── trafficlib.f90 ├── pdf ├── Intro.pdf ├── L01-traffic.pdf ├── E01-traffic-MSc.pdf ├── L01-mpconcepts.pdf └── ARCHER2-overview.pdf └── README.md /images/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /notebooks/mpi4py/.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints/ 2 | -------------------------------------------------------------------------------- /code/C-MPI/uni.h: -------------------------------------------------------------------------------- 1 | float uni(void); 2 | void rinit(int seed); 3 | -------------------------------------------------------------------------------- /code/C-OMP/uni.h: -------------------------------------------------------------------------------- 1 | float uni(void); 2 | void rinit(int seed); 3 | -------------------------------------------------------------------------------- /code/C-SER/uni.h: -------------------------------------------------------------------------------- 1 | float uni(void); 2 | void rinit(int seed); 3 | -------------------------------------------------------------------------------- /pdf/Intro.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/pdf/Intro.pdf -------------------------------------------------------------------------------- /images/euro-cc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/images/euro-cc.jpg -------------------------------------------------------------------------------- /images/epcc_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/images/epcc_logo.jpg -------------------------------------------------------------------------------- /pdf/L01-traffic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/pdf/L01-traffic.pdf -------------------------------------------------------------------------------- /images/Archer2_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/images/Archer2_logo.png -------------------------------------------------------------------------------- /pdf/E01-traffic-MSc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/pdf/E01-traffic-MSc.pdf -------------------------------------------------------------------------------- /pdf/L01-mpconcepts.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/pdf/L01-mpconcepts.pdf -------------------------------------------------------------------------------- /notebooks/numpy/darts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/numpy/darts.png -------------------------------------------------------------------------------- /pdf/ARCHER2-overview.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/pdf/ARCHER2-overview.pdf -------------------------------------------------------------------------------- /notebooks/images/epcc_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/images/epcc_logo.png -------------------------------------------------------------------------------- /notebooks/images/hpe_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/images/hpe_logo.png -------------------------------------------------------------------------------- /notebooks/images/ukri_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/images/ukri_logo.png -------------------------------------------------------------------------------- /notebooks/images/archer2_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/images/archer2_logo.png -------------------------------------------------------------------------------- /notebooks/images/logos/crest_bw.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/images/logos/crest_bw.pdf -------------------------------------------------------------------------------- /notebooks/images/logos/crest_rb.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/images/logos/crest_rb.pdf -------------------------------------------------------------------------------- /notebooks/images/logos/epccvert.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/images/logos/epccvert.pdf -------------------------------------------------------------------------------- /notebooks/images/logos/eucrest.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/images/logos/eucrest.pdf -------------------------------------------------------------------------------- /notebooks/images/logos/nucrest.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/images/logos/nucrest.pdf -------------------------------------------------------------------------------- /notebooks/images/reusematerial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/images/reusematerial.png -------------------------------------------------------------------------------- /notebooks/images/logos/bullet_red.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/images/logos/bullet_red.pdf -------------------------------------------------------------------------------- /notebooks/images/logos/crest_col.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/images/logos/crest_col.pdf -------------------------------------------------------------------------------- /notebooks/images/logos/logo_black.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/images/logos/logo_black.pdf -------------------------------------------------------------------------------- /notebooks/images/logos/logo_colour.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/images/logos/logo_colour.pdf -------------------------------------------------------------------------------- /notebooks/images/logos/logo_white.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/images/logos/logo_white.pdf -------------------------------------------------------------------------------- /notebooks/images/logos/bullet_black.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EPCCed/PythonHPC/HEAD/notebooks/images/logos/bullet_black.pdf -------------------------------------------------------------------------------- /code/C-MPI/traffic.h: -------------------------------------------------------------------------------- 1 | #define SEED 5743 2 | 3 | int initroad(int *road, int n, float density, int seed); 4 | int updateroad(int *newroad, int *oldroad, int n); 5 | -------------------------------------------------------------------------------- /code/C-OMP/traffic.h: -------------------------------------------------------------------------------- 1 | #define SEED 5743 2 | 3 | int initroad(int *road, int n, float density, int seed); 4 | void updatebcs(int *road, int n); 5 | int updateroad(int *newroad, int *oldroad, int n); 6 | double gettime(); 7 | -------------------------------------------------------------------------------- /code/C-SER/traffic.h: -------------------------------------------------------------------------------- 1 | #define SEED 5743 2 | 3 | int initroad(int *road, int n, float density, int seed); 4 | void updatebcs(int *road, int n); 5 | int updateroad(int *newroad, int *oldroad, int n); 6 | double gettime(); 7 | -------------------------------------------------------------------------------- /notebooks/images/logos/epccvert.eps: -------------------------------------------------------------------------------- 1 | %! 2 | %%BoundingBox: -3 0 7 374 3 | % epccvert.ps 4 | % 5 | % Nicholas J Radcliffe 28.22.93 6 | % 7 | 8 | /Palatino-Italic findfont 9 | 24 scalefont 10 | setfont 11 | 12 | 270 rotate 13 | 14 | newpath 15 | (Edinburgh Parallel Computing Centre) 16 | dup stringwidth pop 17 | neg 18 | 0 moveto 19 | show 20 | 21 | showpage 22 | -------------------------------------------------------------------------------- /notebooks/mpi4py/example5.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | from mpi4py import MPI 4 | 5 | def main(comm): 6 | """The sum of a list is...""" 7 | 8 | mylist = [] 9 | mylist = comm.reduce([comm.rank], op=MPI.SUM, root=0) 10 | 11 | if comm.rank == 0: 12 | sys.stdout.write("List of ranks: {}\n".format(mylist)) 13 | 14 | 15 | if __name__ == "__main__": 16 | 17 | main(MPI.COMM_WORLD) 18 | 19 | -------------------------------------------------------------------------------- /notebooks/mpi4py/example1.py: -------------------------------------------------------------------------------- 1 | """Importing and using MPI.COMM_WORLD""" 2 | 3 | import sys 4 | from mpi4py import MPI 5 | 6 | def main(comm): 7 | """Report rank and size of this communicator""" 8 | rank = comm.rank 9 | size = comm.size 10 | sys.stdout.write("Hello from rank {:2d} of {:2d}\n".format(rank, size)) 11 | 12 | if __name__ == "__main__": 13 | """Execute in MPI.COMM_WORLD""" 14 | main(MPI.COMM_WORLD) 15 | -------------------------------------------------------------------------------- /code/P-SER-NP/archer2.job: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Slurm job options (name, compute nodes, job time) 4 | #SBATCH --job-name=traffic 5 | #SBATCH --time=00:05:00 6 | #SBATCH --output=%x-%j.out 7 | #SBATCH --error=%x-%j.err 8 | #SBATCH --nodes=1 9 | #SBATCH --tasks-per-node=128 10 | #SBATCH --cpus-per-task=1 11 | #SBATCH --partition=standard 12 | #SBATCH --qos=reservation 13 | #SBATCH --reservation=ta083_797464 14 | 15 | module load cray-python 16 | 17 | # Run the job 18 | 19 | python traffic.py 20 | -------------------------------------------------------------------------------- /code/P-SER/archer2.job: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Slurm job options (name, compute nodes, job time) 4 | #SBATCH --job-name=traffic 5 | #SBATCH --time=00:10:00 6 | #SBATCH --output=%x-%j.out 7 | #SBATCH --error=%x-%j.err 8 | #SBATCH --nodes=1 9 | #SBATCH --tasks-per-node=128 10 | #SBATCH --cpus-per-task=1 11 | #SBATCH --partition=standard 12 | #SBATCH --qos=reservation 13 | #SBATCH --reservation=ta083_797464 14 | 15 | module load cray-python 16 | 17 | # Run the job 18 | 19 | python traffic.py 20 | -------------------------------------------------------------------------------- /notebooks/mpi4py/example5a.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import sys 3 | from mpi4py import MPI 4 | 5 | def main(comm): 6 | """The sum of a list is...""" 7 | 8 | total_sum = numpy.array(0, 'i') 9 | rank = numpy.array(comm.rank, 'i') 10 | comm.Reduce([rank, MPI.INTEGER], [total_sum, MPI.INTEGER], op=MPI.SUM, root=0) 11 | 12 | if comm.rank == 0: 13 | sys.stdout.write("Sum of ranks: {}\n".format(total_sum)) 14 | 15 | 16 | if __name__ == "__main__": 17 | 18 | main(MPI.COMM_WORLD) 19 | 20 | -------------------------------------------------------------------------------- /notebooks/numpy/example.py: -------------------------------------------------------------------------------- 1 | """Example of timeit from python script""" 2 | 3 | import timeit 4 | 5 | def main(): 6 | 7 | # code to be executed as setup (must include import statement) 8 | init = "import numpy; nd = numpy.arange(100).reshape((10,10))" 9 | 10 | t = timeit.Timer(stmt= "nd[5][5]", setup= init) 11 | print(t.repeat(repeat= 3, number= 10000000)) 12 | 13 | t = timeit.Timer(stmt= "nd[5,5]", setup= init) 14 | print(t.repeat(repeat = 3, number= 10000000)) 15 | 16 | if __name__ == "__main__": 17 | 18 | main() 19 | -------------------------------------------------------------------------------- /notebooks/mpi4py/example4.py: -------------------------------------------------------------------------------- 1 | """Message passing a python object""" 2 | 3 | import sys 4 | from mpi4py import MPI 5 | 6 | def main(comm): 7 | """Send a list from rank 0 to rank 1""" 8 | 9 | if comm.rank == 0: 10 | msg = ["Any", "old", "thing", comm.rank, {"size" : comm.size}] 11 | comm.send(msg, dest = 1, tag = 999) 12 | elif comm.rank == 1: 13 | msg = comm.recv(source = 0, tag = 999) 14 | sys.stdout.write("Rank 1 received {}\n".format(msg)) 15 | 16 | if __name__ == "__main__": 17 | 18 | main(MPI.COMM_WORLD) 19 | -------------------------------------------------------------------------------- /code/C-MPI/archer2.job: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Slurm job options (name, compute nodes, job time) 4 | #SBATCH --job-name=traffic 5 | #SBATCH --time=00:05:00 6 | #SBATCH --output=%x-%j.out 7 | #SBATCH --error=%x-%j.err 8 | #SBATCH --nodes=2 9 | #SBATCH --tasks-per-node=128 10 | #SBATCH --cpus-per-task=1 11 | #SBATCH --partition=standard 12 | #SBATCH --qos=short 13 | #SBATCH --reservation=shortqos 14 | 15 | export OMP_NUM_THREADS=1 16 | 17 | ulimit -s unlimited 18 | 19 | # Launch the parallel job 20 | 21 | srun --unbuffered --distribution=block:block --hint=nomultithread ./traffic 22 | -------------------------------------------------------------------------------- /code/F-MPI/archer2.job: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Slurm job options (name, compute nodes, job time) 4 | #SBATCH --job-name=traffic 5 | #SBATCH --time=00:05:00 6 | #SBATCH --output=%x-%j.out 7 | #SBATCH --error=%x-%j.err 8 | #SBATCH --nodes=2 9 | #SBATCH --tasks-per-node=128 10 | #SBATCH --cpus-per-task=1 11 | #SBATCH --partition=standard 12 | #SBATCH --qos=short 13 | #SBATCH --reservation=shortqos 14 | 15 | export OMP_NUM_THREADS=1 16 | 17 | ulimit -s unlimited 18 | 19 | # Launch the parallel job 20 | 21 | srun --unbuffered --distribution=block:block --hint=nomultithread ./traffic 22 | -------------------------------------------------------------------------------- /code/C-MPI/Makefile: -------------------------------------------------------------------------------- 1 | MF= Makefile 2 | 3 | CC= mpicc 4 | 5 | CFLAGS= -O3 6 | LFLAGS= $(CFLAGS) -lm 7 | 8 | EXE= traffic 9 | 10 | INC= \ 11 | traffic.h \ 12 | uni.h 13 | 14 | SRC= \ 15 | traffic.c \ 16 | trafficlib.c \ 17 | uni.c 18 | 19 | # 20 | # No need to edit below this line 21 | # 22 | 23 | .SUFFIXES: 24 | .SUFFIXES: .c .o 25 | 26 | OBJ= $(SRC:.c=.o) 27 | 28 | .c.o: 29 | $(CC) $(CFLAGS) -c $< 30 | 31 | all: $(EXE) 32 | 33 | $(OBJ): $(INC) 34 | 35 | $(EXE): $(OBJ) 36 | $(CC) $(CFLAGS) -o $@ $(OBJ) $(LFLAGS) 37 | 38 | $(OBJ): $(MF) 39 | 40 | clean: 41 | rm -f $(OBJ) $(EXE) core 42 | -------------------------------------------------------------------------------- /code/C-SER/Makefile: -------------------------------------------------------------------------------- 1 | MF= Makefile 2 | 3 | CC= gcc 4 | 5 | CFLAGS= -O3 6 | LFLAGS= $(CLFAGS) -lm 7 | 8 | EXE= traffic 9 | 10 | INC= \ 11 | traffic.h \ 12 | uni.h 13 | 14 | SRC= \ 15 | traffic.c \ 16 | trafficlib.c \ 17 | uni.c 18 | 19 | # 20 | # No need to edit below this line 21 | # 22 | 23 | .SUFFIXES: 24 | .SUFFIXES: .c .o 25 | 26 | OBJ= $(SRC:.c=.o) 27 | 28 | .c.o: 29 | $(CC) $(CFLAGS) -c $< 30 | 31 | all: $(EXE) 32 | 33 | $(OBJ): $(INC) 34 | 35 | $(EXE): $(OBJ) 36 | $(CC) $(CFLAGS) -o $@ $(OBJ) $(LFLAGS) 37 | 38 | $(OBJ): $(MF) 39 | 40 | clean: 41 | rm -f $(OBJ) $(EXE) core 42 | -------------------------------------------------------------------------------- /code/C-OMP/Makefile: -------------------------------------------------------------------------------- 1 | MF= Makefile 2 | 3 | CC= gcc 4 | 5 | CFLAGS= -O3 -fopenmp 6 | LFLAGS= $(CFLAGS) -lm 7 | 8 | EXE= traffic 9 | 10 | INC= \ 11 | traffic.h \ 12 | uni.h 13 | 14 | SRC= \ 15 | traffic.c \ 16 | trafficlib.c \ 17 | uni.c 18 | 19 | # 20 | # No need to edit below this line 21 | # 22 | 23 | .SUFFIXES: 24 | .SUFFIXES: .c .o 25 | 26 | OBJ= $(SRC:.c=.o) 27 | 28 | .c.o: 29 | $(CC) $(CFLAGS) -c $< 30 | 31 | all: $(EXE) 32 | 33 | $(OBJ): $(INC) 34 | 35 | $(EXE): $(OBJ) 36 | $(CC) $(CFLAGS) -o $@ $(OBJ) $(LFLAGS) 37 | 38 | $(OBJ): $(MF) 39 | 40 | clean: 41 | rm -f $(OBJ) $(EXE) core 42 | -------------------------------------------------------------------------------- /code/P-MPI/archer2.job: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Slurm job options (name, compute nodes, job time) 4 | #SBATCH --job-name=traffic 5 | #SBATCH --time=00:05:00 6 | #SBATCH --output=%x-%j.out 7 | #SBATCH --error=%x-%j.err 8 | #SBATCH --nodes=1 9 | #SBATCH --tasks-per-node=128 10 | #SBATCH --cpus-per-task=1 11 | #SBATCH --partition=standard 12 | #SBATCH --qos=reservation 13 | #SBATCH --reservation=ta083_797464 14 | 15 | module load cray-python 16 | 17 | # Launch the parallel job on the requested resources 18 | 19 | srun --unbuffered --distribution=block:block --hint=nomultithread \ 20 | python traffic.py 21 | -------------------------------------------------------------------------------- /code/C-OMP/archer2.job: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Slurm job options (name, compute nodes, job time) 4 | #SBATCH --job-name=traffic 5 | #SBATCH --time=00:05:00 6 | #SBATCH --output=%x-%j.out 7 | #SBATCH --error=%x-%j.err 8 | #SBATCH --nodes=1 9 | #SBATCH --tasks-per-node=1 10 | #SBATCH --cpus-per-task=128 11 | #SBATCH --partition=standard 12 | #SBATCH --qos=short 13 | #SBATCH --reservation=shortqos 14 | 15 | export OMP_NUM_THREADS=128 16 | export OMP_PLACES=cores 17 | 18 | ulimit -s unlimited 19 | 20 | # Launch the parallel job 21 | 22 | srun --unbuffered --distribution=block:block --hint=nomultithread ./traffic 23 | -------------------------------------------------------------------------------- /code/F-OMP/archer2.job: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Slurm job options (name, compute nodes, job time) 4 | #SBATCH --job-name=traffic 5 | #SBATCH --time=00:05:00 6 | #SBATCH --output=%x-%j.out 7 | #SBATCH --error=%x-%j.err 8 | #SBATCH --nodes=1 9 | #SBATCH --tasks-per-node=1 10 | #SBATCH --cpus-per-task=128 11 | #SBATCH --partition=standard 12 | #SBATCH --qos=short 13 | #SBATCH --reservation=shortqos 14 | 15 | export OMP_NUM_THREADS=128 16 | export OMP_PLACES=cores 17 | 18 | ulimit -s unlimited 19 | 20 | # Launch the parallel job 21 | 22 | srun --unbuffered --distribution=block:block --hint=nomultithread ./traffic 23 | -------------------------------------------------------------------------------- /code/F-MPI/Makefile: -------------------------------------------------------------------------------- 1 | MF= Makefile 2 | 3 | FC= mpif90 4 | 5 | FFLAGS= -O3 6 | LFLAGS= $(FFLAGS) 7 | 8 | EXE= traffic 9 | 10 | SRC= \ 11 | traffic.f90 \ 12 | trafficlib.f90 \ 13 | unirand.f90 14 | 15 | # 16 | # No need to edit below this line 17 | # 18 | 19 | .SUFFIXES: 20 | .SUFFIXES: .f90 .o 21 | 22 | OBJ= $(SRC:.f90=.o) 23 | MOD= $(SRC:.f90=.mod) 24 | 25 | .f90.o: 26 | $(FC) $(FFLAGS) -c $< 27 | 28 | all: $(EXE) 29 | 30 | $(EXE): $(OBJ) 31 | $(FC) $(FFLAGS) -o $@ $(OBJ) $(LFLAGS) 32 | 33 | $(OBJ): $(MF) 34 | 35 | traffic.o: trafficlib.o 36 | trafficlib.o: unirand.o 37 | 38 | clean: 39 | rm -f $(OBJ) $(MOD) $(EXE) core 40 | -------------------------------------------------------------------------------- /code/F-SER/Makefile: -------------------------------------------------------------------------------- 1 | MF= Makefile 2 | 3 | FC= gfortran 4 | 5 | FFLAGS= -O3 6 | LFLAGS= $(FFLAGS) 7 | 8 | EXE= traffic 9 | 10 | SRC= \ 11 | traffic.f90 \ 12 | trafficlib.f90 \ 13 | unirand.f90 14 | 15 | # 16 | # No need to edit below this line 17 | # 18 | 19 | .SUFFIXES: 20 | .SUFFIXES: .f90 .o 21 | 22 | OBJ= $(SRC:.f90=.o) 23 | MOD= $(SRC:.f90=.mod) 24 | 25 | .f90.o: 26 | $(FC) $(FFLAGS) -c $< 27 | 28 | all: $(EXE) 29 | 30 | $(EXE): $(OBJ) 31 | $(FC) $(FFLAGS) -o $@ $(OBJ) $(LFLAGS) 32 | 33 | $(OBJ): $(MF) 34 | 35 | trafficlib.o: unirand.o 36 | traffic.o: trafficlib.o 37 | 38 | clean: 39 | rm -f $(OBJ) $(MOD) $(EXE) core 40 | -------------------------------------------------------------------------------- /code/F-OMP/Makefile: -------------------------------------------------------------------------------- 1 | MF= Makefile 2 | 3 | FC= gfortran 4 | 5 | FFLAGS= -O3 -fopenmp 6 | LFLAGS= $(FFLAGS) 7 | 8 | EXE= traffic 9 | 10 | SRC= \ 11 | traffic.f90 \ 12 | trafficlib.f90 \ 13 | unirand.f90 14 | 15 | # 16 | # No need to edit below this line 17 | # 18 | 19 | .SUFFIXES: 20 | .SUFFIXES: .f90 .o 21 | 22 | OBJ= $(SRC:.f90=.o) 23 | MOD= $(SRC:.f90=.mod) 24 | 25 | .f90.o: 26 | $(FC) $(FFLAGS) -c $< 27 | 28 | all: $(EXE) 29 | 30 | $(EXE): $(OBJ) 31 | $(FC) $(FFLAGS) -o $@ $(OBJ) $(LFLAGS) 32 | 33 | $(OBJ): $(MF) 34 | 35 | trafficlib.o: unirand.o 36 | traffic.o: trafficlib.o 37 | 38 | clean: 39 | rm -f $(OBJ) $(MOD) $(EXE) core 40 | -------------------------------------------------------------------------------- /notebooks/mpi4py/example2.py: -------------------------------------------------------------------------------- 1 | """A simple blocking Send/Recv pair""" 2 | 3 | import sys 4 | import numpy 5 | from mpi4py import MPI 6 | 7 | def main(comm, sz): 8 | """Send a message between ranks 0 and 1""" 9 | rank = comm.Get_rank() 10 | 11 | if rank == 0: 12 | msg = numpy.ones(sz, numpy.double) 13 | comm.Ssend([msg, sz, MPI.DOUBLE], dest = 1, tag = 999) 14 | elif rank == 1: 15 | msg = numpy.zeros(sz, numpy.double) 16 | comm.Recv([msg, sz, MPI.DOUBLE], source = 0, tag = 999) 17 | sys.stdout.write("Rank 1 received {}".format(msg)) 18 | 19 | if __name__ == "__main__": 20 | 21 | sz = 4 22 | main(MPI.COMM_WORLD, sz) 23 | -------------------------------------------------------------------------------- /notebooks/mpi4py/example6.py: -------------------------------------------------------------------------------- 1 | """Creating a Cartesian Communicator""" 2 | 3 | import sys 4 | from mpi4py import MPI 5 | 6 | def main(parent): 7 | """Create a 2-d Cartesian communicator from parent communicator""" 8 | 9 | dims = MPI.Compute_dims(parent.size, 2) 10 | 11 | comm = parent.Create_cart(dims, periods = [True, False]) 12 | rank = comm.Get_rank() 13 | coords = comm.Get_coords(rank) 14 | upx = comm.Shift(0, 1) 15 | upy = comm.Shift(1, 1) 16 | 17 | out = "Rank{:2d} coords{:2d} {:2d} upx(src,dst) {} upy(src,dst) {}\n"\ 18 | .format(rank, coords[0], coords[1], upx, upy) 19 | sys.stdout.write(out) 20 | 21 | if __name__ == "__main__": 22 | """Execute in MPI.COMM_WORLD""" 23 | main(MPI.COMM_WORLD) 24 | -------------------------------------------------------------------------------- /notebooks/numpy/darts.py: -------------------------------------------------------------------------------- 1 | """ Calculate an approximation to pi via Monte Carlo "darts" method""" 2 | 3 | import numpy as np 4 | import timeit 5 | 6 | def calc_pi(ntot): 7 | x = np.random.ranf(ntot) 8 | y = np.random.ranf(ntot) 9 | r = np.sqrt(x*x + y*y) 10 | c = r[ r <= 1.0 ] 11 | return 4.0*float((c.size))/float(ntot) 12 | 13 | 14 | def main(): 15 | """Time result for a number of sample sizes (log spacing)""" 16 | 17 | pts = 6 18 | ntots = np.logspace(1, 8, num = pts, dtype = np.int) 19 | 20 | for n in ntots: 21 | 22 | t = timeit.timeit(stmt = "darts.calc_pi(x)", 23 | setup = "import darts; x =" + str(n), 24 | number = 1) 25 | print("{:9d} {:6.4e} {:10.8f}".format(n, t, calc_pi(n))) 26 | 27 | 28 | if __name__ == "__main__": 29 | main() 30 | -------------------------------------------------------------------------------- /code/P-MPI/trafficlib.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import numpy as np 3 | 4 | def initroad(road, density, seedval): 5 | 6 | # Here we expect a road without halos 7 | 8 | n = len(road) 9 | 10 | np.random.seed(seedval) 11 | 12 | rng = np.random.random(n) 13 | 14 | road[0:n] = np.where(rng[:] < density, 1, 0) 15 | 16 | ncar = np.sum(road, dtype=np.int32) 17 | 18 | return ncar 19 | 20 | 21 | def updateroad(newroad, oldroad): 22 | 23 | n = len(oldroad)-2 24 | 25 | newroad[1:n+1] = np.where(oldroad[1:n+1]==0, oldroad[0:n], oldroad[2:n+2]) 26 | 27 | nmove = np.sum(oldroad[1:n+1] - newroad[1:n+1] == 1, dtype=np.int32) 28 | 29 | return nmove 30 | 31 | 32 | def updatebcs(road): 33 | 34 | n = len(road)-2 35 | 36 | road[0] = road[n] 37 | road[n+1] = road[1] 38 | 39 | 40 | import time 41 | 42 | def gettime(): 43 | 44 | return time.time() 45 | -------------------------------------------------------------------------------- /code/P-SER-NP/trafficlib.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import numpy as np 3 | 4 | def initroad(road, density, seedval): 5 | 6 | # Here we expect a road without halos 7 | 8 | n = len(road) 9 | 10 | np.random.seed(seedval) 11 | 12 | rng = np.random.random(n) 13 | 14 | road[0:n] = np.where(rng[:] < density, 1, 0) 15 | 16 | ncar = np.sum(road, dtype=np.int32) 17 | 18 | return ncar 19 | 20 | 21 | def updateroad(newroad, oldroad): 22 | 23 | n = len(oldroad)-2 24 | 25 | newroad[1:n+1] = np.where(oldroad[1:n+1]==0, oldroad[0:n], oldroad[2:n+2]) 26 | 27 | nmove = np.sum(oldroad[1:n+1] - newroad[1:n+1] == 1, dtype=np.int32) 28 | 29 | return nmove 30 | 31 | 32 | def updatebcs(road): 33 | 34 | n = len(road)-2 35 | 36 | road[0] = road[n] 37 | road[n+1] = road[1] 38 | 39 | 40 | import time 41 | 42 | def gettime(): 43 | 44 | return time.time() 45 | -------------------------------------------------------------------------------- /notebooks/trafficmpi/trafficlib.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import numpy as np 3 | 4 | def initroad(road, density, seedval): 5 | 6 | # Here we expect a road without halos 7 | 8 | n = len(road) 9 | 10 | np.random.seed(seedval) 11 | 12 | rng = np.random.random(n) 13 | 14 | road[0:n] = np.where(rng[:] < density, 1, 0) 15 | 16 | ncar = np.sum(road) 17 | 18 | return ncar 19 | 20 | 21 | def updateroad(newroad, oldroad): 22 | 23 | n = len(oldroad)-2 24 | 25 | newroad[1:n+1] = np.where(oldroad[1:n+1]==0, oldroad[0:n], oldroad[2:n+2]) 26 | 27 | nmove = (newroad[1:n+1] != oldroad[1:n+1]).sum(dtype=int) 28 | nmove = nmove / 2 29 | 30 | return nmove 31 | 32 | 33 | def updatebcs(road): 34 | 35 | n = len(road)-2 36 | 37 | road[0] = road[n] 38 | road[n+1] = road[1] 39 | 40 | 41 | import time 42 | 43 | def gettime(): 44 | 45 | return time.time() 46 | -------------------------------------------------------------------------------- /notebooks/trafficnumpy/trafficlib.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import numpy as np 3 | 4 | def initroad(road, density, seedval): 5 | 6 | # Here we expect a road without halos 7 | 8 | n = len(road) 9 | 10 | np.random.seed(seedval) 11 | 12 | rng = np.random.random(n) 13 | 14 | road[0:n] = np.where(rng[:] < density, 1, 0) 15 | 16 | ncar = np.sum(road) 17 | 18 | return ncar 19 | 20 | 21 | def updateroad(newroad, oldroad): 22 | 23 | n = len(oldroad)-2 24 | 25 | newroad[1:n+1] = np.where(oldroad[1:n+1]==0, oldroad[0:n], oldroad[2:n+2]) 26 | 27 | nmove = (newroad[1:n+1] != oldroad[1:n+1]).sum(dtype=int) 28 | nmove = nmove / 2 29 | 30 | return nmove 31 | 32 | 33 | def updatebcs(road): 34 | 35 | n = len(road)-2 36 | 37 | road[0] = road[n] 38 | road[n+1] = road[1] 39 | 40 | 41 | import time 42 | 43 | def gettime(): 44 | 45 | return time.time() 46 | -------------------------------------------------------------------------------- /notebooks/mpi4py/example3.py: -------------------------------------------------------------------------------- 1 | """An Example of non-blocking Isend/Irecv pairs""" 2 | 3 | import sys 4 | import numpy 5 | from mpi4py import MPI 6 | 7 | def main(comm): 8 | """Exchange messages with 'ajoining' ranks""" 9 | p1 = comm.rank + 1 10 | m1 = comm.rank - 1 11 | if p1 >= comm.size: p1 = 0 12 | if m1 < 0: m1 = comm.size - 1 13 | 14 | smsg = numpy.array([comm.rank], numpy.int32) 15 | rmsg = numpy.zeros(2, numpy.int32) 16 | 17 | reqs1 = comm.Issend(smsg, p1) 18 | reqs2 = comm.Issend(smsg, m1) 19 | reqr1 = comm.Irecv(rmsg[0:], source = p1) 20 | reqr2 = comm.Irecv(rmsg[1:], source = m1) 21 | 22 | reqr1.Wait(); reqr2.Wait() 23 | sys.stdout.write("Rank {} received messages from ranks {} and {}\n"\ 24 | .format(smsg, rmsg[1], rmsg[0])) 25 | 26 | MPI.Request.Waitall([reqs1, reqs2]) 27 | 28 | if __name__ == "__main__": 29 | 30 | main(MPI.COMM_WORLD) 31 | -------------------------------------------------------------------------------- /code/C-MPI/trafficlib.c: -------------------------------------------------------------------------------- 1 | #include "uni.h" 2 | 3 | int initroad(int *road, int n, float density, int seed) 4 | { 5 | int i, ncar; 6 | float rng; 7 | 8 | // seed random number generator 9 | 10 | rinit(seed); 11 | 12 | ncar = 0; 13 | 14 | for (i=0; i < n; i++) 15 | { 16 | rng = uni(); 17 | 18 | if (rng < density) 19 | { 20 | road[i] = 1; 21 | } 22 | else 23 | { 24 | road[i] = 0; 25 | } 26 | 27 | ncar += road[i]; 28 | } 29 | 30 | return ncar; 31 | } 32 | 33 | 34 | int updateroad(int *newroad, int *oldroad, int n) 35 | { 36 | int i, nmove; 37 | 38 | nmove = 0; 39 | 40 | for (i=1; i<=n; i++) 41 | { 42 | if (oldroad[i] == 1) 43 | { 44 | if (oldroad[i+1] == 1) 45 | { 46 | newroad[i] = 1; 47 | } 48 | else 49 | { 50 | newroad[i] = 0; 51 | nmove++; 52 | } 53 | } 54 | else 55 | { 56 | if (oldroad[i-1] == 1) 57 | { 58 | newroad[i] = 1; 59 | } 60 | else 61 | { 62 | newroad[i] = 0; 63 | } 64 | } 65 | } 66 | 67 | return nmove; 68 | } 69 | -------------------------------------------------------------------------------- /code/C-SER/trafficlib.c: -------------------------------------------------------------------------------- 1 | #include "uni.h" 2 | 3 | int initroad(int *road, int n, float density, int seed) 4 | { 5 | int i, ncar; 6 | float rng; 7 | 8 | // seed random number generator 9 | 10 | rinit(seed); 11 | 12 | ncar = 0; 13 | 14 | for (i=0; i < n; i++) 15 | { 16 | rng = uni(); 17 | 18 | if (rng < density) 19 | { 20 | road[i] = 1; 21 | } 22 | else 23 | { 24 | road[i] = 0; 25 | } 26 | 27 | ncar += road[i]; 28 | } 29 | 30 | return ncar; 31 | } 32 | 33 | 34 | int updateroad(int *newroad, int *oldroad, int n) 35 | { 36 | int i, nmove; 37 | 38 | nmove = 0; 39 | 40 | for (i=1; i<=n; i++) 41 | { 42 | if (oldroad[i] == 1) 43 | { 44 | if (oldroad[i+1] == 1) 45 | { 46 | newroad[i] = 1; 47 | } 48 | else 49 | { 50 | newroad[i] = 0; 51 | nmove++; 52 | } 53 | } 54 | else 55 | { 56 | if (oldroad[i-1] == 1) 57 | { 58 | newroad[i] = 1; 59 | } 60 | else 61 | { 62 | newroad[i] = 0; 63 | } 64 | } 65 | } 66 | 67 | return nmove; 68 | } 69 | 70 | 71 | void updatebcs(int *road, int n) 72 | { 73 | road[0] = road[n]; 74 | road[n+1] = road[1]; 75 | } 76 | 77 | 78 | #include 79 | #include 80 | 81 | double gettime() 82 | { 83 | struct timeval tp; 84 | gettimeofday (&tp, NULL); 85 | return tp.tv_sec + tp.tv_usec/(double)1.0e6; 86 | } 87 | -------------------------------------------------------------------------------- /code/C-OMP/trafficlib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "uni.h" 3 | 4 | int initroad(int *road, int n, float density, int seed) 5 | { 6 | int i, ncar; 7 | float rng; 8 | 9 | // seed random number generator 10 | 11 | rinit(seed); 12 | 13 | ncar = 0; 14 | 15 | for (i=0; i < n; i++) 16 | { 17 | rng = uni(); 18 | 19 | if (rng < density) 20 | { 21 | road[i] = 1; 22 | } 23 | else 24 | { 25 | road[i] = 0; 26 | } 27 | 28 | ncar += road[i]; 29 | } 30 | 31 | return ncar; 32 | } 33 | 34 | 35 | int updateroad(int *newroad, int *oldroad, int n) 36 | { 37 | int i, nmove; 38 | 39 | nmove = 0; 40 | 41 | #pragma omp parallel for default(none) \ 42 | shared(n, oldroad, newroad) \ 43 | reduction(+:nmove) 44 | 45 | for (i=1; i<=n; i++) 46 | { 47 | if (oldroad[i] == 1) 48 | { 49 | if (oldroad[i+1] == 1) 50 | { 51 | newroad[i] = 1; 52 | } 53 | else 54 | { 55 | newroad[i] = 0; 56 | nmove++; 57 | } 58 | } 59 | else 60 | { 61 | if (oldroad[i-1] == 1) 62 | { 63 | newroad[i] = 1; 64 | } 65 | else 66 | { 67 | newroad[i] = 0; 68 | } 69 | } 70 | } 71 | 72 | return nmove; 73 | } 74 | 75 | 76 | void updatebcs(int *road, int n) 77 | { 78 | road[0] = road[n]; 79 | road[n+1] = road[1]; 80 | } 81 | 82 | 83 | double gettime() 84 | { 85 | return(omp_get_wtime()); 86 | } 87 | -------------------------------------------------------------------------------- /code/P-SER-NP/traffic.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import time 5 | import numpy as np 6 | 7 | from trafficlib import initroad, updatebcs, updateroad, gettime 8 | 9 | def main(argv): 10 | 11 | # Simulation parameters 12 | seedval = 5743 13 | ncell = 10240000 14 | maxiter = 1024000000//ncell 15 | printfreq = maxiter//10 16 | 17 | newroad = np.zeros(ncell+2, dtype=np.int32) 18 | oldroad = np.zeros(ncell+2, dtype=np.int32) 19 | 20 | density = 0.52 21 | 22 | print(f"Length of road is {ncell}") 23 | print(f"Number of iterations is {maxiter}") 24 | print(f"Target density of cars is {density}") 25 | 26 | # Initialise road accordingly using random number generator 27 | print(f"Initialising ...") 28 | 29 | ncars = initroad(oldroad[1:ncell+1], density, seedval) 30 | 31 | print(f"Actual Density of cars is {format(float(ncars)/float(ncell))}\n") 32 | 33 | tstart = gettime() 34 | 35 | for iter in range(1, maxiter+1): 36 | 37 | updatebcs(oldroad) 38 | 39 | nmove = updateroad(newroad, oldroad) 40 | 41 | # Copy new to old array 42 | oldroad[1:ncell+1] = newroad[1:ncell+1] 43 | 44 | if iter % printfreq == 0: 45 | 46 | print(f"At iteration {iter} average velocity is {float(nmove)/float(ncars):.6f}") 47 | 48 | tstop = gettime() 49 | 50 | print(f"\nFinished\n") 51 | print(f"Time taken was {tstop-tstart:.2f} seconds") 52 | print(f"Update rate was {1.0e-6*ncell*maxiter/(tstop-tstart):.2f} MCOPs") 53 | 54 | if __name__ == "__main__": 55 | main(sys.argv[1:]) 56 | -------------------------------------------------------------------------------- /code/P-SER/trafficlib.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def initroad(road, density, seedval): 4 | 5 | # Here we expect a road without halos 6 | 7 | np.random.seed(seedval) 8 | 9 | n = len(road) 10 | 11 | ncar = 0 12 | 13 | for i in range(0, n): 14 | 15 | rng = np.random.random() 16 | 17 | if rng < density: 18 | road[i] = 1 19 | else: 20 | road[i] = 0 21 | 22 | ncar = ncar + road[i] 23 | 24 | return ncar 25 | 26 | 27 | def updateroad(newroad, oldroad): 28 | 29 | n = len(oldroad)-2 30 | 31 | nmove = 0 32 | 33 | for i in range(1, n+1): 34 | 35 | if oldroad[i] == 1: 36 | if oldroad[i+1] == 1: 37 | newroad[i] = 1 38 | else: 39 | newroad[i] = 0 40 | nmove = nmove + 1 41 | else: 42 | if oldroad[i-1] == 1: 43 | newroad[i] = 1 44 | else: 45 | newroad[i] = 0 46 | 47 | # 48 | # The version below will be easier to express as array operations 49 | # as it has fewer "if" statements. 50 | # 51 | # if oldroad[i] == 0: 52 | # newroad[i] = oldroad[i-1] 53 | # else: 54 | # newroad[i] = oldroad[i+1] 55 | # 56 | # if oldroad[i] - newroad[i] == 1: 57 | # nmove = nmove + 1 58 | 59 | return nmove 60 | 61 | 62 | def updatebcs(road): 63 | 64 | n = len(road)-2 65 | 66 | road[0] = road[n] 67 | road[n+1] = road[1] 68 | 69 | 70 | import time 71 | 72 | def gettime(): 73 | 74 | return time.time() 75 | -------------------------------------------------------------------------------- /notebooks/trafficnumpy/traffic.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import time 5 | import numpy as np 6 | 7 | from trafficlib import initroad, updatebcs, updateroad, gettime 8 | 9 | def main(argv): 10 | 11 | # Simulation parameters 12 | seedval = 5743 13 | ncell = 10240000 14 | maxiter = 1024000000//ncell 15 | printfreq = maxiter//10 16 | 17 | newroad = np.zeros(ncell+2, dtype=np.int32) 18 | oldroad = np.zeros(ncell+2, dtype=np.int32) 19 | 20 | density = 0.52 21 | 22 | print(f"Length of road is {ncell}") 23 | print(f"Number of iterations is {maxiter}") 24 | print(f"Target density of cars is {density}") 25 | 26 | # Initialise road accordingly using random number generator 27 | print(f"Initialising ...") 28 | 29 | ncars = initroad(oldroad[1:ncell+1], density, seedval) 30 | 31 | print(f"Actual Density of cars is {format(float(ncars)/float(ncell))}\n") 32 | 33 | tstart = gettime() 34 | 35 | for iter in range(1, maxiter+1): 36 | 37 | updatebcs(oldroad) 38 | 39 | nmove = updateroad(newroad, oldroad) 40 | 41 | # Copy new to old array 42 | oldroad[1:ncell+1] = newroad[1:ncell+1] 43 | 44 | if iter % printfreq == 0: 45 | 46 | print(f"At iteration {iter} average velocity is {float(nmove)/float(ncars):.6f}") 47 | 48 | tstop = gettime() 49 | 50 | print(f"\nFinished\n") 51 | print(f"Time taken was {tstop-tstart:.2f} seconds") 52 | print(f"Update rate was {1.0e-6*ncell*maxiter/(tstop-tstart):.2f} MCOPs") 53 | 54 | if __name__ == "__main__": 55 | main(sys.argv[1:]) 56 | -------------------------------------------------------------------------------- /notebooks/traffic/trafficlib.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def initroad(road, density, seedval): 4 | 5 | # Here we expect a road without halos 6 | 7 | np.random.seed(seedval) 8 | 9 | n = len(road) 10 | 11 | ncar = 0 12 | 13 | for i in range(0, n): 14 | 15 | rng = np.random.random() 16 | 17 | if rng < density: 18 | road[i] = 1 19 | else: 20 | road[i] = 0 21 | 22 | ncar = ncar + road[i] 23 | 24 | return ncar 25 | 26 | 27 | def updateroad(newroad, oldroad): 28 | 29 | n = len(oldroad)-2 30 | 31 | nmove = 0 32 | 33 | for i in range(1, n+1): 34 | 35 | if oldroad[i] == 1: 36 | if oldroad[i+1] == 1: 37 | newroad[i] = 1 38 | else: 39 | newroad[i] = 0 40 | nmove = nmove + 1 41 | else: 42 | if oldroad[i-1] == 1: 43 | newroad[i] = 1 44 | else: 45 | newroad[i] = 0 46 | 47 | # 48 | # The version below will be easier to express as array operations 49 | # as it has fewer "if" statements. 50 | # 51 | # if oldroad[i] == 0: 52 | # newroad[i] = oldroad[i-1] 53 | # else: 54 | # newroad[i] = oldroad[i+1] 55 | # 56 | # if newroad[i] != oldroad[i]: 57 | # nmove = nmove + 1 58 | # 59 | # nmove = nmove/2 60 | # 61 | 62 | return nmove 63 | 64 | 65 | def updatebcs(road): 66 | 67 | n = len(road)-2 68 | 69 | road[0] = road[n] 70 | road[n+1] = road[1] 71 | 72 | 73 | import time 74 | 75 | def gettime(): 76 | 77 | return time.time() 78 | -------------------------------------------------------------------------------- /code/P-SER/traffic.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import time 5 | 6 | from trafficlib import initroad, updatebcs, updateroad, gettime 7 | 8 | def main(argv): 9 | 10 | # Simulation parameters 11 | seedval = 5743 12 | ncell = 10240000 13 | maxiter = 1024000000//ncell 14 | printfreq = maxiter//10 15 | 16 | tmproad = [0 for col in range(ncell)] 17 | newroad = [0 for col in range(ncell+2)] 18 | oldroad = [0 for col in range(ncell+2)] 19 | 20 | density = 0.52 21 | 22 | print(f"Length of road is {ncell}") 23 | print(f"Number of iterations is {maxiter}") 24 | print(f"Target density of cars is {density}") 25 | 26 | # Initialise road accordingly using random number generator 27 | print(f"Initialising ...") 28 | 29 | ncars = initroad(tmproad, density, seedval) 30 | 31 | print(f"Actual Density of cars is {float(ncars)/float(ncell)}\n") 32 | 33 | oldroad[1:ncell+1] = tmproad 34 | 35 | tstart = gettime() 36 | 37 | for iter in range(1, maxiter+1): 38 | 39 | updatebcs(oldroad) 40 | 41 | nmove = updateroad(newroad, oldroad) 42 | 43 | # Copy new to old array 44 | for i in range(1, ncell+1): 45 | oldroad[i] = newroad[i] 46 | 47 | if iter % printfreq == 0: 48 | 49 | print(f"At iteration {iter} average velocity is {float(nmove)/float(ncars):.6f}") 50 | 51 | tstop = gettime() 52 | 53 | print(f"\nFinished\n") 54 | print(f"Time taken was {tstop-tstart:.2f} seconds") 55 | print(f"Update rate was {1.0e-6*ncell*maxiter/(tstop-tstart):.2f} MCOPs") 56 | 57 | if __name__ == "__main__": 58 | main(sys.argv[1:]) 59 | -------------------------------------------------------------------------------- /notebooks/traffic/traffic.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import time 5 | 6 | from trafficlib import initroad, updatebcs, updateroad, gettime 7 | 8 | def main(argv): 9 | 10 | # Simulation parameters 11 | seedval = 5743 12 | ncell = 10240000 13 | maxiter = 1024000000//ncell 14 | printfreq = maxiter//10 15 | 16 | tmproad = [0 for col in range(ncell)] 17 | newroad = [0 for col in range(ncell+2)] 18 | oldroad = [0 for col in range(ncell+2)] 19 | 20 | density = 0.52 21 | 22 | print(f"Length of road is {ncell}") 23 | print(f"Number of iterations is {maxiter}") 24 | print(f"Target density of cars is {density}") 25 | 26 | # Initialise road accordingly using random number generator 27 | print(f"Initialising ...") 28 | 29 | ncars = initroad(tmproad, density, seedval) 30 | 31 | print(f"Actual Density of cars is {float(ncars)/float(ncell)}\n") 32 | 33 | oldroad[1:ncell+1] = tmproad 34 | 35 | tstart = gettime() 36 | 37 | for iter in range(1, maxiter+1): 38 | 39 | updatebcs(oldroad) 40 | 41 | nmove = updateroad(newroad, oldroad) 42 | 43 | # Copy new to old array 44 | for i in range(1, ncell+1): 45 | oldroad[i] = newroad[i] 46 | 47 | if iter % printfreq == 0: 48 | 49 | print(f"At iteration {iter} average velocity is {float(nmove)/float(ncars):.6f}") 50 | 51 | tstop = gettime() 52 | 53 | print(f"\nFinished\n") 54 | print(f"Time taken was {tstop-tstart:.2f} seconds") 55 | print(f"Update rate was {1.0e-6*ncell*maxiter/(tstop-tstart):.2f} MCOPs") 56 | 57 | if __name__ == "__main__": 58 | main(sys.argv[1:]) 59 | -------------------------------------------------------------------------------- /code/C-SER/traffic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "traffic.h" 5 | 6 | int main(int argc, char **argv) 7 | { 8 | // Set the size of the road 9 | 10 | int ncell = 10240000; 11 | 12 | int *oldroad, *newroad; 13 | 14 | int i, iter, nmove, ncars; 15 | int maxiter, printfreq; 16 | 17 | float density; 18 | 19 | double tstart, tstop; 20 | 21 | oldroad = (int *) malloc((ncell+2)*sizeof(int)); 22 | newroad = (int *) malloc((ncell+2)*sizeof(int)); 23 | 24 | maxiter = 1.024e9/((double) ncell); 25 | printfreq = maxiter/10; 26 | 27 | // Set target density of cars 28 | 29 | density = 0.52; 30 | 31 | printf("Length of road is %d\n", ncell); 32 | printf("Number of iterations is %d \n", maxiter); 33 | printf("Target density of cars is %f \n", density); 34 | 35 | // Initialise road accordingly using random number generator 36 | 37 | for (i=1; i <= ncell; i++) 38 | { 39 | oldroad[i] = 0; 40 | newroad[i] = 0; 41 | } 42 | 43 | printf("Initialising road ...\n"); 44 | 45 | ncars = initroad(&oldroad[1], ncell, density, SEED); 46 | 47 | printf("...done\n"); 48 | 49 | printf("Actual density of cars is %f\n\n", (float) ncars / (float) ncell); 50 | 51 | tstart = gettime(); 52 | 53 | for (iter=1; iter<=maxiter; iter++) 54 | { 55 | updatebcs(oldroad, ncell); 56 | 57 | // Apply CA rules to all cells 58 | 59 | nmove = updateroad(newroad, oldroad, ncell); 60 | 61 | // Copy new to old array 62 | 63 | for (i=1; i<=ncell; i++) 64 | { 65 | oldroad[i] = newroad[i]; 66 | } 67 | 68 | if (iter%printfreq == 0) 69 | { 70 | printf("At iteration %d average velocity is %f \n", 71 | iter, (float) nmove / (float) ncars); 72 | } 73 | } 74 | 75 | tstop = gettime(); 76 | 77 | free(oldroad); 78 | free(newroad); 79 | 80 | printf("\nFinished\n"); 81 | printf("\nTime taken was %f seconds\n", tstop-tstart); 82 | printf("Update rate was %f MCOPs\n\n", \ 83 | 1.e-6*((double) ncell)*((double) maxiter)/(tstop-tstart)); 84 | } 85 | -------------------------------------------------------------------------------- /code/F-OMP/trafficlib.f90: -------------------------------------------------------------------------------- 1 | module trafficlib 2 | 3 | use unirand 4 | use omp_lib 5 | 6 | implicit none 7 | 8 | integer, parameter :: seed = 5743 9 | 10 | contains 11 | 12 | integer function initroad(road, n, density, seedval) 13 | 14 | integer :: n, seedval 15 | integer :: road(n) 16 | real :: density 17 | 18 | integer :: i, ncar 19 | real :: rng 20 | 21 | call rinit(seedval) 22 | 23 | ncar = 0 24 | 25 | do i = 1, n 26 | 27 | rng = uni() 28 | 29 | if (rng .lt. density) then 30 | 31 | road(i) = 1 32 | 33 | else 34 | 35 | road(i) = 0 36 | 37 | end if 38 | 39 | ncar = ncar + road(i) 40 | 41 | end do 42 | 43 | initroad = ncar 44 | 45 | end function initroad 46 | 47 | 48 | integer function updateroad(newroad, oldroad, n) 49 | 50 | integer :: n 51 | integer :: newroad(0:n+1), oldroad(0:n+1) 52 | 53 | integer :: i, nmove 54 | 55 | nmove = 0 56 | 57 | !$omp parallel do default(none) shared(n, oldroad, newroad) reduction(+:nmove) 58 | 59 | do i = 1, n 60 | 61 | if (oldroad(i) .eq. 1) then 62 | 63 | if (oldroad(i+1) .eq. 1) then 64 | 65 | newroad(i) = 1 66 | 67 | else 68 | 69 | newroad(i) = 0 70 | nmove = nmove + 1 71 | 72 | end if 73 | 74 | else 75 | 76 | if (oldroad(i-1) .eq. 1) then 77 | 78 | newroad(i) = 1 79 | 80 | else 81 | 82 | newroad(i) = 0 83 | 84 | end if 85 | 86 | end if 87 | 88 | end do 89 | 90 | updateroad = nmove 91 | 92 | end function updateroad 93 | 94 | 95 | subroutine updatebcs(road, n) 96 | 97 | integer :: n 98 | integer :: road(0:n+1) 99 | 100 | road(0) = road(n) 101 | road(n+1) = road(1) 102 | 103 | end subroutine updatebcs 104 | 105 | double precision function gettime() 106 | 107 | gettime = omp_get_wtime() 108 | 109 | end function gettime 110 | 111 | end module trafficlib 112 | -------------------------------------------------------------------------------- /code/F-SER/traffic.f90: -------------------------------------------------------------------------------- 1 | program traffic 2 | 3 | use trafficlib 4 | 5 | implicit none 6 | 7 | ! Set the size of the road 8 | 9 | integer :: ncell = 10240000 10 | integer :: maxiter, printfreq 11 | 12 | integer :: i, iter, nmove, ncars 13 | real :: density 14 | 15 | integer, allocatable, dimension(:) :: newroad, oldroad 16 | 17 | double precision :: tstart, tstop 18 | 19 | maxiter = 1.024e9/float(ncell) 20 | printfreq = maxiter/10 21 | 22 | ! Set target density of cars 23 | 24 | density = 0.52 25 | 26 | write(*,*) 'Length of road is ', ncell 27 | write(*,*) 'Number of iterations is ', maxiter 28 | write(*,*) 'Target density of cars is ', density 29 | 30 | ! Allocate arrays 31 | 32 | allocate(newroad(0:ncell+1)) 33 | allocate(oldroad(0:ncell+1)) 34 | 35 | ! Initialise road accordingly using random number generator 36 | 37 | do i = 1, ncell 38 | oldroad(i) = 0 39 | newroad(i) = 0 40 | end do 41 | 42 | write(*,*) 'Initialising ...' 43 | 44 | ncars = initroad(oldroad(1), ncell, density, seed) 45 | 46 | write(*,*) '... done' 47 | 48 | write(*,*) 'Actual density of cars is ', float(ncars)/float(ncell) 49 | write(*,*) 50 | 51 | tstart = gettime() 52 | 53 | do iter = 1, maxiter 54 | 55 | call updatebcs(oldroad, ncell) 56 | 57 | nmove = updateroad(newroad, oldroad, ncell) 58 | 59 | ! Copy new to old array 60 | 61 | do i = 1, ncell 62 | 63 | oldroad(i) = newroad(i) 64 | 65 | end do 66 | 67 | if (mod(iter, printfreq) .eq. 0) then 68 | 69 | write(*,*) 'At iteration ', iter, ' average velocity is ', & 70 | float(nmove)/float(ncars) 71 | end if 72 | 73 | end do 74 | 75 | tstop = gettime() 76 | 77 | deallocate(newroad) 78 | deallocate(oldroad) 79 | 80 | write(*,*) 81 | write(*,*) 'Finished' 82 | write(*,*) 83 | write(*,*) 'Time taken was ', tstop - tstart, ' seconds' 84 | write(*,*) 'Update rate was ', & 85 | 1.d-6*float(ncell)*float(maxiter)/(tstop-tstart), ' MCOPs' 86 | write(*,*) 87 | 88 | end program traffic 89 | -------------------------------------------------------------------------------- /code/F-MPI/unirand.f90: -------------------------------------------------------------------------------- 1 | module unirand 2 | 3 | implicit none 4 | 5 | ! Some magic constants 6 | 7 | real, private, parameter :: cd = ( 7654321.0/16777216.0), & 8 | cm = (16777213.0/16777216.0) 9 | 10 | ! The state variables 11 | 12 | integer, private, parameter :: nstate = 97 13 | 14 | real, private, dimension(nstate) :: u 15 | integer, private :: iu, ju 16 | real, private :: c 17 | 18 | ! The routines 19 | 20 | public :: uni, rinit 21 | private :: rstart 22 | 23 | contains 24 | 25 | real function uni() 26 | 27 | ! First call rstart(i,j,k,l), with i,j,k,l integers from 1...168 NOT all 1 28 | 29 | uni = u(iu) - u(ju) 30 | 31 | if (uni < 0.0) uni = uni + 1.0 32 | 33 | u(iu) = uni 34 | 35 | iu = iu - 1 36 | if (iu == 0) iu = nstate 37 | 38 | ju = ju - 1 39 | if (ju == 0) ju = nstate 40 | 41 | c = c - cd 42 | if (c < 0.0) c = c + cm 43 | 44 | uni = uni - c 45 | if (uni < 0.0) uni = uni + 1.0 46 | 47 | end function uni 48 | 49 | ! 50 | ! The algorithm from James RMARIN to generate the 4 seeds from an 51 | ! integer in the range 0 <= seed <= 900,000,000 52 | ! 53 | 54 | subroutine rinit(ijkl) 55 | 56 | integer :: ijkl, ij, kl, i, j, k, l 57 | 58 | ij = ijkl/30082 59 | kl = ijkl - 30082*ij 60 | 61 | i = mod(ij/177,177) + 2 62 | j = mod(ij, 177) + 2 63 | k = mod(kl/169,178) + 1 64 | l = mod(kl, 169) 65 | 66 | call rstart(i, j, k, l) 67 | 68 | end subroutine rinit 69 | 70 | 71 | subroutine rstart(i, j, k, l) 72 | 73 | integer :: i, j, k, l 74 | integer :: ii, jj, m 75 | real :: s, t 76 | 77 | do ii = 1, nstate 78 | 79 | s = 0.0 80 | t = 0.5 81 | 82 | do jj = 1, 24 83 | 84 | m = mod(mod(i*j,179)*k,179) 85 | i = j 86 | j = k 87 | k = m 88 | l = mod(53*L+1,169) 89 | if (mod(l*m,64) >= 32) s = s + t 90 | t = 0.5*t 91 | 92 | end do 93 | 94 | u(ii) = s 95 | 96 | end do 97 | 98 | iu = nstate 99 | ju = 33 100 | c = (362436.0/16777216.0) 101 | 102 | 103 | end subroutine rstart 104 | 105 | end module unirand 106 | -------------------------------------------------------------------------------- /code/F-OMP/unirand.f90: -------------------------------------------------------------------------------- 1 | module unirand 2 | 3 | implicit none 4 | 5 | ! Some magic constants 6 | 7 | real, private, parameter :: cd = ( 7654321.0/16777216.0), & 8 | cm = (16777213.0/16777216.0) 9 | 10 | ! The state variables 11 | 12 | integer, private, parameter :: nstate = 97 13 | 14 | real, private, dimension(nstate) :: u 15 | integer, private :: iu, ju 16 | real, private :: c 17 | 18 | ! The routines 19 | 20 | public :: uni, rinit 21 | private :: rstart 22 | 23 | contains 24 | 25 | real function uni() 26 | 27 | ! First call rstart(i,j,k,l), with i,j,k,l integers from 1...168 NOT all 1 28 | 29 | uni = u(iu) - u(ju) 30 | 31 | if (uni < 0.0) uni = uni + 1.0 32 | 33 | u(iu) = uni 34 | 35 | iu = iu - 1 36 | if (iu == 0) iu = nstate 37 | 38 | ju = ju - 1 39 | if (ju == 0) ju = nstate 40 | 41 | c = c - cd 42 | if (c < 0.0) c = c + cm 43 | 44 | uni = uni - c 45 | if (uni < 0.0) uni = uni + 1.0 46 | 47 | end function uni 48 | 49 | ! 50 | ! The algorithm from James RMARIN to generate the 4 seeds from an 51 | ! integer in the range 0 <= seed <= 900,000,000 52 | ! 53 | 54 | subroutine rinit(ijkl) 55 | 56 | integer :: ijkl, ij, kl, i, j, k, l 57 | 58 | ij = ijkl/30082 59 | kl = ijkl - 30082*ij 60 | 61 | i = mod(ij/177,177) + 2 62 | j = mod(ij, 177) + 2 63 | k = mod(kl/169,178) + 1 64 | l = mod(kl, 169) 65 | 66 | call rstart(i, j, k, l) 67 | 68 | end subroutine rinit 69 | 70 | 71 | subroutine rstart(i, j, k, l) 72 | 73 | integer :: i, j, k, l 74 | integer :: ii, jj, m 75 | real :: s, t 76 | 77 | do ii = 1, nstate 78 | 79 | s = 0.0 80 | t = 0.5 81 | 82 | do jj = 1, 24 83 | 84 | m = mod(mod(i*j,179)*k,179) 85 | i = j 86 | j = k 87 | k = m 88 | l = mod(53*L+1,169) 89 | if (mod(l*m,64) >= 32) s = s + t 90 | t = 0.5*t 91 | 92 | end do 93 | 94 | u(ii) = s 95 | 96 | end do 97 | 98 | iu = nstate 99 | ju = 33 100 | c = (362436.0/16777216.0) 101 | 102 | 103 | end subroutine rstart 104 | 105 | end module unirand 106 | -------------------------------------------------------------------------------- /code/F-SER/unirand.f90: -------------------------------------------------------------------------------- 1 | module unirand 2 | 3 | implicit none 4 | 5 | ! Some magic constants 6 | 7 | real, private, parameter :: cd = ( 7654321.0/16777216.0), & 8 | cm = (16777213.0/16777216.0) 9 | 10 | ! The state variables 11 | 12 | integer, private, parameter :: nstate = 97 13 | 14 | real, private, dimension(nstate) :: u 15 | integer, private :: iu, ju 16 | real, private :: c 17 | 18 | ! The routines 19 | 20 | public :: uni, rinit 21 | private :: rstart 22 | 23 | contains 24 | 25 | real function uni() 26 | 27 | ! First call rstart(i,j,k,l), with i,j,k,l integers from 1...168 NOT all 1 28 | 29 | uni = u(iu) - u(ju) 30 | 31 | if (uni < 0.0) uni = uni + 1.0 32 | 33 | u(iu) = uni 34 | 35 | iu = iu - 1 36 | if (iu == 0) iu = nstate 37 | 38 | ju = ju - 1 39 | if (ju == 0) ju = nstate 40 | 41 | c = c - cd 42 | if (c < 0.0) c = c + cm 43 | 44 | uni = uni - c 45 | if (uni < 0.0) uni = uni + 1.0 46 | 47 | end function uni 48 | 49 | ! 50 | ! The algorithm from James RMARIN to generate the 4 seeds from an 51 | ! integer in the range 0 <= seed <= 900,000,000 52 | ! 53 | 54 | subroutine rinit(ijkl) 55 | 56 | integer :: ijkl, ij, kl, i, j, k, l 57 | 58 | ij = ijkl/30082 59 | kl = ijkl - 30082*ij 60 | 61 | i = mod(ij/177,177) + 2 62 | j = mod(ij, 177) + 2 63 | k = mod(kl/169,178) + 1 64 | l = mod(kl, 169) 65 | 66 | call rstart(i, j, k, l) 67 | 68 | end subroutine rinit 69 | 70 | 71 | subroutine rstart(i, j, k, l) 72 | 73 | integer :: i, j, k, l 74 | integer :: ii, jj, m 75 | real :: s, t 76 | 77 | do ii = 1, nstate 78 | 79 | s = 0.0 80 | t = 0.5 81 | 82 | do jj = 1, 24 83 | 84 | m = mod(mod(i*j,179)*k,179) 85 | i = j 86 | j = k 87 | k = m 88 | l = mod(53*L+1,169) 89 | if (mod(l*m,64) >= 32) s = s + t 90 | t = 0.5*t 91 | 92 | end do 93 | 94 | u(ii) = s 95 | 96 | end do 97 | 98 | iu = nstate 99 | ju = 33 100 | c = (362436.0/16777216.0) 101 | 102 | 103 | end subroutine rstart 104 | 105 | end module unirand 106 | -------------------------------------------------------------------------------- /code/F-OMP/traffic.f90: -------------------------------------------------------------------------------- 1 | program traffic 2 | 3 | use omp_lib 4 | use trafficlib 5 | 6 | implicit none 7 | 8 | ! Set the size of the road 9 | 10 | integer :: ncell = 10240000 11 | integer :: maxiter, printfreq 12 | 13 | integer :: i, iter, nmove, ncars 14 | real :: density 15 | 16 | integer, allocatable, dimension(:) :: newroad, oldroad 17 | 18 | double precision :: tstart, tstop 19 | 20 | maxiter = 1.024e9/ncell 21 | printfreq = maxiter/10 22 | 23 | ! Set target density of cars 24 | 25 | density = 0.52 26 | 27 | write(*,*) 'Length of road is ', ncell 28 | write(*,*) 'Number of iterations is ', maxiter 29 | write(*,*) 'Target density of cars is ', density 30 | 31 | ! Spawn a parallel region so we can compute number of threads 32 | ! Use master directive to ensure only one thread prints to screen 33 | 34 | !$omp parallel 35 | !$omp master 36 | write(*,*) 'Running on ', omp_get_num_threads(), ' thread(s)' 37 | !$omp end master 38 | !$omp end parallel 39 | 40 | ! Parallel region ended - now running on a single thread again 41 | 42 | ! Allocate arrays 43 | 44 | allocate(newroad(0:ncell+1)) 45 | allocate(oldroad(0:ncell+1)) 46 | 47 | ! Initialise road accordingly using random number generator 48 | 49 | do i = 1, ncell 50 | oldroad(i) = 0 51 | newroad(i) = 0 52 | end do 53 | 54 | write(*,*) 'Initialising ...' 55 | 56 | ncars = initroad(oldroad(1), ncell, density, seed) 57 | 58 | write(*,*) '... done' 59 | 60 | write(*,*) 'Actual density of cars is ', float(ncars)/float(ncell) 61 | write(*,*) 62 | 63 | tstart = gettime() 64 | 65 | do iter = 1, maxiter 66 | 67 | call updatebcs(oldroad, ncell) 68 | 69 | nmove = updateroad(newroad, oldroad, ncell) 70 | 71 | ! Copy new to old array 72 | 73 | !$omp parallel do default(none) shared(ncell, oldroad, newroad) 74 | 75 | do i = 1, ncell 76 | 77 | oldroad(i) = newroad(i) 78 | 79 | end do 80 | 81 | if (mod(iter, printfreq) .eq. 0) then 82 | 83 | write(*,*) 'At iteration ', iter, ' average velocity is ', & 84 | float(nmove)/float(ncars) 85 | end if 86 | 87 | end do 88 | 89 | tstop = gettime() 90 | 91 | deallocate(newroad) 92 | deallocate(oldroad) 93 | 94 | write(*,*) 95 | write(*,*) 'Finished' 96 | write(*,*) 97 | write(*,*) 'Time taken was ', tstop - tstart, ' seconds' 98 | write(*,*) 'Update rate was ', & 99 | 1.d-6*float(ncell)*float(maxiter)/(tstop-tstart), ' MCOPs' 100 | write(*,*) 101 | 102 | end program traffic 103 | -------------------------------------------------------------------------------- /code/C-OMP/traffic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "traffic.h" 7 | 8 | int main(int argc, char **argv) 9 | { 10 | // Set the size of the road 11 | 12 | int ncell = 10240000; 13 | 14 | int *oldroad, *newroad; 15 | 16 | int i, iter, nmove, ncars; 17 | int maxiter, printfreq; 18 | 19 | float density; 20 | 21 | double tstart, tstop; 22 | 23 | oldroad = (int *) malloc((ncell+2)*sizeof(int)); 24 | newroad = (int *) malloc((ncell+2)*sizeof(int)); 25 | 26 | maxiter = 1.024e9/((double) ncell); 27 | printfreq = maxiter/10; 28 | 29 | // Set target density of cars 30 | 31 | density = 0.52; 32 | 33 | printf("Length of road is %d\n", ncell); 34 | printf("Number of iterations is %d \n", maxiter); 35 | printf("Target density of cars is %f \n", density); 36 | 37 | // Spawn a parallel region so we can compute number of threads 38 | // Use master directive to ensure only one thread prints to screen 39 | 40 | #pragma omp parallel 41 | { 42 | #pragma omp master 43 | { 44 | printf("Running on %d thread(s)\n", omp_get_num_threads()); 45 | } 46 | } 47 | 48 | // Parallel region ended - now running on a single thread again 49 | 50 | // Initialise road accordingly using random number generator 51 | 52 | for (i=1; i <= ncell; i++) 53 | { 54 | oldroad[i] = 0; 55 | newroad[i] = 0; 56 | } 57 | 58 | printf("Initialising road ...\n"); 59 | 60 | ncars = initroad(&oldroad[1], ncell, density, SEED); 61 | 62 | printf("...done\n"); 63 | 64 | printf("Actual density of cars is %f\n\n", (float) ncars / (float) ncell); 65 | 66 | tstart = gettime(); 67 | 68 | for (iter=1; iter<=maxiter; iter++) 69 | { 70 | updatebcs(oldroad, ncell); 71 | 72 | // Apply CA rules to all cells 73 | 74 | nmove = updateroad(newroad, oldroad, ncell); 75 | 76 | // Copy new to old array 77 | 78 | #pragma omp parallel for default(none) shared(ncell, oldroad, newroad) 79 | 80 | for (i=1; i<=ncell; i++) 81 | { 82 | oldroad[i] = newroad[i]; 83 | } 84 | 85 | if (iter%printfreq == 0) 86 | { 87 | printf("At iteration %d average velocity is %f \n", 88 | iter, (float) nmove / (float) ncars); 89 | } 90 | } 91 | 92 | tstop = gettime(); 93 | 94 | free(oldroad); 95 | free(newroad); 96 | 97 | printf("\nFinished\n"); 98 | printf("\nTime taken was %f seconds\n", tstop-tstart); 99 | printf("Update rate was %f MCOPs\n\n", \ 100 | 1.e-6*((double) ncell)*((double) maxiter)/(tstop-tstart)); 101 | } 102 | -------------------------------------------------------------------------------- /code/F-MPI/trafficlib.f90: -------------------------------------------------------------------------------- 1 | module trafficlib 2 | 3 | use unirand 4 | 5 | implicit none 6 | 7 | integer, parameter :: seed = 5743 8 | 9 | contains 10 | 11 | integer function initroad(road, n, density, seedval) 12 | 13 | integer :: n, seedval 14 | integer :: road(n) 15 | real :: density 16 | 17 | integer :: i, ncar 18 | real :: rng 19 | 20 | call rinit(seedval) 21 | 22 | ncar = 0 23 | 24 | do i = 1, n 25 | 26 | rng = uni() 27 | 28 | if (rng .lt. density) then 29 | 30 | road(i) = 1 31 | 32 | else 33 | 34 | road(i) = 0 35 | 36 | end if 37 | 38 | ncar = ncar + road(i) 39 | 40 | end do 41 | 42 | initroad = ncar 43 | 44 | end function initroad 45 | 46 | 47 | integer function updateroad(newroad, oldroad, n) 48 | 49 | integer :: n 50 | integer :: newroad(0:n+1), oldroad(0:n+1) 51 | 52 | integer :: i, nmove 53 | 54 | nmove = 0 55 | 56 | do i = 1, n 57 | 58 | if (oldroad(i) .eq. 1) then 59 | 60 | if (oldroad(i+1) .eq. 1) then 61 | 62 | newroad(i) = 1 63 | 64 | else 65 | 66 | newroad(i) = 0 67 | nmove = nmove + 1 68 | 69 | end if 70 | 71 | else 72 | 73 | if (oldroad(i-1) .eq. 1) then 74 | 75 | newroad(i) = 1 76 | 77 | else 78 | 79 | newroad(i) = 0 80 | 81 | end if 82 | 83 | end if 84 | 85 | end do 86 | 87 | updateroad = nmove 88 | 89 | ! 90 | ! Equivalent array-syntax code is as follows: 91 | ! 92 | ! where (oldroad(1:n) == 0) 93 | ! 94 | ! newroad(1:n) = oldroad(0:n-1) 95 | ! 96 | ! elsewhere 97 | ! 98 | ! newroad(1:n) = oldroad(2:n+1) 99 | ! 100 | ! end where 101 | ! 102 | ! nmove = count(oldroad(1:n) - newroad(1:n) == 1) 103 | ! 104 | 105 | end function updateroad 106 | 107 | ! The subroutines below are not needed in the MPI parallel version, 108 | ! but are retained here as we want to use exactly the same source file 109 | ! for these library routines between serial and parallel codes. 110 | 111 | subroutine updatebcs(road, n) 112 | 113 | integer :: n 114 | integer :: road(0:n+1) 115 | 116 | road(0) = road(n) 117 | road(n+1) = road(1) 118 | 119 | end subroutine updatebcs 120 | 121 | 122 | double precision function gettime() 123 | 124 | logical, save :: firstcall = .true. 125 | 126 | integer, parameter :: int32kind = selected_int_kind( 9) 127 | integer, parameter :: int64kind = selected_int_kind(18) 128 | 129 | integer, parameter :: intkind = int64kind 130 | 131 | integer(kind = intkind) :: count,rate 132 | 133 | double precision, save :: ticktime 134 | 135 | if (firstcall) then 136 | 137 | firstcall = .false. 138 | 139 | call system_clock(count, rate) 140 | 141 | ticktime = 1.0d0/dble(rate) 142 | gettime = dble(count)*ticktime 143 | 144 | ! write(*,*) 'Clock resolution is ', ticktime*1.0e6, ', usecs' 145 | 146 | else 147 | 148 | call system_clock(count) 149 | 150 | gettime = dble(count)*ticktime 151 | 152 | end if 153 | 154 | end function gettime 155 | 156 | end module trafficlib 157 | -------------------------------------------------------------------------------- /code/F-SER/trafficlib.f90: -------------------------------------------------------------------------------- 1 | module trafficlib 2 | 3 | use unirand 4 | 5 | implicit none 6 | 7 | integer, parameter :: seed = 5743 8 | 9 | contains 10 | 11 | integer function initroad(road, n, density, seedval) 12 | 13 | integer :: n, seedval 14 | integer :: road(n) 15 | real :: density 16 | 17 | integer :: i, ncar 18 | real :: rng 19 | 20 | call rinit(seedval) 21 | 22 | ncar = 0 23 | 24 | do i = 1, n 25 | 26 | rng = uni() 27 | 28 | if (rng .lt. density) then 29 | 30 | road(i) = 1 31 | 32 | else 33 | 34 | road(i) = 0 35 | 36 | end if 37 | 38 | ncar = ncar + road(i) 39 | 40 | end do 41 | 42 | initroad = ncar 43 | 44 | end function initroad 45 | 46 | 47 | integer function updateroad(newroad, oldroad, n) 48 | 49 | integer :: n 50 | integer :: newroad(0:n+1), oldroad(0:n+1) 51 | 52 | integer :: i, nmove 53 | 54 | nmove = 0 55 | 56 | do i = 1, n 57 | 58 | if (oldroad(i) .eq. 1) then 59 | 60 | if (oldroad(i+1) .eq. 1) then 61 | 62 | newroad(i) = 1 63 | 64 | else 65 | 66 | newroad(i) = 0 67 | nmove = nmove + 1 68 | 69 | end if 70 | 71 | else 72 | 73 | if (oldroad(i-1) .eq. 1) then 74 | 75 | newroad(i) = 1 76 | 77 | else 78 | 79 | newroad(i) = 0 80 | 81 | end if 82 | 83 | end if 84 | 85 | end do 86 | 87 | updateroad = nmove 88 | 89 | ! 90 | ! Equivalent array-syntax code is as follows: 91 | ! 92 | ! where (oldroad(1:n) == 0) 93 | ! 94 | ! newroad(1:n) = oldroad(0:n-1) 95 | ! 96 | ! elsewhere 97 | ! 98 | ! newroad(1:n) = oldroad(2:n+1) 99 | ! 100 | ! end where 101 | ! 102 | ! nmove = count(oldroad(1:n) - newroad(1:n) == 1) 103 | ! 104 | 105 | end function updateroad 106 | 107 | ! The subroutines below are not needed in the MPI parallel version, 108 | ! but are retained here as we want to use exactly the same source file 109 | ! for these library routines between serial and parallel codes. 110 | 111 | subroutine updatebcs(road, n) 112 | 113 | integer :: n 114 | integer :: road(0:n+1) 115 | 116 | road(0) = road(n) 117 | road(n+1) = road(1) 118 | 119 | end subroutine updatebcs 120 | 121 | 122 | double precision function gettime() 123 | 124 | logical, save :: firstcall = .true. 125 | 126 | integer, parameter :: int32kind = selected_int_kind( 9) 127 | integer, parameter :: int64kind = selected_int_kind(18) 128 | 129 | integer, parameter :: intkind = int64kind 130 | 131 | integer(kind = intkind) :: count,rate 132 | 133 | double precision, save :: ticktime 134 | 135 | if (firstcall) then 136 | 137 | firstcall = .false. 138 | 139 | call system_clock(count, rate) 140 | 141 | ticktime = 1.0d0/dble(rate) 142 | gettime = dble(count)*ticktime 143 | 144 | ! write(*,*) 'Clock resolution is ', ticktime*1.0e6, ', usecs' 145 | 146 | else 147 | 148 | call system_clock(count) 149 | 150 | gettime = dble(count)*ticktime 151 | 152 | end if 153 | 154 | end function gettime 155 | 156 | end module trafficlib 157 | -------------------------------------------------------------------------------- /code/P-MPI/traffic.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import time 5 | import numpy as np 6 | 7 | from mpi4py import MPI 8 | 9 | from trafficlib import initroad, updatebcs, updateroad, gettime 10 | 11 | def main(argv): 12 | 13 | comm = MPI.COMM_WORLD 14 | 15 | size = comm.Get_size() 16 | rank = comm.Get_rank() 17 | 18 | # Simulation parameters 19 | seedval = 5743 20 | ncell = 10240000 21 | maxiter = 1024000000//ncell 22 | printfreq = maxiter//10 23 | 24 | nlocal = ncell//size 25 | 26 | # Check consistency 27 | 28 | if (nlocal*size != ncell): 29 | if (rank == 0): 30 | print(f"ERROR: ncell = {ncell} not a multiple of size = {size}") 31 | exit() 32 | 33 | bigroad = np.zeros(ncell,dtype=np.int32) 34 | newroad = np.zeros(nlocal+2,dtype=np.int32) 35 | oldroad = np.zeros(nlocal+2,dtype=np.int32) 36 | 37 | sbuf = np.zeros(1) 38 | rbuf = np.zeros(1) 39 | 40 | density = 0.52 41 | 42 | if (rank == 0): 43 | 44 | print(f"Length of road is {ncell}") 45 | print(f"Number of iterations is {maxiter}") 46 | print(f"Target density of cars is {density}") 47 | print(f"Running on {size} process(es)") 48 | 49 | # Initialise road accordingly using random number generator 50 | print(f"Initialising ...") 51 | 52 | ncars = initroad(bigroad, density, seedval) 53 | 54 | print(f"Actual Density of cars is {format(float(ncars)/float(ncell))}\n") 55 | print(f"Scattering data ...") 56 | 57 | comm.Scatter(bigroad, oldroad[1:nlocal+1], root=0) 58 | 59 | if (rank == 0): 60 | print(f"... done\n") 61 | 62 | # Compute neighbours 63 | 64 | rankup = (rank + 1) 65 | rankdown = (rank - 1) 66 | 67 | # Wrap-around for cyclic boundary conditions, i.e. a roundabout 68 | 69 | if (rankup == size): 70 | rankup = 0 71 | 72 | if (rankdown == -1): 73 | rankdown = size-1 74 | 75 | nmove = 0 76 | nmovelocal = 0 77 | 78 | comm.barrier() 79 | 80 | tstart = MPI.Wtime() 81 | 82 | for iter in range(1, maxiter+1): 83 | 84 | comm.Sendrecv(oldroad[nlocal:nlocal+1], dest=rankup, 85 | recvbuf=oldroad[0:1], source=rankdown) 86 | 87 | comm.Sendrecv(oldroad[1:2], dest=rankdown, 88 | recvbuf=oldroad[nlocal+1:nlocal+2], source=rankup) 89 | 90 | nmovelocal = updateroad(newroad, oldroad) 91 | 92 | sbuf[0] = nmovelocal 93 | comm.Allreduce(sbuf, rbuf) 94 | nmove = rbuf[0] 95 | 96 | # Copy new to old array 97 | oldroad[1:nlocal+1] = newroad[1:nlocal+1] 98 | 99 | if iter % printfreq == 0: 100 | 101 | if (rank == 0): 102 | 103 | print(f"At iteration {iter} average velocity is {float(nmove)/float(ncars):.6f}") 104 | 105 | comm.barrier() 106 | 107 | tstop = MPI.Wtime() 108 | 109 | if (rank == 0): 110 | 111 | print(f"\nFinished\n") 112 | print(f"Time taken was {tstop-tstart:.2f} seconds") 113 | print(f"Update rate was {1.0e-6*ncell*maxiter/(tstop-tstart):.2f} MCOPs") 114 | 115 | if __name__ == "__main__": 116 | main(sys.argv[1:]) 117 | -------------------------------------------------------------------------------- /notebooks/trafficmpi/traffic.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import time 5 | import numpy as np 6 | 7 | from mpi4py import MPI 8 | 9 | from trafficlib import initroad, updatebcs, updateroad, gettime 10 | 11 | def main(argv): 12 | 13 | comm = MPI.COMM_WORLD 14 | 15 | size = comm.Get_size() 16 | rank = comm.Get_rank() 17 | 18 | # Simulation parameters 19 | seedval = 5743 20 | ncell = 10240000 21 | maxiter = 1024000000//ncell 22 | printfreq = maxiter//10 23 | 24 | nlocal = ncell//size 25 | 26 | # Check consistency 27 | 28 | if (nlocal*size != ncell): 29 | if (rank == 0): 30 | print(f"ERROR: ncell = {ncell} not a multiple of size = {size}") 31 | exit() 32 | 33 | bigroad = np.zeros(ncell,dtype=np.int32) 34 | newroad = np.zeros(nlocal+2,dtype=np.int32) 35 | oldroad = np.zeros(nlocal+2,dtype=np.int32) 36 | 37 | sbuf = np.zeros(1) 38 | rbuf = np.zeros(1) 39 | 40 | density = 0.52 41 | 42 | if (rank == 0): 43 | 44 | print(f"Length of road is {ncell}") 45 | print(f"Number of iterations is {maxiter}") 46 | print(f"Target density of cars is {density}") 47 | print(f"Running on {size} process(es)") 48 | 49 | # Initialise road accordingly using random number generator 50 | print(f"Initialising ...") 51 | 52 | ncars = initroad(bigroad, density, seedval) 53 | 54 | print(f"Actual Density of cars is {format(float(ncars)/float(ncell))}\n") 55 | print(f"Scattering data ...") 56 | 57 | comm.Scatter(bigroad, oldroad[1:nlocal+1], root=0) 58 | 59 | if (rank == 0): 60 | print(f"... done\n") 61 | 62 | # Compute neighbours 63 | 64 | rankup = (rank + 1) 65 | rankdown = (rank - 1) 66 | 67 | # Wrap-around for cyclic boundary conditions, i.e. a roundabout 68 | 69 | if (rankup == size): 70 | rankup = 0 71 | 72 | if (rankdown == -1): 73 | rankdown = size-1 74 | 75 | nmove = 0 76 | nmovelocal = 0 77 | 78 | comm.barrier() 79 | 80 | tstart = MPI.Wtime() 81 | 82 | for iter in range(1, maxiter+1): 83 | 84 | comm.Sendrecv(oldroad[nlocal:nlocal+1], dest=rankup, 85 | recvbuf=oldroad[0:1], source=rankdown) 86 | 87 | comm.Sendrecv(oldroad[1:2], dest=rankdown, 88 | recvbuf=oldroad[nlocal+1:nlocal+2], source=rankup) 89 | 90 | nmovelocal = updateroad(newroad, oldroad) 91 | 92 | sbuf[0] = nmovelocal 93 | comm.Allreduce(sbuf, rbuf) 94 | nmove = rbuf[0] 95 | 96 | # Copy new to old array 97 | oldroad[1:nlocal+1] = newroad[1:nlocal+1] 98 | 99 | if iter % printfreq == 0: 100 | 101 | if (rank == 0): 102 | 103 | print(f"At iteration {iter} average velocity is {float(nmove)/float(ncars):.6f}") 104 | 105 | comm.barrier() 106 | 107 | tstop = MPI.Wtime() 108 | 109 | if (rank == 0): 110 | 111 | print(f"\nFinished\n") 112 | print(f"Time taken was {tstop-tstart:.2f} seconds") 113 | print(f"Update rate was {1.0e-6*ncell*maxiter/(tstop-tstart):.2f} MCOPs") 114 | 115 | if __name__ == "__main__": 116 | main(sys.argv[1:]) 117 | -------------------------------------------------------------------------------- /notebooks/images/logos/bullet_red.eps: -------------------------------------------------------------------------------- 1 | %!PS-Adobe-2.0 EPSF-2.0 2 | %%Title: bullet.fig 3 | %%Creator: fig2dev Version 3.1 Patchlevel 2 4 | %%CreationDate: Tue Jan 14 13:51:56 1997 5 | %%For: parsons@opal.epcc.ed.ac.uk (Mark Parsons) 6 | %%Orientation: Portrait 7 | %%BoundingBox: 0 0 65 74 8 | %%Pages: 0 9 | %%BeginSetup 10 | %%IncludeFeature: *PageSize A4 11 | %%EndSetup 12 | %%EndComments 13 | /$F2psDict 200 dict def 14 | $F2psDict begin 15 | $F2psDict /mtrx matrix put 16 | /col-1 {0 setgray} bind def 17 | /col0 {0.000 0.000 0.000 srgb} bind def 18 | /col1 {0.000 0.000 1.000 srgb} bind def 19 | /col2 {0.000 1.000 0.000 srgb} bind def 20 | /col3 {0.000 1.000 1.000 srgb} bind def 21 | /col4 {1.000 0.000 0.000 srgb} bind def 22 | /col5 {1.000 0.000 1.000 srgb} bind def 23 | /col6 {1.000 1.000 0.000 srgb} bind def 24 | /col7 {1.000 1.000 1.000 srgb} bind def 25 | /col8 {0.000 0.000 0.560 srgb} bind def 26 | /col9 {0.000 0.000 0.690 srgb} bind def 27 | /col10 {0.000 0.000 0.820 srgb} bind def 28 | /col11 {0.530 0.810 1.000 srgb} bind def 29 | /col12 {0.000 0.560 0.000 srgb} bind def 30 | /col13 {0.000 0.690 0.000 srgb} bind def 31 | /col14 {0.000 0.820 0.000 srgb} bind def 32 | /col15 {0.000 0.560 0.560 srgb} bind def 33 | /col16 {0.000 0.690 0.690 srgb} bind def 34 | /col17 {0.000 0.820 0.820 srgb} bind def 35 | /col18 {0.560 0.000 0.000 srgb} bind def 36 | /col19 {0.690 0.000 0.000 srgb} bind def 37 | /col20 {0.820 0.000 0.000 srgb} bind def 38 | /col21 {0.560 0.000 0.560 srgb} bind def 39 | /col22 {0.690 0.000 0.690 srgb} bind def 40 | /col23 {0.820 0.000 0.820 srgb} bind def 41 | /col24 {0.500 0.190 0.000 srgb} bind def 42 | /col25 {0.630 0.250 0.000 srgb} bind def 43 | /col26 {0.750 0.380 0.000 srgb} bind def 44 | /col27 {1.000 0.500 0.500 srgb} bind def 45 | /col28 {1.000 0.630 0.630 srgb} bind def 46 | /col29 {1.000 0.750 0.750 srgb} bind def 47 | /col30 {1.000 0.880 0.880 srgb} bind def 48 | /col31 {1.000 0.840 0.000 srgb} bind def 49 | 50 | end 51 | save 52 | -71.0 145.0 translate 53 | 1 -1 scale 54 | 55 | /cp {closepath} bind def 56 | /ef {eofill} bind def 57 | /gr {grestore} bind def 58 | /gs {gsave} bind def 59 | /sa {save} bind def 60 | /rs {restore} bind def 61 | /l {lineto} bind def 62 | /m {moveto} bind def 63 | /rm {rmoveto} bind def 64 | /n {newpath} bind def 65 | /s {stroke} bind def 66 | /sh {show} bind def 67 | /slc {setlinecap} bind def 68 | /slj {setlinejoin} bind def 69 | /slw {setlinewidth} bind def 70 | /srgb {setrgbcolor} bind def 71 | /rot {rotate} bind def 72 | /sc {scale} bind def 73 | /sd {setdash} bind def 74 | /ff {findfont} bind def 75 | /sf {setfont} bind def 76 | /scf {scalefont} bind def 77 | /sw {stringwidth} bind def 78 | /tr {translate} bind def 79 | /tnt {dup dup currentrgbcolor 80 | 4 -2 roll dup 1 exch sub 3 -1 roll mul add 81 | 4 -2 roll dup 1 exch sub 3 -1 roll mul add 82 | 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} 83 | bind def 84 | /shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul 85 | 4 -2 roll mul srgb} bind def 86 | /$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def 87 | /$F2psEnd {$F2psEnteredState restore end} def 88 | %%EndProlog 89 | 90 | $F2psBegin 91 | 10 setmiterlimit 92 | n 0 842 m 0 0 l 595 0 l 595 842 l cp clip 93 | 0.06000 0.06000 sc 94 | 7.500 slw 95 | % Polyline 96 | n 1200 1200 m 1200 2400 l 2250 1800 l cp gs col4 1.00 shd ef gr gs col4 s gr 97 | $F2psEnd 98 | rs 99 | -------------------------------------------------------------------------------- /notebooks/images/logos/bullet_black.eps: -------------------------------------------------------------------------------- 1 | %!PS-Adobe-2.0 EPSF-2.0 2 | %%Title: bullet.fig 3 | %%Creator: fig2dev Version 3.1 Patchlevel 2 4 | %%CreationDate: Tue Jan 14 12:56:51 1997 5 | %%For: parsons@opal.epcc.ed.ac.uk (Mark Parsons) 6 | %%Orientation: Portrait 7 | %%BoundingBox: 0 0 65 74 8 | %%Pages: 0 9 | %%BeginSetup 10 | %%IncludeFeature: *PageSize A4 11 | %%EndSetup 12 | %%EndComments 13 | /$F2psDict 200 dict def 14 | $F2psDict begin 15 | $F2psDict /mtrx matrix put 16 | /col-1 {0 setgray} bind def 17 | /col0 {0.000 0.000 0.000 srgb} bind def 18 | /col1 {0.000 0.000 1.000 srgb} bind def 19 | /col2 {0.000 1.000 0.000 srgb} bind def 20 | /col3 {0.000 1.000 1.000 srgb} bind def 21 | /col4 {1.000 0.000 0.000 srgb} bind def 22 | /col5 {1.000 0.000 1.000 srgb} bind def 23 | /col6 {1.000 1.000 0.000 srgb} bind def 24 | /col7 {1.000 1.000 1.000 srgb} bind def 25 | /col8 {0.000 0.000 0.560 srgb} bind def 26 | /col9 {0.000 0.000 0.690 srgb} bind def 27 | /col10 {0.000 0.000 0.820 srgb} bind def 28 | /col11 {0.530 0.810 1.000 srgb} bind def 29 | /col12 {0.000 0.560 0.000 srgb} bind def 30 | /col13 {0.000 0.690 0.000 srgb} bind def 31 | /col14 {0.000 0.820 0.000 srgb} bind def 32 | /col15 {0.000 0.560 0.560 srgb} bind def 33 | /col16 {0.000 0.690 0.690 srgb} bind def 34 | /col17 {0.000 0.820 0.820 srgb} bind def 35 | /col18 {0.560 0.000 0.000 srgb} bind def 36 | /col19 {0.690 0.000 0.000 srgb} bind def 37 | /col20 {0.820 0.000 0.000 srgb} bind def 38 | /col21 {0.560 0.000 0.560 srgb} bind def 39 | /col22 {0.690 0.000 0.690 srgb} bind def 40 | /col23 {0.820 0.000 0.820 srgb} bind def 41 | /col24 {0.500 0.190 0.000 srgb} bind def 42 | /col25 {0.630 0.250 0.000 srgb} bind def 43 | /col26 {0.750 0.380 0.000 srgb} bind def 44 | /col27 {1.000 0.500 0.500 srgb} bind def 45 | /col28 {1.000 0.630 0.630 srgb} bind def 46 | /col29 {1.000 0.750 0.750 srgb} bind def 47 | /col30 {1.000 0.880 0.880 srgb} bind def 48 | /col31 {1.000 0.840 0.000 srgb} bind def 49 | 50 | end 51 | save 52 | -71.0 145.0 translate 53 | 1 -1 scale 54 | 55 | /cp {closepath} bind def 56 | /ef {eofill} bind def 57 | /gr {grestore} bind def 58 | /gs {gsave} bind def 59 | /sa {save} bind def 60 | /rs {restore} bind def 61 | /l {lineto} bind def 62 | /m {moveto} bind def 63 | /rm {rmoveto} bind def 64 | /n {newpath} bind def 65 | /s {stroke} bind def 66 | /sh {show} bind def 67 | /slc {setlinecap} bind def 68 | /slj {setlinejoin} bind def 69 | /slw {setlinewidth} bind def 70 | /srgb {setrgbcolor} bind def 71 | /rot {rotate} bind def 72 | /sc {scale} bind def 73 | /sd {setdash} bind def 74 | /ff {findfont} bind def 75 | /sf {setfont} bind def 76 | /scf {scalefont} bind def 77 | /sw {stringwidth} bind def 78 | /tr {translate} bind def 79 | /tnt {dup dup currentrgbcolor 80 | 4 -2 roll dup 1 exch sub 3 -1 roll mul add 81 | 4 -2 roll dup 1 exch sub 3 -1 roll mul add 82 | 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} 83 | bind def 84 | /shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul 85 | 4 -2 roll mul srgb} bind def 86 | /$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def 87 | /$F2psEnd {$F2psEnteredState restore end} def 88 | %%EndProlog 89 | 90 | $F2psBegin 91 | 10 setmiterlimit 92 | n 0 842 m 0 0 l 595 0 l 595 842 l cp clip 93 | 0.06000 0.06000 sc 94 | 7.500 slw 95 | % Polyline 96 | n 1200 1200 m 1200 2400 l 2250 1800 l cp gs 0.00 setgray ef gr gs col-1 s gr 97 | $F2psEnd 98 | rs 99 | -------------------------------------------------------------------------------- /code/C-MPI/traffic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "traffic.h" 7 | 8 | int main(int argc, char **argv) 9 | { 10 | // Set the size of the road 11 | 12 | int ncell = 10240000; 13 | 14 | int *oldroad, *newroad, *bigroad; 15 | 16 | int i, iter, nmove, nmovelocal, ncars; 17 | int maxiter, printfreq; 18 | 19 | float density; 20 | 21 | double tstart, tstop; 22 | 23 | MPI_Status status; 24 | int rank, size, nlocal, rankup, rankdown; 25 | 26 | maxiter = 1.024e9/((double) ncell); 27 | printfreq = maxiter/10; 28 | 29 | // Set target density of cars 30 | 31 | density = 0.52; 32 | 33 | // Start MPI 34 | 35 | MPI_Init(NULL, NULL); 36 | 37 | // Find size and rank 38 | 39 | MPI_Comm_size(MPI_COMM_WORLD, &size); 40 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 41 | 42 | if (rank == 0) 43 | { 44 | printf("Running message-passing traffic model\n"); 45 | printf("\nLength of road is %d\n", ncell); 46 | printf("Number of iterations is %d \n", maxiter); 47 | printf("Target density of cars is %f \n", density); 48 | printf("Running on %d processes\n", size); 49 | } 50 | 51 | nlocal = ncell/size; 52 | 53 | oldroad = (int *) malloc((nlocal+2)*sizeof(int)); 54 | newroad = (int *) malloc((nlocal+2)*sizeof(int)); 55 | 56 | for (i=1; i <= nlocal; i++) 57 | { 58 | oldroad[i] = 0; 59 | newroad[i] = 0; 60 | } 61 | 62 | if (rank == 0) 63 | { 64 | bigroad = (int *) malloc(ncell*sizeof(int)); 65 | 66 | // Initialise road accordingly using random number generator 67 | 68 | printf("Initialising road ...\n"); 69 | 70 | ncars = initroad(bigroad, ncell, density, SEED); 71 | 72 | printf("...done\n"); 73 | printf("Actual density is %f\n", (float) ncars / (float) ncell); 74 | printf("Scattering data ...\n"); 75 | } 76 | 77 | MPI_Scatter(bigroad, nlocal, MPI_INT, 78 | &oldroad[1], nlocal, MPI_INT, 79 | 0, MPI_COMM_WORLD); 80 | 81 | if (rank == 0) 82 | { 83 | printf("... done\n\n"); 84 | } 85 | 86 | // Compute neighbours 87 | 88 | rankup = (rank + 1) % size; 89 | rankdown = (rank + size - 1) % size; 90 | 91 | MPI_Barrier(MPI_COMM_WORLD); 92 | 93 | tstart = MPI_Wtime(); 94 | 95 | for (iter=1; iter<=maxiter; iter++) 96 | { 97 | 98 | // Implement halo swaps which now includes boundary conditions 99 | 100 | MPI_Sendrecv(&oldroad[nlocal], 1, MPI_INT, rankup, 1, 101 | &oldroad[0], 1, MPI_INT, rankdown, 1, 102 | MPI_COMM_WORLD, &status); 103 | 104 | MPI_Sendrecv(&oldroad[1], 1, MPI_INT, rankdown, 1, 105 | &oldroad[nlocal+1], 1, MPI_INT, rankup, 1, 106 | MPI_COMM_WORLD, &status); 107 | 108 | // Apply CA rules to all cells 109 | 110 | nmovelocal = updateroad(newroad, oldroad, nlocal); 111 | 112 | // Globally sum the value 113 | 114 | MPI_Allreduce(&nmovelocal, &nmove, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); 115 | 116 | // Copy new to old array 117 | 118 | for (i=1; i<=nlocal; i++) 119 | { 120 | oldroad[i] = newroad[i]; 121 | } 122 | 123 | if (iter%printfreq == 0) 124 | { 125 | if (rank == 0) 126 | { 127 | printf("At iteration %d average velocity is %f \n", 128 | iter, (float) nmove / (float) ncars); 129 | } 130 | } 131 | } 132 | 133 | MPI_Barrier(MPI_COMM_WORLD); 134 | 135 | tstop = MPI_Wtime(); 136 | 137 | free(oldroad); 138 | free(newroad); 139 | 140 | if (rank == 0) 141 | { 142 | free(bigroad); 143 | 144 | printf("\nFinished\n"); 145 | printf("\nTime taken was %f seconds\n", tstop-tstart); 146 | printf("Update rate was %f MCOPs\n\n", \ 147 | 1.e-6*((double) ncell)*((double) maxiter)/(tstop-tstart)); 148 | } 149 | 150 | // Finish 151 | 152 | MPI_Finalize(); 153 | } 154 | -------------------------------------------------------------------------------- /code/F-MPI/traffic.f90: -------------------------------------------------------------------------------- 1 | program traffic 2 | 3 | use mpi 4 | 5 | use trafficlib 6 | 7 | implicit none 8 | 9 | ! Set the size of the road 10 | 11 | integer :: ncell = 10240000 12 | integer :: maxiter, printfreq 13 | 14 | integer rank, size, ierr, nlocal, rankup, rankdown 15 | integer, dimension(MPI_STATUS_SIZE) :: status 16 | 17 | integer :: i, iter, nmove, nmovelocal, ncars 18 | real :: density 19 | 20 | integer, allocatable, dimension(:) :: newroad, oldroad, bigroad 21 | 22 | double precision :: tstart, tstop 23 | 24 | maxiter = 1.024e9/ncell 25 | printfreq = maxiter/10 26 | 27 | ! Set target density of cars 28 | 29 | density = 0.52 30 | 31 | ! Start message passing system 32 | 33 | call MPI_Init(ierr) 34 | 35 | ! Compute size and rank 36 | 37 | call MPI_Comm_size(MPI_COMM_WORLD, size, ierr) 38 | call MPI_Comm_rank(MPI_COMM_WORLD, rank, ierr) 39 | 40 | nlocal = ncell/size 41 | 42 | if (rank == 0) then 43 | 44 | write(*,*) 'Running message-passing traffic model' 45 | write(*,*) 46 | write(*,*) 'Length of road is ', ncell 47 | write(*,*) 'Number of iterations is ', maxiter 48 | write(*,*) 'Target density of cars is ', density 49 | write(*,*) 'Running on ', size, ' processes' 50 | 51 | end if 52 | 53 | ! Allocate arrays 54 | 55 | allocate(newroad(0:nlocal+1)) 56 | allocate(oldroad(0:nlocal+1)) 57 | 58 | do i = 1, nlocal 59 | oldroad(i) = 0 60 | newroad(i) = 0 61 | end do 62 | 63 | if (rank == 0) then 64 | 65 | allocate(bigroad(ncell)) 66 | 67 | ! Initialise road accordingly using random number generator 68 | 69 | write(*,*) 'Initialising ...' 70 | 71 | ncars = initroad(bigroad, ncell, density, seed) 72 | 73 | write(*,*) '... done' 74 | 75 | write(*,*) 'Actual density of cars is ', float(ncars)/float(ncell) 76 | write(*,*) 'Scattering data ...' 77 | 78 | end if 79 | 80 | call MPI_Scatter(bigroad, nlocal, MPI_INTEGER, & 81 | oldroad(1), nlocal, MPI_INTEGER, & 82 | 0, MPI_COMM_WORLD, ierr) 83 | 84 | if (rank == 0) then 85 | 86 | write(*,*) '... done' 87 | write(*,*) 88 | 89 | end if 90 | 91 | ! Compute neighbours 92 | 93 | rankup = mod(rank + 1, size) 94 | rankdown = mod(rank + size - 1, size) 95 | 96 | tstart = MPI_Wtime() 97 | 98 | do iter = 1, maxiter 99 | 100 | ! Implement halo swaps which now includes boundary conditions 101 | 102 | call MPI_Sendrecv(oldroad(nlocal), 1, MPI_INTEGER, rankup, 1, & 103 | oldroad(0), 1, MPI_INTEGER, rankdown, 1, & 104 | MPI_COMM_WORLD, status, ierr); 105 | 106 | call MPI_Sendrecv(oldroad(1), 1, MPI_INTEGER, rankdown, 1, & 107 | oldroad(nlocal+1), 1, MPI_INTEGER, rankup, 1, & 108 | MPI_COMM_WORLD, status, ierr); 109 | 110 | nmovelocal = updateroad(newroad, oldroad, nlocal) 111 | 112 | ! Globally sum the value 113 | 114 | call MPI_Allreduce(nmovelocal, nmove, 1, MPI_INTEGER, MPI_SUM, & 115 | MPI_COMM_WORLD, ierr) 116 | 117 | ! Copy new to old array 118 | 119 | do i = 1, nlocal 120 | 121 | oldroad(i) = newroad(i) 122 | 123 | end do 124 | 125 | if (mod(iter, printfreq) == 0) then 126 | 127 | if (rank == 0) write(*,*) 'At iteration ', iter, & 128 | ' average velocity is ', float(nmove)/float(ncars) 129 | end if 130 | 131 | end do 132 | 133 | tstop = MPI_Wtime() 134 | 135 | deallocate(oldroad) 136 | deallocate(newroad) 137 | 138 | if (rank == 0) then 139 | 140 | deallocate(bigroad) 141 | 142 | write(*,*) 143 | write(*,*) 'Finished' 144 | write(*,*) 145 | write(*,*) 'Time taken was ', tstop - tstart, ' seconds' 146 | write(*,*) 'Update rate was ', & 147 | 1.d-6*float(ncell)*float(maxiter)/(tstop-tstart), ' MCOPs' 148 | write(*,*) 149 | 150 | end if 151 | 152 | call MPI_Finalize(ierr) 153 | 154 | end program traffic 155 | -------------------------------------------------------------------------------- /code/C-MPI/uni.c: -------------------------------------------------------------------------------- 1 | /* 2 | * C version of Marsaglia's UNI random number generator 3 | * More or less transliterated from the Fortran -- with 1 bug fix 4 | * Hence horrible style 5 | * 6 | * Features: 7 | * ANSI C 8 | * not callable from Fortran (yet) 9 | */ 10 | 11 | 12 | /* 13 | * Global variables for rstart & uni 14 | */ 15 | #include 16 | #include 17 | 18 | float uni_u[98]; /* Was U(97) in Fortran version -- too lazy to fix */ 19 | float uni_c, uni_cd, uni_cm; 20 | int uni_ui, uni_uj; 21 | 22 | float uni(void) 23 | { 24 | float luni; /* local variable for uni */ 25 | 26 | luni = uni_u[uni_ui] - uni_u[uni_uj]; 27 | if (luni < 0.0) 28 | luni += 1.0; 29 | uni_u[uni_ui] = luni; 30 | if (--uni_ui == 0) 31 | uni_ui = 97; 32 | if (--uni_uj == 0) 33 | uni_uj = 97; 34 | if ((uni_c -= uni_cd) < 0.0) 35 | uni_c += uni_cm; 36 | if ((luni -= uni_c) < 0.0) 37 | luni += 1.0; 38 | return (float) luni; 39 | } 40 | 41 | void rstart(int i, int j, int k, int l) 42 | { 43 | int ii, jj, m; 44 | float s, t; 45 | 46 | for (ii = 1; ii <= 97; ii++) { 47 | s = 0.0; 48 | t = 0.5; 49 | for (jj = 1; jj <= 24; jj++) { 50 | m = ((i*j % 179) * k) % 179; 51 | i = j; 52 | j = k; 53 | k = m; 54 | l = (53*l+1) % 169; 55 | if (l*m % 64 >= 32) 56 | s += t; 57 | t *= 0.5; 58 | } 59 | uni_u[ii] = s; 60 | } 61 | uni_c = 362436.0 / 16777216.0; 62 | uni_cd = 7654321.0 / 16777216.0; 63 | uni_cm = 16777213.0 / 16777216.0; 64 | uni_ui = 97; /* There is a bug in the original Fortran version */ 65 | uni_uj = 33; /* of UNI -- i and j should be SAVEd in UNI() */ 66 | } 67 | 68 | 69 | /* ~rinit: this takes a single integer in the range 70 | 0 <= ijkl <= 900 000 000 71 | and produces the four smaller integers needed for rstart. It is 72 | * based on the ideas contained in the RMARIN subroutine in 73 | * F. James, "A Review of Pseudorandom Number Generators", 74 | * Comp. Phys. Commun. Oct 1990, p.340 75 | * To reduce the modifications to the existing code, rinit now 76 | * takes the role of a preprocessor for rstart. 77 | * 78 | * This is useful for the parallel version of the code as James 79 | * states that any integer ijkl will produce a statistically 80 | * independent sequence of random numbers. 81 | * 82 | * Very funny. If that statement was worth anything he would have provided 83 | * a proof to go with it. spb 12/12/90 84 | */ 85 | 86 | void rinit(int ijkl) 87 | { 88 | int i, j, k, l, ij, kl; 89 | 90 | /* check ijkl is within range */ 91 | if( (ijkl < 0) || (ijkl > 900000000) ) 92 | { 93 | printf("rinit: ijkl = %d -- out of range\n\n", ijkl); 94 | exit(3); 95 | } 96 | 97 | /* printf("rinit: seed_ijkl = %d\n", ijkl); */ 98 | 99 | /* decompose the long integer into the the equivalent four 100 | * integers for rstart. This should be a 1-1 mapping 101 | * ijkl <--> (i, j, k, l) 102 | * though not quite all of the possible sets of (i, j, k, l) 103 | * can be produced. 104 | */ 105 | 106 | ij = ijkl/30082; 107 | kl = ijkl - (30082 * ij); 108 | 109 | i = ((ij/177) % 177) + 2; 110 | j = (ij % 177) + 2; 111 | k = ((kl/169) % 178) + 1; 112 | l = kl % 169; 113 | 114 | if( (i <= 0) || (i > 178) ) 115 | { 116 | printf("rinit: i = %d -- out of range\n\n", i); 117 | exit(3); 118 | } 119 | 120 | if( (j <= 0) || (j > 178) ) 121 | { 122 | printf("rinit: j = %d -- out of range\n\n", j); 123 | exit(3); 124 | } 125 | 126 | if( (k <= 0) || (k > 178) ) 127 | { 128 | printf("rinit: k = %d -- out of range\n\n", k); 129 | exit(3); 130 | } 131 | 132 | if( (l < 0) || (l > 168) ) 133 | { 134 | printf("rinit: l = %d -- out of range\n\n", l); 135 | exit(3); 136 | } 137 | 138 | if (i == 1 && j == 1 && k == 1) 139 | { 140 | printf("rinit: 1 1 1 not allowed for 1st 3 seeds\n\n"); 141 | exit(4); 142 | } 143 | 144 | /* printf("rinit: initialising RNG via rstart(%d, %d, %d, %d)\n", 145 | i, j, k, l); */ 146 | 147 | rstart(i, j, k, l); 148 | 149 | } 150 | 151 | -------------------------------------------------------------------------------- /code/C-OMP/uni.c: -------------------------------------------------------------------------------- 1 | /* 2 | * C version of Marsaglia's UNI random number generator 3 | * More or less transliterated from the Fortran -- with 1 bug fix 4 | * Hence horrible style 5 | * 6 | * Features: 7 | * ANSI C 8 | * not callable from Fortran (yet) 9 | */ 10 | 11 | 12 | /* 13 | * Global variables for rstart & uni 14 | */ 15 | #include 16 | #include 17 | 18 | float uni_u[98]; /* Was U(97) in Fortran version -- too lazy to fix */ 19 | float uni_c, uni_cd, uni_cm; 20 | int uni_ui, uni_uj; 21 | 22 | float uni(void) 23 | { 24 | float luni; /* local variable for uni */ 25 | 26 | luni = uni_u[uni_ui] - uni_u[uni_uj]; 27 | if (luni < 0.0) 28 | luni += 1.0; 29 | uni_u[uni_ui] = luni; 30 | if (--uni_ui == 0) 31 | uni_ui = 97; 32 | if (--uni_uj == 0) 33 | uni_uj = 97; 34 | if ((uni_c -= uni_cd) < 0.0) 35 | uni_c += uni_cm; 36 | if ((luni -= uni_c) < 0.0) 37 | luni += 1.0; 38 | return (float) luni; 39 | } 40 | 41 | void rstart(int i, int j, int k, int l) 42 | { 43 | int ii, jj, m; 44 | float s, t; 45 | 46 | for (ii = 1; ii <= 97; ii++) { 47 | s = 0.0; 48 | t = 0.5; 49 | for (jj = 1; jj <= 24; jj++) { 50 | m = ((i*j % 179) * k) % 179; 51 | i = j; 52 | j = k; 53 | k = m; 54 | l = (53*l+1) % 169; 55 | if (l*m % 64 >= 32) 56 | s += t; 57 | t *= 0.5; 58 | } 59 | uni_u[ii] = s; 60 | } 61 | uni_c = 362436.0 / 16777216.0; 62 | uni_cd = 7654321.0 / 16777216.0; 63 | uni_cm = 16777213.0 / 16777216.0; 64 | uni_ui = 97; /* There is a bug in the original Fortran version */ 65 | uni_uj = 33; /* of UNI -- i and j should be SAVEd in UNI() */ 66 | } 67 | 68 | 69 | /* ~rinit: this takes a single integer in the range 70 | 0 <= ijkl <= 900 000 000 71 | and produces the four smaller integers needed for rstart. It is 72 | * based on the ideas contained in the RMARIN subroutine in 73 | * F. James, "A Review of Pseudorandom Number Generators", 74 | * Comp. Phys. Commun. Oct 1990, p.340 75 | * To reduce the modifications to the existing code, rinit now 76 | * takes the role of a preprocessor for rstart. 77 | * 78 | * This is useful for the parallel version of the code as James 79 | * states that any integer ijkl will produce a statistically 80 | * independent sequence of random numbers. 81 | * 82 | * Very funny. If that statement was worth anything he would have provided 83 | * a proof to go with it. spb 12/12/90 84 | */ 85 | 86 | void rinit(int ijkl) 87 | { 88 | int i, j, k, l, ij, kl; 89 | 90 | /* check ijkl is within range */ 91 | if( (ijkl < 0) || (ijkl > 900000000) ) 92 | { 93 | printf("rinit: ijkl = %d -- out of range\n\n", ijkl); 94 | exit(3); 95 | } 96 | 97 | /* printf("rinit: seed_ijkl = %d\n", ijkl); */ 98 | 99 | /* decompose the long integer into the the equivalent four 100 | * integers for rstart. This should be a 1-1 mapping 101 | * ijkl <--> (i, j, k, l) 102 | * though not quite all of the possible sets of (i, j, k, l) 103 | * can be produced. 104 | */ 105 | 106 | ij = ijkl/30082; 107 | kl = ijkl - (30082 * ij); 108 | 109 | i = ((ij/177) % 177) + 2; 110 | j = (ij % 177) + 2; 111 | k = ((kl/169) % 178) + 1; 112 | l = kl % 169; 113 | 114 | if( (i <= 0) || (i > 178) ) 115 | { 116 | printf("rinit: i = %d -- out of range\n\n", i); 117 | exit(3); 118 | } 119 | 120 | if( (j <= 0) || (j > 178) ) 121 | { 122 | printf("rinit: j = %d -- out of range\n\n", j); 123 | exit(3); 124 | } 125 | 126 | if( (k <= 0) || (k > 178) ) 127 | { 128 | printf("rinit: k = %d -- out of range\n\n", k); 129 | exit(3); 130 | } 131 | 132 | if( (l < 0) || (l > 168) ) 133 | { 134 | printf("rinit: l = %d -- out of range\n\n", l); 135 | exit(3); 136 | } 137 | 138 | if (i == 1 && j == 1 && k == 1) 139 | { 140 | printf("rinit: 1 1 1 not allowed for 1st 3 seeds\n\n"); 141 | exit(4); 142 | } 143 | 144 | /* printf("rinit: initialising RNG via rstart(%d, %d, %d, %d)\n", 145 | i, j, k, l); */ 146 | 147 | rstart(i, j, k, l); 148 | 149 | } 150 | 151 | -------------------------------------------------------------------------------- /code/C-SER/uni.c: -------------------------------------------------------------------------------- 1 | /* 2 | * C version of Marsaglia's UNI random number generator 3 | * More or less transliterated from the Fortran -- with 1 bug fix 4 | * Hence horrible style 5 | * 6 | * Features: 7 | * ANSI C 8 | * not callable from Fortran (yet) 9 | */ 10 | 11 | 12 | /* 13 | * Global variables for rstart & uni 14 | */ 15 | #include 16 | #include 17 | 18 | float uni_u[98]; /* Was U(97) in Fortran version -- too lazy to fix */ 19 | float uni_c, uni_cd, uni_cm; 20 | int uni_ui, uni_uj; 21 | 22 | float uni(void) 23 | { 24 | float luni; /* local variable for uni */ 25 | 26 | luni = uni_u[uni_ui] - uni_u[uni_uj]; 27 | if (luni < 0.0) 28 | luni += 1.0; 29 | uni_u[uni_ui] = luni; 30 | if (--uni_ui == 0) 31 | uni_ui = 97; 32 | if (--uni_uj == 0) 33 | uni_uj = 97; 34 | if ((uni_c -= uni_cd) < 0.0) 35 | uni_c += uni_cm; 36 | if ((luni -= uni_c) < 0.0) 37 | luni += 1.0; 38 | return (float) luni; 39 | } 40 | 41 | void rstart(int i, int j, int k, int l) 42 | { 43 | int ii, jj, m; 44 | float s, t; 45 | 46 | for (ii = 1; ii <= 97; ii++) { 47 | s = 0.0; 48 | t = 0.5; 49 | for (jj = 1; jj <= 24; jj++) { 50 | m = ((i*j % 179) * k) % 179; 51 | i = j; 52 | j = k; 53 | k = m; 54 | l = (53*l+1) % 169; 55 | if (l*m % 64 >= 32) 56 | s += t; 57 | t *= 0.5; 58 | } 59 | uni_u[ii] = s; 60 | } 61 | uni_c = 362436.0 / 16777216.0; 62 | uni_cd = 7654321.0 / 16777216.0; 63 | uni_cm = 16777213.0 / 16777216.0; 64 | uni_ui = 97; /* There is a bug in the original Fortran version */ 65 | uni_uj = 33; /* of UNI -- i and j should be SAVEd in UNI() */ 66 | } 67 | 68 | 69 | /* ~rinit: this takes a single integer in the range 70 | 0 <= ijkl <= 900 000 000 71 | and produces the four smaller integers needed for rstart. It is 72 | * based on the ideas contained in the RMARIN subroutine in 73 | * F. James, "A Review of Pseudorandom Number Generators", 74 | * Comp. Phys. Commun. Oct 1990, p.340 75 | * To reduce the modifications to the existing code, rinit now 76 | * takes the role of a preprocessor for rstart. 77 | * 78 | * This is useful for the parallel version of the code as James 79 | * states that any integer ijkl will produce a statistically 80 | * independent sequence of random numbers. 81 | * 82 | * Very funny. If that statement was worth anything he would have provided 83 | * a proof to go with it. spb 12/12/90 84 | */ 85 | 86 | void rinit(int ijkl) 87 | { 88 | int i, j, k, l, ij, kl; 89 | 90 | /* check ijkl is within range */ 91 | if( (ijkl < 0) || (ijkl > 900000000) ) 92 | { 93 | printf("rinit: ijkl = %d -- out of range\n\n", ijkl); 94 | exit(3); 95 | } 96 | 97 | /* printf("rinit: seed_ijkl = %d\n", ijkl); */ 98 | 99 | /* decompose the long integer into the the equivalent four 100 | * integers for rstart. This should be a 1-1 mapping 101 | * ijkl <--> (i, j, k, l) 102 | * though not quite all of the possible sets of (i, j, k, l) 103 | * can be produced. 104 | */ 105 | 106 | ij = ijkl/30082; 107 | kl = ijkl - (30082 * ij); 108 | 109 | i = ((ij/177) % 177) + 2; 110 | j = (ij % 177) + 2; 111 | k = ((kl/169) % 178) + 1; 112 | l = kl % 169; 113 | 114 | if( (i <= 0) || (i > 178) ) 115 | { 116 | printf("rinit: i = %d -- out of range\n\n", i); 117 | exit(3); 118 | } 119 | 120 | if( (j <= 0) || (j > 178) ) 121 | { 122 | printf("rinit: j = %d -- out of range\n\n", j); 123 | exit(3); 124 | } 125 | 126 | if( (k <= 0) || (k > 178) ) 127 | { 128 | printf("rinit: k = %d -- out of range\n\n", k); 129 | exit(3); 130 | } 131 | 132 | if( (l < 0) || (l > 168) ) 133 | { 134 | printf("rinit: l = %d -- out of range\n\n", l); 135 | exit(3); 136 | } 137 | 138 | if (i == 1 && j == 1 && k == 1) 139 | { 140 | printf("rinit: 1 1 1 not allowed for 1st 3 seeds\n\n"); 141 | exit(4); 142 | } 143 | 144 | /* printf("rinit: initialising RNG via rstart(%d, %d, %d, %d)\n", 145 | i, j, k, l); */ 146 | 147 | rstart(i, j, k, l); 148 | 149 | } 150 | 151 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 |




7 | 8 | 9 | # Make your Python code 10,000 times faster with parallel numpy! 10 | 11 | [![CC BY-NC-SA 4.0][cc-by-nc-sa-shield]][cc-by-nc-sa] 12 | 13 |

David Henty, EPCC.
14 | Wednesday 7th September 2022, 09:00 - 12:30 BST 15 |

16 | 17 | Python is widely used in scientific research for tasks such as data 18 | processing, analysis and visualisation. However, it is not yet widely 19 | used for large-scale modelling and simulation on High Performance 20 | Computer (HPC) architectures due to issues with performance – Python 21 | is primarily designed for ease of use and flexibility, not for 22 | speed. For example, a C program naively translated in to Python can 23 | often be over 100 times slower. However, there are techniques that can 24 | be used to dramatically increase the speed of Python programs such as 25 | fast array processing using numpy, parallelisation using MPI 26 | message-passing and running on HPC systems. 27 | 28 | In this hands-on workshop we will illustrate these techniques in 29 | practice by applying them to a toy application which simulates traffic 30 | flow using a simple cellular automaton model. Having developed and 31 | tested the program on your own laptop, we will then move to the UK 32 | National Supercomputer ARCHER2 which has in excess of 750,000 33 | CPU-cores. With a combination of numpy and mpi4py we will aim for a 34 | performance increase of more than a factor of 10,000 compared to the 35 | original program. 36 | 37 | As much of the programming as possible will be done using Jupyter 38 | Notebooks, but we will run on ARCHER2 via the standard Slurm batch 39 | scheduling system. Although attendees will be encouraged to write 40 | their own code, full solutions will be provided for all exercises. The 41 | main aim is to illustrate the potential power of supercomputer systems 42 | to new users, and to demystify HPC and parallel programming. 43 | 44 |

Pre-requisites

45 | 46 | Follow the installation instructions at 47 | [https://github.com/davidhenty/PythonHPCprep/#readme](https://github.com/davidhenty/PythonHPCprep/#readme). 48 | 49 |

Timetable (all times are in BST)

50 | 51 |

Unless otherwise indicated all material is Copyright 52 | © EPCC, The University of Edinburgh, and is only made available 53 | for private study.

54 | 55 | * 09:00 - 09:10 : Introduction 56 | * 09:10 - 09:30 : Traffic Model (lecture) 57 | * 09:30 - 09:45 : Traffic Model (hands-on) 58 | * 09:45 - 10:10 : Introduction to Numpy Arrays (lecture) 59 | * 10:10 - 10:30 : Traffic Model with Numpy (hands-on) 60 | * 10:30 - 11:00 : Coffee Break 61 | * 11:00 - 11:30 : Message-Passing Concepts (lecture) 62 | * 11:30 - 11:40 : Log on to ARCHER2 (hands-on) 63 | * 11:40 - 12:00 : Basic MPI with mpi4py (lecture) 64 | * 12:00 - 12:30 : Parallel Traffic Model (hands-on) 65 | 66 |

Course material

67 | 68 | Download and unzip 69 | [PythonHPC-main.zip](https://github.com/JimCircadian/PythonHPC/archive/refs/heads/main.zip) 70 | which includes all the Jupyter notebooks for the exercises on your 71 | laptop - see the directory `notebooks`. Lecture material is available in the [pdf](pdf) directory. 72 | 73 |

ARCHER2 exercises

74 | 75 | We are not using Jupyter notebooks on ARCHER2 - follow the instructions below. 76 | 77 | * Log on to ARCHER2 using `ssh` - you will have an account in the project `ta083` 78 | * Change directory to `/work` using `cd /work/ta083/ta083/$USER/` 79 | * Copy over the exercises material: `cp /work/ta083/shared/PythonHPC.zip .` 80 | * Unpack it: `unzip PythonHPC.zip` 81 | * Change directory: `cd PythonHPC/code/` 82 | 83 | You will see a number of directories containing C, Fortran and Python 84 | examples in serial and MPI (and OpenMP except for Python). We are only 85 | concerned with the Python examples today. 86 | 87 | * Load the Python environment: `module load cray-python` 88 | * Go to the `P-SER-NP` directory for the numpy version. 89 | * Execute the traffic model: `python traffic.py` - how does the speed 90 | compare to your laptop? 91 | 92 | We should really be running all computational jobs on the compute 93 | nodes which we access by submitting the batch script `archer2.job` (in 94 | fact you cannot run parallel jobs on the login nodes - you have to use 95 | the compute nodes). 96 | 97 | * Submit the batch job: `sbatch archer2.job` - this will run the numpy 98 | code on a single process. 99 | 100 | * Output will eventually appear in a file called something like 101 | `traffic-1234567.out` - you can monitor progress of your jobs using 102 | `squeue -u $USER` 103 | 104 | * Is the performance on the compute nodes similar to the login nodes? 105 | 106 | We will now run the parallel version on ARCHER2. You will see that we 107 | have increased the number of iterations by a factor of 10 - without 108 | this the runtimes become so short that it is difficult to get accurate 109 | performance figures. As a rule of thumb, anything less than a second 110 | is probably too short. 111 | 112 | * First change directory to `P-MPI` 113 | 114 | * Submit the batch job: `sbatch archer2.job` - this will run the 115 | parallel MPI code code on all 128 processors of a single ARCHER2 116 | node. 117 | 118 | * What is the performance? How much faster is it than the numpy code 119 | on a single process? How much faster is it than the original code? 120 | 121 | * You can run on 256 or 512 processors by editing `archer2.job` and 122 | increasing the value of `nodes` to 2 or 4. These jobs may take a 123 | surprisingly long time to run - this is due to slow startup times 124 | for parallel Python jobs and not slow performance. At these high 125 | process counts you may want to increase the number of iterations by 126 | another factor of 10. 127 | 128 | * Did we manage to break the 10,000 times faster barrier? 129 | 130 | --- 131 | 132 | This work is licensed under a 133 | [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License][cc-by-nc-sa]. 134 | 135 | [cc-by-nc-sa]: http://creativecommons.org/licenses/by-nc-sa/4.0/ 136 | [cc-by-nc-sa-image]: https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png 137 | [cc-by-nc-sa-shield]: https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg 138 | 139 | [![CC BY-NC-SA 4.0][cc-by-nc-sa-image]][cc-by-nc-sa] 140 | 141 | 142 | -------------------------------------------------------------------------------- /notebooks/trafficnumpy/trafficnumpy.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "1239f80c", 6 | "metadata": {}, 7 | "source": [ 8 | "# Library functions\n", 9 | "\n", 10 | "Here are the library functions re-written using numpy array syntax.\n", 11 | "\n", 12 | "The initialisation phase generates a whole array of random numbers at once, the `if` statement is replaced by a simple `where` and we use the `count` function to compute the number of cars.\n", 13 | "\n", 14 | "For updating the road, we also use a `where` but note that we have used the simpler (commented out) version from the original Python code as a single `where` can only implement a single `if`, `then`, `else` construct. To compute the number of moves we count the number of differences between the new and old roads, but note that we have divide this by two as it counts *both* a car entering and a car leaving a cell." 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "id": "0f02bd1d", 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "# %load trafficlib.py\n", 25 | "import sys\n", 26 | "import numpy as np\n", 27 | "\n", 28 | "def initroad(road, density, seedval):\n", 29 | "\n", 30 | " # Here we expect a road without halos\n", 31 | "\n", 32 | " n = len(road)\n", 33 | "\n", 34 | " np.random.seed(seedval)\n", 35 | "\n", 36 | " rng = np.random.random(n)\n", 37 | "\n", 38 | " road[0:n] = np.where(rng[:] < density, 1, 0)\n", 39 | "\n", 40 | " ncar = np.sum(road)\n", 41 | " \n", 42 | " return ncar\n", 43 | "\n", 44 | "\n", 45 | "def updateroad(newroad, oldroad):\n", 46 | "\n", 47 | " n = len(oldroad)-2\n", 48 | "\n", 49 | " newroad[1:n+1] = np.where(oldroad[1:n+1]==0, oldroad[0:n], oldroad[2:n+2])\n", 50 | "\n", 51 | " nmove = (newroad[1:n+1] != oldroad[1:n+1]).sum(dtype=int)\n", 52 | " nmove = nmove / 2\n", 53 | "\n", 54 | " return nmove\n", 55 | "\n", 56 | "\n", 57 | "def updatebcs(road):\n", 58 | "\n", 59 | " n = len(road)-2\n", 60 | "\n", 61 | " road[0] = road[n]\n", 62 | " road[n+1] = road[1]\n", 63 | "\n", 64 | "\n", 65 | "import time\n", 66 | "\n", 67 | "def gettime():\n", 68 | "\n", 69 | " return time.time()\n" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "id": "ab460b38", 75 | "metadata": {}, 76 | "source": [ 77 | "# Main program\n", 78 | "\n", 79 | "All the main arrays are now numpy arrays - we use 32-bit integers as this is what compiled languages such as C, C++ and Fortran use so it allows for a fair comparison.\n", 80 | "\n", 81 | "It is basically the same as the naive Python code except we can initialise `oldroad` directly using the function call - for lists, taking a subarray creates a new copy of the array whereas for numpy arrays a subarray is still part of the original array.\n", 82 | "\n", 83 | " * Run the program as-is - is it any faster than the previous code?\n", 84 | " * Reduce the length of the road by factors of ten down to 1024 and note the speed in each case - can you explain the behaviour?\n", 85 | " * Since numpy arrays operations are so much faster, it is important to use array syntax for all operations. How is the speed affected if you replace the copy-back step with a Python loop?" 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": null, 91 | "id": "6bf3595a", 92 | "metadata": {}, 93 | "outputs": [], 94 | "source": [ 95 | "# %load traffic.py\n", 96 | "#!/usr/bin/env python\n", 97 | "\n", 98 | "import sys\n", 99 | "import time\n", 100 | "import numpy as np\n", 101 | "\n", 102 | "from trafficlib import initroad, updatebcs, updateroad, gettime\n", 103 | "\n", 104 | "def main(argv):\n", 105 | "\n", 106 | " # Simulation parameters\n", 107 | " seedval = 5743\n", 108 | " ncell = 10240000\n", 109 | " maxiter = 1024000000//ncell\n", 110 | " printfreq = maxiter//10\n", 111 | "\n", 112 | " newroad = np.zeros(ncell+2, dtype=np.int32)\n", 113 | " oldroad = np.zeros(ncell+2, dtype=np.int32)\n", 114 | "\n", 115 | " density = 0.52\n", 116 | "\n", 117 | " print(f\"Length of road is {ncell}\")\n", 118 | " print(f\"Number of iterations is {maxiter}\")\n", 119 | " print(f\"Target density of cars is {density}\")\n", 120 | "\n", 121 | " # Initialise road accordingly using random number generator\n", 122 | " print(f\"Initialising ...\")\n", 123 | "\n", 124 | " ncars = initroad(oldroad[1:ncell+1], density, seedval)\n", 125 | "\n", 126 | " print(f\"Actual Density of cars is {format(float(ncars)/float(ncell))}\\n\")\n", 127 | "\n", 128 | " tstart = gettime()\n", 129 | "\n", 130 | " for iter in range(1, maxiter+1):\n", 131 | "\n", 132 | " updatebcs(oldroad)\n", 133 | "\n", 134 | " nmove = updateroad(newroad, oldroad)\n", 135 | "\n", 136 | " # Copy new to old array\n", 137 | " oldroad[1:ncell+1] = newroad[1:ncell+1]\n", 138 | "\n", 139 | " if iter % printfreq == 0:\n", 140 | "\n", 141 | " print(f\"At iteration {iter} average velocity is {float(nmove)/float(ncars):.6f}\")\n", 142 | "\n", 143 | " tstop = gettime()\n", 144 | "\n", 145 | " print(f\"\\nFinished\\n\")\n", 146 | " print(f\"Time taken was {tstop-tstart:.2f} seconds\")\n", 147 | " print(f\"Update rate was {1.0e-6*ncell*maxiter/(tstop-tstart):.2f} MCOPs\")\n", 148 | "\n", 149 | "if __name__ == \"__main__\":\n", 150 | " main(sys.argv[1:])\n" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": null, 156 | "id": "e234cbc7", 157 | "metadata": {}, 158 | "outputs": [], 159 | "source": [] 160 | } 161 | ], 162 | "metadata": { 163 | "kernelspec": { 164 | "display_name": "Python 3 (ipykernel)", 165 | "language": "python", 166 | "name": "python3" 167 | }, 168 | "language_info": { 169 | "codemirror_mode": { 170 | "name": "ipython", 171 | "version": 3 172 | }, 173 | "file_extension": ".py", 174 | "mimetype": "text/x-python", 175 | "name": "python", 176 | "nbconvert_exporter": "python", 177 | "pygments_lexer": "ipython3", 178 | "version": "3.9.12" 179 | } 180 | }, 181 | "nbformat": 4, 182 | "nbformat_minor": 5 183 | } 184 | -------------------------------------------------------------------------------- /notebooks/traffic/traffic.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "096d1239", 6 | "metadata": {}, 7 | "source": [ 8 | "# Library functions\n", 9 | "\n", 10 | "Here are the functions that initialise the road randomly and update it. We import numpy simply to get the random number generator - all the work is done using standard Python lists." 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "id": "71f95b28", 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "# %load trafficlib.py\n", 21 | "import numpy as np\n", 22 | "\n", 23 | "def initroad(road, density, seedval):\n", 24 | "\n", 25 | "# Here we expect a road without halos \n", 26 | "\n", 27 | " np.random.seed(seedval)\n", 28 | "\n", 29 | " n = len(road)\n", 30 | "\n", 31 | " ncar = 0\n", 32 | "\n", 33 | " for i in range(0, n):\n", 34 | " \n", 35 | " rng = np.random.random()\n", 36 | "\n", 37 | " if rng < density:\n", 38 | " road[i] = 1\n", 39 | " else:\n", 40 | " road[i] = 0\n", 41 | "\n", 42 | " ncar = ncar + road[i]\n", 43 | "\n", 44 | " return ncar\n", 45 | "\n", 46 | "\n", 47 | "def updateroad(newroad, oldroad):\n", 48 | "\n", 49 | " n = len(oldroad)-2\n", 50 | "\n", 51 | " nmove = 0\n", 52 | "\n", 53 | " for i in range(1, n+1):\n", 54 | "\n", 55 | " if oldroad[i] == 1:\n", 56 | " if oldroad[i+1] == 1:\n", 57 | " newroad[i] = 1\n", 58 | " else:\n", 59 | " newroad[i] = 0\n", 60 | " nmove = nmove + 1\n", 61 | " else:\n", 62 | " if oldroad[i-1] == 1:\n", 63 | " newroad[i] = 1\n", 64 | " else:\n", 65 | " newroad[i] = 0\n", 66 | "\n", 67 | "#\n", 68 | "# The version below will be easier to express as array operations\n", 69 | "# as it has fewer \"if\" statements.\n", 70 | "#\n", 71 | "# if oldroad[i] == 0:\n", 72 | "# newroad[i] = oldroad[i-1]\n", 73 | "# else:\n", 74 | "# newroad[i] = oldroad[i+1]\n", 75 | "#\n", 76 | "# if newroad[i] != oldroad[i]:\n", 77 | "# nmove = nmove + 1\n", 78 | "#\n", 79 | "# nmove = nmove/2\n", 80 | "#\n", 81 | "\n", 82 | " return nmove\n", 83 | "\n", 84 | "\n", 85 | "def updatebcs(road):\n", 86 | "\n", 87 | " n = len(road)-2\n", 88 | "\n", 89 | " road[0] = road[n]\n", 90 | " road[n+1] = road[1]\n", 91 | "\n", 92 | "\n", 93 | "import time\n", 94 | "\n", 95 | "def gettime():\n", 96 | "\n", 97 | " return time.time()\n" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "id": "c7f299f3", 103 | "metadata": {}, 104 | "source": [ 105 | "# Main program\n", 106 | "\n", 107 | "Here is the main program. Note that with these default parameters it will take a **long** time (several minutes) to run, but we will use this length of road for the numpy examples so it is worth running it at least once. The slightly strange value for `ncell` is because, when we run in parallel, we will require that the `ncell` is an exact multiple of the number of processes we use (just for simplicity). The number of processes is often a power of two, so this value lets us run on process counts from 1, 2, 4, ..., 2048, 4096 on ARCHER2. On your laptop I would not recommend using more than 8 processes!\n", 108 | "\n", 109 | "The number of iterations auto-adjusts to keep the runtime relatively constant. However, if you want to experiment with the performance using shorter roads then I would suggest reducing this, e.g. set `maxiter = 102400000//ncell`." 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "id": "a1149988", 115 | "metadata": {}, 116 | "source": [ 117 | " * Run the program and note the execution speed in MCOPs.\n", 118 | " * Reduce the value of maxiter as suggested above and check you get roughly the same speed.\n", 119 | " * Now try using a road which is a factor of ten smaller - does this affect the speed? What about a very short road of 1024?\n" 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": null, 125 | "id": "ebdf20b6", 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "# %load traffic.py\n", 130 | "#!/usr/bin/env python\n", 131 | "\n", 132 | "import sys\n", 133 | "import time\n", 134 | "\n", 135 | "from trafficlib import initroad, updatebcs, updateroad, gettime\n", 136 | "\n", 137 | "def main(argv):\n", 138 | "\n", 139 | " # Simulation parameters\n", 140 | " seedval = 5743\n", 141 | " ncell = 10240000\n", 142 | " maxiter = 1024000000//ncell\n", 143 | " printfreq = maxiter//10\n", 144 | "\n", 145 | " tmproad = [0 for col in range(ncell)]\n", 146 | " newroad = [0 for col in range(ncell+2)]\n", 147 | " oldroad = [0 for col in range(ncell+2)]\n", 148 | "\n", 149 | " density = 0.52\n", 150 | "\n", 151 | " print(f\"Length of road is {ncell}\")\n", 152 | " print(f\"Number of iterations is {maxiter}\")\n", 153 | " print(f\"Target density of cars is {density}\")\n", 154 | "\n", 155 | " # Initialise road accordingly using random number generator\n", 156 | " print(f\"Initialising ...\")\n", 157 | "\n", 158 | " ncars = initroad(tmproad, density, seedval)\n", 159 | "\n", 160 | " print(f\"Actual Density of cars is {float(ncars)/float(ncell)}\\n\")\n", 161 | "\n", 162 | " oldroad[1:ncell+1] = tmproad\n", 163 | " \n", 164 | " tstart = gettime()\n", 165 | "\n", 166 | " for iter in range(1, maxiter+1):\n", 167 | "\n", 168 | " updatebcs(oldroad)\n", 169 | "\n", 170 | " nmove = updateroad(newroad, oldroad)\n", 171 | "\n", 172 | " # Copy new to old array\n", 173 | " for i in range(1, ncell+1):\n", 174 | " oldroad[i] = newroad[i]\n", 175 | "\n", 176 | " if iter % printfreq == 0:\n", 177 | "\n", 178 | " print(f\"At iteration {iter} average velocity is {float(nmove)/float(ncars):.6f}\")\n", 179 | "\n", 180 | " tstop = gettime()\n", 181 | "\n", 182 | " print(f\"\\nFinished\\n\")\n", 183 | " print(f\"Time taken was {tstop-tstart:.2f} seconds\")\n", 184 | " print(f\"Update rate was {1.0e-6*ncell*maxiter/(tstop-tstart):.2f} MCOPs\")\n", 185 | "\n", 186 | "if __name__ == \"__main__\":\n", 187 | " main(sys.argv[1:])\n" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": null, 193 | "id": "75c662c7", 194 | "metadata": {}, 195 | "outputs": [], 196 | "source": [] 197 | } 198 | ], 199 | "metadata": { 200 | "kernelspec": { 201 | "display_name": "Python 3 (ipykernel)", 202 | "language": "python", 203 | "name": "python3" 204 | }, 205 | "language_info": { 206 | "codemirror_mode": { 207 | "name": "ipython", 208 | "version": 3 209 | }, 210 | "file_extension": ".py", 211 | "mimetype": "text/x-python", 212 | "name": "python", 213 | "nbconvert_exporter": "python", 214 | "pygments_lexer": "ipython3", 215 | "version": "3.9.12" 216 | } 217 | }, 218 | "nbformat": 4, 219 | "nbformat_minor": 5 220 | } 221 | -------------------------------------------------------------------------------- /notebooks/trafficmpi/trafficmpi.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "2f5024be", 6 | "metadata": {}, 7 | "source": [ 8 | "# Library functions\n", 9 | "\n", 10 | "The library functions are *exactly* the same as for the serial numpy version. If you write MPI code carefully, the parallelisation need not affect all of the code base. As here, you are often able to use the same routines to do the serial work (i.e. where there is no communications). The speedup comes because each process is operating on a smaller length of road, i.e. the value of `n` will be smaller here.\n", 11 | "\n", 12 | "`gettime` and `updatebcs` are no longer required as MPI has its own timer and the boundary values are now exchanged between processes using send and receive operations, but they are left it in as we want to use the same file as for the serial code." 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "id": "a07f7381", 19 | "metadata": {}, 20 | "outputs": [], 21 | "source": [ 22 | "# %load trafficlib.py\n", 23 | "import sys\n", 24 | "import numpy as np\n", 25 | "\n", 26 | "def initroad(road, density, seedval):\n", 27 | "\n", 28 | " # Here we expect a road without halos\n", 29 | "\n", 30 | " n = len(road)\n", 31 | "\n", 32 | " np.random.seed(seedval)\n", 33 | "\n", 34 | " rng = np.random.random(n)\n", 35 | "\n", 36 | " road[0:n] = np.where(rng[:] < density, 1, 0)\n", 37 | "\n", 38 | " ncar = np.sum(road)\n", 39 | " \n", 40 | " return ncar\n", 41 | "\n", 42 | "\n", 43 | "def updateroad(newroad, oldroad):\n", 44 | "\n", 45 | " n = len(oldroad)-2\n", 46 | "\n", 47 | " newroad[1:n+1] = np.where(oldroad[1:n+1]==0, oldroad[0:n], oldroad[2:n+2])\n", 48 | "\n", 49 | " nmove = (newroad[1:n+1] != oldroad[1:n+1]).sum(dtype=int)\n", 50 | " nmove = nmove / 2\n", 51 | "\n", 52 | " return nmove\n", 53 | "\n", 54 | "\n", 55 | "def updatebcs(road):\n", 56 | "\n", 57 | " n = len(road)-2\n", 58 | "\n", 59 | " road[0] = road[n]\n", 60 | " road[n+1] = road[1]\n", 61 | "\n", 62 | "\n", 63 | "import time\n", 64 | "\n", 65 | "def gettime():\n", 66 | "\n", 67 | " return time.time()\n" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "id": "0ce8536f", 73 | "metadata": {}, 74 | "source": [ 75 | "# Main program\n", 76 | "\n", 77 | "There are are a number of changes required to the main program.\n", 78 | "\n", 79 | "* We compute `size` and our `rank` - as is conventional, we nominate rank 0 as the controller (e.g. the process that prints any output)\n", 80 | "* The local number of cells `nlocal` is smaller than `ncell` by a factor of `size`. We still need an array `bigroad` to store the whole road as we do the initialisation in serial to ensure it is done identically to the serial numpy code. We also check for consistency - it would be straightforward to allow an arbitary length of road but this would make the code a little more complicated.\n", 81 | "* `bigroad` is scattered to all the processes. Note that *all* processes call `comm.Scatter` although they have different roles as the input data only exists on rank 0.\n", 82 | "* We compute our neighbours up and down, taking into account the periodic boundary conditions (i.e. the cars are on a roundabout).\n", 83 | "* The barrier calls are solely there to make sure that the processes start and finish the main calculation at the same time, which gives us more reliable runtimes. Removing all the barriers will have *no effect* on the correctness of the program.\n", 84 | "* Halos are exchanged up and down using a combined send and receive call `Sendrecv`.\n", 85 | "* Each process can compute how many cars moved on its section of road but to get the total number we need to sum up across all processes using `Allreduce`. To use the fast numpy MPI functions I use a numpy array of size 1 rather than a scalar.\n", 86 | "\n", 87 | "If you execute the program in the notebook it will run using a single process. As the time comparable to what you saw with the serial numpy version? Is the result the same, i.e. is the final velocity identical to before?\n", 88 | "\n", 89 | "The main parallel exercises are in the cell below the program." 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "id": "3f589f32", 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "# %load traffic.py\n", 100 | "#!/usr/bin/env python\n", 101 | "\n", 102 | "import sys\n", 103 | "import time\n", 104 | "import numpy as np\n", 105 | "\n", 106 | "from mpi4py import MPI\n", 107 | "\n", 108 | "from trafficlib import initroad, updatebcs, updateroad, gettime\n", 109 | "\n", 110 | "def main(argv):\n", 111 | "\n", 112 | " comm = MPI.COMM_WORLD\n", 113 | "\n", 114 | " size = comm.Get_size()\n", 115 | " rank = comm.Get_rank()\n", 116 | "\n", 117 | " # Simulation parameters\n", 118 | " seedval = 5743\n", 119 | " ncell = 10240000\n", 120 | " maxiter = 1024000000//ncell\n", 121 | " printfreq = maxiter//10\n", 122 | "\n", 123 | " nlocal = ncell//size\n", 124 | "\n", 125 | " # Check consistency\n", 126 | "\n", 127 | " if (nlocal*size != ncell):\n", 128 | " if (rank == 0):\n", 129 | " print(f\"ERROR: ncell = {ncell} not a multiple of size = {size}\")\n", 130 | " exit()\n", 131 | "\n", 132 | " bigroad = np.zeros(ncell,dtype=np.int32)\n", 133 | " newroad = np.zeros(nlocal+2,dtype=np.int32)\n", 134 | " oldroad = np.zeros(nlocal+2,dtype=np.int32)\n", 135 | "\n", 136 | " sbuf = np.zeros(1)\n", 137 | " rbuf = np.zeros(1)\n", 138 | "\n", 139 | " density = 0.52\n", 140 | "\n", 141 | " if (rank == 0):\n", 142 | "\n", 143 | " print(f\"Length of road is {ncell}\")\n", 144 | " print(f\"Number of iterations is {maxiter}\")\n", 145 | " print(f\"Target density of cars is {density}\")\n", 146 | " print(f\"Running on {size} process(es)\")\n", 147 | "\n", 148 | " # Initialise road accordingly using random number generator\n", 149 | " print(f\"Initialising ...\")\n", 150 | "\n", 151 | " ncars = initroad(bigroad, density, seedval)\n", 152 | "\n", 153 | " print(f\"Actual Density of cars is {format(float(ncars)/float(ncell))}\\n\")\n", 154 | " print(f\"Scattering data ...\")\n", 155 | "\n", 156 | " comm.Scatter(bigroad, oldroad[1:nlocal+1], root=0)\n", 157 | "\n", 158 | " if (rank == 0):\n", 159 | " print(f\"... done\\n\")\n", 160 | "\n", 161 | " # Compute neighbours\n", 162 | "\n", 163 | " rankup = (rank + 1)\n", 164 | " rankdown = (rank - 1)\n", 165 | "\n", 166 | " # Wrap-around for cyclic boundary conditions, i.e. a roundabout\n", 167 | "\n", 168 | " if (rankup == size):\n", 169 | " rankup = 0\n", 170 | "\n", 171 | " if (rankdown == -1):\n", 172 | " rankdown = size-1\n", 173 | "\n", 174 | " nmove = 0\n", 175 | " nmovelocal = 0\n", 176 | "\n", 177 | " comm.barrier()\n", 178 | " \n", 179 | " tstart = MPI.Wtime()\n", 180 | "\n", 181 | " for iter in range(1, maxiter+1):\n", 182 | "\n", 183 | " comm.Sendrecv(oldroad[nlocal:nlocal+1], dest=rankup,\n", 184 | " recvbuf=oldroad[0:1], source=rankdown)\n", 185 | "\n", 186 | " comm.Sendrecv(oldroad[1:2], dest=rankdown,\n", 187 | " recvbuf=oldroad[nlocal+1:nlocal+2], source=rankup)\n", 188 | "\n", 189 | " nmovelocal = updateroad(newroad, oldroad)\n", 190 | "\n", 191 | " sbuf[0] = nmovelocal\n", 192 | " comm.Allreduce(sbuf, rbuf)\n", 193 | " nmove = rbuf[0]\n", 194 | "\n", 195 | " # Copy new to old array\n", 196 | " oldroad[1:nlocal+1] = newroad[1:nlocal+1]\n", 197 | "\n", 198 | " if iter % printfreq == 0:\n", 199 | "\n", 200 | " if (rank == 0):\n", 201 | "\n", 202 | " print(f\"At iteration {iter} average velocity is {float(nmove)/float(ncars):.6f}\")\n", 203 | "\n", 204 | " comm.barrier()\n", 205 | "\n", 206 | " tstop = MPI.Wtime()\n", 207 | "\n", 208 | " if (rank == 0):\n", 209 | "\n", 210 | " print(f\"\\nFinished\\n\")\n", 211 | " print(f\"Time taken was {tstop-tstart:.2f} seconds\")\n", 212 | " print(f\"Update rate was {1.0e-6*ncell*maxiter/(tstop-tstart):.2f} MCOPs\")\n", 213 | "\n", 214 | "if __name__ == \"__main__\":\n", 215 | " main(sys.argv[1:])\n" 216 | ] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "id": "ac2e972f", 221 | "metadata": {}, 222 | "source": [ 223 | "## Parallel exercises\n", 224 | "\n", 225 | "\n", 226 | "Mac and Linux users should use the first method of running. Windows users should use the second method as, without a timeout, Windows MPI does not seem to exit gracefully if the program crashes. Both use shell escape to execute a local command.\n", 227 | "\n", 228 | "To get the best performance and reliable runtimes you should make sure you are running as few other programs as possible on your laptop, e.g. quit as many browser tabs as possible, shut down Spotify and Twitter, ...\n", 229 | "\n", 230 | "* Execute the program on a single process and check if the performance is the same as running in the notebook. You will not see continuous output - it will all appear at the end so there will be some delay.\n", 231 | "\n", 232 | "* Now run on two processes using `-n 2`. Is the program any faster? Is the answer the same as before?\n", 233 | "* Run on 4 and 8 processes - does the code get any faster? It can be interesting to run a performance monitor while doing this as you should see multiple Python programs running at the same time (which may put a heavy load on your machine!).\n", 234 | "\n", 235 | "If you want to change the program (e.g. alter the length of the road) you will have to edit the copy stored locally on your machine - changing the program above in the notebook will have no effect. To do this use \"File -> Open\" from the jupyter menu and remember to save your changes before re-running the program." 236 | ] 237 | }, 238 | { 239 | "cell_type": "markdown", 240 | "id": "6b2f5389", 241 | "metadata": {}, 242 | "source": [ 243 | "**Mac and Linux users run this command**:" 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": null, 249 | "id": "3d73895e", 250 | "metadata": {}, 251 | "outputs": [], 252 | "source": [ 253 | "!mpiexec -n 1 python traffic.py" 254 | ] 255 | }, 256 | { 257 | "cell_type": "markdown", 258 | "id": "8c0d5270", 259 | "metadata": {}, 260 | "source": [ 261 | "**Windows users run this command**:" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": null, 267 | "id": "b32bacbf", 268 | "metadata": {}, 269 | "outputs": [], 270 | "source": [ 271 | "!mpiexec /timeout 20 -n 1 python traffic.py" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": null, 277 | "id": "44d2f00c", 278 | "metadata": {}, 279 | "outputs": [], 280 | "source": [] 281 | } 282 | ], 283 | "metadata": { 284 | "kernelspec": { 285 | "display_name": "Python 3 (ipykernel)", 286 | "language": "python", 287 | "name": "python3" 288 | }, 289 | "language_info": { 290 | "codemirror_mode": { 291 | "name": "ipython", 292 | "version": 3 293 | }, 294 | "file_extension": ".py", 295 | "mimetype": "text/x-python", 296 | "name": "python", 297 | "nbconvert_exporter": "python", 298 | "pygments_lexer": "ipython3", 299 | "version": "3.9.12" 300 | } 301 | }, 302 | "nbformat": 4, 303 | "nbformat_minor": 5 304 | } 305 | -------------------------------------------------------------------------------- /notebooks/mpi4py/mpi4py.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "
\n", 8 | "\n", 9 | "## ARCHER2 SCIENTIFIC PYTHON COURSE\n", 10 | "\n", 11 | "# Parallel Processing and mpi4py\n", 12 | "
\n", 13 | "\n", 14 | "## Website: http://www.archer2.ac.uk \n", 15 | "\n", 16 | "## Helpdesk: support@archer2.ac.uk\n", 17 | "\n", 18 | "
\n", 19 | "\n", 20 | "\n", 21 | "
\n", 22 | "\n", 23 | "
\n", 24 | "\n", 25 | "
\n", 26 | "\n", 27 | "
" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": {}, 33 | "source": [ 34 | "" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "\n", 42 | "
\n", 43 | "
\n", 44 | "\n", 45 | "## General\n", 46 | "\n", 47 | "
\n", 48 | "
\n", 49 | "\n", 50 | "\n", 51 | "The `mpi4py` python interface follows the MPI C++ interface (actually removed from the latest MPI standard). Formally, the MPI standard says nothing about python.\n", 52 | "\n", 53 | "Internally, the `mpi4py` _implementation_ uses C.\n", 54 | "\n", 55 | "Some appreciation of the object-oriented nature in python is useful.\n", 56 | "\n", 57 | "The C++ names can be - annoyingly - slightly different from the corresponding C/Fortran bindings.\n", 58 | "They are often reversed, e.g.,\n", 59 | "\n", 60 | "```\n", 61 | "MPI_Buffer_attach() becomes MPI.Attach_buffer()\n", 62 | "```\n", 63 | "\n", 64 | "The table on C++ bindings in the MPI standard (annex) can provide a useful quick reference.\n", 65 | "\n", 66 | "ipython `help(mpi4py.MPI)` is rather long, so it can be helpful to narrow the search by e.g., `help(mpi4py.MPI.Intracomm)`. However this only works if you know what you are looking for.\n" 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": {}, 72 | "source": [ 73 | "
\n", 74 | "
\n", 75 | "\n", 76 | "## MPI Environment\n", 77 | "\n", 78 | "
\n", 79 | "
\n", 80 | "\n", 81 | "\n", 82 | "Python code using message passing should import the `mpi4py` python module, typically,\n", 83 | "\n", 84 | "```py\n", 85 | "from mpi4py import MPI\n", 86 | "```\n", 87 | "\n", 88 | "The `import mpi4py` statement is responsible for initialising MPI (if not already initialised)\n", 89 | "so there is no analogue of calls to `MPI_Init()` and `MPI_Finalize()` in typical python code.\n", 90 | "\n", 91 | "### `MPI.COMM_WORLD`\n", 92 | "\n", 93 | "All communicators are python objects, and the pre-defined communicator is\n", 94 | "`COMM_WORLD` accessed via the `MPI` module, e.g.,\n", 95 | "\n", 96 | "```py\n", 97 | "rank = MPI.COMM_WORLD.rank # May also see comm.Get_rank()\n", 98 | "size = MPI.COMM_WORLD.size # May also see comm.Get_size()\n", 99 | " ```\n", 100 | "\n", 101 | "\n", 102 | "\n", 103 | "### Example\n" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": null, 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [ 112 | "# %load example1.py\n", 113 | "\"\"\"Importing and using MPI.COMM_WORLD\"\"\"\n", 114 | "\n", 115 | "import sys\n", 116 | "from mpi4py import MPI\n", 117 | "\n", 118 | "def main(comm):\n", 119 | " \"\"\"Report rank and size of this communicator\"\"\"\n", 120 | " rank = comm.rank\n", 121 | " size = comm.size\n", 122 | " sys.stdout.write(\"Hello from rank {:2d} of {:2d}\\n\".format(rank, size))\n", 123 | "\n", 124 | "if __name__ == \"__main__\":\n", 125 | " \"\"\"Execute in MPI.COMM_WORLD\"\"\"\n", 126 | " main(MPI.COMM_WORLD)\n" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": null, 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "# While one may load the examples for inspection (as above),\n", 136 | "# cells themselves execute in serial.\n", 137 | "\n", 138 | "# Use the shell escape to run the script via mpiexec\n", 139 | "!mpiexec -n 8 python example1.py" 140 | ] 141 | }, 142 | { 143 | "cell_type": "markdown", 144 | "metadata": {}, 145 | "source": [ 146 | "
\n", 147 | "
\n", 148 | "
\n", 149 | "\n", 150 | "## Point-to-Point Messages\n", 151 | "\n", 152 | "
\n", 153 | "
\n", 154 | "\n", 155 | "\n", 156 | "The interface makes significant use of optional arguments:\n", 157 | "\n", 158 | "```py\n", 159 | "Comm.Send(self, buf, int dest, int tag=0)\n", 160 | "Comm.Recv(self, buf, int source=ANY_SOURCE, int tag=ANY_TAG,\n", 161 | " Status status=None) \n", 162 | "```\n", 163 | "\n", 164 | "Note **upper case** in `Send()` and `Recv()`: these are for \"buffer-like\" data.\n", 165 | "\n", 166 | "There are also distinct `send()` and `recv()` methods; see below.\n", 167 | "\n", 168 | "There are no count or data type arguments associated with the message buffer `buf` (at first sight).\n", 169 | "\n", 170 | "\n", 171 | "
\n", 172 | "\n", 173 | "### Point-to-point messages using `numpy.ndarray` buffers\n", 174 | "\n", 175 | "A numerical application might typically wish to communicate contiguous arrays of data:\n", 176 | "\n", 177 | "\n", 178 | "```py\n", 179 | "import numpy\n", 180 | "sz = 1000\n", 181 | "buf = numpy.zeros(sz, type = numpy.double)\n", 182 | "\n", 183 | "if rank == 0:\n", 184 | " MPI.COMM_WORLD.Send([buf, sz, MPI.DOUBLE], 1, tag = 99)\n", 185 | "elif rank == 1:\n", 186 | " MPI.COMM_WORLD.Recv([buf, sz, MPI.DOUBLE], source = 0, \\\n", 187 | " tag = 99)\n", 188 | "```\n", 189 | "\n", 190 | "Here the count and data type are explicitly specified as part of a list and can be omitted.\n", 191 | "\n", 192 | "\n", 193 | "
\n", 194 | "\n", 195 | "### Example: Blocking Send/Recv\n", 196 | "\n" 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": null, 202 | "metadata": {}, 203 | "outputs": [], 204 | "source": [ 205 | "# %load example2.py\n", 206 | "\"\"\"A simple blocking Send/Recv pair\"\"\"\n", 207 | "\n", 208 | "import sys\n", 209 | "import numpy\n", 210 | "from mpi4py import MPI\n", 211 | "\n", 212 | "def main(comm, sz):\n", 213 | " \"\"\"Send a message between ranks 0 and 1\"\"\"\n", 214 | " rank = comm.Get_rank()\n", 215 | "\n", 216 | " if rank == 0:\n", 217 | " msg = numpy.ones(sz, numpy.double)\n", 218 | " comm.Ssend([msg, sz, MPI.DOUBLE], dest = 1, tag = 999)\n", 219 | " elif rank == 1:\n", 220 | " msg = numpy.zeros(sz, numpy.double)\n", 221 | " comm.Recv([msg, sz, MPI.DOUBLE], source = 0, tag = 999)\n", 222 | " sys.stdout.write(\"Rank 1 received {}\".format(msg))\n", 223 | "\n", 224 | "if __name__ == \"__main__\":\n", 225 | "\n", 226 | " sz = 4\n", 227 | " main(MPI.COMM_WORLD, sz)\n" 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": null, 233 | "metadata": {}, 234 | "outputs": [], 235 | "source": [ 236 | "!mpiexec -n 2 python ./example2.py" 237 | ] 238 | }, 239 | { 240 | "cell_type": "markdown", 241 | "metadata": {}, 242 | "source": [ 243 | "
\n", 244 | "
\n", 245 | "
\n", 246 | "\n", 247 | "## Non-Blocking Point-to-Point Messages\n", 248 | "\n", 249 | "
\n", 250 | "
\n", 251 | "\n", 252 | "\n", 253 | "Non-blocking (\"immediate\") methods are of the form:\n", 254 | "\n", 255 | "```py\n", 256 | "Comm.Isend(self, buf, int dest, int tag=0)\n", 257 | "Comm.Irecv(self, buf, int source=ANY_SOURCE, int tag=ANY_TAG)\n", 258 | "```\n", 259 | "\n", 260 | "Both these return objects of the `Request` class. \n", 261 | "\n", 262 | "Request objects are handled either by instance methods, e.g.,\n", 263 | "```py\n", 264 | "request.Wait(self, Status status=None)\n", 265 | "```\n", 266 | "\n", 267 | "or class methods, e.g.,\n", 268 | "```py\n", 269 | "Request.Waitall(type cls, requests, statuses=None)\n", 270 | "```\n", 271 | "\n", 272 | "
\n", 273 | "\n", 274 | "### Example: Non-blocking communication" 275 | ] 276 | }, 277 | { 278 | "cell_type": "code", 279 | "execution_count": null, 280 | "metadata": {}, 281 | "outputs": [], 282 | "source": [ 283 | "# %load example3.py\n", 284 | "\"\"\"An Example of non-blocking Isend/Irecv pairs\"\"\"\n", 285 | "\n", 286 | "import sys\n", 287 | "import numpy\n", 288 | "from mpi4py import MPI\n", 289 | "\n", 290 | "def main(comm):\n", 291 | " \"\"\"Exchange messages with 'ajoining' ranks\"\"\"\n", 292 | " p1 = comm.rank + 1\n", 293 | " m1 = comm.rank - 1\n", 294 | " if p1 >= comm.size: p1 = 0\n", 295 | " if m1 < 0: m1 = comm.size - 1\n", 296 | "\n", 297 | " smsg = numpy.array([comm.rank], numpy.int32)\n", 298 | " rmsg = numpy.zeros(2, numpy.int32)\n", 299 | "\n", 300 | " reqs1 = comm.Issend(smsg, p1)\n", 301 | " reqs2 = comm.Issend(smsg, m1)\n", 302 | " reqr1 = comm.Irecv(rmsg[0:], source = p1)\n", 303 | " reqr2 = comm.Irecv(rmsg[1:], source = m1)\n", 304 | "\n", 305 | " reqr1.Wait(); reqr2.Wait()\n", 306 | " sys.stdout.write(\"Rank {} received messages from ranks {} and {}\\n\"\\\n", 307 | " .format(smsg, rmsg[1], rmsg[0]))\n", 308 | "\n", 309 | " MPI.Request.Waitall([reqs1, reqs2])\n", 310 | "\n", 311 | "if __name__ == \"__main__\":\n", 312 | "\n", 313 | " main(MPI.COMM_WORLD)\n" 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": null, 319 | "metadata": { 320 | "scrolled": true 321 | }, 322 | "outputs": [], 323 | "source": [ 324 | "!mpiexec /timeout 10 -n 4 python ./example3.py" 325 | ] 326 | }, 327 | { 328 | "cell_type": "markdown", 329 | "metadata": {}, 330 | "source": [ 331 | "
\n", 332 | "
\n", 333 | "
\n", 334 | "\n", 335 | "## Python objects as messages\n", 336 | "\n", 337 | "
\n", 338 | "
\n", 339 | "\n", 340 | "\n", 341 | "As python objects can be serialised, message passing is available:\n", 342 | "\n", 343 | "```py\n", 344 | "Comm.send(self, obj, int dest, int tag=0)\n", 345 | "Comm.recv(self, buf=None, int source=ANY_SOURCE, int tag=ANY_TAG,\n", 346 | " Status status=None) \n", 347 | "```\n", 348 | "\n", 349 | "Note **lower case** `send()` and `recv()`.\n", 350 | "\n", 351 | "Serialisation and deserialisation carry an overhead in memory and time; complex/large objects may be slow.\n", 352 | "\n", 353 | "
\n", 354 | "\n", 355 | "### Example: sending a list\n", 356 | "\n", 357 | "Note the incoming message is received as the return value\n", 358 | "\n", 359 | "```py\n", 360 | "msg = comm.recv(...)\n", 361 | "```" 362 | ] 363 | }, 364 | { 365 | "cell_type": "code", 366 | "execution_count": null, 367 | "metadata": {}, 368 | "outputs": [], 369 | "source": [ 370 | "# %load example4.py\n", 371 | "\"\"\"Message passing a python object\"\"\"\n", 372 | "\n", 373 | "import sys\n", 374 | "from mpi4py import MPI\n", 375 | "\n", 376 | "def main(comm):\n", 377 | " \"\"\"Send a list from rank 0 to rank 1\"\"\"\n", 378 | "\n", 379 | " if comm.rank == 0:\n", 380 | " msg = [\"Any\", \"old\", \"thing\", comm.rank, {\"size\" : comm.size}]\n", 381 | " comm.send(msg, dest = 1, tag = 999)\n", 382 | " elif comm.rank == 1:\n", 383 | " msg = comm.recv(source = 0, tag = 999)\n", 384 | " sys.stdout.write(\"Rank 1 received {}\\n\".format(msg))\n", 385 | "\n", 386 | "if __name__ == \"__main__\":\n", 387 | "\n", 388 | " main(MPI.COMM_WORLD)\n" 389 | ] 390 | }, 391 | { 392 | "cell_type": "code", 393 | "execution_count": null, 394 | "metadata": {}, 395 | "outputs": [], 396 | "source": [ 397 | "! mpiexec -n 2 python ./example4.py" 398 | ] 399 | }, 400 | { 401 | "cell_type": "markdown", 402 | "metadata": {}, 403 | "source": [ 404 | "
\n", 405 | "
\n", 406 | "
\n", 407 | "\n", 408 | "## Collective Communication\n", 409 | "\n", 410 | "
\n", 411 | "
\n", 412 | "\n", 413 | "\n", 414 | "Collective operations are implemented as methods on `Comm`, e.g.,\n", 415 | "\n", 416 | "```py\n", 417 | "Comm.Bcast(self, buf, int root=0)\n", 418 | "```\n", 419 | "\n", 420 | "Use `numpy.ndarray` as the actual data, or other contiguous buffer.\n", 421 | "\n", 422 | "Again, note **upper case** `Bcast()`. Distinct `bcast()` etc are intended for python objects.\n", 423 | "\n", 424 | "\n", 425 | "
\n", 426 | "\n", 427 | "### Reductions\n", 428 | "\n", 429 | "Reductions involve the `Op` class, e.g.,\n", 430 | "\n", 431 | "```py\n", 432 | "Comm.Reduce(self, sendbuf, recvbuf, Op op=SUM, int root=0)\n", 433 | "```\n", 434 | "\n", 435 | "Operations include `MIN`, `MAX`, `SUM` and so on.\n", 436 | "\n", 437 | "\n" 438 | ] 439 | }, 440 | { 441 | "cell_type": "code", 442 | "execution_count": null, 443 | "metadata": {}, 444 | "outputs": [], 445 | "source": [ 446 | "# %load example5a.py\n", 447 | "import numpy\n", 448 | "import sys\n", 449 | "from mpi4py import MPI\n", 450 | "\n", 451 | "def main(comm):\n", 452 | " \"\"\"The sum of a list is...\"\"\"\n", 453 | "\n", 454 | " total_sum = numpy.array(0, 'i')\n", 455 | " rank = numpy.array(comm.rank, 'i')\n", 456 | " comm.Reduce([rank, MPI.INTEGER], [total_sum, MPI.INTEGER], op=MPI.SUM, root=0)\n", 457 | "\n", 458 | " if comm.rank == 0:\n", 459 | " sys.stdout.write(\"Sum of ranks: {}\\n\".format(total_sum))\n", 460 | "\n", 461 | "\n", 462 | "if __name__ == \"__main__\":\n", 463 | "\n", 464 | " main(MPI.COMM_WORLD)\n", 465 | "\n" 466 | ] 467 | }, 468 | { 469 | "cell_type": "code", 470 | "execution_count": null, 471 | "metadata": {}, 472 | "outputs": [], 473 | "source": [ 474 | "!mpiexec -n 4 python example5a.py" 475 | ] 476 | }, 477 | { 478 | "cell_type": "markdown", 479 | "metadata": {}, 480 | "source": [ 481 | "\n", 482 | "## Reductions with python objects\n", 483 | "\n", 484 | "\n", 485 | "The equivalent method for python objects is:\n", 486 | "\n", 487 | "```py\n", 488 | "Comm.reduce(self, sendobj, op=SUM, int root=0)\n", 489 | "```\n", 490 | "\n", 491 | "Built-in `Op` operations rely on the relevant special method [e.g., `__add__()`] being implemented for `sendobj`.\n", 492 | "\n", 493 | "You can create your own `Op` functions.\n", 494 | "\n", 495 | "Standard python types should behave \"as expected\".\n", 496 | "\n", 497 | "\n", 498 | "### Example" 499 | ] 500 | }, 501 | { 502 | "cell_type": "code", 503 | "execution_count": null, 504 | "metadata": {}, 505 | "outputs": [], 506 | "source": [ 507 | "# %load example5.py\n", 508 | "\n", 509 | "import sys\n", 510 | "from mpi4py import MPI\n", 511 | "\n", 512 | "def main(comm):\n", 513 | " \"\"\"The sum of a list is...\"\"\"\n", 514 | "\n", 515 | " mylist = []\n", 516 | " mylist = comm.reduce([comm.rank], op=MPI.SUM, root=0)\n", 517 | "\n", 518 | " if comm.rank == 0:\n", 519 | " sys.stdout.write(\"List of ranks: {}\\n\".format(mylist))\n", 520 | "\n", 521 | "\n", 522 | "if __name__ == \"__main__\":\n", 523 | "\n", 524 | " main(MPI.COMM_WORLD)\n", 525 | "\n" 526 | ] 527 | }, 528 | { 529 | "cell_type": "code", 530 | "execution_count": null, 531 | "metadata": {}, 532 | "outputs": [], 533 | "source": [ 534 | "! mpiexec -n 4 python ./example5.py" 535 | ] 536 | }, 537 | { 538 | "cell_type": "markdown", 539 | "metadata": {}, 540 | "source": [ 541 | "
\n", 542 | "
\n", 543 | "
\n", 544 | "\n", 545 | "## Communicators\n", 546 | "\n", 547 | "
\n", 548 | "
\n", 549 | "\n", 550 | "A communicator provides the context within which communication will occur.\n", 551 | "\n", 552 | "\n", 553 | "The class heirarchy for communicator objects is\n", 554 | "\n", 555 | "```\n", 556 | "Comm\n", 557 | " Intracomm\n", 558 | " Intercomm\n", 559 | " Topocomm\n", 560 | " Cartcomm\n", 561 | " Distgraphcomm\n", 562 | " Graphcomm\n", 563 | "```\n", 564 | "\n", 565 | "Many methods are implemented on `Comm` and inherited by subclasses, e.g.,\n", 566 | "\n", 567 | "```\n", 568 | " comm.Barrier()\n", 569 | "```\n", 570 | "\n", 571 | "\n", 572 | "
\n", 573 | "\n", 574 | "### Example\n", 575 | "\n", 576 | "Creating a Cartesian communicator `Cartcomm` object from an existing `Intracomm` object uses:\n", 577 | "```\n", 578 | "Intracomm.Create_cart(self, dims, periods=None, reorder=False)\n", 579 | "```\n", 580 | "\n", 581 | "and will return a new Cartesian communicator.\n", 582 | "\n", 583 | "Methods for this object include `Get_cart_rank()`, `Get_coords()` and so on.\n", 584 | "\n", 585 | "
\n" 586 | ] 587 | }, 588 | { 589 | "cell_type": "code", 590 | "execution_count": null, 591 | "metadata": {}, 592 | "outputs": [], 593 | "source": [ 594 | "# %load example6.py\n", 595 | "\"\"\"Creating a Cartesian Communicator\"\"\"\n", 596 | "\n", 597 | "import sys\n", 598 | "from mpi4py import MPI\n", 599 | "\n", 600 | "def main(parent):\n", 601 | " \"\"\"Create a 2-d Cartesian communicator from parent communicator\"\"\"\n", 602 | "\n", 603 | " dims = MPI.Compute_dims(parent.size, 2)\n", 604 | "\n", 605 | " comm = parent.Create_cart(dims, periods = [True, False])\n", 606 | " rank = comm.Get_rank()\n", 607 | " coords = comm.Get_coords(rank)\n", 608 | " upx = comm.Shift(0, 1)\n", 609 | " upy = comm.Shift(1, 1)\n", 610 | "\n", 611 | " out = \"Rank{:2d} coords{:2d} {:2d} upx(src,dst) {} upy(src,dst) {}\\n\"\\\n", 612 | " .format(rank, coords[0], coords[1], upx, upy)\n", 613 | " sys.stdout.write(out)\n", 614 | "\n", 615 | "if __name__ == \"__main__\":\n", 616 | " \"\"\"Execute in MPI.COMM_WORLD\"\"\"\n", 617 | " main(MPI.COMM_WORLD)\n" 618 | ] 619 | }, 620 | { 621 | "cell_type": "code", 622 | "execution_count": null, 623 | "metadata": {}, 624 | "outputs": [], 625 | "source": [ 626 | "!mpiexec -n 16 python ./example6.py" 627 | ] 628 | }, 629 | { 630 | "cell_type": "markdown", 631 | "metadata": {}, 632 | "source": [ 633 | "
\n", 634 | "
\n", 635 | "
\n", 636 | "\n", 637 | "## Other features\n", 638 | "\n", 639 | "
\n", 640 | "
\n", 641 | "\n", 642 | "\n", 643 | "Standard MPI functionality is available (`mpi4py` version 2.0):\n", 644 | "\n", 645 | "* `Comm.Abort()`, `MPI.Wtime()`, ...\n", 646 | "\n", 647 | "\n", 648 | "* User-defined types (methods implemented in the `Datatype` class)\n", 649 | "\n", 650 | "\n", 651 | "* Single-sided communication (see the `Win` class)\n", 652 | "\n", 653 | "\n", 654 | "* MPI-IO (see the `File` class)\n", 655 | "\n", 656 | "\n", 657 | "* a few functions are not implemented, e.g., `AlltoAllw()`\n", 658 | "\n", 659 | "\n", 660 | "
\n", 661 | "\n", 662 | "### Some other considerations\n", 663 | "\n", 664 | "* Large numbers of MPI tasks can mean that the action of using `import` to load libraries across all these tasks can take a long time and slow down the starting of the parallel program. \n", 665 | "\n", 666 | "\n", 667 | "* Can start in python and call another language\n", 668 | " - can typically pass a communicator and other relevant data\n", 669 | " - convenient access to C `MPI_Comm` handles requires `mpi4py` 2.0.\n", 670 | "\n", 671 | "
\n", 672 | "\n", 673 | "\n", 674 | "\n" 675 | ] 676 | }, 677 | { 678 | "cell_type": "code", 679 | "execution_count": null, 680 | "metadata": {}, 681 | "outputs": [], 682 | "source": [ 683 | "from mpi4py import MPI\n", 684 | "help(MPI)" 685 | ] 686 | }, 687 | { 688 | "cell_type": "markdown", 689 | "metadata": {}, 690 | "source": [ 691 | "
\n", 692 | "
\n", 693 | "
\n", 694 | "\n", 695 | "## ARCHER2 Exercise: Compare `Send()` and `send()`\n", 696 | "\n", 697 | "
\n", 698 | "
\n", 699 | "\n", 700 | "\n", 701 | "#### Logging in\n", 702 | "\n", 703 | "With your guest account:\n", 704 | "```bash\n", 705 | "local-shell> ssh -X username@login.archer2.ac.uk\n", 706 | "```\n", 707 | "\n", 708 | "\n", 709 | "
\n", 710 | "\n", 711 | "#### The `Send()` example `exercise1.py`\n", 712 | "\n", 713 | "This python script measures the time taken to pass a message back and forth between two MPI tasks. If we know the amount of data transferred, and the time taken, we can work out the bandwidth in bytes/second. In the\n", 714 | "limit of zero message size, this time is the latency - the time taken to transfer a message at all.\n", 715 | "\n", 716 | "Have a look at the script `exercise1.py` and check exactly what the `Send()` - actually `Ssend()` - and `Recv()` are doing.\n", 717 | "\n", 718 | "\n", 719 | "
\n", 720 | "\n", 721 | "#### Running the Send() version\n", 722 | "\n", 723 | "Compute jobs on ARCHER2 must be submitted to the queue system. A submission script is provided. This should look like:\n", 724 | "\n", 725 | "```bash\n", 726 | "#!/bin/bash --login\n", 727 | "\n", 728 | "#SBATCH --job-name mpi4py\n", 729 | "#SBATCH --nodes=1 # Request one node\n", 730 | "#SBATCH --tasks-per-node=2 # Request two tasks per node\n", 731 | "#SBATCH --cpus-per-task=1 # Request one cpu per task\n", 732 | "#SBATCH --time=00:20:00\n", 733 | "\n", 734 | "#SBATCH --account=ta054\n", 735 | "#SBATCH --partition=standard\n", 736 | "#SBATCH --qos=short\n", 737 | "\n", 738 | "module load cray-python\n", 739 | "\n", 740 | "# Run with two MPI tasks \n", 741 | "srun python exercise1.py\n", 742 | "```\n", 743 | "\n", 744 | "Note that two relevant modules are loaded to provide access to python on the back end. For further information see\n", 745 | "\n", 746 | "https://docs.archer2.ac.uk/user-guide/python/\n", 747 | "\n", 748 | "
\n", 749 | "\n", 750 | "#### Submitting the script\n", 751 | "\n", 752 | "The `bash` script (not the python script) is submitted to the queue system using\n", 753 | "```bash\n", 754 | "guest123@login001:> sbatch \n", 755 | "```\n", 756 | "\n", 757 | "Run the example script and check the output; this should report the time taken to exchange messages of a given size bettwen two MPI tasks.\n", 758 | "\n", 759 | "
\n", 760 | "\n", 761 | "#### Change the placement of the MPI tasks (optional)\n", 762 | "\n", 763 | "Try altering the submission script so that the two MPI tasks each run on a different node. The altered script should include the changes:\n", 764 | "```bash\n", 765 | "#SBATCH --nodes=2 # Request two nodes\n", 766 | "#SBATCH --tasks-per-node=1 # Request one task per node\n", 767 | "...\n", 768 | "srun python ./exercise1.py\n", 769 | "```\n", 770 | "\n", 771 | "Can you see if there is any difference in the reported results?\n", 772 | "\n", 773 | "\n", 774 | "
\n", 775 | "\n", 776 | "#### Exercise\n", 777 | "\n", 778 | "Rewrite the python example so that is uses `send()` and `recv()` rather than `Send()` and `Recv()`.\n", 779 | "\n", 780 | "Instead of using a `numpy.ndarray` object for the message data, try using a standard python list containing the required number of data items.\n", 781 | "\n", 782 | "What influence does this have on the measured bandwidth?\n", 783 | "
" 784 | ] 785 | }, 786 | { 787 | "cell_type": "markdown", 788 | "metadata": {}, 789 | "source": [ 790 | "
\n", 791 | "
\n", 792 | "\n", 793 | "## Summary\n", 794 | "\n", 795 | "
\n", 796 | "
\n", 797 | "\n", 798 | "* `mpi4py` can be used to write parallel python programs\n", 799 | "\n", 800 | "\n", 801 | "* Can be used to interact with other programs using suitable communicator\n", 802 | " \n", 803 | "
\n", 804 | "\n", 805 | "* Useful links\n", 806 | "\n", 807 | " * https://mpi4py.readthedocs.io/en/stable/" 808 | ] 809 | }, 810 | { 811 | "cell_type": "markdown", 812 | "metadata": {}, 813 | "source": [ 814 | "
\n", 815 | "
\n", 816 | "
\n", 817 | "
" 818 | ] 819 | }, 820 | { 821 | "cell_type": "code", 822 | "execution_count": null, 823 | "metadata": {}, 824 | "outputs": [], 825 | "source": [] 826 | } 827 | ], 828 | "metadata": { 829 | "anaconda-cloud": {}, 830 | "kernelspec": { 831 | "display_name": "Python 3 (ipykernel)", 832 | "language": "python", 833 | "name": "python3" 834 | }, 835 | "language_info": { 836 | "codemirror_mode": { 837 | "name": "ipython", 838 | "version": 3 839 | }, 840 | "file_extension": ".py", 841 | "mimetype": "text/x-python", 842 | "name": "python", 843 | "nbconvert_exporter": "python", 844 | "pygments_lexer": "ipython3", 845 | "version": "3.9.12" 846 | } 847 | }, 848 | "nbformat": 4, 849 | "nbformat_minor": 4 850 | } 851 | -------------------------------------------------------------------------------- /notebooks/images/logos/logo_black.eps: -------------------------------------------------------------------------------- 1 | %!PS-Adobe-3.0 EPSF-3.0 2 | %%Creator: Adobe Illustrator(TM) 5.5 3 | %%For: (Scott Fraser) (Navy Blue) 4 | %%Title: (NewVectors.logo) 5 | %%CreationDate: (7/1/97) (10:23 am) 6 | %%BoundingBox: -110 480 550 671 7 | %%HiResBoundingBox: -109.625 480.6876 549.4375 670.75 8 | %%DocumentProcessColors: 9 | %%DocumentSuppliedResources: procset Adobe_level2_AI5 1.0 0 10 | %%+ procset Adobe_IllustratorA_AI5 1.0 0 11 | %AI5_FileFormat 1.2 12 | %AI3_ColorUsage: Color 13 | %%DocumentCustomColors: (PANTONE 199 CV) 14 | %%+ (PANTONE 289 CV) 15 | %%CMYKCustomColor: 1 0.9 0.1 0 (Dark Blue) 16 | %%+ 0 0 0.51 0 (PANTONE 100 CV) 17 | %%+ 0 0 1 0 (PANTONE 102 CV) 18 | %%+ 0 0 1 0.51 (PANTONE 105 CV) 19 | %%+ 0 0.18 1 0.27 (PANTONE 118 CV) 20 | %%+ 0 0.11 1 0.51 (PANTONE 119 CV) 21 | %%+ 0 0.09 0.65 0 (PANTONE 128 CV) 22 | %%+ 0 0.43 1 0.18 (PANTONE 153 CV) 23 | %%+ 0 0.91 0.76 0.06 (PANTONE 186 CV) 24 | %%+ 0 1 0.65 0 (PANTONE 199 CV) 25 | %%+ 1 0.6 0 0.56 (PANTONE 289 CV) 26 | %%+ 0.91 0.72 0.27 0 (PANTONE 534 CV) 27 | %%+ 0 0.45 0.6 0 (Peach) 28 | %AI3_TemplateBox: 306 396 306 396 29 | %AI3_TileBox: -885 -441 1503 1227 30 | %AI3_DocumentPreview: Macintosh_ColorPic 31 | %AI5_ArtSize: 2476 1750 32 | %AI5_RulerUnits: 0 33 | %AI5_ArtFlags: 1 0 0 1 0 0 1 1 0 34 | %AI5_TargetResolution: 800 35 | %AI5_NumLayers: 1 36 | %AI5_OpenToView: -1278 1476 -3 1082 772 26 1 1 7 43 37 | %AI5_OpenViewLayers: 7 38 | %%EndComments 39 | %%BeginProlog 40 | %%BeginResource: procset Adobe_level2_AI5 1.0 0 41 | %%Title: (Adobe Illustrator (R) Version 5.0 Level 2 Emulation) 42 | %%Version: 1.0 43 | %%CreationDate: (04/10/93) () 44 | %%Copyright: ((C) 1987-1993 Adobe Systems Incorporated All Rights Reserved) 45 | userdict /Adobe_level2_AI5 21 dict dup begin 46 | put 47 | /packedarray where not 48 | { 49 | userdict begin 50 | /packedarray 51 | { 52 | array astore readonly 53 | } bind def 54 | /setpacking /pop load def 55 | /currentpacking false def 56 | end 57 | 0 58 | } if 59 | pop 60 | userdict /defaultpacking currentpacking put true setpacking 61 | /initialize 62 | { 63 | Adobe_level2_AI5 begin 64 | } bind def 65 | /terminate 66 | { 67 | currentdict Adobe_level2_AI5 eq 68 | { 69 | end 70 | } if 71 | } bind def 72 | mark 73 | /setcustomcolor where not 74 | { 75 | /findcmykcustomcolor 76 | { 77 | 5 packedarray 78 | } bind def 79 | /setcustomcolor 80 | { 81 | exch aload pop pop 82 | 4 83 | { 84 | 4 index mul 4 1 roll 85 | } repeat 86 | 5 -1 roll pop 87 | setcmykcolor 88 | } 89 | def 90 | } if 91 | 92 | /gt38? mark {version cvx exec} stopped {cleartomark true} {38 gt exch pop} ifelse def 93 | userdict /deviceDPI 72 0 matrix defaultmatrix dtransform dup mul exch dup mul add sqrt put 94 | userdict /level2? 95 | systemdict /languagelevel known dup 96 | { 97 | pop systemdict /languagelevel get 2 ge 98 | } if 99 | put 100 | level2? not 101 | { 102 | /setcmykcolor where not 103 | { 104 | /setcmykcolor 105 | { 106 | exch .11 mul add exch .59 mul add exch .3 mul add 107 | 1 exch sub setgray 108 | } def 109 | } if 110 | /currentcmykcolor where not 111 | { 112 | /currentcmykcolor 113 | { 114 | 0 0 0 1 currentgray sub 115 | } def 116 | } if 117 | /setoverprint where not 118 | { 119 | /setoverprint /pop load def 120 | } if 121 | /selectfont where not 122 | { 123 | /selectfont 124 | { 125 | exch findfont exch 126 | dup type /arraytype eq 127 | { 128 | makefont 129 | } 130 | { 131 | scalefont 132 | } ifelse 133 | setfont 134 | } bind def 135 | } if 136 | /cshow where not 137 | { 138 | /cshow 139 | { 140 | [ 141 | 0 0 5 -1 roll aload pop 142 | ] cvx bind forall 143 | } bind def 144 | } if 145 | } if 146 | cleartomark 147 | /anyColor? 148 | { 149 | add add add 0 ne 150 | } bind def 151 | /testColor 152 | { 153 | gsave 154 | setcmykcolor currentcmykcolor 155 | grestore 156 | } bind def 157 | /testCMYKColorThrough 158 | { 159 | testColor anyColor? 160 | } bind def 161 | userdict /composite? 162 | level2? 163 | { 164 | gsave 1 1 1 1 setcmykcolor currentcmykcolor grestore 165 | add add add 4 eq 166 | } 167 | { 168 | 1 0 0 0 testCMYKColorThrough 169 | 0 1 0 0 testCMYKColorThrough 170 | 0 0 1 0 testCMYKColorThrough 171 | 0 0 0 1 testCMYKColorThrough 172 | and and and 173 | } ifelse 174 | put 175 | composite? not 176 | { 177 | userdict begin 178 | gsave 179 | /cyan? 1 0 0 0 testCMYKColorThrough def 180 | /magenta? 0 1 0 0 testCMYKColorThrough def 181 | /yellow? 0 0 1 0 testCMYKColorThrough def 182 | /black? 0 0 0 1 testCMYKColorThrough def 183 | grestore 184 | /isCMYKSep? cyan? magenta? yellow? black? or or or def 185 | /customColor? isCMYKSep? not def 186 | end 187 | } if 188 | end defaultpacking setpacking 189 | %%EndResource 190 | %%BeginResource: procset Adobe_IllustratorA_AI5 1.1 0 191 | %%Title: (Adobe Illustrator (R) Version 5.0 Abbreviated Prolog) 192 | %%Version: 1.1 193 | %%CreationDate: (3/7/1994) () 194 | %%Copyright: ((C) 1987-1994 Adobe Systems Incorporated All Rights Reserved) 195 | currentpacking true setpacking 196 | userdict /Adobe_IllustratorA_AI5_vars 70 dict dup begin 197 | put 198 | /_lp /none def 199 | /_pf 200 | { 201 | } def 202 | /_ps 203 | { 204 | } def 205 | /_psf 206 | { 207 | } def 208 | /_pss 209 | { 210 | } def 211 | /_pjsf 212 | { 213 | } def 214 | /_pjss 215 | { 216 | } def 217 | /_pola 0 def 218 | /_doClip 0 def 219 | /cf currentflat def 220 | /_tm matrix def 221 | /_renderStart 222 | [ 223 | /e0 /r0 /a0 /o0 /e1 /r1 /a1 /i0 224 | ] def 225 | /_renderEnd 226 | [ 227 | null null null null /i1 /i1 /i1 /i1 228 | ] def 229 | /_render -1 def 230 | /_rise 0 def 231 | /_ax 0 def 232 | /_ay 0 def 233 | /_cx 0 def 234 | /_cy 0 def 235 | /_leading 236 | [ 237 | 0 0 238 | ] def 239 | /_ctm matrix def 240 | /_mtx matrix def 241 | /_sp 16#020 def 242 | /_hyphen (-) def 243 | /_fScl 0 def 244 | /_cnt 0 def 245 | /_hs 1 def 246 | /_nativeEncoding 0 def 247 | /_useNativeEncoding 0 def 248 | /_tempEncode 0 def 249 | /_pntr 0 def 250 | /_tDict 2 dict def 251 | /_wv 0 def 252 | /Tx 253 | { 254 | } def 255 | /Tj 256 | { 257 | } def 258 | /CRender 259 | { 260 | } def 261 | /_AI3_savepage 262 | { 263 | } def 264 | /_gf null def 265 | /_cf 4 array def 266 | /_if null def 267 | /_of false def 268 | /_fc 269 | { 270 | } def 271 | /_gs null def 272 | /_cs 4 array def 273 | /_is null def 274 | /_os false def 275 | /_sc 276 | { 277 | } def 278 | /discardSave null def 279 | /buffer 256 string def 280 | /beginString null def 281 | /endString null def 282 | /endStringLength null def 283 | /layerCnt 1 def 284 | /layerCount 1 def 285 | /perCent (%) 0 get def 286 | /perCentSeen? false def 287 | /newBuff null def 288 | /newBuffButFirst null def 289 | /newBuffLast null def 290 | /clipForward? false def 291 | end 292 | userdict /Adobe_IllustratorA_AI5 74 dict dup begin 293 | put 294 | /initialize 295 | { 296 | Adobe_IllustratorA_AI5 dup begin 297 | Adobe_IllustratorA_AI5_vars begin 298 | discardDict 299 | { 300 | bind pop pop 301 | } forall 302 | dup /nc get begin 303 | { 304 | dup xcheck 1 index type /operatortype ne and 305 | { 306 | bind 307 | } if 308 | pop pop 309 | } forall 310 | end 311 | newpath 312 | } def 313 | /terminate 314 | { 315 | end 316 | end 317 | } def 318 | /_ 319 | null def 320 | /ddef 321 | { 322 | Adobe_IllustratorA_AI5_vars 3 1 roll put 323 | } def 324 | /xput 325 | { 326 | dup load dup length exch maxlength eq 327 | { 328 | dup dup load dup 329 | length 2 mul dict copy def 330 | } if 331 | load begin 332 | def 333 | end 334 | } def 335 | /npop 336 | { 337 | { 338 | pop 339 | } repeat 340 | } def 341 | /sw 342 | { 343 | dup length exch stringwidth 344 | exch 5 -1 roll 3 index mul add 345 | 4 1 roll 3 1 roll mul add 346 | } def 347 | /swj 348 | { 349 | dup 4 1 roll 350 | dup length exch stringwidth 351 | exch 5 -1 roll 3 index mul add 352 | 4 1 roll 3 1 roll mul add 353 | 6 2 roll /_cnt 0 ddef 354 | { 355 | 1 index eq 356 | { 357 | /_cnt _cnt 1 add ddef 358 | } if 359 | } forall 360 | pop 361 | exch _cnt mul exch _cnt mul 2 index add 4 1 roll 2 index add 4 1 roll pop pop 362 | } def 363 | /ss 364 | { 365 | 4 1 roll 366 | { 367 | 2 npop 368 | (0) exch 2 copy 0 exch put pop 369 | gsave 370 | false charpath currentpoint 371 | 4 index setmatrix 372 | stroke 373 | grestore 374 | moveto 375 | 2 copy rmoveto 376 | } exch cshow 377 | 3 npop 378 | } def 379 | /jss 380 | { 381 | 4 1 roll 382 | { 383 | 2 npop 384 | (0) exch 2 copy 0 exch put 385 | gsave 386 | _sp eq 387 | { 388 | exch 6 index 6 index 6 index 5 -1 roll widthshow 389 | currentpoint 390 | } 391 | { 392 | false charpath currentpoint 393 | 4 index setmatrix stroke 394 | } ifelse 395 | grestore 396 | moveto 397 | 2 copy rmoveto 398 | } exch cshow 399 | 6 npop 400 | } def 401 | /sp 402 | { 403 | { 404 | 2 npop (0) exch 405 | 2 copy 0 exch put pop 406 | false charpath 407 | 2 copy rmoveto 408 | } exch cshow 409 | 2 npop 410 | } def 411 | /jsp 412 | { 413 | { 414 | 2 npop 415 | (0) exch 2 copy 0 exch put 416 | _sp eq 417 | { 418 | exch 5 index 5 index 5 index 5 -1 roll widthshow 419 | } 420 | { 421 | false charpath 422 | } ifelse 423 | 2 copy rmoveto 424 | } exch cshow 425 | 5 npop 426 | } def 427 | /pl 428 | { 429 | transform 430 | 0.25 sub round 0.25 add exch 431 | 0.25 sub round 0.25 add exch 432 | itransform 433 | } def 434 | /setstrokeadjust where 435 | { 436 | pop true setstrokeadjust 437 | /c 438 | { 439 | curveto 440 | } def 441 | /C 442 | /c load def 443 | /v 444 | { 445 | currentpoint 6 2 roll curveto 446 | } def 447 | /V 448 | /v load def 449 | /y 450 | { 451 | 2 copy curveto 452 | } def 453 | /Y 454 | /y load def 455 | /l 456 | { 457 | lineto 458 | } def 459 | /L 460 | /l load def 461 | /m 462 | { 463 | moveto 464 | } def 465 | } 466 | { 467 | /c 468 | { 469 | pl curveto 470 | } def 471 | /C 472 | /c load def 473 | /v 474 | { 475 | currentpoint 6 2 roll pl curveto 476 | } def 477 | /V 478 | /v load def 479 | /y 480 | { 481 | pl 2 copy curveto 482 | } def 483 | /Y 484 | /y load def 485 | /l 486 | { 487 | pl lineto 488 | } def 489 | /L 490 | /l load def 491 | /m 492 | { 493 | pl moveto 494 | } def 495 | } ifelse 496 | /d 497 | { 498 | setdash 499 | } def 500 | /cf 501 | { 502 | } def 503 | /i 504 | { 505 | dup 0 eq 506 | { 507 | pop cf 508 | } if 509 | setflat 510 | } def 511 | /j 512 | { 513 | setlinejoin 514 | } def 515 | /J 516 | { 517 | setlinecap 518 | } def 519 | /M 520 | { 521 | setmiterlimit 522 | } def 523 | /w 524 | { 525 | setlinewidth 526 | } def 527 | /H 528 | { 529 | } def 530 | /h 531 | { 532 | closepath 533 | } def 534 | /N 535 | { 536 | _pola 0 eq 537 | { 538 | _doClip 1 eq 539 | { 540 | clip /_doClip 0 ddef 541 | } if 542 | newpath 543 | } 544 | { 545 | /CRender 546 | { 547 | N 548 | } ddef 549 | } ifelse 550 | } def 551 | /n 552 | { 553 | N 554 | } def 555 | /F 556 | { 557 | _pola 0 eq 558 | { 559 | _doClip 1 eq 560 | { 561 | gsave _pf grestore clip newpath /_lp /none ddef _fc 562 | /_doClip 0 ddef 563 | } 564 | { 565 | _pf 566 | } ifelse 567 | } 568 | { 569 | /CRender 570 | { 571 | F 572 | } ddef 573 | } ifelse 574 | } def 575 | /f 576 | { 577 | closepath 578 | F 579 | } def 580 | /S 581 | { 582 | _pola 0 eq 583 | { 584 | _doClip 1 eq 585 | { 586 | gsave _ps grestore clip newpath /_lp /none ddef _sc 587 | /_doClip 0 ddef 588 | } 589 | { 590 | _ps 591 | } ifelse 592 | } 593 | { 594 | /CRender 595 | { 596 | S 597 | } ddef 598 | } ifelse 599 | } def 600 | /s 601 | { 602 | closepath 603 | S 604 | } def 605 | /B 606 | { 607 | _pola 0 eq 608 | { 609 | _doClip 1 eq 610 | gsave F grestore 611 | { 612 | gsave S grestore clip newpath /_lp /none ddef _sc 613 | /_doClip 0 ddef 614 | } 615 | { 616 | S 617 | } ifelse 618 | } 619 | { 620 | /CRender 621 | { 622 | B 623 | } ddef 624 | } ifelse 625 | } def 626 | /b 627 | { 628 | closepath 629 | B 630 | } def 631 | /W 632 | { 633 | /_doClip 1 ddef 634 | } def 635 | /* 636 | { 637 | count 0 ne 638 | { 639 | dup type /stringtype eq 640 | { 641 | pop 642 | } if 643 | } if 644 | newpath 645 | } def 646 | /u 647 | { 648 | } def 649 | /U 650 | { 651 | } def 652 | /q 653 | { 654 | _pola 0 eq 655 | { 656 | gsave 657 | } if 658 | } def 659 | /Q 660 | { 661 | _pola 0 eq 662 | { 663 | grestore 664 | } if 665 | } def 666 | /*u 667 | { 668 | _pola 1 add /_pola exch ddef 669 | } def 670 | /*U 671 | { 672 | _pola 1 sub /_pola exch ddef 673 | _pola 0 eq 674 | { 675 | CRender 676 | } if 677 | } def 678 | /D 679 | { 680 | pop 681 | } def 682 | /*w 683 | { 684 | } def 685 | /*W 686 | { 687 | } def 688 | /` 689 | { 690 | /_i save ddef 691 | clipForward? 692 | { 693 | nulldevice 694 | } if 695 | 6 1 roll 4 npop 696 | concat pop 697 | userdict begin 698 | /showpage 699 | { 700 | } def 701 | 0 setgray 702 | 0 setlinecap 703 | 1 setlinewidth 704 | 0 setlinejoin 705 | 10 setmiterlimit 706 | [] 0 setdash 707 | /setstrokeadjust where {pop false setstrokeadjust} if 708 | newpath 709 | 0 setgray 710 | false setoverprint 711 | } def 712 | /~ 713 | { 714 | end 715 | _i restore 716 | } def 717 | /O 718 | { 719 | 0 ne 720 | /_of exch ddef 721 | /_lp /none ddef 722 | } def 723 | /R 724 | { 725 | 0 ne 726 | /_os exch ddef 727 | /_lp /none ddef 728 | } def 729 | /g 730 | { 731 | /_gf exch ddef 732 | /_fc 733 | { 734 | _lp /fill ne 735 | { 736 | _of setoverprint 737 | _gf setgray 738 | /_lp /fill ddef 739 | } if 740 | } ddef 741 | /_pf 742 | { 743 | _fc 744 | fill 745 | } ddef 746 | /_psf 747 | { 748 | _fc 749 | ashow 750 | } ddef 751 | /_pjsf 752 | { 753 | _fc 754 | awidthshow 755 | } ddef 756 | /_lp /none ddef 757 | } def 758 | /G 759 | { 760 | /_gs exch ddef 761 | /_sc 762 | { 763 | _lp /stroke ne 764 | { 765 | _os setoverprint 766 | _gs setgray 767 | /_lp /stroke ddef 768 | } if 769 | } ddef 770 | /_ps 771 | { 772 | _sc 773 | stroke 774 | } ddef 775 | /_pss 776 | { 777 | _sc 778 | ss 779 | } ddef 780 | /_pjss 781 | { 782 | _sc 783 | jss 784 | } ddef 785 | /_lp /none ddef 786 | } def 787 | /k 788 | { 789 | _cf astore pop 790 | /_fc 791 | { 792 | _lp /fill ne 793 | { 794 | _of setoverprint 795 | _cf aload pop setcmykcolor 796 | /_lp /fill ddef 797 | } if 798 | } ddef 799 | /_pf 800 | { 801 | _fc 802 | fill 803 | } ddef 804 | /_psf 805 | { 806 | _fc 807 | ashow 808 | } ddef 809 | /_pjsf 810 | { 811 | _fc 812 | awidthshow 813 | } ddef 814 | /_lp /none ddef 815 | } def 816 | /K 817 | { 818 | _cs astore pop 819 | /_sc 820 | { 821 | _lp /stroke ne 822 | { 823 | _os setoverprint 824 | _cs aload pop setcmykcolor 825 | /_lp /stroke ddef 826 | } if 827 | } ddef 828 | /_ps 829 | { 830 | _sc 831 | stroke 832 | } ddef 833 | /_pss 834 | { 835 | _sc 836 | ss 837 | } ddef 838 | /_pjss 839 | { 840 | _sc 841 | jss 842 | } ddef 843 | /_lp /none ddef 844 | } def 845 | /x 846 | { 847 | /_gf exch ddef 848 | findcmykcustomcolor 849 | /_if exch ddef 850 | /_fc 851 | { 852 | _lp /fill ne 853 | { 854 | _of setoverprint 855 | _if _gf 1 exch sub setcustomcolor 856 | /_lp /fill ddef 857 | } if 858 | } ddef 859 | /_pf 860 | { 861 | _fc 862 | fill 863 | } ddef 864 | /_psf 865 | { 866 | _fc 867 | ashow 868 | } ddef 869 | /_pjsf 870 | { 871 | _fc 872 | awidthshow 873 | } ddef 874 | /_lp /none ddef 875 | } def 876 | /X 877 | { 878 | /_gs exch ddef 879 | findcmykcustomcolor 880 | /_is exch ddef 881 | /_sc 882 | { 883 | _lp /stroke ne 884 | { 885 | _os setoverprint 886 | _is _gs 1 exch sub setcustomcolor 887 | /_lp /stroke ddef 888 | } if 889 | } ddef 890 | /_ps 891 | { 892 | _sc 893 | stroke 894 | } ddef 895 | /_pss 896 | { 897 | _sc 898 | ss 899 | } ddef 900 | /_pjss 901 | { 902 | _sc 903 | jss 904 | } ddef 905 | /_lp /none ddef 906 | } def 907 | /A 908 | { 909 | pop 910 | } def 911 | /annotatepage 912 | { 913 | userdict /annotatepage 2 copy known {get exec} {pop pop} ifelse 914 | } def 915 | /discard 916 | { 917 | save /discardSave exch store 918 | discardDict begin 919 | /endString exch store 920 | gt38? 921 | { 922 | 2 add 923 | } if 924 | load 925 | stopped 926 | pop 927 | end 928 | discardSave restore 929 | } bind def 930 | userdict /discardDict 7 dict dup begin 931 | put 932 | /pre38Initialize 933 | { 934 | /endStringLength endString length store 935 | /newBuff buffer 0 endStringLength getinterval store 936 | /newBuffButFirst newBuff 1 endStringLength 1 sub getinterval store 937 | /newBuffLast newBuff endStringLength 1 sub 1 getinterval store 938 | } def 939 | /shiftBuffer 940 | { 941 | newBuff 0 newBuffButFirst putinterval 942 | newBuffLast 0 943 | currentfile read not 944 | { 945 | stop 946 | } if 947 | put 948 | } def 949 | 0 950 | { 951 | pre38Initialize 952 | mark 953 | currentfile newBuff readstring exch pop 954 | { 955 | { 956 | newBuff endString eq 957 | { 958 | cleartomark stop 959 | } if 960 | shiftBuffer 961 | } loop 962 | } 963 | { 964 | stop 965 | } ifelse 966 | } def 967 | 1 968 | { 969 | pre38Initialize 970 | /beginString exch store 971 | mark 972 | currentfile newBuff readstring exch pop 973 | { 974 | { 975 | newBuff beginString eq 976 | { 977 | /layerCount dup load 1 add store 978 | } 979 | { 980 | newBuff endString eq 981 | { 982 | /layerCount dup load 1 sub store 983 | layerCount 0 eq 984 | { 985 | cleartomark stop 986 | } if 987 | } if 988 | } ifelse 989 | shiftBuffer 990 | } loop 991 | } 992 | { 993 | stop 994 | } ifelse 995 | } def 996 | 2 997 | { 998 | mark 999 | { 1000 | currentfile buffer readline not 1001 | { 1002 | stop 1003 | } if 1004 | endString eq 1005 | { 1006 | cleartomark stop 1007 | } if 1008 | } loop 1009 | } def 1010 | 3 1011 | { 1012 | /beginString exch store 1013 | /layerCnt 1 store 1014 | mark 1015 | { 1016 | currentfile buffer readline not 1017 | { 1018 | stop 1019 | } if 1020 | dup beginString eq 1021 | { 1022 | pop /layerCnt dup load 1 add store 1023 | } 1024 | { 1025 | endString eq 1026 | { 1027 | layerCnt 1 eq 1028 | { 1029 | cleartomark stop 1030 | } 1031 | { 1032 | /layerCnt dup load 1 sub store 1033 | } ifelse 1034 | } if 1035 | } ifelse 1036 | } loop 1037 | } def 1038 | end 1039 | userdict /clipRenderOff 15 dict dup begin 1040 | put 1041 | { 1042 | /n /N /s /S /f /F /b /B 1043 | } 1044 | { 1045 | { 1046 | _doClip 1 eq 1047 | { 1048 | /_doClip 0 ddef clip 1049 | } if 1050 | newpath 1051 | } def 1052 | } forall 1053 | /Tr /pop load def 1054 | /Bb {} def 1055 | /BB /pop load def 1056 | /Bg {12 npop} def 1057 | /Bm {6 npop} def 1058 | /Bc /Bm load def 1059 | /Bh {4 npop} def 1060 | end 1061 | /Lb 1062 | { 1063 | 4 npop 1064 | 6 1 roll 1065 | pop 1066 | 4 1 roll 1067 | pop pop pop 1068 | 0 eq 1069 | { 1070 | 0 eq 1071 | { 1072 | (%AI5_BeginLayer) 1 (%AI5_EndLayer--) discard 1073 | } 1074 | { 1075 | /clipForward? true def 1076 | 1077 | /Tx /pop load def 1078 | /Tj /pop load def 1079 | currentdict end clipRenderOff begin begin 1080 | } ifelse 1081 | } 1082 | { 1083 | 0 eq 1084 | { 1085 | save /discardSave exch store 1086 | } if 1087 | } ifelse 1088 | } bind def 1089 | /LB 1090 | { 1091 | discardSave dup null ne 1092 | { 1093 | restore 1094 | } 1095 | { 1096 | pop 1097 | clipForward? 1098 | { 1099 | currentdict 1100 | end 1101 | end 1102 | begin 1103 | 1104 | /clipForward? false ddef 1105 | } if 1106 | } ifelse 1107 | } bind def 1108 | /Pb 1109 | { 1110 | pop pop 1111 | 0 (%AI5_EndPalette) discard 1112 | } bind def 1113 | /Np 1114 | { 1115 | 0 (%AI5_End_NonPrinting--) discard 1116 | } bind def 1117 | /Ln /pop load def 1118 | /Ap 1119 | /pop load def 1120 | /Ar 1121 | { 1122 | 72 exch div 1123 | 0 dtransform dup mul exch dup mul add sqrt 1124 | dup 1 lt 1125 | { 1126 | pop 1 1127 | } if 1128 | setflat 1129 | } def 1130 | /Mb 1131 | { 1132 | q 1133 | } def 1134 | /Md 1135 | { 1136 | } def 1137 | /MB 1138 | { 1139 | Q 1140 | } def 1141 | /nc 3 dict def 1142 | nc begin 1143 | /setgray 1144 | { 1145 | pop 1146 | } bind def 1147 | /setcmykcolor 1148 | { 1149 | 4 npop 1150 | } bind def 1151 | /setcustomcolor 1152 | { 1153 | 2 npop 1154 | } bind def 1155 | currentdict readonly pop 1156 | end 1157 | currentdict readonly pop 1158 | end 1159 | setpacking 1160 | %%EndResource 1161 | %%EndProlog 1162 | %%BeginSetup 1163 | Adobe_level2_AI5 /initialize get exec 1164 | Adobe_IllustratorA_AI5 /initialize get exec 1165 | %AI5_Begin_NonPrinting 1166 | Np 1167 | %AI3_BeginPattern: (Yellow Stripe) 1168 | (Yellow Stripe) 8.4499 4.6 80.4499 76.6 [ 1169 | %AI3_Tile 1170 | (0 O 0 R 0 0.4 1 0 k 0 0.4 1 0 K) @ 1171 | ( 1172 | 800 Ar 1173 | 0 J 0 j 3.6 w 4 M []0 d 1174 | %AI3_Note: 1175 | 0 D 1176 | 8.1999 8.1999 m 1177 | 80.6999 8.1999 L 1178 | S 1179 | 8.1999 22.6 m 1180 | 80.6999 22.6 L 1181 | S 1182 | 8.1999 37.0001 m 1183 | 80.6999 37.0001 L 1184 | S 1185 | 8.1999 51.3999 m 1186 | 80.6999 51.3999 L 1187 | S 1188 | 8.1999 65.8 m 1189 | 80.6999 65.8 L 1190 | S 1191 | 8.1999 15.3999 m 1192 | 80.6999 15.3999 L 1193 | S 1194 | 8.1999 29.8 m 1195 | 80.6999 29.8 L 1196 | S 1197 | 8.1999 44.1999 m 1198 | 80.6999 44.1999 L 1199 | S 1200 | 8.1999 58.6 m 1201 | 80.6999 58.6 L 1202 | S 1203 | 8.1999 73.0001 m 1204 | 80.6999 73.0001 L 1205 | S 1206 | ) & 1207 | ] E 1208 | %AI3_EndPattern 1209 | %AI5_End_NonPrinting-- 1210 | %AI5_Begin_NonPrinting 1211 | Np 1212 | 8 Bn 1213 | %AI5_BeginGradient: (Black & White) 1214 | (Black & White) 0 2 Bd 1215 | [ 1216 | < 1217 | FFFEFDFCFBFAF9F8F7F6F5F4F3F2F1F0EFEEEDECEBEAE9E8E7E6E5E4E3E2E1E0DFDEDDDCDBDAD9D8 1218 | D7D6D5D4D3D2D1D0CFCECDCCCBCAC9C8C7C6C5C4C3C2C1C0BFBEBDBCBBBAB9B8B7B6B5B4B3B2B1B0 1219 | AFAEADACABAAA9A8A7A6A5A4A3A2A1A09F9E9D9C9B9A999897969594939291908F8E8D8C8B8A8988 1220 | 87868584838281807F7E7D7C7B7A797877767574737271706F6E6D6C6B6A69686766656463626160 1221 | 5F5E5D5C5B5A595857565554535251504F4E4D4C4B4A494847464544434241403F3E3D3C3B3A3938 1222 | 37363534333231302F2E2D2C2B2A292827262524232221201F1E1D1C1B1A19181716151413121110 1223 | 0F0E0D0C0B0A09080706050403020100 1224 | > 1225 | 0 %_Br 1226 | [ 1227 | 0 0 50 100 %_Bs 1228 | 1 0 50 0 %_Bs 1229 | BD 1230 | %AI5_EndGradient 1231 | %AI5_BeginGradient: (Purple, Red & Yellow copy) 1232 | (Purple, Red & Yellow copy) 0 3 Bd 1233 | [ 1234 | 0 1235 | < 1236 | FFFEFDFCFBFAF9F8F7F6F5F4F3F2F1F0EFEEEDECEBEAE9E8E7E6E5E4E3E2E1E0DFDEDDDCDBDAD9D8 1237 | D7D6D5D4D3D2D1D0CFCECDCCCBCAC9C8C7C6C5C4C3C2C1C0BFBEBDBCBBBAB9B8B7B6B5B4B3B2B1B0 1238 | AFAEADACABAAA9A8A7A6A5A4A3A2A1A09F9E9D9C9B9A999897969594939291908F8E8D8C8B8A8988 1239 | 87868584838281807F7E7D7C7B7A797877767574737271706F6E6D6C6B6A69686766656463626160 1240 | 5F5E5D5C5B5A595857565554535251504F4E4D4C4B4A494847464544434241403F3E3D3C3B3A3938 1241 | 37363534333231302F2E2D2C2B2A292827262524232221201F1E1D1C1B1A19181716151413121110 1242 | 0F0E0D0C0B0A 1243 | > 1244 | < 1245 | CCCCCCCDCDCDCDCDCECECECECECFCFCFCFD0D0D0D0D0D1D1D1D1D1D2D2D2D2D2D3D3D3D3D3D4D4D4 1246 | D4D5D5D5D5D5D6D6D6D6D6D7D7D7D7D7D8D8D8D8D8D9D9D9D9DADADADADADBDBDBDBDBDCDCDCDCDC 1247 | DDDDDDDDDDDEDEDEDEDFDFDFDFDFE0E0E0E0E0E1E1E1E1E1E2E2E2E2E2E3E3E3E3E4E4E4E4E4E5E5 1248 | E5E5E5E6E6E6E6E6E7E7E7E7E7E8E8E8E8E9E9E9E9E9EAEAEAEAEAEBEBEBEBEBECECECECECEDEDED 1249 | EDEEEEEEEEEEEFEFEFEFEFF0F0F0F0F0F1F1F1F1F1F2F2F2F2F3F3F3F3F3F4F4F4F4F4F5F5F5F5F5 1250 | F6F6F6F6F6F7F7F7F7F8F8F8F8F8F9F9F9F9F9FAFAFAFAFAFBFBFBFBFBFCFCFCFCFDFDFDFDFDFEFE 1251 | FEFEFEFFFFFF 1252 | > 1253 | 0 1254 | 1 %_Br 1255 | < 1256 | E5E4E3E2E1E0DFDEDDDCDBDAD9D8D7D6D5D4D3D2D1D0CFCECDCCCBCAC9C8C7C6C5C4C3C2C1C0BFBE 1257 | BDBCBBBAB9B8B7B6B5B4B3B2B1B0AFAEADACABAAA9A8A7A6A5A4A3A2A1A09F9E9D9C9B9A99989796 1258 | 9594939291908F8E8D8C8B8A898887868584838281807F7E7D7C7B7A797877767574737271706F6E 1259 | 6D6C6B6A696867666564636261605F5E5D5C5B5A595857565554535251504F4E4D4C4B4A49484746 1260 | 4544434241403F3E3D3C3B3A393837363534333231302F2E2D2C2B2A292827262524232221201F1E 1261 | 1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100 1262 | > 1263 | < 1264 | E5E6E6E6E6E6E6E6E6E7E7E7E7E7E7E7E7E7E8E8E8E8E8E8E8E8E8E9E9E9E9E9E9E9E9E9EAEAEAEA 1265 | EAEAEAEAEAEBEBEBEBEBEBEBEBEBECECECECECECECECECEDEDEDEDEDEDEDEDEDEEEEEEEEEEEEEEEE 1266 | EEEFEFEFEFEFEFEFEFEFF0F0F0F0F0F0F0F0F0F1F1F1F1F1F1F1F1F1F2F2F2F2F2F2F2F2F2F3F3F3 1267 | F3F3F3F3F3F3F4F4F4F4F4F4F4F4F4F5F5F5F5F5F5F5F5F5F6F6F6F6F6F6F6F6F6F7F7F7F7F7F7F7 1268 | F7F7F8F8F8F8F8F8F8F8F8F9F9F9F9F9F9F9F9F9FAFAFAFAFAFAFAFAFAFBFBFBFBFBFBFBFBFBFCFC 1269 | FCFCFCFCFCFCFCFDFDFDFDFDFDFDFDFDFEFEFEFEFEFEFEFEFEFFFFFFFFFF 1270 | > 1271 | < 1272 | 00010203040405060708090A0B0C0C0D0E0F10111213141415161718191A1B1C1D1D1E1F20212223 1273 | 242525262728292A2B2C2D2D2E2F30313233343535363738393A3B3C3D3D3E3F4041424344454546 1274 | 4748494A4B4C4D4E4E4F50515253545556565758595A5B5C5D5E5E5F60616263646566666768696A 1275 | 6B6C6D6E6E6F70717273747576767778797A7B7C7D7E7F7F80818283848586878788898A8B8C8D8E 1276 | 8F8F90919293949596979798999A9B9C9D9E9F9FA0A1A2A3A4A5A6A7A7A8A9AAABACADAEAFAFB0B1 1277 | B2B3B4B5B6B7B8B8B9BABBBCBDBEBFC0C0C1C2C3C4C5C6C7C8C8C9CACBCC 1278 | > 1279 | 0 1280 | 1 %_Br 1281 | [ 1282 | 0 0.04 1 0 1 50 100 %_Bs 1283 | 0 1 0.8 0 1 50 50 %_Bs 1284 | 0.9 0.9 0 0 1 50 0 %_Bs 1285 | BD 1286 | %AI5_EndGradient 1287 | %AI5_BeginGradient: (Purple, Red & Yellow copy 2) 1288 | (Purple, Red & Yellow copy 2) 0 3 Bd 1289 | [ 1290 | 0 1291 | < 1292 | FFFEFDFCFBFAF9F8F7F6F5F4F3F2F1F0EFEEEDECEBEAE9E8E7E6E5E4E3E2E1E0DFDEDDDCDBDAD9D8 1293 | D7D6D5D4D3D2D1D0CFCECDCCCBCAC9C8C7C6C5C4C3C2C1C0BFBEBDBCBBBAB9B8B7B6B5B4B3B2B1B0 1294 | AFAEADACABAAA9A8A7A6A5A4A3A2A1A09F9E9D9C9B9A999897969594939291908F8E8D8C8B8A8988 1295 | 87868584838281807F7E7D7C7B7A797877767574737271706F6E6D6C6B6A69686766656463626160 1296 | 5F5E5D5C5B5A595857565554535251504F4E4D4C4B4A494847464544434241403F3E3D3C3B3A3938 1297 | 37363534333231302F2E2D2C2B2A292827262524232221201F1E1D1C1B1A19181716151413121110 1298 | 0F0E0D0C0B0A 1299 | > 1300 | < 1301 | CCCCCCCDCDCDCDCDCECECECECECFCFCFCFD0D0D0D0D0D1D1D1D1D1D2D2D2D2D2D3D3D3D3D3D4D4D4 1302 | D4D5D5D5D5D5D6D6D6D6D6D7D7D7D7D7D8D8D8D8D8D9D9D9D9DADADADADADBDBDBDBDBDCDCDCDCDC 1303 | DDDDDDDDDDDEDEDEDEDFDFDFDFDFE0E0E0E0E0E1E1E1E1E1E2E2E2E2E2E3E3E3E3E4E4E4E4E4E5E5 1304 | E5E5E5E6E6E6E6E6E7E7E7E7E7E8E8E8E8E9E9E9E9E9EAEAEAEAEAEBEBEBEBEBECECECECECEDEDED 1305 | EDEEEEEEEEEEEFEFEFEFEFF0F0F0F0F0F1F1F1F1F1F2F2F2F2F3F3F3F3F3F4F4F4F4F4F5F5F5F5F5 1306 | F6F6F6F6F6F7F7F7F7F8F8F8F8F8F9F9F9F9F9FAFAFAFAFAFBFBFBFBFBFCFCFCFCFDFDFDFDFDFEFE 1307 | FEFEFEFFFFFF 1308 | > 1309 | 0 1310 | 1 %_Br 1311 | < 1312 | E5E4E3E2E1E0DFDEDDDCDBDAD9D8D7D6D5D4D3D2D1D0CFCECDCCCBCAC9C8C7C6C5C4C3C2C1C0BFBE 1313 | BDBCBBBAB9B8B7B6B5B4B3B2B1B0AFAEADACABAAA9A8A7A6A5A4A3A2A1A09F9E9D9C9B9A99989796 1314 | 9594939291908F8E8D8C8B8A898887868584838281807F7E7D7C7B7A797877767574737271706F6E 1315 | 6D6C6B6A696867666564636261605F5E5D5C5B5A595857565554535251504F4E4D4C4B4A49484746 1316 | 4544434241403F3E3D3C3B3A393837363534333231302F2E2D2C2B2A292827262524232221201F1E 1317 | 1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100 1318 | > 1319 | < 1320 | E5E6E6E6E6E6E6E6E6E7E7E7E7E7E7E7E7E7E8E8E8E8E8E8E8E8E8E9E9E9E9E9E9E9E9E9EAEAEAEA 1321 | EAEAEAEAEAEBEBEBEBEBEBEBEBEBECECECECECECECECECEDEDEDEDEDEDEDEDEDEEEEEEEEEEEEEEEE 1322 | EEEFEFEFEFEFEFEFEFEFF0F0F0F0F0F0F0F0F0F1F1F1F1F1F1F1F1F1F2F2F2F2F2F2F2F2F2F3F3F3 1323 | F3F3F3F3F3F3F4F4F4F4F4F4F4F4F4F5F5F5F5F5F5F5F5F5F6F6F6F6F6F6F6F6F6F7F7F7F7F7F7F7 1324 | F7F7F8F8F8F8F8F8F8F8F8F9F9F9F9F9F9F9F9F9FAFAFAFAFAFAFAFAFAFBFBFBFBFBFBFBFBFBFCFC 1325 | FCFCFCFCFCFCFCFDFDFDFDFDFDFDFDFDFEFEFEFEFEFEFEFEFEFFFFFFFFFF 1326 | > 1327 | < 1328 | 00010203040405060708090A0B0C0C0D0E0F10111213141415161718191A1B1C1D1D1E1F20212223 1329 | 242525262728292A2B2C2D2D2E2F30313233343535363738393A3B3C3D3D3E3F4041424344454546 1330 | 4748494A4B4C4D4E4E4F50515253545556565758595A5B5C5D5E5E5F60616263646566666768696A 1331 | 6B6C6D6E6E6F70717273747576767778797A7B7C7D7E7F7F80818283848586878788898A8B8C8D8E 1332 | 8F8F90919293949596979798999A9B9C9D9E9F9FA0A1A2A3A4A5A6A7A7A8A9AAABACADAEAFAFB0B1 1333 | B2B3B4B5B6B7B8B8B9BABBBCBDBEBFC0C0C1C2C3C4C5C6C7C8C8C9CACBCC 1334 | > 1335 | 0 1336 | 1 %_Br 1337 | [ 1338 | 0 0.04 1 0 1 50 100 %_Bs 1339 | 0 1 0.8 0 1 50 50 %_Bs 1340 | 0.9 0.9 0 0 1 50 0 %_Bs 1341 | BD 1342 | %AI5_EndGradient 1343 | %AI5_BeginGradient: (Purple, Red & Yellow copy 3) 1344 | (Purple, Red & Yellow copy 3) 0 3 Bd 1345 | [ 1346 | 0 1347 | < 1348 | FFFEFDFCFBFAF9F8F7F6F5F4F3F2F1F0EFEEEDECEBEAE9E8E7E6E5E4E3E2E1E0DFDEDDDCDBDAD9D8 1349 | D7D6D5D4D3D2D1D0CFCECDCCCBCAC9C8C7C6C5C4C3C2C1C0BFBEBDBCBBBAB9B8B7B6B5B4B3B2B1B0 1350 | AFAEADACABAAA9A8A7A6A5A4A3A2A1A09F9E9D9C9B9A999897969594939291908F8E8D8C8B8A8988 1351 | 87868584838281807F7E7D7C7B7A797877767574737271706F6E6D6C6B6A69686766656463626160 1352 | 5F5E5D5C5B5A595857565554535251504F4E4D4C4B4A494847464544434241403F3E3D3C3B3A3938 1353 | 37363534333231302F2E2D2C2B2A292827262524232221201F1E1D1C1B1A19181716151413121110 1354 | 0F0E0D0C0B0A 1355 | > 1356 | < 1357 | CCCCCCCDCDCDCDCDCECECECECECFCFCFCFD0D0D0D0D0D1D1D1D1D1D2D2D2D2D2D3D3D3D3D3D4D4D4 1358 | D4D5D5D5D5D5D6D6D6D6D6D7D7D7D7D7D8D8D8D8D8D9D9D9D9DADADADADADBDBDBDBDBDCDCDCDCDC 1359 | DDDDDDDDDDDEDEDEDEDFDFDFDFDFE0E0E0E0E0E1E1E1E1E1E2E2E2E2E2E3E3E3E3E4E4E4E4E4E5E5 1360 | E5E5E5E6E6E6E6E6E7E7E7E7E7E8E8E8E8E9E9E9E9E9EAEAEAEAEAEBEBEBEBEBECECECECECEDEDED 1361 | EDEEEEEEEEEEEFEFEFEFEFF0F0F0F0F0F1F1F1F1F1F2F2F2F2F3F3F3F3F3F4F4F4F4F4F5F5F5F5F5 1362 | F6F6F6F6F6F7F7F7F7F8F8F8F8F8F9F9F9F9F9FAFAFAFAFAFBFBFBFBFBFCFCFCFCFDFDFDFDFDFEFE 1363 | FEFEFEFFFFFF 1364 | > 1365 | 0 1366 | 1 %_Br 1367 | < 1368 | E5E4E3E2E1E0DFDEDDDCDBDAD9D8D7D6D5D4D3D2D1D0CFCECDCCCBCAC9C8C7C6C5C4C3C2C1C0BFBE 1369 | BDBCBBBAB9B8B7B6B5B4B3B2B1B0AFAEADACABAAA9A8A7A6A5A4A3A2A1A09F9E9D9C9B9A99989796 1370 | 9594939291908F8E8D8C8B8A898887868584838281807F7E7D7C7B7A797877767574737271706F6E 1371 | 6D6C6B6A696867666564636261605F5E5D5C5B5A595857565554535251504F4E4D4C4B4A49484746 1372 | 4544434241403F3E3D3C3B3A393837363534333231302F2E2D2C2B2A292827262524232221201F1E 1373 | 1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100 1374 | > 1375 | < 1376 | E5E6E6E6E6E6E6E6E6E7E7E7E7E7E7E7E7E7E8E8E8E8E8E8E8E8E8E9E9E9E9E9E9E9E9E9EAEAEAEA 1377 | EAEAEAEAEAEBEBEBEBEBEBEBEBEBECECECECECECECECECEDEDEDEDEDEDEDEDEDEEEEEEEEEEEEEEEE 1378 | EEEFEFEFEFEFEFEFEFEFF0F0F0F0F0F0F0F0F0F1F1F1F1F1F1F1F1F1F2F2F2F2F2F2F2F2F2F3F3F3 1379 | F3F3F3F3F3F3F4F4F4F4F4F4F4F4F4F5F5F5F5F5F5F5F5F5F6F6F6F6F6F6F6F6F6F7F7F7F7F7F7F7 1380 | F7F7F8F8F8F8F8F8F8F8F8F9F9F9F9F9F9F9F9F9FAFAFAFAFAFAFAFAFAFBFBFBFBFBFBFBFBFBFCFC 1381 | FCFCFCFCFCFCFCFDFDFDFDFDFDFDFDFDFEFEFEFEFEFEFEFEFEFFFFFFFFFF 1382 | > 1383 | < 1384 | 00010203040405060708090A0B0C0C0D0E0F10111213141415161718191A1B1C1D1D1E1F20212223 1385 | 242525262728292A2B2C2D2D2E2F30313233343535363738393A3B3C3D3D3E3F4041424344454546 1386 | 4748494A4B4C4D4E4E4F50515253545556565758595A5B5C5D5E5E5F60616263646566666768696A 1387 | 6B6C6D6E6E6F70717273747576767778797A7B7C7D7E7F7F80818283848586878788898A8B8C8D8E 1388 | 8F8F90919293949596979798999A9B9C9D9E9F9FA0A1A2A3A4A5A6A7A7A8A9AAABACADAEAFAFB0B1 1389 | B2B3B4B5B6B7B8B8B9BABBBCBDBEBFC0C0C1C2C3C4C5C6C7C8C8C9CACBCC 1390 | > 1391 | 0 1392 | 1 %_Br 1393 | [ 1394 | 0 0.04 1 0 1 50 100 %_Bs 1395 | 0 1 0.8 0 1 50 50 %_Bs 1396 | 0.9 0.9 0 0 1 50 0 %_Bs 1397 | BD 1398 | %AI5_EndGradient 1399 | %AI5_BeginGradient: (Purple, Red & Yellow copy 4) 1400 | (Purple, Red & Yellow copy 4) 0 3 Bd 1401 | [ 1402 | 0 1403 | < 1404 | FFFEFDFCFBFAF9F8F7F6F5F4F3F2F1F0EFEEEDECEBEAE9E8E7E6E5E4E3E2E1E0DFDEDDDCDBDAD9D8 1405 | D7D6D5D4D3D2D1D0CFCECDCCCBCAC9C8C7C6C5C4C3C2C1C0BFBEBDBCBBBAB9B8B7B6B5B4B3B2B1B0 1406 | AFAEADACABAAA9A8A7A6A5A4A3A2A1A09F9E9D9C9B9A999897969594939291908F8E8D8C8B8A8988 1407 | 87868584838281807F7E7D7C7B7A797877767574737271706F6E6D6C6B6A69686766656463626160 1408 | 5F5E5D5C5B5A595857565554535251504F4E4D4C4B4A494847464544434241403F3E3D3C3B3A3938 1409 | 37363534333231302F2E2D2C2B2A292827262524232221201F1E1D1C1B1A19181716151413121110 1410 | 0F0E0D0C0B0A 1411 | > 1412 | < 1413 | CCCCCCCDCDCDCDCDCECECECECECFCFCFCFD0D0D0D0D0D1D1D1D1D1D2D2D2D2D2D3D3D3D3D3D4D4D4 1414 | D4D5D5D5D5D5D6D6D6D6D6D7D7D7D7D7D8D8D8D8D8D9D9D9D9DADADADADADBDBDBDBDBDCDCDCDCDC 1415 | DDDDDDDDDDDEDEDEDEDFDFDFDFDFE0E0E0E0E0E1E1E1E1E1E2E2E2E2E2E3E3E3E3E4E4E4E4E4E5E5 1416 | E5E5E5E6E6E6E6E6E7E7E7E7E7E8E8E8E8E9E9E9E9E9EAEAEAEAEAEBEBEBEBEBECECECECECEDEDED 1417 | EDEEEEEEEEEEEFEFEFEFEFF0F0F0F0F0F1F1F1F1F1F2F2F2F2F3F3F3F3F3F4F4F4F4F4F5F5F5F5F5 1418 | F6F6F6F6F6F7F7F7F7F8F8F8F8F8F9F9F9F9F9FAFAFAFAFAFBFBFBFBFBFCFCFCFCFDFDFDFDFDFEFE 1419 | FEFEFEFFFFFF 1420 | > 1421 | 0 1422 | 1 %_Br 1423 | < 1424 | E5E4E3E2E1E0DFDEDDDCDBDAD9D8D7D6D5D4D3D2D1D0CFCECDCCCBCAC9C8C7C6C5C4C3C2C1C0BFBE 1425 | BDBCBBBAB9B8B7B6B5B4B3B2B1B0AFAEADACABAAA9A8A7A6A5A4A3A2A1A09F9E9D9C9B9A99989796 1426 | 9594939291908F8E8D8C8B8A898887868584838281807F7E7D7C7B7A797877767574737271706F6E 1427 | 6D6C6B6A696867666564636261605F5E5D5C5B5A595857565554535251504F4E4D4C4B4A49484746 1428 | 4544434241403F3E3D3C3B3A393837363534333231302F2E2D2C2B2A292827262524232221201F1E 1429 | 1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100 1430 | > 1431 | < 1432 | E5E6E6E6E6E6E6E6E6E7E7E7E7E7E7E7E7E7E8E8E8E8E8E8E8E8E8E9E9E9E9E9E9E9E9E9EAEAEAEA 1433 | EAEAEAEAEAEBEBEBEBEBEBEBEBEBECECECECECECECECECEDEDEDEDEDEDEDEDEDEEEEEEEEEEEEEEEE 1434 | EEEFEFEFEFEFEFEFEFEFF0F0F0F0F0F0F0F0F0F1F1F1F1F1F1F1F1F1F2F2F2F2F2F2F2F2F2F3F3F3 1435 | F3F3F3F3F3F3F4F4F4F4F4F4F4F4F4F5F5F5F5F5F5F5F5F5F6F6F6F6F6F6F6F6F6F7F7F7F7F7F7F7 1436 | F7F7F8F8F8F8F8F8F8F8F8F9F9F9F9F9F9F9F9F9FAFAFAFAFAFAFAFAFAFBFBFBFBFBFBFBFBFBFCFC 1437 | FCFCFCFCFCFCFCFDFDFDFDFDFDFDFDFDFEFEFEFEFEFEFEFEFEFFFFFFFFFF 1438 | > 1439 | < 1440 | 00010203040405060708090A0B0C0C0D0E0F10111213141415161718191A1B1C1D1D1E1F20212223 1441 | 242525262728292A2B2C2D2D2E2F30313233343535363738393A3B3C3D3D3E3F4041424344454546 1442 | 4748494A4B4C4D4E4E4F50515253545556565758595A5B5C5D5E5E5F60616263646566666768696A 1443 | 6B6C6D6E6E6F70717273747576767778797A7B7C7D7E7F7F80818283848586878788898A8B8C8D8E 1444 | 8F8F90919293949596979798999A9B9C9D9E9F9FA0A1A2A3A4A5A6A7A7A8A9AAABACADAEAFAFB0B1 1445 | B2B3B4B5B6B7B8B8B9BABBBCBDBEBFC0C0C1C2C3C4C5C6C7C8C8C9CACBCC 1446 | > 1447 | 0 1448 | 1 %_Br 1449 | [ 1450 | 0 0.04 1 0 1 50 100 %_Bs 1451 | 0 1 0.8 0 1 50 50 %_Bs 1452 | 0.9 0.9 0 0 1 50 0 %_Bs 1453 | BD 1454 | %AI5_EndGradient 1455 | %AI5_BeginGradient: (Red & Yellow) 1456 | (Red & Yellow) 0 2 Bd 1457 | [ 1458 | 0 1459 | < 1460 | 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627 1461 | 28292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F 1462 | 505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F7071727374757677 1463 | 78797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F 1464 | A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7 1465 | C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF 1466 | F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF 1467 | > 1468 | < 1469 | FFFFFEFEFDFDFDFCFCFBFBFBFAFAF9F9F9F8F8F7F7F7F6F6F5F5F5F4F4F3F3F3F2F2F1F1F1F0F0EF 1470 | EFEFEEEEEDEDEDECECEBEBEBEAEAE9E9E9E8E8E7E7E7E6E6E5E5E5E4E4E3E3E3E2E2E1E1E1E0E0DF 1471 | DFDFDEDEDDDDDDDCDCDBDBDBDADAD9D9D9D8D8D7D7D7D6D6D5D5D5D4D4D3D3D3D2D2D1D1D1D0D0CF 1472 | CFCFCECECDCDCDCCCCCBCBCBCACAC9C9C9C8C8C7C7C7C6C6C5C5C5C4C4C3C3C3C2C2C1C1C1C0C0BF 1473 | BFBFBEBEBDBDBDBCBCBBBBBBBABAB9B9B9B8B8B7B7B7B6B6B5B5B5B4B4B3B3B3B2B2B1B1B1B0B0AF 1474 | AFAFAEAEADADADACACABABABAAAAA9A9A9A8A8A7A7A7A6A6A5A5A5A4A4A3A3A3A2A2A1A1A1A0A09F 1475 | 9F9F9E9E9D9D9D9C9C9B9B9B9A9A9999 1476 | > 1477 | 0 1478 | 1 %_Br 1479 | [ 1480 | 0 1 0.6 0 1 50 100 %_Bs 1481 | 0 0 1 0 1 50 0 %_Bs 1482 | BD 1483 | %AI5_EndGradient 1484 | %AI5_BeginGradient: (White & Purple Radial copy) 1485 | (White & Purple Radial copy) 0 2 Bd 1486 | [ 1487 | < 1488 | FFFEFDFCFBFAF9F8F7F6F5F4F3F2F1F0EFEEEDECEBEAE9E8E7E6E5E4E3E2E1E0DFDEDDDCDBDAD9D8 1489 | D7D6D5D4D3D2D1D0CFCECDCCCBCAC9C8C7C6C5C4C3C2C1C0BFBEBDBCBBBAB9B8B7B6B5B4B3B2B1B0 1490 | AFAEADACABAAA9A8A7A6A5A4A3A2A1A09F9E9D9C9B9A999897969594939291908F8E8D8C8B8A8988 1491 | 87868584838281807F7E7D7C7B7A797877767574737271706F6E6D6C6B6A69686766656463626160 1492 | 5F5E5D5C5B5A595857565554535251504F4E4D4C4B4A494847464544434241403F3E3D3C3B3A3938 1493 | 37363534333231302F2E2D2C2B2A292827262524232221201F1E1D1C1B1A19181716151413121110 1494 | 0F0E0D0C0B0A09080706050403020100 1495 | > 1496 | < 1497 | E5E5E4E3E2E1E0DFDEDDDCDCDBDAD9D8D7D6D5D4D3D3D2D1D0CFCECDCCCBCACAC9C8C7C6C5C4C3C2 1498 | C1C1C0BFBEBDBCBBBAB9B8B8B7B6B5B4B3B2B1B0AFAFAEADACABAAA9A8A7A6A6A5A4A3A2A1A09F9E 1499 | 9D9D9C9B9A99989796959494939291908F8E8D8C8B8B8A89888786858483828281807F7E7D7C7B7A 1500 | 7A79787776757473727170706F6E6D6C6B6A69686767666564636261605F5E5E5D5C5B5A59585756 1501 | 555554535251504F4E4D4C4C4B4A49484746454443434241403F3E3D3C3B3A3A3938373635343332 1502 | 3131302F2E2D2C2B2A29282827262524232221201F1F1E1D1C1B1A19181716161514131211100F0E 1503 | 0D0D0C0B0A0908070605040403020100 1504 | > 1505 | < 1506 | 1A191919191919191919191818181818181818181817171717171717171717161616161616161616 1507 | 16151515151515151515151414141414141414141413131313131313131313121212121212121212 1508 | 1211111111111111111111101010101010101010100F0F0F0F0F0F0F0F0F0F0E0E0E0E0E0E0E0E0E 1509 | 0E0D0D0D0D0D0D0D0D0D0D0C0C0C0C0C0C0C0C0C0C0B0B0B0B0B0B0B0B0B0B0A0A0A0A0A0A0A0A0A 1510 | 0A090909090909090909090808080808080808080807070707070707070707060606060606060606 1511 | 06050505050505050505050404040404040404040403030303030303030303020202020202020202 1512 | 02010101010101010101010000000000 1513 | > 1514 | 0 1515 | < 1516 | 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627 1517 | 28292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F 1518 | 505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F7071727374757677 1519 | 78797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F 1520 | A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7 1521 | C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF 1522 | F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF 1523 | > 1524 | 2 %_Br 1525 | [ 1526 | 0 0 0 0 1 50 100 %_Bs 1527 | 1 0.9 0.1 0 (Dark Blue) 0 3 50 75 %_Bs 1528 | BD 1529 | %AI5_EndGradient 1530 | %AI5_BeginGradient: (Yellow & Blue Radial) 1531 | (Yellow & Blue Radial) 1 2 Bd 1532 | [ 1533 | < 1534 | 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627 1535 | 28292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F 1536 | 505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F7071727374757677 1537 | 78797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F 1538 | A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7 1539 | C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF 1540 | F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF 1541 | > 1542 | < 1543 | 1415161718191A1B1C1D1E1F1F202122232425262728292A2A2B2C2D2E2F30313233343536363738 1544 | 393A3B3C3D3E3F40414142434445464748494A4B4C4D4D4E4F50515253545556575858595A5B5C5D 1545 | 5E5F60616263646465666768696A6B6C6D6E6F6F707172737475767778797A7B7B7C7D7E7F808182 1546 | 83848586868788898A8B8C8D8E8F90919292939495969798999A9B9C9D9D9E9FA0A1A2A3A4A5A6A7 1547 | A8A9A9AAABACADAEAFB0B1B2B3B4B4B5B6B7B8B9BABBBCBDBEBFC0C0C1C2C3C4C5C6C7C8C9CACBCB 1548 | CCCDCECFD0D1D2D3D4D5D6D7D7D8D9DADBDCDDDEDFE0E1E2E2E3E4E5E6E7E8E9EAEBECEDEEEEEFF0 1549 | F1F2F3F4F5F6F7F8F9F9FAFBFCFDFEFF 1550 | > 1551 | < 1552 | ABAAAAA9A8A7A7A6A5A5A4A3A3A2A1A1A09F9F9E9D9D9C9B9B9A9999989797969595949393929191 1553 | 908F8F8E8D8D8C8B8B8A8989888787868585848383828181807F7F7E7D7D7C7B7B7A797978777776 1554 | 7575747373727171706F6F6E6D6D6C6B6B6A6969686767666565646362626160605F5E5E5D5C5C5B 1555 | 5A5A5958585756565554545352525150504F4E4E4D4C4C4B4A4A4948484746464544444342424140 1556 | 403F3E3E3D3C3C3B3A3A3938383736363534343332323130302F2E2E2D2C2C2B2A2A292828272626 1557 | 25242423222121201F1F1E1D1D1C1B1B1A1919181717161515141313121111100F0F0E0D0D0C0B0B 1558 | 0A090908070706050504030302010100 1559 | > 1560 | 0 1561 | 1 %_Br 1562 | [ 1563 | 0 0.08 0.67 0 1 50 14 %_Bs 1564 | 1 1 0 0 1 50 100 %_Bs 1565 | BD 1566 | %AI5_EndGradient 1567 | %AI5_End_NonPrinting-- 1568 | %AI5_BeginPalette 1569 | 108 0 Pb 1570 | Pn 1571 | Pc 1572 | 1 g 1573 | Pc 1574 | 0 g 1575 | Pc 1576 | 0 0 0 0 k 1577 | Pc 1578 | 0.75 g 1579 | Pc 1580 | 0.5 g 1581 | Pc 1582 | 0.25 g 1583 | Pc 1584 | 0 g 1585 | Pc 1586 | Bb 1587 | 2 (Black & White) -4014 4716 0 0 1 0 0 1 0 0 Bg 1588 | 0 BB 1589 | Pc 1590 | 0.25 0 0 0 k 1591 | Pc 1592 | 0.5 0 0 0 k 1593 | Pc 1594 | 0.75 0 0 0 k 1595 | Pc 1596 | 1 0 0 0 k 1597 | Pc 1598 | 0.25 0.25 0 0 k 1599 | Pc 1600 | 0.5 0.5 0 0 k 1601 | Pc 1602 | 0.75 0.75 0 0 k 1603 | Pc 1604 | 1 1 0 0 k 1605 | Pc 1606 | Bb 1607 | 2 (Red & Yellow) -4014 4716 0 0 1 0 0 1 0 0 Bg 1608 | 0 BB 1609 | Pc 1610 | 0 0.25 0 0 k 1611 | Pc 1612 | 0 0.5 0 0 k 1613 | Pc 1614 | 0 0.75 0 0 k 1615 | Pc 1616 | 0 1 0 0 k 1617 | Pc 1618 | 0 0.25 0.25 0 k 1619 | Pc 1620 | 0 0.5 0.5 0 k 1621 | Pc 1622 | 0 0.75 0.75 0 k 1623 | Pc 1624 | 0 1 1 0 k 1625 | Pc 1626 | Bb 1627 | 0 0 0 0 Bh 1628 | 2 (Yellow & Blue Radial) -4014 4716 0 0 1 0 0 1 0 0 Bg 1629 | 0 BB 1630 | Pc 1631 | 0 0 0.25 0 k 1632 | Pc 1633 | 0 0 0.5 0 k 1634 | Pc 1635 | 0 0 0.75 0 k 1636 | Pc 1637 | 0 0 1 0 k 1638 | Pc 1639 | 0.25 0 0.25 0 k 1640 | Pc 1641 | 0.5 0 0.5 0 k 1642 | Pc 1643 | 0.75 0 0.75 0 k 1644 | Pc 1645 | 1 0 1 0 k 1646 | Pc 1647 | (Yellow Stripe) 0 0 1 1 0 0 0 0 0 [1 0 0 1 0 0] p 1648 | Pc 1649 | 0.25 0.125 0 0 k 1650 | Pc 1651 | 0.5 0.25 0 0 k 1652 | Pc 1653 | 0.75 0.375 0 0 k 1654 | Pc 1655 | 1 0.5 0 0 k 1656 | Pc 1657 | 0.125 0.25 0 0 k 1658 | Pc 1659 | 0.25 0.5 0 0 k 1660 | Pc 1661 | 0.375 0.75 0 0 k 1662 | Pc 1663 | 0.5 1 0 0 k 1664 | Pc 1665 | 0 0 0 0 k 1666 | Pc 1667 | 0 0.25 0.125 0 k 1668 | Pc 1669 | 0 0.5 0.25 0 k 1670 | Pc 1671 | 0 0.75 0.375 0 k 1672 | Pc 1673 | 0 1 0.5 0 k 1674 | Pc 1675 | 0 0.125 0.25 0 k 1676 | Pc 1677 | 0 0.25 0.5 0 k 1678 | Pc 1679 | 0 0.375 0.75 0 k 1680 | Pc 1681 | 0 0.5 1 0 k 1682 | Pc 1683 | 0 0 0 0 k 1684 | Pc 1685 | 0.125 0 0.25 0 k 1686 | Pc 1687 | 0.25 0 0.5 0 k 1688 | Pc 1689 | 0.375 0 0.75 0 k 1690 | Pc 1691 | 0.5 0 1 0 k 1692 | Pc 1693 | 0.25 0 0.125 0 k 1694 | Pc 1695 | 0.5 0 0.25 0 k 1696 | Pc 1697 | 0.75 0 0.375 0 k 1698 | Pc 1699 | 1 0 0.5 0 k 1700 | Pc 1701 | 0 0 0 0 k 1702 | Pc 1703 | 0.25 0.125 0.125 0 k 1704 | Pc 1705 | 0.5 0.25 0.25 0 k 1706 | Pc 1707 | 0.75 0.375 0.375 0 k 1708 | Pc 1709 | 1 0.5 0.5 0 k 1710 | Pc 1711 | 0.25 0.25 0.125 0 k 1712 | Pc 1713 | 0.5 0.5 0.25 0 k 1714 | Pc 1715 | 0.75 0.75 0.375 0 k 1716 | Pc 1717 | 1 1 0.5 0 k 1718 | Pc 1719 | 0 0 0 0 k 1720 | Pc 1721 | 0.125 0.25 0.125 0 k 1722 | Pc 1723 | 0.25 0.5 0.25 0 k 1724 | Pc 1725 | 0.375 0.75 0.375 0 k 1726 | Pc 1727 | 0.5 1 0.5 0 k 1728 | Pc 1729 | 0.125 0.25 0.25 0 k 1730 | Pc 1731 | 0.25 0.5 0.5 0 k 1732 | Pc 1733 | 0.375 0.75 0.75 0 k 1734 | Pc 1735 | 0.5 1 1 0 k 1736 | Pc 1737 | 0 0 0 0 k 1738 | Pc 1739 | 0.125 0.125 0.25 0 k 1740 | Pc 1741 | 0.25 0.25 0.5 0 k 1742 | Pc 1743 | 0.375 0.375 0.75 0 k 1744 | Pc 1745 | 0.5 0.5 1 0 k 1746 | Pc 1747 | 0.25 0.125 0.25 0 k 1748 | Pc 1749 | 0.5 0.25 0.5 0 k 1750 | Pc 1751 | 0.75 0.375 0.75 0 k 1752 | Pc 1753 | 1 0.5 1 0 k 1754 | Pc 1755 | PB 1756 | %AI5_EndPalette 1757 | %%EndSetup 1758 | %AI5_BeginLayer 1759 | 1 1 1 1 0 0 0 79 128 255 Lb 1760 | (Layer 1) Ln 1761 | 0 A 1762 | 1 Ap 1763 | 0 R 1764 | 1 G 1765 | 800 Ar 1766 | 0 J 0 j 19.5 w 4 M []0 d 1767 | %AI3_Note: 1768 | 0 D 1769 | 501.4552 625.3638 m 1770 | S 1771 | 0 Ap 1772 | 1 w 1773 | 4626 586.6667 m 1774 | (N) * 1775 | -4014 586.6667 m 1776 | (N) * 1777 | 1 Ap 1778 | 0 O 1779 | 0 0 0 1 (PANTONE 199 CV) 0 x 1780 | -103.625 480.75 m 1781 | -103.625 670.2501 L 1782 | -109.625 670.2501 L 1783 | -109.625 480.75 L 1784 | -103.625 480.75 L 1785 | f 1786 | 549.4375 481.1666 m 1787 | 549.4375 670.6667 L 1788 | 543.4375 670.6667 L 1789 | 543.4375 481.1666 L 1790 | 549.4375 481.1666 L 1791 | f 1792 | 0 Ap 1793 | 4626 523.4375 m 1794 | (N) * 1795 | -4014 523.4375 m 1796 | (N) * 1797 | -26.125 -3924 m 1798 | (N) * 1799 | -26.125 4716 m 1800 | (N) * 1801 | 189.5625 -3924 m 1802 | (N) * 1803 | 189.5625 4716 m 1804 | (N) * 1805 | 0 O 1806 | 0 0 0 1 (PANTONE 289 CV) 0 x 1807 | 16.5 w 1808 | 515.5 628.9375 m 1809 | 498.9375 670.4376 450.4375 670.4376 v 1810 | 403.1875 670.4376 379.5 629.6875 379.5 599.9375 c 1811 | 379.5 567.1875 404.25 528.1875 451 528.1875 c 1812 | 499.5 528.1875 515.75 569.4375 y 1813 | 500.25 576.6875 l 1814 | 488.75 544.9375 450.25 544.9375 v 1815 | 413 544.9375 396.35 578.0875 396.35 600.3375 c 1816 | 396.35 624.1875 416.25 654.1875 450.25 654.1875 c 1817 | 484.25 654.1875 498.5 626.6875 500.5 621.9375 c 1818 | 515.5 628.9375 l 1819 | f 1820 | 374.25 628.9375 m 1821 | 357.6875 670.4376 309.1875 670.4376 v 1822 | 261.9375 670.4376 238.25 629.6875 238.25 599.9375 c 1823 | 238.25 567.1875 263 528.1875 309.75 528.1875 c 1824 | 358.25 528.1875 374.4999 569.4375 y 1825 | 359 576.6875 l 1826 | 347.4999 544.9375 309 544.9375 v 1827 | 271.7499 544.9375 255.0999 578.0875 255.0999 600.3375 c 1828 | 255.0999 624.1875 275 654.1875 309 654.1875 c 1829 | 342.9999 654.1875 357.2499 626.6875 359.25 621.9375 c 1830 | 374.25 628.9375 l 1831 | f 1832 | 1 w 1833 | 59.5 -3924 m 1834 | (N) * 1835 | 59.5 4716 m 1836 | (N) * 1837 | 4626 563.0833 m 1838 | (N) * 1839 | -4014 563.0833 m 1840 | (N) * 1841 | *u 1842 | u 1843 | 1 Ap 1844 | 0 O 1845 | 0 0 0 1 (PANTONE 289 CV) 0 x 1846 | 153.6249 654.4704 m 1847 | 184.2806 654.4704 209.1328 629.6183 209.1328 598.9626 c 1848 | 209.1328 568.3069 184.2806 543.4547 153.6249 543.4547 c 1849 | 122.9692 543.4547 98.1171 568.3069 98.1171 598.9626 c 1850 | 98.1171 629.6183 122.9692 654.4704 153.6249 654.4704 c 1851 | f 1852 | U 1853 | 1 D 1854 | 153.725 526.7537 m 1855 | 193.3626 526.7537 225.4963 558.8874 225.4963 598.525 c 1856 | 225.4963 638.1627 193.3626 670.2964 153.725 670.2964 c 1857 | 131.2459 670.2964 112.0142 660.9273 98.8542 644.75 C 1858 | 98.8542 670.3752 L 1859 | 82.4168 670.3752 L 1860 | 82.4168 480.6876 L 1861 | 98.8542 480.6876 L 1862 | 98.8542 552.25 L 1863 | 112.0196 536.4489 131.5504 526.7537 153.725 526.7537 c 1864 | f 1865 | *U 1866 | 0 Ap 1867 | 0 D 1868 | 35.75 -3924 m 1869 | (N) * 1870 | 35.75 4716 m 1871 | (N) * 1872 | 83.5 -3924 m 1873 | (N) * 1874 | 83.5 4716 m 1875 | (N) * 1876 | 78 -3924 m 1877 | (N) * 1878 | 78 4716 m 1879 | (N) * 1880 | -3 4716 m 1881 | -3 -3924 L 1882 | (N) * 1883 | -4014 597.6042 m 1884 | 4626 597.6042 L 1885 | (N) * 1886 | *u 1887 | 1 Ap 1888 | 0 O 1889 | 0 0 0 1 (PANTONE 289 CV) 0 x 1890 | 68.5 597.6042 m 1891 | -58 597.6042 l 1892 | -58 576.8542 -40.75 545 -3.25 545 c 1893 | 34.25 545 46.5 576.25 y 1894 | 61.3661 568.876 l 1895 | 49.9383 544.7464 25.7637 528.0569 -2.7082 528.0569 c 1896 | -42.0854 528.0569 -74.6079 559.9795 -74.6079 599.3567 c 1897 | -74.6079 631.25 -48.25 670.75 -3.25 670.75 c 1898 | 43.25 670.75 69.25 630 68.5 597.6042 c 1899 | f 1900 | 1 D 1901 | -55.625 614.125 m 1902 | -55.625 614.125 l 1903 | 49.75 614.25 l 1904 | 43.2979 637.263 22.099 654.1492 -2.9745 654.1492 c 1905 | -28.1196 654.1492 -49.3012 637.1763 -55.6875 614.0625 c 1906 | F 1907 | *U 1908 | 0 Ap 1909 | 0 D 1910 | 68.5 4716 m 1911 | 68.5 -3924 L 1912 | (N) * 1913 | -4014 670.6667 m 1914 | 4626 670.6667 L 1915 | (N) * 1916 | -4014 480.6667 m 1917 | 4626 480.6667 L 1918 | (N) * 1919 | LB 1920 | %AI5_EndLayer-- 1921 | %%PageTrailer 1922 | gsave annotatepage grestore showpage 1923 | %%Trailer 1924 | Adobe_IllustratorA_AI5 /terminate get exec 1925 | Adobe_level2_AI5 /terminate get exec 1926 | %%EOF 1927 | --------------------------------------------------------------------------------