diff --git a/parts/distributed/explanation/01_distributed.ipynb b/parts/distributed/explanation/01_distributed.ipynb new file mode 100644 index 0000000..1a907fd --- /dev/null +++ b/parts/distributed/explanation/01_distributed.ipynb @@ -0,0 +1,1133 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "01e47d24-5538-4a03-8aaf-f10436701dd7", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "source": [ + "# Setup\n", + "\n", + "Note: you might need to run `Pkg.instantiate()` to ensure that the `Manifest.toml` is up to date. This only needs to be done once." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "926348ac-de9d-4fb9-879e-e2102dae99d8", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1m Activating\u001b[22m\u001b[39m project at `/global/u1/b/blaschke/juliacon24-hpcworkshop/parts/distributed/explanation`\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1mStatus\u001b[22m\u001b[39m `/global/u1/b/blaschke/juliacon24-hpcworkshop/parts/distributed/explanation/Project.toml`\n", + " \u001b[90m[34f1f09b] \u001b[39mClusterManagers v0.4.6\n", + " \u001b[90m[d58978e5] \u001b[39mDagger v0.18.12\n", + " \u001b[90m[aaf54ef3] \u001b[39mDistributedArrays v0.6.7\n", + " \u001b[90m[6f74fd91] \u001b[39mNetworkInterfaceControllers v0.1.0\n", + " \u001b[90m[91a5bcdd] \u001b[39mPlots v1.40.5\n" + ] + } + ], + "source": [ + "import Pkg;\n", + "Pkg.activate(@__DIR__)\n", + "Pkg.status()" + ] + }, + { + "cell_type": "markdown", + "id": "40fbafa5-a78f-42a1-94be-7f5ccdcd0340", + "metadata": { + "slideshow": { + "slide_type": "slide" + }, + "tags": [] + }, + "source": [ + "# Working with `Distributed.jl` on HPC" + ] + }, + { + "cell_type": "markdown", + "id": "a8f75749-8cd6-42ff-8a85-ac746fe1b3c4", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "As an example: we'll be working with 4 workers (Slurm decides where to place them)." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ab1c3b87-e3bb-455c-9d3d-f5402a6e9348", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "NWORKERS = 4" + ] + }, + { + "cell_type": "markdown", + "id": "b309452f-a599-410c-ae49-907df442e13f", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "**Disclaimer:** This is a work in progress -- and things will get easier (and better) over time. So keep an eye out for this!" + ] + }, + { + "cell_type": "markdown", + "id": "a091249f-d444-442a-843c-4b7eaf827f45", + "metadata": { + "slideshow": { + "slide_type": "slide" + }, + "tags": [] + }, + "source": [ + "## Using `ElasticManager`" + ] + }, + { + "cell_type": "markdown", + "id": "87fcd45e-5a31-47a0-a60c-8e7f25f296be", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "On your laptop you would do something like:\n", + "\n", + "```julia\n", + "using Distributed\n", + "addprocs(4)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "bca7ab68-7d71-443c-ae1a-cb79cad0c778", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "On most HPC systems you need to work with a resource manager to request resources. You have two options:\n", + "1. Use something like `SlurmManager.jl` to handle talking to your resource manager for you. Pro: when it works it's \"cleaner\". Con: when it doesn't work, it's harder to fix.\n", + "2. Use `ElasticManager` (from `ClusterManagers.jl`) to \"wait for\" incoming workers. Then have your resource manager to launch workers for you. Pro: more flexible. Con: more work for you." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a4650a3c-afcc-40af-8eef-4f6eb995f385", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "ip\"128.55.84.171\"" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "using Distributed, ClusterManagers\n", + "\n", + "using NetworkInterfaceControllers, Sockets\n", + "interfaces = NetworkInterfaceControllers.get_interface_data(IPv4)\n", + "\n", + "hsn0_public = filter(\n", + " x->(x.name==\"hsn0:chn\" && x.version==:v4), interfaces\n", + ") |> only \n", + "hsn0_public.ip" + ] + }, + { + "cell_type": "markdown", + "id": "67e4c1c9-af5a-4a11-b378-0ca618aa3923", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "Hack 1: you need all workers to know their public HSN address. In the future this will be automated. Currently what we do is we do is to add the code above to: `$JULIA_DEPOT_PATH/config/startup.jl`:\n", + "\n", + "```julia\n", + "using NetworkInterfaceControllers, Sockets\n", + "...\n", + "Sockets.getipaddr() = hsn0_public.ip\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "9daadd7a-6ce4-447c-8e74-f2eefd604cf0", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ElasticManager:\n", + " Active workers : []\n", + " Number of workers to be added : 0\n", + " Terminated workers : []\n", + " Worker connect command : \n", + " /global/common/software/nersc/n9/julia/1.9.4/bin/julia --project=/global/u1/b/blaschke/juliacon24-hpcworkshop/parts/distributed/explanation/Project.toml -e 'using ClusterManagers; ClusterManagers.elastic_worker(\"hYwEFbsjp1TfBPEP\",\"128.55.84.171\",10001)'\n", + "Waiting for workers, got: 1\n", + "Waiting for workers, got: 1\n", + "Waiting for workers, got: 1\n", + "Waiting for workers, got: 1\n", + "Waiting for workers, got: 1\n", + "Waiting for workers, got: 1\n", + "Waiting for workers, got: 1\n" + ] + } + ], + "source": [ + "em = ElasticManager(addr=hsn0_public.ip, port=10001) # or use: `addr=:auto`\n", + "\n", + "println(em)\n", + "\n", + "# launch workers\n", + "@async run(`srun -n $NWORKERS sh -c $(ClusterManagers.get_connect_cmd(em))`)\n", + "\n", + "# wait for them to connect\n", + "while nworkers()x==target_index, idx)\n", + " loc[loc_i] = target_value\n", + " end\n", + " loc\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "c4226f08-478d-422f-9366-f5b63a05a0bf", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "Sanity check: the \"area under the curve\" should be 1." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "15d1300c-c84d-4a2d-94b1-a68365c08975", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(C)*ds" + ] + }, + { + "cell_type": "markdown", + "id": "7c009769-6f13-4ca6-be8e-4602669eae07", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "You can access any part from `DArray` from anywhere (this is what makes them more convenient than MPI, but possibly slower if you use unneccessary communication)" + ] + }, + { + "cell_type": "markdown", + "id": "95a83481-0ad2-4637-bdc2-d37266833602", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "So now we can write our 1D diffusion algorithm :)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "5c167e00-781d-4948-9bd7-bb95b66adf6d", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "step_diffusion (generic function with 1 method)" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function step_diffusion(C)\n", + " DArray(size(C)) do I\n", + " loc = localpart(C)\n", + " idx = localindices(C)[1] # working in 1D\n", + "\n", + " off = idx[1]-1\n", + " for i in idx\n", + " # absorbing boundary conditions (lo, hi are always set to zero)\n", + " if i==1 || i==size(C, 1)\n", + " continue\n", + " end\n", + " il = i - off\n", + " loc[il] = loc[il] - dt * (qx(i, D, C, ds) - qx(i-1, D, C, ds)) / ds\n", + " end\n", + "\n", + " loc\n", + " end\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "bd0ced63-f6ba-4472-ba20-aa3657099643", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "Which we run for 100 time steps. We save every 10th timestep. This is great for debugging and illustration, but it's is super wasteful, don't do this in production" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "f7a9f106-3fa7-4987-98a1-15fe068e45a6", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "sols = [Array(C)]\n", + "for i in 1:100\n", + " C = step_diffusion(C)\n", + " # Save timesteps:\n", + " if i%10 == 0\n", + " push!(sols, Array(C))\n", + " end\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "50907b05-4ed9-4158-beee-f5eb39e9c719", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "using Plots" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "0f67b11e-d202-4a82-b7c8-cb829d715a37", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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6KGHHnvssSPn33DDDc8///yUKVMefvjhlStXFqu8M96bb775z//8zxjjQCDw/e9//9vf/vbY/KGhoXnz5kmS5PP5Lrjggs2bNxe3zjPY66+/nk6n6+rqwuFwc3Pz3r17x+bfeOONzz77bGNj469//evvfOc7xS1yMrj11lt/8IMfbNq0aexhd3f3/PnzxwapXrRo0e7du09q7eRLYnR0lOf5vr4+QkgqlbJYLIcOHSp2UWc+wzAIIT//+c+vvPLKwzMPHTpksVhSqRQhpL+/XxCEaDRatBLPaJIkHZ7esmULx3GKohBC7rrrrmuvvXZs/n333Xf11VcXp75J5oorrrj33nsJIZ2dnYIgJJNJQsjg4CDP8yMjI8Wu7kz21FNP3XTTTfPmzfv9738/NmflypW33HLL2PSdd965fPnyk1n/l+aIcPPmzTU1NdXV1QDgdrvHBm8qdlFnvnHPeb7//vutra1utxsAQqFQbW2teURyihzZfYksyzzPMwwDAOvXrz98pnrJkiXr168vTn2TSSKROHDgwNgpuA0bNsydO9fj8QBAZWVlfX394SMV04QbGRl54IEHfvGLXxw5c/369UuWLBmbPvlN4EsThCMjI0eOVlhaWmoOCVYsR30WJSUl5mdxqimKcscdd/zwhz8c+2kyPDx8+CMoKSlJJpOKohS1wDPZE088EQqFysvLly1bdtVVV4G5Ozq9br/99nvvvfeo0WqP2gTGjsj/x//iSxOEDMMYhnH44RfqYsc0sczP4jQzDOMb3/hGRUXFj3/847E5LMvquj42res6RVFmY41T5/rrr9+xY8ebb7758ssvP/PMM2BuAqfR6tWrRVG89tprj5rPMMyRmwDDMCfTAfWXJgiDwWA4HD78MBwOB4PBItYzmVVUVJifxWljGMby5ctzudzq1avHzosCQEVFxeFDkHA4XFpaevgp04QTBCEQCFx00UW33XbbCy+8AOYmcBo999xzhw4dam1tbW1t3b9//z333HP//ffDZzaBioqKk/kvX5ogXLx4cTab3bp1KwB0d3e3t7cvXbq02EVNUkuXLm1vb+/u7gaAbdu2ZTKZ8847r9hFnZkwxjfffHM8Hn/55ZePPOZYtmzZSy+9NHYuaM2aNcuWLStejWc4URQPT2/fvj0UCgHAkiVLDh482NnZCQA7duxIJpPnn39+0Uo8oz300EOrV69etWrVqlWrqqurb7rppuXLlwPAsmXL1qxZM7bMBGwCJ9eW57R6+OGHy8rKVqxYEQqF7rnnnmKXMym89tprLS0tFRUVbre7paXlgQceGJt/9913h0KhFStWlJWVPfLII8Ut8gy2evVqAJg2bVrLx8LhMCEkk8k0NzdfdNFF11xzTTAY7O7uLnalZ6xQKPSVr3xl+fLlzc3NTU1NY+8/IeS+++6rqqpasWJFeXn5r371q+IWOUkc2Wo0Ho83NTUtXbr0yiuvDIVCg4ODJ7PmL9noE+3t7Xv37m1qapo7d26xa5kUEolEX1/f4YeBQGDsFzEA7Ny5c+xu4unTpxenuEkgmUz29vYeOWfGjBljYwDJsvzOO+/IsnzJJZeYw0OeOvF4fOvWrZlMJhQKLViw4MhrsWObQHNz84wZM4pY4eRx8ODBQCDg8/nGHoqiuHbtWsMwLrnkEofDcTJr/pIFoclkMplME+tLc43QZDKZTKZTwQxCk8lkMk1qZhCaTCaTaVIzg9BkMplMk5oZhCaTyWSa1MwgNJlMJtOkZgahyWQymSY1MwhNJpPJNKmZQWgymUymSc0MQpPJZDJNamYQmkwmk2lS+//6O+TJzgov6wAAAABJRU5ErkJggg==", + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/html": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "plot()\n", + "for (i,c) in enumerate(sols)\n", + " plot!(c, label=i)\n", + "end\n", + "plot!()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "5113ce93-772d-40b8-8bd7-d05297aef242", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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cnNzL7nkEiHErbfe+tprr23evPmnn356+umnb7nlFiFE7969x48ff//99x8+fPiFF16oq6ubNGlSxD4gAAAtCenUaF1d3ZNPPimEGDt27JNPPtmlS5dLL71UCOHz+YIj5T/44IM77rhj2LBhAwYMWLZsmXZVb9y4cQ8//PC0adO8Xu+NN9544403ao0XLVqkNe7Tp8/HH38cvBcGAIAO1orhE7po1fCJ6LRjx45NmzZNmzZN70Jiw9q1a71e7y9/+Uu9C4kNf//73/v373/BBRfoXUhsePnllydPnpyfn693IbHh8ccfnz17dnJyst6FRByzsETcli1bSktL9a4iZpSVlX3xxRd6VxEzVq5c+c033+hdRcx4//33bTab3lXEjL/97W8HDhzQu4qOQBACAAyNIAQAGBpBCAAwtGi/WSYlJSU3N9dkiuHArq2tra2tbdvMDgbkcDiklNnZ2XoXEhsOHz6clJRkkFHP7VddXZ2dnd3CNI1o7Oeff87Pz4/16Rquu+66Rx55pOU20R6EVVVV9fX1elfRLtqEYQwRCZE2ICfW//Y6jM/nM5vNMX2k2JEaGhqYtCh08bG7unXrdtYbX6M9CAEAiCgOJAEAhkYQAgAMjSAEABgaQQgAMLTwPI8QTXK5XJ988snWrVsTExOvuOKKESNG6F1RbPj000+rqqr+67/+S+9Cop3T6XzjjTf27NnTo0ePG264gSE6LfD7/R988MHmzZszMzOnTp3ar18/vSuKOgcPHty4ceO+ffvGjh3beP/8/PPPixYtcrlcv/3tb0eOHKljhZFDjzCCHn/88TfffDMrK0tV1QkTJixevFjvimLA9u3br7/++jlz5uhdSLTbv3//0KFD161bd84551RWVtrtdr0rimo333zzs88+O2jQIK/XO2zYsO+//17viqLO2LFjH3nkkXnz5m3YsCG48tChQyNGjDhy5Ei3bt0mTpy4Zs0aHSuMHHqEEfTggw8Gh+526tTpb3/7G8+gaFkgELj11lv/+7//+3/+53/0riXazZs3b+zYsQsWLNC7kNiwdOnS1atXFxcXCyG2bNnyySefDBo0SO+iost3331nMpkuvPDCxisXLFhQVFT03HPPCSHMZvMTTzwxfvx4nQqMIHqEEdR4Aov6+vrU1FQdi4kJzzzzzMUXX1xUVKR3ITFg5cqV11xzzdtvv/3iiy+Wl5frXU60Gzhw4NatW4UQbrd7586dpOCZmpyWoaysrKSkRHtdUlJSVlYWl0PPCcKOsHfv3vnz59977716FxLVdu7cuWjRooceekjvQmJAbW3twYMH77nnnu3bt+/evXvEiBHr1q3Tu6io9sEHH8yfP79///49e/a87rrrJk2apHdFsaG6ujonJ0d73bVrV6/Xe+TIEX1LigROjUbcoUOHLr/88rlz544bN07vWqKXqqq33HLLc889x7SZodAO3qdOnXr//fcLIVJSUh5//PFPPvlE77qilJRy+vTpJSUld911188//3zzzTcXFxdPnDhR77pigMVi8fv92mvtRVzOFkmPMLKOHDkyYcKEKVOm0B1s2d69ezds2HD//fcXFRXddNNNLperqKho165detcVpZKTkzt37jxw4EBtcdCgQXv37tW3pGhmt9v/9a9//eUvfyksLCwpKbnxxhsXLlyod1GxoXv37lVVVdrrysrKtLS0jIwMfUuKBIIwghwOx8SJE0tKSjjdd1b5+fnr169/5ZVXXnnllXvvvTclJeWVV17p1q2b3nVFr1/96lfBu/s2bNgQDEWcqXPnzlLK3bt3a4s//fRTly5d9C0pVlx55ZUffvihNhX+3//+9yuvvFLviiKCSbcjaPbs2S+88MKQIUO0xa5du65cuVLfkmLC+vXrJ02adOzYMb0LiWq7du0aM2bMxRdfrKrqhg0bPvvss4KCAr2Lil5z585dvHjxVVddVVFRsXXr1rKysvPOO0/voqLLnDlzvvzyS5vNlpOT07lz5xdeeKG4uNjj8fziF7+wWq09evT4/PPPv/jii7g85CIII6iiouLQoUPBxYSEhMGDB+tYT6xwu93l5eXBAwg0p6amZs2aNYmJiaNGjYrLE1bhtWPHju3bt2dkZIwaNYpbuM9UXl7udDqDi/369dN+qbxe7+eff15TUzNhwoROnTrpV2AEEYQAAEPjGiEAwNAIQgCAoRGEAABDIwgBAIZGEAIADI0gBAAYGkEIADA0ghAAYGgEIQDA0AhCAIChEYQAAEP7/4fFyxHUNfcbAAAAAElFTkSuQmCC", + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/html": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "plot([sum(s) for s in sols]*ds)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b6b1213-0dee-4b39-9ed2-a1d4cb0927e0", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "f073268b-f643-476d-9ab7-af55f8546303", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1+1" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Julia 1.9.4", + "language": "julia", + "name": "julia-1.9.4" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.9.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/parts/distributed/explanation/01_distributed.slides.html b/parts/distributed/explanation/01_distributed.slides.html new file mode 100644 index 0000000..67d93e4 --- /dev/null +++ b/parts/distributed/explanation/01_distributed.slides.html @@ -0,0 +1,8318 @@ + + + + + + + +01_distributed slides + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/parts/distributed/explanation/02_dagger.ipynb b/parts/distributed/explanation/02_dagger.ipynb new file mode 100644 index 0000000..75cb2ec --- /dev/null +++ b/parts/distributed/explanation/02_dagger.ipynb @@ -0,0 +1,865 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "id": "2c352005-c33b-49be-bcb8-06d6aa9efd3e", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1m Activating\u001b[22m\u001b[39m project at `/global/u1/b/blaschke/juliacon24-hpcworkshop/parts/distributed/explanation`\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1mStatus\u001b[22m\u001b[39m `/global/u1/b/blaschke/juliacon24-hpcworkshop/parts/distributed/explanation/Project.toml`\n", + " \u001b[90m[d58978e5] \u001b[39mDagger v0.18.12\n", + " \u001b[90m[aaf54ef3] \u001b[39mDistributedArrays v0.6.7\n", + " \u001b[90m[91a5bcdd] \u001b[39mPlots v1.40.5\n" + ] + } + ], + "source": [ + "import Pkg;\n", + "Pkg.activate(@__DIR__)\n", + "Pkg.status()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "9341a3c6-244c-4cdf-85a7-bbfce5de5678", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "using Plots" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "e30f8641-767b-4e2f-898b-28891b33bac4", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4-element Vector{Int64}:\n", + " 2\n", + " 3\n", + " 4\n", + " 5" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "using Distributed\n", + "\n", + "addprocs(4)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "7f7c3233-35ac-485c-b018-2512abc05214", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "@everywhere begin\n", + " D = 1e-4\n", + " ds = 1e-4\n", + " dt = ds^2 / D / 8.2\n", + " qx(ix, D, C, ds) = -D * (C[ix+1, 1] - C[ix, 1]) / ds\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "4a89a1df-831c-4be9-a3f3-cecc4c6244aa", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING: using Dagger.In in module Main conflicts with an existing identifier.\n", + "WARNING: using Dagger.Out in module Main conflicts with an existing identifier.\n" + ] + } + ], + "source": [ + "@everywhere using Dagger" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "e2fb2446-56bf-45ec-8aa3-9bb40576ead3", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "40x1 DMatrix{Float64} with 4x1 partitions of size 10x1:\n", + "\u001b[33m~0% completed\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " ⋮\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Also: try AutoBlocks()\n", + "C = zeros(Blocks(10, 1), 40, 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "28245817-b668-44aa-93d5-f5b4b0b6d11e", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "40x1 DMatrix{Float64} with 4x1 partitions of size 10x1:\n", + " \u001b[31m0.0\u001b[39m\n", + " \u001b[31m0.0\u001b[39m\n", + " \u001b[31m0.0\u001b[39m\n", + " \u001b[31m0.0\u001b[39m\n", + " \u001b[31m0.0\u001b[39m\n", + " \u001b[31m0.0\u001b[39m\n", + " \u001b[31m0.0\u001b[39m\n", + " \u001b[31m0.0\u001b[39m\n", + " \u001b[31m0.0\u001b[39m\n", + " \u001b[31m0.0\u001b[39m\n", + " \u001b[32m0.0\u001b[39m\n", + " \u001b[32m0.0\u001b[39m\n", + " \u001b[32m0.0\u001b[39m\n", + " ⋮\n", + " \u001b[33m0.0\u001b[39m\n", + " \u001b[33m0.0\u001b[39m\n", + " \u001b[34m0.0\u001b[39m\n", + " \u001b[34m0.0\u001b[39m\n", + " \u001b[34m0.0\u001b[39m\n", + " \u001b[34m0.0\u001b[39m\n", + " \u001b[34m0.0\u001b[39m\n", + " \u001b[34m0.0\u001b[39m\n", + " \u001b[34m0.0\u001b[39m\n", + " \u001b[34m0.0\u001b[39m\n", + " \u001b[34m0.0\u001b[39m\n", + " \u001b[34m0.0\u001b[39m" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "C" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "e9b871fe-5437-41da-8634-53b660ba70c9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "10000.0" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "C[20, 1] = 1/ds" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "6c04364c-2e88-4ca6-aea3-7961f503264c", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "40x1 DMatrix{Float64} with 4x1 partitions of size 10x1:\n", + "\u001b[33m~0% completed\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " ⋮\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m\n", + " \u001b[90m...\u001b[39m" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "C2 = similar(C)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "966122c7-873a-4dd3-a595-b443a95860e3", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "40x1 DMatrix{Float64} with 4x1 partitions of size 10x1:\n", + " \u001b[31m6.93402e-310\u001b[39m\n", + " \u001b[31m6.93402e-310\u001b[39m\n", + " \u001b[31m6.93402e-310\u001b[39m\n", + " \u001b[31m6.93402e-310\u001b[39m\n", + " \u001b[31m6.93402e-310\u001b[39m\n", + " \u001b[31m6.93402e-310\u001b[39m\n", + " \u001b[31m6.93402e-310\u001b[39m\n", + " \u001b[31m6.93402e-310\u001b[39m\n", + " \u001b[31m6.93402e-310\u001b[39m\n", + " \u001b[31m6.93402e-310\u001b[39m\n", + " \u001b[32m2.0e-323\u001b[39m\n", + " \u001b[32m4.0e-323\u001b[39m\n", + " \u001b[32m1.5e-323\u001b[39m\n", + " ⋮\n", + " \u001b[33m1.0e-322\u001b[39m\n", + " \u001b[33m5.0e-324\u001b[39m\n", + " \u001b[34m2.0e-323\u001b[39m\n", + " \u001b[34m4.0e-323\u001b[39m\n", + " \u001b[34m1.5e-323\u001b[39m\n", + " \u001b[34m1.6e-322\u001b[39m\n", + " \u001b[34m2.61194e-314\u001b[39m\n", + " \u001b[34m4.94e-322\u001b[39m\n", + " \u001b[34m4.94e-321\u001b[39m\n", + " \u001b[34m1.235e-321\u001b[39m\n", + " \u001b[34m1.0e-322\u001b[39m\n", + " \u001b[34m5.0e-324\u001b[39m" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "C2" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "446100c0-5e47-4470-8be0-448e34ec053b", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "myid()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "181d41c3-08b0-4eb4-b1f0-47d7fb798eb2", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "DTask (running)" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d1 = Dagger.@spawn myid()\n", + "d2 = Dagger.@spawn myid()" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "b996adf3-5b66-4bf1-916f-f67d693c0cc1", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fetch(d1)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "9be58c29-2ddc-457f-a295-3aafc7c479c3", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fetch(d2)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "19641792-05cd-4bc9-af30-86fbab594b42", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "@everywhere function step_diffusion_local!(C2, C)\n", + " proc = myid() - 1\n", + " idx = C.subdomains[proc].indexes[1] # working in 1D\n", + " \n", + " for i in idx\n", + " # absorbing boundary conditions (lo, hi are always set to zero)\n", + " if i==1 || i==size(C, 1)\n", + " continue\n", + " end\n", + " C2[i] = C[i] - dt * (qx(i, D, C, ds) - qx(i-1, D, C, ds)) / ds\n", + " end\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "5b39f1be-24a8-4957-af77-6093c202c7c5", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "DTask (running)" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d = Dagger.@spawn scope=Dagger.scope(worker=2) myid()" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "d8344f4a-826e-4ce6-89f4-d21a2a9fb4fe", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fetch(d)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "0d389831-a061-4687-bfd6-cd8416e1a212", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "DTask (running)" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "C2 = similar(C)\n", + "d = Dagger.@spawn scope=Dagger.scope(worker=3) step_diffusion_local!(C2, C)" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "0a51e594-ceea-48ed-a220-ba423bf4e1aa", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "21x1 DMatrix{Float64} with 3x1 partitions of size 10x1:\n", + " \u001b[31m6.93403e-310\u001b[39m\n", + " \u001b[32m0.0\u001b[39m\n", + " \u001b[32m0.0\u001b[39m\n", + " \u001b[32m0.0\u001b[39m\n", + " \u001b[32m0.0\u001b[39m\n", + " \u001b[32m0.0\u001b[39m\n", + " \u001b[32m0.0\u001b[39m\n", + " \u001b[32m0.0\u001b[39m\n", + " \u001b[32m0.0\u001b[39m\n", + " \u001b[32m1219.51\u001b[39m\n", + " \u001b[32m7560.98\u001b[39m\n", + " \u001b[33m2.0e-323\u001b[39m\n", + " \u001b[33m4.0e-323\u001b[39m\n", + " \u001b[33m1.5e-323\u001b[39m\n", + " \u001b[33m1.6e-322\u001b[39m\n", + " \u001b[33m6.93402e-310\u001b[39m\n", + " \u001b[33m4.94e-322\u001b[39m\n", + " \u001b[33m4.94e-321\u001b[39m\n", + " \u001b[33m1.235e-321\u001b[39m\n", + " \u001b[33m1.0e-322\u001b[39m\n", + " \u001b[33m6.93403e-310\u001b[39m" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fetch(C2[10:30, 1])" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "66e26940-57b9-4937-8ccf-6dfc6b87fa83", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "step_diffusion (generic function with 1 method)" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function step_diffusion(C)\n", + " C2 = similar(C)\n", + " fill!(C2, 0)\n", + " \n", + " dtasks = Dagger.DTask[]\n", + " for w in workers()\n", + " push!(\n", + " dtasks,\n", + " Dagger.@spawn scope=Dagger.scope(worker=w) step_diffusion_local!(C2, C)\n", + " )\n", + " end\n", + " \n", + " # wait for workers\n", + " [fetch(d) for d in dtasks]\n", + " C2\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "a1697ffd-f38a-4d1f-bef2-8117d0807388", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "sols = [Array(C)]\n", + "for i in 1:100\n", + " C = step_diffusion(C)\n", + " # Save timesteps. This is super wasteful, don't do this in production\n", + " if i%10 == 0\n", + " push!(sols, Array(C))\n", + " end\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a44c2b7-a79d-4aa2-96dd-b061fa039787", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# using Plots" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "f9f25bc5-56d4-4666-aef3-dee02a54ec72", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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cBITBKni4uLGxkb3402bNlksloyMDP+WhDtCgKDRNGtUIabqHQhCCEpHjx69//77w8LCGIYhhKxatSoiIsK/JSEIAYJGs71GSanV39UAeOT222+fPHlyaWmpTCaLjo6mKP+vAkIQAgQNGysoaYqwmCwDwU0ikSQlJfm7ioswRggQNBisIwS4BhCEAEEDp08AXAsIQoDg4OIJRYhERMjFLdYAgpjFYrn33nuXLl3q70IwRggQJBj2wu0gIUSJTbch+C1YsOC3335zOBz+LgR3hABBomk1PUHXKAS/3377LTs7e86cOf4uhBAEIUCwYNxTRgkhOJgXgpzD4Xj00Uc//PDDQFg7QdA1ChAsmlbTE8waBe+4LOyJ9wsF3hfDzCKa6vmnJKn2kqx56aWXpk+f7vcNZZogCAGCQ9MZTIQQhZjCFmvgMYma7vlIom+CkFCkRQoeOXJk06ZNu3btamhosNvtTqfTaDTqdDpfFHMFCEKA4MA0C0IsqAcvyQwSf730yZMnq6qq0tPTCSF2u53juMGDB58+fdpf9RCMEQIEi0tmjdKEYQluCSEYzZ07t/53zz333NSpU/2bggRBCBAsbNzFyTIiikhExImbQghyCoXC78fTE3SNAgSL5l2j5PebQpn4yl8AEPCeffZZf5dACO4IAYJF81mjBPNlADoPghAgONguuyPEfBmAToEgBAgODEeUzTpCsZQQoLNgjBAgONhYQUFf3IYDu6xBkHI6nZMnT276cPr06Y888ogf6yEIQoBgwbAkSnnxQ6UYQQhBief57du3b968WSaTEULi4+P9XRGCECBIMGzLrlGMEULwGj16tFKpvPp1PoEgBAgOzU+fIIQoafes0YDYsxigo26//XaKokaNGvXEE08oFAr/FoMgBAgOLdYRYowQPOZgand+fZsg8L54MYoaNWOdXB3V1CAWi5cuXTpgwICGhoZXXnnlwIED69at80UlV4YgBAgOzKWTZXASE3hMpgy7ecZannP64LVEYlnzFCSESCSSJ5980v24V69eiYmJNTU14eHhPijmShCEAMHBxhLlpUGIO0LwmEId7e8SCCEkLCyMoiiLxeLfIMQ6QoDg0LJrFLNGITjl5uaWlJQQQpxO54IFC1JSUhITE/1bEoIQIDg0P32CEKKgKRuHLdYg+Jw9e/amm24yGAwGg+HIkSPfffed38+p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zQBcH9pYPsHbLI79W6/0hUqasmGXNYAO8qj/hRBCiNVqbWxsfPfdd+Pi4r744osJEyacO3fOYDCYzebs7OxVq1a5L8vIyHA/ePPNN3fv3r1jx468vLzp06f36NEjPT3dbrdPnjz55ZdfnjZt2l//+teHH374u+++64S3BRDM2ugabfj6n46c/VaNVjVowuWfxaxRAA94HoQhISEffvih+/FLL720dOnS7Ozsm2++mRCiUCjGjRvX4vqPP/74ww8/TEhISEhImDZt2ooVK958880NGzaEhYU9+uijhJC///3v8fHxFRUV0dHRHlcFcB240vIJ674f2aoS1QOLjStflyV1pyPiWlyABfUAHuicMcKcnBybzZaWlub+sKamZvjw4RMmTHjvvfdYliWEmM3m8+fP9+vXz31B3759T5065f7CpsaoqKjIyMizZ892SkkAwYthBYW45axRtrbcuPmzkLl/Fcd01WbdW79yicC1DD0cUg/gAc/vCJuYzeZ77rnn5ZdfjoyMJIRERER89NFHGRkZJSUlzzzzTElJyZtvvllXV0cI0Wg07i/R6XTV1dWEkLq6uqZGQoher7/SMKHFYiksLDQYDIIgUBRFCHnuuecee+wx7+uHDrHb7WKxWCKR+LuQ65nFKaFcTouFb2oRWJfl08XysbMdmjCrxaK+aZSQs7923TJF1tzmX0jz4kaGslhwV3gNWa1Wf5dwQ+N53m638zx/9UsJIYTI5XKavkrSeRuEDMPceuutAwcOfP75590tcXFxc+bMIYT069dPqVTefffdb775psFgIIRYLBaVSkUIMZvNoaGhhBCDwVBaWtr0bCaTKSQkpNUXUqvVCQkJhw4dslgsarWaEKLRaK769qDT0TSNILzWHAIbqpGq1RdvCo3rl0nDokNG30EIEQRBrVYr732u6q1H6czB8rS+TZfplLyNFdRquR+KvpG4fwWBX/A8T9O0UqnsxOf0qmvUZrNNnTo1MTHxo48+ct+ltaDX6+12uyAIOp0uNDT0zJkz7vbTp0937dqVENK1a9fTp0+7G41GY0VFRXJy8hVrFYkMBoNerzcYDAaDASkI16sWs0btZw4zx3YbZj3R/BqRUh1y9zMN/3mLMzc0NWKMEMADngeh0+mcNm2aXC5/6623jEZjQ0OD0+kkhBw4cKCsrIwQUl1d/fLLL0+cONGdkfPmzXvrrbdcLldhYeHXX389b948Qsj06dNPnz69fft2QRCWLl06ZMiQxMTEznlnAEGr+axR3mJsWL005J75IpW2xWWylEzlwPENXy0lwoUlExgjBPCA50FYXl6em5t76tSpQYMG9e/fv3///j/99BMh5ODBg5mZmSqVKiUlJTQ09KOPPnJfv3DhQoqiwsPD+/bt+8wzzwwaNIgQotPpVq1aNXfu3JCQkM2bN3/yySed8q4AgtrF8wgFoX71UtWgCbLUm1q9UnfLXN5mtuze6P4QyycAPEAJwjVZfmu32+XyVgYqHA6HVCq9vB/1Stc3yc/PnzRpUl5entlsbj6/BnwMk2V8QPm5q3aOREkTy651zOH/hf9lKSW+2FXa4keArauofueJ8D+9IYntuqaQ/6ZAWDPWo21poH2apimAX7gnywTQGGEbrpRqMpms1dHEtlMQ4MYhEOLgiIImrooi00//Dbn3ueYpeDk6NFo/9cH6lX8XnA4lTTHYWQagg7DXKEBgsbFELibE6aj7/HX9HQ/T4bFX/RLlgHGS2BTjhk9w+gSABxCEAIGFYYlCTBrXfSSN76bsN6adX2WY+bg992jY2V9w+gRARyEIAQKLjRNuMf/myDumn/5o+7+KkilC7n1Ot+3famv1tasN4LqEIAQILLb62mcL/x0y5zmRvGPTAaTx3bihtz11eilp96YbAEAQhACBRRDotW9v6DJVmpjuwVfLRs92ErF5x5pOrwvgOoYgBAggpm2rBZ77MXGaZ1+uoEXPJTxl2bXeWXS6cwsDuI4hCAEChfN8rmX3xtJJT8slHv5gKmlSLArRz3i8fuU/eDvTueUBXK8QhAABQeDY+i/eMMx+wqgMv/wMpnaSiQnLE2nmUFl6X+PG5Z1bIcD1CkEIEBDsOQfE+jBFz8E27orH07eHgiY2luhumWc7+gtvs3RegQDXLQQhQECw7t+mHDSBXPl4+nZybzcqUmllaX1sR3Z1Wn0A1y8EIYD/caYGZ+FJZe/h5LIzmDpKIb6wy5pq0ATr/m2dVSHAdQxBCOB/zIFtil7DKZmCEGLjiNKLTbObTmKSp/fnTPWusoJOqhHguoUgBPA/68HtqsET3Y87pWuUEEIoSjVgnPXg9k6oD+C6hiAE8DNHQQ4RBGnChRX0NlZQ0B7OGiWEKJodSagcPJE59LPAurwvEuA6hiAE8DNm/1bV4Czy+/Fk7k23PaYUXzykng6NkkQl2HP2e10jwPUMQQjgT4LDZsveq+x/8ZQJL5dPtDiSUDVognX/Vm8qBLjuIQgB/Ik5+ossOVOsDbnYcuUxwkaH8ZPjK5/d87fvz21n+dbPW2reNUoIUdx0s7PoDNdY05lFA1xfEIQA/mTdv005aGLzllaXT9TZGj448um9Gx+1OK3TU27dUbz77o0Pr8/93sk5W1ypvDQIKYlUcdMI5uDP16R6gOuCF10wAOAdtqaMqyuXZ/Rv3siwgpK++BdqlbXm69PrtxftGpc48rPJ74UpQ81m87jUkfkNBStPrvk8+79TUyfNzJiqklw4s+nyQ+pVgybUf7lEM25W0zAkADSHIATwG+u+H5UDxlHiS34Mbb93jVZYqtac2fh7BL4fqjA0vyzF0PVvI54raCxefeq7ezY+MjV10oz029RSlUJ8yR0hIUSakE7J5I6Ck7LkzGv+lgCCEIIQwE94jjm0I+zRN1o0Mywx2s+/tve7fWWHpqRMWHXbv7VSzZWeo6s+4YWhT5ZbKr/KWXvXhoempEygRVNtXMvrlQPGWfdvQxACtApjhAD+YTt1UBwSJYmMb954rrFIZHtv2aGXY9RR/739k4f7zGsjBZvEqKOeHvTnT255x8E5D+Q/dqpiRa2tvvkFqoHj7Sd/w8FMAK1CEAL4B7N/m2rQhKYP7az9hV2vPfe/V+wk+c1xH9/X666mYb92ilJF/F//B8dnLGV54b7Nj688+U3Tp0QqrSylt+0o9uAGaAWCEMAPOHODI/+Ess/N7g95QXjl17c0Ms3q2z6u4m8xyOQeP3OYIsyge+DzKf/aXrRrQ94PTe3YgxvgShCEAH7AHPxZ0Wuoe5dtQsi/j3xqdTLzBz4qEUu8Pn2CMBwJVRiWjF74RfZ/95YddLfLM/pzDdWuyvPeFw9wnUEQAvgBc+Di8sEt537aV3741ZELJCKadMrpEywhhESpIl4f+eI/9r1/rrGIEEJEYuWAccwB3BQCtIQgBPA1Z9FpgeNkSd0JIQcrji4/vurvo17SSNWEECdPKEJoL34um2+xlh6a+sSAh1/Y9VqDvZEQoho0gTn4s8CxbT4BwA0HQQjga9Z9W1WDJhCKKjKWvP7bu4tHPB+riXZ/ytb2GUwCsZbbG09anaYrhlmLdYSj4odN6jruuf+9YmcddHgsHR5rP3Wgs94IwPUB6wgBfEpw2m0n9kQ+97HRYVqw69U/9bmvZ3h602db3WjU0eBqzLU05lka86ximUgaIi77vkGqpvXd1LpUlS5FRcsv9qU2HczbZG7mzDJLxeu/vbNo+HPKQROY/dsUmUOv4TsECDYIQgCfYo7tlib15NSa57e/ODFpzISkUZd8lhUUYooQwjl4czHTmGttzLU4Gl26ZJW+myrhlkh5iNRsNms0GnudszHXUnPYmLe6TB4q1XdT67uptF1VLfYaJYRQhHp20ONP73h5xfGVD/SZadywjDPVN9/mG+AGhyAE8Clm/zbVyNuX7Hs/UhU+N3Nm80/xLt6cb72n1HJsKWOrcWgSlPpuqpSZMepYBblsl1B5qDRqSEjUkBCBF6zl9sZcS+mOWsvnJXSsYqxTYSnVNv8qWiR+5ea/Prr12UhVxIhew5iDP2vGzvDJ2wUIAghCAN9ha8rY6tL/cHlV1pp3xi6mmpJKIJX76os2V3FhcoFWdL0tWhOvoMTt2iObElHqOIU6ThE3Jpy1cyWnrNpt5tz/lAo86XZ3rCbhwqp8rVTzj9ELH9/218j0aQlbNmjG3Ik9uAHcMFkGwHes+7f9dlPqjvN7Xx35vEQscTe6zOypT4sr9zf0/ktX1+zE3Slh2iRlO1OwBVoujuilfS8qou9zqV1vjzrzRUnR5iqBuzCJNEYdtXD4M6/nrymRCc6iU532rgCCHIIQwFd4/kjOtpVCwZLRC/Uynbut9rjp6Fv5ykh578e7KiJkV5k12g4KMbFxRCDEkKG56alkW43j2NJz1jK7+7O9Inr8qe99b8U5yvZv8fLdAFw30DUK4COFJ356L8b+4vBF8dpYQghr54o2VZkKrN0fSFB3ubDFDMMKSrr1e0G7pdJUd9ZYe8bUUBISmaENS9OGpkku25JbRBGpiDg4IhcTiZrOuC++9rgxZ1lRzMjQ2FFhlIjK6jrmfN25N45u+Rdjkiq11/QtAwQFBCGAL5gc5udPrrgnYni/qN6EkMZcS95/ywwZmt5PJoulFztmGI4oxIQQ4rQ3murOmGrPGutOm2rPmurOUCJaF5ahDU2TyPX1FYcLs1eZ63OlihBtaJo2NF0Xlq4NTdOEdBPTMgVNGJY0LakI663TxCtzV5fWnzR3uztOHiZ9cMAD50//uvinv70y9S3q8nk4ADcYShAEf9fQLvn5+ZMmTcrLy3PPHfd3OTcuu90uFoslEom/CwkmLM/N/2lB4uncvzy8ktDK81trao8ZU2bF6lNVTddwrK0sb8svZ7OZ+jOxXC7nYrShadrQNHfCacMyZIpQ95XNfgQEq/G8qfbMhTvFurOWxgKFJuZXW/rkHulJCQMjEm4ml87HOf9jdXxWRNSQEOPx3U8ffX94v2nzMmf7+LsR7CwWi1qt9ncVNy6e5+12u1LZsbNZ2oY7QoBrbsm+99RW29yIYdYace5X51TR8pueSqZ/31HUaW84d+yzc8c/D4nu66AHVMaMmDMsQ6mJbccTUypdgkqXEJ18YdtSnndZGgo/2ZIzgcvL3r1Y+EXo1v/RLmlTKRFNKBI1JETbVZX7n9K6bFPqzIHzN4gXarfGaWPHJoy4Zm8dIAggCAGurbVnNxU2Fr98zmVOnFj9aXHytJjQXhdG5hhTacGJL4pOro5MHD1yxlpNSOrOY7zdJSg1Hu66LRJJtKHd8tRdNb3Fw0c/W1t2IPfQv3L2/j2170OJPe+hJUplpKz3E13LdtYee7cwOn7GC+KSlw4uS9LFd9UndN47BggyCEKAayin9szKk2veiv1zVbVFFaLtMz9WoqEJIcba07mHPqws3B6fcefYOT8p1L/vNcoJit8ny7CCUGazFVuZIoYpsjLFDFNsZYoZpt7hjFEqEpXKRJUyQalMcP9XqYyUy9xf2LS5TFjswLDYLxtrcvIOf3R639L4jDvTBjwmV0XGjQnXJavOruQlZ+2P3//Ai7+8vixrqVqqauUNANwAMEYIHYMxwvZrsDc+9MNTT4Y+JtpCRadXJ9w3kRDivkurrzzWtdfclD4PSuW/r6NwOL+vrPz3WQsrMEqZrdjKVNrtEXJZglKZpFIlKBVNgSd1Ok20uMj6ezoyTLHVVsRYrSznjsbsWnlWrOr2LrrxkREy0YWZOFZjcf7R5cWnvonuOiFj8JNqfVfOyZ969TuHuNuhkceKSfHro17ExJn2wBihf12LMUIEIXQMgrCdOIF7avtLI40ju5yMD3N83OW5F2rqj549+C87U5Ny0/1JmfeKaTkhpMHpWldW/nVJ6YH6hvGREYVGdXet6g8pykSVKk4hl4haWel7pR8BK8sVMdZiK/PcYUua3l7HNpwwmm6NiZrVJW5cRLj7qRxM7bnjnxcc/ywkul/G4Kdk5+vKtp2rd968tccPXbvHzemJfdeuDkHoXwhCBKH/IQjb6YPDn8oOqvoa+8bG7GgQZ593HpPIdN0G/DkmOYuiRAzHbamo/LLo/C+1tTeHhc3oEjstNkZN03/YxY2OoealNss/jufNNsHI8I2MYGL4RqvDZJWF6URahUinonRKkU5JqeXNX3rmz9ydSdTMrqI6p3NLReXK4pIjDY2To6NmxMVmRUVIRCKX01yYvSr/yDJtWEbUOUGT+VThHvWm+I1TJ48fGNPX19+pYIMg9C8EIYLQ/xCE7bG7aH/e6rK+2l6xoyqOff+AJC6lx4gXw+OG2Dhue1XNmtKyLRWVQ0NDZnSJvSM2RkPThBC+0crmVny+3zxQyqRSNsHE8A1WwWzjLXaRRkFpFSKDitIqRXqVk+KlDp432fhGq2BieCMj2F0irUKkV1E6pUir2GhSxkQqhvQ00KnRlFxCCCm12daWlq8pLTtjMt8SHTUjLnZSdKSIZ8+fXpPzy2t6Rttz+vqcL2v26H6994Hbo9WR/v7+BTQEoX8hCBGE/ocgvKqiytL9H53smhCr6b4zd987yVGTuk5/d3t13ZrSso3lFT112rkJ8bO6xOokEoFxsKfLXKdK2VMlvMlGp8dusijT49U9ElWUVikyqCiNQqRVthi5a+VHgOUu5KKR4Y3MphOWeGLLsDZwRdXi+DC6exdJ9zg6JYqIRecZZl1ZxZrSsrNm87TYmHsT4gdqJEc+vr1OXJE+eOH5H1ML5IX3/N+tconMl9+x4IIg9C8EIYLQ/xCEbWusMu99/zidmEcknypl4ep8xbrbnv+wqLiPXj+rS9wdsdEGIuKKati8CjanhC2oortG0j26SHrEiRPCCUVN3Gz9a7p5kMbosjWwNiPL1LM29+NGlmlkbUbW1sA6GYlCR8t1tFJPK/QSZQitcD/Qux8sPq0J1RvmZ0oEJ8vmVbC5FVxeRYvXyrVYvy4p/bqkjCfCM+GawVteK403KTRxtflTaRI56S/DxTJsRNw6BKF/IQgRhP6HIGyDsch6cNmhuvh/acT5sSNe/c++gs80XSZ16fJiWrfkBpsrp5TNKWHPVYqiDJIecXSPLpK0GIe10lJ2wlJ+wlKeba085XQ6xUq9UqVvlm0GWmmQKC600Aq9zcnKacLaGlib0cU0sEwDa2t02RovhKW90WhqlLgaZeowVXSmJraXOqaXKiZTTBTsuUo2p8SVU8rXmenkSLpbjKRH3G9q8ZIzuSfKzz+qEk2ONhYeeKfROiyefXjoo73dKz2gBQShfyEIEYT+hyC8ktrjxqPrV9br/6npOecH/ZTV50vvqCv424jp+oNFzn25Ir1K0r0L3SOOJKisdact5dmWshOW8mxKJFLH9FLH9FLH9lJF9+zzvfgfg9gYlbPR5TC6HEaXo9HlMLqcRpej0ekwuhwNLrvF6TDIlDqJVCeR6aUyg0TufnChRSL7MlfC8dJX0+os5Sct5ScsZSeslTlSdbg7EdWxvRTSLvzZGtepEjanlFJKpSMyTneRvrxvx8HI5D/GhQ7MX2opz42zvjTggSnKSPSRtoQg9C8EIYLQ/xCErSr86VjOsRdPKRuP9nh+m5E8EBX98KYNoXwSYQTp0FRXD5HVkmspz7aUn3BZ69TRPdWxvZXRvWpDks5y1Eljbbax9qSxNtfSwPGSKLksTCbTSaR6qcwdbDrJhQd6qUwvkfEOJyehf89IR6PL0eh0NA/OcpvTytoVYlF3bWgvfVhPbVgPbUgqZ5FXn3HXYKs9pwxLVsf2VsdkKkVdqUMNzv15gspZ3lX2dt+bvq+tmSCtvPnsO3HM4IG3vxaSHuHv725gQRD6F4IQQeh/CMIWONZ5cNV7+yxbvgjJKlWl/50Om1ZiIUfOcapGkpVicmXX5+2QKA3a+AFsVM/z2i7niCzHXH+4oep4Y42GlvbQhXbXhvZQSOIJEy2YFuxnnky3KCiry2V1uqwul9XhMDldVhdrc7msDofR6bLyPEvTCqlEKZGo5DIdTSvdj2UyrVSikkhURxqU522qe7rrG8X6QkF1xNR4ylSXbax18nx3bUgPbViGWtudZ5JMxaQi21j4m0QVEpIyVs3E85tOiUUR9r5JnyeoXrNUDGJO3VP7w6D0J7pnzfT3tzmAIAj9C0GIIPQ/BGFzNcX7P//+7bXqm618yLMNrltLKEojtqcx1Xlf2GUMHZZ0PmHkUUPqXitztLGaEJKpC8tUqTMk9ljBonPWMpbihsaiRmOhQHi9LkmvTVxdpJqeHiaRqgVaxVBqXqTkxCqGyFlK5qSkjCB1ENpkc4UoaClhZZRLIdho3i4VHDLCiFirhLdIOPPZenON0TjIUN/YWGA0nVcoQvW6RIMuSaSMqZeEFAuak05xtqnhtLk+QqYcEBIxTCa5qSFXm/sja6xRSWJDoibLDtOcRLI81PxBbFiGI/t+oWrqzFfkWFZBCEEQ+huCEEHofwhCN9ZpWbX1nY/qlAPqI+dWmCMZs7g3MUnPmKqPl6mTj8pijqYM3dNQk6xQTFBxPYRarbPGbiltNBY4nBaDLlHQdrcp0kzSLg3iiGpeW+qgiqzOIsZp53i9VKwQiRRikV4qlotFSrFIJxHLxZTqwgMRzblsFG3neYbljS7OxgkMxxtdnJ3jrSxvYjmLi7fzvEEqTlTJEpWSKIkzgjIa2EqNo0hmPeU05VosFWpVlE6XRCmjayURhzj9D2aWoqiRGn3mkU39Fo+zpAAAIABJREFU1HZNY5EmpLe8IUZ8Rn02NvKdcLGgyn4pM3NIv7vJDb8NG4LQvxCECEL/QxASQnac/um9vcdGnTdMq642xRaXi4+ycsW5pFGHNUk/Wx36xvMTQ0TJtFFpKTA2nuMNg4y6IUZJQi0VWsUrS+ykyOrU0KJElSxBJU1UShNVskSVNFElS1BKtV9w7B8lbUfNVX8Evi8R/nWKWzmaFFmdRVZHkdVZzDgLrQ73h1KRKFEliZWyEZQlRKjW2/I0Db+IbYW6sMwGRcLRRv5HVifThA+X8H0bznY/v5d3yVIdA9i6hA/iNca40lfGz0qI6Na538/ggiD0L5xHCOBn+Q1Vy9d91zvP/nd7Xm3k+VWZ2s3q6FLtPaFc4xiJZYB118DqoxVUpJkfs4vqX6y+97RYopeIb1IoU9WyQe7AU0qT1DKluJVVelaWKMSc9zdcSprYWBIqpUOldD9Dy98XtQ62yOosYty5GHdI6H5UPl6lEmUo7fF8aaQs55H6jWEO3qbPOKMI+ShpPKF1qaZzk0N/uM+sVO+L33JmR2nmnmemzjbIOvM3EYAfIQgB2qXMbNn0zXe9T2WP0eTviBEvj+jZwCszHHm3kBPWupONuqFF1KCdVNRpNa2Xy/uFqPsZlPcYVINCVRGy9v6UMSxRdMZPZNMxTK0Kk9FhMrp/yCUxVm5zHW5gDjfEHW7odZLNqnOyyTSbIJwfzh2JsOwXRK6toVEfRHdNSRQNqd4y9Zj4wJH8qn6DZ9wxWUF7eHQiQOBAEAJcRV2tcf9XXwq1B85GONdnGihKkSayxdmKzHSPPZrJ3wkh6Vp5X4N6tEGRcmhTL400ZvK9nr2QjRWUdCeMwCnEbQVhq2IUkhiF7tYYHSGECEL2u88WDJqWYxh7tGHIjnpzld0VLZxPtB+L4s9WREifDdfLXKa+Ff/ZvfA7Knn8iLtnyOVS78sG8BcEIcAV1Z849uP2VbvllcUaEa0VpCSkge5RosjcSxQqvnhKXOz93Xr3NSilIooQwlaVVJ/cFrVghccvx7BE2Ul3hDbOi6+nqNRJs8PXfjj1+WVEJCaENLq4X2tT3s2WbjANbiThcaLGGOvhUrV1mdbi5LdsXrZtBBU7+fZHFF3iO6F6AJ9DEAK0ZDfXrv1hxa6Go+UyhRCqrRT1L5L2Fjj5sBDV3clRcZL6lUf+Pn/wwyO6DGn+VY0bV2jHzRIpPZ9GwbBE0RkdjUqaYlivJsHJ0/uJDRHWfVtVQ28hhOgl4snR+luiJ394+NMj1dvn9n3+qLHXj6fP/2rjZMRcJjmZI5R9tv2VNIdzWkrWsDEzKRF+sUAwwb9XgAucrOvbX1ZsKj5eSofWUgmF+sd0gnGo0jaLycyspMbMTZIZJMercxbuXrJg6BMDoy85t89RkOOqKAi97wVvCuisO0JFm2OE7aSf+kDtspeV/cdQ0guHHVKE+nO/P3564qsVBxctHbv4iW4D6vMtW749n98jcqfzzH5XxK8K187KHN3K57tT1ll977i553hviwDwCQQh3OhsrGP5we++L8gvJJEldLpCnhgtNA4Tjr0tPzxswAuFXzsVEbLUR2NEEtGB8iOv7l26cPgz/aJ6t3gS46ZPdZPmUbRXq0psXKd1jXofhJLYZFnXnuZd67Tj72refn+vu+W07MntLywduzg8JWzmI6mnPzt/d8RQ/bDiLbve3aocc9Q1+IAo7JuTli7HliaIG2dlDJh10xTqhl99CIEMQQg3qNy68veP/u/XGlMRleQiUYm8I9nu7BXvus3430h7Yc/hLyqFoWeXlcTcHBo3JpwQsrfs4D/2vf/GqBd7hKW3eCrbib2C3arsP8bLkphOmiwjFRGBEJYntHcnKWmn3Fe99P/UQyaJ1Prm7Xd3n66g5Y//9PzSsYtj9FG9Hu+a/01ZxdrIu/7w1ejy7079trgseuL31DiuUpErD7nvrP7Pp39Mos6PiQ5/qt+YqEufCiAQIAjhBlJts3+Te2xtYfFJu4ahlLGsPYOp+XN9/VZdsqRH0jTjesX5jzIGPxWfPrtsR/3Z30rS5nTRpagIITuKd793aNkbo17KCL1sLTnPm77/XDf1IUJ5m2GdtXyCuCeOckTrXRDSoVHKPiNN27/R3/5Qi0/d0W2ymBI//tPzb495JVHXpdtdcaX/q81+/3zy9Nsm3ndH7qF/Rx/7S2PaA9+Jeow5VRptP7ctRPLfMs0/K3OiSNUAHZmT2iMrMVXe2mJKAN9DEMJ1zsxy20rOf3sub3cDX8erYrhzEVzpLKbiD0XyqujBL0SEHu0b+4hlh/XkK8k33Z+WtdeY6zr6jwJ1rLz3E8kyvYQQ8lPRzo+OfrF07Ktd9QmXP791/1aRWifP6O99qTaOKDtpVZ57Tb3W6/1/tFlzKt94UD38VjosusWnbkvNUkoU83e8/I/RC5P1iXFjwjQJinNrK2S/0V3veDwpc87pfW8b8v7M9X5sFTVOdab826K6SnbVf2Lkua7YxxtcNYdqu0rqJ0bqpqd0HxwRSnv9ZwSAxxCEcB1iBeFovWVj0bnNZTWn7OpwriyUL85wlY+w10wt04aTHtvSBt4+0DktIeYD576Ggy/oU24ZNu8XwaY9+3mFrcaRPC3akHFhD7ONeT+uyvn2n+Nei9PEXP5Cgstp2vpV6B+8miPTpLMmy5CLE0e9TReRWqcecZvpx5Uhc569/LPjEkeKKfH8n19y3yvrklV9nk6u2FN/4r2C8L763rcsSenz4Ol9bz1Q8yXd/7l/2fseK476R72o99HjZyP+t1XXeFjZZRcT8W1xVY04toeCuSU27PauqX30KhEyEXwLQQjXCVYQDtYz35eUbCurPmGVRnFFsVyBgdTe5azqY+cnGkNDKhLq0sZ8epPsS4nzz8ldtonyz//2Jzqy96jZm5WqhNIdteV7zsUMD03/Q7zo94G6dblbvj69/t1xr8aoo1p9UfNPq6WJ6dLElqOGnunkrlGv58u4aUZPr3ztfmfhKWlS98s/OzphuJyWP7/z1VdvXtAzPJ0SUTE3h0b005/fVn1kSV7CLZGDbllWXbI7e/fi+0S0YsALHzXI50WS56hhj/5/e3ceH2V1/wv8e5599pnMlmSSyUZYExAILrjgT0FaLe6/viySVqne+vvV1vtqbW1tve6vvn6t9t5bt0trodYdtfWqrfRK64IIZQ2BEAhk3yYzmX159nPuH9FIlc0SmELO+6+ZJ08m35nJM5855znPOf15YVvrQEXPO7bNOxz+Pt23LutddWBIZZ0LXfiKqurLygNTHdLEPAeKOio66Tb1xfxLTbpdMPHmeP5vQ9G/RaK7ckyJGanV2wSI8ZxeLcsXyulzlEp7TymEp6+rcd3DFyod9ltqqi8mPQc3PsByUuOF/8MXOjvRlu3647C1VKy7rlz0fPq8Xtj72tud63956YN+q+/zf5oYeuq1J9WuNv9tD7GeiVm69v4dmAC5b94xukeP5xBY8Lrx1AVsk29i2lZK298TL/7SfeUt1rMPf0XEzpHd93/4i3vO//6h42lz/XLna0MMz9ReW2YrEwc63trz4UM2V7X/3J+uTfNrenrDiPuxIpzbMSpHWpNVkb9yka0O/wAvCNiaQOWd/GxgLed5uCWh0KKgu9FlYf81uk/ppNvFRVefoEFYfEUPwqxh/j2ef28k/t7QyPYsCuBIld5WwiTSJG0TvLMy0UVqfAaaZuvwic7qfbNK/1cJeiefua6i/LaqUtfQ+u7dz2JTbTj/7rK6pXJM63p9WIlrddeWuad++tE2Kid+0/L7jkTnLy990CMdZpSjmRqNr3mIdXo8N97JSLaJemo/2mp6BHTXnGMMITmeQ2DRW8aDTexFpROWHEZ0IL76Qb5yiuerdyD+MBOqtYzsvu/Dn39t5nXXTr2CZz/59yAQ3Z7qeSvim+MKfzmAOKOz5emObU96y8+ualjRZm14tm/greHhf7e6v5PAFTt6ZL4/UzqwGfd+5J3WxTGCnrKBPUKC/dLsGArMc7L/Vh680O+8wGcv4kAbGoTFRYOQBmHxFSUIB2RtQyz3QTT1biQxoJi1pLdC2Rlkk3GcS/HBcsZ+Vmz3IgaVsvW2nqCo+7NN1c+GpP+diDa6XM1VlUuE5HDbcwMdb3iCZ9U0rgjVX050NPDu6PBHidAib2iRD7EfB0ZeL7y49w9vHFj3lSmX3Tjreht/mINNPdiaePa/7Bdd5bjk3098pOihvrvJrHei78yagCD80jrjvzewX6qYyPKIKide+p9GbMh7808572H6ivsyg79ueeZAouuWs1ZcWrWI+eTFMQpm31+io62Z8GX+0nNLTFMd7vp/3bufyyY6wjOuL5nZ/FYKP3GwSzPNH/Gur/RmmR1dSmUq7+/vKezbIfl2eWf06mmPkbIje8T09UlzhiEw24EWh8ov9DvP89qc/Cmd+JsGYXHRIKRBWHynJggTmrE1UdiayG8eTW5N5HXTqMMHK+UdZXwmCmYnWxYSSuZmus9Nttfa62xqlbXby5k2Zk7VpiklvzCzezOZ5qrwN6vKhIG/drY8reSj4RnX18252eIoB4BEW7bzD0OuOlvNslLe8fFJOQObb3etX9P6QqN/5n/Mu7nUdrjeTkKyf3sl9/4fS1bcJU49a8Kf9S0bzHMD6JZpExCE1643m6ega6onvtmU/+jPmXXPepbfKU2ff9gd9o52/J+dazJq9r/N/cbC0ILx7bkBufMPwwSTumvLHWELAGTiHX3tr/TsecHlb6hpXBHxLvxNT9+rA0OLPJ47Ctz8A3Fj74BZiuXKVJ7rbk/s21Eys9VZOaREppOUhPk+XDpgXdBNysol5lyf+xyfs8ljO8ttOdmNRRqExUWDkAZh8Z2kIMwZuCVV2J4sbB3NbImnhhQcZkZLlb1lRqdP1NII7YSScltlkzwye2DTNMnuEGfYM2G+nWWCHqWh4sOQ7SVQ18eiF/l8t9RWn8+nB/e9Ov4JG6q/HCFWTerRbanothQrMXXXlDmqPz2QPhrc8sT21UGb/z/nrZziqTlshVgpJF941MymvDfdzbq8E/v0x9z4rnlFGC2vm4AgXPGe+eUKdOOUkxIJateexDM/s533ZefSG4/UJt4e2fXY9qdLJPd/zLu53lP78VYCI1uSvX8esQTFQJPbN8fFioxp/EMD0Ter+Y8J87fdPTFV+0pp8DoszO/LoNY+M5HQpqgF53BS27/dMHYF5+7knUah/2w2JRrmsG6JSo3DwrRus6TSwi7wepq8tvke64ISmzjRg1BpEBYXDUIahMU3UUE4qhp70nJbRtmWyG2OpftkvYZLVRidvtzWkJDTeXGnaU/zpWFLyUw5Vj+6e5aSKPHOtak1ln43xHW2ofJAre91N/N6Mj6sKEuCgS+XBhf7PObge+MfqbVzbrI6QljHib3ZyKZkbkD2zXEFmtzOauv4ZQV7R/c/uWONhrXbzrppXunsI1Wrj/TFVz8k1jW4r/tPxJ6ssdbXvGN+Yyq6umoCgvDWDebZfnTr9JPVNjLT8fjvHmasjpIbf3CkecZNYv65c/3vWl9s8M+4be5NZfbg2HaCSfpgPrIpmdyf9UxzBJrcnhl2xKDPNBAzwQvfHon9JRLdHE8sKPFc43BfntCD+0f0tn7TT+SKWI7tHErsbffN6vBM2cO7+vIj9ZBsYPK6avbr9qh1bh9bF8O2Rid/XsA7122d5bLMcEqHXRL5C6FBWFw0CGkQFt8/F4QJzWhLK3sz8p60sjuV2ZOWdYyr2GTA7Pfnd1SxCUHk9hLXbuIpsZZONwv1mZ6ZI7tqHV6rdarVLBNHPexBgw37ctPLN5RaX0XK+mi01mZbHAwsDvjmMcnEwIex/o2jg5t8ofNqZ389ULUIAZPpKUS3pUZ3pZ3V1sACt7fBOX4uEAD6MgO/3fV8e7yjueGrV9Rdxhz5bF9h+7upPzzluupW2xHGTE6UpW8b32tklx7rxN7xHAJ3bDLrnOi7xzrdeEKwmf7TM3LrRu/NP+XLD9+MBgDFUP7Q8aeX9v5xcfWilbOX24VPxxYZsjnako5uSylxzTfHFTjbYw9Jhpbr3/969+5nlcJosOpif+X5jvLztsvs+mjszaHhuKZdWlJyo8qfN5Rn9/RjWdammqojLqOBXHJfxNA7yptaHVUHCJvK9zWiRA3JZTQ0yFSPWmZHmPI+w14mMg0ue6PHNstpmemUZjilL9qVSoOwuGgQ0iAsvmMGIQEYkvWunLovq7Sl5d2p7J5UoWDiMJsqwwMuub3U7PeKWOfELuRqMyycpWIGQ2blB+siu+pBc7imWqDSkvby/SLkTLYmoIe9BwK2/+tAf0zEk7r2pdLgl0qDC0XZiGyO9X8YG/iIFxz+yoX+ygsClReIVl9hRB1tSY9sSbICE1jgDp7t4e3/0IZLqenf7177t94Pvjrj6uunLRPYIy8qO/5Zv/Ievqx64l7Fw7voLePhJvbCYw31PJ5D4MdbTZeAfnSsAagnrrDjvdRrT7quvMV2zmVH2S2tZp7Z/fKRXnM5qsZ2pqPbUgyHAgvcwQUe3sFlEwej/RtifR/GBjaJVq+/8vxA5QUpz9y/pZS3h0c2xuNzXK6vSs5LU0ZoJAvdMTyUwGWCVpZVrNG80RNPHjzorOrwztgj+Tu1vFOPzmLyFSRt6DiiCgmpLi5OG0LlQ6atXESzSxyznPYGl1TvkGptokc42ugbGoTFdSYHoa7rR29k0CD8F3FoEKZ0szundue1rrzanVcPZnJdWblPIU7GCLLZgDHgVvYFzT6fQDAvdCNnmy7pYsDHibVGvjofqUh11SujPneNVaiRcn5h0MaOmCjkzVV5u/yWLQ7uXdZoSWfyhjHX414c8F/i4kO5tljfByO972FT84XOCYQvClYtsjoriUnyETV9MBfbltILZmC+O9DktgTE8bIJkN70wK7onl3Rtm3DLV+Zctnymdcd2jr5vE96/5wlK+5kLKfig6/pdWPVBez8Y138dzyHwAM7sYnJ/fNPxXBKY6R/dPWDx9Nv3JcZWLXzmQPJroWhs+cEZs0JzCqxeD79MYHUwXx0WzKxJ+ussfrnuhzVVqlEAAT5dG+0b0O074No3we86A6EL3RVXLjPOmt9ovDB6Gh7Jltltc5zOi/C3PyMHo4WLAMJsz+m+1XVn5Vt0YLeHcsMjbjC/e7aXmugk7VFtTSjjExnCmHIYANiujgqTktI9VEIREwbg5gqK1vnsE5x2GpsYo1NqLGLNTZx7HQjDcLiOjOD8K233vrWt76VzWanTZv2wgsv1NfXH3Y3GoTFEteMiKxHFGNY0UcUvTcr9+Xz3XmlR8GGCWVcpgTHXfqAU+0uQSkna4ismWKkASJ1E7soBMtYrkZNVmT7KlM9tSznEEsFFBB1D5+1cXGJS7MQKkmH3F1+2yYHu47Rd6RTTo6f73HPczvrOCVsRsuV3my8IzbwkaEXApXn+ysv8FcutFirCkNKbkDJDcr5QaUwokpewVlt8c9zu+psY6cAMSFdqZ6x8Ns10mblLXOCDWcFGs4um/sPn7+fgbE+0qd1tWX+8rz9oqscl351Yq+ROIqZrxqvLmZnuicgCH/RiqMy+cU5p+i6Aqzkk88/YmZTjn+7VqiawboPMwXBuK5U7/bIrpaR3a2xvW7ROTswa06g4axgQ+CTiQtMDcd3Z+KtmdyAbMrYViHZQ5ItZLFXWCQfkxrdHevfGO3fmBje7vBM8YYWWD1TImJlL+M7qLHbk+ntyZRsmvNs9itUdm5Gr4nl3YNpHM/ggKl7ZM2W0biEakZH5ZFuTux3T+m3l/XwjpiW47RYHauUgWzBmmxyKYNPssG0WJ3mykdJSRRbPDxUW7hqq1TndgYlvtzCl0p8qcSXW/gTP/VIHaczMAgzmUxlZeUrr7yyZMmSe+65Z+PGje++++5h96RBeDKkdTOpmUnNSOpmUjPimjmYl4fyuf68ElX1YY0kdEZEphvlHSRtN+M2Y9RpDDmRYmdUFmkZBL3YkmddPOewMxafqfrVlC8f8xci5QiFkc3C+ATNw+fsfFzkTI/mcWU8lqhT6rNxByWmVYAWZER1rcHlmGlhp7L5GnMkVDhI0gdyqW4lP2Kxh+yeGrun1uGpL/E1MWpVrl/ODci5flmJa5JXsFda7BUWe6XFHpIYgQEATHBvZmBPrH3bcMuOkVY7b2vwz5gdmLmgbO7hr4gAAACcS2m9+7Se/WpPu97fwThLhKrptnOWilMaT+G7ATUvG+9ezlY7JiAIn9iL21Pk8YWn8AI7QvKb3lb2btF69wPDCFXTheoZQvV0oaIeCeKRfmkoF9ke2dUa3dsystskZqN/ZlPZWQ3+GTWu8NgOhmIWhtXDvu/WEKdxe1OxnblUVy7ZnUt1aUrS7q61uatl59R+saoT+fcb4p6c2p0vTJUscw1mpkrqZVye1X0ZxZEqMMm47izo7rxmzWpcMkNGe0w5wkhxe2nM6h0RXGnEFHBB19MBkgshWSKsRoSMKWSQJ8cF8lwgx3gyyJHEVgZBkCd+EZVbhHKrtcJuL7eIHoH18JxHYD0C5xFYGpYT4gwMwjVr1jz11FNbtmwBgFQqFQgEOjo6qqurP78nDcKj0DDJGzhjmLKJ8wbO6KZs4pyuJ1S5oGtZXU+pakLVPw48naR1yGA2h3kBDCuSLaRgJTkLztrMhAMnrCBLRGaQDqDIyEyBAIyFQQKDJN4ECYNXTfuV0VKMyzGqMgSbKbKqjVEEviBymhXEEk1y5yQpZuN6rdx+C9rJkV0CRla+nCMBDgcZxU8KJTjtM+JudcSRaVeyA5ItaHNUWyzVkhgW2DCHKxg9aORAzxp6zlDTulEwbSGLPSTZKiR7yIK8JKElk0o6paQTSjKppFJKZjA73Da6L2jznxVsHOt5O+y8MAAA2NSjA1pXm9rdpvcfMFOjXFmNWDtLrJ0pVM9kbM5T+wZ+LPi8vvtaPmA5xm7Hcwis6cAbImT1Raf0SvNxZjquDxxQu/aqXW36UBfn8fOV9WLNLKF2Fh8MH6mF3Zvu3xVt2xVta4nuQYAa/NN9lhKP5C6xeNyiq0Ryl1jcDnBoESM3qOQHlNygLMc0qYTn7Rzv4AQHx1g0UxjSyYBm9ClaXz7fnU93m7oseKYk7VOTvC/BeWLIGUPWmCkMGcyIRtwFY4HBNGioXsGVOcNT0KyqzCoJ0NK6TTUtiiHJhJOjvDrEmkPIGOZtMcmbFSQFMToyMGgEaxzOuQjmGYEhvA4WGaQ8sWb4gMw4ZeSQka0A1gKSgICDMZwcdnFQIjAejvWIvFcSrBznFSULxzlFm1tgLSxjYRk3/8kNgf2XmFnuX8YZGIQ/+clPhoaG1qxZM3a3urr66aefXrx48ef3PHjw4NKlS7dt2zbeQe9wODjuiCck3t/1UTKbP/pf1wion99KCDaVI/0KAZTFSAF8hB9j2dCNsY45YgI2xzbnyFid2MCaQQADKJ9Md26YuopZADCAaMDqhAAxAEAD3iCgE6IiHgAU4EwCOrA6sAQhhYgAREMiBqQiCwIsEEUkCkd0kcgSFASiCaAIWGHA4InOgcaAyoBGwGSJisBgwRCIDgQAMzwmLAEWQMTIYYDLJG7d9GpQoiGfCl6VJUTCIGlINJGkMlKWtcU4e1TgIwI7ILB5AeUFovKmIhqE1a1EtWHZRhSXnvAYUY827MMZL0ECY0PIxTJOhJ0McrKmi2AbYzoYpZwplCKZB55gC8YSxpKpWVQsYlVUNFEzRCPLp0e5eEpPp7VMUkmmtQyDGI/gcgsOp+BwCy634HTytoBQUi+VOTACQwelQHQFDJ3IOWLooKugFIihE61AUqM40s34Qmx4BhOexlZOR/6KU9b/eRThl/Sh5bzjWANyjycIX+7CL3eR315YnCA8FDF0MtRp9u3D/ftw3z6i5JmKqWCxIcmOOB54EUk24HgkSCBYEMcjiw14IWJkD2qxpJZOa5mUlknr2ZSWSamZtJYVWd4tul2iyy24PILHI3tshp2TWUm18BrHyhyvCUhmWJkFhQEAsOZN6yDmRoHPEMiZbJqQDIYMJlkMaRnLUcQlWU9CKE/w/hzryiNBZmwFxAu6yGmMRWdtGngVUqYbAR0HNdWrp21YEYnMEo0HBRHFYLWYaMZFkhRQkockz6RYpsBhlQEDMQDYZIAANhnGBF5HvIl4AB4RFiPRAAEDryHJQLwJrIwcGhJ0EGRk1xneAF5GNg4MnmgcGBwxGCAiaAAgIZ0FwoPJA0FALERjGE4EkwUTAAHiJIR5IAwgALByGCEOABAhFmQCgIAYhhEAgAMioY8/qeCTQUwSA/wRBpEhABtCVnS04CAIsewRJ0y3I4JOYF0UAmRmRc206sOfRPvnFHn1iVQqZbN9OmDB4XAkk8nD7pnL5Xp7e2trawkhCCEAuOuuu26//fbD7rztQOuVHQUDjjwaEAAAOGLwRPv8dnLkX0QIi0Q++mOyYH5mo0RSAEAACDAAhAEifPIgBIAnKgAwABxoDMEEEABwRGfBFIB4sAqAWGJwxOSJiYAgQixEAcLwRGcI2E0dEcRiliUsEGTBDMG8xWQYAgixAjYRgN1kEQGEOavJ6sipM8gEIc8yJghAcIFjMXAqw+oITM7IAM5xWodIEGDMmAaj8WCyBKwYAxRsZorDyIqRBZMZJp4vAyoIABwQ0UTIBBYIByAQcGjgBjJLRayB2D5WJgRUTiYEVFYmiGhsgSU5E/Wr9u2KKwcoZyJTxIjFwABIBYACiBixBBggFhMFDVRvgN1AvClZDAePAcAASAJ8+g+jM9wBRsozksbwOdaqMJKG+Axr0xleYSxZtkRHfIGxjkquvTOnyIwEGsBBgIMAoB/9X+XUKBEQVnK5w3w7+wf5fB4dK7Z9DPPeEFf78mf/FYukFqAWmMuhGrx6amah05aRHcmcQAzGFY1IAAAJUklEQVQJq3YjKxDdglUblnms2828hHWRaAEzF/zshy0CcCos0biUwsQyPGQ4yLIkygIAaCwxAAChAkvADpoTDIawRGCIw2LYeCwiXSSABMXLQEDAAkNY3hRYYHkslGIzBDqHFA40AAJoFABzoDMEAZMHAgajyCyr8KTXzrVx2EBIZThMUJ4TAUQdCZzJspgDgnjMsLrgUsFOeItJBGIwgO2maQKSiCJiYEHnCGGJqbAmRnmMMnmWAIDKICBEY4nOYII0hQUGTJUlCBkqsCqLDMSaDGMipCIRgJiINYDVEU8QEPTxF2IV8RhYAGAAp5FgAjv2EmrIwgAGAAKMij6OKAYKAGACq6PxvuuP2wA6CBgd8aAgwKhwjI4LBEdc/URmbAAn1ACbc/CNd67/1nHuLEnSUZpMY4ochD6fr7u7e/xuMpkMBA5/Isdut9fU1Bxn1+jFcxdm5k5kndS4ok+6faY79gtLCDnmqMVL7ZConpiCJloAYGIW6ygWOmq0uDDGirJgYrtGi3zydsaMGTt37hy7PTIyEo1Gp06dWtySKIqiqEmlyEF49dVXR6PRVatWxWKxu++++4orrigrKytuSRRFUdSkUuQglCTpT3/603PPPTd//vxcLvfrX/+6uPVQFEVRk03xr2tpamrasGFDX1/fyy+/7Pf7j7n/I488gvERBm1SJ9/69eu3bt1a7Comr1wu9+STTxa7ikntueeeGxoaKnYVk9eBAwdeffXViX3M4gfhF/XII4/I8tHGbVIn1fr16z/88MNiVzF5DQ8P/+Y3vyl2FZPayy+/3N7eXuwqJq+WlpY33nhjYh/z9AtCiqIoippANAgpiqKoSY0GIUVRFDWpFX/1iePU3t4+d+7cUCjU29sbDoePObMGdZIkEgmWZV0uV7ELmaQMw4hEIhUVFcUuZPKKRCJut1uSjjiFGHVSFQqFXC53pKlXPm/58uUPPvjg0fc5bYIQALq6ugBAVVVRPOJk9tTJZhgGQohliz+J5aRFD4Hioq9/cRFCdF0XhGPMoDmurKzMYjnWhHCnURBSFEVR1ISj5wgpiqKoSY0GIUVRFDWp0SCkKIqiJjUahBRFUdSkVuT1CL8QjPHvf//71tbWqVOnfvOb36RL4p0Csiy3tLS0t7dXVFRcdtll49s1TVu9enVHR8ecOXOam5sZhn6jOil0XX/vvfc2btwoy/LChQuvvPLK8QuHurq6nnnmGUVRbrjhhrlz6fKbJ8uOHTveeeedkZERn8/3ta99raamZmy7ruurV6/ev3//7Nmzm5ub6Tjqk214ePjNN9+8+OKLx5fq6+joePbZZw3DWL58eWNj44k8+On0+fXd73738ccfr6+vf/HFF1esWFHsciaFe++9d+XKlY8++uhTTz116PYVK1a8+OKLU6dOfeyxx+64445ilXfGW7du3Q9/+EOMsd/v/973vvftb397bPvAwMCCBQtkWfZ6vRdffPHmzZuLW+cZ7M0330ylUrW1tYODg42Nja2trWPbv/71rz///PP19fVPPPHEd77zneIWORnceuutd95556ZNm8budnZ2nnPOOWOLVF9wwQUtLS0n9OjkNDEyMiKKYk9PDyEkmUxaLJb9+/cXu6gzn2mahJCf/exnV1999fjG/fv3WyyWZDJJCOnt7ZUkKRqNFq3EM5osy+O3//73vwuCoKoqIeTuu+++4YYbxrY/8MAD1157bXHqm2Suuuqq+++/nxBy4MABSZISiQQhpL+/XxTFSCRS7OrOZM8888xNN920YMGC3/3ud2Nb7rjjjltuuWXs9l133dXc3Hwij3/atAg3b95cXV1dVVUFAG63e2zxpmIXdeY7bJ/nBx980NTU5Ha7ASAcDtfU1NAWyUly6PQliqKIoshxHAC8//774z3VS5Ysef/994tT32QSj8fb29vHuuA2bNgwb948j8cDABUVFXV1deMtFWrCRSKRhx9++Oc///mhG99///0lS5aM3T7xQ+C0CcJIJHLoaoXBYJAuCVYsn3kvAoEAfS9ONlVVv//97//gBz8Y+2oyPDw8/hYEAoFEIqGqalELPJOtXr06HA6XlZUtW7bsmmuuAfpxdGrdfvvt999//2dWq/3MITDWIv+n/8RpE4Qcx5mmOX73C02xQ00s+l6cYqZpfuMb3wiFQj/+8Y/HtvA8bxjG2G3DMBiGoYM1Tp7ly5dv37593bp1r7322nPPPQf0EDiF1q5dWygUbrjhhs9s5zju0EOA47gTmYD6tAnC8vLywcHB8buDg4Pl5eVFrGcyC4VC9L04ZUzTbG5uzmaza9euHesXBYBQKDTeBBkcHAwGg+M/oiacJEl+v/+SSy657bbbXnrpJaCHwCn0wgsv7N+/v6mpqampae/evffdd99DDz0EnzsEQqHQifyV0yYIL7rookwms2XLFgDo7Oxsa2tbunRpsYuapJYuXdrW1tbZ2QkAW7duTafTF154YbGLOjNhjG+++ebR0dHXXnvt0DbHsmXLXn311bG+oFdeeWXZsmXFq/EMVygUxm9v27YtHA4DwJIlS/bt23fgwAEA2L59eyKRWLRoUdFKPKM9+uija9euXbVq1apVq6qqqm666abm5mYAWLZs2SuvvDK2zwQcAic2lueUeuyxx0pLS1euXBkOh++7775ilzMpvPHGG/Pnzw+FQm63e/78+Q8//PDY9nvvvTccDq9cubK0tPTxxx8vbpFnsLVr1wLAzJkz539icHCQEJJOpxsbGy+55JLrr7++vLy8s7Oz2JWescLh8OWXX97c3NzY2Dht2rSx158Q8sADD1RWVq5cubKsrOxXv/pVcYucJA4dNTo6Ojpt2rSlS5deffXV4XC4v7//RB75NFt9oq2trbW1ddq0afPmzSt2LZNCPB7v6ekZv+v3+8e+EQPAjh07xq4mnjVrVnGKmwQSiUR3d/ehWxoaGsbWAFIU5a9//auiKIsXL6bLQ548o6OjW7ZsSafT4XD43HPPPfRc7Ngh0NjY2NDQUMQKJ499+/b5/X6v1zt2t1AorF+/3jTNxYsXOxyOE3nk0ywIKYqiKGpinTbnCCmKoijqZKBBSFEURU1qNAgpiqKoSY0GIUVRFDWp0SCkKIqiJjUahBRFUdSkRoOQoiiKmtRoEFIURVGTGg1CiqIoalKjQUhRFEVNajQIKYqiqEnt/wN36HuiMla3EAAAAABJRU5ErkJggg==", + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/html": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "plot()\n", + "for (i,c) in enumerate(sols)\n", + " plot!(c, label=i)\n", + "end\n", + "plot!()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "05e1a1e0-cd8d-4408-be3c-bb2edde853d8", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c8fb83e6-8dab-41e9-9098-3c28a37e3f25", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c4daa47-bf5a-4258-a344-11184f1d54e6", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "55c9e588-9210-4eae-87a7-73c14460203a", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1+1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6282c3dc-56e8-40f2-b72d-60d64e6b3ca0", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Julia 1.9.4", + "language": "julia", + "name": "julia-1.9.4" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.9.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/parts/distributed/explanation/02_dagger.slides.html b/parts/distributed/explanation/02_dagger.slides.html new file mode 100644 index 0000000..a7259b4 --- /dev/null +++ b/parts/distributed/explanation/02_dagger.slides.html @@ -0,0 +1,8372 @@ + + + + + + + +02_dagger slides + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/parts/distributed/explanation/Project.toml b/parts/distributed/explanation/Project.toml new file mode 100644 index 0000000..579f82c --- /dev/null +++ b/parts/distributed/explanation/Project.toml @@ -0,0 +1,6 @@ +[deps] +ClusterManagers = "34f1f09b-3a8b-5176-ab39-66d58a4d544e" +Dagger = "d58978e5-989f-55fb-8d15-ea34adc7bf54" +DistributedArrays = "aaf54ef3-cdf8-58ed-94cc-d582ad619b94" +NetworkInterfaceControllers = "6f74fd91-2978-43ad-8164-3af8c0ec0142" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" diff --git a/parts/mpi/README.md b/parts/mpi/README.md new file mode 100644 index 0000000..760b29b --- /dev/null +++ b/parts/mpi/README.md @@ -0,0 +1,55 @@ +# Diffusion 2D - MPI + +In this part, we want to use MPI (distributed parallelism) to parallelize our Diffusion 2D example. + +The starting point is (once again) the serial loop version [`diffusion_2d_loop.jl`](./../diffusion_2d/diffusion_2d_loop.jl). The file [`diffusion_2d_mpi.jl`](./diffusion_2d_mpi.jl) in this folder is a modified copy of this variant. While the computational kernel `diffusion_step!` is essentially untouched, we included MPI bits at the beginning of the `run_diffusion` function and introduced the key function `update_halo!`, which is supposed to take care of data exchange between MPI ranks. However, as of now, the function isn't communicating anything and it will be (one of) your tasks to fix that 😉. + + +## Task 1 - Running the MPI code + +Although incomplete from a semantic point of view, the code in `diffusion_2d_mpi.jl` is perfectly runnable as is. It won't compute the right thing, but it runs 😉. So **let's run it**. But how? + +First thing to realize is that, on Perlmutter, **you can't run MPI on a login node**. You have two options to work on a compute node: + +1) **Interactive session**: You can try to get an interactive session on a compute node by running `sh get_compute_node_interactive.sh`. But unfortunately, we don't have a node for everyone, so you might not get one (Sorry!). **If you can get one**, you can use `mpiexecjl --project -n 4 julia diffusion_2d_mpi.jl` to run the code. Alternatively, you can run `sh job_mpi_singlenode.sh`. + +2) **Compute job**: You can always submit a job that runs the code: `sbatch job_mpi_singlenode.sh`. The output will land in `slurm_mpi_singlenode.out`. Check out the [Perlmutter cheetsheet](../../help/perlmutter_cheatsheet.md) to learn more about jobs. + +Irrespective of which option you choose, **go ahead an run the code** (with 4 MPI ranks). + +To see that the code is currently not working properly (in the sense of computing the right thing), run `julia --project visualize_mpi.jl` to combine the results of different MPI ranks (`*.jld2` files) into a visualization (`visualization.png`). Inspect the visualization and notice the undesired dark lines. + +## Task 2 - Halo exchange + +Take a look at the general MPI setup (the beginning of `run_diffusion`) and the `update_halo!` function (the bits that are already there) and try to understand it. + +Afterwards, implement the necessary MPI communication. To that end, find the "TODO" block in `update_halo!` and follow the instructions. Note that we want to use **non-blocking** communication, i.e. you should use the functions `MPI.Irecv` and `MPI.Isend`. + +Check that your code is working by comparing the `visualization.png` that you get to this (basic "eye test"): + + + +## Task 3 - Benchmark + +### Part A + +Our goal is to perform a rough and basic scaling analysis with 4, 8, and 16 MPI ranks distributed across multiple nodes. Specifically, we want to run 4 MPI ranks on a node and increase the number of nodes to get up to 16 ranks in total. + +The file `job_mpi_multinode.sh` is a job script that currently requests a single node (see the line `#SBATCH --nodes=1`) that runs 4 MPI ranks (see the line `#SBATCH --ntasks-per-node=4`), and then runs our Julia MPI code with `do_save=false` for simplicity and `ns=6144`. + +Submit this file to SLURM via `sbatch job_mpi_multinode.sh`. Once the job has run, the output will land in `slurm_mpi_multinode.sh`. Write the output down somewhere (copy & paste), change the number of nodes to 2 (= 8 MPI ranks in total) and rerun the experiment. Repeat the same thing, this time requesting 3 nodes (= 12 MPI ranks in total) and then requesting 4 nodes (= 16 MPI ranks in total). + +### Part B + +Inspect the results that you've obtained and compare them. + +**Questions** +* What do you observe? +* Is this what you'd expected? + +Note that in setting up our MPI ranks, we split our global grid into local grids. In the process, the meaning of the input parameter `ns` changed compared to previous codes (serial & multithreading). It now determines the resolution of the **local grid** - that each MPI rank is holding - rather than the resolution of the global grid. Since we keep `ns` fixed (6144 in `job_mpi_multinode.sh`), we thus increase the problem size (the total grid resolution) when we increase the number of MPI ranks. This is known as a "weak scaling" analysis. + +**Question** + +* Given the comment above, what does "ideal parallel scaling" mean in the context of a "weak scaling" analysis? +* What do the observed results tell you? diff --git a/parts/mpi/diffusion_2d_mpi.jl b/parts/mpi/diffusion_2d_mpi.jl new file mode 100644 index 0000000..415343b --- /dev/null +++ b/parts/mpi/diffusion_2d_mpi.jl @@ -0,0 +1,124 @@ +# 2D linear diffusion solver - MPI +using Printf +using JLD2 +using MPI +include(joinpath(@__DIR__, "../shared.jl")) + +# convenience macros simply to avoid writing nested finite-difference expression +macro qx(ix, iy) esc(:(-D * (C[$ix+1, $iy] - C[$ix, $iy]) / dx)) end +macro qy(ix, iy) esc(:(-D * (C[$ix, $iy+1] - C[$ix, $iy]) / dy)) end + +function diffusion_step!(params, C2, C) + (; dx, dy, dt, D) = params + for iy in 1:size(C, 2)-2 + for ix in 1:size(C, 1)-2 + @inbounds C2[ix+1, iy+1] = C[ix+1, iy+1] - dt * ((@qx(ix+1, iy+1) - @qx(ix, iy+1)) / dx + + (@qy(ix+1, iy+1) - @qy(ix+1, iy)) / dy) + end + end + return nothing +end + +# MPI functions +@views function update_halo!(A, bufs, neighbors, comm) + # + # !!! TODO + # + # Complete the halo exchange implementation. Specifically, use non-blocking + # MPI communication (Irecv and Isend) at the positions marked by "TODO..." below. + # + # Help: + # left neighbor: neighbors.x[1] + # right neighbor: neighbors.x[2] + # up neighbor: neighbors.y[1] + # down neighbor: neighbors.y[2] + # + + # dim-1 (x) + (neighbors.x[1] != MPI.PROC_NULL) && copyto!(bufs.send_1_1, A[2 , :]) + (neighbors.x[2] != MPI.PROC_NULL) && copyto!(bufs.send_1_2, A[end-1, :]) + + reqs = MPI.MultiRequest(4) + (neighbors.x[1] != MPI.PROC_NULL) && # TODO... receive from left neighbor into bufs.recv_1_1 + (neighbors.x[2] != MPI.PROC_NULL) && # TODO... receive from right neighbor into bufs.recv_1_2 + + (neighbors.x[1] != MPI.PROC_NULL) && # TODO... send bufs.send_1_1 to left neighbor + (neighbors.x[2] != MPI.PROC_NULL) && # TODO... send bufs.send_1_2 to right neighbor + MPI.Waitall(reqs) # blocking + + (neighbors.x[1] != MPI.PROC_NULL) && copyto!(A[1 , :], bufs.recv_1_1) + (neighbors.x[2] != MPI.PROC_NULL) && copyto!(A[end, :], bufs.recv_1_2) + + # dim-2 (y) + (neighbors.y[1] != MPI.PROC_NULL) && copyto!(bufs.send_2_1, A[:, 2 ]) + (neighbors.y[2] != MPI.PROC_NULL) && copyto!(bufs.send_2_2, A[:, end-1]) + + reqs = MPI.MultiRequest(4) + (neighbors.y[1] != MPI.PROC_NULL) && # TODO... receive from up neighbor into bufs.recv_2_1 + (neighbors.y[2] != MPI.PROC_NULL) && # TODO... receive from down neighbor into bufs.recv_2_2 + + (neighbors.y[1] != MPI.PROC_NULL) && # TODO... send bufs.send_2_1 to up neighbor + (neighbors.y[2] != MPI.PROC_NULL) && # TODO... send bufs.send_2_2 to down neighbor + MPI.Waitall(reqs) # blocking + + (neighbors.y[1] != MPI.PROC_NULL) && copyto!(A[:, 1 ], bufs.recv_2_1) + (neighbors.y[2] != MPI.PROC_NULL) && copyto!(A[:, end], bufs.recv_2_2) + return nothing +end + +function init_bufs(A) + return (; send_1_1=zeros(size(A, 2)), send_1_2=zeros(size(A, 2)), + send_2_1=zeros(size(A, 1)), send_2_2=zeros(size(A, 1)), + recv_1_1=zeros(size(A, 2)), recv_1_2=zeros(size(A, 2)), + recv_2_1=zeros(size(A, 1)), recv_2_2=zeros(size(A, 1))) +end + +function run_diffusion(; ns=64, nt=100, do_save=false) + MPI.Init() + comm = MPI.COMM_WORLD + nprocs = MPI.Comm_size(comm) + dims = MPI.Dims_create(nprocs, (0, 0)) |> Tuple + comm_cart = MPI.Cart_create(comm, dims) + me = MPI.Comm_rank(comm_cart) + coords = MPI.Cart_coords(comm_cart) |> Tuple + neighbors = (; x=MPI.Cart_shift(comm_cart, 0, 1), y=MPI.Cart_shift(comm_cart, 1, 1)) + (me == 0) && println("nprocs = $(nprocs), dims = $dims") + + params = init_params_mpi(; dims, coords, ns, nt, do_save) + C, C2 = init_arrays_mpi(params) + bufs = init_bufs(C) + t_tic = 0.0 + # time loop + for it in 1:nt + # time after warmup (ignore first 10 iterations) + (it == 11) && (t_tic = Base.time()) + # diffusion + diffusion_step!(params, C2, C) + update_halo!(C2, bufs, neighbors, comm_cart) + C, C2 = C2, C # pointer swap + end + t_toc = (Base.time() - t_tic) + # "master" prints performance + (me == 0) && print_perf(params, t_toc) + # save to (maybe) visualize later + if do_save + jldsave(joinpath(@__DIR__, "out_$(me).jld2"); C = Array(C[2:end-1, 2:end-1]), lxy = (; lx=params.L, ly=params.L)) + end + MPI.Finalize() + return nothing +end + +# Running things... + +# enable save to disk by default +(!@isdefined do_save) && (do_save = true) +# enable execution by default +(!@isdefined do_run) && (do_run = true) + +if do_run + if !isempty(ARGS) + run_diffusion(; ns=parse(Int, ARGS[1]), do_save) + else + run_diffusion(; ns=256, do_save) + end +end diff --git a/parts/mpi/explanation/01_mpi+jupyter.ipynb b/parts/mpi/explanation/01_mpi+jupyter.ipynb new file mode 100644 index 0000000..6827654 --- /dev/null +++ b/parts/mpi/explanation/01_mpi+jupyter.ipynb @@ -0,0 +1,552 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "650f758f-84da-4dd3-9479-8dbc49ebc3d4", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "source": [ + "# Setup\n", + "\n", + "Note: you might need to run `Pkg.instantiate()` to ensure that the `Manifest.toml` is up to date. This only needs to be done once." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "89ab4e89-10ca-4ba8-a7bc-d33fcf3f2e60", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1m Activating\u001b[22m\u001b[39m project at `/global/u1/b/blaschke/juliacon24-hpcworkshop/parts/mpi/explanation`\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1mStatus\u001b[22m\u001b[39m `/global/u1/b/blaschke/juliacon24-hpcworkshop/parts/mpi/explanation/Project.toml`\n", + " \u001b[90m[1520ce14] \u001b[39mAbstractTrees v0.4.5\n", + " \u001b[90m[052768ef] \u001b[39mCUDA v5.4.2\n", + " \u001b[90m[adafc99b] \u001b[39mCpuId v0.3.1\n", + " \u001b[90m[0e44f5e4] \u001b[39mHwloc v3.0.1\n", + " \u001b[90m[da04e1cc] \u001b[39mMPI v0.20.20\n", + " \u001b[90m[e7922434] \u001b[39mMPIClusterManagers v0.2.4\n", + " \u001b[90m[6f74fd91] \u001b[39mNetworkInterfaceControllers v0.1.0\n" + ] + } + ], + "source": [ + "import Pkg;\n", + "Pkg.activate(@__DIR__)\n", + "Pkg.status()" + ] + }, + { + "cell_type": "markdown", + "id": "53799c57-9c82-4cb2-9a73-f858a8725071", + "metadata": { + "slideshow": { + "slide_type": "slide" + }, + "tags": [] + }, + "source": [ + "# Julia + Jupyter + MPI\n", + "\n", + "`MPI.jl` provides wrappers for the system MPI libraries. And the `MPIClusterManagers.jl` package lets you control MPI workflows within Julia" + ] + }, + { + "cell_type": "markdown", + "id": "89cfa159-4234-4961-b18e-6f7a4472bb04", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "## MPI.jl" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "6bcb1ba8-c4da-4311-a873-3354126c952d", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "using MPI" + ] + }, + { + "cell_type": "markdown", + "id": "1f4228e3-d910-451b-8523-7b60f342788d", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "`MPI.versioninfo()` tells you which MPI backend is being used by `MPI.jl`. On HPC systems, which rely on vendor-provided MPI implementations (e.g. on HPE Cray systems like Perlmutter), make sure that `MPI.jl` loads the \"right\" `libmpi.so`:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "eb4f99e3-63a2-43af-903d-36cfbe011415", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MPIPreferences:\n", + " binary: system\n", + " abi: MPICH\n", + " libmpi: libmpi_gnu_123.so\n", + " mpiexec: srun\n", + "\n", + "Package versions\n", + " MPI.jl: 0.20.20\n", + " MPIPreferences.jl: 0.1.11\n", + "\n", + "Library information:\n", + " libmpi: libmpi_gnu_123.so\n", + " libmpi dlpath: /opt/cray/pe/lib64/libmpi_gnu_123.so\n", + " MPI version: 3.1.0\n", + " Library version: \n", + " MPI VERSION : CRAY MPICH version 8.1.28.29 (ANL base 3.4a2)\n", + " MPI BUILD INFO : Wed Nov 15 20:57 2023 (git hash 1cde46f)\n", + " \n" + ] + } + ], + "source": [ + "MPI.versioninfo()" + ] + }, + { + "cell_type": "markdown", + "id": "0ebcbfaa-839b-4d40-a9ef-fc99cee61b04", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "## MPIClusterManagers.jl" + ] + }, + { + "cell_type": "markdown", + "id": "338abb9b-48de-4c85-9e82-bc08927ad43a", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "`MPIClusterManagers.jl` provide a way for Jupyter to connect to MPI processes." + ] + }, + { + "cell_type": "markdown", + "id": "8725708a-b5b5-4cac-8983-c95a0c4b7ab9", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "On Perlmutter, we have a choice among network interfaces:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "d2e41152-6380-4b21-8bbe-71257eb8aba7", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6-element Vector{NetworkInterfaceControllers.Interface}:\n", + " NetworkInterfaceControllers.Interface(\"nmn0\", :v4, ip\"10.100.108.57\")\n", + " NetworkInterfaceControllers.Interface(\"hsn0\", :v4, ip\"10.249.42.35\")\n", + " NetworkInterfaceControllers.Interface(\"hsn0:chn\", :v4, ip\"128.55.84.171\")\n", + " NetworkInterfaceControllers.Interface(\"hsn1\", :v4, ip\"10.249.42.19\")\n", + " NetworkInterfaceControllers.Interface(\"hsn2\", :v4, ip\"10.249.42.20\")\n", + " NetworkInterfaceControllers.Interface(\"hsn3\", :v4, ip\"10.249.42.36\")" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "using NetworkInterfaceControllers, Sockets\n", + "interfaces = NetworkInterfaceControllers.get_interface_data(IPv4)" + ] + }, + { + "cell_type": "markdown", + "id": "78c91aa1-41ce-450a-b646-d8574e8740f4", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "Buf we have to be careful about which network we connect to:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "a31df2d1-6a35-4420-9385-b60af0831074", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "filter (generic function with 11 methods)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import Base: filter, Fix1\n", + "filter(f::Function)::Function = Fix1(filter, f)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "26e0a840-7b61-4202-974c-1cda95820690", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "using Hwloc, AbstractTrees\n", + "\n", + "import AbstractTrees: PreOrderDFS\n", + "import Hwloc: hwloc_pci_class_string\n", + "\n", + "sys_devs = children(gettopology())\n", + "pci_devs = PreOrderDFS(sys_devs) |> collect |> filter(x->x.type==:PCI_Device)\n", + "net_devs = pci_devs |> filter(x->hwloc_pci_class_string(nodevalue(x).attr.class_id) == \"Ethernet\")\n", + "\n", + ";" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "848daddc-d8cb-4ad0-9a33-eed34197e3cb", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Device hsn0 is a Slingshot device\n", + "Device nmn0 is a Unknown device\n", + "Device hsn1 is a Slingshot device\n", + "Device hsn2 is a Slingshot device\n", + "Device hsn3 is a Slingshot device\n" + ] + } + ], + "source": [ + "# net_devs are populated using Hwloc, please take a look at the source notebook\n", + "# for further information\n", + "\n", + "for dev in net_devs\n", + " io = dev.io_children |> only\n", + " name = io.object.name\n", + " kind = io.object.subtype\n", + " kind = kind == \"\" ? \"Unknown\" : kind\n", + " println(\"Device $(name) is a $(kind) device\")\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "36cb812b-3779-48ae-a982-d3aa8599b39f", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "Therefore only the `hsn*` defivices are Slingshot devices." + ] + }, + { + "cell_type": "markdown", + "id": "f6d965b3-1002-41ec-a964-6e4f71faf95e", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "Let's now use this information to find a HSN device with which we manage our MPI cluster. Note: we'll take the one with `:chn` in the name (as it's the only one with a public IP):" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "af6bdb63-1f0e-4bf6-ad6a-144d365a7e97", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NetworkInterfaceControllers.Interface(\"hsn0:chn\", :v4, ip\"128.55.84.171\")" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hsn0_public = filter(\n", + " x->(x.name==\"hsn0:chn\" && x.version==:v4), interfaces\n", + ") |> only " + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "1a502b97-b4e1-44f9-a5e9-3bc09c0e8491", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"nid200344-hsn0\"" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "public_slingshot_name = getnameinfo(hsn0_public.ip)" + ] + }, + { + "cell_type": "markdown", + "id": "70db6ae1-a001-4606-9933-55f2ac158be2", + "metadata": { + "slideshow": { + "slide_type": "slide" + }, + "tags": [] + }, + "source": [ + "## MPI Worker Cluster\n", + "\n", + "We use `MPIClusterManagers.jl` to start a cluster of workers. Each worker uses MPI to communicate (`MPIWorkerManager` stars an `srun` session), and is controlled via the device at `public_slingshot_name` (previous section):" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "1c81c337-5e88-4688-bcf2-f48b6eeb98e8", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4-element Vector{Int64}:\n", + " 2\n", + " 3\n", + " 4\n", + " 5" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# to import MPIManager\n", + "using MPIClusterManagers\n", + "\n", + "# need to also import Distributed to use addprocs()\n", + "using Distributed\n", + "\n", + "# specify, number of mpi workers, launch cmd, etc.\n", + "manager=MPIWorkerManager(4)\n", + "\n", + "# start mpi workers and add them as julia workers too.\n", + "addprocs(\n", + " manager,\n", + " exeflags=`--project=$(Base.active_project())`,\n", + " master_tcp_interface=public_slingshot_name\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "343ca90a-f66e-43d6-a887-2b6956fae59e", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "Now we can use `@mpi_do` to issue instructions to all of our MPI workers:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "0f6bc5b9-2973-4dc5-8fdd-bfd483f01460", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " From worker 5:\tHello world, I am 3 of 4 on nid200349\n", + " From worker 4:\tHello world, I am 2 of 4 on nid200348\n", + " From worker 2:\tHello world, I am 0 of 4 on nid200344\n", + " From worker 3:\tHello world, I am 1 of 4 on nid200345\n" + ] + } + ], + "source": [ + "@mpi_do manager begin\n", + " using MPI: MPI, Comm, Win, free\n", + " comm = MPI.COMM_WORLD\n", + " rank = MPI.Comm_rank(comm)\n", + " size = MPI.Comm_size(comm)\n", + " name = gethostname()\n", + " println(\"Hello world, I am $(rank) of $(size) on $(name)\")\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "98174d30-5828-43f9-b63d-11d85a46185c", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "We started this in a 4-node job. Therefore each worker is on a different node." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "88e46e3b-f8d4-48d5-b8fc-2ae660f5a4a8", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Julia 1.9.4", + "language": "julia", + "name": "julia-1.9.4" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.9.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/parts/mpi/explanation/01_mpi+jupyter.slides.html b/parts/mpi/explanation/01_mpi+jupyter.slides.html new file mode 100644 index 0000000..be24e8a --- /dev/null +++ b/parts/mpi/explanation/01_mpi+jupyter.slides.html @@ -0,0 +1,7892 @@ + + + + + + + +01_mpi+jupyter slides + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/parts/mpi/explanation/02_comms.ipynb b/parts/mpi/explanation/02_comms.ipynb new file mode 100644 index 0000000..fa83988 --- /dev/null +++ b/parts/mpi/explanation/02_comms.ipynb @@ -0,0 +1,854 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4d3cf46f-8189-4609-b217-29948b377255", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "source": [ + "# Setup\n", + "\n", + "Note: you might need to run `Pkg.instantiate()` to ensure that the `Manifest.toml` is up to date. This only needs to be done once." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "89ab4e89-10ca-4ba8-a7bc-d33fcf3f2e60", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1m Activating\u001b[22m\u001b[39m project at `/global/u1/b/blaschke/juliacon24-hpcworkshop/parts/mpi/explanation`\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1mStatus\u001b[22m\u001b[39m `/global/u1/b/blaschke/juliacon24-hpcworkshop/parts/mpi/explanation/Project.toml`\n", + " \u001b[90m[1520ce14] \u001b[39mAbstractTrees v0.4.5\n", + " \u001b[90m[052768ef] \u001b[39mCUDA v5.4.2\n", + " \u001b[90m[adafc99b] \u001b[39mCpuId v0.3.1\n", + " \u001b[90m[0e44f5e4] \u001b[39mHwloc v3.0.1\n", + " \u001b[90m[da04e1cc] \u001b[39mMPI v0.20.20\n", + " \u001b[90m[e7922434] \u001b[39mMPIClusterManagers v0.2.4\n", + " \u001b[90m[6f74fd91] \u001b[39mNetworkInterfaceControllers v0.1.0\n" + ] + } + ], + "source": [ + "import Pkg;\n", + "Pkg.activate(@__DIR__)\n", + "Pkg.status()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "1c81c337-5e88-4688-bcf2-f48b6eeb98e8", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4-element Vector{Int64}:\n", + " 2\n", + " 3\n", + " 4\n", + " 5" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "using MPI\n", + "\n", + "using NetworkInterfaceControllers, Sockets\n", + "interfaces = NetworkInterfaceControllers.get_interface_data(IPv4)\n", + "\n", + "hsn0_public = filter(x->(x.name==\"hsn0:chn\" && x.version==:v4), interfaces) |> only \n", + "public_slingshot_name = getnameinfo(hsn0_public.ip)\n", + "\n", + "# to import MPIManager\n", + "using MPIClusterManagers\n", + "\n", + "# need to also import Distributed to use addprocs()\n", + "using Distributed\n", + "\n", + "# specify, number of mpi workers, launch cmd, etc.\n", + "manager=MPIWorkerManager(4)\n", + "\n", + "# start mpi workers and add them as julia workers too.\n", + "addprocs(\n", + " manager,\n", + " exeflags=`--project=$(Base.active_project())`,\n", + " master_tcp_interface=public_slingshot_name\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "53799c57-9c82-4cb2-9a73-f858a8725071", + "metadata": { + "slideshow": { + "slide_type": "slide" + }, + "tags": [] + }, + "source": [ + "# Communication with MPI.jl" + ] + }, + { + "cell_type": "markdown", + "id": "332001ad-3b08-4ceb-b4e4-54e619451191", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "Picking up from the previous demo, we have a job with 4 ranks: " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "0f6bc5b9-2973-4dc5-8fdd-bfd483f01460", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " From worker 5:\tHello world, I am 3 of 4 on nid200349\n", + " From worker 2:\tHello world, I am 0 of 4 on nid200344\n", + " From worker 4:\tHello world, I am 2 of 4 on nid200348\n", + " From worker 3:\tHello world, I am 1 of 4 on nid200345\n" + ] + } + ], + "source": [ + "@mpi_do manager begin\n", + " using MPI: MPI, Comm, Win, free\n", + " comm = MPI.COMM_WORLD\n", + " rank = MPI.Comm_rank(comm)\n", + " size = MPI.Comm_size(comm)\n", + " name = gethostname()\n", + " println(\"Hello world, I am $(rank) of $(size) on $(name)\")\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "7982d349-c25e-4bc9-9624-bbf6f2b6c8cc", + "metadata": { + "slideshow": { + "slide_type": "slide" + }, + "tags": [] + }, + "source": [ + "## Domain Decomposition" + ] + }, + { + "cell_type": "markdown", + "id": "63c5872e-ab53-4871-8bf0-be59956fd42e", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "PDE solvers often break up work over a \"grid\" of ranks (domain decomposition). This will find the dimension of this grid:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "1122c61b-aa2b-47e5-871f-ea7f2f1d501b", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " dims = [0]\n", + " MPI.Dims_create!(size, dims)\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "612ccdb8-8e29-41cc-8c1f-af533e355715", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " From worker 3:\t[4]\n", + " From worker 2:\t[4]\n", + " From worker 4:\t[4]\n", + " From worker 5:\t[4]\n" + ] + } + ], + "source": [ + "@mpi_do manager begin\n", + " println(dims)\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "4ec74bff-4668-4c33-b93a-ee19f67551ac", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "Each rank has the same value for `dims`. In $N$-dimensions, `length(dims) == N`." + ] + }, + { + "cell_type": "markdown", + "id": "3b3679ec-dfac-46d7-97ef-e0ad10ffe295", + "metadata": { + "slideshow": { + "slide_type": "slide" + }, + "tags": [] + }, + "source": [ + "## Cartesian Grids" + ] + }, + { + "cell_type": "markdown", + "id": "871f8fd5-7504-4b03-9a62-e63d3278d098", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "We will now lay out each rank in a \"grid\" (in this example, $N=1$ so it's actually a line). In the excercise, $N=2$, so this will be an actual \"grid\". The steps here are pretty much the same though." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c33bfb02-e341-40e4-8315-83734796a18b", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " comm_cart = MPI.Cart_create(\n", + " comm, # MPI Communicator\n", + " dims, # Dimensions of grid\n", + " [0], # 0 == not periodic, 1 == periodic\n", + " 1, # 0 == not allowed to reorder, 1 == allowed to reoder\n", + " )\n", + " me = MPI.Comm_rank(comm_cart)\n", + " coords = MPI.Cart_coords(comm_cart)\n", + " neighbors = MPI.Cart_shift(\n", + " comm_cart,\n", + " 0, # Which dimension to shift (zero-indexed)\n", + " 1, # Shift magnitude\n", + " )\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "e8cf1293-b416-415f-a14e-d529a9e3e7bc", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " comm_cart = MPI.Cart_create(\n", + " comm, # MPI Communicator\n", + " dims, # Dimensions of grid\n", + " [0], # 0 == not periodic, 1 == periodic\n", + " 1, # 0 == not allowed to reorder, 1 == allowed to reoder\n", + " )\n", + " me = MPI.Comm_rank(comm_cart)\n", + " coords = MPI.Cart_coords(comm_cart)\n", + " neighbors = MPI.Cart_shift(\n", + " comm_cart,\n", + " 0, # Which dimension to shift (zero-indexed)\n", + " 1, # Shift magnitude\n", + " )\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d3ab1a58-0aea-4ec5-a79b-48bcd810c631", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " From worker 2:\trank=0; coord=[0], neighbors=(-1, 1)\n", + " From worker 3:\trank=1; coord=[1], neighbors=(0, 2)\n", + " From worker 5:\trank=3; coord=[3], neighbors=(2, -1)\n", + " From worker 4:\trank=2; coord=[2], neighbors=(1, 3)\n" + ] + } + ], + "source": [ + "@mpi_do manager begin\n", + " println(\"rank=$(me); coord=$(coords), neighbors=$(neighbors)\")\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "63bda425-3a47-4a1c-ba8b-ae3c891d3021", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " From worker 5:\trank=3; coord=[3], neighbors=(2, -1)\n", + " From worker 2:\trank=0; coord=[0], neighbors=(-1, 1)\n", + " From worker 4:\trank=2; coord=[2], neighbors=(1, 3)\n", + " From worker 3:\trank=1; coord=[1], neighbors=(0, 2)\n" + ] + } + ], + "source": [ + "@mpi_do manager begin\n", + " println(\"rank=$(me); coord=$(coords), neighbors=$(neighbors)\")\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "b80b410a-c68c-4e38-ab1c-e355c4d20d8c", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "MPI contains several constants, for example what `-1` means in the context above. This means that there is \"no neighbor\" there:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "94bc63d1-24cc-47f6-a6ab-4624d95523fd", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "MPI.PROC_NULL" + ] + }, + { + "cell_type": "markdown", + "id": "b165e80a-91ce-4233-a8e4-4bd3f09786c1", + "metadata": { + "slideshow": { + "slide_type": "slide" + }, + "tags": [] + }, + "source": [ + "## Point-to-point Communication" + ] + }, + { + "cell_type": "markdown", + "id": "07f6f278-6dc3-4042-aa06-4abc9a7fa7f4", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "Let's do something harder:\n", + "1. Each rank draws a random number between 1 and 100\n", + "2. Each rank's random number is shared with its neighbors" + ] + }, + { + "cell_type": "markdown", + "id": "b286f218-4851-4f11-b3e2-550635a2c688", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "This is an example of point-to-point communication on a grid. We'll be using the same communication pattern in the excercise." + ] + }, + { + "cell_type": "markdown", + "id": "45478166-3101-4380-9149-e9ee101b3b06", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "First we generate a andom number on each rank" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "e5187bd3-8699-4a3b-a43c-28d4a647cdc0", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " using Random\n", + " my_int = rand(1:100)\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "a926edfd-9b22-4e33-851d-6d9e26429065", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " From worker 2:\trank=0; my_int=38\n", + " From worker 4:\trank=2; my_int=29\n", + " From worker 5:\trank=3; my_int=70\n", + " From worker 3:\trank=1; my_int=71\n" + ] + } + ], + "source": [ + "@mpi_do manager begin\n", + " println(\"rank=$(me); my_int=$(my_int)\")\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "064d74de-4b8a-4962-a521-f620f8164cae", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "MPI uses zero-copy memory access => we need to set up buffers (arrays) to send and receive data." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "343bd286-e07b-49b6-8342-ebd85b1a2af7", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " send_1 = zeros(Int64, 1)\n", + " send_2 = zeros(Int64, 1)\n", + " recv_1 = zeros(Int64, 1)\n", + " recv_2 = zeros(Int64, 1)\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "5669bf32-cc11-42b3-b353-31b3231999b4", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "Now we fill the buffers by copying out data into it -- wherever a buffer is needed." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "48a0fa62-2cd2-4071-9046-958e0b335916", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " if neighbors[1] != MPI.PROC_NULL\n", + " copyto!(send_1, my_int)\n", + " end\n", + " if neighbors[2] != MPI.PROC_NULL\n", + " copyto!(send_2, my_int)\n", + " end \n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "b79dfa66-e9c8-455f-b658-004e49ea4df2", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "Now we're ready to perform a data transfer with MPI. MPI is (largely) transaction based. There is a receiving end, and a sending end. In order for a send to be successful, the receiver must be ready to receive." + ] + }, + { + "cell_type": "markdown", + "id": "2d89f9e2-2527-4700-9eeb-600c1844eb06", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "To help coordinate all of this, we set up a request store:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "c05abe1a-8d67-4aff-9191-d135272ca4be", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " reqs = MPI.MultiRequest(4)\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "2256d83d-f6fe-4bed-88d0-e405f53dd664", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "And we transfer the data using non-blocking MPI communivation (`Isend` and `Irecv`). Pro tip: initiate receive before send" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "d847d757-71b4-4d62-8faa-228962bb4794", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " # Initiate data reciever\n", + " if neighbors[1] != MPI.PROC_NULL\n", + " MPI.Irecv!(recv_1, comm_cart, reqs[1]; source=neighbors[1])\n", + " end\n", + " if neighbors[2] != MPI.PROC_NULL\n", + " MPI.Irecv!(recv_2, comm_cart, reqs[2]; source=neighbors[2])\n", + " end\n", + " # Send data\n", + " if neighbors[1] != MPI.PROC_NULL\n", + " MPI.Isend(send_1, comm_cart, reqs[3]; dest=neighbors[1])\n", + " end\n", + " if neighbors[2] != MPI.PROC_NULL\n", + " MPI.Isend(send_2, comm_cart, reqs[4]; dest=neighbors[2])\n", + " end\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "4b2ef6ff-dead-4aff-981c-21407f01c9ef", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "Notice how we tagged data with `source` and `dest`. This makes sure that data is received in the correct order (the middle ranks receive data from _both_ sides), and -- in the case of `Isend` -- that the data is sent to the correct rank." + ] + }, + { + "cell_type": "markdown", + "id": "54ed66db-1274-4efe-a2a0-a8b2b7986527", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "When using non-blocking communication, it's good to wait for all transactions to be completed before using the buffers:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "113d8a31-1834-4d6d-931f-3991592e7ab5", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " # Wait for all requests to finish\n", + " MPI.Waitall(reqs)\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "26e5ed91-9afd-4764-b875-ebbf924dc077", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "Let's take a look at what we've transferred:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "c7f159d3-e651-4795-b63b-2b49a03af961", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " From worker 4:\trank=2; my_int=29; prev=[71]; next=[70]\n", + " From worker 2:\trank=0; my_int=38; prev=[0]; next=[71]\n", + " From worker 5:\trank=3; my_int=70; prev=[29]; next=[0]\n", + " From worker 3:\trank=1; my_int=71; prev=[38]; next=[29]\n" + ] + }, + { + "ename": "KeyError", + "evalue": "KeyError: key \"usage_request\" not found", + "output_type": "error", + "traceback": [ + "KERNEL EXCEPTION", + "KeyError: key \"usage_request\" not found", + "", + "Stacktrace:", + " [1] getindex(h::Dict{String, Function}, key::String)", + " @ Base ./dict.jl:484", + " [2] eventloop(socket::ZMQ.Socket)", + " @ IJulia ~/.julia/packages/IJulia/Vo51o/src/eventloop.jl:8", + " [3] (::IJulia.var\"#14#17\")()", + " @ IJulia ./task.jl:514" + ] + }, + { + "ename": "KeyError", + "evalue": "KeyError: key \"usage_request\" not found", + "output_type": "error", + "traceback": [ + "KERNEL EXCEPTION", + "KeyError: key \"usage_request\" not found", + "", + "Stacktrace:", + " [1] getindex(h::Dict{String, Function}, key::String)", + " @ Base ./dict.jl:484", + " [2] eventloop(socket::ZMQ.Socket)", + " @ IJulia ~/.julia/packages/IJulia/Vo51o/src/eventloop.jl:8", + " [3] (::IJulia.var\"#14#17\")()", + " @ IJulia ./task.jl:514" + ] + }, + { + "ename": "KeyError", + "evalue": "KeyError: key \"usage_request\" not found", + "output_type": "error", + "traceback": [ + "KERNEL EXCEPTION", + "KeyError: key \"usage_request\" not found", + "", + "Stacktrace:", + " [1] getindex(h::Dict{String, Function}, key::String)", + " @ Base ./dict.jl:484", + " [2] eventloop(socket::ZMQ.Socket)", + " @ IJulia ~/.julia/packages/IJulia/Vo51o/src/eventloop.jl:8", + " [3] (::IJulia.var\"#14#17\")()", + " @ IJulia ./task.jl:514" + ] + } + ], + "source": [ + "@mpi_do manager begin\n", + " println(\n", + " \"rank=$(me); \" *\n", + " \"my_int=$(my_int); prev=$(recv_1); next=$(recv_2)\"\n", + " )\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "88e46e3b-f8d4-48d5-b8fc-2ae660f5a4a8", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Julia 1.9.4", + "language": "julia", + "name": "julia-1.9.4" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.9.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/parts/mpi/explanation/02_comms.slides.html b/parts/mpi/explanation/02_comms.slides.html new file mode 100644 index 0000000..c9ebe60 --- /dev/null +++ b/parts/mpi/explanation/02_comms.slides.html @@ -0,0 +1,8136 @@ + + + + + + + +02_comms slides + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/parts/mpi/explanation/03_halo.ipynb b/parts/mpi/explanation/03_halo.ipynb new file mode 100644 index 0000000..30b4b02 --- /dev/null +++ b/parts/mpi/explanation/03_halo.ipynb @@ -0,0 +1,651 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7d81f9b4-89d8-4597-a458-4bfff3c27b81", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "source": [ + "# Setup\n", + "\n", + "Note: you might need to run `Pkg.instantiate()` to ensure that the `Manifest.toml` is up to date. This only needs to be done once." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "df64b70e-4682-4885-b055-056bc4e88a59", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1m Activating\u001b[22m\u001b[39m project at `/global/u1/b/blaschke/juliacon24-hpcworkshop/parts/mpi/explanation`\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1mStatus\u001b[22m\u001b[39m `/global/u1/b/blaschke/juliacon24-hpcworkshop/parts/mpi/explanation/Project.toml`\n", + " \u001b[90m[1520ce14] \u001b[39mAbstractTrees v0.4.5\n", + " \u001b[90m[052768ef] \u001b[39mCUDA v5.4.2\n", + " \u001b[90m[adafc99b] \u001b[39mCpuId v0.3.1\n", + " \u001b[90m[0e44f5e4] \u001b[39mHwloc v3.0.1\n", + " \u001b[90m[da04e1cc] \u001b[39mMPI v0.20.20\n", + " \u001b[90m[e7922434] \u001b[39mMPIClusterManagers v0.2.4\n", + " \u001b[90m[6f74fd91] \u001b[39mNetworkInterfaceControllers v0.1.0\n" + ] + } + ], + "source": [ + "import Pkg;\n", + "Pkg.activate(@__DIR__)\n", + "Pkg.status()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "cd1e1253-87a0-47b9-a225-33dffac6d33f", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"nid200360-hsn0\"" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "using MPI\n", + "\n", + "using NetworkInterfaceControllers, Sockets\n", + "interfaces = NetworkInterfaceControllers.get_interface_data(IPv4)\n", + "\n", + "hsn0_public = filter(x->(x.name==\"hsn0:chn\" && x.version==:v4), interfaces) |> only \n", + "public_slingshot_name = getnameinfo(hsn0_public.ip)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "68377016-c3df-4a1c-9c42-150d6af80de8", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4-element Vector{Int64}:\n", + " 2\n", + " 3\n", + " 4\n", + " 5" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# to import MPIManager\n", + "using MPIClusterManagers\n", + "\n", + "# need to also import Distributed to use addprocs()\n", + "using Distributed\n", + "\n", + "# specify, number of mpi workers, launch cmd, etc.\n", + "manager=MPIWorkerManager(4)\n", + "\n", + "# start mpi workers and add them as julia workers too.\n", + "addprocs(\n", + " manager,\n", + " exeflags=`--project=$(Base.active_project())`,\n", + " master_tcp_interface=public_slingshot_name\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "b243745d-ad52-4d52-873a-b9bc6575054a", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " From worker 5:\tHello world, I am 3 of 4 on nid200365\n", + " From worker 2:\tHello world, I am 0 of 4 on nid200360\n", + " From worker 4:\tHello world, I am 2 of 4 on nid200364\n", + " From worker 3:\tHello world, I am 1 of 4 on nid200361\n" + ] + }, + { + "ename": "KeyError", + "evalue": "KeyError: key \"usage_request\" not found", + "output_type": "error", + "traceback": [ + "KERNEL EXCEPTION", + "KeyError: key \"usage_request\" not found", + "", + "Stacktrace:", + " [1] getindex(h::Dict{String, Function}, key::String)", + " @ Base ./dict.jl:484", + " [2] eventloop(socket::ZMQ.Socket)", + " @ IJulia ~/.julia/packages/IJulia/Vo51o/src/eventloop.jl:8", + " [3] (::IJulia.var\"#14#17\")()", + " @ IJulia ./task.jl:514" + ] + }, + { + "ename": "KeyError", + "evalue": "KeyError: key \"usage_request\" not found", + "output_type": "error", + "traceback": [ + "KERNEL EXCEPTION", + "KeyError: key \"usage_request\" not found", + "", + "Stacktrace:", + " [1] getindex(h::Dict{String, Function}, key::String)", + " @ Base ./dict.jl:484", + " [2] eventloop(socket::ZMQ.Socket)", + " @ IJulia ~/.julia/packages/IJulia/Vo51o/src/eventloop.jl:8", + " [3] (::IJulia.var\"#14#17\")()", + " @ IJulia ./task.jl:514" + ] + } + ], + "source": [ + "@mpi_do manager begin\n", + " using MPI: MPI, Comm, Win, free\n", + " comm = MPI.COMM_WORLD\n", + " rank = MPI.Comm_rank(comm)\n", + " mpi_size = MPI.Comm_size(comm) # don't use \"size\" as this overwrites the `size` function\n", + " name = gethostname()\n", + " println(\"Hello world, I am $(rank) of $(mpi_size) on $(name)\")\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f4197ff5-6ba6-4964-aca4-178147857b74", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " dims = [0]\n", + " MPI.Dims_create!(mpi_size, dims)\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "e56347d7-018b-4daa-8b0f-7934a3097718", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " comm_cart = MPI.Cart_create(\n", + " comm, # MPI Communicator\n", + " dims, # Dimensions of grid\n", + " [0], # 0 == not periodic, 1 == periodic\n", + " 1, # 0 == not allowed to reorder, 1 == allowed to reoder\n", + " )\n", + " me = MPI.Comm_rank(comm_cart)\n", + " coords = MPI.Cart_coords(comm_cart)\n", + " neighbors = MPI.Cart_shift(\n", + " comm_cart,\n", + " 0, # Which dimension to shift (zero-indexed)\n", + " 1, # Shift magnitude\n", + " )\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "e591dff1-e930-405a-aced-7ba54ef75164", + "metadata": { + "slideshow": { + "slide_type": "slide" + }, + "tags": [] + }, + "source": [ + "# Halo Exchange" + ] + }, + { + "cell_type": "markdown", + "id": "5eac60ff-cd2d-4561-bf87-a732e93cdbc5", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "When cast into the discrete form:\n", + "\n", + "$$\n", + "\\partial_t x = -D \\mathrm{div}(\\mathrm{grad}(x)) \\\\\n", + "\\Delta_t x = -D \\frac{q_i - q_{i-1}}{\\Delta s} = \\frac{(x_{i+1} - x_i) - (x_{i} - x_{i-1})}{(\\Delta s)^2} = \\frac{x_{i+1} + 2 x_i - x_{i-1}}{(\\Delta s)^2}\n", + "$$\n", + "\n", + "The diffusion equation has a stencil width of 2, but the necessary halo only needs 1 cell to be transferred:" + ] + }, + { + "cell_type": "markdown", + "id": "c67e1b1e-7bec-4b02-bcd8-4fecefd8170b", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "![1D_halo](l8_1D_global_grid.png)" + ] + }, + { + "cell_type": "markdown", + "id": "7ebe2ba1-2ea3-498d-be6e-bd34d6a50ad9", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "In 2D this will look as follows:\n", + "\n", + "![2D_halo](diffusion_2d_halo_exchange.png)" + ] + }, + { + "cell_type": "markdown", + "id": "d22a1ac9-cc48-4bed-87fc-a2113ebb8067", + "metadata": { + "slideshow": { + "slide_type": "slide" + }, + "tags": [] + }, + "source": [ + "## 1D Solver Example" + ] + }, + { + "cell_type": "markdown", + "id": "bc43046b-6490-429c-bd4e-a442e0c2cafd", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "source": [ + "Let's set up a basic example: 1D diffusion! First we need some parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": 245, + "id": "49fc3f16-27d8-4589-8f89-76d868c4f3c1", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " D = 1e-4\n", + " ds = 1e-4\n", + " dt = ds^2 / D / 8.2 \n", + " qx(ix, D, C, ds) = -D * (C[ix+1, 1] - C[ix, 1]) / ds\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "2062fc6f-d631-46a8-8908-08db59eb3c43", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "We can now iterate over the local array (which has a halo of 2 cells):" + ] + }, + { + "cell_type": "code", + "execution_count": 248, + "id": "b1f885ad-b823-45da-a33c-8ab615425362", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " function step_diffusion!(C2, C)\n", + " for i in 1:size(C, 1) - 2\n", + " C2[i+1] = C[i+1] - dt * (qx(i+1, D, C, ds) - qx(i, D, C, ds)) / ds\n", + " end\n", + " end\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "3f237d99-4457-4e0f-abbf-2dbb676ef837", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "We set up an initial condition where a single cell at the edge of domain 2 (rank 1) is non-zero. Recall that the halo is 2-cells wide => `C[8]` is at the very end of domain 2." + ] + }, + { + "cell_type": "code", + "execution_count": 246, + "id": "e0085955-4b07-4942-b471-7f9a130ab908", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " C = zeros(10, 1)\n", + " if rank == 1\n", + " C[8] = 1/ds\n", + " end\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 247, + "id": "9c67b1a0-3dfa-44a6-b0a7-32e2cf550db8", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " From worker 2:\t[0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0;;]\n", + " From worker 4:\t[0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0;;]\n", + " From worker 5:\t[0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0;;]\n", + " From worker 3:\t[0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 10000.0; 0.0; 0.0;;]\n" + ] + } + ], + "source": [ + "@mpi_do manager begin\n", + " println(C)\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 249, + "id": "b09f3b69-a733-467a-84ed-a91b577c89ba", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " C2 = similar(C)\n", + " fill!(C2, 0.)\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "f8307d70-8e35-4285-bca5-63ed716a417a", + "metadata": { + "slideshow": { + "slide_type": "slide" + }, + "tags": [] + }, + "source": [ + "## Halo Exchanges in 1D" + ] + }, + { + "cell_type": "markdown", + "id": "1e075e1e-0575-4a32-b5ad-da0fa724279b", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "In the previous example we exchanged `Int64`, now we're going to tranfer `Float64`" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d36f644b-65fb-44d0-998c-09c74e805235", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " send_1 = zeros(Float64, 1)\n", + " send_2 = zeros(Float64, 1)\n", + " recv_1 = zeros(Float64, 1)\n", + " recv_2 = zeros(Float64, 1)\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "8f3b2de0-563f-4bb0-acb7-99d7be2a2c66", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "We set up a halo-exchange function using the previous section's point-to-point communication pattern" + ] + }, + { + "cell_type": "code", + "execution_count": 257, + "id": "a6891fa9-6d92-4c65-b791-2aa4246e1e2e", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " function halo_exchange!(A)\n", + " # Copy to buffers\n", + " (neighbors[1] != MPI.PROC_NULL) && copyto!(send_1, A[2:2, 1])\n", + " (neighbors[2] != MPI.PROC_NULL) && copyto!(send_2, A[(end-1):(end-1), 1]) \n", + " # Request handler\n", + " reqs = MPI.MultiRequest(4)\n", + " # Initiate data reciever\n", + " (neighbors[1] != MPI.PROC_NULL) && MPI.Irecv!(recv_1, comm_cart, reqs[1]; source=neighbors[1])\n", + " (neighbors[2] != MPI.PROC_NULL) && MPI.Irecv!(recv_2, comm_cart, reqs[2]; source=neighbors[2])\n", + " # Send data\n", + " (neighbors[1] != MPI.PROC_NULL) && MPI.Isend(send_1, comm_cart, reqs[3]; dest=neighbors[1])\n", + " (neighbors[2] != MPI.PROC_NULL) && MPI.Isend(send_2, comm_cart, reqs[4]; dest=neighbors[2])\n", + " # Block until all transactions are done before touching buffers\n", + " MPI.Waitall(reqs) \n", + " # Copy from buffers (copyto! needs a pointer to the cell)\n", + " r1 = @view A[1:1, 1] \n", + " r2 = @view A[end:end, 1]\n", + " (neighbors[1] != MPI.PROC_NULL) && copyto!(r1, recv_1)\n", + " (neighbors[2] != MPI.PROC_NULL) && copyto!(r2, recv_2)\n", + " end\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "1944ce97-2586-42b5-b954-b5b4a587766c", + "metadata": { + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, + "source": [ + "Let's run 1 step of the diffusion algorithm to see how the halo exchane works:" + ] + }, + { + "cell_type": "code", + "execution_count": 250, + "id": "871692b2-3589-4e13-9889-bad943325e23", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "@mpi_do manager begin\n", + " step_diffusion!(C2, C)\n", + " halo_exchange!(C2)\n", + " C, C2 = C2, C\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 252, + "id": "7ccd49d0-3eee-46ea-a888-8094047e3bd8", + "metadata": { + "slideshow": { + "slide_type": "fragment" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " From worker 5:\t[0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0;;]\n", + " From worker 4:\t[1219.5121951219512; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0;;]\n", + " From worker 2:\t[0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0;;]\n", + " From worker 3:\t[0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 1219.5121951219512; 7560.975609756098; 1219.5121951219512; 0.0;;]\n" + ] + } + ], + "source": [ + "@mpi_do manager begin\n", + " println(C)\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2dc8e0fd-66e7-43aa-9035-95e84915971b", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Julia 1.9.4", + "language": "julia", + "name": "julia-1.9.4" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.9.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/parts/mpi/explanation/03_halo.slides.html b/parts/mpi/explanation/03_halo.slides.html new file mode 100644 index 0000000..f4e0786 --- /dev/null +++ b/parts/mpi/explanation/03_halo.slides.html @@ -0,0 +1,7847 @@ + + + + + + + +03_halo slides + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/parts/mpi/explanation/Project.toml b/parts/mpi/explanation/Project.toml new file mode 100644 index 0000000..5161d4b --- /dev/null +++ b/parts/mpi/explanation/Project.toml @@ -0,0 +1,8 @@ +[deps] +AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" +CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" +CpuId = "adafc99b-e345-5852-983c-f28acb93d879" +Hwloc = "0e44f5e4-bd66-52a0-8798-143a42290a1d" +MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" +MPIClusterManagers = "e7922434-ae4b-11e9-05c5-9780451d2c66" +NetworkInterfaceControllers = "6f74fd91-2978-43ad-8164-3af8c0ec0142" diff --git a/parts/mpi/explanation/advanced/00_gpu_select.ipynb b/parts/mpi/explanation/advanced/00_gpu_select.ipynb new file mode 100644 index 0000000..67555e7 --- /dev/null +++ b/parts/mpi/explanation/advanced/00_gpu_select.ipynb @@ -0,0 +1,550 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "7fc2f000-ba64-483f-99d7-37b7f24969d1", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1m Activating\u001b[22m\u001b[39m project at `/global/u1/b/blaschke/juliacon24-hpcworkshop/parts/mpi/explanation`\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1mStatus\u001b[22m\u001b[39m `/global/u1/b/blaschke/juliacon24-hpcworkshop/parts/mpi/explanation/Project.toml`\n", + " \u001b[90m[1520ce14] \u001b[39mAbstractTrees v0.4.5\n", + " \u001b[90m[052768ef] \u001b[39mCUDA v5.4.2\n", + " \u001b[90m[adafc99b] \u001b[39mCpuId v0.3.1\n", + " \u001b[90m[0e44f5e4] \u001b[39mHwloc v3.0.1\n", + " \u001b[90m[da04e1cc] \u001b[39mMPI v0.20.20\n", + " \u001b[90m[e7922434] \u001b[39mMPIClusterManagers v0.2.4\n", + " \u001b[90m[6f74fd91] \u001b[39mNetworkInterfaceControllers v0.1.0\n" + ] + } + ], + "source": [ + "import Pkg;\n", + "Pkg.activate(@__DIR__)\n", + "Pkg.status()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2fffd2fb-ff8c-45a3-963a-06e40f4511f7", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "cpucycle_coreid (generic function with 1 method)" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "using CpuId\n", + "\n", + "const cpucycle_mask = (\n", + " (1 << (64 - leading_zeros(CpuId.cputhreads()))) - 1\n", + ") % UInt32\n", + "\n", + "cpucycle_coreid() = Int(cpucycle_id()[2] & cpucycle_mask)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a38f335a-0c2c-43c3-bd9a-45656331d464", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "13" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cpucycle_coreid()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "0ab0999d-bcac-4a10-885a-c689eda97924", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "using MPI, CUDA" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "7bc9f6ae-0ba6-4206-84d6-ce4dc6576f24", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MPIPreferences:\n", + " binary: system\n", + " abi: MPICH\n", + " libmpi: libmpi_gnu_123.so\n", + " mpiexec: srun\n", + "\n", + "Package versions\n", + " MPI.jl: 0.20.20\n", + " MPIPreferences.jl: 0.1.11\n", + "\n", + "Library information:\n", + " libmpi: libmpi_gnu_123.so\n", + " libmpi dlpath: /opt/cray/pe/lib64/libmpi_gnu_123.so\n", + " MPI version: 3.1.0\n", + " Library version: \n", + " MPI VERSION : CRAY MPICH version 8.1.28.29 (ANL base 3.4a2)\n", + " MPI BUILD INFO : Wed Nov 15 20:57 2023 (git hash 1cde46f)\n", + " \n" + ] + } + ], + "source": [ + "MPI.versioninfo()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "27fb385f-7c83-421f-b77d-a59289004f8e", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CUDA runtime 12.2, local installation\n", + "CUDA driver 12.2\n", + "NVIDIA driver 525.105.17\n", + "\n", + "CUDA libraries: \n", + "- CUBLAS: 12.2.1\n", + "- CURAND: 10.3.3\n", + "- CUFFT: 11.0.8\n", + "- CUSOLVER: 11.5.0\n", + "- CUSPARSE: 12.1.1\n", + "- CUPTI: 20.0.0\n", + "- NVML: 12.0.0+525.105.17\n", + "\n", + "Julia packages: \n", + "- CUDA: 5.4.2\n", + "- CUDA_Driver_jll: 0.9.1+1\n", + "- CUDA_Runtime_jll: 0.14.1+0\n", + "- CUDA_Runtime_Discovery: 0.3.4\n", + "\n", + "Toolchain:\n", + "- Julia: 1.9.4\n", + "- LLVM: 14.0.6\n", + "\n", + "Preferences:\n", + "- CUDA_Runtime_jll.version: 12.2\n", + "- CUDA_Runtime_jll.local: true\n", + "\n", + "4 devices:\n", + " 0: NVIDIA A100-SXM4-80GB (sm_80, 79.150 GiB / 80.000 GiB available)\n", + " 1: NVIDIA A100-SXM4-80GB (sm_80, 79.150 GiB / 80.000 GiB available)\n", + " 2: NVIDIA A100-SXM4-80GB (sm_80, 79.150 GiB / 80.000 GiB available)\n", + " 3: NVIDIA A100-SXM4-80GB (sm_80, 79.150 GiB / 80.000 GiB available)\n" + ] + } + ], + "source": [ + "CUDA.versioninfo()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "57b8e1ad-c17a-4af5-abda-a6bddb59c15f", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "filter (generic function with 26 methods)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import Base: filter, Fix1\n", + "filter(f::Function)::Function = Fix1(filter, f)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "f51cb426-9357-4ed3-9ca9-319e81bc4f69", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "get_device_attributes (generic function with 1 method)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function get_device_attributes()\n", + " attr = Dict{Tuple{Int32, Int32}, Int32}()\n", + " for i in 0:(ndevices()-1)\n", + " d = CuDevice(i)\n", + " attr[(\n", + " attribute(d, CUDA.CU_DEVICE_ATTRIBUTE_PCI_BUS_ID),\n", + " attribute(d, CUDA.CU_DEVICE_ATTRIBUTE_PCI_DEVICE_ID)\n", + " )] = d\n", + " end\n", + " attr\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "bc7e78ce-3ed7-4663-981e-99da96e9f5c7", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "using Hwloc, AbstractTrees\n", + "\n", + "\n", + "import AbstractTrees: PreOrderDFS\n", + "import Hwloc: hwloc_pci_class_string" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "cc2c286d-9822-4ad6-8d55-efd4dcc442b0", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "distance_to_core (generic function with 1 method)" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function tag_subtree!(tree_node, val)\n", + " for n in collect(AbstractTrees.PreOrderDFS(tree_node))\n", + " n.tag = val\n", + " end\n", + "end\n", + "\n", + "function distance_to_core!(node, target_index)\n", + " # shield re-entrance when iterating\n", + " node.tag = 1\n", + "\n", + " if node.type == :PU\n", + " # println(\"Checking: $(nodevalue(node).os_index)\")\n", + " if nodevalue(node).os_index == target_index\n", + " return true, 0\n", + " end\n", + " end\n", + "\n", + " for child in node.children\n", + " if child.tag == 1\n", + " continue\n", + " end\n", + "\n", + " found, dist = distance_to_core!(child, target_index)\n", + " if found\n", + " return true, dist + 1\n", + " end\n", + " end\n", + "\n", + " if node.parent != nothing\n", + " found, dist = distance_to_core!(node.parent, target_index)\n", + " if found\n", + " return true, dist + 1\n", + " end\n", + " end\n", + "\n", + " return false, typemax(Int)\n", + "end\n", + "\n", + "function distance_to_core(root, node, target_index)\n", + " tag_subtree!(root, 0) \n", + " found, dist = distance_to_core!(node, target_index)\n", + " tag_subtree!(root, 0) \n", + " return found, dist\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "ddbadb39-1998-4472-b940-09648284ad8c", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Dict{Tuple{Int32, Int32}, Int32} with 4 entries:\n", + " (65, 0) => 1\n", + " (193, 0) => 3\n", + " (130, 0) => 2\n", + " (3, 0) => 0" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_device_attributes()" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "00f54295-a079-474c-8028-57fcea1fa288", + "metadata": { + "slideshow": { + "slide_type": "skip" + }, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "get_device_distances (generic function with 1 method)" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sys_devs = children(gettopology())\n", + "pci_devs = PreOrderDFS(sys_devs) |> collect |> filter(x->x.type==:PCI_Device)\n", + "gpu_devs = pci_devs |> filter(x->hwloc_pci_class_string(nodevalue(x).attr.class_id) == \"3D\")\n", + "\n", + "function get_device_distances(core)\n", + " attr = get_device_attributes()\n", + " dist = Dict{Int32, Int32}()\n", + " dev = Dict{Int32, Int32}()\n", + " for d in gpu_devs\n", + " idx = attr[(nodevalue(d).attr.bus, nodevalue(d).attr.dev)]\n", + " found, dev_d = distance_to_core(sys_devs, d, core)\n", + " if found\n", + " dist[idx] = dev_d\n", + " dev[dev_d] = idx\n", + " end\n", + " end\n", + " dist, dev\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "d35ac271-b857-46f3-9744-a2512642c009", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "49" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cpucycle_coreid()" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "c867b0ed-85df-47c1-b85a-c99312c66430", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dist, dev = get_device_distances(cpucycle_coreid())\n", + "closest_dev = dev[dev |> keys |> minimum]" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "e22f3df1-afef-45da-baef-8554c5f69189", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Dict{Int32, Int32} with 4 entries:\n", + " 0 => 18\n", + " 2 => 516\n", + " 3 => 516\n", + " 1 => 516" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dist" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "24afd17a-2308-4089-933b-5d138f555482", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Dict{Int32, Int32} with 2 entries:\n", + " 18 => 0\n", + " 516 => 1" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dev" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "8c844ec9-bb7b-4142-899c-acac3035d841", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a17f0a7a-5ced-4fa7-8c65-ee4da39d54af", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Julia 1.9.4", + "language": "julia", + "name": "julia-1.9.4" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.9.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/parts/mpi/explanation/diffusion_2d_halo_exchange.pdf b/parts/mpi/explanation/diffusion_2d_halo_exchange.pdf new file mode 100644 index 0000000..9513e4f Binary files /dev/null and b/parts/mpi/explanation/diffusion_2d_halo_exchange.pdf differ diff --git a/parts/mpi/explanation/diffusion_2d_halo_exchange.png b/parts/mpi/explanation/diffusion_2d_halo_exchange.png new file mode 100644 index 0000000..0c16394 Binary files /dev/null and b/parts/mpi/explanation/diffusion_2d_halo_exchange.png differ diff --git a/parts/mpi/explanation/l8_1D_global_grid.png b/parts/mpi/explanation/l8_1D_global_grid.png new file mode 100644 index 0000000..0342ca9 Binary files /dev/null and b/parts/mpi/explanation/l8_1D_global_grid.png differ diff --git a/parts/mpi/get_compute_node_interactive.sh b/parts/mpi/get_compute_node_interactive.sh new file mode 100644 index 0000000..e08329b --- /dev/null +++ b/parts/mpi/get_compute_node_interactive.sh @@ -0,0 +1 @@ +salloc --nodes 1 --cpus-per-task=1 --qos interactive --time 00:45:00 --constraint cpu --ntasks-per-node=4 --account=ntrain1 diff --git a/parts/mpi/job_mpi_multinode.sh b/parts/mpi/job_mpi_multinode.sh new file mode 100644 index 0000000..c523ca3 --- /dev/null +++ b/parts/mpi/job_mpi_multinode.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +#SBATCH -A ntrain1 +#SBATCH -C cpu +#SBATCH -q regular +#SBATCH --output=slurm_mpi_multinode.out +#SBATCH --time=00:05:00 +#SBATCH --nodes=4 +#SBATCH --ntasks=16 +#SBATCH --exclusive + +ml use /global/common/software/nersc/n9/julia/modules +ml julia + +mpiexecjl --project=../.. julia -e 'do_save=false; include("diffusion_2d_mpi.jl");' diff --git a/parts/mpi/job_mpi_singlenode.sh b/parts/mpi/job_mpi_singlenode.sh new file mode 100644 index 0000000..7bf15a6 --- /dev/null +++ b/parts/mpi/job_mpi_singlenode.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +#SBATCH -A ntrain1 +#SBATCH -C cpu +#SBATCH -q regular +#SBATCH --output=slurm_mpi_singlenode.out +#SBATCH --time=00:05:00 +#SBATCH --nodes=1 +#SBATCH --ntasks=4 +#SBATCH --exclusive + +ml use /global/common/software/nersc/n9/julia/modules +ml julia + +mpiexecjl --project=../.. julia diffusion_2d_mpi.jl diff --git a/parts/mpi/solution/diffusion_2d_mpi.jl b/parts/mpi/solution/diffusion_2d_mpi.jl new file mode 100644 index 0000000..6fddbc2 --- /dev/null +++ b/parts/mpi/solution/diffusion_2d_mpi.jl @@ -0,0 +1,111 @@ +# 2D linear diffusion solver - MPI +using Printf +using JLD2 +using MPI +include(joinpath(@__DIR__, "../../shared.jl")) + +# convenience macros simply to avoid writing nested finite-difference expression +macro qx(ix, iy) esc(:(-D * (C[$ix+1, $iy] - C[$ix, $iy]) / dx)) end +macro qy(ix, iy) esc(:(-D * (C[$ix, $iy+1] - C[$ix, $iy]) / dy)) end + +function diffusion_step!(params, C2, C) + (; dx, dy, dt, D) = params + for iy in 1:size(C, 2)-2 + for ix in 1:size(C, 1)-2 + @inbounds C2[ix+1, iy+1] = C[ix+1, iy+1] - dt * ((@qx(ix+1, iy+1) - @qx(ix, iy+1)) / dx + + (@qy(ix+1, iy+1) - @qy(ix+1, iy)) / dy) + end + end + return nothing +end + +# MPI functions +@views function update_halo!(A, bufs, neighbors, comm) + # dim-1 (x) + (neighbors.x[1] != MPI.PROC_NULL) && copyto!(bufs.send_1_1, A[2 , :]) + (neighbors.x[2] != MPI.PROC_NULL) && copyto!(bufs.send_1_2, A[end-1, :]) + + reqs = MPI.MultiRequest(4) + (neighbors.x[1] != MPI.PROC_NULL) && MPI.Irecv!(bufs.recv_1_1, comm, reqs[1]; source=neighbors.x[1]) + (neighbors.x[2] != MPI.PROC_NULL) && MPI.Irecv!(bufs.recv_1_2, comm, reqs[2]; source=neighbors.x[2]) + + (neighbors.x[1] != MPI.PROC_NULL) && MPI.Isend(bufs.send_1_1, comm, reqs[3]; dest=neighbors.x[1]) + (neighbors.x[2] != MPI.PROC_NULL) && MPI.Isend(bufs.send_1_2, comm, reqs[4]; dest=neighbors.x[2]) + MPI.Waitall(reqs) # blocking + + (neighbors.x[1] != MPI.PROC_NULL) && copyto!(A[1 , :], bufs.recv_1_1) + (neighbors.x[2] != MPI.PROC_NULL) && copyto!(A[end, :], bufs.recv_1_2) + + # dim-2 (y) + (neighbors.y[1] != MPI.PROC_NULL) && copyto!(bufs.send_2_1, A[:, 2 ]) + (neighbors.y[2] != MPI.PROC_NULL) && copyto!(bufs.send_2_2, A[:, end-1]) + + reqs = MPI.MultiRequest(4) + (neighbors.y[1] != MPI.PROC_NULL) && MPI.Irecv!(bufs.recv_2_1, comm, reqs[1]; source=neighbors.y[1]) + (neighbors.y[2] != MPI.PROC_NULL) && MPI.Irecv!(bufs.recv_2_2, comm, reqs[2]; source=neighbors.y[2]) + + (neighbors.y[1] != MPI.PROC_NULL) && MPI.Isend(bufs.send_2_1, comm, reqs[3]; dest=neighbors.y[1]) + (neighbors.y[2] != MPI.PROC_NULL) && MPI.Isend(bufs.send_2_2, comm, reqs[4]; dest=neighbors.y[2]) + MPI.Waitall(reqs) # blocking + + (neighbors.y[1] != MPI.PROC_NULL) && copyto!(A[:, 1 ], bufs.recv_2_1) + (neighbors.y[2] != MPI.PROC_NULL) && copyto!(A[:, end], bufs.recv_2_2) + return nothing +end + +function init_bufs(A) + return (; send_1_1=zeros(size(A, 2)), send_1_2=zeros(size(A, 2)), + send_2_1=zeros(size(A, 1)), send_2_2=zeros(size(A, 1)), + recv_1_1=zeros(size(A, 2)), recv_1_2=zeros(size(A, 2)), + recv_2_1=zeros(size(A, 1)), recv_2_2=zeros(size(A, 1))) +end + +function run_diffusion(; ns=64, nt=100, do_save=false) + MPI.Init() + comm = MPI.COMM_WORLD + nprocs = MPI.Comm_size(comm) + dims = MPI.Dims_create(nprocs, (0, 0)) |> Tuple + comm_cart = MPI.Cart_create(comm, dims) + me = MPI.Comm_rank(comm_cart) + coords = MPI.Cart_coords(comm_cart) |> Tuple + neighbors = (; x=MPI.Cart_shift(comm_cart, 0, 1), y=MPI.Cart_shift(comm_cart, 1, 1)) + (me == 0) && println("nprocs = $(nprocs), dims = $dims") + + params = init_params_mpi(; dims, coords, ns, nt, do_save) + C, C2 = init_arrays_mpi(params) + bufs = init_bufs(C) + t_tic = 0.0 + # time loop + for it in 1:nt + # time after warmup (ignore first 10 iterations) + (it == 11) && (t_tic = Base.time()) + # diffusion + diffusion_step!(params, C2, C) + update_halo!(C2, bufs, neighbors, comm_cart) + C, C2 = C2, C # pointer swap + end + t_toc = (Base.time() - t_tic) + # "master" prints performance + (me == 0) && print_perf(params, t_toc) + # save to (maybe) visualize later + if do_save + jldsave(joinpath(@__DIR__, "out_$(me).jld2"); C = Array(C[2:end-1, 2:end-1]), lxy = (; lx=params.L, ly=params.L)) + end + MPI.Finalize() + return nothing +end + +# Running things... + +# enable save to disk by default +(!@isdefined do_save) && (do_save = true) +# enable execution by default +(!@isdefined do_run) && (do_run = true) + +if do_run + if !isempty(ARGS) + run_diffusion(; ns=parse(Int, ARGS[1]), do_save) + else + run_diffusion(; ns=256, do_save) + end +end diff --git a/parts/mpi/solution/job_mpi_multinode.sh b/parts/mpi/solution/job_mpi_multinode.sh new file mode 100644 index 0000000..7b48788 --- /dev/null +++ b/parts/mpi/solution/job_mpi_multinode.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +#SBATCH -A ntrain1 +#SBATCH -C cpu +#SBATCH -q regular +#SBATCH --output=slurm_mpi_multinode.out +#SBATCH --time=00:05:00 +#SBATCH --nodes=4 +#SBATCH --ntasks=16 +#SBATCH --exclusive + +ml use /global/common/software/nersc/n9/julia/modules +ml julia + +mpiexecjl --project=../../.. julia -e 'do_save=false; include("diffusion_2d_mpi.jl");' diff --git a/parts/mpi/solution/job_mpi_singlenode.sh b/parts/mpi/solution/job_mpi_singlenode.sh new file mode 100644 index 0000000..950bfd3 --- /dev/null +++ b/parts/mpi/solution/job_mpi_singlenode.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +#SBATCH -A ntrain1 +#SBATCH -C cpu +#SBATCH -q regular +#SBATCH --output=slurm_mpi_singlenode.out +#SBATCH --time=00:05:00 +#SBATCH --nodes=1 +#SBATCH --ntasks=4 +#SBATCH --exclusive + +ml use /global/common/software/nersc/n9/julia/modules +ml julia + +mpiexecjl --project=../../.. julia diffusion_2d_mpi.jl diff --git a/parts/mpi/solution/multinode_results.txt b/parts/mpi/solution/multinode_results.txt new file mode 100644 index 0000000..5b11cca --- /dev/null +++ b/parts/mpi/solution/multinode_results.txt @@ -0,0 +1,15 @@ +# 1 node, 4 MPI ranks +nprocs = 4, dims = [2, 2] +Time = 6.5865e+00 s, T_eff = 8.25 GB/s + +# 2 nodes, 8 MPI ranks +nprocs = 8, dims = [4, 2] +Time = 6.5964e+00 s, T_eff = 8.24 GB/s + +# 3 nodes, 12 MPI ranks +nprocs = 12, dims = [4, 3] +Time = 6.5889e+00 s, T_eff = 8.25 GB/s + +# 4 nodes, 16 MPI ranks +nprocs = 16, dims = [4, 4] +Time = 6.6004e+00 s, T_eff = 8.24 GB/s \ No newline at end of file diff --git a/parts/mpi/solution/slurm_mpi_singlenode.out b/parts/mpi/solution/slurm_mpi_singlenode.out new file mode 100644 index 0000000..94abeb7 --- /dev/null +++ b/parts/mpi/solution/slurm_mpi_singlenode.out @@ -0,0 +1,2 @@ +nprocs = 4, dims = [2, 2] +Time = 1.2309e-02 s, T_eff = 7.67 GB/s diff --git a/parts/mpi/solution/visualization_before.png b/parts/mpi/solution/visualization_before.png new file mode 100644 index 0000000..4287972 Binary files /dev/null and b/parts/mpi/solution/visualization_before.png differ diff --git a/parts/mpi/solution/visualization_desired.png b/parts/mpi/solution/visualization_desired.png new file mode 100644 index 0000000..5974a3d Binary files /dev/null and b/parts/mpi/solution/visualization_desired.png differ diff --git a/parts/mpi/visualize_mpi.ipynb b/parts/mpi/visualize_mpi.ipynb new file mode 100644 index 0000000..55e5028 --- /dev/null +++ b/parts/mpi/visualize_mpi.ipynb @@ -0,0 +1,117 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e1de83bf-d928-48c4-a5c2-7805dce26a46", + "metadata": {}, + "source": [ + "This is a copy of `visualize_mpi.jl` in Jupyter -- for those folks that aren't using VSCode" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "990870c8-cccf-4b07-a03d-a9f3b0e511b6", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "using CairoMakie\n", + "using JLD2" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "98e9b391-c411-4195-816c-833b74f3fec0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "vizme2D_mpi (generic function with 1 method)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function vizme2D_mpi(nprocs)\n", + " C = []\n", + " lx = ly = 0.0\n", + " ip = 1\n", + " for ipx in 1:nprocs[1]\n", + " for ipy in 1:nprocs[2]\n", + " C_loc, lxy = load(joinpath(@__DIR__, \"out_$(ip-1).jld2\"), \"C\", \"lxy\")\n", + " nx_i, ny_i = size(C_loc, 1), size(C_loc, 2)\n", + " ix1, iy1 = 1 + (ipx - 1) * nx_i, 1 + (ipy - 1) * ny_i\n", + " if ip == 1\n", + " C = zeros(nprocs[1] * nx_i, nprocs[2] * ny_i)\n", + " lx, ly = lxy\n", + " end\n", + " C[ix1:ix1+nx_i-1, iy1:iy1+ny_i-1] .= C_loc\n", + " ip += 1\n", + " end\n", + " end\n", + " xc, yc = LinRange.(0, (lx, ly), size(C))\n", + " fig = Figure(; size=(500, 400), fontsize=14)\n", + " ax = Axis(fig[1, 1][1, 1]; aspect=DataAspect(), title=\"C\")\n", + " hm = heatmap!(ax, xc, yc, C; colormap=:turbo, colorrange=(0, 1))\n", + " cb = Colorbar(fig[1, 1][1, 2], hm)\n", + " display(fig)\n", + " return\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "a0ac601e-47d6-4ec0-bd85-446de2a13d4c", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA+gAAAMgCAIAAAA/et9qAAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nOy9eYw1aVmHfd9PnRnWEUTQCTADiCyG7RuNQ6KJiAqSAKIGCEs0QY2GcYkgifFjiWAYw2KCCRD+UDFCUFATcAYxYYwiEh35B4K4YODTQWaAsC/DLO957u+PZ6/lLN1V3X36vS7HfutUPVWnurrpvurX93OXmpkAAAAAAMDZxp32CQAAAAAAwHYQdwAAAACAAwBxBwAAAAA4ABB3AAAAAIADAHEHAAAAADgAEHcAAAAAgAMAcQcAAAAAOAAQdwAAAACAAwBxBwAAAAA4ABB3AAAAAIADAHEHAAAAADgAEHcAAAAAgAMAcQcAAAAAOAAQdwAAAACAAwBxBwAAAAA4ABB3AAAAAIADAHEHAAAAADgAEHcAAAAAgAMAcQcAAAAAOAAQdwAAAACAAwBxBwAAAAA4ABB3AAAAAIADAHEHAAAAADgAEHcAAAAAgAMAcQcAAAAAOAAQdwAAAACAAwBxBwAAAAA4ABB3AAAAAIADAHEHAAAAADgAEHcAAAAAgAMAcQcAAAAAOAAQdwAAAACAAwBxBwAAAAA4ABB3AAAAAIADAHEHAAAAADgAEHcAAAAAgANgddonAABwUXDnnXd+6EMfev/733/TTTd9+ctffsADHvDwhz/8kY985JOf/ORLLrnktM8OAAAOAMQdAGBx3va2t730pS/99Kc/Pdz03d/93a961aue97znqerJnxgAABwQamanfQ4AAOeWCxcu/NIv/dJb3/rWzcOe/vSnv/vd73aO8kUAAJiEXxIAAAvyG7/xG0Nrv/zyyy+77LJ6zXXXXffyl7/8BM8LAAAOD8QdAGApbrjhhje96U355WWXXfbHf/zHN9988y233PK1r33tox/96I//+I/nrddee+3f//3fn8ZpAgDAYUCpDADAUvzIj/zIBz7wgbB8+eWX33DDDY961KPqAbfddtsP//APf/jDHw4vn/WsZ73rXe866bMEAIADgcQdAGAR/vu//ztbu4j89m//ds/aReSud73rK17xivzyH/7hH07m3AAA4BChqwwAwCLUFn7f+973F3/xF0eHPeUpT3nFK16xXq/DyzvuuOPSSy89gdMDAICDA3EHAFiEXAAjIo9//OPvfve7jw5brVavfOUrT+qkAADggKFUBgBgET73uc/l5Qc96EGneCYAAHA+QNwBABbhy1/+cl6+8sorT/FMAADgfIC4AwAsQl2qfvvtt5/imQAAwPkAcQcAWIT73Oc+efmmm246xTMBAIDzAeIOALAID3nIQ/Ly//7v/57imQAAwPkAcQcAWIQf+qEfysv/+I//ePPNN48O++pXv3rPe97zronPfvazJ3WCAABwYCDuAACL8IQnPOEe97hHWL7jjjve8IY3jA57//vf/81vfvP222+//fbb73KXu1x++eUneI4AAHBIIO4AAItwr3vd6+d+7ufyyz/4gz/4oz/6o96Yz33ucy960Yvyy6c97WkndHIAAHCAqJmd9jkAAJxPbrnllsc85jFf/OIX85pnPvOZP/qjP/rYxz72S1/60oc//OE3vvGNuWvk3e52txtvvPExj3nMKZ0sAACcdRB3AIAFuf7663/mZ37mzjvv3DryT//0T3/2Z3/2BE4JAAAOFEplAAAW5GlPe9p111337d/+7RvG3POe93zta1+LtQMAwGZI3AEAFudLX/rSa17zmj//8z/vNXS/733v+/znP/+lL33p/e53v9M6NwAAOBQQdwCAk+MTn/jE//3f/33ta1974AMf+D3f8z33vve9T/uMAADgYEDcAQAAAAAOAGrcAQAAAAAOAMQdAAAAAOAAWJ32CRwGX/jCF2644YarrrrqEY94xOaRn/nMZ/7jP/7j1ltvvfLKKx/3uMep6smcIQAAAADsyB133PGiF73oUY961DXXXHOE3W+55ZZ3v/vdn/rUp7que/jDH/5TP/VT97nPfWY/ySEk7jvxh3/4h8997nOvu+66DWP+53/+5ylPecoVV1zxpCc96RnPeMZVV1314Ac/+B3veMeJnSQAAAAA7ML73ve+N7/5zX/zN3+z745m9spXvvKKK6645pprXv/617/mNa/5hV/4hQc84AFvetObljjPHiTu27n99tv/5E/+ZPOYT37yk49//OPD8xFXq9Vll1325S9/+aabbnr+859/8803v+QlLzmJEwUAAACAbVy4cOH1r3/90fZ92ctedu2114rI3e52tyc84Qm33nrrP//zP992222/+qu/6px74QtfOOuZ9iFx38IXvvCFF7zgBf/1X/+1edjznve8L37xi865t7zlLV/84he/8IUv3HjjjQ972MNE5Ld+67c+8pGPnMjJAgAAAMAmPvKRjzzzmc/8p3/6pyPs+7GPfez3fu/3ROT7v//7P//5z7/vfe/7wAc+cNNNNz34wQ8WkRe/+MWf/exn5z3bHoj7OB//+Mdf9rKXPf3pT7/iiiv+7M/+bPPgv/3bv/3Xf/1XEbn22mt/+Zd/+du+7ducc1dfffV73/vee93rXt77V7/61Sdy1gAAAAAwws033/ykJz3p/ve//1VXXfWe97znaAd53eteZ2b3ute9/vqv//qe97xnWHn55Zdfd911qnrbbbe98Y1vnO+UR0Dcx/ngBz/46le/+vrrr7/tttu2Dn7nO98pIt/5nd/54he/uF7/sIc97JnPfKaIXH/99d/61rcWOlUAAAAA2MzXvva1G2644ZZbbjnyEdbr9fXXXy8iz33uc+9///vXmx796Ef/wA/8gIgc+ZZgRxD3cZ74xCe+tWLz4Pe///0i8hM/8ROXXHJJb9PTn/50EbntttuO9hcZAAAAADg+D33oQ/+/isc//vH7HuFjH/vYl7/8ZRH5sR/7seHWn/zJnxSRf/u3fwtjFoLJqeM84hGPqDs/vuAFL5ga+fWvf/0zn/mMiFx11VXDrU984hPDwn/+538+6UlPmvs0AQAAAGA7l1xySahED9z1rnfd9wh5xuOjH/3o4darr746LHziE584wl3BjpC4H5dPfvKTYeFBD3rQcOu3fdu33fve966HAQAAAMDB8elPfzosPPCBDxxuzStvuumm5c6BxP24fO1rXwsLQdCH3Pve9/7KV77y1a9+dfNxnv3sZ2/Y+hd/8RdHOz0AAABYFDM77VM405zkwygX/Vp84xvfCAv3uMc9hlvzXNWvf/3ry50D4n5cbr311rAw9TeXu93tbiLyzW9+c/NxUHMAAACAM0toNHLppZeO3orc5S53CQvZDJcAcT8uq1W8huv1enTAnXfeKTvcbr7rXe/asDXk8f/PfX7oKKcIAAAAC/CRL33otE/hYPh/V90xj3DthbWc6t83QkQbvG7I7bffHhYuvfTS5c4BcT8u+c8lU40jw/r8B5QpnvWsZ817YgAAAAAwF0H5zOzWW2+9+93v3tuag/atynccmJx6XO53v/uFhc997nPDrWb2+c9/XkTue9/7nuhpAQAAAJwh7Nj/nTIPeMADwsLNN9883Bp6DIpIr8X7vCDux+UhD3lI+JvIpz71qeHWz3zmM3fccYeIPPKRjzzpMwMAAAA4G+ix/zt1cqPw3Bey5hOf+ERY+N7v/d7lzgFxPy5d1z3ucY8TkX/5l38Zbr3xxhvDwvd93/ed6GkBAAAAnBmcHve/U+exj33sZZddJiIf/OAHh1vDyoc+9KHf9V3ftdw5IO4z8NSnPlVE/u7v/u6LX/xib1OYcnrllVcGuQcAAAC4CDkHifull176lKc8RUTe8Y53hHqKzFe+8pX3vOc9IvLTP/3Ti54D4j4DP//zP3/ppZfecccdL3vZy+r1N95441/91V+JyDXXXHNKpwYAAABw+hyWuH/9619/znOe85znPOc3f/M36/UveclLROTTn/70y1/+8rzSe/8rv/Ir3/zmN+9yl7v8+q//+qInRleZGbjiiite8pKXXHvttW95y1u+8Y1vPOc5z7nf/e53ww03vO51r1uv1w9/+MN/7dd+7bTPEQAAAODUOAu1Lrtz++23v/Od7xSRRzziEb//+7+f11999dXXXHPNm9/85te+9rUf+tCHnvrUp37jG99473vf+9GPflREXv3qV19xxRWLnhjiPg+/+7u/++lPf/ptb3vb29/+9re//e15/UMf+tDrr79+2DMIAAAA4OLhoLx9E294wxtE5M1vfvOHPvShD30oNvJfrVavetWrevH8EijP6d2F3/md3xGRJz/5yT/4gz+4Ydh111331re+9eMf//i3vvWtK6+88hnPeMYLX/jCWdp5huc38QAmAACAs0N4ABMqtZngML97l+OWZ7/8di/zXe2PfOQjX/nKV77jO77jMY95zHDrnXfeGaT87ne/+9VXXz0c8O///u9/+Zd/+alPfarrukc+8pHPfvazH/SgB81yYptB3A8DxB0AAOCsgbjvQnCYVx9b3F86q7gfKJTKAAAAAMCynJtSmdMFcQcAAACAZVHMfQ4QdwAAAABYFhqQzwLiDgAAAADLQuA+C4g7AAAAACyL04t6UulcIO4AAAAAsCwk7rOAuAMAAADAsiDus4C4AwAAAMCy0FVmFhB3AAAAAFgWusrMAuIOAAAAAMtC4j4LiDsAAAAALAvePguIOwAAAAAsC4n7LCDuAAAAALAsePssIO4AAAAAsCwOc58DxB0AAAAAlgVvnwXEHQAAAACWhRr3WUDcAQAAAGBZ8PZZQNwBAAAAYFkQ91lA3AEAAABg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", + "text/html": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "nprocs = (4, 4) # nprocs (x, y) dim\n", + "vizme2D_mpi(nprocs)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47059edf-bb8c-45a1-9aff-8e3c43abdd7b", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Julia 1.9.4", + "language": "julia", + "name": "julia-1.9.4" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.9.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/parts/mpi/visualize_mpi.jl b/parts/mpi/visualize_mpi.jl new file mode 100644 index 0000000..7620719 --- /dev/null +++ b/parts/mpi/visualize_mpi.jl @@ -0,0 +1,36 @@ +# Visualisation script for the 2D MPI solver +using CairoMakie +using JLD2 + +function vizme2D_mpi(nprocs) + C = [] + lx = ly = 0.0 + ip = 1 + for ipx in 1:nprocs[1] + for ipy in 1:nprocs[2] + C_loc, lxy = load("out_$(ip-1).jld2", "C", "lxy") + nx_i, ny_i = size(C_loc, 1), size(C_loc, 2) + ix1, iy1 = 1 + (ipx - 1) * nx_i, 1 + (ipy - 1) * ny_i + if ip == 1 + C = zeros(nprocs[1] * nx_i, nprocs[2] * ny_i) + lx, ly = lxy + end + C[ix1:ix1+nx_i-1, iy1:iy1+ny_i-1] .= C_loc + ip += 1 + end + end + xc, yc = LinRange.(0, (lx, ly), size(C)) + fig = Figure(; size=(500, 400), fontsize=14) + ax = Axis(fig[1, 1][1, 1]; aspect=DataAspect(), title="C") + hm = heatmap!(ax, xc, yc, C; colormap=:turbo, colorrange=(0, 1)) + cb = Colorbar(fig[1, 1][1, 2], hm) + if isinteractive() + display(fig) + else + save("visualization.png", fig) + end + return +end + +nprocs = (2, 2) # nprocs (x, y) dim +vizme2D_mpi(nprocs)