mirror of
https://github.com/smyalygames/FiniteVolumeGPU.git
synced 2025-05-18 14:34:13 +02:00
MPI prototype
This commit is contained in:
parent
074e38de84
commit
c51afef9fc
@ -115,15 +115,17 @@ class IPEngine(object):
|
|||||||
|
|
||||||
# attach to a running cluster
|
# attach to a running cluster
|
||||||
import ipyparallel
|
import ipyparallel
|
||||||
cluster = ipyparallel.Client()#profile='mpi')
|
self.cluster = ipyparallel.Client()#profile='mpi')
|
||||||
while(len(cluster.ids) != n_engines):
|
while(len(self.cluster.ids) != n_engines):
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
self.logger.info("Waiting for cluster...")
|
self.logger.info("Waiting for cluster...")
|
||||||
cluster = ipyparallel.Client()#profile='mpi')
|
self.cluster = ipyparallel.Client()#profile='mpi')
|
||||||
|
|
||||||
self.logger.info("Done")
|
self.logger.info("Done")
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
|
self.cluster.shutdown(hub=True)
|
||||||
|
|
||||||
self.e.terminate()
|
self.e.terminate()
|
||||||
try:
|
try:
|
||||||
self.e.communicate(timeout=3)
|
self.e.communicate(timeout=3)
|
||||||
@ -137,6 +139,11 @@ class IPEngine(object):
|
|||||||
self.logger.info("IPEngine cerr: {:s}".format(cerr))
|
self.logger.info("IPEngine cerr: {:s}".format(cerr))
|
||||||
|
|
||||||
|
|
||||||
|
while(len(self.cluster.ids) != 0):
|
||||||
|
time.sleep(0.5)
|
||||||
|
self.logger.info("Waiting for cluster to shutdown...")
|
||||||
|
|
||||||
|
|
||||||
self.c.terminate()
|
self.c.terminate()
|
||||||
try:
|
try:
|
||||||
self.c.communicate(timeout=3)
|
self.c.communicate(timeout=3)
|
||||||
@ -337,11 +344,19 @@ class CudaArray2D:
|
|||||||
"""
|
"""
|
||||||
Enables downloading data from GPU to Python
|
Enables downloading data from GPU to Python
|
||||||
"""
|
"""
|
||||||
def download(self, stream, async=False):
|
def download(self, stream, async=False, extent=None):
|
||||||
|
if (extent == None):
|
||||||
|
x = self.x_halo
|
||||||
|
y = self.y_halo
|
||||||
|
nx = self.nx
|
||||||
|
ny = self.ny
|
||||||
|
else:
|
||||||
|
x, y, nx, ny = extent
|
||||||
|
|
||||||
#self.logger.debug("Downloading [%dx%d] buffer", self.nx, self.ny)
|
#self.logger.debug("Downloading [%dx%d] buffer", self.nx, self.ny)
|
||||||
#Allocate host memory
|
#Allocate host memory
|
||||||
#cpu_data = cuda.pagelocked_empty((self.ny, self.nx), np.float32)
|
#cpu_data = cuda.pagelocked_empty((self.ny, self.nx), np.float32)
|
||||||
cpu_data = np.empty((self.ny, self.nx), dtype=np.float32)
|
cpu_data = np.empty((ny, nx), dtype=np.float32)
|
||||||
|
|
||||||
#Create copy object from device to host
|
#Create copy object from device to host
|
||||||
copy = cuda.Memcpy2D()
|
copy = cuda.Memcpy2D()
|
||||||
@ -349,20 +364,52 @@ class CudaArray2D:
|
|||||||
copy.set_dst_host(cpu_data)
|
copy.set_dst_host(cpu_data)
|
||||||
|
|
||||||
#Set offsets and pitch of source
|
#Set offsets and pitch of source
|
||||||
copy.src_x_in_bytes = self.x_halo*self.data.strides[1]
|
copy.src_x_in_bytes = x*self.data.strides[1]
|
||||||
copy.src_y = self.y_halo
|
copy.src_y = y
|
||||||
copy.src_pitch = self.data.strides[0]
|
copy.src_pitch = self.data.strides[0]
|
||||||
|
|
||||||
#Set width in bytes to copy for each row and
|
#Set width in bytes to copy for each row and
|
||||||
#number of rows to copy
|
#number of rows to copy
|
||||||
copy.width_in_bytes = self.nx*cpu_data.itemsize
|
copy.width_in_bytes = nx*cpu_data.itemsize
|
||||||
copy.height = self.ny
|
copy.height = ny
|
||||||
|
|
||||||
copy(stream)
|
copy(stream)
|
||||||
if async==False:
|
if async==False:
|
||||||
stream.synchronize()
|
stream.synchronize()
|
||||||
|
|
||||||
return cpu_data
|
return cpu_data
|
||||||
|
|
||||||
|
|
||||||
|
def upload(self, cpu_data, stream, extent=None):
|
||||||
|
if (extent == None):
|
||||||
|
x = self.x_halo
|
||||||
|
y = self.y_halo
|
||||||
|
nx = self.nx
|
||||||
|
ny = self.ny
|
||||||
|
else:
|
||||||
|
x, y, nx, ny = extent
|
||||||
|
|
||||||
|
assert(nx == cpu_data.shape[1])
|
||||||
|
assert(ny == cpu_data.shape[0])
|
||||||
|
assert(x+nx <= self.nx + 2*self.x_halo)
|
||||||
|
assert(y+ny <= self.ny + 2*self.y_halo)
|
||||||
|
|
||||||
|
#Create copy object from device to host
|
||||||
|
copy = cuda.Memcpy2D()
|
||||||
|
copy.set_dst_device(self.data.gpudata)
|
||||||
|
copy.set_src_host(cpu_data)
|
||||||
|
|
||||||
|
#Set offsets and pitch of source
|
||||||
|
copy.dst_x_in_bytes = x*self.data.strides[1]
|
||||||
|
copy.dst_y = y
|
||||||
|
copy.dst_pitch = self.data.strides[0]
|
||||||
|
|
||||||
|
#Set width in bytes to copy for each row and
|
||||||
|
#number of rows to copy
|
||||||
|
copy.width_in_bytes = nx*cpu_data.itemsize
|
||||||
|
copy.height = ny
|
||||||
|
|
||||||
|
copy(stream)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,9 +129,16 @@ class MagicLogger(Magics):
|
|||||||
logger.addHandler(ch)
|
logger.addHandler(ch)
|
||||||
logger.log(args.level, "Console logger using level %s", logging.getLevelName(args.level))
|
logger.log(args.level, "Console logger using level %s", logging.getLevelName(args.level))
|
||||||
|
|
||||||
|
#Get the outfilename (try to evaluate if Python expression...)
|
||||||
|
try:
|
||||||
|
outfile = eval(args.out, self.shell.user_global_ns, self.shell.user_ns)
|
||||||
|
except:
|
||||||
|
outfile = args.out
|
||||||
|
|
||||||
#Add log to file
|
#Add log to file
|
||||||
logger.log(args.level, "File logger using level %s to %s", logging.getLevelName(args.file_level), args.out)
|
logger.log(args.level, "File logger using level %s to %s", logging.getLevelName(args.file_level), outfile)
|
||||||
fh = logging.FileHandler(args.out)
|
|
||||||
|
fh = logging.FileHandler(outfile)
|
||||||
formatter = logging.Formatter('%(asctime)s:%(name)s:%(levelname)s: %(message)s')
|
formatter = logging.Formatter('%(asctime)s:%(name)s:%(levelname)s: %(message)s')
|
||||||
fh.setFormatter(formatter)
|
fh.setFormatter(formatter)
|
||||||
fh.setLevel(args.file_level)
|
fh.setLevel(args.file_level)
|
||||||
@ -161,6 +168,12 @@ class MagicMPI(Magics):
|
|||||||
self.cluster = None
|
self.cluster = None
|
||||||
self.cluster = Common.IPEngine(args.num_engines)
|
self.cluster = Common.IPEngine(args.num_engines)
|
||||||
|
|
||||||
|
# Handle CUDA context when exiting python
|
||||||
|
import atexit
|
||||||
|
def exitfunc():
|
||||||
|
self.logger.info("Exitfunc: Resetting MPI cluster")
|
||||||
|
self.cluster = None
|
||||||
|
atexit.register(exitfunc)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -143,4 +143,4 @@ class KP07 (Simulator.BaseSimulator):
|
|||||||
|
|
||||||
def computeDt(self):
|
def computeDt(self):
|
||||||
max_dt = gpuarray.min(self.cfl_data, stream=self.stream).get();
|
max_dt = gpuarray.min(self.cfl_data, stream=self.stream).get();
|
||||||
return max_dt*0.5**self.order
|
return max_dt*0.5**(self.order-1)
|
@ -138,4 +138,4 @@ class KP07_dimsplit(Simulator.BaseSimulator):
|
|||||||
|
|
||||||
def computeDt(self):
|
def computeDt(self):
|
||||||
max_dt = gpuarray.min(self.cfl_data, stream=self.stream).get();
|
max_dt = gpuarray.min(self.cfl_data, stream=self.stream).get();
|
||||||
return max_dt*0.5
|
return max_dt
|
@ -53,11 +53,11 @@ class BoundaryCondition(object):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, types={ \
|
def __init__(self, types={
|
||||||
'north': Type.Reflective, \
|
'north': Type.Reflective,
|
||||||
'south': Type.Reflective, \
|
'south': Type.Reflective,
|
||||||
'east': Type.Reflective, \
|
'east': Type.Reflective,
|
||||||
'west': Type.Reflective \
|
'west': Type.Reflective
|
||||||
}):
|
}):
|
||||||
"""
|
"""
|
||||||
Constructor
|
Constructor
|
||||||
@ -170,25 +170,24 @@ class BaseSimulator(object):
|
|||||||
t_start = self.simTime()
|
t_start = self.simTime()
|
||||||
t_end = t_start + t
|
t_end = t_start + t
|
||||||
|
|
||||||
local_dt = dt
|
update_dt = False
|
||||||
|
if (dt == None):
|
||||||
if (local_dt == None):
|
update_dt = True
|
||||||
local_dt = self.computeDt()
|
|
||||||
|
|
||||||
while(self.simTime() < t_end):
|
while(self.simTime() < t_end):
|
||||||
if (dt == None and self.simSteps() % 100 == 0):
|
if (update_dt and (self.simSteps() % 100 == 0)):
|
||||||
local_dt = self.computeDt()
|
dt = self.computeDt()*self.cfl_scale
|
||||||
|
|
||||||
# Compute timestep for "this" iteration (i.e., shorten last timestep)
|
# Compute timestep for "this" iteration (i.e., shorten last timestep)
|
||||||
local_dt = np.float32(min(local_dt*self.cfl_scale, t_end-self.simTime()))
|
dt = np.float32(min(dt, t_end-self.simTime()))
|
||||||
|
|
||||||
# Stop if end reached (should not happen)
|
# Stop if end reached (should not happen)
|
||||||
if (local_dt <= 0.0):
|
if (dt <= 0.0):
|
||||||
self.logger.warning("Timestep size {:d} is less than or equal to zero!".format(self.simSteps()))
|
self.logger.warning("Timestep size {:d} is less than or equal to zero!".format(self.simSteps()))
|
||||||
break
|
break
|
||||||
|
|
||||||
# Step forward in time
|
# Step forward in time
|
||||||
self.step(local_dt)
|
self.step(dt)
|
||||||
|
|
||||||
#Print info
|
#Print info
|
||||||
print_string = printer.getPrintString(self.simTime() - t_start)
|
print_string = printer.getPrintString(self.simTime() - t_start)
|
||||||
@ -197,7 +196,7 @@ class BaseSimulator(object):
|
|||||||
try:
|
try:
|
||||||
self.check()
|
self.check()
|
||||||
except AssertionError as e:
|
except AssertionError as e:
|
||||||
e.args += ("Step={:d}, time={:f}".format(self.simSteps(), self.simTime()))
|
e.args += ("Step={:d}, time={:f}".format(self.simSteps(), self.simTime()),)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,7 +48,11 @@ def downsample(highres_solution, x_factor, y_factor=None):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def bump(nx, ny, width, height, bump_size=None, h_ref=0.5, h_amp=0.1, u_ref=0.0, u_amp=0.1, v_ref=0.0, v_amp=0.1, ref_nx=None, ref_ny=None):
|
def bump(nx, ny, width, height,
|
||||||
|
bump_size=None,
|
||||||
|
ref_nx=None, ref_ny=None,
|
||||||
|
x_center=0.5, y_center=0.5,
|
||||||
|
h_ref=0.5, h_amp=0.1, u_ref=0.0, u_amp=0.1, v_ref=0.0, v_amp=0.1):
|
||||||
|
|
||||||
if (ref_nx == None):
|
if (ref_nx == None):
|
||||||
ref_nx = nx
|
ref_nx = nx
|
||||||
@ -64,8 +68,8 @@ def bump(nx, ny, width, height, bump_size=None, h_ref=0.5, h_amp=0.1, u_ref=0.0,
|
|||||||
ref_dx = width / float(ref_nx)
|
ref_dx = width / float(ref_nx)
|
||||||
ref_dy = height / float(ref_ny)
|
ref_dy = height / float(ref_ny)
|
||||||
|
|
||||||
x_center = ref_dx*ref_nx/2.0
|
x_center = ref_dx*ref_nx*x_center
|
||||||
y_center = ref_dy*ref_ny/2.0
|
y_center = ref_dy*ref_ny*y_center
|
||||||
|
|
||||||
x = ref_dx*(np.arange(0, ref_nx, dtype=np.float32)+0.5) - x_center
|
x = ref_dx*(np.arange(0, ref_nx, dtype=np.float32)+0.5) - x_center
|
||||||
y = ref_dy*(np.arange(0, ref_ny, dtype=np.float32)+0.5) - y_center
|
y = ref_dy*(np.arange(0, ref_ny, dtype=np.float32)+0.5) - y_center
|
||||||
|
761
MPITest.ipynb
Normal file
761
MPITest.ipynb
Normal file
@ -0,0 +1,761 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# Starting cluster\n",
|
||||||
|
"\n",
|
||||||
|
"## Prerequisites\n",
|
||||||
|
"First, you need to install MPI, on windows use MS-MPI:\n",
|
||||||
|
"https://msdn.microsoft.com/en-us/library/bb524831(v=vs.85).aspx\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"## With a profile (not working)\n",
|
||||||
|
"In theory, you should be able to create a profile using\n",
|
||||||
|
"```\n",
|
||||||
|
"ipython profile create --parallel --profile=myprofile\n",
|
||||||
|
"```\n",
|
||||||
|
"and then set\n",
|
||||||
|
"```\n",
|
||||||
|
"c.IPClusterEngines.engine_launcher_class = 'MPIEngineSetLauncher'\n",
|
||||||
|
"```\n",
|
||||||
|
"in ```<IPYTHON-DIR>/profile_myprofile/ipcluster_config.py```. This should then enable you to start a cluster using\n",
|
||||||
|
"```\n",
|
||||||
|
"ipcluster start --profile=myprofile\n",
|
||||||
|
"```\n",
|
||||||
|
"or alternatively through the Clusters tab in Jupyter\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"## Without a profile (not working)\n",
|
||||||
|
"An alternative is to run\n",
|
||||||
|
"```\n",
|
||||||
|
"ipcluster start --engines=MPI\n",
|
||||||
|
"```\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"## Manual start (working)\n",
|
||||||
|
"This, however, does *not* work for me on Windows. What does work is the following:\n",
|
||||||
|
"\n",
|
||||||
|
"Start a controller using\n",
|
||||||
|
"```\n",
|
||||||
|
"ipcontroller --ip='*'\n",
|
||||||
|
"```\n",
|
||||||
|
"and then start several engines using mpiexec:\n",
|
||||||
|
"```\n",
|
||||||
|
"mpiexec -n 4 ipengine --mpi\n",
|
||||||
|
"```"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 1,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"from GPUSimulators import IPythonMagic"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 2,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stderr",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Console logger using level INFO\n",
|
||||||
|
"File logger using level DEBUG to mpi.log\n",
|
||||||
|
"Python version 3.6.7 |Anaconda custom (64-bit)| (default, Oct 28 2018, 19:44:12) [MSC v.1915 64 bit (AMD64)]\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"%setup_logging --out mpi.log"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 3,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stderr",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Starting IPController\n",
|
||||||
|
"Starting IPEngines\n",
|
||||||
|
"Waiting for cluster...\n",
|
||||||
|
"Waiting for cluster...\n",
|
||||||
|
"Waiting for cluster...\n",
|
||||||
|
"Waiting for cluster...\n",
|
||||||
|
"Waiting for cluster...\n",
|
||||||
|
"Waiting for cluster...\n",
|
||||||
|
"Waiting for cluster...\n",
|
||||||
|
"Waiting for cluster...\n",
|
||||||
|
"Waiting for cluster...\n",
|
||||||
|
"Waiting for cluster...\n",
|
||||||
|
"Waiting for cluster...\n",
|
||||||
|
"Waiting for cluster...\n",
|
||||||
|
"Done\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"%setup_mpi --num_engines 4"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 4,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"profile: default\n",
|
||||||
|
"Number of ids: 4\n",
|
||||||
|
"IDs: [0, 1, 2, 3]\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"import ipyparallel\n",
|
||||||
|
"\n",
|
||||||
|
"# attach to a running cluster\n",
|
||||||
|
"cluster = ipyparallel.Client()#profile='mpi')\n",
|
||||||
|
"\n",
|
||||||
|
"print('profile:', cluster.profile)\n",
|
||||||
|
"print('Number of ids:', len(cluster.ids))\n",
|
||||||
|
"print(\"IDs:\", cluster.ids) # Print process id numbers"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 5,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"[stdout:0] MPI rank 0 of 4 OK\n",
|
||||||
|
"[stdout:1] MPI rank 1 of 4 OK\n",
|
||||||
|
"[stdout:2] MPI rank 2 of 4 OK\n",
|
||||||
|
"[stdout:3] MPI rank 3 of 4 OK\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"%%px\n",
|
||||||
|
"\n",
|
||||||
|
"from mpi4py import MPI\n",
|
||||||
|
"comm = MPI.COMM_WORLD\n",
|
||||||
|
"print(\"MPI rank {:d} of {:d} OK\".format(comm.rank, comm.size))\n",
|
||||||
|
"comm.Barrier() "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 6,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"[stdout:0] \n",
|
||||||
|
"Starting\n",
|
||||||
|
"0: sent data to 1: [91 81 57 75 60 73 2 68 11 50 71 17 65 20 4 64 48 34 66 37 5 85 42 80\n",
|
||||||
|
" 25 78 83 94 38 72 27 93 82 56 92 51 88 22 29 79 14 13 47 35 0 30 63 3\n",
|
||||||
|
" 12 99 24 23 15 32 44 1 62 67 19 8 87 45 16 61 84 7 49 10 21 69 89 70\n",
|
||||||
|
" 39 86 98 90 36 9 53 54 33 43 26 58 77 40 95 6 41 74 46 28 59 76 31 97\n",
|
||||||
|
" 55 18 96 52]\n",
|
||||||
|
"\n",
|
||||||
|
"[stdout:1] \n",
|
||||||
|
"Starting\n",
|
||||||
|
"1: received data from 0: [91 81 57 75 60 73 2 68 11 50 71 17 65 20 4 64 48 34 66 37 5 85 42 80\n",
|
||||||
|
" 25 78 83 94 38 72 27 93 82 56 92 51 88 22 29 79 14 13 47 35 0 30 63 3\n",
|
||||||
|
" 12 99 24 23 15 32 44 1 62 67 19 8 87 45 16 61 84 7 49 10 21 69 89 70\n",
|
||||||
|
" 39 86 98 90 36 9 53 54 33 43 26 58 77 40 95 6 41 74 46 28 59 76 31 97\n",
|
||||||
|
" 55 18 96 52]\n",
|
||||||
|
"\n",
|
||||||
|
"[stdout:2] \n",
|
||||||
|
"Starting\n",
|
||||||
|
"2: idle\n",
|
||||||
|
"\n",
|
||||||
|
"[stdout:3] \n",
|
||||||
|
"Starting\n",
|
||||||
|
"3: idle\n",
|
||||||
|
"\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"%%px\n",
|
||||||
|
"\n",
|
||||||
|
"from mpi4py import MPI\n",
|
||||||
|
"import numpy\n",
|
||||||
|
"\n",
|
||||||
|
"comm = MPI.COMM_WORLD\n",
|
||||||
|
"rank = comm.Get_rank()\n",
|
||||||
|
"\n",
|
||||||
|
"print(\"Starting\")\n",
|
||||||
|
"# passing MPI datatypes explicitly\n",
|
||||||
|
"if rank == 0:\n",
|
||||||
|
" data = numpy.arange(100, dtype='i')\n",
|
||||||
|
" numpy.random.shuffle(data)\n",
|
||||||
|
" comm.Send([data, MPI.INT], dest=1, tag=77)\n",
|
||||||
|
" print(\"{0}: sent data to 1: {1}\".format(rank, data))\n",
|
||||||
|
"elif rank == 1:\n",
|
||||||
|
" data = numpy.empty(100, dtype='i')\n",
|
||||||
|
" comm.Recv([data, MPI.INT], source=0, tag=77)\n",
|
||||||
|
" print(\"{0}: received data from 0: {1}\".format(rank, data))\n",
|
||||||
|
"else:\n",
|
||||||
|
" print(\"{0}: idle\".format(rank))\n",
|
||||||
|
" \n",
|
||||||
|
"print()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 7,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"%%px\n",
|
||||||
|
"from GPUSimulators import IPythonMagic"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 8,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stderr",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"[stderr:0] \n",
|
||||||
|
"Console logger using level INFO\n",
|
||||||
|
"File logger using level DEBUG to mpi_0.log\n",
|
||||||
|
"Python version 3.6.7 |Anaconda custom (64-bit)| (default, Oct 28 2018, 19:44:12) [MSC v.1915 64 bit (AMD64)]\n",
|
||||||
|
"[stderr:1] \n",
|
||||||
|
"Console logger using level INFO\n",
|
||||||
|
"File logger using level DEBUG to mpi_1.log\n",
|
||||||
|
"Python version 3.6.7 |Anaconda custom (64-bit)| (default, Oct 28 2018, 19:44:12) [MSC v.1915 64 bit (AMD64)]\n",
|
||||||
|
"[stderr:2] \n",
|
||||||
|
"Console logger using level INFO\n",
|
||||||
|
"File logger using level DEBUG to mpi_2.log\n",
|
||||||
|
"Python version 3.6.7 |Anaconda custom (64-bit)| (default, Oct 28 2018, 19:44:12) [MSC v.1915 64 bit (AMD64)]\n",
|
||||||
|
"[stderr:3] \n",
|
||||||
|
"Console logger using level INFO\n",
|
||||||
|
"File logger using level DEBUG to mpi_3.log\n",
|
||||||
|
"Python version 3.6.7 |Anaconda custom (64-bit)| (default, Oct 28 2018, 19:44:12) [MSC v.1915 64 bit (AMD64)]\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"%%px\n",
|
||||||
|
"%setup_logging --out \"'mpi_' + str(MPI.COMM_WORLD.rank) + '.log'\""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 9,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stderr",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"[stderr:0] \n",
|
||||||
|
"Registering my_context in user workspace\n",
|
||||||
|
"PyCUDA version 2018.1.1\n",
|
||||||
|
"CUDA version (10, 0, 0)\n",
|
||||||
|
"Driver version 10000\n",
|
||||||
|
"Using 'GeForce 840M' GPU\n",
|
||||||
|
"Created context handle <863101499808>\n",
|
||||||
|
"Using CUDA cache dir c:\\Users\\anbro\\Documents\\projects\\GPUSimulators\\GPUSimulators\\cuda_cache\n",
|
||||||
|
"Autotuning enabled. It may take several minutes to run the code the first time: have patience\n",
|
||||||
|
"[stderr:1] \n",
|
||||||
|
"Registering my_context in user workspace\n",
|
||||||
|
"PyCUDA version 2018.1.1\n",
|
||||||
|
"CUDA version (10, 0, 0)\n",
|
||||||
|
"Driver version 10000\n",
|
||||||
|
"Using 'GeForce 840M' GPU\n",
|
||||||
|
"Created context handle <912376276640>\n",
|
||||||
|
"Using CUDA cache dir c:\\Users\\anbro\\Documents\\projects\\GPUSimulators\\GPUSimulators\\cuda_cache\n",
|
||||||
|
"Autotuning enabled. It may take several minutes to run the code the first time: have patience\n",
|
||||||
|
"[stderr:2] \n",
|
||||||
|
"Registering my_context in user workspace\n",
|
||||||
|
"PyCUDA version 2018.1.1\n",
|
||||||
|
"CUDA version (10, 0, 0)\n",
|
||||||
|
"Driver version 10000\n",
|
||||||
|
"Using 'GeForce 840M' GPU\n",
|
||||||
|
"Created context handle <390889602416>\n",
|
||||||
|
"Using CUDA cache dir c:\\Users\\anbro\\Documents\\projects\\GPUSimulators\\GPUSimulators\\cuda_cache\n",
|
||||||
|
"Autotuning enabled. It may take several minutes to run the code the first time: have patience\n",
|
||||||
|
"[stderr:3] \n",
|
||||||
|
"Registering my_context in user workspace\n",
|
||||||
|
"PyCUDA version 2018.1.1\n",
|
||||||
|
"CUDA version (10, 0, 0)\n",
|
||||||
|
"Driver version 10000\n",
|
||||||
|
"Using 'GeForce 840M' GPU\n",
|
||||||
|
"Created context handle <550832168304>\n",
|
||||||
|
"Using CUDA cache dir c:\\Users\\anbro\\Documents\\projects\\GPUSimulators\\GPUSimulators\\cuda_cache\n",
|
||||||
|
"Autotuning enabled. It may take several minutes to run the code the first time: have patience\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"%%px\n",
|
||||||
|
"%cuda_context_handler my_context"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 10,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"%%px\n",
|
||||||
|
"%matplotlib inline"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 11,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"%%px\n",
|
||||||
|
"import numpy as np\n",
|
||||||
|
"from matplotlib import pyplot as plt"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 12,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"%%px\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def getFactors(number, num_factors):\n",
|
||||||
|
" # Adapted from https://stackoverflow.com/questions/28057307/factoring-a-number-into-roughly-equal-factors\n",
|
||||||
|
" # Original code by https://stackoverflow.com/users/3928385/ishamael\n",
|
||||||
|
" \n",
|
||||||
|
" #Dictionary to remember already computed permutations\n",
|
||||||
|
" memo = {}\n",
|
||||||
|
" def dp(n, left): # returns tuple (cost, [factors])\n",
|
||||||
|
" \"\"\"\n",
|
||||||
|
" Recursively searches through all factorizations\n",
|
||||||
|
" \"\"\"\n",
|
||||||
|
" \n",
|
||||||
|
" #Already tried: return existing result\n",
|
||||||
|
" if (n, left) in memo: \n",
|
||||||
|
" return memo[(n, left)]\n",
|
||||||
|
"\n",
|
||||||
|
" #Spent all factors: return number itself\n",
|
||||||
|
" if left == 1:\n",
|
||||||
|
" return (n, [n])\n",
|
||||||
|
"\n",
|
||||||
|
" #Find new factor\n",
|
||||||
|
" i = 2\n",
|
||||||
|
" best = n\n",
|
||||||
|
" bestTuple = [n]\n",
|
||||||
|
" while i * i < n:\n",
|
||||||
|
" #If factor found\n",
|
||||||
|
" if n % i == 0:\n",
|
||||||
|
" #Factorize remainder\n",
|
||||||
|
" rem = dp(n // i, left - 1)\n",
|
||||||
|
" \n",
|
||||||
|
" #If new permutation better, save it\n",
|
||||||
|
" if rem[0] + i < best:\n",
|
||||||
|
" best = rem[0] + i\n",
|
||||||
|
" bestTuple = [i] + rem[1]\n",
|
||||||
|
" i += 1\n",
|
||||||
|
"\n",
|
||||||
|
" #Store calculation\n",
|
||||||
|
" memo[(n, left)] = (best, bestTuple)\n",
|
||||||
|
" return memo[(n, left)]\n",
|
||||||
|
" \n",
|
||||||
|
" assert(isinstance(number, int))\n",
|
||||||
|
" assert(isinstance(num_factors, int))\n",
|
||||||
|
" \n",
|
||||||
|
" factors = dp(number, num_factors)[1]\n",
|
||||||
|
" \n",
|
||||||
|
" if (len(factors) < num_factors):\n",
|
||||||
|
" #Split problematic 4\n",
|
||||||
|
" if (4 in factors):\n",
|
||||||
|
" factors.remove(4)\n",
|
||||||
|
" factors.append(2)\n",
|
||||||
|
" factors.append(2)\n",
|
||||||
|
" \n",
|
||||||
|
" factors = factors + [1]*(num_factors - len(factors))\n",
|
||||||
|
" return factors"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 13,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"%%px\n",
|
||||||
|
"\n",
|
||||||
|
"def getCoordinate(rank, grid):\n",
|
||||||
|
" i = (rank % grid[0])\n",
|
||||||
|
" j = (rank // grid[0])\n",
|
||||||
|
" \n",
|
||||||
|
" return i, j\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def getRank(i, j, grid):\n",
|
||||||
|
" return j*grid[0] + i\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def getEast(rank, grid):\n",
|
||||||
|
" i, j = getCoordinate(rank, grid)\n",
|
||||||
|
" i = (i+1) % grid[0]\n",
|
||||||
|
" return getRank(i, j, grid)\n",
|
||||||
|
"\n",
|
||||||
|
"def getWest(rank, grid):\n",
|
||||||
|
" i, j = getCoordinate(rank, grid)\n",
|
||||||
|
" i = (i+grid[0]-1) % grid[0]\n",
|
||||||
|
" return getRank(i, j, grid)\n",
|
||||||
|
"\n",
|
||||||
|
"def getNorth(rank, grid):\n",
|
||||||
|
" i, j = getCoordinate(rank, grid)\n",
|
||||||
|
" j = (j+1) % grid[1]\n",
|
||||||
|
" return getRank(i, j, grid)\n",
|
||||||
|
"\n",
|
||||||
|
"def getSouth(rank, grid):\n",
|
||||||
|
" i, j = getCoordinate(rank, grid)\n",
|
||||||
|
" j = (j+grid[1]-1) % grid[1]\n",
|
||||||
|
" return getRank(i, j, grid)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 14,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"[stdout:0] \n",
|
||||||
|
"[3, 4]\n",
|
||||||
|
"0 1 2 \n",
|
||||||
|
"3 4 5 \n",
|
||||||
|
"6 7 8 \n",
|
||||||
|
"9 10 11 \n",
|
||||||
|
"0 (0, 0) 3 9 1 2\n",
|
||||||
|
"1 (1, 0) 4 10 2 0\n",
|
||||||
|
"2 (2, 0) 5 11 0 1\n",
|
||||||
|
"3 (0, 1) 6 0 4 5\n",
|
||||||
|
"4 (1, 1) 7 1 5 3\n",
|
||||||
|
"5 (2, 1) 8 2 3 4\n",
|
||||||
|
"6 (0, 2) 9 3 7 8\n",
|
||||||
|
"7 (1, 2) 10 4 8 6\n",
|
||||||
|
"8 (2, 2) 11 5 6 7\n",
|
||||||
|
"9 (0, 3) 0 6 10 11\n",
|
||||||
|
"10 (1, 3) 1 7 11 9\n",
|
||||||
|
"11 (2, 3) 2 8 9 10\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"%%px \n",
|
||||||
|
"\n",
|
||||||
|
"if (MPI.COMM_WORLD.rank == 0):\n",
|
||||||
|
" n_procs = 12\n",
|
||||||
|
" grid = getFactors(n_procs, 2)\n",
|
||||||
|
" \n",
|
||||||
|
" print(grid)\n",
|
||||||
|
" for j in range(grid[1]):\n",
|
||||||
|
" for i in range(grid[0]):\n",
|
||||||
|
" print(j*grid[0]+i, end=\" \")\n",
|
||||||
|
" print()\n",
|
||||||
|
"\n",
|
||||||
|
" for i in range(n_procs):\n",
|
||||||
|
" print(getRank(*getCoordinate(i, grid), grid), getCoordinate(i, grid), getNorth(i, grid), getSouth(i, grid), getEast(i, grid), getWest(i, grid))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 15,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"%%px\n",
|
||||||
|
"\n",
|
||||||
|
"def plotSolution(data, grid):\n",
|
||||||
|
" ny, nx = data.shape\n",
|
||||||
|
" \n",
|
||||||
|
" if (MPI.COMM_WORLD.rank != 0):\n",
|
||||||
|
" mpi_request = MPI.COMM_WORLD.Isend(data, dest=0, tag=MPI.COMM_WORLD.rank)\n",
|
||||||
|
" mpi_request.wait()\n",
|
||||||
|
" else:\n",
|
||||||
|
" def my_imshow(data, idx):\n",
|
||||||
|
" i, j = getCoordinate(idx, grid)\n",
|
||||||
|
" x = i * width\n",
|
||||||
|
" y = j * height \n",
|
||||||
|
" extent=[x, x+width, y, y+height]\n",
|
||||||
|
" plt.imshow(data, extent=extent, vmin=0.4, vmax=0.6)\n",
|
||||||
|
" \n",
|
||||||
|
" mpi_requests = []\n",
|
||||||
|
" for k in range(1, MPI.COMM_WORLD.size):\n",
|
||||||
|
" buffer = np.empty((ny, nx), dtype=np.float32)\n",
|
||||||
|
" mpi_requests += [(buffer, MPI.COMM_WORLD.Irecv(buffer, source=k, tag=k))]\n",
|
||||||
|
"\n",
|
||||||
|
" plt.figure()\n",
|
||||||
|
" my_imshow(data, 0)\n",
|
||||||
|
" idx = 1\n",
|
||||||
|
" for buffer, request in mpi_requests:\n",
|
||||||
|
" request.wait()\n",
|
||||||
|
" my_imshow(buffer, idx)\n",
|
||||||
|
" idx += 1\n",
|
||||||
|
" plt.axis('tight')\n",
|
||||||
|
" plt.colorbar()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 16,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"%%px\n",
|
||||||
|
"\n",
|
||||||
|
"from GPUSimulators.helpers import InitialConditions\n",
|
||||||
|
"from GPUSimulators.Simulator import BoundaryCondition\n",
|
||||||
|
"\n",
|
||||||
|
"nx = 16\n",
|
||||||
|
"ny = 15\n",
|
||||||
|
"g = 9.81\n",
|
||||||
|
"dt = 0.05\n",
|
||||||
|
"width = 100\n",
|
||||||
|
"height = 100\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"if (MPI.COMM_WORLD.rank == 0):\n",
|
||||||
|
" h0, hu0, hv0, dx, dy = InitialConditions.bump(nx, ny, width, height, g, x_center=0.75, y_center=0.75)\n",
|
||||||
|
"else:\n",
|
||||||
|
" h0, hu0, hv0, dx, dy = InitialConditions.bump(nx, ny, width, height, g, h_ref=0.5, h_amp=0.0, u_amp=0.0, v_amp=0.0)\n",
|
||||||
|
" \n",
|
||||||
|
"bc = BoundaryCondition({\n",
|
||||||
|
" 'north': BoundaryCondition.Type.Dirichlet,\n",
|
||||||
|
" 'south': BoundaryCondition.Type.Dirichlet,\n",
|
||||||
|
" 'east': BoundaryCondition.Type.Dirichlet,\n",
|
||||||
|
" 'west': BoundaryCondition.Type.Dirichlet\n",
|
||||||
|
"})\n",
|
||||||
|
"\n",
|
||||||
|
"arguments = {\n",
|
||||||
|
" 'context': my_context,\n",
|
||||||
|
" 'h0': h0, 'hu0': hu0, 'hv0': hv0,\n",
|
||||||
|
" 'nx': nx, 'ny': ny,\n",
|
||||||
|
" 'dx': dx, 'dy': dy, \n",
|
||||||
|
" 'g': g,\n",
|
||||||
|
" 'boundary_conditions': bc\n",
|
||||||
|
"} \n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 19,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"[output:0]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "display_data"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"image/png": "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\n",
|
||||||
|
"text/plain": [
|
||||||
|
"<Figure size 432x288 with 2 Axes>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"engine": 0
|
||||||
|
},
|
||||||
|
"output_type": "display_data"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"image/png": "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\n",
|
||||||
|
"text/plain": [
|
||||||
|
"<Figure size 432x288 with 2 Axes>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"engine": 0
|
||||||
|
},
|
||||||
|
"output_type": "display_data"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"image/png": "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\n",
|
||||||
|
"text/plain": [
|
||||||
|
"<Figure size 432x288 with 2 Axes>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"engine": 0
|
||||||
|
},
|
||||||
|
"output_type": "display_data"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"image/png": "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\n",
|
||||||
|
"text/plain": [
|
||||||
|
"<Figure size 432x288 with 2 Axes>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"engine": 0
|
||||||
|
},
|
||||||
|
"output_type": "display_data"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"image/png": "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\n",
|
||||||
|
"text/plain": [
|
||||||
|
"<Figure size 432x288 with 2 Axes>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"engine": 0
|
||||||
|
},
|
||||||
|
"output_type": "display_data"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"%%px\n",
|
||||||
|
"from GPUSimulators import KP07_dimsplit\n",
|
||||||
|
"\n",
|
||||||
|
"sim = KP07_dimsplit.KP07_dimsplit(**arguments)\n",
|
||||||
|
"\n",
|
||||||
|
"grid = getFactors(MPI.COMM_WORLD.size, 2)\n",
|
||||||
|
"rank = MPI.COMM_WORLD.rank\n",
|
||||||
|
" \n",
|
||||||
|
"gc_x = int(sim.u0[0].x_halo)\n",
|
||||||
|
"gc_y = int(sim.u0[0].y_halo)\n",
|
||||||
|
"nx = int(sim.nx)\n",
|
||||||
|
"ny = int(sim.ny)\n",
|
||||||
|
"\n",
|
||||||
|
"n_steps = 1\n",
|
||||||
|
"t_end = 5*dt\n",
|
||||||
|
"for i in range(n_steps):\n",
|
||||||
|
" t_this = t_end/n_steps\n",
|
||||||
|
" n_steps = int(t_this/dt)\n",
|
||||||
|
" for j in range(n_steps):\n",
|
||||||
|
" #Swap east-west boundary\n",
|
||||||
|
" read_e = [nx , gc_y, gc_x, ny]\n",
|
||||||
|
" write_e = [nx+gc_x, gc_y, gc_x, ny]\n",
|
||||||
|
" out_e = sim.u0[0].download(sim.stream, async=False, extent=read_e)\n",
|
||||||
|
" in_e = np.empty((ny, gc_x), dtype=np.float32)\n",
|
||||||
|
" send_e = MPI.COMM_WORLD.Isend(out_e, dest=getEast(rank, grid), tag=1)\n",
|
||||||
|
" recv_e = MPI.COMM_WORLD.Irecv(in_e, source=getEast(rank, grid), tag=0)\n",
|
||||||
|
" #print(\"Send 1 to \", getEast(rank, grid), \" receive 0 from \", getEast(rank, grid))\n",
|
||||||
|
"\n",
|
||||||
|
" read_w = [gc_x, gc_y, gc_x, ny]\n",
|
||||||
|
" write_w = [ 0, gc_y, gc_x, ny]\n",
|
||||||
|
" out_w = sim.u0[0].download(sim.stream, async=False, extent=read_w)\n",
|
||||||
|
" in_w = np.empty((ny, gc_x), dtype=np.float32)\n",
|
||||||
|
" send_w = MPI.COMM_WORLD.Isend(out_w, dest=getWest(rank, grid), tag=0)\n",
|
||||||
|
" recv_w = MPI.COMM_WORLD.Irecv(in_w, source=getWest(rank, grid), tag=1)\n",
|
||||||
|
" #print(\"Send 0 to \", getWest(rank, grid), \" receive 1 from \", getWest(rank, grid))\n",
|
||||||
|
"\n",
|
||||||
|
" #Swap north-south boundary\n",
|
||||||
|
" read_n = [gc_x, ny , nx, gc_y]\n",
|
||||||
|
" write_n = [gc_x, ny+gc_y, nx, gc_y]\n",
|
||||||
|
" out_n = sim.u0[0].download(sim.stream, async=False, extent=read_n)\n",
|
||||||
|
" in_n = np.empty((gc_y, nx), dtype=np.float32)\n",
|
||||||
|
" send_n = MPI.COMM_WORLD.Isend(out_n, dest=getNorth(rank, grid), tag=3)\n",
|
||||||
|
" recv_n = MPI.COMM_WORLD.Irecv(in_n, source=getNorth(rank, grid), tag=2)\n",
|
||||||
|
"\n",
|
||||||
|
" read_s = [gc_x, gc_y, nx, gc_y]\n",
|
||||||
|
" write_s = [gc_x, 0, nx, gc_y]\n",
|
||||||
|
" out_s = sim.u0[0].download(sim.stream, async=False, extent=read_s)\n",
|
||||||
|
" in_s = np.empty((gc_y, nx), dtype=np.float32)\n",
|
||||||
|
" send_s = MPI.COMM_WORLD.Isend(out_s, dest=getSouth(rank, grid), tag=2)\n",
|
||||||
|
" recv_s = MPI.COMM_WORLD.Irecv(in_s, source=getSouth(rank, grid), tag=3)\n",
|
||||||
|
"\n",
|
||||||
|
" \n",
|
||||||
|
" recv_e.wait()\n",
|
||||||
|
" recv_w.wait()\n",
|
||||||
|
" recv_n.wait()\n",
|
||||||
|
" recv_s.wait()\n",
|
||||||
|
" \n",
|
||||||
|
" sim.u0[0].upload(in_e, sim.stream, extent=write_e)\n",
|
||||||
|
" sim.u0[0].upload(in_w, sim.stream, extent=write_w)\n",
|
||||||
|
" sim.u0[0].upload(in_n, sim.stream, extent=write_n)\n",
|
||||||
|
" sim.u0[0].upload(in_s, sim.stream, extent=write_s)\n",
|
||||||
|
" \n",
|
||||||
|
" send_e.wait()\n",
|
||||||
|
" send_w.wait()\n",
|
||||||
|
" send_n.wait()\n",
|
||||||
|
" send_s.wait()\n",
|
||||||
|
" \n",
|
||||||
|
" if (rank == 0):\n",
|
||||||
|
" h1 = sim.u0[0].download(sim.stream, extent=[0, 0, nx+2*gc_x, ny+2*gc_y])\n",
|
||||||
|
" plt.figure()\n",
|
||||||
|
" plt.subplot(1,2,1)\n",
|
||||||
|
" plt.imshow(h1)\n",
|
||||||
|
" \n",
|
||||||
|
" #print(sim.simTime())\n",
|
||||||
|
" sim.simulate(dt, dt=dt)\n",
|
||||||
|
" \n",
|
||||||
|
" if (rank == 0):\n",
|
||||||
|
" h1 = sim.u0[0].download(sim.stream, extent=[0, 0, nx+2*gc_x, ny+2*gc_y])\n",
|
||||||
|
" plt.subplot(1,2,2)\n",
|
||||||
|
" plt.imshow(h1)\n",
|
||||||
|
" \n",
|
||||||
|
" \n",
|
||||||
|
" h1 = sim.u0[0].download(sim.stream)\n",
|
||||||
|
" #plotSolution(h1, grid)\n",
|
||||||
|
" "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python [default]",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython3",
|
||||||
|
"version": "3.6.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 2
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user