Ejemplo n.º 1
0
def main(args):
    print('\nUsage: particle.py [num_chares_x num_chares_y] [max_particles_per_cell_start]')
    if len(args) >= 3:
        array_dims = (int(args[1]), int(args[2]))
    else:
        array_dims = (8, 4)  # default: 2D chare array of 8 x 4 cells
    if len(args) == 4:
        max_particles_per_cell_start = int(args[3])
    else:
        max_particles_per_cell_start = 10000

    print('\nCell array size:', array_dims[0], 'x', array_dims[1], 'cells')

    # create 2D Cell chare array and start simulation
    sim_done = Future()
    cells = Array(Cell, array_dims,
                  args=[array_dims, max_particles_per_cell_start, sim_done],
                  useAtSync=True)
    num_particles_per_cell = cells.getNumParticles(ret=True).get()
    print('Total particles created:', sum(num_particles_per_cell))
    print('Initial conditions:\n\tmin particles per cell:', min(num_particles_per_cell),
          '\n\tmax particles per cell:', max(num_particles_per_cell))
    print('\nStarting simulation')
    t0 = time.time()
    cells.run()  # this is a broadcast
    # wait for the simulation to finish
    sim_done.get()
    print('Particle simulation done, elapsed time=', round(time.time() - t0, 3), 'secs')
    exit()
Ejemplo n.º 2
0
def main(args):
    f1 = Future()
    f2 = Future()
    Group(Test, args=[f1, f2])
    assert f1.get() == charm.numPes()
    assert f2.get() == charm.numPes()
    exit()
Ejemplo n.º 3
0
class Cell(Chare):
    def __init__(self, numChares):
        idx = self.thisIndex[0]
        self.nbs = []
        for i in range(1, 4):
            self.nbs.append(self.thisProxy[(idx + i) % numChares])
            self.nbs.append(self.thisProxy[(idx - i) % numChares])
        self.iteration = -1
        self.msgs_recvd = 0

    @coro
    def work(self, done_fut):
        for self.iteration in range(NUM_ITER):
            for nb in self.nbs:
                nb.recvData(self.iteration, None)
            self.iter_complete = Future()
            self.iter_complete.get()
        self.reduce(done_fut)

    @when('self.iteration == iteration')
    def recvData(self, iteration, data):
        self.msgs_recvd += 1
        if self.msgs_recvd == len(self.nbs):
            self.msgs_recvd = 0
            self.iter_complete()
Ejemplo n.º 4
0
def main(args):
    numChares = charm.numPes() * CHARES_PER_PE
    cells = Array(Cell, numChares, args=[numChares])
    charm.awaitCreation(cells)
    f = Future()
    cells.work(f)
    f.get()
    exit()
Ejemplo n.º 5
0
 def __init__(self, args):
     assert charm.numPes() >= 2
     g = Group(Test)
     done_fut = Future()
     g[1].work(self.thisProxy, done_fut)
     ch = Channel(self, remote=g[1])
     for i in range(NUM_STEPS):
         ch.send(i)
     done_fut.get()
     exit()
