From 888d9f401f82e8a0b708f013c403090f8823076c Mon Sep 17 00:00:00 2001 From: Craig Warren Date: Mon, 9 Jul 2018 11:05:04 +0100 Subject: [PATCH] Moved (and updated) memory estimate function, now attached to Grid class. --- gprMax/grid.py | 62 ++++++++++++++++++++++++++++++++++++++++++++- gprMax/utilities.py | 51 +------------------------------------ 2 files changed, 62 insertions(+), 51 deletions(-) diff --git a/gprMax/grid.py b/gprMax/grid.py index 7b779e50..f619c174 100644 --- a/gprMax/grid.py +++ b/gprMax/grid.py @@ -87,6 +87,7 @@ class FDTDGrid(Grid): self.title = '' self.messages = True self.tqdmdisable = False + self.memoryusage = 0 # Get information about host machine self.hostinfo = None @@ -95,12 +96,17 @@ class FDTDGrid(Grid): self.nthreads = 0 # GPU - # Threads per block + # Threads per block - electric and magnetic field updates self.tpb = (256, 1, 1) # GPU object self.gpu = None + # Copy snapshot data from GPU to CPU during simulation + # N.B. This will happen if the requested snapshots are too large to fit + # on the memory of the GPU. If True this will slow performance significantly + self.snapsgpu2cpu = False + # Threshold (dB) down from maximum power (0dB) of main frequency used # to calculate highest frequency for numerical dispersion analysis self.highestfreqthres = 40 @@ -179,6 +185,60 @@ class FDTDGrid(Grid): self.Tz = np.zeros((Material.maxpoles, self.nx + 1, self.ny + 1, self.nz + 1), dtype=complextype) self.updatecoeffsdispersive = np.zeros((len(self.materials), 3 * Material.maxpoles), dtype=complextype) + def memory_estimate_basic(self): + """Estimate the amount of memory (RAM) required to run a model.""" + + stdoverhead = 45e6 + + # 6 x field arrays + 6 x ID arrays + fieldarrays = (6 + 6) * (self.nx + 1) * (self.ny + 1) * (self.nz + 1) * np.dtype(floattype).itemsize + + solidarray = self.nx * self.ny * self.nz * np.dtype(np.uint32).itemsize + + # 12 x rigidE array components + 6 x rigidH array components + rigidarrays = (12 + 6) * self.nx * self.ny * self.nz * np.dtype(np.int8).itemsize + + # PML arrays + pmlarrays = 0 + for (k, v) in self.pmlthickness.items(): + if v > 0: + if 'x' in k: + pmlarrays += ((v + 1) * self.ny * (self.nz + 1)) + pmlarrays += ((v + 1) * (self.ny + 1) * self.nz) + pmlarrays += (v * self.ny * (self.nz + 1)) + pmlarrays += (v * (self.ny + 1) * self.nz) + elif 'y' in k: + pmlarrays += (self.nx * (v + 1) * (self.nz + 1)) + pmlarrays += ((self.nx + 1) * (v + 1) * self.nz) + pmlarrays += ((self.nx + 1) * v * self.nz) + pmlarrays += (self.nx * v * (self.nz + 1)) + elif 'z' in k: + pmlarrays += (self.nx * (self.ny + 1) * (v + 1)) + pmlarrays += ((self.nx + 1) * self.ny * (v + 1)) + pmlarrays += ((self.nx + 1) * self.ny * v) + pmlarrays += (self.nx * (self.ny + 1) * v) + + self.memoryusage = int(stdoverhead + fieldarrays + solidarray + rigidarrays + pmlarrays) + + def memory_check(self, snapsmemsize=0): + """Check the required amount of memory (RAM) is available on the host and GPU if specified. + + Args: + snapsmemsize (int): amount of memory (bytes) required to store all requested snapshots + """ + + # Check if model can be built and/or run on host + if self.memoryusage > self.hostinfo['ram']: + raise GeneralError('Memory (RAM) required ~{} exceeds {} detected!\n'.format(human_size(self.memoryusage), human_size(self.hostinfo['ram'], a_kilobyte_is_1024_bytes=True))) + + # Check if model can be run on specified GPU if required + if self.gpu is not None: + if self.memoryusage > self.gpu.totalmem: + if snapmemsize != 0: + G.snapsgpu2cpu = True + else: + raise GeneralError('Memory (RAM) required ~{} exceeds {} detected on specified {} - {} GPU!\n'.format(human_size(self.memoryusage), human_size(self.gpu.totalmem, a_kilobyte_is_1024_bytes=True), self.gpu.deviceID, self.gpu.name)) + def gpu_set_blocks_per_grid(self): """Set the blocks per grid size used for updating the electric and magnetic field arrays on a GPU.""" self.bpg = (int(np.ceil(((self.nx + 1) * (self.ny + 1) * (self.nz + 1)) / self.tpb[0])), 1, 1) diff --git a/gprMax/utilities.py b/gprMax/utilities.py index 22e7fdbc..5f823688 100644 --- a/gprMax/utilities.py +++ b/gprMax/utilities.py @@ -392,53 +392,4 @@ def detect_gpus(): print('GPU(s) detected: {}'.format(' | '.join(gputext))) return gpus - - -def memory_usage(G): - """Estimate the amount of memory (RAM) required to run a model. - - Args: - G (class): Grid class instance - holds essential parameters describing the model. - - Returns: - memestimate (int): Estimate of required memory in bytes - """ - - stdoverhead = 50e6 - - # 6 x field arrays + 6 x ID arrays - fieldarrays = (6 + 6) * (G.nx + 1) * (G.ny + 1) * (G.nz + 1) * np.dtype(floattype).itemsize - - solidarray = G.nx * G.ny * G.nz * np.dtype(np.uint32).itemsize - - # 12 x rigidE array components + 6 x rigidH array components - rigidarrays = (12 + 6) * G.nx * G.ny * G.nz * np.dtype(np.int8).itemsize - - # PML arrays - pmlarrays = 0 - for (k, v) in G.pmlthickness.items(): - if v > 0: - if 'x' in k: - pmlarrays += ((v + 1) * G.ny * (G.nz + 1)) - pmlarrays += ((v + 1) * (G.ny + 1) * G.nz) - pmlarrays += (v * G.ny * (G.nz + 1)) - pmlarrays += (v * (G.ny + 1) * G.nz) - elif 'y' in k: - pmlarrays += (G.nx * (v + 1) * (G.nz + 1)) - pmlarrays += ((G.nx + 1) * (v + 1) * G.nz) - pmlarrays += ((G.nx + 1) * v * G.nz) - pmlarrays += (G.nx * v * (G.nz + 1)) - elif 'z' in k: - pmlarrays += (G.nx * (G.ny + 1) * (v + 1)) - pmlarrays += ((G.nx + 1) * G.ny * (v + 1)) - pmlarrays += ((G.nx + 1) * G.ny * v) - pmlarrays += (G.nx * (G.ny + 1) * v) - - # Any dispersive material coefficients - disparrays = 0 - if Material.maxpoles != 0: - disparrays = 3 * Material.maxpoles * (G.nx + 1) * (G.ny + 1) * (G.nz + 1) * np.dtype(complextype).itemsize - - memestimate = int(stdoverhead + fieldarrays + solidarray + rigidarrays + pmlarrays + disparrays) - - return memestimate +