From ed58515d806b1c959d8f08f459badc954e4456f3 Mon Sep 17 00:00:00 2001 From: kstohr Date: Thu, 2 May 2019 11:38:59 -0400 Subject: [PATCH 1/2] test travis --- Pipfile | 13 ++ Pipfile.lock | 101 +++++++++++++ build/lib/pycontest/__init__.py | 0 build/lib/pycontest/elastic_collisions.py | 48 ++++++ build/lib/pycontest/movies.py | 126 ++++++++++++++++ build/lib/pycontest/simulation.py | 140 ++++++++++++++++++ build/lib/pycontest/transport.py | 21 +++ build/lib/pycontest/utils.py | 30 ++++ build/lib/pycontest/version.py | 56 +++++++ build/lib/tests/__init__.py | 0 build/lib/tests/test_elastic_collisions_1d.py | 13 ++ build/lib/tests/test_elastic_collisions_2d.py | 23 +++ build/lib/tests/test_hypothesis.py | 14 ++ build/lib/tests/test_regression.py | 26 ++++ .../lib/tests}/test_simulation.py | 20 +-- build/lib/tests/test_simulation_fixture.py | 54 +++++++ build/lib/tests/test_transport.py | 34 +++++ dist/pycontest-0.1.dev0-py3.7.egg | Bin 0 -> 22602 bytes pycontest.egg-info/PKG-INFO | 31 ++++ pycontest.egg-info/SOURCES.txt | 22 +++ pycontest.egg-info/dependency_links.txt | 1 + pycontest.egg-info/requires.txt | 3 + pycontest.egg-info/top_level.txt | 2 + pycontest/transport.py | 24 ++- solutions/test_elastic_collisions_1d.py | 45 ------ solutions/test_elastic_collisions_2d.py | 39 ----- solutions/test_hypothesis.py | 33 ----- solutions/test_regression.py | 37 ----- solutions/transport.py | 29 ---- tests/test_elastic_collisions_1d.py | 32 ++++ tests/test_elastic_collisions_2d.py | 18 ++- tests/test_hypothesis.py | 25 +++- tests/test_regression.py | 23 ++- tests/test_simulation.py | 20 ++- tests/test_transport.py | 48 +++--- solutions/travis.yml => travis.yml | 0 36 files changed, 906 insertions(+), 245 deletions(-) create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 100644 build/lib/pycontest/__init__.py create mode 100644 build/lib/pycontest/elastic_collisions.py create mode 100644 build/lib/pycontest/movies.py create mode 100644 build/lib/pycontest/simulation.py create mode 100644 build/lib/pycontest/transport.py create mode 100644 build/lib/pycontest/utils.py create mode 100644 build/lib/pycontest/version.py create mode 100644 build/lib/tests/__init__.py create mode 100644 build/lib/tests/test_elastic_collisions_1d.py create mode 100644 build/lib/tests/test_elastic_collisions_2d.py create mode 100644 build/lib/tests/test_hypothesis.py create mode 100644 build/lib/tests/test_regression.py rename {solutions => build/lib/tests}/test_simulation.py (63%) create mode 100644 build/lib/tests/test_simulation_fixture.py create mode 100644 build/lib/tests/test_transport.py create mode 100644 dist/pycontest-0.1.dev0-py3.7.egg create mode 100644 pycontest.egg-info/PKG-INFO create mode 100644 pycontest.egg-info/SOURCES.txt create mode 100644 pycontest.egg-info/dependency_links.txt create mode 100644 pycontest.egg-info/requires.txt create mode 100644 pycontest.egg-info/top_level.txt delete mode 100644 solutions/test_elastic_collisions_1d.py delete mode 100644 solutions/test_elastic_collisions_2d.py delete mode 100644 solutions/test_hypothesis.py delete mode 100644 solutions/test_regression.py delete mode 100644 solutions/transport.py rename solutions/travis.yml => travis.yml (100%) diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..aede842 --- /dev/null +++ b/Pipfile @@ -0,0 +1,13 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +pytest = "*" +numpy = "*" + +[requires] +python_version = "3.7" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..9956e7f --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,101 @@ +{ + "_meta": { + "hash": { + "sha256": "8c364fbe4be6bc611b2c1423f61b9f0a5c5a023d80ba3cc114fe49a5b29a4b8e" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.7" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "atomicwrites": { + "hashes": [ + "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", + "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" + ], + "version": "==1.3.0" + }, + "attrs": { + "hashes": [ + "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", + "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" + ], + "version": "==19.1.0" + }, + "more-itertools": { + "hashes": [ + "sha256:2112d2ca570bb7c3e53ea1a35cd5df42bb0fd10c45f0fb97178679c3c03d64c7", + "sha256:c3e4748ba1aad8dba30a4886b0b1a2004f9a863837b8654e7059eebf727afa5a" + ], + "markers": "python_version > '2.7'", + "version": "==7.0.0" + }, + "numpy": { + "hashes": [ + "sha256:0e2eed77804b2a6a88741f8fcac02c5499bba3953ec9c71e8b217fad4912c56c", + "sha256:1c666f04553ef70fda54adf097dbae7080645435fc273e2397f26bbf1d127bbb", + "sha256:1f46532afa7b2903bfb1b79becca2954c0a04389d19e03dc73f06b039048ac40", + "sha256:315fa1b1dfc16ae0f03f8fd1c55f23fd15368710f641d570236f3d78af55e340", + "sha256:3d5fcea4f5ed40c3280791d54da3ad2ecf896f4c87c877b113576b8280c59441", + "sha256:48241759b99d60aba63b0e590332c600fc4b46ad597c9b0a53f350b871ef0634", + "sha256:4b4f2924b36d857cf302aec369caac61e43500c17eeef0d7baacad1084c0ee84", + "sha256:54fe3b7ed9e7eb928bbc4318f954d133851865f062fa4bbb02ef8940bc67b5d2", + "sha256:5a8f021c70e6206c317974c93eaaf9bc2b56295b6b1cacccf88846e44a1f33fc", + "sha256:754a6be26d938e6ca91942804eb209307b73f806a1721176278a6038869a1686", + "sha256:771147e654e8b95eea1293174a94f34e2e77d5729ad44aefb62fbf8a79747a15", + "sha256:78a6f89da87eeb48014ec652a65c4ffde370c036d780a995edaeb121d3625621", + "sha256:7fde5c2a3a682a9e101e61d97696687ebdba47637611378b4127fe7e47fdf2bf", + "sha256:80d99399c97f646e873dd8ce87c38cfdbb668956bbc39bc1e6cac4b515bba2a0", + "sha256:88a72c1e45a0ae24d1f249a529d9f71fe82e6fa6a3fd61414b829396ec585900", + "sha256:a4f4460877a16ac73302a9c077ca545498d9fe64e6a81398d8e1a67e4695e3df", + "sha256:a61255a765b3ac73ee4b110b28fccfbf758c985677f526c2b4b39c48cc4b509d", + "sha256:ab4896a8c910b9a04c0142871d8800c76c8a2e5ff44763513e1dd9d9631ce897", + "sha256:abbd6b1c2ef6199f4b7ca9f818eb6b31f17b73a6110aadc4e4298c3f00fab24e", + "sha256:b16d88da290334e33ea992c56492326ea3b06233a00a1855414360b77ca72f26", + "sha256:b78a1defedb0e8f6ae1eb55fa6ac74ab42acc4569c3a2eacc2a407ee5d42ebcb", + "sha256:cfef82c43b8b29ca436560d51b2251d5117818a8d1fb74a8384a83c096745dad", + "sha256:d160e57731fcdec2beda807ebcabf39823c47e9409485b5a3a1db3a8c6ce763e" + ], + "index": "pypi", + "version": "==1.16.3" + }, + "pluggy": { + "hashes": [ + "sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f", + "sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746" + ], + "version": "==0.9.0" + }, + "py": { + "hashes": [ + "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", + "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53" + ], + "version": "==1.8.0" + }, + "pytest": { + "hashes": [ + "sha256:3773f4c235918987d51daf1db66d51c99fac654c81d6f2f709a046ab446d5e5d", + "sha256:b7802283b70ca24d7119b32915efa7c409982f59913c1a6c0640aacf118b95f5" + ], + "index": "pypi", + "version": "==4.4.1" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" + } + }, + "develop": {} +} diff --git a/build/lib/pycontest/__init__.py b/build/lib/pycontest/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/pycontest/elastic_collisions.py b/build/lib/pycontest/elastic_collisions.py new file mode 100644 index 0000000..e13140d --- /dev/null +++ b/build/lib/pycontest/elastic_collisions.py @@ -0,0 +1,48 @@ +import numpy as np + +def collision_1d(v1_i, v2_i, m1=1, m2=1): + """ 1d elastic collisions + + Arguments: + v1_1, v2_i: initial values of particles velocities + m1, m2: masses + + Returns: + final velocities after collisions + """ + + v1_f = (m1 - m2) / (m1 + m2) * v1_i + 2 * m2 / (m1 + m2) * v2_i + v2_f = (m2 - m1) / (m1 + m2) * v2_i + 2 * m1 / (m1 + m2) * v1_i + return v1_f, v2_f + + +def collision_2d(v1, v2, r12, m1, m2): + """ 2d elastic collisions + + Arguments: + v1_1, v2_i: initial values of particles velocities + m1, m2: masses + r12: vector between particles: r2-r1 + + Returns: + final velocities after collisions + """ + + if np.linalg.norm(r12) == 0: + raise Exception("two particles are exactly in the same place, " + "consider using smaller time step") + n_vect = r12 / np.linalg.norm(r12) + + # scalars + v1n = np.dot(v1, n_vect) + v2n = np.dot(v2, n_vect) + v1s = v1 - v1n * n_vect + v2s = v2 - v2n * n_vect + + v1n_f, v2n_f = collision_1d(v1n, v2n, m1, m2) + v1n_f *= n_vect + v2n_f *= n_vect + + v1_f = v1s + v1n_f + v2_f = v2s + v2n_f + return v1_f, v2_f \ No newline at end of file diff --git a/build/lib/pycontest/movies.py b/build/lib/pycontest/movies.py new file mode 100644 index 0000000..a3da8a5 --- /dev/null +++ b/build/lib/pycontest/movies.py @@ -0,0 +1,126 @@ +import matplotlib +matplotlib.use("TkAgg") +import matplotlib.pyplot as plt +import matplotlib.animation as anm +import numpy as np +import math as mt +import pdb + +""" +Matplotlib Animation Example + +author: Jake Vanderplas +email: vanderplas@astro.washington.edu +website: http://jakevdp.github.com +license: BSD +Please feel free to use and modify this, but keep the above information. Thanks! +""" +class Movie_2d: + + def __init__(self, fun, dt, t_max, loc, vel, domain, mass, radius=10): + self.dt = dt + self.fps = int(mt.floor(1 / dt)) + self.n_max = int(mt.floor(t_max / dt)) + self.loc = np.copy(loc) + self.vel = np.copy(vel) + self.domain = domain + self.mass = mass + self.radius = radius + self.fun = fun + + self.fig = plt.figure() + self.fig.subplots_adjust(left=0.1, bottom=0.1, right=.9, top=.9) + + self.ax = self.fig.add_subplot(111, aspect='equal', autoscale_on=False, xlim=(domain[0]), ylim=domain[1]) + for el in range(loc.shape[0]): + self.circ = plt.Circle(self.loc[el, :], self.radius, color='r', fill=False, linewidth=2) + self.ax.add_patch(self.circ) + + self.frame = plt.Rectangle([0, 0], domain[0][1] - domain[0][0], domain[1][1] - domain[1][0], fc='none') + self.ax.add_patch(self.frame) + + def generate_frame(self, i): + # pdb.set_trace() + loc, vel = self.fun(self.dt, self.mass, self.radius, self.loc, self.vel, self.domain) + #pdb.set_trace() + for el in range(loc.shape[0]): + self.circ.set_center(loc[el, :]) + return self.circ, self.frame + + def animate(self, name): + animation = anm.FuncAnimation(self.fig, self.generate_frame, frames=self.n_max, interval=10, blit=True) + + fps = self.fps + try: + animation.save(name + '.mp4', fps=fps, extra_args=['-vcodec', 'libx264']) + except ValueError: + print("\n mp4 file can not be created, will try to create html") + try: + animation.save(name + '.html', fps=fps, extra_args=['-vcodec', 'libx264']) + except: + print("\n html also can not be created") + +class Plot_2d: + + def __init__(self, dt, t_max, loc1_0, loc2_0, vel1_0, vel2_0, + domain, radius, m1=1, m2=1, nr=4, nc=5): + self.fig = plt.figure() + self.nr = nr + self.nc = nc + self.dt = dt + self.t_max = t_max + self.loc1_0 = loc1_0 + self.loc2_0 = loc2_0 + self.vel1_0 = vel1_0 + self.vel2_0 = vel2_0 + self.domain = domain + self.radius = radius + self.m1 = m1 + self.m2 = m2 + + + def simulation(self): + tt = 0 + ii = 0 + loc1, loc2 = self.loc1_0, self.loc2_0 + vel1, vel2 = self.vel1_0, self.vel2_0 + while tt <= t_max: + [loc1, loc2], [vel1, vel2] = \ + simulation_step(dt=self.dt, m1=self.m1, m2=self.m2, radius=self.radius, + loc1_0=loc1, loc2_0=loc2, v1_0=vel1, v2_0=vel2, + domain=self.domain) + tt += dt + if ii % 5 == 0: + #pdb.set_trace() + self.plotting(ii/5+1, loc1.copy(), loc2.copy(), vel1.copy(), vel2.copy()) + ii += 1 + plt.show() + + def plotting(self, ii, loc1, loc2, vel1, vel2): + ax = self.fig.add_subplot(self.nr, self.nc, ii, aspect='equal') + # ax = plt.gca() + # ax.quiver(r1[0], r1[1], r12[0], r12[1], angles='xy', scale_units='xy', scale=1, color="k", width=1.e-3) + # ax.quiver(0, 0, r1[0], r1[1], angles='xy', scale_units='xy', scale=1, color="k", width=1.e-3) + # ax.quiver(0, 0, r2[0], r2[1], angles='xy', scale_units='xy', scale=1, color="k", width=1.e-3) + # ax.quiver(r1[0], r1[1], v1[0], v1[1], angles='xy', scale_units='xy', scale=1, color="g") + # ax.quiver(r2[0], r2[1], v2[0], v2[1], angles='xy', scale_units='xy', scale=1, color="g") + # ax.quiver(r1[0], r1[1], v1n[0], v1n[1], angles='xy', scale_units='xy', scale=1, color="y") + # ax.quiver(r2[0], r2[1], v2n[0], v2n[1], angles='xy', scale_units='xy', scale=1, color="y") + # ax.quiver(r1[0], r1[1], v1s[0], v1s[1], angles='xy', scale_units='xy', scale=1, color="y") + # ax.quiver(r2[0], r2[1], v2s[0], v2s[1], angles='xy', scale_units='xy', scale=1, color="y") + # + # ax.quiver(r1[0], r1[1], u1[0], u1[1], angles='xy', scale_units='xy', scale=1, color="b") + # ax.quiver(r2[0], r2[1], u2[0], u2[1], angles='xy', scale_units='xy', scale=1, color="b") + # ax.quiver(r1[0], r1[1], u1n[0], u1n[1], angles='xy', scale_units='xy', scale=1, color="c") + # ax.quiver(r2[0], r2[1], u2n[0], u2n[1], angles='xy', scale_units='xy', scale=1, color="c") + # ax.quiver(r1[0], r1[1], u1s[0], u1s[1], angles='xy', scale_units='xy', scale=1, color="c") + # ax.quiver(r2[0], r2[1], u2s[0], u2s[1], angles='xy', scale_units='xy', scale=1, color="c") + + # ax.quiver(X, Y, V, U, angles='xy', scale_units='xy', scale=1, color="b") + c1 = plt.Circle(loc1, self.radius, color='r', fill=False) + c2 = plt.Circle(loc2, self.radius, color='r', fill=False) + ax.set_xlim([0, 100]) + ax.set_ylim([0, 100]) + # ax.set_aspec("equal") + ax.add_patch(c1) + ax.add_patch(c2) diff --git a/build/lib/pycontest/simulation.py b/build/lib/pycontest/simulation.py new file mode 100644 index 0000000..9ccd19a --- /dev/null +++ b/build/lib/pycontest/simulation.py @@ -0,0 +1,140 @@ +import numpy as np +import math as mt + +from scipy.spatial.distance import pdist, squareform + +from pycontest import elastic_collisions as ec +import pycontest.movies as mv +from pycontest.transport import transport + + +def simulation_step(dt, mass, radius, loc, vel, domain): + """ calculating positions and velocities of particles after one step + + Arguments: + dt: time steps + mass: masses + radius: radius for all balls + loc: locations of balls + vel: 2d velocities of balls + domain: domain for the box + Returns: + positions and velocities of particles after one step + """ + + + # transport + loc = transport(loc, vel, dt) + + r12_x = squareform(pdist(loc, lambda r1, r2: r1[0] - r2[0])) + r12_y = squareform(pdist(loc, lambda r1, r2: r1[1] - r2[1])) + r12 = np.stack((r12_x, r12_y), axis=-1) + dist = np.apply_along_axis(np.linalg.norm, 2, r12) + + # finding particles that are colliding + ind1, ind2 = np.where(dist < 2 * radius) + unique = (ind1 < ind2) + + ind1 = ind1[unique] + ind2 = ind2[unique] + + # collisions + for id1, id2 in zip(ind1, ind2): + vel[id1], vel[id2] = ec.collision_2d(vel[id1], vel[id2], r12[id1, id2], mass[id1], mass[id2]) + + # find outside domain points ... + out_left = (loc[:, 0] < domain[0][0] + radius) + out_right = (loc[:, 0] > domain[0][1] - radius) + out_top = (loc[:, 1] < domain[1][0] + radius) + out_bottom = (loc[:, 1] > domain[1][1] - radius) + + # ... change their velocities + vel[out_left | out_right, 0] *= -1 + vel[out_top | out_bottom, 1] *= -1 + + return loc, vel + + +def simulation(t_max, dt, mass, radius, loc_0, vel_0, domain, t0=0): + """ calculating positions and velocities of particles after t_max + + Arguments: + t_max: length of the simulation + dt: time steps + mass: masses + radius: radius for all balls + loc_0: initial locations of balls + vel_0: 2d initial velocities of balls + t0: initial time (0 as default) + domain: domain for the box + Returns: + positions and velocities of particles at t_max + """ + t = t0 + loc = np.copy(loc_0) + vel = np.copy(vel_0) + while(tJsq2PDoNEt_2jC@Ap}eP%f?+hqIq_@GB$~s-?bx?X}7arS40`2c-r*~VYlXuRG)~w zvJiUJJX=*q$jnv{hpHi}4|}93-9pv7wl|$BgIF?{A0vAxg;9*eW8k*|#9%GSM*MMw zU8sx%;pG8^t{g#v(~TV1!U8g>^QU5IvX3vtEWZUT+t7;eDB8xGXHdBeOisffL>c)H z4>~>gqo?-L=nL0!M}BUclQ*p+6a^iKmJuFB(msL)P*mckz3JO)+NE(Niv~F{WIBas z@@f@{rEm0XsK;l>hxkBG#|6zm9=nsT0}@2I8yCPQFDtDTtyqB%?{=Vn^o3sb~qL94C)lHlrr)t*FoSceL=Qw(SGgMEBcaWvK%J*qBpXO>y?O!^Q zjIls}$-g}c3gQfXU<@j+OW*U#7?Gx?Rc-+Wz=ntf9wx90b)wYj9ZOj&+JaWOz^ zS!n^Uwxv7JR+eTkx!$a{Ro-PHO8PAh-R8(F{+l)&cUg1C_w0Bz_NgwLXuG|KBMyas zAco5}rgr=jDQusp`GceXLX48U%2yFFC3LZ=PJ*HeWjUzg;3M z(@vnaNfFdc7t|=E;!)0;z3+2%x;s85Mb+hf7rR5lj}8LS`?(u(31Oi9=0;xUbW)Xo z%IpA!yYP_P{qhX7jpqrBdrZAyKqgM=dU1r9*ocPU$S$yv)8jWaW%32 zb8VV3naL>{03H1{2=d_Hf1(3WAoocH^eOrO`1^Aidk-T!TW1p|X9hhz3tJ0kJw1AR z5756o_YaYEEq+GZC(H7{fPe`8L1bcW;N)y!q-SJjZEfLXVQ1^~Nj|7F7Q04>)cHcA zIcdVn(gBT2_Y^PUODMo-=-?GN&<}F9{`O+iW9602B@P1#hNhw;%P|@~De*lCYf}j9 zyOGKrr@ymiWnNZS-8*YTW_#cGoVEI}`2n8Q3XJ)@3w#yiJh3PlhhZ7>>)a+q5PPU2 z4}7ALSPi&|in4G&?QJmd8c^ZyqCyLnLf1rnIGSI3OLUmXAoR$-Mp>Qf$B3ikgDLQa z;YSaX7@HyTt`J9C#+bYb?x34D&xwnNU^FZCn%KSrlwk$EU*L0Cm-Mi`=kyV88$zc% zjCr11LQm9Ta-pC^l;rdpl~s436HCvOcm7`<`PNIEw2rQP1&*3H?I!)J3X)sEH=Dd zN-fZbG<`7s{8fxZ@DYIc7ux?w{fSU%cLf{>X!X-fp#DSZHg>KSpRl#}Xi~SaTV+Rj zuhHvm!=xptFtnKP$3p#XYbAh2sbH;BM+6V1SzyhMR-Bk@`0;`#cCs$V;>Ly)HC%Xc z(Wb`D$^e_M5UcVNX7Fg?IMtUdqQl+q!?8uH3YVMjI8~xcSiK;z9MyZAN}+M<8S22p zD_G6LW8SM1x3g)H$Ah9&rX{vPoi?i_Tky6X_3_02CJ}kKH)Z9EVbI2wS0KNB4vHc( zEncG0D0|t3t8eeuh(5m_NBJi?^fd~Lect%$qDbp$-Ev1KpEu}%d5h!AvK2Kg0V>C^ znP7Wp3E#IHw?jMPDu+_QA_#au;vuuhu~yE5I3!PFvT47 zzNO2;2e0wVeK+_%u<@iL8$~6Qu;U>IB=4_@`J5$L6`ZbQ2I*n-kzSN4_+K4j5%CSh zcJebAmuP|L8J&>!hys)}e^RQ~vd2)q&7iz0=R<)-b2NPIqu(E$NXe}$K(h6(*`~0pTXz4P^DgxgiG7?*y5v^?FOdh)vHGw^65QXb; zpfLVXIfx^`ESM~y{o{$|KVjv*Ra$75sIp;HJ!`HZ%Ymtq+F~{nTU-vAwHrMpmZ?K$ z!6}u|&Cy?@yV-Dg`moD;uX=8rMh@b96JvN==4wh6{ecX2E{D8*Z*XY>Q$XGOayse;_HvS=H1)7NHX!K@38R>bGXQU*=u%asM7_pbuZ1 zV40Vv4DaD*<7+6!iLepc7XpK4N4(?sG6VZ4SCDxe{5ngzB|JsTv}_Hba~-Tvc?M;Z z)8GK@?{9{|V!FiKPc_#Hl4i#AX${%DBl=S;BkT4!Xi^;&i# zeBGs;@(iw1E^<~vcj?0WI>}uL;5{w8@MS-HxOvU?hI=pLm_#D1UU*p;pQi0+7-K<> zlZ!>e$*oj;XIC6`&g+)opbRvh!0B1mnD>yboLj^Lce78OJ|>E@viyMm=?bJ|(??rBoqy?Pj`1gq z`73I0vaoTnHgNv*{GaY%U3J1SQxd8Bl&ZZm%s&W<&|+7CT*bq`>rACM1G`@`0|pKZ zVs&M--);(#ab&^toHNzlKmYLbqSmh@KuvaC3d!eJmcJ`u1~^ASvlIk6wnP*G801L+Gw@A6&>J>_LS`6@2k>Sd2(TXO*Y~vU;!nR0P9UwY7_qeJ0 zNzhg^6Cg1iMfz7~V0RrI9U-|fZL26yR+r)8d@7ibS|@J_-8+2j6+_Bm;VM?Z>5*{*u8{aowqU~KbGZ|KIk16s=79vw6)i5wDBR}Wzw6h9m&nIMh9Vh zWY(s%(#%PHY0Y(XG$HjZup|t-LOfcB82L>C_$*z0!e$7=VW5gA@_~h^AD%g;XqX+o zK(`5Q%gpTRCQJSnbIBCKW16_(f$o$vZF&1dXd^`aqM5(L2F}_jop)o>lO?Euse<@f zER{r%!;G(NHTZBf#(2*kF;H5YvyC2a1N~01K=_J7fr`z1ECD41T0yJu*8?L-inai` zQZ-oRDf(UwGGlecc()r1Ei1&T+4wF$#|-DCcB!iS{_fogP#OWO}_+QUESf zuVGOiV#y-^SkEs?$IRlw@Dz-`x%~-rPg;Id$o+FFS|7!N?2a)XoUoAAGsvK-OE+u4 z^NR!pD&*^Aemk}Y>Y*NY9Uyt9ZR>>RIcT{$p0f+`x1X%TG3Kw}E=eEmIsh?nV1DK-W+KDZ?D(;r#yOq8OtX=hZ__jO7(Pbip8_AS zR3C&PZ}^ai%?F_ZXoK8>DQ$!|itm%_BsAHIfA)zM;)pBkD4RvcH`DQXtlDG+yN4zs zg)O>zpl%7R1b1W$Ow9r5ia~9NVs}Q`%egzCi1BOdiJ;luV!$MsGwPpmH{{CmfTIaTNzp^@$T$ zf-R1!-z&;#Q1@HPZ3R2546HK+i^@1(ra%}5Tg2{)7xZEwzSY#SsIi>QYfd4_Dc>!M z-^ick#+6XmvQxerFVtBeb!~9|cJ~(;!)~EKJ)JP#*)?UZixrQvf_%65{fLV9%DSL| zlQ~?xn9BZiaPFyn^KMXxGe2nCvik93AKf?0>I3AT#!l}T3={b?+mZgvlm28`e;GSx zM*~|Ydpk$xPg@tGz$7=oh;()nsbZc;!cBTaT-+K8IZYhdWgJAyWo2X+f97z^9UrzUg-CKVd6N$%FEp+^SnQqi1H66x;R@{|3~f+FKgM) zh!pa`J-C%(5(ph&4X5Ee0~>tSza{ro@lA{*<@r8kuN6R99QBnB%#+0~=Rj$w1F4sM zmqHNYG{ik0m6u>M*=7?^+e6rZ3${1M3LjlIT*QPjpcc3$22qF|KpS!cb^%t#<_@PZ zKhRY+S!ypAYQ5*7z~%9%K+jQ87XITlkz<}GN@ZP1rguXFk-ng2xn*xZXonle)gv8W zq^JChXps4(Tfr?8PojHI>f)?jRz+Ag#N|gw4JT)MGj{vz>ahQ}E+flgZ})Ma7@AYl zu}iqr1-r9%&~^AtlottmHJLbn$&V2;@W*`jmTx<#|3sR$)*Oz{XE_26@;_@ae<97) z#PLry{fRRlWgEL)cDTjN?e1z<&$^TV+#|Ee+ew zicAYdzLy)O5&W01^a7Gk>__BBta_8gZiF(QpDM(~)*)&_ywL5ovNDcXK_!j98>9Ly zg(FloS}{aZgVs4GINUk{Xct=Zp= z%JNTp^uyK)?)KBD0L$=AI4Tkn3|L{2?Rc8eBF>I)QyvSbobpewsB~GSye5LVse{^9 z$rE>N?+4?gMZZCl@_rapd8R1yZ$DDsGkC8%`N-}`XmSK+?gKPTA2c+Rb9`8b`CO&K z>XTzTdi$JVe)M=ra(C^{kOuQV6)0bXC^D-f&8x|{xC5sFB$Ufb6Z4TNt!{tUBMh$w zMvlmKb?B!?VHd+6eU|SEn(_~AUwsm6w#vHoTW8H%ZxB`RtNML!fv^&Oi;?twiCz%5 zfxq;!`{vY!_sAZsORy&Q6BYueSnMq$61BR_PN@wYWrhQAJNPhzYhx4an(yIid1PLX z#7P-($rG4CWhF=GhI9-%kt%Xp!a97CUNSQU2ET=*Y3GwC9E6j_9O#A|GJ+l`hR_B`qTB!U8J1v3yp=8=YA_{MG4rQ`R^~)%{Tr9FL zh~X@honiU|llrJFGd*~k8CjECvm~tgx=7;TZz`%tCD8MH;gOE zgyc>puYX;)oM^w@+bntCS)|t{dfX`lLm~4lOz4%hBTM76|EQgO%8>S!Sc_|`xiNP7 zL5<-{AZj-hg7S{JlV!)%Jo3$Mz;`*pUA1a*uyJ}Rx6yh?S-u&KW>x^do)~WtsoLEq zoE&LcGaAwoZk=i|f+C~r<8NiKKgIT4-l}M)Pg@rMdFcLO)AaN{RoB4C+(b|BuR5BM zy@#{8oh=;;$EO+_IqmH48v=oBx0D(R-WoxW_CO6F2x^amkbcDs#A3yIQ)NaA2u2=Y z7%Q3`&q8}EN*>=q=~^4ud)ro_ zPkeOm3LMqKWp8_;D&{J8B@E&Zssj6$+fP=UK@EReW0=p94#&T}{r{$*_s{njRFbtt zWkPG_WBF;Em=`H-DN43yLed1!k_ST^>>o)+s~;quQgFNk4SIvRESI4?8F~+vm8atf zMBj^CAFT34+aG;OWTnUo!~K?jb+c38h3|(oJ0F1?+R)AfIz;q&aS%+n1_3t3B{1uZYR3RwV+^ZhfRas< zDx7UT9cxQdG2jIQ*0acE4CNNr#>z~PkeBOC+(+C48;~-2OB5O~RMG79~@ZqEO^MNW=i;js$xI7_e+xAottR+sUvnjtAmT5K}!d z_i5D(5$oLuNILFb4P}Uf4i0*nil0XIRC|kMOPPxZLlcrV5~d@f(86)3W7-mq^L_jM z^4mSDsLE>`rTo5ul77#5%J2K7z*;6h{}BQ2$@TVko{T}UnpWlY{p?${ga?TRv0c;E zhjgk&)atQPr|r8$!>)(oOBq{CO>2Z~SY$q+AkP^Eh?S`z;Ll5`?;!N+P3E+n_Jn% zj7FQ50?Xm*iuQvAeq!z2XX(DXFXKA?3_+Q{P5RWb=k(&gxrA0W>+Cj^B6h5AjaIKg zKt)J%IbhY=4>hIEhgh^-CAM;Nw1GGE+N4cVoyE-sQQo>fV&OBF$&mLW;*7p8Z0vxu$M78q=uF`Pb?O)!O|j1g zz>dZok2*I)lI_EG`WEy@pEh7EO1}gpepxlk6P$h9{qp$wCiI~9F>v?;QPVSj*g&HF zK$qHNg#ixP<-sKXr?dUHzW6?Qby5J zxcWZH&Tc>T@5Ty8Lfo9xjhyf>=Y-L)Nu@plQZQYYW*P=Cxf1_~t3Fj|C-x)XKM3KV2Q_hQl^B{U- zopq$hZ|_Z`zGLBSKZw}t^1<%LK4Rml(^9zr)*4#_0@;FHf-|9T9&n}c-eSc?=Wcba=sG0fal-wBWwf z{L<(L?i`Fb*(IwgA&#S~6B)5DUXy6WBIPpZcaT6H06-S8MHVJWGA7BnIjFMnLGo!O z>0&&8d$bk>Hd!2Cb|IoMhkNHCd5eZOmS3>}k{VwD95! zC=@Q6B)8Bu@T+{qyU4r4rnX#22VkXj%h{-8-nv^HfF)g=W2kW3q+`0b#vh``9^NVO zN7w1x@D`0+$1P>pd{jqyrP+w)@zCv(%fRs-R4p|p>WmaWr?fRzI8q+WGKZbSQ&Ll> za_hfo8u)-@L1cd6e3bzfcl*wmj&8+%w>6al8CNdlMOzDE=>d;gAr%oS)kz&Nzz9s*x5hJ=LK|5&sXBl$$R)DLvaSu~#zk`mj*xQNP8nHN}ZXoUCGMy~ihIZ{ActWts2MME%S zUT_t{vhC2`^{YppzoP+!ASU$gIL8PSy`Z*MKvNF$6$RlLweKb=*<7@hzLHLU{av^I_9GM&%H=}8OpY=)NVCxF|d}dcc9;qw#NC7ibmIacGNULC&0Kk zPY{jJGj+-PdsCveoI_$XG{gaFRRw3?G+9>Vb6cvI7t(`^@WkxxfDaX0m5z=$Vch5? zx>)p{=F-#45$y1E!{|hgY6Z*d7KuFY8RRE;2{6)7MM&;4l|-ZiJ?h=~ffb5AP<@Q0 zx=I5}%^Y2h!PAS4SHRQZP3surJ5EXv^zK7yrEDiO@6YJXTEK0=)S>N0K=>|^<2l5e z%SiIDrZ`<4q?q4^27wd|o~p|ibZ~CU+kcc}Y-|s-NXskK-75v>5_v~Yi$8MpRgPl! zHjf34X?_tPC(AZ*DZapg@67h^BkDd#U~O{7G1t!ZOmJs(srW|a>wn&Y?8_OWa`n^s zou=&$4=5{tPjy10zLrYEs?Oej!T})ZUc){v{IH@8v`B5;W~z~DyU6+Am!sJoH+HtO z9AmshfE$5s-7l+r0c-5i8gR`wcxbs@>8i10m%6-**QgR&r>`WjzG3ZMc{lO#w~F>3 zkKdeMORxq31eA&Z1jO)fJ^tUS+yC_XO==T%Bmnfw6U2^l&Sq3ITr={dV=Plbe9S{5 z3D`8sOgvRd$uSt$raU@$OBZdEQi`|jWBMub%I4r?p`H4097y_~&<=-)X)J!idR$Nb z!NHB1L`a=#wyV30BCL7msy{s6ylN|SyFBllUS@U-6$2{oQw8|m-x3R6H9U?hdYpjd zy>*n(N*H&jl1EgFu^TH&Um)0CaVL-)t#mgyECQl^qTWI106GsCZ_IQ-w%^yBLR6R7 z$5?K-@>RMAV1`fZ8p|esJ~7kO8_Ju&R?RDUoq*@FMbOtX=b@`kOruB8*O>?wCm*pz zEG$KOS2kEVr~I-As-9)j^>oZud!)Aejjp@9=$>6$H*`qe)oYhk+t36noIG~ZaC!z2 zUDZVm(>%GrvWi~!J>%9h$=tS^AG>)JEltSJM`o)o!$TVjYc<~24rR5v(lE%u>=B|t zaO2w}UW6arP3}$?im1I(tb6J@={T}Ye$9!YTw$oS`5rv14E>fp|gE*rXPG4hZk&Fv|l?0i`gYQB*#e#ik6J zcwW>fxO^&wsOt2tfbSO{Iio*U7~k=6nr^^?`KCF~TDs9@ak%0K74e zzOUX!828J-p*0>YDa6P`J+Qg41a4cl8J6NQ3S?z+{vkQ{GU#&WJ8k44EYh!?43K)b zSs9EZM>X&m$z(!L176h`D|ydzDtW&*7IpZnmsbWAQz^TkbSoJCh<2DdIGWnVFVha~Sy8lpb-16i& zf%nD_Z>7__GAyuxUL^1uR9bS|X{95}4M^x;1Mb;f2qbR6ooYZ38qXrkV23foB_3e4 z3v*vxxY%z!^ie#hkB#k~yQKx9oW5_D&V7%2y_2q1ht-f2 zn;xsaFWVTGcg@eG*^EfM7+Q6wW9M)|svoRu54^(y#*^=|m?w6alV&Yu`0V1VEB#rX zSH4y|FXH)eBc!t>*!zkc6g}!&$8oDD4s!&_g?6ZCR-+)D~%yYeIeL?BWysSGO*UH$^J7r09_ z&fgXQlAK9Mb};EE7zUmv#X_{H8hS8yDrm2t5G<4>@GcNcWWCAdL8m22gKLL>JAmvu zY{WG-t~N@2@4D&;3bl4sv-RZ`EHs_-gQEnQ0iGIkW34%B$*}ViUNf(wSO8<_VR~7* zzRL~e%iuR94d#iE`S^QtaM&yCNNy<05pM+%dUnmELwV~`QPIkyxyPvncs+s}^nWt@`%c#xw3(Cb;htSs@ z3e=h{y%%ceX&o4yNIY%Y+p}9ury#?|@pMpgoK(M^zT&&Y@TGZozOpkaR;Npd{pQ-GSwz zWtT4!5_og$4siSmjdL!~WuaE8^)@%U4TE0^wL;T)2X`N40K^tzFx2TX{0V`8=g^{{ z+yFOKay7hd{4EdU_Jt#VXE4g|;4dRiKidw4|`RMuv0h=xca z+Vz&m&ckJluhZAXCdni1UMsT<(a2wZ<7F{+HzTyRn}eBKy#^QZIZ5XZQ)-%wWmy#p zMN#{Y>_HYYde?jhS}v#uX?4-&q*%Fazw4^F-m)&wfBS&}%>~)@buFlZg6Tt3Xm@-; zGLvWkBzbqCtoqxCd-|-e!b&@z^zfUgmklqJQU$-zl`rNmFVho4YvZguOlU-#>PUww zJOP;`exYpQ-YYNiZ5oG;#~;3jH{xo{nbPM5%_zlpr~QSH4TZb?ce_b$6miT=+NLgA znxLa1%}+xO?dNVBlxG3Cp)qM2g-Whr8$}Q09RZjwVI>;Bs&R-S%PBke`-Ja#N@-{^ zrTFr<%fCnPoc(PM>`$m<8toRb`H2Aa&rSvPzeT`*wsrm~{id%xc4$mU&0QVCrbNQT zR8-CD#9pF#sN7c71WOx77&_dV6}O5td$qq&wTTpqw-6PW8ix76 z3XDnPhK;69UtX=jV97?Q^0z5H&UwGV>riUvGn!~A2?6ebb>Ealznl|71X`l zHOX>FX^PasT5VzW)l;tDYaTJw0A#y4ypbPB4Z<&ODbE%uq6P8(^3WO`&2aD&$V4$DD+f zZ&EtsV?CtO*bOPA7n|1y7S!j9vdyydSXH>`<)&19%5hnhSmhpx^@u(s5v<+c+opeD zB1FlbyZs4~`Oo???Z1V||7vXg6DT&yTAy=-XwBXcHp_r4>AA7?P}uJvz}Q}5VFggl z`I=SM)ZPFxGw^WWNa9&$mIyS;QR&xqfm!|wzcUMgfLgB;k&m7naqgT`c(QyE-$P#n z#M`&$b`1rq!~R?Wj>?cey^xe*`|aZAdI2;caN~iH0e|tY!GlF$(xC_FyN+K2tez1n z%fPJ4;7-dxWVHvI%AjysC*>V>ZBQ3CvBZHhy;&tn#Ldrbz-xEm*B+}MC;c6K_jc(@`5Ge=w(dniD81zCT92}9gCZAXxHWN95&&w@QmA~n$-!6sHpL>Au{AB9GL z=Pb=vc$|*krx8E$644k9q$T1_5%+4|98|(>VC_`~k_RK6>bYWahluq@FHO>KHk+f` zj-EtM-V0VlN15t&F6%)v0IR}9mJz8c*5&-+s-3m%AC|Pdix15-anP542t*^xl}Sw# z@-hiUJJ1kh5a~e(731%g1f8{p4I~jF>4Hmb8aK%q<|EHia$-- z=Vo<~c|vUH!*M(+s(4`#oDeJA4jpMkWd%1J8Uo~o6x5m*vi&SX4#ypHD%}Xh=qB$l zYYB9UAINA3I6sm+z?Uy??yDwhV)>e_Enm3;7EY8NXlyPmF)su)s%Rs?)RMrXMDrF5 z3@rf28Sk>ljLT;gykANG&T}hfzkNY}Z%KDG`OXBdAxvi|9jd2NaO-!f;8PRjulPen z##M1O>XMxw(TV;8=ka9qMHQM%ySa?3kC#rPFcV3);;QxWW&O%2B}}MHZMeAjl5=W> zG>)-8xnF*bTqly7@O7$uYnl_3aThC;su$?5KT%daj99Gz7jhwO#~7Tgh4JI=>my?xe>evY7x>67O}TY1$l{X~mZH#2b} zYV3AXA+^sMQ`p%E>)jMIKBp;(YR{8f_nZ!4_0LICnqJjy_oq(7OaCNR)Jzk8)h z+I!T|jPm(!aqbTU+F4EvP~YW3zOTXq)TYf~198Vk7_)+IAv!FRz;DrO^R=OHBE)NC zT$i*m9NP5r(K3{+g*4+VKZm$R19{sf@o9V+?ss>Fw|^f@o8kCy8)ka3ZF0U{27J)-xk1W6=GOjPlmHji6-F%4um=Z%j8JJElvCM1B|-(TZ{u^*|;Was{nc9I=_0X(n~L z?R_Pk1~bpu8fM~=_fnDsHo_Ny&prPoMl=9R3F|3He`WafsEdWWu)5bUZU&_Y>M4*h z|EGn&5Vb9L9G2X_gMF7wKTs4F zs_cH%U><4Qthmjf)`MI;8G!R+rKVy#H;O?yDH%eYhZhKlHB-{MD!gvqSftpPxI7~D zY?7|lgaVrISaDabkX2;sxqf1A{D3Lo>MFOH3if*tV()~lNCCwYTEN zT?X8NDRP8+i`pYeV@u~TP3X^kD?)(63+rCXo)K7>x2-lS2y@U9fA|rE9Cm3UYwrjD z3ODe%QT4wTul_*z!+8;#KVH}fPW4M=IRKX=T__H}nF5Rvz_S9uM6{@qgEOMB z3d~wlw&vhd(C+Ug-Oyk&3ZywvPJ{*S^1arcYt7WGESDKc_*mszAn zt+%^!C8N1)e6d{}ES(D!#GFrxd9)|(4n^QUV4XA(%a+QUf!6yyS;jZWs?&;obZ{9h zHexLKTxdiwG_mpgupj>6)YP^EGzQEEGgNmgK(NlcWMlkhxq!Z%koGT}u+d3`8~xCd{Lev_VkJJiHAcAgH=6KM!!I?nu2%k_+|E;p>suPP*(e$)lI!5_ z55=zz4xt#}CxF-KIj^`z9Hl)tixZ;}(l%_)*h`|;VtU(7&vAMPL8=)yt?FWXygB81 zqTUa09Ni34d@Ho#3SW9E7UCb+9xmmk!V%lE6JA~HL#(SRy*R+1hCII%FH#UM zD3|ct?P>_f9&E4Scfndm=4DfH=$~k5+>wAZh6A#8d&H_;f0AE19Gvnxi;a**__U_p z3bA8KmVNrd83?jk;hR5?NmRR*t~&C6Bz`T#xdK~`xefS+J!5?hTUMdw$Y1cL=J=b# z)y?o`2yp2fWqr2qAS>{NdIohjmWEjM_d54@&*sT~;=790)CC~D zKs6owYfT%0B6X?f%dNUyWfd-cRX)4?`EfE`+K09a;6hLD3g%rxdE;b+dzC!FZ(mZa7A`lH9#Qht zBlxhitZ{Y!@}Vp`w;<%3P`$V&ns=fmn+C5DVRg*eP161%Q#A9-8(MCpSR{qQxsqwN zGw4U;y0#cHw6=PXq?g1gCxp-&DLYwuL=llBisZEwYlx*Y$XG&@h_(g_I;}CKRV6|B z(#~L3+UzP`fld*6tOVXfm(y6x{94pA*?HC=Wg%)N@5{|YahH0SJKxiRv>7kI@s=K} z1Ub*n`_o!GJ4ls6r9+rCu$63Izd2lc6j}?_|4Ug_( zzAeD{>FHWJLeXu3280YLaqQ}`DE^h_<>on0JFK24&J z(wOD?r(-&$32sObVp^m+CbrMd^bRqK{_YY~rkk>l)N;GYgDpK!v+c=N$aOf5W#tSz zsqiCqQk+YoBHE2;RwG?3sw}J6P!6jN&wc_WB?tD|<*}T$bO_60*AVLGp(~TGEXYDC z$q#aFqP<}ZHjX>2RKjImZiditGv8-8huW<&grv!PdNsugwJPr zjZh#-_=!GZ(_MG%1IC}lg9%j^$P8C`A3Cj^l2!PN-U(J5Jpo<#Oe3v?OJ}Oy{#2_QgiYM&od@h7gu4{LU-Awszk1dZaPbHSq_;>v9x2Fz)BWh9=K4nI3 z3`LkI%~#%-?15Ry|F8((lq2N~z=X!@Gvd~;yp?@PhM2My)C152Qvl9UZWne$LZyq(q zvUK(MnCy z&Q#VtM#(V%!kxyi+-W-2A6y>*^X#x&-PmBEFqR5(4@QfojQRWhP-T9&XW2x?7VjJJOAe*kDl@i-XVSX|HoMXatLMKn2RyL$#~SiwCT*ILj| z`F0Y2=bS#NZ~i5SKWTaQt|pz`rvRcq57mDO;{V5iwYmS7fwj+BwM4^zGpr`^Ijr{g zjnhB3zJXHb0DQt{`SbAnZ*Tp72>$I4=EErHLA1SSF# zw$@pyl2XMfP3e`ZSQ0v)j!A*sR&fumlEHHah8X1%Rw0#ZpEBEYd&s_QUgZ3>QDB!Md}dJZ5}4dz}Nigiwr;M-RsxS)T~MYeHx4Hx#a@`@JG#w&HK zh>=uJl%{GNVZlqJR%nxAQK}g2FqaQ%qe%<;a6q09T_vES9M~r*1?{9YMy zWTkYVp$n~eQjdL-wj!xokC{=|*em3!if}V{4d3WPu?^W5uh$bW2fVK~HxsGZEYDiTpqt>1OXA%6@1|KK3KjT58w69=xJIAHwun)!fr5Q3TlAyq{^*kq;r zrCZlFxTJTrQcX=T3XZ|Lcx)=S)5lBq{)LX8-ccBp?AB3hBveH6?7sZ66vpcC#lD@` zkL?QCS(u@VUZ@sUCW7HdK~@^(B>7eRd(h_LVobcX>qM}Nl1deI2T!^{&SY-ZSxz?H zRL-Qk#w2db5`UFEQ~0qsHw0}VdsIn01*L4juA|QZ^6tk_*|*$% z^H$=8`Gj%RR-sPv6rwgN2V{twWRz%diTe7t81N4YCXUjAEj#hY9NG-=T6oAfVFeV+`7PX2 zn$(h!>`|UJ&%llZ`;NV&F8riO&ZDOvXPgAzPw5CDH_j`xtZUuXdY}4)f zE$Or?eHQVZesqgAO(iiDdzwu6w1CN^`IH#xCCv(CFt#xp7QO>5mU6c*cG-=-O66C3 zuv)uGjJ<{8I5kydU&vP`IaHeH>XpRUG$!HIKmb?DXgIAsu(f=UG?4{J&fydEF|zgT zsXKEmDS^DZiS@?!a_0Zh$+?F^)$MUy6dGz2uR>l$lA{aCt>iky5Mg?yW=ajpZ5T<) zxZf)g(kPeQlR_Adh$EL06KM+LGW04$u0>Rwna;zWSOyF?_okc$nQE{UN8F&>gYGx&^iRbVb`a<+0lYpiR%F~89id+Bu927oX(~)e5AAcF zs&fo~f2a0)qrL7rdB}vj7k8I=ZTWoQ7|+ZaZ>~SL9eSoe!KE2wZj?mmd$tz8wHep8 z|Lh~Ng6hPd4wtIF7Sq=rgr6QPtF;eng_kJucL*y!5`td~ zw=3O56%n_@!HjdBwLLNlY}kAhezyA?5#4w71m(6!Yr>?ceOtj6ou5P$PPd_?PFB9B zP2RB)>DezC`!dhF2IlczXLWkn(cxQ_h#ez51wY!PDyFC%3W4L?*a+km2^;5B>7QAB z<4U#*Td?yZg8OIzE$Oz;Gczy5pGwO?EEYL`ORBht)H*{xHzZr(_S#3FJS=kWM6t$8 z(YZ5kor{sut#-rr<_;#B-cj{Dp_^f$A9bOo-fDN%c*~CJS&0l2Y_TnQ;4{@0g?PfR ztYB-+Tg2acEDDR7ac+~MnB7OIKal6oZ(d^=hCx@iX}22e7dq8pUQ`(2G^=i{n0q_b zNm4#;LN6EXTH<8=(T4aXl;^{<={qm<)E#>TN<%M*W=(h;+ZmT8x=VnRFJHGhG{Sv! zOiP9Y%UT?jTo;GJNj4%jCGQ&jFi$A1=o!}P*7Prt7#(vlz*pL@&w2#U_o9kj|0J<^ zMz_7&(wDX%r9iAHUJtKL>P@>e3B%Oq*@bf(Qszq(7Hla^-^0#PW9;9=+|uZHT=BNl zob*P@A^4^;mH!ge*#sM1)c4L*nbzc?VVd#WP>Nhb!rygo?gWa*y{=WsUZndjLn}j_ z^BWNI-{rF~>mGOuMaFd*3iV zcKO!cV{a=)kyRDq)1~Pim{~V3=({=v_9at)+yH*@T!PoGFM~%nCUqubUir}o`dTBi zJlySMnxEff?~VcMBD~E6VO8|JsGwX@_V$cz8R`zXw>s03HPWyj|N0b`GU+!YuvoFa ztbCg1!=Bk!FXY$pj7`51TU(*ohnYBp^VK(es*D8ca|(sI{7-(HGNgtHmZSh`g*wN9z-y4x2Egv&(sGtpWotK+s%$x)7@iYQ*ub9 z49VBb+fC1jk04bE_Y)xS{otzP^YhW`jg@)nHj%u~e*?vCm{J2POcB0cPp*ygQ%o}6 z;HWfk^Ts>>Q)u)9B`UAdMIokrJ6_QfMNN*A#cZJb;r_TbPFFErTH%!3WtW&VM3$*Xk9si*F?Be; zwdtc3I-WQjU2ys@n5v2kqX$H_IdN@ zSS-d^Y0uyhxUkve^~!&ZOPfmQTBW9p_P2R+Q_@kA*VWW+pv!qG#1n2u2^))%B?P8S z2E=EiUq#1iBIAU&9qws}d;c9Kw13auBK{{g@RA3v+%b4w6iuQ%D?^J-W?D}18uFem zeN6~)XfG+O4E?*yJ3q>nfbeM{7;1H8t9F<5m*%$2Z0^Xr`*kz`IWUu!V{v|9?vHGu z%TT)GI$z)L0Mb~D)sU0$V>RiA*(yHu6k&4j(Tr5=JnWjLNO{a;x1v+x*2tUo4`e4P z4kIR6AqcS)6C0!B`gYq0F~%wPgf-McPGX0=q5@pUT7-(+#GB3?^bBw8_gD6r(N1(M zEji((@L+w3FWJq#ar~42RSIB z%*B-%s7!yuEe~hX+5!onLKQ?%r0+4y1a^8=E1?)#PfJ&3AdVTuEM5g7K3|UvAL=QSbZL>@oRv-e@u!78RU_tzzPo#$|K^Q2F z1i{esnqtVFWuYVx019eAfOCS(0O+N`#tN`e9fF|fSyjji=71Y$2-v3$F%s!*+Yn=^ zYa1L7wlG8EtLU0h(D;1$w4}J(|t$ z&-$7MCjooMph*r(aGHc|Cm9F^OVlB-h72b#lfSHNHCWaM!Ozlbyx89w$d6WLg9T#{ z+m2os#GcKrd?x6AG;VB3x}{BY_XgigI*TH`6*!74~BH1iLU6dv!ByMzXf=iy^%2y7&C+>3ew{^ z5tC=GybLfF1<@lA9O+CRyE45Lh*m)KIzx_hCQn|O4xS|;dWi8qpf7tkILjLPX9=Tn QNQUbJjEl?s2;;y10#O%U@c;k- literal 0 HcmV?d00001 diff --git a/pycontest.egg-info/PKG-INFO b/pycontest.egg-info/PKG-INFO new file mode 100644 index 0000000..5c49668 --- /dev/null +++ b/pycontest.egg-info/PKG-INFO @@ -0,0 +1,31 @@ +Metadata-Version: 1.2 +Name: pycontest +Version: 0.1.dev0 +Summary: pycontest: project used during PyCon tutorial +Home-page: UNKNOWN +Author: Dorota Jarecka & Anna Jaruga +Author-email: djarecka@gmail.com +Maintainer: Dorota Jatecka & Anna Jaruga +Maintainer-email: djarecka@gmail.com +License: MIT +Description: + pycontest + ======== + + License + ======= + ``pycontest`` is licensed under the terms of the MIT license. See the file + "LICENSE" for information on the history of this software, terms & conditions + for usage, and a DISCLAIMER OF ALL WARRANTIES. + +Platform: OS Independent +Classifier: Development Status :: 3 - Alpha +Classifier: Environment :: Console +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Scientific/Engineering +Requires: numpy +Requires: scipy +Requires: matplotlib diff --git a/pycontest.egg-info/SOURCES.txt b/pycontest.egg-info/SOURCES.txt new file mode 100644 index 0000000..825ca05 --- /dev/null +++ b/pycontest.egg-info/SOURCES.txt @@ -0,0 +1,22 @@ +README.md +setup.py +pycontest/__init__.py +pycontest/elastic_collisions.py +pycontest/movies.py +pycontest/simulation.py +pycontest/transport.py +pycontest/utils.py +pycontest/version.py +pycontest.egg-info/PKG-INFO +pycontest.egg-info/SOURCES.txt +pycontest.egg-info/dependency_links.txt +pycontest.egg-info/requires.txt +pycontest.egg-info/top_level.txt +tests/__init__.py +tests/test_elastic_collisions_1d.py +tests/test_elastic_collisions_2d.py +tests/test_hypothesis.py +tests/test_regression.py +tests/test_simulation.py +tests/test_simulation_fixture.py +tests/test_transport.py \ No newline at end of file diff --git a/pycontest.egg-info/dependency_links.txt b/pycontest.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pycontest.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/pycontest.egg-info/requires.txt b/pycontest.egg-info/requires.txt new file mode 100644 index 0000000..f23634b --- /dev/null +++ b/pycontest.egg-info/requires.txt @@ -0,0 +1,3 @@ +numpy +scipy +matplotlib diff --git a/pycontest.egg-info/top_level.txt b/pycontest.egg-info/top_level.txt new file mode 100644 index 0000000..dc69f55 --- /dev/null +++ b/pycontest.egg-info/top_level.txt @@ -0,0 +1,2 @@ +pycontest +tests diff --git a/pycontest/transport.py b/pycontest/transport.py index 7465a5e..404a182 100644 --- a/pycontest/transport.py +++ b/pycontest/transport.py @@ -2,20 +2,28 @@ def transport(loc, vel, dt): """transport equations - Args: - loc: initial location (can be int/float or array) - vel: velocity (can be int/float or array) - dt: time step - - Returns: - location after one time step - """ + Args: + loc: initial location (can be int/float or array) + vel: velocity (can be int/float or array) + dt: time step + Returns: + location after one time step + """ # if loc, vel are simple numbers if isinstance(loc, (int, float)) and isinstance(vel, (int, float)): loc = loc + vel * dt + # if loc or vel is list, we want to convert to numpy arrays + if type(loc) is list: + loc = np.array(loc) + if type(vel) is list: + vel = np.array(vel) + + # for numpy arrays if isinstance(loc, np.ndarray) and isinstance(vel, np.ndarray): + # be sure that the type is float + loc = loc.astype(np.float32, copy=False) vel = vel.astype(np.float32, copy=False) loc[:] = loc[:] + vel[:] *dt return loc \ No newline at end of file diff --git a/solutions/test_elastic_collisions_1d.py b/solutions/test_elastic_collisions_1d.py deleted file mode 100644 index d93589d..0000000 --- a/solutions/test_elastic_collisions_1d.py +++ /dev/null @@ -1,45 +0,0 @@ -import pytest - -from pycontest import elastic_collisions as ec -from pycontest.utils import momentum, E_kin - - -# a simple tests for equal masses (balls should exchange velocities) - -# using default values of m1 and m2 -def test_collision_1d_1(): - v1_f, v2_f = ec.collision_1d(v1_i=1, v2_i=-2) - assert v1_f == -2 - assert v2_f == 1 - -# the same masses, but specifying the values -def test_collision_1d_2(): - v1_f, v2_f = ec.collision_1d(v1_i=1, v2_i=-2, m1=2, m2=2) - assert v1_f == -2 - assert v2_f == 1 - -# we can imagine what happens when one ball is much heavier. -# The heavy ball won't change the velocity too much -# we can use pytest.approx -def test_collision_1d_3(): - v1_f, v2_f = ec.collision_1d(v1_i=1, v2_i=-2, m1=1, m2=1e6) - assert v2_f == pytest.approx(-2, rel=1e-3) - - - -# it is hard to know the answer when masses are completely independed, -# but we still can test if we don't break any physical lawa -# we can tests total kinetic energy and total momentum - -def test_collision_1d_en(): - v1_i, v2_i = 1, -2 - m1, m2 = 1, 3 - v1_f, v2_f = ec.collision_1d(v1_i, v2_i, m1, m2) - assert E_kin([v1_i, v2_i], [m1, m2]) == E_kin([v1_f, v2_f], [m1, m2]) - - -def test_collision_1d_mom(): - v1_i, v2_i = 1, -2 - m1, m2 = 1, 3 - v1_f, v2_f = ec.collision_1d(v1_i, v2_i, m1, m2) - assert momentum([v1_i, v2_i], [m1, m2]) == momentum([v1_f, v2_f], [m1, m2]) diff --git a/solutions/test_elastic_collisions_2d.py b/solutions/test_elastic_collisions_2d.py deleted file mode 100644 index 292cc32..0000000 --- a/solutions/test_elastic_collisions_2d.py +++ /dev/null @@ -1,39 +0,0 @@ -import pytest - -from pycontest import elastic_collisions as ec -from pycontest.utils import momentum, E_kin - - -# 2d are more complicated, depends on the vector r12 -# but we can always check the simplest cases, that are basically 1d cases - -# let's start from 2 balls that have only x velocity, and r12 also has only x component -def test_collision_2d_1(): - v1_f, v2_f = ec.collision_2d(v1=[1, 0], v2=[-2, 0], r12=[1, 0], m1=1, m2=1) - assert (v1_f == [-2, 0]).all() - assert (v2_f == [1, 0]).all() - -# you cannow do the same for y components -def test_collision_2d_2(): - v1_f, v2_f = ec.collision_2d(v1=[0, 1], v2=[0, -2], r12=[0, 1], m1=1, m2=1) - assert (v1_f == [0, -2]).all() - assert (v2_f == [0, 1]).all() - - -# you can also check the energy and momentum - -def test_collision_2d_en(): - v1_i, v2_i = [3, 1], [1, -2] - m1, m2 = 1, 2 - v1_f, v2_f = ec.collision_2d(v1=v1_i, v2=v2_i, r12=[0, 1], m1=m1, m2=m2) - assert E_kin([v1_i,v2_i], [m1, m2]) == E_kin([v1_f,v2_f], [m1, m2]) - - -def test_collision_2d_mom(): - v1_i, v2_i = [3, 1], [1, -2] - m1, m2 = 1, 2 - v1_f, v2_f = ec.collision_2d(v1=v1_i, v2=v2_i, r12=[0, 1], m1=m1, m2=m2) - assert (momentum([v1_i,v2_i], [m1, m2]) == momentum([v1_f, v2_f], [m1, m2])).all() - - - diff --git a/solutions/test_hypothesis.py b/solutions/test_hypothesis.py deleted file mode 100644 index 42495bb..0000000 --- a/solutions/test_hypothesis.py +++ /dev/null @@ -1,33 +0,0 @@ -import numpy as np -import pytest - -from hypothesis import given, strategies as st - -from pycontest import simulation as sim2d -from pycontest.utils import momentum, E_kin - -# hypothesis simple example -@given(mass1 = st.floats(min_value=.1, max_value=1e3), - mass2 = st.floats(min_value=.1, max_value=1e3)) -def test_energy_hypothesis(mass1, mass2): - - # initial condition and simulation parameters - domain = ([0, 20], [0, 20]) - dt = 0.5 - t_max = 6 - loc_0 = np.array([[3, 4],[15, 2]]) - vel_0 = np.array([[1, 0.5], [-1, -.25]]) - radius = 1 - - # mass chosen by hypothesis - mass = [mass1, mass2] - - # run the simulation - loc, vel = sim2d.simulation(t_max, dt, mass, radius, loc_0, vel_0, domain) - - E_ini = E_kin(vel_0, mass) - E_end = E_kin(vel, mass) - - print("testing for mass = {}. E_end = {}".format(mass, E_end)) - - assert E_ini == E_end diff --git a/solutions/test_regression.py b/solutions/test_regression.py deleted file mode 100644 index 58a30c9..0000000 --- a/solutions/test_regression.py +++ /dev/null @@ -1,37 +0,0 @@ -import pycontest -from pycontest import simulation as sim2d -import numpy as np -import pytest -import os - -def test_regression(): - - # initial condition and simulation params - vel_0 = np.array([[12., 12.], [-8, -15.], [-3, 4], [10, 0], [2., 12.], [-8, -15.], - [3, -4], [-10, 0], [-12., 12.], [8, -15.], [-3, -4], [-10, 0]]) - loc_0 = np.array([[20., 45.], [65, 70.], [85., 90.], [10., 10.], [50., 45.], [15, 70.], - [15., 90.], [45., 10.], [75., 45.], [40, 70.], [45., 90.], [90., 10.]]) - domain = np.array([[0., 100.], [0., 100.]]) - mass = np.array([1., 2., 1., 3, 1., 2., 1., 3, 1., 2., 1., 3]) - dt = 1 / 30. - t_max = 30 - radius = 5 - - # run simulation - loc, vel = sim2d.simulation(t_max, dt, mass, radius, loc_0, vel_0, domain, t0=dt) - - # save reference simulations - #np.save(os.path.join(os.path.dirname(__file__),"../data/loc_ref.npy"), loc) - #np.save(os.path.join(os.path.dirname(__file__),"../data/vel_ref.npy"), vel) - - # read in reference values - loc_ref = np.load(os.path.join(os.path.dirname(__file__), "../data/loc_ref.npy")) - vel_ref = np.load(os.path.join(os.path.dirname(__file__), "../data/vel_ref.npy")) - - # from scipy docs: - # The tolerance values are positive, typically very small numbers. - # The relative difference (rtol * abs(b)) and the absolute difference atol - # are added together to compare against the absolute difference - # between a and b. - np.testing.assert_allclose(loc, loc_ref, atol=0, rtol=1e-9, err_msg="for locations") - np.testing.assert_allclose(vel, vel_ref, atol=0, rtol=1e-7, err_msg="for velocities") diff --git a/solutions/transport.py b/solutions/transport.py deleted file mode 100644 index 404a182..0000000 --- a/solutions/transport.py +++ /dev/null @@ -1,29 +0,0 @@ -import numpy as np - -def transport(loc, vel, dt): - """transport equations - Args: - loc: initial location (can be int/float or array) - vel: velocity (can be int/float or array) - dt: time step - - Returns: - location after one time step - """ - # if loc, vel are simple numbers - if isinstance(loc, (int, float)) and isinstance(vel, (int, float)): - loc = loc + vel * dt - - # if loc or vel is list, we want to convert to numpy arrays - if type(loc) is list: - loc = np.array(loc) - if type(vel) is list: - vel = np.array(vel) - - # for numpy arrays - if isinstance(loc, np.ndarray) and isinstance(vel, np.ndarray): - # be sure that the type is float - loc = loc.astype(np.float32, copy=False) - vel = vel.astype(np.float32, copy=False) - loc[:] = loc[:] + vel[:] *dt - return loc \ No newline at end of file diff --git a/tests/test_elastic_collisions_1d.py b/tests/test_elastic_collisions_1d.py index a3c739f..d93589d 100644 --- a/tests/test_elastic_collisions_1d.py +++ b/tests/test_elastic_collisions_1d.py @@ -9,5 +9,37 @@ # using default values of m1 and m2 def test_collision_1d_1(): v1_f, v2_f = ec.collision_1d(v1_i=1, v2_i=-2) + assert v1_f == -2 + assert v2_f == 1 +# the same masses, but specifying the values +def test_collision_1d_2(): + v1_f, v2_f = ec.collision_1d(v1_i=1, v2_i=-2, m1=2, m2=2) + assert v1_f == -2 + assert v2_f == 1 +# we can imagine what happens when one ball is much heavier. +# The heavy ball won't change the velocity too much +# we can use pytest.approx +def test_collision_1d_3(): + v1_f, v2_f = ec.collision_1d(v1_i=1, v2_i=-2, m1=1, m2=1e6) + assert v2_f == pytest.approx(-2, rel=1e-3) + + + +# it is hard to know the answer when masses are completely independed, +# but we still can test if we don't break any physical lawa +# we can tests total kinetic energy and total momentum + +def test_collision_1d_en(): + v1_i, v2_i = 1, -2 + m1, m2 = 1, 3 + v1_f, v2_f = ec.collision_1d(v1_i, v2_i, m1, m2) + assert E_kin([v1_i, v2_i], [m1, m2]) == E_kin([v1_f, v2_f], [m1, m2]) + + +def test_collision_1d_mom(): + v1_i, v2_i = 1, -2 + m1, m2 = 1, 3 + v1_f, v2_f = ec.collision_1d(v1_i, v2_i, m1, m2) + assert momentum([v1_i, v2_i], [m1, m2]) == momentum([v1_f, v2_f], [m1, m2]) diff --git a/tests/test_elastic_collisions_2d.py b/tests/test_elastic_collisions_2d.py index dd9eeac..292cc32 100644 --- a/tests/test_elastic_collisions_2d.py +++ b/tests/test_elastic_collisions_2d.py @@ -15,9 +15,25 @@ def test_collision_2d_1(): # you cannow do the same for y components def test_collision_2d_2(): - pass + v1_f, v2_f = ec.collision_2d(v1=[0, 1], v2=[0, -2], r12=[0, 1], m1=1, m2=1) + assert (v1_f == [0, -2]).all() + assert (v2_f == [0, 1]).all() + # you can also check the energy and momentum +def test_collision_2d_en(): + v1_i, v2_i = [3, 1], [1, -2] + m1, m2 = 1, 2 + v1_f, v2_f = ec.collision_2d(v1=v1_i, v2=v2_i, r12=[0, 1], m1=m1, m2=m2) + assert E_kin([v1_i,v2_i], [m1, m2]) == E_kin([v1_f,v2_f], [m1, m2]) + + +def test_collision_2d_mom(): + v1_i, v2_i = [3, 1], [1, -2] + m1, m2 = 1, 2 + v1_f, v2_f = ec.collision_2d(v1=v1_i, v2=v2_i, r12=[0, 1], m1=m1, m2=m2) + assert (momentum([v1_i,v2_i], [m1, m2]) == momentum([v1_f, v2_f], [m1, m2])).all() + diff --git a/tests/test_hypothesis.py b/tests/test_hypothesis.py index a529220..42495bb 100644 --- a/tests/test_hypothesis.py +++ b/tests/test_hypothesis.py @@ -3,12 +3,31 @@ from hypothesis import given, strategies as st -from pycontest import simulation as sim2d +from pycontest import simulation as sim2d from pycontest.utils import momentum, E_kin -@pytest.mark.skip(reason="TODO") +# hypothesis simple example @given(mass1 = st.floats(min_value=.1, max_value=1e3), mass2 = st.floats(min_value=.1, max_value=1e3)) def test_energy_hypothesis(mass1, mass2): - pass + # initial condition and simulation parameters + domain = ([0, 20], [0, 20]) + dt = 0.5 + t_max = 6 + loc_0 = np.array([[3, 4],[15, 2]]) + vel_0 = np.array([[1, 0.5], [-1, -.25]]) + radius = 1 + + # mass chosen by hypothesis + mass = [mass1, mass2] + + # run the simulation + loc, vel = sim2d.simulation(t_max, dt, mass, radius, loc_0, vel_0, domain) + + E_ini = E_kin(vel_0, mass) + E_end = E_kin(vel, mass) + + print("testing for mass = {}. E_end = {}".format(mass, E_end)) + + assert E_ini == E_end diff --git a/tests/test_regression.py b/tests/test_regression.py index e11b8d3..58a30c9 100644 --- a/tests/test_regression.py +++ b/tests/test_regression.py @@ -4,19 +4,29 @@ import pytest import os -@pytest.mark.skip(reason="TODO") def test_regression(): # initial condition and simulation params + vel_0 = np.array([[12., 12.], [-8, -15.], [-3, 4], [10, 0], [2., 12.], [-8, -15.], + [3, -4], [-10, 0], [-12., 12.], [8, -15.], [-3, -4], [-10, 0]]) + loc_0 = np.array([[20., 45.], [65, 70.], [85., 90.], [10., 10.], [50., 45.], [15, 70.], + [15., 90.], [45., 10.], [75., 45.], [40, 70.], [45., 90.], [90., 10.]]) + domain = np.array([[0., 100.], [0., 100.]]) + mass = np.array([1., 2., 1., 3, 1., 2., 1., 3, 1., 2., 1., 3]) + dt = 1 / 30. + t_max = 30 + radius = 5 # run simulation - loc, vel = sim2d.simulation(...) + loc, vel = sim2d.simulation(t_max, dt, mass, radius, loc_0, vel_0, domain, t0=dt) - # save reference simulations (only once when the test is created) - np.save(os.path.join(os.path.dirname(__file__),"../data/locations_ref.npy"), loc) + # save reference simulations + #np.save(os.path.join(os.path.dirname(__file__),"../data/loc_ref.npy"), loc) + #np.save(os.path.join(os.path.dirname(__file__),"../data/vel_ref.npy"), vel) - # read in reference values (every time the test is run) - loc_ref = np.load(os.path.join(os.path.dirname(__file__), "../data/locations_ref.npy")) + # read in reference values + loc_ref = np.load(os.path.join(os.path.dirname(__file__), "../data/loc_ref.npy")) + vel_ref = np.load(os.path.join(os.path.dirname(__file__), "../data/vel_ref.npy")) # from scipy docs: # The tolerance values are positive, typically very small numbers. @@ -24,3 +34,4 @@ def test_regression(): # are added together to compare against the absolute difference # between a and b. np.testing.assert_allclose(loc, loc_ref, atol=0, rtol=1e-9, err_msg="for locations") + np.testing.assert_allclose(vel, vel_ref, atol=0, rtol=1e-7, err_msg="for velocities") diff --git a/tests/test_simulation.py b/tests/test_simulation.py index d4d7877..ff18abe 100644 --- a/tests/test_simulation.py +++ b/tests/test_simulation.py @@ -5,12 +5,11 @@ import pytest - -def test_simulation_1(): +@pytest.mark.parametrize("dt", [0.5, 0.1, 0.01]) +def test_simulation_1(dt): # initial condition and simulation parameters domain = ([-2, 12], [0, 3]) t_max = 6 - dt = 0.5 loc_0 = np.array([[0, 1.5],[10, 1.5]]) vel_0 = np.array([[1, 0], [-1, 0]]) radius = 1 @@ -34,3 +33,18 @@ def test_simulation_1(): assert vel[1][0] == 1 # Y component should stay the same assert (vel[0][1], vel[1][1]) == (vel_0[0][1], vel_0[1][1]) + + +def test_simulation_1_fail(): + # initial condition and simulation parameters + domain = ([-2, 12], [0, 3]) + dt = 1. + t_max = 12 + loc_0 = np.array([[0, 1.5],[10, 1.5]]) + vel_0 = np.array([[1, 0], [-1, 0]]) + radius = 1 + mass = [1, 1] + # checking if exception is raised properly + with pytest.raises(Exception) as excinfo: + loc, vel = sim.simulation(t_max, dt, mass, radius, loc_0, vel_0, domain) + assert "two particles are exactly in the same place" in str(excinfo.value) diff --git a/tests/test_transport.py b/tests/test_transport.py index de6277a..092428f 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -4,31 +4,31 @@ from pycontest.transport import transport + # # first process is transport, and we can easily write a simple test -# def test_transport_1(): -# loc = 3. -# vel = 1. -# dt = .5 -# assert transport(loc, vel, dt) == 3.5 -# -# # but we also want to check if it works for arrays with 2d -# def test_transport_2(): -# loc = np.array([[1, 2], [11, 12]]) -# vel = np. array([[1, 1], [-1, -1]]) -# dt = 1 -# assert (transport(loc, vel, dt) == np.array([[2, 3], [10, 11]])).all() -# +def test_transport_1(): + loc = 3. + vel = 1. + dt = .5 + assert transport(loc, vel, dt) == 3.5 # +## but we also want to check if it works for arrays with 2d +def test_transport_2(): + loc = np.array([[1, 2], [11, 12]]) + vel = np. array([[1, 1], [-1, -1]]) + dt = 1 + assert (transport(loc, vel, dt) == np.array([[2, 3], [10, 11]])).all() + # # let's try one more, with dt that is float -# def test_transport_3(): -# loc = np.array([[1, 2], [11, 12]]) -# vel = np. array([[1, 1], [-1, -1]]) -# dt = .5 -# assert (transport(loc, vel, dt) == np.array([[1.5, 2.5], [10.5, 11.5]])).all() -# +def test_transport_3(): + loc = np.array([[1, 2], [11, 12]]) + vel = np. array([[1, 1], [-1, -1]]) + dt = .5 + assert (transport(loc, vel, dt) == np.array([[1.5, 2.5], [10.5, 11.5]])).all() + # # and now let's try with lists -# def test_transport_4(): -# loc = [[1, 2], [11, 12]] -# vel = [[1, 1], [-1, -1]] -# dt = .5 -# assert (transport(loc, vel, dt) == np.array([[1.5, 2.5], [10.5, 11.5]])).all() +def test_transport_4(): + loc = [[1, 2], [11, 12]] + vel = [[1, 1], [-1, -1]] + dt = .5 + assert (transport(loc, vel, dt) == np.array([[1.5, 2.5], [10.5, 11.5]])).all() diff --git a/solutions/travis.yml b/travis.yml similarity index 100% rename from solutions/travis.yml rename to travis.yml From ccf08c45a81b7ed9c36896ffce93b141c0b50bbe Mon Sep 17 00:00:00 2001 From: kstohr Date: Thu, 2 May 2019 11:53:38 -0400 Subject: [PATCH 2/2] change file name --- travis.yml => .travis.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename travis.yml => .travis.yml (100%) diff --git a/travis.yml b/.travis.yml similarity index 100% rename from travis.yml rename to .travis.yml