architecture/lab3-poisson/task.py
from math import sin
import numpy as np
from mpi4py import MPI
class Task:
f = staticmethod(lambda x, y: x * y)
f_left = f1 = staticmethod(lambda y: y ** 2)
f_right = f2 = staticmethod(lambda y: sin(y))
f_bottom = f3 = staticmethod(lambda x: x ** 3)
f_top = f4 = staticmethod(lambda x: x ** 2)
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self):
return '({0:.2f}, {1:.2f})'.format(self.x, self.y)
class Index:
def __init__(self, rows, rows_start, cols, cols_start):
self.rows, self.rows_start, self.cols, self.cols_start = rows, rows_start, cols, cols_start
class Region:
def __init__(self, top=0, right=0, bottom=0, left=0):
self.top, self.right, self.bottom, self.left = top, right, bottom, left
def __repr__(self):
return '{' \
+ 't: {0}, r: {1}, b: {2}, l: {3}'.format(self.top, self.right, self.bottom, self.left) \
+ '}'
class ProcSquare:
def __init__(self, full_region, region, left_top, step_x, step_y):
self.full_region, self.region, self.left_top, self.step_x, self.step_y = full_region, region, left_top, step_x, step_y
self.rows = region.bottom - region.top
self.cols = region.right - region.left
self.calc_region = Region(
top=int(region.top == full_region.top),
left=int(region.left == full_region.left),
right=self.cols - int(region.right == full_region.right),
bottom=self.rows - int(region.bottom == full_region.bottom)
)
self.diff = 0.0
if self.rows > 0 and self.cols > 0:
self.top_border = np.zeros(self.cols, dtype=np.float64)
self.left_border = np.zeros(self.rows, dtype=np.float64)
self.bottom_border = np.zeros(self.cols, dtype=np.float64)
self.right_border = np.zeros(self.rows, dtype=np.float64)
self.sqr_step_x = self.step_x * self.step_x
self.sqr_step_y = self.step_y * self.step_y
self.weight = 1. / (2 * (1. / self.sqr_step_x + 1. / self.sqr_step_y))
if self.region.top == self.full_region.top:
for j in range(self.cols):
self.set(0, j, Task.f_top(left_top.x + step_x * j))
else:
self.neighbor_top_border = np.zeros(self.cols, dtype=np.float64)
if region.bottom == full_region.bottom:
for j in range(self.cols):
self.set(self.rows - 1, j, Task.f_bottom(left_top.x + step_x * j))
else:
self.neighbor_bottom_border = np.zeros(self.cols, dtype=np.float64)
if region.left == full_region.left:
for i in range(self.rows):
self.set(i, 0, Task.f_left(left_top.y + step_y * i))
else:
self.neighbor_left_border = np.zeros(self.rows, dtype=np.float64)
if region.right == full_region.right:
for i in range(self.rows):
self.set(i, self.cols - 1, Task.f_right(left_top.y + step_y * i))
else:
self.neighbor_right_border = np.zeros(self.rows, dtype=np.float64)
if self.rows > 2 and self.cols > 2:
self.inner_lines = []
for i in range(self.rows - 2):
self.inner_lines.append(np.zeros(self.cols - 2, dtype=np.float64))
def get(self, i, j):
if j == -1:
return self.neighbor_left_border[i]
elif j == self.cols:
return self.neighbor_right_border[i]
elif i == -1:
return self.neighbor_top_border[j]
elif i == self.rows:
return self.neighbor_bottom_border[j]
elif j == 0:
return self.left_border[i]
elif j == self.cols - 1:
return self.right_border[i]
elif i == 0:
return self.top_border[j]
elif i == self.rows - 1:
return self.bottom_border[j]
else:
return self.inner_lines[i - 1][j - 1]
def set(self, i, j, val):
if j == -1:
self.neighbor_left_border[i] = val
elif j == self.cols:
self.neighbor_right_border[i] = val
elif i == -1:
self.neighbor_top_border[j] = val
elif i == self.rows:
self.neighbor_bottom_border[j] = val
else:
if j == 0:
self.left_border[i] = val
if j == self.cols - 1:
self.right_border[i] = val
if i == 0:
self.top_border[j] = val
if i == self.rows - 1:
self.bottom_border[j] = val
if (0 < i < self.rows - 1) and (0 < j < self.cols - 1):
self.inner_lines[i - 1][j - 1] = val
def exch(self, comm):
left, right = comm.Shift(1, 1)
top, bottom = comm.Shift(0, 1)
if top != MPI.PROC_NULL:
comm.send(self.top_border, dest=top)
if bottom != MPI.PROC_NULL:
self.neighbor_bottom_border = comm.recv(source=bottom)
if bottom != MPI.PROC_NULL:
comm.send(self.bottom_border, dest=bottom)
if top != MPI.PROC_NULL:
self.neighbor_top_border = comm.recv(source=top)
if right != MPI.PROC_NULL:
comm.send(self.right_border, dest=right)
if left != MPI.PROC_NULL:
self.neighbor_left_border = comm.recv(source=left)
if left != MPI.PROC_NULL:
comm.send(self.left_border, dest=left)
if right != MPI.PROC_NULL:
self.neighbor_right_border = comm.recv(source=right)
comm.barrier()
def calc(self):
self.diff = 0.0
for i in range(self.calc_region.top, self.calc_region.bottom):
for j in range(self.calc_region.left, self.calc_region.right):
x = self.left_top.x + j * self.step_x
y = self.left_top.y + i * self.step_y
val = self.weight * (
(self.get(i + 1, j) + self.get(i - 1, j)) / self.sqr_step_x +
(self.get(i, j + 1) + self.get(i, j - 1)) / self.sqr_step_y -
Task.f(x, y)
)
self.diff = max(self.diff, abs(self.get(i, j) - val))
self.set(i, j, val=val)
def data(self):
temp = np.zeros(self.cols * self.rows, dtype=np.float64)
for i in range(self.rows):
for j in range(self.cols):
temp[i * self.cols + j] = self.get(i, j)
return temp