Ejemplo n.º 6
0
def main(args):
    assert charm.numPes() % 2 == 0
    g = Group(Test)
    gsec = g[::2]  # make a section with even numbered elements

    f = Future(2)
    g.work(f, charm.numPes())
    gsec.work(f, charm.numPes() // 2, gsec)
    f.get()
    g.verify(awaitable=True).get()
    exit()
Ejemplo n.º 7
0
def main(args):
    f1 = Future()
    f2 = Future()
    Group(Test, args=[f1])
    Array(Test, charm.numPes() * 4, args=[f2])
    np.testing.assert_allclose(f1.get(),
                               np.arange(10, dtype='float64') * charm.numPes())
    np.testing.assert_allclose(
        f2.get(),
        np.arange(10, dtype='float64') * charm.numPes() * 4)
    exit()
Ejemplo n.º 8
0
def main(args):
    numChares = charm.numPes() * 10
    a = Array(Test, numChares)
    g = Group(Test)
    charm.awaitCreation(a, g)
    f1 = Future()
    f2 = Future()
    a.start(f1)
    g.start(f2)
    f1.get()
    f2.get()
    exit()
Ejemplo n.º 9
0
def main(args):
    testProxy = Array(Test, charm.numPes() * CHARES_PER_PE)

    sum_f = Future()
    min_f = Future()
    max_f = Future()
    testProxy.getStats((sum_f, min_f, max_f))

    print('[Main] Sum: ' + str(sum_f.get()) + ', Min: ' + str(min_f.get()) +
          ', Max: ' + str(max_f.get()))
    print('[Main] All done.')
    exit()
Ejemplo n.º 10
0
 def __init__(self, args):
     assert charm.numPes() >= 3
     done_fut = Future(2)
     chare0 = Chare(Test, onPE=1, args=[0])
     chare1 = Chare(Test, onPE=2, args=[1])
     chare0.work(self.thisProxy, chare1, done_fut)
     chare1.work(self.thisProxy, chare0, done_fut)
     ch0 = Channel(self, remote=chare0)
     ch1 = Channel(self, remote=chare1)
     assert ch0.recv() == 'hello from 0'
     assert ch1.recv() == 'hello from 1'
     done_fut.get()
     exit()
Ejemplo n.º 11
0
def main(args):
    assert charm.numPes() >= 2
    # create the Scheduler on PE 0
    scheduler = Chare(Scheduler, onPE=0)
    # create Futures to receive the results of two jobs
    future1 = Future()
    future2 = Future()
    # send two map_async jobs at the same time to the scheduler
    scheduler.map_async(square, [1, 2, 3, 4, 5], callback=future1)
    scheduler.map_async(square, [1, 3, 5, 7, 9], callback=future2)
    # wait for the two jobs to complete and print the results
    print('Final results are:')
    print(future1.get())
    print(future2.get())
    exit()
Ejemplo n.º 12
0
def test_op_logical(done, op, vector_size, use_numpy=False):
    if vector_size > 1:
        if use_numpy:
            data = np.random.rand(vector_size)
            p = 0.1
            data = np.random.choice(a=[False, True],
                                    size=(vector_size),
                                    p=[p, 1 - p])
        else:
            data = list(map(bool, range(0, vector_size)))
    else:
        data = bool(random.randint(0, 1))

    finished_future = Future(2)
    chares = Array(TestVec, vector_size, args=[op, data])
    chares.do_test(None, finished_future)
    section = chares[0:vector_size]
    section.do_test(section, finished_future)
    val1, val2 = finished_future.get()

    try:
        if vector_size > 1:
            assert list(val1) == list(val2)
        else:
            assert val1 == val2
        print('[Main] Reduction with Reducer.%s passes.' % get_op_name(op))
        done(True)
    except AssertionError:
        print('[Main] Reduction with Reducer.%s is not correct.' %
              get_op_name(op))
        done(False)
Ejemplo n.º 13
0
def test_op(done, op, vector_size, use_numpy=False):
    if vector_size > 1:
        if use_numpy:
            data = np.random.rand(vector_size)
        else:
            data = list(range(0, vector_size))
    else:
        data = random.uniform(0, 5)

    finished_future = Future(2)
    chares = Array(TestVec, vector_size, args=[op, data])
    chares.do_test(None, finished_future, awaitable=True).get()
    section = chares[0:vector_size]
    section.do_test(section, finished_future)
    val1, val2 = finished_future.get()

    try:
        if vector_size > 1:
            if use_numpy:
                assert np.isclose(val1, val2, atol=1e-5).all()
            else:
                assert list(val1) == list(val2)
        else:
            assert val1 == val2
        print('[Main] Reduction with Reducer.%s passes.' % get_op_name(op))
        done(True)
    except AssertionError:
        print('[Main] Reduction with Reducer.%s is not correct.' %
              get_op_name(op))
        done(False)
Ejemplo n.º 14
0
    def __init__(self, args):
        if sys.version_info < (3, 0, 0):  # not supported in Python 2.7
            exit()
        assert charm.numPes() >= 4
        self.done = -1
        workers = Group(Worker)
        controllers = Array(Controller, charm.numPes())
        receivers = Array(CallbackReceiver,
                          charm.numPes(),
                          args=[self.thisProxy])
        workers.work(receivers[1].getResult)
        self.wait('self.done == 1')
        self.done = -1

        controllers[1].start(workers, receivers[2].getResult)
        self.wait('self.done == 2')
        self.done = -1

        controllers[2].start(workers, receivers.getResultBroadcast)
        self.wait('self.done == ' + str(charm.numPes()))
        self.done = -1

        f = Future()
        controllers[3].start(workers, f)
        assert f.get() == (charm.numPes() * (charm.numPes() - 1)) // 2

        exit()
Ejemplo n.º 15
0
def main(args):
    t0 = time.time()
    durations2 = []
    num_cpus = charm.numPes()
    # same seed for fair comparison
    np.random.seed(seed=1234)
    streaming_actors = Group(StreamingPrefixCount)
    for _ in range(num_trials):
        streaming_actors.reset(awaitable=True).get()
        start_time = time.time()

        for i in range(num_cpus * 10):
            document = [np.random.bytes(20) for _ in range(10000)]
            #document = [np.random.bytes(5) for _ in range(1000)]   # smaller task size
            streaming_actors[i % num_cpus].add_document(document)

        # wait for quiescence
        charm.waitQD()
        # get the aggregated results
        results = Future()
        streaming_actors.get_popular(results)
        popular_prefixes = results.get()

        duration2 = time.time() - start_time
        durations2.append(duration2)
        print(
            'Stateful computation workload took {} seconds.'.format(duration2))
    print('Total time=', time.time() - t0)
    exit()
Ejemplo n.º 16
0
    def start(self):
        assert charm.myPe() == 1

        N = charm.numPes() * 3
        a1 = charm.thisProxy[0].createArray(Test, N, ret=True).get()
        f = Future()
        a1.work(f, 5)
        assert f.get() == N * 5

        N = 25
        a2 = charm.thisProxy[0].createArray(Test, (5, 5), args=[33],
                                            ret=True).get()
        f = Future()
        a2.work(f, 6)
        assert f.get() == N * (6 + 33)

        exit()
def main(args):
    done_future = Future()
    test_chare = Chare(TestChare, args=[done_future], onPE=1)

    test_chare.send_future(awaitable=True).get()
    test_chare.wait_future()
    assert done_future.get() == TEST_VALUE

    done_future = Future()
    test_chare = Chare(TestChare, args=[done_future], onPE=1)

    # now make sure it works when we wait before sending
    test_chare.wait_future()
    test_chare.send_future()
    assert done_future.get() == TEST_VALUE

    charm.exit()
Ejemplo n.º 18
0
 def testQD(self, callback):
     self.qdReached = False
     check_fut = Future()
     t0 = time()
     self.workers.start()
     if callback is not None:
         charm.startQD(callback)
         if isinstance(callback, threads.Future):
             callback.get()
             print('QD reached')
         else:
             self.wait('self.qdReached')
     else:
         charm.waitQD()
     assert time() - t0 > WORK_TIME
     self.workers.check(check_fut)
     check_fut.get()
Ejemplo n.º 19
0
def main(args):
    g1 = Group(Test)
    g2 = Group(Test)
    g3 = Group(Test)
    g4 = Group(Test)

    P = charm.numPes()
    a1 = Array(Test, P * 8)
    a2 = Array(Test, P * 10)
    a3 = Array(Test, P * 4)
    a4 = Array(Test, P * 1)

    charm.awaitCreation(g1, g2, g3, g4, a1, a2, a3, a4)

    chares = []  # proxies to all individual chares created
    for collection in (g1, g2, g3, g4):
        for idx in range(P):
            chares.append(collection[idx])

    for collection, numelems in ((a1, P * 8), (a2, P * 10), (a3, P * 4), (a4,
                                                                          P)):
        for idx in range(numelems):
            chares.append(collection[idx])

    print('There are', len(chares), 'chares')

    # establish random channels between chares
    global gchannels
    gchannels = {}
    num_self_channels = 0
    for level in range(NUM_LEVELS):
        gchannels[level] = defaultdict(list)
        for _ in range(NUM_CHANNELS):
            a = random.choice(chares)
            b = random.choice(chares)
            if a == b:
                num_self_channels += 1
            gchannels[level][a].append(b)
            gchannels[level][b].append(a)
    charm.thisProxy.updateGlobals({
        'gchannels': gchannels
    }, awaitable=True).get()

    done_fut = Future(8 *
                      NUM_LEVELS)  # wait for 8 collections to finish 3 levels
    for collection in (g1, g2, g3, g4, a1, a2, a3, a4):
        collection.setup(awaitable=True).get()
    print(NUM_CHANNELS * NUM_LEVELS, 'channels set up,', num_self_channels,
          'self channels')
    for collection in (g1, g2, g3, g4, a1, a2, a3, a4):
        for lvl in range(NUM_LEVELS):
            collection.work(lvl, done_fut)

    msgs = sum(done_fut.get())
    assert msgs == sum(LEVELS_NUM_ITER[:NUM_LEVELS]) * NUM_CHANNELS * 2
    print('total msgs received by chares=', msgs)
    exit()
Ejemplo n.º 20
0
def main(args):
    home_pes = Future()
    array = Array(Test, charm.numPes() * 4, args=[home_pes], useAtSync=True)
    charm.thisProxy.updateGlobals(
        {
            'all_created': True,
            'arrayElemHomeMap': home_pes.get()
        },
        '__main__',
        awaitable=True).get()
    array.start()
Ejemplo n.º 21
0
def main(args):
    numChares = min(charm.numPes() * CHARES_PER_PE, 64)
    testProxy = Array(Test, numChares)

    f = Future(num_vals=numChares)
    testProxy.getData(f)

    data = f.get()
    print('[Main] Received data: ' + str(data))
    assert sorted(data) == list(range(numChares)), 'Multi-futures failed!'
    print('[Main] All done.')
    exit()
Ejemplo n.º 22
0
def main(args):
    threaded = False
    if len(args) > 1 and args[1] == '-t':
        threaded = True
    pings = Array(Ping, 2)
    charm.awaitCreation(pings)
    for _ in range(2):
        done_future = Future()
        pings[0].start(done_future, threaded)
        totalTime = done_future.get()
        print("ping pong time per iter (us)=", totalTime / NITER * 1000000)
    exit()
Ejemplo n.º 23
0
def main(args):

    g = Group(Worker)

    random.seed(45782)
    ids = []
    for i in range(MAX_VALS):
        #for _ in range(PHASE_NUM):
        #ids.append(i)
        ids.append(i)
    random.shuffle(ids)

    done = Future()
    g.start(done, awaitable=True).get()
    t0 = time.time()
    for id in ids:
        #g.recv_id(id)
        for _ in range(PHASE_NUM):
            g.recv_id(id)
    done.get()
    print("Elapsed=", time.time() - t0)
    exit()
Ejemplo n.º 24
0
def main(args):
    g = Group(Test)

    f1, f2, f3 = Future(), Future(), Future()
    g.doallfalse(f1, f2, f3, array=False)
    assert bool(f1.get()) is False
    assert bool(f2.get()) is False
    assert bool(f3.get()) is False

    f1, f2, f3 = Future(), Future(), Future()
    g.doallfalse(f1, f2, f3, array=True)
    np.testing.assert_array_equal(f1.get(), np.zeros(ARRAY_SIZE,
                                                     dtype=np.bool))
    np.testing.assert_array_equal(f2.get(), np.zeros(ARRAY_SIZE,
                                                     dtype=np.bool))
    np.testing.assert_array_equal(f3.get(), np.zeros(ARRAY_SIZE,
                                                     dtype=np.bool))

    f1, f2, f3 = Future(), Future(), Future()
    g.doalltrue(f1, f2, f3, array=False)
    assert bool(f1.get()) is True
    assert bool(f2.get()) is True
    assert bool(f3.get()) is False

    f1, f2, f3 = Future(), Future(), Future()
    g.doalltrue(f1, f2, f3, array=True)
    np.testing.assert_array_equal(f1.get(), np.ones(ARRAY_SIZE, dtype=np.bool))
    np.testing.assert_array_equal(f2.get(), np.ones(ARRAY_SIZE, dtype=np.bool))
    np.testing.assert_array_equal(f3.get(), np.zeros(ARRAY_SIZE,
                                                     dtype=np.bool))

    f1, f2, f3 = Future(), Future(), Future()
    g.test1(f1, f2, f3)
    assert bool(f1.get()) is False
    assert bool(f2.get()) is True
    assert bool(f3.get()) is False

    exit()
class TestChare(Chare):
    @coro
    def __init__(self, done_future):
        self.done_future = done_future
        self.test_future = Future()

    @coro
    def send_future(self):
        self.test_future(TEST_VALUE)

    @coro
    def wait_future(self):
        data = self.test_future.get()
        assert data == TEST_VALUE
        self.done_future(data)
Ejemplo n.º 26
0
def main(args):
    f1 = Future()
    f2 = Future()
    done1 = Future()
    done2 = Future()
    a = Array(Test, charm.numPes() * 10, args=[f1, done1])
    g = Group(Test, args=[f2, done2])

    collections = []
    collections.append((a, f1.get(), charm.numPes() * 10))
    collections.append((g, f2.get(), charm.numPes()))
    for collection, vals, num_elems in collections:
        indexes = list(range(num_elems))
        random_idxs = random.sample(indexes, int(max(len(indexes) * 0.8, 1)))
        for random_idx in random_idxs:
            retval = collection[random_idx].getVal(False, awaitable=False, ret=False)
            assert retval is None

            retval = collection[random_idx].getVal(False, ret=True).get()
            assert retval == vals[random_idx]

            retval = collection[random_idx].getVal_th(False, awaitable=False, ret=False)
            assert retval is None

            retval = collection[random_idx].getVal_th(False, ret=True).get()
            assert retval == vals[random_idx]

        retval = collection.getVal(True, ret=False)
        assert retval is None

        retval = collection.getVal(True, awaitable=True).get()
        assert retval is None

        retval = collection.getVal(True, ret=True).get()
        assert retval == [vals[i] for i in range(num_elems)]

        retval = collection.getVal_th(True, awaitable=False)
        assert retval is None

        retval = collection.getVal_th(True, awaitable=True).get()
        assert retval is None

        retval = collection.getVal_th(True, ret=True).get()
        assert retval == [vals[i] for i in range(num_elems)]

    done1.get()
    done2.get()
    exit()
Ejemplo n.º 27
0
class Main(Chare):

    def __init__(self, args):
        self.RENDER = True
        try:
            args.remove('--NO-RENDER')
            self.RENDER = False
        except ValueError:
            pass

        print('\nUsage: wave2d.py [num_iterations] [max_framerate])')
        global NUM_ITERATIONS, MAX_FRAMERATE
        if len(args) > 1:
            NUM_ITERATIONS = int(args[1])
        if len(args) > 2:
            MAX_FRAMERATE = int(args[2])

        print('Running wave2d on', charm.numPes(), 'processors for', NUM_ITERATIONS, 'iterations')
        print('Max framerate is', MAX_FRAMERATE, 'frames per second')

        self.count = 0  # tracks from how many workers I have received a subimage for this iteration
        programStartTime = frameStartTime = time.time()

        # Create new 2D array of worker chares
        array = Array(Wave, (CHARE_ARRAY_WIDTH, CHARE_ARRAY_HEIGHT))
        # tell all the worker chares to start the simulation
        array.work(self.thisProxy)

        if self.RENDER:
            tk = tkinter.Tk()
            self.frame = Image.new('RGB', (IMAGE_WIDTH, IMAGE_HEIGHT))
            img = ImageTk.PhotoImage(self.frame)
            label_image = tkinter.Label(tk, image=img)
            label_image.pack()

        self.frameReady = Future()
        for i in range(NUM_ITERATIONS):
            self.frameReady.get()  # wait for the next frame
            if MAX_FRAMERATE > 0:
                elapsed = time.time() - frameStartTime
                if elapsed < 1/MAX_FRAMERATE:
                    # enforce framerate
                    charm.sleep(1/MAX_FRAMERATE - elapsed)
            if self.RENDER:
                fps = round(1/(time.time() - frameStartTime))
                # draw frames per second value on image
                d = ImageDraw.Draw(self.frame)
                d.text((10,10), str(fps) + ' fps', fill=(0,0,0,255))
                img = ImageTk.PhotoImage(self.frame)
                label_image.configure(image=img)
                label_image.image = img
                tk.update_idletasks()
                tk.update()

            # loop simulation every 1000 iterations
            reset = (i % 1000 == 0)
            frameStartTime = time.time()
            array.resume(reset)  # tell workers to resume
            self.frameReady = Future()

        print('Program Done!, Total time=', time.time() - programStartTime)
        exit()

    # every worker calls this method to deposit their subimage
    def depositSubImage(self, data, pos, img_size):
        self.count += 1
        if self.RENDER:
            self.frame.paste(Image.frombytes('RGB', img_size, data), box=pos)
        if self.count == CHARE_ARRAY_WIDTH * CHARE_ARRAY_HEIGHT:
            # received image data from all chares
            self.count = 0
            self.frameReady()  # signal main that the next frame is ready
Ejemplo n.º 28
0
 def start(self, workers, callback):
     f = Future()
     workers.work(f)
     result = f.get()
     callback(result)
Ejemplo n.º 29
0
def main(args):
    collections = []
    cid = 0  # collection ID

    for _ in range(NUM_ARRAYS):
        proxy = Array(Test, random.randint(1, charm.numPes() * 10), args=[cid])
        f = Future()
        proxy.getIds(f)
        collections.append(Collection(f.get(), proxy))
        cid += 1

    for _ in range(NUM_GROUPS):
        proxy = Group(Test, args=[cid])
        f = Future()
        proxy.getIds(f)
        collections.append(Collection(f.get(), proxy))
        cid += 1

    sections_split = 0
    sections_combined = 0

    for i in range(NUM_GEN):
        if i < 20 or random.random() <= 0.5:
            # split
            while True:
                c = random.choice(collections)
                if len(c.elems) > 1:
                    break
            if random.random() < 0.9:
                N = random.randint(1, len(c.elems) // 2)  # ***
            else:
                N = random.randint(1, len(c.elems))
            collections.extend(c.split(N))
            if c.proxy.issec:
                sections_split += 1
        else:
            # combine
            N = random.randint(2, 10)
            cs = random.sample(collections, N)
            proxies = [c.proxy for c in cs]
            proxy = charm.combine(*proxies)
            c = Collection([], proxy)
            for c_ in cs:
                c.addElems(c_.elems)
            assert hasattr(c.proxy, 'section') and c.proxy.issec
            sections_combined += 1
            collections.append(c)
    print(len(collections), 'collections created, sections_split=',
          sections_split, 'sections_combined=', sections_combined)

    if VERBOSE:
        section_sizes = []
        for c in collections:
            if c.proxy.issec is not None:
                section_sizes.append(len(c.elems))
        section_sizes = numpy.array(section_sizes)
        print(len(section_sizes), 'sections, sizes:')
        print('min size=', numpy.min(section_sizes))
        print('median size=', numpy.median(section_sizes))
        print('mean size=', numpy.mean(section_sizes))
        print('max size=', numpy.max(section_sizes))

    for c in collections:
        if c.proxy.issec:
            # this is a section proxy
            sid = c.proxy.section[1]
            c.proxy.recvSecProxy(sid, c.proxy, awaitable=True).get()

    for _ in range(NUM_ITER):
        futures = [Future() for _ in range(len(collections))]
        charm.thisProxy.updateGlobals(
            {
                'DATA_VERIFY': random.randint(0, 100000)
            }, awaitable=True).get()
        data = DATA_VERIFY
        for i, c in enumerate(collections):
            sid = None
            if c.proxy.issec:
                sid = c.proxy.section[1]
            c.proxy.recvBcast(data, futures[i], sid)
        for i, f in enumerate(futures):
            result = futures[i].get()
            collections[i].verify(result)

    print('DONE')
    exit()
Ejemplo n.º 30
0
def main(args):
    global blockDimX, blockDimY, num_chare_x, num_chare_y
    if len(args) != 3 and len(args) != 5:
        print('\nUsage:\t', args[0], 'array_size block_size')
        print('\t', args[0],
              'array_size_X array_size_Y block_size_X block_size_Y')
        exit()

    if len(args) == 3:
        arrayDimX = arrayDimY = int(args[1])
        blockDimX = blockDimY = int(args[2])
    elif len(args) == 5:
        arrayDimX, arrayDimY = [int(arg) for arg in args[1:3]]
        blockDimX, blockDimY = [int(arg) for arg in args[3:5]]

    assert (arrayDimX >= blockDimX) and (arrayDimX % blockDimX == 0)
    assert (arrayDimY >= blockDimY) and (arrayDimY % blockDimY == 0)
    num_chare_x = arrayDimX // blockDimX
    num_chare_y = arrayDimY // blockDimY

    # set the following global variables on every PE, wait for the call to complete
    charm.thisProxy.updateGlobals(
        {
            'blockDimX': blockDimX,
            'blockDimY': blockDimY,
            'num_chare_x': num_chare_x,
            'num_chare_y': num_chare_y
        },
        awaitable=True).get()

    print('\nRunning Jacobi on', charm.numPes(), 'processors with',
          num_chare_x, 'x', num_chare_y, 'chares')
    print('Array Dimensions:', arrayDimX, 'x', arrayDimY)
    print('Block Dimensions:', blockDimX, 'x', blockDimY)
    print('Max iterations:', MAX_ITER)
    print('Threshold:', THRESHOLD)

    if numbaFound:
        # wait until Numba functions are compiled on every PE, so we can get consistent benchmark results
        Group(Util).compile(awaitable=True).get()
        print('Numba compilation complete')
    else:
        print(
            '!!WARNING!! Numba not found. Will run without Numba but it will be very slow'
        )

    sim_done = Future()
    # create 2D chare array of Jacobi objects (each chare will hold one block)
    array = Array(Jacobi, (num_chare_x, num_chare_y), args=[sim_done])
    charm.awaitCreation(array)

    print('Starting computation')
    initTime = time.time()
    array.run()  # this is a broadcast
    total_iterations = sim_done.get()  # wait until the computation completes
    totalTime = time.time() - initTime
    if total_iterations >= MAX_ITER:
        print('Finished due to max iterations', total_iterations, 'total time',
              round(totalTime, 3), 'seconds')
    else:
        print('Finished due to convergence, iterations', total_iterations,
              'total time', round(totalTime, 3), 'seconds')
    exit()