refactor(simulator): move Simulator to its own package

This commit is contained in:
Anthony Berg 2025-06-25 12:37:27 +02:00
parent 3ea511f445
commit 0d3227303a
17 changed files with 159 additions and 156 deletions

View File

@ -29,7 +29,7 @@ import numpy as np
import pycuda.driver as cuda
from tqdm.auto import tqdm
from GPUSimulators import Simulator
from GPUSimulators.simulator import BaseSimulator, BoundaryCondition
from GPUSimulators.common import Timer
from GPUSimulators.gpu import KernelContext
@ -250,7 +250,7 @@ class Autotuner:
logger = logging.getLogger(__name__)
assert issubclass(simulator, Simulator.BaseSimulator)
assert issubclass(simulator, BaseSimulator)
key = simulator.__name__
if key in self.performance:

View File

@ -25,9 +25,11 @@ from mpi4py import MPI
import time
import pycuda.driver as cuda
from GPUSimulators.simulator import BaseSimulator, BoundaryCondition
#import nvtx
from GPUSimulators import Simulator
from GPUSimulators.simulator import boundary
def get_grid(num_nodes, num_dims):
@ -205,7 +207,7 @@ class MPIGrid(object):
return local_rank
class MPISimulator(Simulator.BaseSimulator):
class MPISimulator(BaseSimulator):
"""
Class which handles communication between simulators on different MPI nodes
"""
@ -248,24 +250,24 @@ class MPISimulator(Simulator.BaseSimulator):
#Get coordinate of this node
#and handle global boundary conditions
new_boundary_conditions = Simulator.BoundaryCondition({
'north': Simulator.BoundaryCondition.Type.Dirichlet,
'south': Simulator.BoundaryCondition.Type.Dirichlet,
'east': Simulator.BoundaryCondition.Type.Dirichlet,
'west': Simulator.BoundaryCondition.Type.Dirichlet
new_boundary_conditions = BoundaryCondition({
'north': BoundaryCondition.Type.Dirichlet,
'south': BoundaryCondition.Type.Dirichlet,
'east': BoundaryCondition.Type.Dirichlet,
'west': BoundaryCondition.Type.Dirichlet
})
gi, gj = grid.get_coordinate()
#print("gi: " + str(gi) + ", gj: " + str(gj))
if gi == 0 and boundary_conditions.west != Simulator.BoundaryCondition.Type.Periodic:
if gi == 0 and boundary_conditions.west != BoundaryCondition.Type.Periodic:
self.west = None
new_boundary_conditions.west = boundary_conditions.west
if gj == 0 and boundary_conditions.south != Simulator.BoundaryCondition.Type.Periodic:
if gj == 0 and boundary_conditions.south != BoundaryCondition.Type.Periodic:
self.south = None
new_boundary_conditions.south = boundary_conditions.south
if gi == grid.grid[0]-1 and boundary_conditions.east != Simulator.BoundaryCondition.Type.Periodic:
if gi == grid.grid[0]-1 and boundary_conditions.east != BoundaryCondition.Type.Periodic:
self.east = None
new_boundary_conditions.east = boundary_conditions.east
if gj == grid.grid[1]-1 and boundary_conditions.north != Simulator.BoundaryCondition.Type.Periodic:
if gj == grid.grid[1]-1 and boundary_conditions.north != BoundaryCondition.Type.Periodic:
self.north = None
new_boundary_conditions.north = boundary_conditions.north
sim.set_boundary_conditions(new_boundary_conditions)

View File

@ -20,11 +20,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import logging
from GPUSimulators import Simulator
from GPUSimulators.simulator import BaseSimulator, BaseSimulator
import numpy as np
class SHMEMSimulator(Simulator.BaseSimulator):
class SHMEMSimulator(BaseSimulator):
"""
Class which handles communication and synchronization between simulators in different
contexts (presumably on different GPUs)
@ -89,23 +90,23 @@ class SHMEMSimulator(Simulator.BaseSimulator):
#Get coordinate of this subdomain
#and handle global boundary conditions
new_boundary_conditions = Simulator.BoundaryCondition({
'north': Simulator.BoundaryCondition.Type.Dirichlet,
'south': Simulator.BoundaryCondition.Type.Dirichlet,
'east': Simulator.BoundaryCondition.Type.Dirichlet,
'west': Simulator.BoundaryCondition.Type.Dirichlet
new_boundary_conditions = BoundaryCondition({
'north': BoundaryCondition.Type.Dirichlet,
'south': BoundaryCondition.Type.Dirichlet,
'east': BoundaryCondition.Type.Dirichlet,
'west': BoundaryCondition.Type.Dirichlet
})
gi, gj = grid.get_coordinate(i)
if (gi == 0 and boundary_conditions.west != Simulator.BoundaryCondition.Type.Periodic):
if (gi == 0 and boundary_conditions.west != BoundaryCondition.Type.Periodic):
self.west = None
new_boundary_conditions.west = boundary_conditions.west;
if (gj == 0 and boundary_conditions.south != Simulator.BoundaryCondition.Type.Periodic):
if (gj == 0 and boundary_conditions.south != BoundaryCondition.Type.Periodic):
self.south = None
new_boundary_conditions.south = boundary_conditions.south;
if (gi == grid.grid[0]-1 and boundary_conditions.east != Simulator.BoundaryCondition.Type.Periodic):
if (gi == grid.grid[0]-1 and boundary_conditions.east != BoundaryCondition.Type.Periodic):
self.east = None
new_boundary_conditions.east = boundary_conditions.east;
if (gj == grid.grid[1]-1 and boundary_conditions.north != Simulator.BoundaryCondition.Type.Periodic):
if (gj == grid.grid[1]-1 and boundary_conditions.north != BoundaryCondition.Type.Periodic):
self.north = None
new_boundary_conditions.north = boundary_conditions.north;
sim.set_boundary_conditions(new_boundary_conditions)

View File

@ -20,12 +20,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import logging
from GPUSimulators import Simulator
from GPUSimulators.gpu import CudaContext
import numpy as np
import pycuda.driver as cuda
from GPUSimulators.simulator import BoundaryCondition
from GPUSimulators.gpu import KernelContext
class SHMEMGrid(object):
"""
@ -63,7 +63,7 @@ class SHMEMGrid(object):
for i in range(self.ngpus):
# XXX: disabled for testing on single-GPU system
#self.cuda_contexts.append(CudaContext(device=i, autotuning=False))
self.cuda_contexts.append(CudaContext(device=0, autotuning=False))
self.cuda_contexts.append(KernelContext(device=0, autotuning=False))
def getCoordinate(self, index):
i = (index % self.grid[0])
@ -224,23 +224,23 @@ class SHMEMSimulatorGroup(object):
#Get coordinate of this subdomain
#and handle global boundary conditions
new_boundary_conditions = Simulator.BoundaryCondition({
'north': Simulator.BoundaryCondition.Type.Dirichlet,
'south': Simulator.BoundaryCondition.Type.Dirichlet,
'east': Simulator.BoundaryCondition.Type.Dirichlet,
'west': Simulator.BoundaryCondition.Type.Dirichlet
new_boundary_conditions = BoundaryCondition({
'north': BoundaryCondition.Type.Dirichlet,
'south': BoundaryCondition.Type.Dirichlet,
'east': BoundaryCondition.Type.Dirichlet,
'west': BoundaryCondition.Type.Dirichlet
})
gi, gj = grid.get_coordinate(i)
if (gi == 0 and boundary_conditions.west != Simulator.BoundaryCondition.Type.Periodic):
if (gi == 0 and boundary_conditions.west != BoundaryCondition.Type.Periodic):
self.west = None
new_boundary_conditions.west = boundary_conditions.west;
if (gj == 0 and boundary_conditions.south != Simulator.BoundaryCondition.Type.Periodic):
if (gj == 0 and boundary_conditions.south != BoundaryCondition.Type.Periodic):
self.south = None
new_boundary_conditions.south = boundary_conditions.south;
if (gi == grid.grid[0]-1 and boundary_conditions.east != Simulator.BoundaryCondition.Type.Periodic):
if (gi == grid.grid[0]-1 and boundary_conditions.east != BoundaryCondition.Type.Periodic):
self.east = None
new_boundary_conditions.east = boundary_conditions.east;
if (gj == grid.grid[1]-1 and boundary_conditions.north != Simulator.BoundaryCondition.Type.Periodic):
if (gj == grid.grid[1]-1 and boundary_conditions.north != BoundaryCondition.Type.Periodic):
self.north = None
new_boundary_conditions.north = boundary_conditions.north;
sim.set_boundary_conditions(new_boundary_conditions)

View File

@ -23,7 +23,7 @@ import gc
import numpy as np
from GPUSimulators.Simulator import BoundaryCondition
from GPUSimulators.simulator.boundary import BoundaryCondition
def get_extent(width, height, nx, ny, grid, index=None):

View File

@ -24,7 +24,7 @@ import numpy as np
from pycuda import gpuarray
from GPUSimulators.common import ArakawaA2D
from GPUSimulators.Simulator import BaseSimulator, BoundaryCondition
from GPUSimulators.simulator import BaseSimulator, BoundaryCondition
class EE2DKP07Dimsplit(BaseSimulator):

View File

@ -24,12 +24,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import numpy as np
from pycuda import gpuarray
from GPUSimulators import Simulator
from GPUSimulators.common import ArakawaA2D
from GPUSimulators.Simulator import BoundaryCondition
from GPUSimulators.simulator import BaseSimulator, BoundaryCondition
class Force(Simulator.BaseSimulator):
class Force(BaseSimulator):
"""
Class that solves the SW equations
"""

View File

@ -23,12 +23,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import numpy as np
from pycuda import gpuarray
from GPUSimulators import Simulator
from GPUSimulators.common import ArakawaA2D
from GPUSimulators.Simulator import BoundaryCondition
from GPUSimulators.simulator import BaseSimulator, BoundaryCondition
class HLL(Simulator.BaseSimulator):
class HLL(BaseSimulator):
"""
Class that solves the SW equations using the Harten-Lax -van Leer approximate Riemann solver
"""

View File

@ -23,12 +23,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import numpy as np
from pycuda import gpuarray
from GPUSimulators import Simulator
from GPUSimulators.common import ArakawaA2D
from GPUSimulators.Simulator import BoundaryCondition
from GPUSimulators.simulator import BaseSimulator, BoundaryCondition
class HLL2(Simulator.BaseSimulator):
class HLL2(BaseSimulator):
"""
Class that solves the SW equations using the Forward-Backward linear scheme
"""

View File

@ -28,12 +28,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import numpy as np
from pycuda import gpuarray
from GPUSimulators import Simulator
from GPUSimulators.common import ArakawaA2D
from GPUSimulators.Simulator import BoundaryCondition
from GPUSimulators.simulator import BaseSimulator, BoundaryCondition, conversion
class KP07(Simulator.BaseSimulator):
class KP07(BaseSimulator):
"""
Class that solves the SW equations using the Forward-Backward linear scheme
"""
@ -122,7 +121,7 @@ class KP07(Simulator.BaseSimulator):
self.dx, self.dy, dt,
self.g,
self.theta,
Simulator.step_order_to_coded_int(step=substep, order=self.order),
conversion.step_order_to_coded_int(step=substep, order=self.order),
self.boundary_conditions,
self.u0[0].data.gpudata, self.u0[0].data.strides[0],
self.u0[1].data.gpudata, self.u0[1].data.strides[0],

View File

@ -28,12 +28,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import numpy as np
from pycuda import gpuarray
from GPUSimulators import Simulator
from GPUSimulators.common import ArakawaA2D
from GPUSimulators.Simulator import BoundaryCondition
from GPUSimulators.simulator import BaseSimulator, BoundaryCondition
class KP07Dimsplit(Simulator.BaseSimulator):
class KP07Dimsplit(BaseSimulator):
"""
Class that solves the SW equations using the dimentionally split KP07 scheme
"""

View File

@ -24,12 +24,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import numpy as np
from pycuda import gpuarray
from GPUSimulators import Simulator
from GPUSimulators.common import ArakawaA2D
from GPUSimulators.Simulator import BoundaryCondition
from GPUSimulators.simulator import BaseSimulator, BoundaryCondition
class LxF(Simulator.BaseSimulator):
class LxF(BaseSimulator):
"""
Class that solves the SW equations using the Lax Friedrichs scheme
"""

View File

@ -24,12 +24,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import numpy as np
from pycuda import gpuarray
from GPUSimulators import Simulator
from GPUSimulators.common import ArakawaA2D
from GPUSimulators.Simulator import BoundaryCondition
from GPUSimulators.simulator import BaseSimulator, BoundaryCondition
class WAF(Simulator.BaseSimulator):
class WAF(BaseSimulator):
"""
Class that solves the SW equations using the Forward-Backward linear scheme
"""

View File

@ -0,0 +1,4 @@
from .boundary import BoundaryCondition
# TODO make this dependent on HIP or CUDA
from .simulator import BaseSimulator

View File

@ -0,0 +1,80 @@
# -*- coding: utf-8 -*-
"""
This python module implements the classical Lax-Friedrichs numerical
scheme for the shallow water equations
Copyright (C) 2016 SINTEF ICT
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
# Import packages we need
import numpy as np
from enum import IntEnum
class BoundaryCondition(object):
"""
Class for holding boundary conditions for global boundaries
"""
class Type(IntEnum):
"""
Enum that describes the different types of boundary conditions
WARNING: MUST MATCH THAT OF common.h IN CUDA
"""
Dirichlet = 0,
Neumann = 1,
Periodic = 2,
Reflective = 3
def __init__(self, types: dict[str: Type.Reflective]):
"""
Constructor
"""
self.north = types['north']
self.south = types['south']
self.east = types['east']
self.west = types['west']
if (self.north == BoundaryCondition.Type.Neumann
or self.south == BoundaryCondition.Type.Neumann
or self.east == BoundaryCondition.Type.Neumann
or self.west == BoundaryCondition.Type.Neumann):
raise (NotImplementedError("Neumann boundary condition not supported"))
def __str__(self):
return f"[north={str(self.north)}, south={str(self.south)}, east={str(self.east)}, west={str(self.west)}]"
def as_coded_int(self):
"""
Helper function which packs four boundary conditions into one integer
"""
bc = 0
bc = bc | (self.north & 0x0000000F) << 24
bc = bc | (self.south & 0x0000000F) << 16
bc = bc | (self.east & 0x0000000F) << 8
bc = bc | (self.west & 0x0000000F) << 0
# for t in types:
# print("{0:s}, {1:d}, {1:032b}, {1:08b}".format(t, types[t]))
# print("bc: {0:032b}".format(bc))
return np.int32(bc)

View File

@ -0,0 +1,13 @@
import numpy as np
def step_order_to_coded_int(step, order):
"""
Helper function which packs the step and order into a single integer
"""
step_order = (step << 16) | (order & 0x0000ffff)
# print("Step: {0:032b}".format(step))
# print("Order: {0:032b}".format(order))
# print("Mix: {0:032b}".format(step_order))
return np.int32(step_order)

View File

@ -1,32 +1,8 @@
# -*- coding: utf-8 -*-
"""
This python module implements the classical Lax-Friedrichs numerical
scheme for the shallow water equations
Copyright (C) 2016 SINTEF ICT
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
# Import packages we need
import numpy as np
import logging
from enum import IntEnum
import pycuda.driver as cuda
import numpy as np
from . import BoundaryCondition
from GPUSimulators.common import ProgressPrinter
from GPUSimulators.gpu import KernelContext
@ -38,72 +14,6 @@ def get_types(bc):
'west': BoundaryCondition.Type((bc >> 0) & 0x0000000F)}
return types
def step_order_to_coded_int(step, order):
"""
Helper function which packs the step and order into a single integer
"""
step_order = (step << 16) | (order & 0x0000ffff)
# print("Step: {0:032b}".format(step))
# print("Order: {0:032b}".format(order))
# print("Mix: {0:032b}".format(step_order))
return np.int32(step_order)
class BoundaryCondition(object):
"""
Class for holding boundary conditions for global boundaries
"""
class Type(IntEnum):
"""
Enum that describes the different types of boundary conditions
WARNING: MUST MATCH THAT OF common.h IN CUDA
"""
Dirichlet = 0,
Neumann = 1,
Periodic = 2,
Reflective = 3
def __init__(self, types: dict[str: Type.Reflective]):
"""
Constructor
"""
self.north = types['north']
self.south = types['south']
self.east = types['east']
self.west = types['west']
if (self.north == BoundaryCondition.Type.Neumann
or self.south == BoundaryCondition.Type.Neumann
or self.east == BoundaryCondition.Type.Neumann
or self.west == BoundaryCondition.Type.Neumann):
raise (NotImplementedError("Neumann boundary condition not supported"))
def __str__(self):
return f"[north={str(self.north)}, south={str(self.south)}, east={str(self.east)}, west={str(self.west)}]"
def as_coded_int(self):
"""
Helper function which packs four boundary conditions into one integer
"""
bc = 0
bc = bc | (self.north & 0x0000000F) << 24
bc = bc | (self.south & 0x0000000F) << 16
bc = bc | (self.east & 0x0000000F) << 8
bc = bc | (self.west & 0x0000000F) << 0
# for t in types:
# print("{0:s}, {1:d}, {1:032b}, {1:08b}".format(t, types[t]))
# print("bc: {0:032b}".format(bc))
return np.int32(bc)
class BaseSimulator(object):
def __init__(self,
@ -116,7 +26,7 @@ class BaseSimulator(object):
block_width: int, block_height: int):
"""
Initialization routine
Args:
context: GPU context to use
kernel_wrapper: wrapper function of GPU kernel
@ -173,7 +83,7 @@ class BaseSimulator(object):
return f"{self.__class__.__name__} [{self.nx}x{self.ny}]"
def simulate(self, t, dt=None):
"""
"""
Function which simulates t_end seconds using the step function
Requires that the step() function is implemented in the subclasses
"""
@ -218,7 +128,7 @@ class BaseSimulator(object):
def step(self, dt: int):
"""
Function which performs one single timestep of size dt
Args:
dt: Size of each timestep (seconds)
"""