##// END OF EJS Templates
update parallel example notebooks to remove `%pylab`
MinRK -
Show More
@@ -1,430 +1,412 b''
1 1 {
2 2 "metadata": {
3 "name": "InteractiveMPI-publish-data"
3 "name": ""
4 4 },
5 5 "nbformat": 3,
6 6 "nbformat_minor": 0,
7 7 "worksheets": [
8 8 {
9 9 "cells": [
10 10 {
11 11 "cell_type": "heading",
12 12 "level": 1,
13 13 "metadata": {},
14 14 "source": [
15 15 "Interactive visualization of MPI simulaitons"
16 16 ]
17 17 },
18 18 {
19 19 "cell_type": "markdown",
20 20 "metadata": {},
21 21 "source": [
22 22 "In this example, which builds on our previous one of interactive MPI monitoring, we now demonstrate how to use the IPython data publication APIs."
23 23 ]
24 24 },
25 25 {
26 26 "cell_type": "heading",
27 27 "level": 2,
28 28 "metadata": {},
29 29 "source": [
30 30 "Load IPython support for working with MPI tasks"
31 31 ]
32 32 },
33 33 {
34 34 "cell_type": "markdown",
35 35 "metadata": {},
36 36 "source": [
37 37 "If you have not done so yet, use [the cluster tab in the Dashboard](/#tab2) to start your `mpi` cluster, it should be OK to leave the number of engines field empty (IPython will auto-detect the number of cores on your machine), unless you want to limit the run to use less cores than available in total. Once your MPI cluster is running, you can proceed with the rest of the code.\n",
38 38 "\n",
39 39 "We begin by creating a cluster client that gives us a local handle on the engines running in the (possibly remote) MPI cluster. From the client we make a `view` object, which we set to use blocking mode by default as it is more convenient for interactive control. Since the real computation will be done over MPI without IPython intervention, setting the default behavior to be blocking will have no significant performance impact.\n",
40 40 "\n",
41 41 "**Note:** if on first try the following cell gives you an error message, wait a few seconds and run it again. It's possible that the system is simply initializing all your MPI engines, which may take a bit of time to be completely ready if you hadn't used any MPI libraries recently and the disk cache is cold."
42 42 ]
43 43 },
44 44 {
45 45 "cell_type": "code",
46 46 "collapsed": false,
47 47 "input": [
48 48 "from IPython.parallel import Client, error\n",
49 49 "cluster = Client(profile=\"mpi\")\n",
50 50 "view = cluster[:]\n",
51 51 "view.block = True"
52 52 ],
53 53 "language": "python",
54 54 "metadata": {},
55 55 "outputs": [],
56 56 "prompt_number": 1
57 57 },
58 58 {
59 59 "cell_type": "markdown",
60 60 "metadata": {},
61 61 "source": [
62 62 "Let's also load the plotting and numerical libraries so we have them ready for visualization later on."
63 63 ]
64 64 },
65 65 {
66 66 "cell_type": "code",
67 67 "collapsed": false,
68 68 "input": [
69 "%pylab inline"
69 "%matplotlib inline\n",
70 "import numpy as np\n",
71 "import matplotlib.pyplot as plt"
70 72 ],
71 73 "language": "python",
72 74 "metadata": {},
73 "outputs": [
74 {
75 "output_type": "stream",
76 "stream": "stdout",
77 "text": [
78 "\n",
79 "Welcome to pylab, a matplotlib-based Python environment [backend: module://IPython.kernel.zmq.pylab.backend_inline].\n",
80 "For more information, type 'help(pylab)'.\n"
81 ]
82 }
83 ],
75 "outputs": [],
84 76 "prompt_number": 2
85 77 },
86 78 {
87 79 "cell_type": "markdown",
88 80 "metadata": {},
89 81 "source": [
90 82 "Now, we load the MPI libraries into the engine namespaces, and do a simple printing of their MPI rank information to verify that all nodes are operational and they match our cluster's real capacity. \n",
91 83 "\n",
92 84 "Here, we are making use of IPython's special `%%px` cell magic, which marks the entire cell for parallel execution. This means that the code below will not run in this notebook's kernel, but instead will be sent to *all* engines for execution there. In this way, IPython makes it very natural to control your entire cluster from within the notebook environment:"
93 85 ]
94 86 },
95 87 {
96 88 "cell_type": "code",
97 89 "collapsed": false,
98 90 "input": [
99 91 "%%px\n",
100 92 "# MPI initialization, library imports and sanity checks on all engines\n",
101 93 "from mpi4py import MPI\n",
102 94 "# Load data publication API so engines can send data to notebook client\n",
103 95 "from IPython.kernel.zmq.datapub import publish_data\n",
104 96 "import numpy as np\n",
105 97 "import time\n",
106 98 "\n",
107 99 "mpi = MPI.COMM_WORLD\n",
108 100 "bcast = mpi.bcast\n",
109 101 "barrier = mpi.barrier\n",
110 102 "rank = mpi.rank\n",
111 103 "print \"MPI rank: %i/%i\" % (mpi.rank,mpi.size)"
112 104 ],
113 105 "language": "python",
114 106 "metadata": {},
115 107 "outputs": [
116 108 {
117 109 "output_type": "stream",
118 110 "stream": "stdout",
119 111 "text": [
120 112 "[stdout:0] MPI rank: 2/4\n",
121 "[stdout:1] MPI rank: 1/4\n",
122 "[stdout:2] MPI rank: 0/4\n",
123 "[stdout:3] MPI rank: 3/4\n"
113 "[stdout:1] MPI rank: 0/4\n",
114 "[stdout:2] MPI rank: 3/4\n",
115 "[stdout:3] MPI rank: 1/4\n"
124 116 ]
125 117 }
126 118 ],
127 119 "prompt_number": 3
128 120 },
129 121 {
130 122 "cell_type": "markdown",
131 123 "metadata": {},
132 124 "source": [
133 125 "We write a utility that reorders a list according to the mpi ranks of the engines, since all gather operations will return data in engine id order, not in MPI rank order. We'll need this later on when we want to reassemble in IPython data structures coming from all the engines: IPython will collect the data ordered by engine ID, but our code creates data structures based on MPI rank, so we need to map from one indexing scheme to the other. This simple function does the job:"
134 126 ]
135 127 },
136 128 {
137 129 "cell_type": "code",
138 130 "collapsed": false,
139 131 "input": [
140 132 "ranks = view['rank']\n",
141 133 "engine_mpi = np.argsort(ranks)\n",
142 134 "\n",
143 135 "def mpi_order(seq):\n",
144 136 " \"\"\"Return elements of a sequence ordered by MPI rank.\n",
145 137 "\n",
146 138 " The input sequence is assumed to be ordered by engine ID.\"\"\"\n",
147 139 " return [seq[x] for x in engine_mpi]"
148 140 ],
149 141 "language": "python",
150 142 "metadata": {},
151 143 "outputs": [],
152 144 "prompt_number": 4
153 145 },
154 146 {
155 147 "cell_type": "heading",
156 148 "level": 2,
157 149 "metadata": {},
158 150 "source": [
159 151 "MPI simulation example"
160 152 ]
161 153 },
162 154 {
163 155 "cell_type": "markdown",
164 156 "metadata": {},
165 157 "source": [
166 158 "This is our 'simulation', a toy example that computes $\\sin(f(x^2+y^2))$ for a slowly increasing frequency $f$ over a gradually refined mesh. In a real-world example, there typically is a 'simulate' method that, afer setting up initial parameters, runs the entire computation. But having this simple example will be sufficient to see something that changes visually as the computation evolves and that is quick enough for us to test.\n",
167 159 "\n",
168 160 "And while simple, this example has a realistic decomposition of the spatial domain in one array per MPI node that requires care in reordering the data for visualization, as would be needed in a real-world application (unless your code accumulates data in the rank 0 node that you can grab directly)."
169 161 ]
170 162 },
171 163 {
172 164 "cell_type": "code",
173 165 "collapsed": false,
174 166 "input": [
175 167 "%%px\n",
176 168 "\n",
177 169 "# Global flag in the namespace\n",
178 170 "stop = False\n",
179 171 "\n",
180 172 "def simulation(nsteps=100, delay=0.1):\n",
181 173 " \"\"\"Toy simulation code, computes sin(f*(x**2+y**2)) for a slowly increasing f\n",
182 174 " over an increasingly fine mesh.\n",
183 175 "\n",
184 176 " The purpose of this code is simply to illustrate the basic features of a typical\n",
185 177 " MPI code: spatial domain decomposition, a solution which is evolving in some \n",
186 178 " sense, and local per-node computation. In this case the nodes only communicate when \n",
187 179 " gathering results for publication.\"\"\"\n",
188 180 " # Problem geometry\n",
189 181 " xmin, xmax = 0, np.pi\n",
190 182 " ymin, ymax = 0, 2*np.pi\n",
191 183 " dy = (ymax-ymin)/mpi.size\n",
192 184 "\n",
193 185 " freqs = np.linspace(0.6, 1, nsteps)\n",
194 186 " for j in range(nsteps):\n",
195 187 " nx, ny = 2+j/4, 2+j/2/mpi.size\n",
196 188 " nyt = mpi.size*ny\n",
197 189 " Xax = np.linspace(xmin, xmax, nx)\n",
198 190 " Yax = np.linspace(ymin+rank*dy, ymin+(rank+1)*dy, ny, endpoint=rank==mpi.size)\n",
199 191 " X, Y = np.meshgrid(Xax, Yax)\n",
200 192 " f = freqs[j]\n",
201 193 " Z = np.cos(f*(X**2 + Y**2))\n",
202 194 " \n",
203 195 " # We are now going to publish data to the clients. We take advantage of fast\n",
204 196 " # MPI communications and gather the Z mesh at the rank 0 node in the Zcat variable:\n",
205 197 " Zcat = mpi.gather(Z, root=0)\n",
206 198 " if mpi.rank == 0:\n",
207 199 " # Then we use numpy's concatenation to construct a single numpy array with the\n",
208 200 " # full mesh that can be sent to the client for visualization:\n",
209 201 " Zcat = np.concatenate(Zcat)\n",
210 202 " # We now can send a dict with the variables we want the client to have access to:\n",
211 203 " publish_data(dict(Z=Zcat, nx=nx, nyt=nyt, j=j, nsteps=nsteps))\n",
212 204 " \n",
213 205 " # We add a small delay to simulate that a real-world computation\n",
214 206 " # would take much longer, and we ensure all nodes are synchronized\n",
215 207 " time.sleep(delay)\n",
216 208 " # The stop flag can be set remotely via IPython, allowing the simulation to be\n",
217 209 " # cleanly stopped from the outside\n",
218 210 " if stop:\n",
219 211 " break"
220 212 ],
221 213 "language": "python",
222 214 "metadata": {},
223 215 "outputs": [],
224 "prompt_number": 26
216 "prompt_number": 5
225 217 },
226 218 {
227 219 "cell_type": "heading",
228 220 "level": 2,
229 221 "metadata": {},
230 222 "source": [
231 223 "IPython tools to interactively monitor and plot the MPI results"
232 224 ]
233 225 },
234 226 {
235 227 "cell_type": "markdown",
236 228 "metadata": {},
237 229 "source": [
238 230 "We now define a local (to this notebook) plotting function that fetches data from the engines' global namespace. Once it has retrieved the current state of the relevant variables, it produces and returns a figure:"
239 231 ]
240 232 },
241 233 {
242 234 "cell_type": "code",
243 235 "collapsed": false,
244 236 "input": [
245 "from IPython.display import clear_output\n",
237 "from IPython.display import display, clear_output\n",
246 238 "\n",
247 239 "def plot_current_results(ar, in_place=True):\n",
248 240 " \"\"\"Makes a blocking call to retrieve remote data and displays the solution mesh\n",
249 241 " as a contour plot.\n",
250 242 " \n",
251 243 " Parameters\n",
252 244 " ----------\n",
253 245 " ar : async result object\n",
254 246 "\n",
255 247 " in_place : bool\n",
256 248 " By default it calls clear_output so that new plots replace old ones. Set\n",
257 249 " to False to allow keeping of all previous outputs.\n",
258 250 " \"\"\"\n",
259 251 " # Read data from MPI rank 0 engine\n",
260 252 " data = ar.data[engine_mpi[0]]\n",
261 253 " \n",
262 254 " try:\n",
263 255 " nx, nyt, j, nsteps = [data[k] for k in ['nx', 'nyt', 'j', 'nsteps']]\n",
264 256 " Z = data['Z']\n",
265 257 " except KeyError:\n",
266 258 " # This can happen if we read from the engines so quickly that the data \n",
267 259 " # hasn't arrived yet.\n",
268 260 " fig, ax = plt.subplots()\n",
269 261 " ax.plot([])\n",
270 262 " ax.set_title(\"No data yet\")\n",
271 263 " display(fig)\n",
272 264 " return fig\n",
273 265 " else:\n",
274 266 " \n",
275 267 " fig, ax = plt.subplots()\n",
276 268 " ax.contourf(Z)\n",
277 269 " ax.set_title('Mesh: %i x %i, step %i/%i' % (nx, nyt, j+1, nsteps))\n",
278 " axis('off')\n",
270 " plt.axis('off')\n",
279 271 " # We clear the notebook output before plotting this if in-place \n",
280 272 " # plot updating is requested\n",
281 273 " if in_place:\n",
282 274 " clear_output()\n",
283 275 " display(fig)\n",
284 276 " \n",
285 277 " return fig"
286 278 ],
287 279 "language": "python",
288 280 "metadata": {},
289 281 "outputs": [],
290 "prompt_number": 33
282 "prompt_number": 6
291 283 },
292 284 {
293 285 "cell_type": "markdown",
294 286 "metadata": {},
295 287 "source": [
296 288 "Finally, this is a convenience wrapper around the plotting code so that we can interrupt monitoring at any point, and that will provide basic timing information:"
297 289 ]
298 290 },
299 291 {
300 292 "cell_type": "code",
301 293 "collapsed": false,
302 294 "input": [
303 295 "def monitor_simulation(ar, refresh=5.0, plots_in_place=True):\n",
304 296 " \"\"\"Monitor the simulation progress and call plotting routine.\n",
305 297 "\n",
306 298 " Supress KeyboardInterrupt exception if interrupted, ensure that the last \n",
307 299 " figure is always displayed and provide basic timing and simulation status.\n",
308 300 "\n",
309 301 " Parameters\n",
310 302 " ----------\n",
311 303 " ar : async result object\n",
312 304 "\n",
313 305 " refresh : float\n",
314 306 " Refresh interval between calls to retrieve and plot data. The default\n",
315 307 " is 5s, adjust depending on the desired refresh rate, but be aware that \n",
316 308 " very short intervals will start having a significant impact.\n",
317 309 "\n",
318 310 " plots_in_place : bool\n",
319 311 " If true, every new figure replaces the last one, producing a (slow)\n",
320 312 " animation effect in the notebook. If false, all frames are plotted\n",
321 313 " in sequence and appended in the output area.\n",
322 314 " \"\"\"\n",
323 315 " import datetime as dt, time\n",
324 316 " \n",
325 317 " if ar.ready():\n",
326 318 " plot_current_results(ar, in_place=plots_in_place)\n",
327 319 " plt.close('all')\n",
328 320 " print 'Simulation has already finished, no monitoring to do.'\n",
329 321 " return\n",
330 322 " \n",
331 323 " t0 = dt.datetime.now()\n",
332 324 " fig = None\n",
333 325 " try:\n",
334 326 " while not ar.ready():\n",
335 327 " fig = plot_current_results(ar, in_place=plots_in_place)\n",
336 328 " plt.close('all') # prevent re-plot of old figures\n",
337 329 " time.sleep(refresh)\n",
338 330 " except (KeyboardInterrupt, error.TimeoutError):\n",
339 331 " msg = 'Monitoring interrupted, simulation is ongoing!'\n",
340 332 " else:\n",
341 333 " msg = 'Simulation completed!'\n",
342 334 " tmon = dt.datetime.now() - t0\n",
343 335 " if plots_in_place and fig is not None:\n",
344 336 " clear_output()\n",
345 337 " plt.close('all')\n",
346 338 " display(fig)\n",
347 339 " print msg\n",
348 340 " print 'Monitored for: %s.' % tmon"
349 341 ],
350 342 "language": "python",
351 343 "metadata": {},
352 344 "outputs": [],
353 "prompt_number": 34
345 "prompt_number": 7
354 346 },
355 347 {
356 348 "cell_type": "heading",
357 349 "level": 2,
358 350 "metadata": {},
359 351 "source": [
360 352 "Interactive monitoring in the client of the published data"
361 353 ]
362 354 },
363 355 {
364 356 "cell_type": "markdown",
365 357 "metadata": {},
366 358 "source": [
367 359 "Now, we can monitor the published data. We submit the simulation for execution as an asynchronous task, and then monitor this task at any frequency we desire."
368 360 ]
369 361 },
370 362 {
371 363 "cell_type": "code",
372 364 "collapsed": false,
373 365 "input": [
374 366 "# Create the local client that controls our IPython cluster with MPI support\n",
375 367 "from IPython.parallel import Client\n",
376 368 "cluster = Client(profile=\"mpi\")\n",
377 369 "# We make a view that encompasses all the engines\n",
378 370 "view = cluster[:]\n",
379 371 "# And now we call on all available nodes our simulation routine,\n",
380 372 "# as an asynchronous task\n",
381 "nsteps = 10\n",
382 "delay = 0.1\n",
383 "ar = view.apply_async(lambda : simulation(nsteps, delay))"
373 "ar = view.apply_async(lambda : simulation(nsteps=10, delay=0.1))"
384 374 ],
385 375 "language": "python",
386 376 "metadata": {},
387 377 "outputs": [],
388 "prompt_number": 38
378 "prompt_number": 8
389 379 },
390 380 {
391 381 "cell_type": "code",
392 382 "collapsed": false,
393 383 "input": [
394 384 "monitor_simulation(ar, refresh=1)"
395 385 ],
396 386 "language": "python",
397 387 "metadata": {},
398 388 "outputs": [
399 389 {
400 390 "metadata": {},
401 391 "output_type": "display_data",
402 "png": "iVBORw0KGgoAAAANSUhEUgAAAXIAAAEICAYAAABCnX+uAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJztnXusVcX1x78HG1C5CNeKcHk/TB+IUDXWRhStTayPFFsb\nH1SgVVOtta/EalVSmbQYtT8fjW1slT7EirQa+UNFCipVq1QaX6CiERBB0MpTuSiCyvz+oOf0Ps45\ne8/Mmpk1e69PYtJ79z6zN3TOZy3Wnj2rorXWEARBEJKlR+wbEARBENwQkQuCICSOiFwQBCFxROSC\nIAiJIyIXBEFIHBG5IAhC4ojIhW6MGDECjz76aOzbEAQhJyLyBBkxYgR69eqFLVu2dPr94Ycfjh49\nemDdunVO41cqFVQqFavPzp8/H8ceeyxaW1vR1taG7373u9ixY0encx555BEcccQRaGlpwdChQ3Hv\nvfc63S8AvPHGG+jRowf69OlT+++aa67pdM5zzz2HiRMnok+fPhg4cCBuueUW5+v26NEDr7/+uvM4\njbjmmmswfPhw9O3bF5MnT0Z7e3vt2IYNG3D66afj05/+NIYOHYrbbrut2+fnzp2Lc889FwBw4YUX\n4nOf+xz22WcfzJ49u9u5N998M9ra2tC3b19ccMEF2L17d+3Y1q1b8Y1vfAMtLS0YMWIE5s6d6+FP\nK9giIk+QSqWCUaNGdfoyvfjii9i5c6e1gKnYvn07rr76arz99tt45ZVXsGHDBlx22WW14ytWrMC5\n556La6+9Ftu3b8fy5ctx5JFHkl6/vb0d7e3tmD59eu33mzdvximnnIKLL74YW7duxerVq3HSSSeR\nXNPXO3WzZ8/GXXfdhSVLluCtt97Czp078cMf/rB2fMqUKRg9ejQ2btyI+fPn46qrrsJjjz3WaYz5\n8+fjtNNOAwB84QtfwK233oojjjii2zxZuHAhrr/+eixevBhr167F66+/jhkzZtSOX3LJJdh3332x\nceNGzJkzBxdffDFWrFjh5c8tWKCF5BgxYoSeOXOmPuqoo2q/u/TSS/U111yjK5WKXrt2rdZa6w8/\n/FBfeumletiwYXrAgAH6e9/7nt65c6fWWutNmzbp0047Tffr108feOCB+rjjjus0/g033KDHjRun\n+/btq88++2z94YcfWt3rvHnz9GGHHVb7efLkyfrqq6/O9dnrrrtOH3300frjjz/WWmt966236kMP\nPVTv2rWr27lr1qzRlUqldm5XrrzySj1t2jSLP4HWK1eu1BMnTtR9+/bVBx10kD7nnHO01lofd9xx\nulKp6N69e+uWlhZ9zz33aK21fuCBB/T48eN1v3799DHHHKOXL19eG2v48OH62muv1WPGjNGtra36\nvPPOa/h3+81vflP/3//9X+3nJUuW6H333Vfv3LlTt7e360qlojdt2lQ7fuGFF+qpU6fWfv7kk0/0\ngAED9JYtWzqNe+yxx+rZs2d3+t3kyZP19OnTaz8vXrxYDxw4UGut9Y4dO3TPnj31ypUra8enTZum\nr7jiinx/gYJ3JCNPlC996UvYvn07Xn31VXzyySf429/+hilTpnQ654orrsCqVauwbNkyrFq1Chs2\nbMAvfvELAMCNN96IoUOHYvPmzdi4cSOuvfba2ue01rj33nuxcOFCrFmzBsuXL8cdd9xRO97a2ool\nS5bkus/HH38cY8eOrf28dOlSaK0xbtw4DBo0CFOnTsW2bdvqfvbyyy9Hr169MHPmTKxcuRLTp0/H\nnDlz0LNnz4bXGz58OIYOHYrzzz+/U+lp6dKlaG1txYQJEzBgwABMmjQJb775Zq4/w89//nOcfPLJ\nePfdd7Fhw4ZaVvzEE08AAJYvX4729naceeaZeP7553HBBRdg1qxZ2Lp1Ky666CJMmjQJH330UW28\nu+++G4sWLcLq1avx2muvYebMmXWvW6lUOmX7e/bswa5du7By5cra77sef+mll2o///vf/8aoUaNw\n4IEHZv4ZV6xYgfHjx9d+HjduHN555x1s27YNr732Gj71qU/hkEMOqR0fP348Xn755cxxhTCIyBNm\n6tSpuPPOO/Hwww9jzJgxGDx4cO2Y1hqzZs3CTTfdhH79+qGlpQVXXnkl/vrXvwIAevbsibfffhtv\nvPEG9tlnH0yYMKH22Uqlgh/96EcYOHAgWltb8bWvfQ0vvPBC7fi2bdtwzDHHZN7fww8/jDvvvLMW\nPADgzTffxF133YV58+Zh5cqV3coFHalUKrjzzjtxyy234PTTT8fPfvazTrLpSP/+/fHMM89g3bp1\nePbZZ9He3l6rDVevO3v2bNxyyy1Yt24dRo4cicmTJ2f+GYC9f1dvvPEGNmzYgJ49ezb9s99+++24\n6KKLcNRRR6FSqWDatGno1asXnn766dqf6Qc/+AEGDx6M1tZWTJ8+vWG9+eSTT8Yf/vAHrF27Fu+9\n9x6uv/56AMAHH3yAPn36YMKECfjlL3+JXbt24bnnnsO8efOwc+fO2uc7llWy2LFjB/r27Vv7+YAD\nDgAAtLe3Y8eOHbWfq/Tp06dTvV6Ii4g8USqVCqZOnYo5c+Zg9uzZmDZtWqfsbNOmTfjggw9w5JFH\norW1Fa2trTjllFOwefNmAMBll12GQw45BCeddBJGjx5dk0SVgQMH1v73fvvt1+2BZRZPP/00zj33\nXNx3332dMrn9998f5513Hg455BD07t0bV111FR566KGG4wwfPhwnnHAC1q5di0suuaTheb1798YR\nRxyBHj164OCDD8Zvf/tbLFq0CO+//37tumeccQaOPPJI9OrVCzNmzMCSJUtyyehXv/oVtNb44he/\niLFjx+LPf/5zw3PXrl2LG2+8sfZ33traivXr1+Ott96qnTN06NDa/x42bFinYx05//zzMXnyZJxw\nwgk47LDDcOKJJwIAhgwZAgCYM2cO1qxZg6FDh+KSSy7BlClTOgXzBQsW4NRTT8388wFAS0sLtm/f\nXvv5vffeA7BX2F2PVY/36dMn19iCf0TkCTNs2DCMGjUKCxYswBlnnNHp2EEHHYT99tsPK1aswLZt\n27Bt2za8++67tS9kS0sLbrjhBqxevRr3338/brrpJvzjH/+oex3TB6jPP/88Tj/9dNxxxx348pe/\n3OnYuHHjjMaaP38+nn76aXzlK1/BT3/6U6PPAnvLDTbX7ciAAQNw++23Y8OGDbjtttvw/e9/v+FK\nlWHDhmH69Om1v/Nt27Zhx44dOPvss2vndFxVtG7dOgwaNKjuWJVKBUoprFmzBuvWrcOYMWMwZMiQ\nmqyHDRuGBx54ABs3bsS//vUvbNq0CUcffTQA4D//+Q/efvttHH744bn+jIceeminf3UtW7YMAwYM\nQGtrKz7zmc/g448/xqpVqzod71gyEyITrzwv2DJixAj96KOPaq21Xr16tX722We11lp/9NFHnR52\n/vjHP9ZnnXWW3rhxo9Za6/Xr1+uFCxdqrbV+8MEH9cqVK/WePXv0unXrdFtbm37ssce6ja+11jNm\nzNBTpkzJdW8vvviiPvjgg2sP/rrypz/9SY8cOVK//vrr+v3339dnnnlmw4eQmzZt0m1tbXrBggV6\ny5YtetCgQfqhhx6qe+7SpUv1q6++qj/55BO9efNmfdZZZ+kTTzyxdnzx4sW6tbVVv/DCC3r37t36\nJz/5iZ44cWLt+PHHH6+VUnXHvueee/Sbb76ptdb6pZde0vvtt59es2aN1lrrgQMH6kWLFtXOfeaZ\nZ/TQoUP10qVL9Z49e/SOHTv0gw8+qNvb27XWex92jhs3Tq9fv15v2bJFT5gwodNDxo5s3bpVr1q1\nSu/Zs0e//PLLeuzYsXrWrFm146+88orevn273rVrl/7LX/6iDzroIL158+ba3/MFF1zQabzdu3fr\nnTt36mOOOUbPmjVL79y5U+/Zs0drrfXf//53PXDgQL1ixQq9detWffzxx+srr7yy9tlzzjlHT548\nWb///vv6n//8p+7bt69esWJF3fsWwiMiT5Cuoq3y0Ucf6R49enRatXLVVVfpUaNG6QMOOEB//vOf\n17/5zW+01lrffPPNesSIEbp37956yJAheubMmQ3HV0p1Wg3R0tKin3zyybr3dt555+l99tlHt7S0\n1P4bO3Zsp3NmzJih+/fvr/v376+nTZum33333bpjnXHGGfriiy+u/bxgwQI9aNAgvXXr1m7nzp07\nV48cOVL37t1bt7W16W9/+9v6nXfe6XTO7373Oz148GDd2tqqJ02apNevX187Nnr0aP3II4/UvY/L\nL79cDx48WLe0tOjRo0d3kunvf/973dbWpvv166fvvfderfVeKR511FG6X79+uq2tTZ911ll6x44d\nWuu9f7fXXXedHjNmjO7Xr5/+zne+U1tJ1JXXXntNf/azn9X777+/Hj58uL755ps7Hf/1r3+t+/fv\nr3v37q2PO+64WkDXeu+Kl/vuu6/T+ccff7yuVCq6R48eulKp6Eqloh9//PHa8ZtuukkPGDBAH3DA\nAfr888/Xu3fvrh3bunWr/vrXv6579+6thw8frufOnVv3noU4VLSWxhJCuVm/fj3OOeccPPnkk96v\nNXLkSPzxj3+s1bt98PHHH6OtrQ1r1qxBS0uLt+sIfJAauVB6hgwZEkTiodi2bRtmzpwpEi8RInJB\nKBj9+/fHRRddFPs2hIBIaUUQBCFxPuVr4Kci7/mRIhPyvZ9iz8/cPn7/ePO9SX6P/JnhgifOyD6p\nI8rsdADAP5ZafMgzXz4633mq8aFTJs5reOx76L6ZFgBMWrao8YDXNz4EAE9F2jMr13ekwTyvN38b\nzc+6c1FlXJdobmmdcz50wFtGLiJ3x7vY8xBB/oBZAAACBYF6UHx584q8imp+OLTUAf9id/4uNJnH\n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\n5FczVAAAAABJRU5ErkJggg==\n",
392 "png": "iVBORw0KGgoAAAANSUhEUgAAAk4AAAGKCAYAAAD6yM7KAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAGIZJREFUeJzt3XuQV3X9+PHXZ9W4SLYIISERuowojZKOoyRFhuYtFUPU\nMElMSZukpkgdhWrN+1iTZSEKKKGoQ+p4SR1RQi3FqEa3BiEV0dAcZxCRIkSXPd8//Lm/gF1473I+\n+7k9HjPMxOdzzvu8d9f2PDm3TyHLsiwAANiuulJPAACgUggnAIBEwgkAIJFwAgBIJJwAABIJJwCA\nRMIJqtwrr7wSe+21V6mnQQds2rSp1FMA2iGcoAs1NjZGXV1dXHfdde0uc88990RdXV2cddZZXTiz\nNC+++GKMGTMmevfuHZ/4xCdi4sSJ8eabb+a6jffeey+mTp0an/rUp6K+vj6OP/74eOGFF3LdRils\n3LgxTjvttPjkJz+5zeVee+21GD58+GavrVq1Kvbff/+YMGFCm+vMmTMnhg4dGj179ozPfOYz8dBD\nD+U2b2Bzwgm6WI8ePWL27Nntvj9r1qzo2bNnFAqFLpzV9q1bty6OPPLIGDhwYLz00kvx7LPPRnNz\nc5x00km5bueiiy6KhQsXxoIFC+KVV16J4cOHxzHHHBPr16/PdTtd6e23344vfelL8cwzz2z35zp7\n9uw47rjjWv/e1NQUI0aMiHXr1rW57h133BHTpk2LOXPmxJo1a+LKK6+MM844IxYvXpz71wEIJ+hS\nhUIhjj766Fi1alU888wzW73/2muvxcKFC+Pkk0+Ocnuo/5IlS2Lw4MHx61//Ovr06RP9+/eP2bNn\nx7PPPht/+9vfctvOzTffHNdee20MHTo06uvr44orroiIiEcffTS3bdTVde2vvnHjxkXfvn1j5syZ\n2/y5trS0xC233BLf/OY3IyLinXfeiSOOOCK++93vxje+8Y021/3Rj34Uv/rVr+Kzn/1sdO/ePY47\n7ri45JJL4tJLLy3a1wO1TDhBF9t1113j9NNPj1mzZm313s033xxHHnlkDBo0qAQz27Yjjzwynnji\nic1e69atW/To0SNaWlpy207Pnj23eq1QKLT5emeUIkhvvPHG+O1vfxsf+chHtrncww8/HHvvvXcM\nGTIkIiI+9rGPxR//+Me48MIL25z3ihUr4tVXX41jjjlms9ePPfbYWLRoUbz33nv5fRFARAgnKIlJ\nkybF/PnzNzv9lGVZzJkzp/Vow5b+/Oc/x8iRI6NHjx4xcODAuPzyyze7iPjBBx+MAw44IHr27BkH\nHHBALFy4cLP1n3zyyTj44IOjV69ecfDBB8fTTz+92fuHHHJIfP/73+/Q1/G73/0uIiL222+/Nt+/\n+OKLY999940NGza0fo2jR4+OqVOntjvm5MmT44ILLogXX3wx3nnnnZg2bVr06tUrjjjiiOR5vfnm\nmzF27Nior6+PPfbYIy655JKIiJg4cWLstNNOEfHBUaeddtop/vnPf0bEB6civ/Wtb0Xfvn2jV69e\ncdJJJ8Wrr77aOmZjY2P88Ic/jFmzZsWwYcOie/fuMWzYsPjNb36z3fkMGTKkdbvbctNNN2318993\n333bXX7FihUxZMiQ6N69+2av77ffflFXVxerVq3a7jaBjhFO0IWyLItCoRAHHnhg7LPPPnHHHXe0\nvvfoo4/Gxo0b4/jjj9/q6MIzzzwTX/3qV2Pq1Knx1ltvxVNPPRXLli2Lb3/72xERsWbNmjjllFPi\npz/9aaxduzbOPvvsmDlzZuv6q1evjmnTpsXs2bPjjTfeiHHjxsWpp5662RGJoUOHduhI16pVq2LS\npElx8cUXR7du3dpc5vLLL4/evXvHhRdeGBERM2bMiHfeeWebp5GmTJkSu+22WwwdOjR69+4d06dP\nj1tuuSUpPD50/vnnR79+/WLVqlXx2GOPxYIFC2LlypUxZ86c1qNjLS0tsWnTphg0aFA0NzfH4Ycf\nHv3794+lS5fGG2+8EWPHjo1Ro0bF2rVrI+KDo1633npr3HnnnXH77bfHmjVrYvr06XHVVVfFD37w\ng+S5tedf//pXLFmyJE4++eTkdd5+++3Ybbfdtnq9rq4uPvrRj8aaNWt2eF7AFjKgy/z4xz/Ozjjj\njCzLsmzGjBnZoYce2vreKaeckl1yySVZlmXZ1KlTs4kTJ7a+N2LEiGzRokWbjbVx48asV69e2Vtv\nvZU1NTVlu+66a7Z27dqttrly5cqsUChky5Yt2+z1AQMGZEuXLu3U17F69epsv/32y0444YTtLrty\n5cqsvr4+u+mmm7KPf/zj2fPPP7/N5U866aRszJgx2UsvvZS9/fbb2fXXX5/169cvW7lyZfL8Djro\noGzmzJntvl8oFDb7+4wZM7Izzzxzq+XOPvvs7Gc/+1mWZR/87BoaGrINGzZstszzzz+fdevWLVu1\natV257Vo0aJs4MCBbb73k5/8JJsyZUq76/7vfzsfuvPOO7MRI0a0uXy/fv2yJUuWbHdOQMc44gQl\nMn78+Fi6dGksXbo0Vq9eHQ888ECcc845Wy33/vvvx5/+9KcYPXp01NXVtf7p3r17/Pe//41ly5bF\n/vvvH6NHj44hQ4bEWWedFbfddlts3LixdYy+fftudcpnr732irfeeqvD816/fn18+ctfjr59+8b8\n+fO3u/zgwYPj6quvjnPPPTfOP//8dk/rRUQ899xz8fvf/z7mzZsXDQ0NUV9fH+eff34cffTR8ctf\n/jJ5jlOmTInvfOc7cdRRR8XVV18dK1eu3Obyf/jDH2Lu3LmbfX/r6urilltuiWXLlrUuN2bMmDZP\nizU0NGx16rMjWlpa4uabb45JkyZ1aL3evXvHunXr2hxv3bp1sfvuu3d6TkDbhBOUyG677Rannnpq\nzJw5M+bOnRuf+9znWh9UueVt54VCIf7+979HS0vLZn82bdoUI0eOjEKhEPfff3888MAD0dDQEFde\neWWMGjUqmpubI+KDC9K3tMsuu3T4Qun3338/xo0bFy0tLfHQQw9tFRFtybIs5s+fH/vvv3/cdddd\nmwXdlpYvXx5DhgzZar4HHXRQLF++PHmep59+erz00ktx6qmnxuLFi2P48OFt3sX4obq6upgyZUqb\n398PT3kWCoVtfr925PERjzzySAwaNCiGDh3aofUaGhrixRdf3Op7unz58mhpadnuM6OAjhNO0IW2\n3LlOmjQpbr311pg1a1a7Rxt22WWXOOyww+K2227b7PVNmzbF0qVLW//e3NwcI0aMiGnTpkVTU1Ms\nW7Ys18cEZFkWEydOjNdffz0eeeSR6NWrV9J61157bWzYsCGWLFkSPXr0iO9973vtLrvnnnvGihUr\ntnpm07PPPhsDBw5Mnmtzc3MMGDAgzjnnnLjvvvti/PjxMW/evHaX/8IXvhB33333VgHS1NTU+r+z\nLIsHHngg3n333c2WWb58eaxYsSIOO+yw5Pltqa2LwrfUVpg1NDTE4MGDt3rg5YMPPhijR4/e7l18\nQMcJJ+hCWx6xGDFiRAwYMCBWr14dX/nKV9pd7uc//3lcf/318Ytf/CLWrFkTr7zySpx++ukxZcqU\niIh44oknYtiwYdHU1BTvvvtu3HvvvbFp06btxsb/bmfChAnbfKL51KlTY8mSJbFgwYLo3bt30tf7\n17/+Na6++uqYO3dudOvWLebNmxe33XZb3HvvvW0u//nPfz4OO+ywmDBhQqxcuTLWrl0bN9xwQ9x/\n//1x0UUXtS43ffr0GDZsWJtjNDc3x4EHHhjTp0+PDRs2xAsvvBBPP/107LPPPq3L9O/fP5588slY\ns2ZNrF+/Ps4666zo06dPnHzyyfHCCy/Ev//977jpppti9OjR8fLLL7eu9/7778cJJ5wQzz33XPzn\nP/+Jxx9/PMaOHRuTJ0+OPffcM+l7sqU33ngjFi9eHKeccso2l2vvaNdll10WkydPjqeffjo2bNgQ\nDz74YFx11VXR2NjYqfkA2yacoAsVCoU2jzp9/etfj1122aXd5Q4++OB44okn4u67744999wzRowY\nEXvssUfcddddEfHBEZMzzzwzTjzxxOjdu3dcc801cd9990W/fv1ax2tvPh/6xz/+0XprflsWL14c\nL7/8cgwYMGCra4Hmzp271fLr16+Pr33ta3H55Ze3PpdoyJAhcd1118U555wTr7/+epvbmT9/fgwe\nPDgOP/zwGDx4cNx3333x+OOPR0NDQ+syjz32WLt3n+28885xww03xK233hq77757fPGLX4wxY8bE\n5MmTW5e59NJL4/jjj49Pf/rT8eabb0ZdXV0sWrQoBg0aFCNHjoz+/fvH/Pnz45FHHom999679Xs1\nYcKEGDduXJx22mnRp0+fOO+88+KCCy6Ia6+9tt3v25a2/FnMmTMnxo8fv92jQ239txMRcdppp8UV\nV1zRGn/Tpk2LefPmxaGHHpo8JyBdIevoRQ4AJdTS0hJ9+/aNhQsXxoEHHthl27300kujubk5Lrvs\nslzHXbt2bWzatCn69OmT67hAcexc6gkAdMRf/vKXqK+v79JoKqb6+vpSTwHoAKfqgIpyyCGHbHbd\nEUBXEk4AiXbkkQNAdXCNEwBAotyucXqqg/8SGzk+ry0DABXtou0vkrvhnTtulNsRp46Ek2gCAHLX\nkQCrlHASTQBAyd3eufzp0ovDRRMAUMm65DlOggkAqAZFP+IkmgCAalHUcBJNAEA1KVo4iSYAoNoU\nJZxEEwBQjXK9OFwwAQDVLLcjTqIJAKh2PuQXACCRcAIASCScAAASCScAgETCCQAgkXACAEgknAAA\nEgknAIBEwgkAIJFwAgBIJJwAABIJJwCARMIJACCRcAIASCScAAASCScAgETCCQAgkXACAEgknAAA\n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\n",
403 393 "text": [
404 "<matplotlib.figure.Figure at 0x57a9ad0>"
394 "<matplotlib.figure.Figure at 0x10b00b350>"
405 395 ]
406 396 },
407 397 {
408 398 "output_type": "stream",
409 399 "stream": "stdout",
410 400 "text": [
411 401 "Simulation completed!\n",
412 "Monitored for: 0:00:39.732973.\n"
402 "Monitored for: 0:00:01.229672.\n"
413 403 ]
414 404 }
415 405 ],
416 "prompt_number": 40
417 },
418 {
419 "cell_type": "code",
420 "collapsed": false,
421 "input": [],
422 "language": "python",
423 "metadata": {},
424 "outputs": []
406 "prompt_number": 9
425 407 }
426 408 ],
427 409 "metadata": {}
428 410 }
429 411 ]
430 412 } No newline at end of file
@@ -1,555 +1,543 b''
1 1 {
2 2 "metadata": {
3 "name": "InteractiveMPI"
3 "name": ""
4 4 },
5 5 "nbformat": 3,
6 6 "nbformat_minor": 0,
7 7 "worksheets": [
8 8 {
9 9 "cells": [
10 10 {
11 11 "cell_type": "heading",
12 12 "level": 1,
13 13 "metadata": {
14 14 "slideshow": {
15 15 "slide_start": false
16 16 }
17 17 },
18 18 "source": [
19 19 "Interactive monitoring of a parallel MPI simulation with the IPython Notebook"
20 20 ]
21 21 },
22 22 {
23 23 "cell_type": "code",
24 24 "collapsed": false,
25 25 "input": [
26 "%pylab inline\n",
26 "%matplotlib inline\n",
27 "import numpy as np\n",
28 "import matplotlib.pyplot as plt\n",
29 "\n",
30 "from IPython.display import display\n",
27 31 "from IPython.parallel import Client, error\n",
32 "\n",
28 33 "cluster = Client(profile=\"mpi\")\n",
29 34 "view = cluster[:]\n",
30 35 "view.block = True"
31 36 ],
32 37 "language": "python",
33 38 "metadata": {
34 39 "slideshow": {
35 40 "slide_start": false
36 41 }
37 42 },
38 "outputs": [
39 {
40 "output_type": "stream",
41 "stream": "stdout",
42 "text": [
43 "\n",
44 "Welcome to pylab, a matplotlib-based Python environment [backend: module://IPython.kernel.zmq.pylab.backend_inline].\n",
45 "For more information, type 'help(pylab)'.\n"
46 ]
47 }
48 ],
49 "prompt_number": 12
43 "outputs": [],
44 "prompt_number": 1
50 45 },
51 46 {
52 47 "cell_type": "code",
53 48 "collapsed": false,
54 49 "input": [
55 50 "cluster.ids"
56 51 ],
57 52 "language": "python",
58 53 "metadata": {},
59 54 "outputs": [
60 55 {
61 56 "metadata": {},
62 57 "output_type": "pyout",
63 "prompt_number": 13,
58 "prompt_number": 2,
64 59 "text": [
65 60 "[0, 1, 2, 3]"
66 61 ]
67 62 }
68 63 ],
69 "prompt_number": 13
64 "prompt_number": 2
70 65 },
71 66 {
72 67 "cell_type": "markdown",
73 68 "metadata": {
74 69 "slideshow": {
75 70 "slide_start": false
76 71 }
77 72 },
78 73 "source": [
79 74 "Now, we load the MPI libraries into the engine namespaces, and do a simple printing of their MPI rank information to verify that all nodes are operational and they match our cluster's real capacity. \n",
80 75 "\n",
81 76 "Here, we are making use of IPython's special `%%px` cell magic, which marks the entire cell for parallel execution. This means that the code below will not run in this notebook's kernel, but instead will be sent to *all* engines for execution there. In this way, IPython makes it very natural to control your entire cluster from within the notebook environment:"
82 77 ]
83 78 },
84 79 {
85 80 "cell_type": "code",
86 81 "collapsed": false,
87 82 "input": [
88 83 "%%px\n",
89 84 "# MPI initialization, library imports and sanity checks on all engines\n",
90 85 "from mpi4py import MPI\n",
91 86 "import numpy as np\n",
92 87 "import time\n",
93 88 "\n",
94 89 "mpi = MPI.COMM_WORLD\n",
95 90 "bcast = mpi.bcast\n",
96 91 "barrier = mpi.barrier\n",
97 92 "rank = mpi.rank\n",
98 93 "print \"MPI rank: %i/%i\" % (mpi.rank,mpi.size)"
99 94 ],
100 95 "language": "python",
101 96 "metadata": {
102 97 "slideshow": {
103 98 "slide_start": false
104 99 }
105 100 },
106 101 "outputs": [
107 102 {
108 103 "output_type": "stream",
109 104 "stream": "stdout",
110 105 "text": [
111 106 "[stdout:0] MPI rank: 3/4\n",
112 107 "[stdout:1] MPI rank: 2/4\n",
113 108 "[stdout:2] MPI rank: 0/4\n",
114 109 "[stdout:3] MPI rank: 1/4\n"
115 110 ]
116 111 }
117 112 ],
118 "prompt_number": 14
113 "prompt_number": 3
119 114 },
120 115 {
121 116 "cell_type": "markdown",
122 117 "metadata": {
123 118 "slideshow": {
124 119 "slide_start": false
125 120 }
126 121 },
127 122 "source": [
128 123 "We write a utility that reorders a list according to the mpi ranks of the engines, since all gather operations will return data in engine id order, not in MPI rank order. We'll need this later on when we want to reassemble in IPython data structures coming from all the engines: IPython will collect the data ordered by engine ID, but our code creates data structures based on MPI rank, so we need to map from one indexing scheme to the other. This simple function does the job:"
129 124 ]
130 125 },
131 126 {
132 127 "cell_type": "code",
133 128 "collapsed": false,
134 129 "input": [
135 130 "ranks = view['rank']\n",
136 131 "rank_indices = np.argsort(ranks)\n",
137 132 "\n",
138 133 "def mpi_order(seq):\n",
139 134 " \"\"\"Return elements of a sequence ordered by MPI rank.\n",
140 135 "\n",
141 136 " The input sequence is assumed to be ordered by engine ID.\"\"\"\n",
142 137 " return [seq[x] for x in rank_indices]"
143 138 ],
144 139 "language": "python",
145 140 "metadata": {
146 141 "slideshow": {
147 142 "slide_start": false
148 143 }
149 144 },
150 145 "outputs": [],
151 "prompt_number": 15
146 "prompt_number": 4
152 147 },
153 148 {
154 149 "cell_type": "heading",
155 150 "level": 2,
156 151 "metadata": {
157 152 "slideshow": {
158 153 "slide_start": false
159 154 }
160 155 },
161 156 "source": [
162 157 "MPI simulation example"
163 158 ]
164 159 },
165 160 {
166 161 "cell_type": "markdown",
167 162 "metadata": {
168 163 "slideshow": {
169 164 "slide_start": false
170 165 }
171 166 },
172 167 "source": [
173 168 "This is our 'simulation', a toy example that computes $\\sin(f(x^2+y^2))$ for a slowly increasing frequency $f$ over a gradually refined mesh. In a real-world example, there typically is a 'simulate' method that, afer setting up initial parameters, runs the entire computation. But having this simple example will be sufficient to see something that changes visually as the computation evolves and that is quick enough for us to test.\n",
174 169 "\n",
175 170 "And while simple, this example has a realistic decomposition of the spatial domain in one array per MPI node that requires care in reordering the data for visualization, as would be needed in a real-world application (unless your code accumulates data in the rank 0 node that you can grab directly)."
176 171 ]
177 172 },
178 173 {
179 174 "cell_type": "code",
180 175 "collapsed": false,
181 176 "input": [
182 177 "%%px\n",
183 178 "\n",
184 179 "stop = False\n",
185 180 "nsteps = 100\n",
186 181 "delay = 0.1\n",
187 182 "\n",
188 183 "xmin, xmax = 0, np.pi\n",
189 184 "ymin, ymax = 0, 2*np.pi\n",
190 185 "dy = (ymax-ymin)/mpi.size\n",
191 186 "\n",
192 187 "def simulation():\n",
193 188 " \"\"\"Toy simulation code, computes sin(f*(x**2+y**2)) for a slowly increasing f\n",
194 189 " over an increasingly fine mesh.\n",
195 190 "\n",
196 191 " The purpose of this code is simply to illustrate the basic features of a typical\n",
197 192 " MPI code: spatial domain decomposition, a solution which is evolving in some \n",
198 193 " sense, and local per-node computation. In this case the nodes don't really\n",
199 194 " communicate at all.\n",
200 195 " \"\"\"\n",
201 196 " # By making these few variables global, we allow the IPython client to access them\n",
202 197 " # remotely for interactive introspection\n",
203 198 " global j, Z, nx, nyt\n",
204 199 " freqs = np.linspace(0.6, 1, nsteps)\n",
205 200 " for j in range(nsteps):\n",
206 201 " nx, ny = 2+j/4, 2+j/2/mpi.size\n",
207 202 " nyt = mpi.size*ny\n",
208 203 " Xax = np.linspace(xmin, xmax, nx)\n",
209 204 " Yax = np.linspace(ymin+rank*dy, ymin+(rank+1)*dy, ny, endpoint=rank==mpi.size)\n",
210 205 " X, Y = np.meshgrid(Xax, Yax)\n",
211 206 " f = freqs[j]\n",
212 207 " Z = np.cos(f*(X**2 + Y**2))\n",
213 208 " # We add a small delay to simulate that a real-world computation\n",
214 209 " # would take much longer, and we ensure all nodes are synchronized\n",
215 210 " time.sleep(delay)\n",
216 211 " # The stop flag can be set remotely via IPython, allowing the simulation to be\n",
217 212 " # cleanly stopped from the outside\n",
218 213 " if stop:\n",
219 214 " break"
220 215 ],
221 216 "language": "python",
222 217 "metadata": {
223 218 "slideshow": {
224 219 "slide_start": false
225 220 }
226 221 },
227 222 "outputs": [],
228 "prompt_number": 17
223 "prompt_number": 5
229 224 },
230 225 {
231 226 "cell_type": "heading",
232 227 "level": 2,
233 228 "metadata": {
234 229 "slideshow": {
235 230 "slide_start": false
236 231 }
237 232 },
238 233 "source": [
239 234 "IPython tools to interactively monitor and plot the MPI results"
240 235 ]
241 236 },
242 237 {
243 238 "cell_type": "markdown",
244 239 "metadata": {
245 240 "slideshow": {
246 241 "slide_start": false
247 242 }
248 243 },
249 244 "source": [
250 245 "We now define a local (to this notebook) plotting function that fetches data from the engines' global namespace. Once it has retrieved the current state of the relevant variables, it produces and returns a figure:"
251 246 ]
252 247 },
253 248 {
254 249 "cell_type": "code",
255 250 "collapsed": false,
256 251 "input": [
257 252 "from IPython.display import clear_output\n",
258 253 "\n",
259 254 "def plot_current_results(in_place=True):\n",
260 255 " \"\"\"Makes a blocking call to retrieve remote data and displays the solution mesh\n",
261 256 " as a contour plot.\n",
262 257 " \n",
263 258 " Parameters\n",
264 259 " ----------\n",
265 260 " in_place : bool\n",
266 261 " By default it calls clear_output so that new plots replace old ones. Set\n",
267 262 " to False to allow keeping of all previous outputs.\n",
268 263 " \"\"\"\n",
269 264 " \n",
270 265 " # We make a blocking call to load the remote data from the simulation into simple named \n",
271 266 " # variables we can read from the engine namespaces\n",
272 267 " #view.apply_sync(load_simulation_globals)\n",
273 268 " # And now we can use the view to read these variables from all the engines. Then we\n",
274 269 " # concatenate all of them into single arrays for local plotting\n",
275 270 " try:\n",
276 271 " Z = np.concatenate(mpi_order(view['Z']))\n",
277 272 " except ValueError:\n",
278 273 " print \"dimension mismatch in Z, not plotting\"\n",
279 274 " ax = plt.gca()\n",
280 275 " return ax.figure\n",
281 276 " \n",
282 277 " nx, nyt, j, nsteps = view.pull(['nx', 'nyt', 'j', 'nsteps'], targets=0)\n",
283 278 " fig, ax = plt.subplots()\n",
284 279 " ax.contourf(Z)\n",
285 280 " ax.set_title('Mesh: %i x %i, step %i/%i' % (nx, nyt, j+1, nsteps))\n",
286 " axis('off')\n",
281 " plt.axis('off')\n",
287 282 " # We clear the notebook output before plotting this if in-place plot updating is requested\n",
288 283 " if in_place:\n",
289 284 " clear_output()\n",
290 285 " display(fig)\n",
291 286 " return fig"
292 287 ],
293 288 "language": "python",
294 289 "metadata": {
295 290 "slideshow": {
296 291 "slide_start": false
297 292 }
298 293 },
299 294 "outputs": [],
300 "prompt_number": 18
295 "prompt_number": 6
301 296 },
302 297 {
303 298 "cell_type": "markdown",
304 299 "metadata": {
305 300 "slideshow": {
306 301 "slide_start": false
307 302 }
308 303 },
309 304 "source": [
310 305 "It will also be useful to be able to check whether the simulation is still alive or not. Below we will wrap the main simulation function into a thread to allow IPython to pull data from the engines, and we will call this object `simulation_thread`. So to check whether the code is still running, all we have to do is call the `is_alive` method on all of our engines and see whether any of them returns True:"
311 306 ]
312 307 },
313 308 {
314 309 "cell_type": "code",
315 310 "collapsed": false,
316 311 "input": [
317 312 "def simulation_alive():\n",
318 313 " \"\"\"Return True if the simulation thread is still running on any engine.\n",
319 314 " \"\"\"\n",
320 315 " return any(view.apply_sync(lambda : simulation_thread.is_alive()))"
321 316 ],
322 317 "language": "python",
323 318 "metadata": {
324 319 "slideshow": {
325 320 "slide_start": false
326 321 }
327 322 },
328 323 "outputs": [],
329 "prompt_number": 19
324 "prompt_number": 7
330 325 },
331 326 {
332 327 "cell_type": "markdown",
333 328 "metadata": {
334 329 "slideshow": {
335 330 "slide_start": false
336 331 }
337 332 },
338 333 "source": [
339 334 "Finally, this is a convenience wrapper around the plotting code so that we can interrupt monitoring at any point, and that will provide basic timing information:"
340 335 ]
341 336 },
342 337 {
343 338 "cell_type": "code",
344 339 "collapsed": false,
345 340 "input": [
346 341 "def monitor_simulation(refresh=5.0, plots_in_place=True):\n",
347 342 " \"\"\"Monitor the simulation progress and call plotting routine.\n",
348 343 "\n",
349 344 " Supress KeyboardInterrupt exception if interrupted, ensure that the last \n",
350 345 " figure is always displayed and provide basic timing and simulation status.\n",
351 346 "\n",
352 347 " Parameters\n",
353 348 " ----------\n",
354 349 " refresh : float\n",
355 350 " Refresh interval between calls to retrieve and plot data. The default\n",
356 351 " is 5s, adjust depending on the desired refresh rate, but be aware that \n",
357 352 " very short intervals will start having a significant impact.\n",
358 353 "\n",
359 354 " plots_in_place : bool\n",
360 355 " If true, every new figure replaces the last one, producing a (slow)\n",
361 356 " animation effect in the notebook. If false, all frames are plotted\n",
362 357 " in sequence and appended in the output area.\n",
363 358 " \"\"\"\n",
364 359 " import datetime as dt, time\n",
365 360 " \n",
366 361 " if not simulation_alive():\n",
367 362 " plot_current_results(in_place=plots_in_place)\n",
368 363 " plt.close('all')\n",
369 364 " print 'Simulation has already finished, no monitoring to do.'\n",
370 365 " return\n",
371 366 " \n",
372 367 " t0 = dt.datetime.now()\n",
373 368 " fig = None\n",
374 369 " try:\n",
375 370 " while simulation_alive():\n",
376 371 " fig = plot_current_results(in_place=plots_in_place)\n",
377 372 " plt.close('all') # prevent re-plot of old figures\n",
378 373 " time.sleep(refresh) # so we don't hammer the server too fast\n",
379 374 " except (KeyboardInterrupt, error.TimeoutError):\n",
380 375 " msg = 'Monitoring interrupted, simulation is ongoing!'\n",
381 376 " else:\n",
382 377 " msg = 'Simulation completed!'\n",
383 378 " tmon = dt.datetime.now() - t0\n",
384 379 " if plots_in_place and fig is not None:\n",
385 380 " clear_output()\n",
386 381 " plt.close('all')\n",
387 382 " display(fig)\n",
388 383 " print msg\n",
389 384 " print 'Monitored for: %s.' % tmon"
390 385 ],
391 386 "language": "python",
392 387 "metadata": {
393 388 "slideshow": {
394 389 "slide_start": false
395 390 }
396 391 },
397 392 "outputs": [],
398 "prompt_number": 20
393 "prompt_number": 8
399 394 },
400 395 {
401 396 "cell_type": "heading",
402 397 "level": 2,
403 398 "metadata": {
404 399 "slideshow": {
405 400 "slide_start": false
406 401 }
407 402 },
408 403 "source": [
409 404 "Making a simulation object that can be monitored interactively"
410 405 ]
411 406 },
412 407 {
413 408 "cell_type": "code",
414 409 "collapsed": false,
415 410 "input": [
416 411 "%%px\n",
417 412 "from threading import Thread\n",
418 413 "stop = False\n",
419 414 "nsteps = 100\n",
420 415 "delay=0.5\n",
421 416 "# Create a thread wrapper for the simulation. The target must be an argument-less\n",
422 417 "# function so we wrap the call to 'simulation' in a simple lambda:\n",
423 418 "simulation_thread = Thread(target = lambda : simulation())\n",
424 419 "# Now we actually start the simulation\n",
425 420 "simulation_thread.start()"
426 421 ],
427 422 "language": "python",
428 423 "metadata": {
429 424 "slideshow": {
430 425 "slide_start": false
431 426 }
432 427 },
433 428 "outputs": [],
434 "prompt_number": 24
429 "prompt_number": 9
435 430 },
436 431 {
437 432 "cell_type": "code",
438 433 "collapsed": false,
439 434 "input": [
440 435 "monitor_simulation(refresh=1);"
441 436 ],
442 437 "language": "python",
443 438 "metadata": {
444 439 "slideshow": {
445 440 "slide_start": false
446 441 }
447 442 },
448 443 "outputs": [
449 444 {
450 445 "metadata": {},
451 446 "output_type": "display_data",
452 "png": "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\n",
447 "png": "iVBORw0KGgoAAAANSUhEUgAAAlMAAAGKCAYAAAAomMSSAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJztvXvwnkV5//9+UkNOQAgQKAxnQjgIhFA5lNgQUvyGgA0e\nIAK1lFP40VoYEAsjh2GnxVq0RenUEUJQUQtKZ9IhQGxEAgQDgi0QkEkrIVBAwikRwYSEQPb3R/g8\n+Tyf53Tvfe/huvZ+v2Yc5Xn23t1g9vq8Ptde927DWmtBCCGEEEJKMSz1BAghhBBCNEOZIoQQQgip\nAGWKEEIIIaQClClCCCGEkApQpgghhBBCKkCZIoQQQgipAGWKEGW88MIL2HvvvVNPgwhk06ZNqadA\nSC2hTBFSAWMMhg0bhm9+85td28yfPx/Dhg3D2WefHXFmxXj22Wdx8sknY9y4cdhll11w1lln4bXX\nXuvY9oknnsCIESPwq1/9yvs8pk2bhmHDhrX85w/+4A86tn3llVewzTbb4Cc/+Yn3ecRm3rx52G+/\n/TBixAhMnDgRt956a8v3GzduxD/+4z9izz33xMiRIzFp0iTcc889Xfu77rrr8NWvfrXlsx/96EcY\nOXIkFi9e3Nb+nXfewbnnnovx48dj3LhxOOWUU/Dqq6+2tFm1ahU++9nPYty4cRg/fjzOO+88rF27\ntsKfmpD8oEwRUpFRo0bhlltu6fr9vHnzMHr0aDQajYiz6s/bb7+N448/HrvtthtWrFiBJ554Au+/\n/z4+9alPdWx/0UUX4eyzz8bBBx/sfS6NRgPz5s3Dpk2bmv/54IMPOra97LLLMGXKFMycOdP7PGJy\n3XXX4brrrsPNN9+MNWvWYO7cubj22mvxr//6r802F110EebPn4/58+dj9erV+Pu//3ucc845uPvu\nu9v6s9billtuwcknn9z87Otf/zr+5m/+BiNHjuw4h1NPPRW///3v8fTTT2PlypXYddddccIJJzT/\n3b/33nuYMWMGdt99dzz//PN46qmn8Pbbb2P27Nme/20QohxLCCmNMcZ++tOftmPHjrWPPPJI2/cv\nvfSS3WqrreyZZ55pzzrrLC9jPv/883avvfaq3M+9995rp06d2vLZ+vXr7YgRI+yyZctaPr/tttvs\ntttua19//fXK43Zi2rRp9pZbbunbbunSpXb48OH2mWee8T6H+++/306bNs17v51477337NZbb22X\nLFnS8vmDDz5ot9tuO/v+++/b3/zmN3bYsGH2xRdfbGlz66232oMOOqitz5/+9Kd2ypQpzX/+/ve/\nb3fZZRf71FNP2b322sved999Le0feughu/POO9v169e3fH7wwQfb22+/3Vpr7Q9+8AN72GGHtXy/\nbt06O378+I5/3wmpK8xMEVKRMWPG4IwzzsC8efPavvvOd76D448/HnvssUeCmfXm+OOPx4MPPtjy\n2YgRIzBq1KiW2pu1a9fisssuwxVXXIHx48cHm4/tc7PVpk2bcNFFF+Hcc8/FQQcdFGweMXjjjTew\ndu1aTJ48ueXzww8/HL/73e/w6quv4oUXXsB2222H3Xffva3N//7v/7b1OXfuXJx//vnNf549ezYe\nfvhhHHLIIR3nsGjRIhx//PEYMWJEy+czZ85sbqEuWrQIJ510Usv3o0aNwrRp07LYZiXEF5QpQjww\nZ84c3HHHHS21JNZafO9732v5ATeYX/7yl5gyZQpGjRqF3XbbDddee23L1tY999yDQw89FKNHj8ah\nhx6K++67r+X5JUuW4GMf+xi23nprfOxjH8PDDz/c8v2RRx6JL37xi05/joHtowMPPLD52Ve/+lV8\n5CMfwcUXX1yoj9NPPx3Tpk1rytH69etxwAEH4Oabb+75nDEGo0ePxvjx43H55Zfj/fffb/n+O9/5\nDp599ln83d/9ncsfqYVHH30Uf/zHf4wxY8Zg4sSJ+NGPfgQA2GuvvTB9+nQ8+OCDGDZsGPbZZ5/m\nM88++yxmzpyJMWPGYKeddsIll1yCdevWNb+fNm0a7r77blx++eXYbbfdMHr0aEydOhWPPvpo13ns\nuOOOGD16NJ588smWz5944gkAwLbbbos99tgDb731Fl5++eW2NmPHjm357PXXX8eSJUvwuc99rvnZ\niBEjsNdee3Wdw8qVKzFp0qS2zydNmoTnnnuuZ5vDDjus2YYQQpkipBLWWjQaDUyePBkTJ07E7bff\n3vzu3nvvxYYNG/DJT36yLevyi1/8AqeddhquvPJKrF69GkuXLsXy5cvxhS98AQCwZs0anHrqqfin\nf/onvPXWWzj33HNbZOTNN9/EVVddhVtuuQWrVq3CKaecgtmzZ+O9995rttl///2dMmIvvfQS5syZ\ngy9/+cvNbMVrr72Gf/7nf8aIESMwYcIE7L777vjSl76Ed999t2s/c+fOxUsvvYTrr78eAHDNNddg\nv/32w5w5c7o+c/rpp+Oee+7Bm2++ifnz5+POO+/EBRdc0Px+w4YNuPrqq7H99tvj6KOPxi677IJz\nzz0Xa9asKfzns9Zi1qxZOP/887FmzRp85StfwY033ohNmzbhhRdewP33349jjz0WmzZtwsqVKwFs\nfnPy+OOPx5lnnolXX30VTz/9NBqNBj7zmc80+200GrjwwguxYcMGPPTQQ3jllVdw3nnnYebMmbjz\nzjs7zmWrrbbCZZddhjlz5uDhhx/GunXr8Mgjj2DOnDk4+uijsc0222C33XbDX/7lX2L27Nl4+umn\nsXbtWixatAiXXXYZZsyY0dLfd7/7XXzuc59ryzL14re//S223Xbbts+322675r/Xbm3Gjh3r9O+e\nkOxJucdIiHauueYa+/nPf95aa+2NN95ojzrqqOZ3p556qr3iiiustdZeeeWVLTVTRx99tL3//vtb\n+tqwYYPdeuut7erVq+2yZcvsmDFj7FtvvdU25vPPP28bjYZdvnx5y+e77rpr6VqiN9980x544IH2\nz/7sz1o+v/LKK+3w4cOtMcY+/vjjduHChfbwww+3J510Us/+Hn30UbvNNtvYW265xf7hH/6hfe21\n15zm86tf/coOHz7cPvfcc9Zaa2+++WbbaDTsRRddZB977DG7ePFi+6d/+qf2sMMOsxs3bizU51tv\nvWWHDRtmn3322Y7fd6qZOu200+x3v/vdtrb77bef/e///m9rrbXHHnusPf3009va3HTTTXbixIld\n57Np0yZ7/fXX2913392OGDHC7r///rbRaNj58+c322zYsMFeccUVdqeddrKjRo2yEyZMsMOGDbOP\nP/54Sz/77befffrpp7uO1almaubMmfamm25qa3vPPffYAw880Fpr7YEHHmgXLVrU1uZb3/qWnTlz\nZtfxCKkbzEwR4onTTz8dzzzzDJ555hm8+eabuOuuu3Deeee1tdu4cSMeffRRTJ8+veUogJEjR2Ld\nunVYvnw5DjnkEEyfPh0TJkzA2WefjR/+8IfYsGFDs48dd9wRBxxwQEu/e++9N1avXu0877Vr1+Kk\nk07CjjvuiDvuuKPlu0WLFuGSSy7BNddcg8mTJ2PmzJn4z//8Tzz44IN45JFHuvZ55JFH4gtf+ALO\nO+88XHfdddhpp52c5vTRj34U++yzDx577LHmPE499VTccMMNOOKII3Dcccfh7rvvxm9/+1v8+7//\ne6E+x44diwsuuACHH344TjvtNMydOxdvv/12z2ceeughnHPOOW3HNjz33HNYvnw5gM2ZqdNPP73t\n2U996lN49tln8cYbb3Tsu9Fo4JJLLsGLL76I9evXY+rUqTjmmGPw6U9/utlmq622wle+8hW89tpr\nWLduHfbZZx+cdtppLbVWixcvxo477uj8luW4cePwu9/9ru3zt956CzvssEPfNttvv73TeITkDGWK\nEE9su+22mD17Nm6++WZ8//vfx8c//vHm4ZpDj0VoNBp4+umnW44CGDgOYMqUKWg0GliwYAHuuusu\n7LvvvviHf/gHTJ06tVlHNGbMmLbxhw8f3reIeygbN27EKaecgk2bNmHhwoVtr9C//fbb+PjHP97y\n2fjx43HAAQf0rJl59913sWDBAhxyyCH4t3/7N6c5DbDVVls1z5rqNI+RI0fiiCOOwIoVKwr3+a1v\nfQs///nPccQRR2DevHmYPHlyR1kYYNiwYbjrrrs6/v/053/+5812rv/eh/LUU0/he9/7Hv7lX/6l\na5u7774bS5cuxde//vWWz+fOndtzC7Ub++67L5YtW9b2+ZNPPol99923b5sJEyY4j0lIrlCmCKnA\nUEmaM2cOfvCDH2DevHldf8ANHz4cxxxzDH74wx+2fP7BBx/gmWeeaf7z+++/j6OPPhpXXXUVli1b\nhuXLl+Opp57yNndrLc466yz85je/waJFi7D11lu3tdl///1b5gQA69atw8qVK7Hnnnt27fvSSy/F\nRz/6UTz00EP49a9/ja997Wtd2z733HP49re/3fLZypUrsWLFChx11FFd57Fp0yY888wzPYush/L+\n++/j0EMPxaWXXorHHnsMw4cPbxb2NxqNthPEjz322Lb/nwC0CIa1Fj/+8Y/b2tx5552YOHFioTcg\nL774Ypx11lk4/PDDO36/ceNGfOlLX8IVV1yBXXfdtfn5G2+8gfvvvx+nnXZa3zGGcsIJJ+BnP/sZ\n1q9f3/JnWbhwYfMMrxNOOKHtTKt169bhgQceUH/OFyFeSbrJSIhyBtdMDXDwwQfb8ePH2/fee6/5\n2dCaqV/+8pd2zJgx9pvf/KZdvXq1ff755+3s2bPtjBkzrLXWPvDAA3a//fazTz75pH333XftHXfc\nYUePHm1fe+21rudMTZs2zT7wwAPNf/785z9vv/GNb3Sd+5e//GU7YcIEu2rVqq5tHn/8cbv99tvb\nH//4x/btt9+2K1assCeffLL9xCc+0fWZBQsW2F122cWuXr3aWmvtz3/+cztq1Cj72GOPdWz/i1/8\nwm611Vb2G9/4hn3nnXfs448/bidPnmwvv/zyZpuXX37Z7rDDDvbb3/62XbNmjX355Zft+eefbw8+\n+GC7YcOGZrtTTz3V/tVf/VXHcX7961/b3XbbzT7wwAN2/fr19r777rPbbLONffLJJ6211v7P//yP\n3Xnnne3rr79u/+///s9aa+2LL75ox44da6+44gr76quv2lWrVtkLL7zQHnTQQfaDDz6w1m6umdpz\nzz3txRdfbFeuXGnXrFljb731Vrv99tvbO++8s+u/pwHmz59vt9tuO/vGG290bXP99dfbffbZp+XP\naq21X/va1+xf//Vf9x2jU82UtZvrpmbPnm1XrVplV69ebS+88EI7efLk5p9t48aN9tBDD7UXXXSR\nXbNmjX3llVfsKaecYj/5yU/2HZOQOkGZIqQCxhj7F3/xFy2f3XDDDfbSSy9t+eyqq66yZ599dstn\n//Vf/2X/5E/+xI4cOdLuvPPO9sILL7TvvPNO8/trr73W7rHHHnbkyJH2j/7oj+y9995rrd1cgL73\n3nu3zWXatGn2wQcfbP7zEUccYS+55JKuc582bZodNmyYbTQabf+59dZbm+0efvhhe8wxx9iRI0fa\nnXbayV5yySUt8xzMK6+8YnfaaSd7zz33tHx+9dVX2wkTJtjf//73HZ+799577ZFHHmlHjRpl99xz\nT3vDDTe0tVm+fLmdMWOGHT16tB03bpw9++yzWwrbN23aZHfccUf7s5/9rOufee7cuXbixIl25MiR\n9qCDDmoeTjnAOeecY0eMGGEPP/zw5mcrVqywJ554oh0zZozdfvvt7ZlnnmlfffXVln+Pd911l/3i\nF79od955Zzty5Eg7ZcqUQodabtiwwU6YMKGn9L755pt23Lhx9j/+4z/avtt///2bMtiLbjL1zjvv\n2HPPPdfusMMOduzYsfaUU05pe1lg1apV9rOf/awdO3as3WGHHex5551n165d23dMQupEw9qKm/2E\nECKAxx9/HJ/4xCfwxhtvYNiweBUMxx13HK6++mpMnz492pgDrFixgrVLhAiANVOEkCy47777MGvW\nrKgilRqKFCEy+EjqCRBCiA/+9m//NvUUCCE1pT6/whFCCCGEBIA1U4QQQgghFYiyzbd0yFk8pH5M\naT8gWi6XxxtqwaT/573PG/H/ee3vJ0s+07+RC8Zvd23c3/2CYbUcd1S1541b85lT5zu1vwA3FW47\na9lPizW8zmkKLSy9vX8byXiLlwVimUsM6hdbCsUKU3i4ViKva2vd1lyUzBRligxFlVwVJaKEAWFE\nDFAgY0B4IRtKSkGrKlL9MO6PiJCtwSgVr2RxsGCsii5aA5jiTZt4XqOUKVIrspMy5VkxwK+MqRYx\nX8E9tEz1w7g/4ipbgJtwAfGlqxNFRcxnnBo8ptf4VyL2FIkhReKB8zo3Dm1LrkORMoUzKFP90J6W\nrhuqJC6woPmWMh8y5k3CjJ9uOlIkyKcWqSKY8o+GzHAN4CxdnoXLFz5+RlSOWw6xxJdoAQ7r2RRr\nVmTtUaZIVyhs+SB9e6AoVUVMjHiZ6l0EJ7W8mfKPukhXcOGKLFup4nbpGOOpTsubaJlC3Wxm0Bqh\nTBGvUMDqSTBZ8yBjVQSsjHxVki1T/lFnpBTe+xIy49Y8hHClzGppiL2l4kSfGNBvfXvZNjR9u4Bd\n3L/NYOLI1DLFMiU05ZsCDYubhCd4ViyxcAHlM14ixEuKVBUh4luKvmUr5FuJ2mJtjO3D2JJFmSJb\nyEgEtQWXOqF5yzHlNmPl7UVT7fGuhJCxFLVfpnjTZKIFqJStaGu+YhZrgDJvGtqphbpuEkWmFmBG\n6CFqQam3V6SiXPRSBzOtJBEvj3VequUL0FHbFRJT7rGisiV1+7BsvBL5oo3HYx16rceFcFtvUWTq\nRLi/Jps7ZYokpSNe9ihwqkgayD0JmLrtRlPuseB0y5b5zHiZco+FKo6XXBRfFNeYVWnNe37TcBYW\nOQ0fRaYaS0KPEIcyZ6fkgkT5EyNvQgPZAHWTsH4ElbQEW49Rs12m9FDlkHhWl3Frnly2EsSn0DEn\nROH7AAPrT6ZMTXdobELNggxFshymkrfogpZIxChYfvAiZiUFLLR0JRct7afOm2LNtNVr9UJSXHFa\nm53W4CQ3NZInU6Q6JvUE+hNb5ELLWVAJiyRckgJhTsS8Z20wZbYYXYQrmmxpeTvRVcBMsWa+6rVy\nL4rvRak1eJtEmWooWQy90HAScWxM6gnEk7KQMhZMxAJLmOTgKR3vW40RRAuIkN0C8j0ioujPEOPW\nbb8YmEqypMSH0muNMiUEyldvTOoJbIFC1oOanPYsAYkHpYbObg2QVLqAdOIVSLCA6pI1gLaMlrd1\nRJnKFMpZb0zqCcSRMgqZO9oFLcpbjRUL52MVzUet45IuWAMYt+ZF4pQU0RpMr3Xse40svR2Y4qhG\nlCniTt3EzqQZNqSc+ZYyrSKmXbSKknpbERBYwzWAcWirRbAGY4o39SlaQIG4IORN6E5xgDJFCKBH\n+Ez4IUJImS8Z8yZhGb+VlJIUbyu6SJe3y3CHYhzapq7NinQlj8+DS1NLVpH1LVOm+r3Nl/ovIyG+\nSSVzxm93PkTMh3h5kS4PAZqS1Z3S4hVItoKIlineVOzPtQDbiD5EK7RgOR8gqlKmSHykLnTSmdBy\nZvx1VUXAqohXZeGibHklxuW3Q/EtWkBA2RqKpJhcJN6YYl0Ff9vQcd0WXaMyZSqTE9CDY1JPQDiS\ngo0EYma/jP8uU2e9pGS7BlMXGYt1bchgQmwfAokON00RCz0KFhDpSIcKbxeKlCntd/N5uWA0J0zq\nCUREo8Clrhcz/rtMLV6ATPkaSg4ylupUefWyFTtW5X5ulsSjEbTLlHZUyqBJPQEHNArXYFLLFyBS\nwHKXL43i5fWtxMAHnRYRruDna0kVrAFM8aY+zs1yWpMSr5NZgBmhh8iSKheYpkCctJnE42uXrDKk\nEDPjp5uU8lVavLjN2JeYW4pFhCuIZJmC7aRuD3bD9G8S7HBSiTKFZY3gQ0ii7FUNUkgtcdGlzMQd\nDkA9RassoQTNVO/CVcCiCpcn0cpRsHpR+YLcLkQXLVOsmYhYVGaNm95fV94uxCKn6VCmMkO6yKUS\ntVoIGiAjMEol0nk8QymT8XKVrpiiVTe5AsIJFuBPsoAAojVArLji4xcn0/vropIlU6bOyFymKl7F\noBVJ4pZC0qIKmok3VFdyFbUIAbwXZbcXgwvXAJHP99GApDO1kovWUPrFCYGlAJ3W4EK4xXfKVJ1Q\nKn0ppS2mpEWRMxN+iL5okTLfQd+UfzSWcAEy6rc0CljMc7WSnadlCncnH9P7azvVrbsoMrW0IVum\nolwkSrYgTOpiylpIOQsuYyZs9x2RKF4hf7M21R6PsaU4QCnpqmEdl7efLwXjpu8jHqJltFwJkQEz\nW/4nZYp4JQvRjCxvIeXMt4wFETDjv8smEuWqGzWXrtgZLk2C1Y0Y24c+tg6THFQ6GB9xoM/6tIvd\nuqNMEZWIkLyAkuZbyHxImDfxMn66aaJJsFzwKWPG/ZHQwhUjs5WDYHUjRFF8v7jjTbJMsWZtRCyE\np0wR0oOkEqZEvnxlv7zIl6neRRu5yhcQ/Y1FF+EKKloOkpWzYAEOMc5jNiuJZAVex9a6rSXKFCGD\nEJHxAoJvTUqUL0Bw9mso2oQs0RuLIWQr1jEQuUhXymMdktZkVVyjlClCAiFGtHqhJPsFCMuAAXEK\naCVImMK3FCWfuwXoE69SsSxiJmuAoNuGfdaiSJni0QhuaFuYpBzi5CyQiEnLglG+CpC4XgsIt4UI\nULY6EePanaiSZQp11RWRNVOUKd1oCASknSSy5lHIfEhYWflivVcBlBXIB9tGBErJlra4GvJNw2hX\n7Zj+cxlApkzlcp1MwBvcc0FbgCBuBBW0iiJWRb6iS5cp91gbkuSqFwlqtkLIVmjRyiF++iyAj3KM\ng+n8MWUqRzKVuBwCB+lNEPlKJF1RhcuUGqoVLaLVjQCX3w6liHAFyWjVTLAGKBQPPEiWD8ESKVML\nMCP0EEEpfficNpRJW05Bpk4E3370sNWoQrgGMOUfbUGbfLnKlnFr7ku06ihZA2u8yBxjCRbgJlki\nT0DXLlMaUSeASkROcgDTTrQaL091Xaq2FQcw1R5vQ5qABRYswG82C3CI1UKOeCi6TquM60uwgPKS\nJfKi4xNR7pLOulP2ziwJiJQ5Cls2RC2uT5jpAsqJV/JargEkyFbZei3j1jzpmVrCYluIGOb7MNJ+\na3IWFhUccDNRZKqxJPQIMih7s7sWJMpdcmkTFsQGQynrjOQieqCceEXLdJlSw7SiWbAGY4o3rZto\nxYg9zuvYUbJkytR0h8Ym1CzIUKTKXyppSyJmmQa63PEmZCXlK4ZwRS2clyBYQ4l4NY/Pw0tD1WgV\nRUJ88SJak9zUSJ5MkXKY1BMoRgqBiyFnwUUsgnRJCII54kW8SkiXq3C5yFaUjJZEwepGwFot6YXw\ng5EeQ5zW4m0SZaqhaFFUxfdVDdIxaYePIWehZCyIgAWULumBUhNetxo936fWCdFZLUC+eLn8XDDF\nm/aLf94kK5O3DIfScx1SppRCCYsORWwIkbYcNQXbmEg6kytkVgtIePYWIEe8AgkWEFGygKxEq2UN\nUqZqDIUsKqFlTJWIAVHrv6QHZZ8EK5ivUCwfWraAmhbHJz47S0Lxu5S1PcVRjShTxJ1cpc3EHzKk\nkIWQMWbEdJByG3EA9TVbA2gTrAFMsWZJToGvsOZjrWPKFMkbTSJnwg8RQsZ8Spg3+WItWFBSFMmH\nkq0oW4haBWsAU6xZVNESJliUKUJCkFLiTJhufYqYDwGrLF6ehYuS1ZlK4uUgXC6yVUS0nCTLFG8K\nIL1cdSPQ/YbR3jBMuE0oU6ZyPhpB6iIieoghasZPN1UFrIp0SZAtClZnKme2AmW1gmWzjFtz0T8n\nisYf07+JD8nyLVhl1yxlivhFchCoC7GyYsZfVymlC5AhXgPUUcBSHmrqW7SAiDVaEuKt57cMowgW\n4F2yZMpUTa6T8YJJPQFBSAgs0om9/Wj8dudjqzEn8RpMzhIWO5M1QHLRMsWb9iR2bAxwjINUyQI2\nrz2RMiX1ouPKN7DnjEk9AY/UVcpSF+sbv91RvDqTo3RJLogPcqaWceqynVQxzuMW4QBVJcvbG4US\nz5mSKlNaUSuBJvUEelBX4eqH4sxXqq1GL28wspi+LzGzW8mK4U3hYVtJGc8CnZVV9Yws5+t0JMrU\nAswIPYQYyt7eLg1RwmZST+BDKFzlCC1kpnoXVcSrjHRVEi7WczlRWroKypbPjFYwyZIQuxJIViXB\nknjRMZY1mv+zzL1QxA2pQpdc0Eza4UUENI34ljFT/tEy0hVNtvi2ohOULMiJSZ5Fy4tgSZcpsgXt\nYilF2pJJmkkzbAtSgmFqfAqXqfa4q3BRtmQg7fys2p+dlViwZmGR0/BxZOoMB5mqcF8UcUey0KWS\ntSRyZuIP2ULqwBmCENuLpnoXuWe3gPykq5RoBTrWIcjZWaZ4UwCy3yYcjOn9da+1uBBuPwfkyRTp\nTCaSmVreYglaNCEzcYbpikYJE17DVbZ+K2rtFrNcTUJvFwKKJAuIExOqrGHT++uB9SdSppY2wspU\nsFvVSXcEyl1MUQspZUFFzITruolGwepFKPky5R+NJVwpZWsAzdIlQbS8n5llCnfXimTJMu0f2alu\nXWQhU6Q3qmUzkbSFEjPfEhZEvIz/LgHkJ1ndEFIwH2M70Vm2uI3YQkjZ8pXNCiJY0s/FAmAXu3VN\nmSJeES1uEcQshIT5FDCv8mX8ddWkLsI1lKoCZso95iJcQUWrgmTlIlaDCSVZFKweDFmDlCkiHtHC\nNUCkjJh0+QIUCNgAqYNxDAIV4nZCu2jlJFmh3zQULVlAkrVtrdtao0wRNVDCNiNdwLxvPRq/3bWQ\ng4AlyGqFPP4h5tahVuGKcfp79CMcTKHh2gm0hilThPRAtJAFFjHfEiZSwIyfbrqiSb581G4Z90dE\niRZQu8yWlsNIpWexKFOEREaMoAWSMV8SVlW+VGw3SpctAcXxoWQr9puHtZEtj5LlRbAAt793Jdek\nSJkaes7+J9WjAAAgAElEQVSU1r+EhMQkqqR5FDEf8lVFvCpLl6n2eAvS5aobvqTLuD8SqlYrVkE8\noPdnnFPM8XR8Q3TBKrgmVciUVrQuEFJvgkmZBwGrIl5lhauSbJnyjzbRKljdCHiA4lCKilYQyaph\nbdZgfItWlfOxvApWl/VImVJEDguM6CZ49kupcAECpAvIR7wivIXoW7RCSlYOsT9EFqvqAaQ+67BE\nHo2Q7KJjj6fwkjwCAClOlG1GT9uLqYQLECJdgD7xipTRSiZZQK3qsVLVYoXaJqRM1ZEaSKPG4JIz\ndaznyqaOC5ArXhGPeQhRm8Utwy2kPN3dh2CJvE5mAWaEHkIcpd8s0YRyidMSlHJCS7ZLTS2XKTVU\nZ3IVLECPZAHZbxumOt3d9TwskTJ1IspdylmEMjel1xU1gqdA0jQFr1wIImIVxauMdEWTLVNqmFak\nCtZgFGezQh5Qqi1GlVrfAQVrIdzWm3qZyhVtkihK1BLLmLYgljteJKyCdLkKVxnZiipaGgSrE2Wk\nyxRv6rM2K0QmS3Nc8lnsXlSwRMpUY0noEdJQ5lZ2qUiQtyRCFlm8NAe0XIlxNUc3YmS2om8f1kW2\nTPGmRX5W9IvBFKwt+C5277QOZ2GR0xCUKaVoELlUghZVyhJkwbQGQG1421YsKVtl67aiyBZQfRtR\ng3QJFyyAkgWEkSuZMjU99AgBMaknkA5JwhZbzChkpBvea7dylC3j/khHJAqXgO3CVJIF6IodlbYH\nJ7mpEWVKMyb1BMqTUtRiiVkUIUtUH6YpoMZEWpF8dvVaA0iTrMCHkiapx8r4jKxC6/Q2yhQJjUk9\ngXZiy1lIIQsqYZHkS0NAlYC2rcSgWS3j1HU7uQgW4FWyvB7fkPH5WG1rUaRMNYT9Jc8J37fAS8Gk\nGzqkmIWSMO8CFlC6pAdVqaR4K9FFtlxEK4pkSZOrwQjIZHnLYmWawZriqEaUKeIXyXJnwg8RQsR8\nCphX6QogXNIDrFRii1aojFbtJWuAQIXv0d4qdIwNEtc9ZYrkgRQpM2G79y1fvsSL0qWfFFuIIbcN\no9VmSZatAJJFweoMZYrUBynCNRgTtnvKV3lSB2cJeC2QL1GnJWLbEMhHtARnsIACcUHw1TmUKUK6\nUTP5yl68WNflnZSnxYsQLVO8aUekyJZLrDPFm9YpiyVTprS+zSdlYRAZSJExE65rXwJG8cqXFCfG\nFxUtkZIl6edIAMmKerp7xENHKVNakLTASHxSiJnx36UP+aoqXpWFizVd3qkkXAFqtIJIlincZSvS\nYn+iLFavde/7wNEy65EyVVekLVAShxhSZqp3kVq6SguXR9Gqu2ANJoZs+RatQpJlCnXVjsT4nSCL\n5UWwPGWvZMpUHe7mM6knIASJQYG0EkrATPUuqkiXVtmiZMXbOgxRl1WrTFbktwljbg8OXYeUKdIZ\nk3oCAZAUZHIhZKbL+OkmhXBV2kr0lN2qm3TFLIanZJUkwVEN0QRL4gnoJ0LOhbkSKX1ru0RM6gmU\nQEJQ0kDoLUXjp5uqW4qahQvIW7okH05aRLKCFr1LiGOBLoFOIlgSZWoBZoQeojRlLvbMBfESZ1JP\noAcSApdEalLDVUa4pMgWQOHqSKCrdpJKloQ4pVWwJlGmskSL9IkRNJNwbAkBTAvCtxXLSlfU7Ba3\nEZ0oJVues1nJBEtKbEogWM6HjEqUKSxrBB8iNa7XKEhEgrBFlzETdzgAcgKaVnwLmKn2eBnhipLZ\nYnG8M86iFVmygtRhSYpHnmuwKr09iEVOU6FMKUa6wKWSs6hCZuIN1YKkAKgBHwJmyj3mKluuohVb\nsihYfZCcxTLFmgGQFWM8HtNQVLBkytQZimWq5NUI2pEkaimkLJqQmTjDtCApSKYmoWQBcbJalC3/\nZCVYgD7JiiBXC+H2M4AypRHFgpdS0mJJWXARM2G774iEABoTX1uJptxjsWq1UtRo5SpbIWuxfAkW\nEPDIhpQxIsD2oEiZWtrYLFNebywncREocLHFLLSMBZUwE67rJnURLp81W6bcYyIzWgNQtgDIOeXd\nm2SZQt20kyouVMxe2aluw0WVKVKdbIU0kayFFrIQAhZEuoz/LpvURbIAMcXxudVpDVB70SoQJ6Nu\nE5r+TdpIHQ8KrlG72K1byhQpjWixiyBnoUTMp4B5Ey/jp5s2UgfWlCgpig8qWsxitRGiHku0YEmI\nAR3WImWKiEekhCmUL5HSBYQRLwkBNxVVpMu4PxJKtGJIFuXqQyQJluk/lzYErHdr3dYdZYqIhuLl\nh1qJFyAiGAeHkuVELqKVxRah6T+XNiKvacoUqTUi5QsILmA+5cuXeFG6EpBg61CEZAG1z2bVMoMF\nBFvDlClCKiJGyAIJWLbiZap30UJOoiVcsoDiohVDsnIRLMAxnkV8i1B6/RVlihABJBOyAALmQ76q\nSpfIQvocZCtREXyIbFboM7NyEizAIUZJEixTbC5NKqxRyhQhmRBFyDzKV1XpqiJclWXLVHu8SQ6C\nNRShtVkSMlm5CRbgV7L6xYTo2SuH9UmZIqSGBBcvT9KVSrgqyZYp/2gLOYrWAGWEy7g1TypZNRas\n2BksKdkrkTJVt+tkcltMRC/RthvrLFsAhasbQiQrtWABef1ciClYqbJXlClSiJwWNqmOpi3FKsKV\nbCvRlH+0hRxkS4hgAZQsX0jKXvm6e1DkoZ1YVmOZ8nSNgmZyCBakHsKlWrY0i1bZuizj1jyZZNXs\n2IbYbxCGyF5Rpkgrmcmc1uBSR4LJlwfhip3domiVIFLhu2/BAgpKVo0OIE1xRU7V7JXIi44XYEbo\nIcRT+gZ2iSgSNG1Bp854l68K0hUzs1VatEy5x5poFCxArWSFFCyNcc63YPmWK5EydSL8FQiS/ogX\nN2EypjEQ1Rmv0lVSuMrIVjTRMu6PNNEqWAME3i70fT5WKMHSGtNiCla/9bgQbmtPjEzVAY3CKEbM\nBAiY1gBVFyRIFpCxaAG6ZStg4bvPLBYFawsp5UqkTDWWFGvnev0A6Y5UcUsqZ4mFTGMw006Qui2K\nVne0yFaErUIKln9iytUsLHIaSpRM1QXN0ihF0pJIWUIZ0xb0tKC9TiuKaBnnIVrRIlhA8GMbKFh+\nCSlXlCnShmR5SyFnUUWM2TD1SNg+jCFaUbNZmgQLcJcsU7ypL8Hiie4l12q3NTnJTY3iyNT00CMo\nxKSegD9Sy1pMIQsuYpHkS1OA1IA34SohW6FFK5pkaRMsILlkpRAsbbGjdPaKMkUKYVJPoDMxxSy0\nhAUTr8DCpS1YasGLcDnKlqtoBZUs49YcgE7BAtwkyxRv2i8+etseBChXt0mUqYbSBRGLKsWQEjHp\nhg4tYyEELIh0BRIubYFTOqkyWpSsBATKYkUTrLptDVKmakYuImbSDBtSviheYfrNHU1bhuIkC9Aj\nWonkCvC4PZhh9qq5/ihTxAnNMmbiD6lJvjSJF6Ar4KYgpWQBbqIlUrIA+aKlPXsFFIoRGtb6FEc1\nokwRP2iSMhNvqFDyJVq8KFxJSFGTBQiRLFO8aUekSlagoxqkZa8krmvKFNGPVDEz4YcIIV8+xcub\ndHFrMTqVZStQXVawIxyMU7et5CRXgBfBipm9krCOKVOEDCW1nJlwXfuSL1/CJVm2JARoqcQUrRCS\nFVywpMoVEEywfGSvAD/F7SnWLmWKkFDElDLjv0sf4lVVurzIlkfRomD1ppJkBRAsoLhk1VqwgCBb\nhFLeHIyxbilThKQmlnQZv91RttqhbLUTK4uVVLCA/CQrgVwBnmqvEtRdyZSpOhzaKXUBEVkwu5Ve\nuDxvIdZduGIXvjOL5QnKVU8oU3VG4oIl7sSu8TL+uqJsbYGSVbEDLYIFuK8hibE60ZuDXrYGA5x3\nRZki1ZC4yEl3FGa6qgpXDrJF0Sr5YOI3CoMJltS4q7XuyoNcyZSpJaFHEIBJPYFESA0CpDOh5cv4\n6SaVcFUSLWa0vFBKtBK9TVi77BXg/WBRSXIFbFl/lKk6YFJPwANSAwVpJ4SAmWqPV5GtMqKVWrLq\nLlhACckKkMVKKliSY6bHi52j1F0VWZMSr5M5EWEvn42N8/UHGjGpJ+CI5EBTB4QJV1nZiipalKzK\nULCExr0c5IoyVQ/UCZ1JPYE+SA1K2hEmWQBFK3dCCpb4LUKpcUyjXEmUqQWYEXqIZLhehaAFkbJm\nUk9gCFIDlxYoWqXGomi5k1qwkh3RIDVGaZCrSZSpWiFd5pJLmUk7vNhgJplQRfKm2uNlRCtqIXxF\nyaqTYEkock+SvZIcj4que9P7a29yJVGmsKwRfAgJuJx1IhkJghZdwkzc4VqQHOAk41u6TPlHY4lW\nbMmiYPVBsmCZAm0kx55IctVtHc7ComLjfwhlSjlSBS6FkEUTMBNnGACyg50GfAqXKfdYDNGKKVl1\nEiwgnGSJlCtAdswpsp5N76+LypVMmTpDqUw5vvmhHSliFlPEogiYCT9EC5KDoTR8yZZxf0SkZFGw\n+hIygxVVsEyBCQGy40lAuVoIt58NlClpKBe4lEIWS8KCC5gJ230LkgNlSjKWLGax/BLyNHdmrxzp\nt25N/y4G1h9livRGuKzFlrHQAhZUvEy4rtuQGjxjknjLkJKlAw1yBXjOXgEyY0QFubJT3YaKIlNL\nG3nIlJfb0XNDkJzFErFQAhZMvEyYbluQGEhjIKAIPifJAvIULQ2CRblCy5+PMlVDspG8BGIWWsB8\ni1cQ4TL+u2wiMaDGJOF2IeAmWlLrsQbITbKyKGw3/Zs0kRYL+qxNu9itO8oUcUK0uEWSsRAC5lO6\nvAqX8ddVE2lBNSVVZcuUe0yMZDGD1UIIwaJcFWTIWqRMEVGIla8I4uVbuihcNaSKbBm35qEEi3JV\nnlTZK8oVYK3b2qNMETGIFK/A0iU5y0XhEkpEwQJ0S1ZugkW5igdlitQGUfKlTLrECZfx000LdZIt\nClZhchIs1XJl+s+jhcjrmTJFSB+SS1hA8fIpXT6Ey4tsmepdtFEH0Ypcj0XBSg/lyh+UKUICkUTC\nAoiXL+GqKlviRIuCVQxTvGlywaq5XAEl4hblCgBlihBxRJMwj+LlQ7iSypapNHQrlKz+mOJNXc/F\nKipZFKziOMUkjXLlYc1SpghRiDbhqipbVUSrckbLVHu8CSWrN8ateVHJ8i5XQCnBolx1p6pcScha\nUaYIyZAospWBaAFCMlo5ixYFqyO5yJXvbUEf51ylyFqJlCmJd/Pl8hefEE2iBVSTLYqWUCIJVoga\nrJDbgzn8nImdtQJkyBVlKhNyWIREBppkK1VWS8TWYW6SJUywmL3yR+GYom1LcNAapEyRvuSyoIlf\ngkqXB9lKIVrMZAWgjGQZt+Y+BSvkHYS5xGJfciUpayXyOhksUypTFS/mzJFcFj+pRhDxqihcZWVL\npWRRsIIIVursVS7xVYpcVRErypRWMhO3XIICKYd32aogWioky5R7rIXcBAtwlyzj1tyXYFGuelMo\nHgjbEhQpUwswwz1VSqqhUM5yCRykM7lks6LWZZlSQ7WSm2QFFCzKVXi0ZK3s1L7dtxBNpupIVgKp\nRM5yCDZ1g5JFyaqEArkCwrw1mEO8k5q1okzVEDXSJlTIcghIuSJpuxAQLlmm1DBboFz1JUlhe43k\nKlbWqohYiZSpE+F2fYA2XO6U0oA4ORMmYdoDVk54la3IdVnRarKM+yNNKFh9kbo1mEOcSpm1Wgi3\ntUaZUoYGcRMhYwkFLIcglgveZKukaMWQrKiCRbnqS3S5KhjrtMel2FkrkTLVWBJ6hDi4XtCpASly\nlkTAEgmX9qCWCylFy1WyKFiBCXg0A+UqDD6yVr3W4SwscpoPZUo40gUulYxFla+I0qU9wGlHk2AB\nbpJFwXIkUPaKcuWfEFkrmTI1PfAAJnD/GSBNymJLWBT5ipzp0hz8NJGzYAERC921C1aN5EpzbPF2\n1c0kNzXKQ6YkYlJPwC8SZCyWgOUmXpoDo1RSF76H3CZkBqsANdsW1BxDSm8HUqZqgkk9geKkErEY\n8hVUvCIJl+ZAKQ1NWSyRgkW5asOHXFGsNuOUtaJMkVKY1BNoJ6aEhRQvzcKlOXBKo7JoRXijMKhg\nGbfmTTQKVkK5YtaqGH3X420SZaqhcDH4pszi0ohJO3wMAQslXkGkK6BsaQ2i0kghWaoFqy5yBRT6\n9xNNrjIuZO+4BilTpIk2gTNxhwspXr6Fy7toUbJEU1vBMsWbtqBNsALJVdR6q0yzVs21R5kildAi\nYCbeUKGkS7RwBZItjcFVCpUES6tcAfXIXgXaFoxWb5Vh1mqKoxpRpkg1pMuXiTNMCOGibJF+1FKw\nTPGmLWgSrERyxazVFihTRDZS5cuEH0KycEkXLemBVxJSBYtyVYEAciUpayVxfVOmSJ5IkTATtnvf\nwiVOtjyLlsQgLJXSkuUoWMmzV6Zwl61okStmraJAmSJkgNQCZsJ061O4fMiWRNGSEIw1IE2wRGWv\naixXzFpRpgipRgoBM/679CFcYkTLk2RRsPoTa4swRPaKcvUhCeQqx0NDKVOExCCmdBm/3UkQrcqS\nxSxWNGJkryhXgRAoVoCOrBVlihAJxJIt47c7itYWKFmdkZa9olw5EOCy5ihZqwRiJVOmpkPHXzRC\nYlBT0cpFsgCK1mCylytTrFkb0n/mCcxaSRIruTKVI9IXC9GHQtFKnc2qJFkULO9Qrrog/eeF56yV\ndrGiTGlF+kIjsggtXcZPNymzWRIki4K1mRiCpUqupMf7yNuBEk9hp0zVGekLlMQlpHCZ6l1UEa0k\nkuVBsChXm6FcDUFy7BZWZxXrzUCZMrUk9AgFMaknoAzJC5xUJ5RsmepdlBWtspJFwUpLtnJlCnXV\nivS4W5PtQMqUFkzqCURAelAg7WQoWEBkyeIWYWW0yRWzVgUxvb+WJFaUqbpjUk+gIpIDR53JULK0\nZbEoVyXxKFfMWvVAkFh5karbBMrUifB/wWtMnM4n0YxJPQEHJAeVOpGZZGkSLMpVSQrIFbcEPeAS\nG0zvr5OIFWVKJ2qFzaSeQB8kB5s64Fu2TLXHY2axKFjxCH1Ku4otQcmxTqNYSZSpBZjhpR+XSzDr\nimgpM6knMATJwacOCBKtWFms2IJFuXJEqlyZQsO1IjW+eRQroPyRC33X4qSMZUoyuYieKBkzqSfw\nIVKDUs74FC1T7jHRgsXslRMh5YpiVYGi69wUa+ZVrChT+aFB1ERImEk8vtSAlQsUrN4we1WYUnKV\nSyG71DglbStQokxhWSP4EEVwuZk8R6RJWTIBM2mGBSA3kGmlJoLF7cFw1DprJTUeCRCrWVhUfA6o\nmUxJQKvQpRax6OJl4g7XRGpw0wQFqzuUq75oyFpRrDpg+jdx2QakTNUQqYKWSsCiiZeJMwwAuYFO\nCzUQLNZe+Sd11orbgUOIJFYAsBBuP0fiyNQZymXK4SZzjUiRsVjyFUW2TPghAMgMeJrwJVmm3GOu\ngsXsVVqYtRJGkfVr+jfptA4pU9pQKmopBSyGdAUXLhO2ewByA6B0FAkW5SotoeSKYuVIAKmiTNUR\nBUKWQr5CS1dQ4TLhugYgMyBKxodgmXKPhcxeUa78QbESgiexslPdhqVM1RmhEhZTvEIKVzDZMmG6\nbSIxQEokkWBRruSTcjswiVhJjRkVxEqkTC1t5CFTle6DygkhEhZLukIJl0rZkho0JaAke0W5ik+q\nrBXFahCOYkWZyoAspS2hgIWWrhCyFUS0jP8um0gNoKlRkL0KXndFuWqBYpWYgmvSLnbrljJVE9QJ\nWmT5CilcvmXLu2gZv901kRhIJVBVsIz7I2LkimLVQm3ESmos6LEWKVPEO6JFLKJ0hRAu0aJl/HXV\nRGpQlUBZyTLlHisqWJSrOEguYK+jWFGmSDLESlcE4ZIuWuIlC5AbZFNRJYNl3B8JIVfcEiwHxSox\nxx1FmSKyESlcCmVLpGgZP920IDXYpiCiXIkoaKdYNXGOm9wKrIy1buuNMkXEIka8AsuWVNHyIlmm\nehctCAu4SaFc9YViBYpVSShTpHaIkK5AwuVTtLKVLAGBVwyR6q5CyFWMLcHc5EqiWOUiVZQpQnqQ\nTLwCyJYv0RIjWcbLNDZDwdqMMLli1iocFCu/UKYIqUgS4fIsWz5Ey4dkUbCEUUaujPsjlKu0UKyq\nQ5kiJCDRRStDyaJgCSKCXCXfEqRYudEn5kSRKqDY37OA65cyRUgiooqWMMnKJotVZ7lSuiVIsSqO\nU4yqebaKMkWIQKKJlkfJYhbrQ+oqWBHkKmnWimJVnBqKFWWKEGVEES1BklVFsMS8SVhHwRK0JSgh\na1VLsSoYR6qIlZT7ASlThGREcNHKQLJEZK+AegmWoKwVxcovPsUqeLbK9J9DE8f1SZkipCYEFS1P\nkqVSsEz5R1ugXPXGuDUvIlcUK39IylYB8cVKpEzhjDgylctfYkKqIF2yqghWsi1CU/7RJpSr3pji\nTZNlrShWvZGQrQK81FbVWqakkMsCIvlAwWqHchUJihWAPH4uqCtaN/3nAKDjWqRMZUoOC5HII5hk\nKRQsylUEKFbZxHKfYiVRqihTpCO5LGAShyCSVVGwVGWvTLnHWqBctWPcmieps6JYdSdwtsrnFqBd\nXKzdAHFkapkgmSpxtkhdyWWhEz9QsChXQRGQtaJYVSOnbBVlSgo1kbYcAgCphjTJKitYlCthBMxa\n+RKrUG8F5hBXfWWriqznStkq0/ljylQuZCJjOQQF4o53waJc9YZi1Yop3pRiFZ5C8UBYwTpliqgU\nsRwCBumOJLkCygkW5UoIFCu1xMpW+ZAqkTK1ADNCDxEcp8WhESUCpj2YkC1IEqys5YpitQVTvGl0\nsaJUtVMxW1VFquzU/mMPhjKVENWCJly+tAebOkK5Kohxf6SFXOUqkFj5PG6BYtWKlIL1TuuQMlUj\nVMiYMOnSHHjqiFfBoly1Q7HajCnWLLpY1eRgUIlSJVKmTkSxv4CpKfparGZECpgQ4dIaiOqGN8ES\nLlfMWnkg4HELkrcBAZ3xzHlt91jDVeuqFsJt/VGmIqFV1ETIlwDZ0hiY6oKE7FWWclV3sTLFm1Ks\n/OJTqoBydVWUqQyRLmLJhSuhbGkLUnVAY+aKYhWZXMQqc6kC4m0BDl2DImWqsST0CFsouqddB6RJ\nWDLpSiRbGgNXjqSWq+yyVhSrQkjOVmmMTbHrqmovUzHJTdwkyFd04aJo1Q5tckWxikgOYsVs1RYq\nSNUsLCo+IVCmxKBJzFJKV+6ypTGwaSdnuaJYlSRQ4TqzVf7xcbp6pzVImaohUkUshXRFk62IkqU1\nyGnFi1wJFCughFwZt+Yt5CBXubwNyGzVZhwOAZUpU9NDj1ABk3oC8ZEkXzGFK4poRZIsjcFOK7nK\nFcXKkYTZKqB/rKRU+T1ZHZPc1IgyFRqTegLVSSlfsWQruGhFkCxtgU8rqeQqC7GiVPWE2So/eJEq\nylQmmNQTcCOFcMUQLe2SpSkAaoRiBYqVK6Z/E0qVHypJFWWqxpjUE+hOjrIVTLQoWGqpLFcUK70I\nfhOQUuXQeGANUqZIIUzqCbQTS7hCShYFiwygIWslTqxykCogiFhJzVZpih1Oa/I2iTLVULZAyqRu\nc8aknoB+0QoiWQEFS1OA1IL0rBXFKgCJslWUqt4UWouUKcHkLGkm7fAxZCuEaGmSLE3BUjoUK0e0\ni5V2qQIKxxUtcaLvGqRMZY52ITPxhwwtWr4li4JVLyhWDmiXKkDsFmBdpQrosgYpU6SJRvEy8YYK\nKVniBYtyJZZKcqVVrEzxpk20i5XQbBWl6kMoU8QJLcJl4g4XSrR8SpYGwdIUSCVCsSoIxaoNaXVV\n2mLBFEc1okyR/kgXLhNvqBCSJVawKFeiiLkdGOq4BdZX9YFSJQbKFEmDVOEy4YegYJVHS2CVRu3E\nyjhNYTOapQpwi6mmfxOJ51VJXv+UKSIXScJlwg/hW7JEChazV8mJuRXIbcAEUKqSQJkiupEgXCZc\n11IFS6pcSQyyktEuVpSqPhSNj6Z/kyhSVSIeSFnzlCmSJ5SswvgQLMqVfiSKFbNVnshcqiSsc8oU\nqRepJcuE6ZZy1R8JAVcLscSK2arIeJQqIMKxCoqkijJFyACpRMuE6daXYInaGqRcRae0WGnKVpnC\nXW5Bq1R5rqkCZElVqnVNmSKkHykky4Tp1odg5Zi5olj1h9mqHmgUq8wL1WOvacoUIWWJLVnGf5dS\nslfMWulCmliJyVYB+sTK81lVkqQq5jqmTBHim5iSZfx3KSF7JUmuKFbdkSZVQIBslSk8dCuapKpM\nzDK9v5Z0+GeMNUyZIiQWsSTL+O0uC7li1io40sSKW4AlyFiqQq9byhQhqaBclUKKWFGquhOjaJ1S\nFRCt19QkzFJRpgiRRAzBMn67qypXOWStKFadkSRVgJAtQE1SBUQvVNdapE6ZIkQ6ygQrpVxRrORS\nSqwSH68QTKq0CRWgT6oiH6dAmSJEG4rkimJVvY8cCS1WarYAc5YqU6xZLlJFmSIkB0ILlvHTjVq5\nolgFQUq2ilJVkiJxx/RvUvU4BQknqcuUqemhR4Dev7yEFKEGckWxygdK1Ydo/LkU8d4/ydfT1Fem\nYqBxYZA8CSlXxk83qbJWFCs5UKo+ROPPjkylquj6pExJRuOCIjoQLld1FCtK1RakvAXIQnVHIhap\nx3zrD+i/PilTuaBt0RFZZCxXFCu9SJEqoJhY8UgF1PatP8pU3dCyIElaQsmVqd6FKrHiNqAXKFWD\n0BLDBUlVjLOpKFOkFS0LlcSDYtUCxSodlKpBaIjVkS9RTnmBskyZWuKxM+OxL6JjAZOwCJWrOokV\nparkg5SqNNRAqvKXqViY1BMQhIbFTfwRQq5MtcdjixWzVWmQIlUsVC9A5kJFmZKAST2BSGhY8KQa\nFCtmqxIh4VR1r1JlCnXVioYYq02qCgoVZUojJvUEAqAhCBA3MhKrqNuAzFZVQotU1TpLBXgtUBeR\npRCBeVIAAArtSURBVLqNMpUvJvUEPKAlMJD++JYrU+3xmGLFbFV8nKUqp3oqTXEzl8M+KVM1x6Se\nQEk0BQvSTgZixWyVDihVSvAkVcnu+ZMoUyei2unHPnC6JiBnTOoJOKApcJAt1FSsmK2KS2qpSlak\nri0uRro82XuWijIVlqylzKSeQAG0BZK6I0isxG8DUqqcqXU9lbZY6EGqogoVZUoeWQiYST2BHmgL\nKnXFp1iZao+LzlZRqpwJKVXc+vOIgCxVYaGiTOlFpXSZ1BPogqYAU0eEiJVoqQIqiVXdpCr0GVXi\nj1LQEvM8CRUQ+OLkSZSp7FEhXSb1BDqgJdjUDQFiFWsLkFIVnpBSJT5LBeiIc5He+KskVBJlagFm\nBOm36G8BdUO0bJnUExiEhqBTJwRIFSA8W0WpKkxWW3+m0HCtSI9vQo5Q6LoO6yRTIchd0ESKlkk9\ngQ+RHnzqhACxolTpR8vWX23v+hNy0GfHNUiZik8uAiZKtEzqCUB2EKoTvsTKlHtM9BYgpaoQzFIJ\njmUehQrwuO1HmZKNRvESIVkm9QQgOyDVgZpkqyhV4UgtVRSqLkS836+wUFGm9KNFuJJLlkk7vOjg\nlDOUqu5Qqgqh4cDPWr7xJ2nbjzJVD6QKV20FS3qQyhWFW4CUKjmEkirRtVTSY5XHLFUlocIip2lE\nkSksawTruuh+dZ2QJlpJBcskGld6wMoNSlVnKFWFcJKqyFmqWh6hIECoaidTvsldziSJVhLJMvGH\nFB20coNS1RlKVV8kCxVQwyxVYqGiTEUkJ/GSIFnR5crEHQ6A7OCVGz7EypR7TKRU8ZqavnDbTxgJ\nC9MpU0LRKl4pJSt7uZIcxHKCUtUKs1Q9kf7GX+2ECkhSmE6ZUoom2UolWJQrUglKVSuUqp7ULkul\nIf54OjW9iFBRpjJFumxlL1gmzjBNNAQ2rSiSKsn1VBSqLni6449C1YVIQrUQbj9bKFOZIFG2UghW\ndnKlIbhpJWGxujipolB1RbpQAZ7PpNISc4qsX9P7617rUKZMnRFQphzeqqgj0iQrtmBFkSsTfggA\neoKcNpip2gKlqiMh7/gTe8inhngTUKjqJ1NVqaGMSRKsmHJFsSJdUZSlAtykKtbWH4WqC5qFCpAf\nawIJFWUqNBnLlwTJiiVX2YiV9ECnDUVSxSxVGqRv+1GoumB6fz10/VGmJJCRcKUUrGzEyoTtvon0\ngKeJTLf+mKXyA4VKIB6ECtiy/ihTWlAsXKkEK4ZcZSFW0oOeJpRIFbNU8UkpVICn86hMoaG2IDm2\neHrLD9i89ihTOaBMtChXJTHhum4iOfhpIpFUcetPPurPozLF5tOC1LjiUajsVLehKVPaUCBaucqV\narGSGvy0UVWqTLnHmKWSDYVKEJ6EijJVZwSLVmzBUitWJky3TaQGQG0kkCpRWSqent4GhUoQHoSK\nMkW2QLkCEFas1EoVIDcQaqKKVJlyj+WQpQIoVQAoVKHwcJcfZYr0RqBgUaz6YMJ020RiMNREZlkq\nClV1KFQCqChUImVqaSOMTJU+RI20IkiwchArSlVNiSxV3PaTT+GfUdKECsjjtPQKQlUrmaoCRawP\nQgQrhlypy1YZ/122IDUwaqDOWSoKVUckCpX3C5IBuXGjpFBRpgJRe/kSIFeaxUpltkpqcJROgmMU\nxAgVwLf9hhBiyw/oHw8pVB/iuh7N5v+iTCWkVsKVWK4oVkMw/rtsIjFAaiCjbT8KVTWk1lBRqLpj\nF7u1p0xFImvRylysKFWQGSA1UOcsFYWqhdoIldRY4bgWKVPKyFKyEsqVRrGiVGVOnYUK4N1+g6BQ\nJcZhLVKmMiArwUokVpQqcOtPEhQqZ3IUqlQHe3p7ww+ojVBRpjIlC8HKUKxqLVVSg6VkWEflBIUK\n8oTKFJuP2PhAmSKdUC1ZkeWKUuW3uyZSg6ZUhB+fQKEKD4UqMQXWIGWq5qiUqwQZK01iRanKEApV\nYXKUKSCMUIk81FNqXOizBilTpAV1cpVJtqq2mSqpgVMiFConcpOqUrE5klB5rZ+SHBN6rEHKFOmK\nKrGiVHVEhVABsgOoNCJflkyhkkMqoWJ2ahBd1h9lihRGjVxFFCtKlUckB1BpUKgKQ6FC35gobrtP\neizosP4oU6QUKsSKUtWGV6ky/rpqQXoglQKFqjA5CVXp2Euh8suQ9UeZIl4QL1eRxEqDVDFLlREU\nqsLUXqgk1U+Z/k0AyI8Dg9YfZYp4R7RYUaqaiM9SSQ+kkigrVcb9ERFCxatnWD8lhQ/XHmWKBEWs\nWCmWqloJFSA/mEqBQlWIXIRKcnYKqJ9QUaZIFChVlKpKSA+mUqBQFYJC1Rtx232A+BhgrdvaGxZo\nHiRzlt4uNIBdh9IB2QXnmpACuPyA6ofrNSI9Mf66auLjrro6UPYHjvE6i3iU/GVI7C93jpSKqRHi\nHVAwphiHDjOLAZQpUokBqRInVhGkatayn3qXKgoV8YZxa+6SzXTJooa+dDw3QsTSInHKZ+ypI9zm\nI94R91tihK2/2mz7GT/dtCA83S+CiG/5cbsvPbUpRgfErn9u85HkiMtUMUvlL0tl/HTTAjNU/any\nA8e4NReRoeJ2XxKKxByvGW8gm/VPmSLBqKtU+YRCRbxgwnUd4h7KKtRWqArEtmjbfcaxfQbrnzJF\ngiNSqgJCoSJBiLgd4v0g2A+JkZ0i4fGencoAyhSJhiipolBVx/jppkkGv52Kxrg1F7HdV5LaZqcK\nwGL0MFCmSHTESJWyOqoLcJO3IEehUkrV7JTxMouOBNnuY3bKjUjHJBTCOLZXvvYpUyQZYoSqplkq\nCpVSIgqViO2+kjA71R0fMYlbfa1QpkhS6pSl8gmFisQi1HZfYZidcouRnmJZkq0+xeueMkVEQKFy\nh0JVYwRv9xWFRyWkJUp2ylQeQg2UKSIGClUGGM/9UahEkDw7RZLERxaiF4cyRURBoXJDXHaKxIPZ\nqdpROD5KKkR3RekvUJQpIg4KlRvihMr46aaJ0uCaG1qzU9zq6w63+vxBmSIiEVGYrui3O6bja4rQ\ne81cYHbKjdhxkbGlGJQpIpqchUpi/RSzUzXDhOtaUnaqlij6ZbANheudMkXEQ6EqBrf7SGhCnTvl\nRInsFLf6uhPllzoTfojUUKYIKULNhIooom6F6EQcfIGFMkWUkDw7BehOmzvC7BTphtZC9Jxg3ZQ8\nKFNEDSKEKhDMThHiAAvRi1GjXwBTQ5kixIUaBSem7hWRwVt9oWHdVHdE1k0py0JTpogqmJ1SiPHc\nn7IgqwKTegKsmyK6oUwRdSQXKgXZKW71kZCwbooMpe6ZbMoUIaQrdQ+QqqjbVh/rpqLCX9B6Q5ki\nhBBCCKkAZYqoJPlWXyCyfavPeO6PdVOEEEFQpgghhBASHpN6AuGgTBFSBgVF6IRog2/0Fadwdr5g\nrMr2beJIUKYIIYRknTUYgGdNkVBQpohacq2bIkQDIi49JkQIDWutTT0JQgghhBCtMDNFCCGEEFIB\nyhQhhBBCSAUoU4QQQgghFaBMEUIIIYRUgDJFCCGEEFIByhQhhBBCSAUoU4QQQgghFaBMEUIIIYRU\ngDJFCCGEEFIByhQhhBBCSAUoU4QQQgghFaBMEUIIIYRUgDJFCCGEEFIByhQhhBBCSAUoU4QQQggh\nFaBMEUIIIYRUgDJFCCGEEFIByhQhhBBCSAUoU4QQQgghFaBMEUIIIYRUgDJFCCGEEFIByhQhhBBC\nSAUoU4QQQgghFfj/AcxNvvk8Uc7VAAAAAElFTkSuQmCC\n",
453 448 "text": [
454 "<matplotlib.figure.Figure at 0x5fd82d0>"
449 "<matplotlib.figure.Figure at 0x1141a1450>"
455 450 ]
456 451 },
457 452 {
458 453 "output_type": "stream",
459 454 "stream": "stdout",
460 455 "text": [
461 "Simulation has already finished, no monitoring to do.\n"
456 "Simulation completed!\n",
457 "Monitored for: 0:00:50.653178.\n"
462 458 ]
463 459 }
464 460 ],
465 "prompt_number": 27
461 "prompt_number": 10
466 462 },
467 463 {
468 464 "cell_type": "markdown",
469 465 "metadata": {
470 466 "slideshow": {
471 467 "slide_start": false
472 468 }
473 469 },
474 470 "source": [
475 471 "If you execute the following cell before the MPI code is finished running, it will stop the simulation at that point, which you can verify by calling the monitoring again:"
476 472 ]
477 473 },
478 474 {
479 475 "cell_type": "code",
480 476 "collapsed": false,
481 477 "input": [
482 478 "view['stop'] = True"
483 479 ],
484 480 "language": "python",
485 481 "metadata": {
486 482 "slideshow": {
487 483 "slide_start": false
488 484 }
489 485 },
490 486 "outputs": [],
491 "prompt_number": 26
487 "prompt_number": 11
492 488 },
493 489 {
494 490 "cell_type": "code",
495 491 "collapsed": false,
496 492 "input": [
497 493 "%%px --target 0\n",
498 494 "from IPython.parallel import bind_kernel; bind_kernel()\n",
499 495 "%connect_info"
500 496 ],
501 497 "language": "python",
502 498 "metadata": {},
503 499 "outputs": [
504 500 {
505 501 "output_type": "stream",
506 502 "stream": "stdout",
507 503 "text": [
508 504 "{\n",
509 " \"stdin_port\": 62126, \n",
505 " \"stdin_port\": 65310, \n",
510 506 " \"ip\": \"127.0.0.1\", \n",
511 " \"control_port\": 41092, \n",
512 " \"hb_port\": 42611, \n",
513 " \"key\": \"00d832a7-200f-41e2-9d74-aac119f126a3\", \n",
514 " \"shell_port\": 60748, \n",
507 " \"control_port\": 58188, \n",
508 " \"hb_port\": 58187, \n",
509 " \"key\": \"e4f5cda8-faa8-48d3-a62c-dbde67db9827\", \n",
510 " \"shell_port\": 65083, \n",
515 511 " \"transport\": \"tcp\", \n",
516 " \"iopub_port\": 55151\n",
512 " \"iopub_port\": 54934\n",
517 513 "}\n",
518 514 "\n",
519 515 "Paste the above JSON into a file, and connect with:\n",
520 516 " $> ipython <app> --existing <file>\n",
521 517 "or, if you are local, you can connect with just:\n",
522 " $> ipython <app> --existing kernel-32429.json \n",
518 " $> ipython <app> --existing kernel-64604.json \n",
523 519 "or even just:\n",
524 520 " $> ipython <app> --existing \n",
525 521 "if this is the most recent IPython session you have started.\n"
526 522 ]
527 523 }
528 524 ],
529 "prompt_number": 14
525 "prompt_number": 12
530 526 },
531 527 {
532 528 "cell_type": "code",
533 529 "collapsed": false,
534 530 "input": [
535 531 "%%px --target 0\n",
536 532 "%qtconsole"
537 533 ],
538 534 "language": "python",
539 535 "metadata": {},
540 536 "outputs": [],
541 "prompt_number": 15
542 },
543 {
544 "cell_type": "code",
545 "collapsed": false,
546 "input": [],
547 "language": "python",
548 "metadata": {},
549 "outputs": []
537 "prompt_number": 13
550 538 }
551 539 ],
552 540 "metadata": {}
553 541 }
554 542 ]
555 543 } No newline at end of file
@@ -1,475 +1,487 b''
1 1 {
2 2 "metadata": {
3 "name": "Parallel Magics"
3 "name": ""
4 4 },
5 5 "nbformat": 3,
6 6 "nbformat_minor": 0,
7 7 "worksheets": [
8 8 {
9 9 "cells": [
10 10 {
11 11 "cell_type": "heading",
12 12 "level": 1,
13 13 "metadata": {},
14 14 "source": [
15 15 "Using Parallel Magics"
16 16 ]
17 17 },
18 18 {
19 19 "cell_type": "markdown",
20 20 "metadata": {},
21 21 "source": [
22 22 "IPython has a few magics for working with your engines.\n",
23 23 "\n",
24 24 "This assumes you have started an IPython cluster, either with the notebook interface,\n",
25 25 "or the `ipcluster/controller/engine` commands."
26 26 ]
27 27 },
28 28 {
29 29 "cell_type": "code",
30 30 "collapsed": false,
31 31 "input": [
32 32 "from IPython import parallel\n",
33 33 "rc = parallel.Client()\n",
34 34 "dv = rc[:]\n",
35 35 "rc.ids"
36 36 ],
37 37 "language": "python",
38 38 "metadata": {},
39 39 "outputs": []
40 40 },
41 41 {
42 42 "cell_type": "markdown",
43 43 "metadata": {},
44 44 "source": [
45 45 "Creating a Client registers the parallel magics `%px`, `%%px`, `%pxresult`, `pxconfig`, and `%autopx`. \n",
46 46 "These magics are initially associated with a DirectView always associated with all currently registered engines."
47 47 ]
48 48 },
49 49 {
50 50 "cell_type": "markdown",
51 51 "metadata": {},
52 52 "source": [
53 53 "Now we can execute code remotely with `%px`:"
54 54 ]
55 55 },
56 56 {
57 57 "cell_type": "code",
58 58 "collapsed": false,
59 59 "input": [
60 60 "%px a=5"
61 61 ],
62 62 "language": "python",
63 63 "metadata": {},
64 64 "outputs": []
65 65 },
66 66 {
67 67 "cell_type": "code",
68 68 "collapsed": false,
69 69 "input": [
70 70 "%px print a"
71 71 ],
72 72 "language": "python",
73 73 "metadata": {},
74 74 "outputs": []
75 75 },
76 76 {
77 77 "cell_type": "code",
78 78 "collapsed": false,
79 79 "input": [
80 80 "%px a"
81 81 ],
82 82 "language": "python",
83 83 "metadata": {},
84 84 "outputs": []
85 85 },
86 86 {
87 87 "cell_type": "code",
88 88 "collapsed": false,
89 89 "input": [
90 90 "with dv.sync_imports():\n",
91 91 " import sys"
92 92 ],
93 93 "language": "python",
94 94 "metadata": {},
95 95 "outputs": []
96 96 },
97 97 {
98 98 "cell_type": "code",
99 99 "collapsed": false,
100 100 "input": [
101 101 "%px print >> sys.stderr, \"ERROR\""
102 102 ],
103 103 "language": "python",
104 104 "metadata": {},
105 105 "outputs": []
106 106 },
107 107 {
108 108 "cell_type": "markdown",
109 109 "metadata": {},
110 110 "source": [
111 111 "You don't have to wait for results. The `%pxconfig` magic lets you change the default blocking/targets for the `%px` magics:"
112 112 ]
113 113 },
114 114 {
115 115 "cell_type": "code",
116 116 "collapsed": false,
117 117 "input": [
118 118 "%pxconfig --noblock"
119 119 ],
120 120 "language": "python",
121 121 "metadata": {},
122 122 "outputs": []
123 123 },
124 124 {
125 125 "cell_type": "code",
126 126 "collapsed": false,
127 127 "input": [
128 128 "%px import time\n",
129 129 "%px time.sleep(5)\n",
130 130 "%px time.time()"
131 131 ],
132 132 "language": "python",
133 133 "metadata": {},
134 134 "outputs": []
135 135 },
136 136 {
137 137 "cell_type": "markdown",
138 138 "metadata": {},
139 139 "source": [
140 140 "But you will notice that this didn't output the result of the last command.\n",
141 141 "For this, we have `%pxresult`, which displays the output of the latest request:"
142 142 ]
143 143 },
144 144 {
145 145 "cell_type": "code",
146 146 "collapsed": false,
147 147 "input": [
148 148 "%pxresult"
149 149 ],
150 150 "language": "python",
151 151 "metadata": {},
152 152 "outputs": []
153 153 },
154 154 {
155 155 "cell_type": "markdown",
156 156 "metadata": {},
157 157 "source": [
158 158 "Remember, an IPython engine is IPython, so you can do magics remotely as well!"
159 159 ]
160 160 },
161 161 {
162 162 "cell_type": "code",
163 163 "collapsed": false,
164 164 "input": [
165 165 "%pxconfig --block\n",
166 "%px %pylab inline"
166 "%px %matplotlib inline"
167 ],
168 "language": "python",
169 "metadata": {},
170 "outputs": []
171 },
172 {
173 "cell_type": "code",
174 "collapsed": false,
175 "input": [
176 "%%px\n",
177 "import numpy as np\n",
178 "import matplotlib.pyplot as plt"
167 179 ],
168 180 "language": "python",
169 181 "metadata": {},
170 182 "outputs": []
171 183 },
172 184 {
173 185 "cell_type": "markdown",
174 186 "metadata": {},
175 187 "source": [
176 188 "`%%px` can also be used as a cell magic, for submitting whole blocks.\n",
177 189 "This one acceps `--block` and `--noblock` flags to specify\n",
178 190 "the blocking behavior, though the default is unchanged.\n"
179 191 ]
180 192 },
181 193 {
182 194 "cell_type": "code",
183 195 "collapsed": false,
184 196 "input": [
185 197 "dv.scatter('id', dv.targets, flatten=True)\n",
186 198 "dv['stride'] = len(dv)"
187 199 ],
188 200 "language": "python",
189 201 "metadata": {},
190 202 "outputs": []
191 203 },
192 204 {
193 205 "cell_type": "code",
194 206 "collapsed": false,
195 207 "input": [
196 208 "%%px --noblock\n",
197 "x = linspace(0,pi,1000)\n",
209 "x = np.linspace(0,np.pi,1000)\n",
198 210 "for n in range(id,12, stride):\n",
199 211 " print n\n",
200 " plt.plot(x,sin(n*x))\n",
212 " plt.plot(x,np.sin(n*x))\n",
201 213 "plt.title(\"Plot %i\" % id)"
202 214 ],
203 215 "language": "python",
204 216 "metadata": {},
205 217 "outputs": []
206 218 },
207 219 {
208 220 "cell_type": "code",
209 221 "collapsed": false,
210 222 "input": [
211 223 "%pxresult"
212 224 ],
213 225 "language": "python",
214 226 "metadata": {},
215 227 "outputs": []
216 228 },
217 229 {
218 230 "cell_type": "markdown",
219 231 "metadata": {},
220 232 "source": [
221 233 "It also lets you choose some amount of the grouping of the outputs with `--group-outputs`:\n",
222 234 "\n",
223 235 "The choices are:\n",
224 236 "\n",
225 237 "* `engine` - all of an engine's output is collected together\n",
226 238 "* `type` - where stdout of each engine is grouped, etc. (the default)\n",
227 239 "* `order` - same as `type`, but individual displaypub outputs are interleaved.\n",
228 240 " That is, it will output the first plot from each engine, then the second from each,\n",
229 241 " etc."
230 242 ]
231 243 },
232 244 {
233 245 "cell_type": "code",
234 246 "collapsed": false,
235 247 "input": [
236 248 "%%px --group-outputs=engine\n",
237 "x = linspace(0,pi,1000)\n",
249 "x = np.linspace(0,np.pi,1000)\n",
238 250 "for n in range(id+1,12, stride):\n",
239 251 " print n\n",
240 252 " plt.figure()\n",
241 " plt.plot(x,sin(n*x))\n",
253 " plt.plot(x,np.sin(n*x))\n",
242 254 " plt.title(\"Plot %i\" % n)"
243 255 ],
244 256 "language": "python",
245 257 "metadata": {},
246 258 "outputs": []
247 259 },
248 260 {
249 261 "cell_type": "markdown",
250 262 "metadata": {},
251 263 "source": [
252 264 "When you specify 'order', then individual display outputs (e.g. plots) will be interleaved.\n",
253 265 "\n",
254 266 "`%pxresult` takes the same output-ordering arguments as `%%px`, \n",
255 267 "so you can view the previous result in a variety of different ways with a few sequential calls to `%pxresult`:"
256 268 ]
257 269 },
258 270 {
259 271 "cell_type": "code",
260 272 "collapsed": false,
261 273 "input": [
262 274 "%pxresult --group-outputs=order"
263 275 ],
264 276 "language": "python",
265 277 "metadata": {},
266 278 "outputs": []
267 279 },
268 280 {
269 281 "cell_type": "heading",
270 282 "level": 2,
271 283 "metadata": {},
272 284 "source": [
273 285 "Single-engine views"
274 286 ]
275 287 },
276 288 {
277 289 "cell_type": "markdown",
278 290 "metadata": {},
279 291 "source": [
280 292 "When a DirectView has a single target, the output is a bit simpler (no prefixes on stdout/err, etc.):"
281 293 ]
282 294 },
283 295 {
284 296 "cell_type": "code",
285 297 "collapsed": false,
286 298 "input": [
287 299 "def generate_output():\n",
288 300 " \"\"\"function for testing output\n",
289 301 " \n",
290 302 " publishes two outputs of each type, and returns something\n",
291 303 " \"\"\"\n",
292 304 " \n",
293 305 " import sys,os\n",
294 " from IPython.core.display import display, HTML, Math\n",
306 " from IPython.display import display, HTML, Math\n",
295 307 " \n",
296 308 " print \"stdout\"\n",
297 309 " print >> sys.stderr, \"stderr\"\n",
298 310 " \n",
299 311 " display(HTML(\"<b>HTML</b>\"))\n",
300 312 " \n",
301 313 " print \"stdout2\"\n",
302 314 " print >> sys.stderr, \"stderr2\"\n",
303 315 " \n",
304 316 " display(Math(r\"\\alpha=\\beta\"))\n",
305 317 " \n",
306 318 " return os.getpid()\n",
307 319 "\n",
308 320 "dv['generate_output'] = generate_output"
309 321 ],
310 322 "language": "python",
311 323 "metadata": {},
312 324 "outputs": []
313 325 },
314 326 {
315 327 "cell_type": "markdown",
316 328 "metadata": {},
317 329 "source": [
318 330 "You can also have more than one set of parallel magics registered at a time.\n",
319 331 "\n",
320 332 "The `View.activate()` method takes a suffix argument, which is added to `'px'`."
321 333 ]
322 334 },
323 335 {
324 336 "cell_type": "code",
325 337 "collapsed": false,
326 338 "input": [
327 339 "e0 = rc[-1]\n",
328 340 "e0.block = True\n",
329 341 "e0.activate('0')"
330 342 ],
331 343 "language": "python",
332 344 "metadata": {},
333 345 "outputs": []
334 346 },
335 347 {
336 348 "cell_type": "code",
337 349 "collapsed": false,
338 350 "input": [
339 351 "%px0 generate_output()"
340 352 ],
341 353 "language": "python",
342 354 "metadata": {},
343 355 "outputs": []
344 356 },
345 357 {
346 358 "cell_type": "code",
347 359 "collapsed": false,
348 360 "input": [
349 361 "%px generate_output()"
350 362 ],
351 363 "language": "python",
352 364 "metadata": {},
353 365 "outputs": []
354 366 },
355 367 {
356 368 "cell_type": "markdown",
357 369 "metadata": {},
358 370 "source": [
359 371 "As mentioned above, we can redisplay those same results with various grouping:"
360 372 ]
361 373 },
362 374 {
363 375 "cell_type": "code",
364 376 "collapsed": false,
365 377 "input": [
366 378 "%pxresult --group-outputs order"
367 379 ],
368 380 "language": "python",
369 381 "metadata": {},
370 382 "outputs": []
371 383 },
372 384 {
373 385 "cell_type": "code",
374 386 "collapsed": false,
375 387 "input": [
376 388 "%pxresult --group-outputs engine"
377 389 ],
378 390 "language": "python",
379 391 "metadata": {},
380 392 "outputs": []
381 393 },
382 394 {
383 395 "cell_type": "heading",
384 396 "level": 2,
385 397 "metadata": {},
386 398 "source": [
387 399 "Parallel Exceptions"
388 400 ]
389 401 },
390 402 {
391 403 "cell_type": "markdown",
392 404 "metadata": {},
393 405 "source": [
394 406 "When you raise exceptions with the parallel exception,\n",
395 407 "the CompositeError raised locally will display your remote traceback."
396 408 ]
397 409 },
398 410 {
399 411 "cell_type": "code",
400 412 "collapsed": false,
401 413 "input": [
402 414 "%%px\n",
403 415 "from numpy.random import random\n",
404 416 "A = random((100,100,'invalid shape'))"
405 417 ],
406 418 "language": "python",
407 419 "metadata": {},
408 420 "outputs": []
409 421 },
410 422 {
411 423 "cell_type": "heading",
412 424 "level": 2,
413 425 "metadata": {},
414 426 "source": [
415 427 "Remote Cell Magics"
416 428 ]
417 429 },
418 430 {
419 431 "cell_type": "markdown",
420 432 "metadata": {},
421 433 "source": [
422 434 "Remember, Engines are IPython too, so the cell that is run remotely by %%px can in turn use a cell magic."
423 435 ]
424 436 },
425 437 {
426 438 "cell_type": "code",
427 439 "collapsed": false,
428 440 "input": [
429 441 "%%px\n",
430 442 "%%timeit\n",
431 443 "from numpy.random import random\n",
432 444 "from numpy.linalg import norm\n",
433 445 "A = random((100,100))\n",
434 "norm(A, 2) "
446 "norm(A, 2)"
435 447 ],
436 448 "language": "python",
437 449 "metadata": {},
438 450 "outputs": []
439 451 },
440 452 {
441 453 "cell_type": "heading",
442 454 "level": 2,
443 455 "metadata": {},
444 456 "source": [
445 457 "Local Execution"
446 458 ]
447 459 },
448 460 {
449 461 "cell_type": "markdown",
450 462 "metadata": {},
451 463 "source": [
452 464 "As of IPython 0.14, you can instruct `%%px` to also execute the cell locally.\n",
453 465 "This is useful for interactive definitions,\n",
454 466 "or if you want to load a data source everywhere,\n",
455 467 "not just on the engines."
456 468 ]
457 469 },
458 470 {
459 471 "cell_type": "code",
460 472 "collapsed": false,
461 473 "input": [
462 474 "%%px --local\n",
463 475 "import os\n",
464 476 "thispid = os.getpid()\n",
465 477 "print thispid"
466 478 ],
467 479 "language": "python",
468 480 "metadata": {},
469 481 "outputs": []
470 482 }
471 483 ],
472 484 "metadata": {}
473 485 }
474 486 ]
475 487 } No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now