Basic cylinder_Ascan_2D model is running, but still work to do.

这个提交包含在:
Craig Warren
2019-10-15 12:24:43 +01:00
父节点 0359cef4d6
当前提交 5b8a9e7a8e
共有 13 个文件被更改,包括 235 次插入227 次删除

查看文件

@@ -20,7 +20,6 @@ import decimal as d
import inspect import inspect
import logging import logging
import os import os
import sys
from colorama import init from colorama import init
from colorama import Fore from colorama import Fore
@@ -34,7 +33,6 @@ import gprMax.config as config
from .exceptions import CmdInputError from .exceptions import CmdInputError
from .waveforms import Waveform from .waveforms import Waveform
from .utilities import round_value from .utilities import round_value
from .utilities import set_openmp_threads
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -118,15 +116,6 @@ class Domain(UserObjectSingle):
log.info(f'Mode: {G.mode}') log.info(f'Mode: {G.mode}')
log.info(f'Time step (at CFL limit): {G.dt:g} secs') log.info(f'Time step (at CFL limit): {G.dt:g} secs')
# Number of threads (OpenMP) to use
G.nthreads = set_openmp_threads()
log.info(f'Number of CPU (OpenMP) threads: {G.nthreads}')
if G.nthreads > config.sim_config.hostinfo['physicalcores']:
log.warning(Fore.RED + f"You have specified more threads ({G.nthreads}) \
than available physical CPU cores ({config.hostinfo['physicalcores']}). \
This may lead to degraded performance." + Style.RESET_ALL)
class Discretisation(UserObjectSingle): class Discretisation(UserObjectSingle):
"""Allows you to specify the discretization of space in the x, y, and z """Allows you to specify the discretization of space in the x, y, and z
@@ -288,29 +277,14 @@ class NumThreads(UserObjectSingle):
return '#n_threads:' return '#n_threads:'
def create(self, G, uip): def create(self, G, uip):
# Get information about host machine
try: try:
n = self.kwargs['n'] n = self.kwargs['n']
except KeyError: except KeyError:
raise CmdInputError(self.__str__() + ' requires exactly one parameter to specify the number of threads to use') raise CmdInputError(self.__str__() + ' requires exactly one parameter to specify the number of threads to use')
if n < 1: if n < 1:
raise CmdInputError(self.__str__() + ' requires the value to be an integer not less than one') raise CmdInputError(self.__str__() + ' requires the value to be an integer not less than one')
G.nthreads = n config.model_configs[G.model_num].ompthreads = set_omp_threads(n)
os.environ['OMP_NUM_THREADS'] = str(G.nthreads)
log.info(f'Number of CPU (OpenMP) threads: {G.nthreads}')
if G.nthreads > config.hostinfo['physicalcores']:
log.warning(Fore.RED + f"You have specified more threads ({G.nthreads}) \
than available physical CPU cores (\
{config.sim_config.hostinfo['physicalcores']}). \
This may lead to degraded performance." + Style.RESET_ALL)
# Print information about any GPU in use
if G.gpu is not None:
log.info(f'GPU solving using: {G.gpu.deviceID} - {G.gpu.name}')
class TimeStepStabilityFactor(UserObjectSingle): class TimeStepStabilityFactor(UserObjectSingle):

查看文件

@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>. # along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import logging
from pathlib import Path from pathlib import Path
from colorama import init from colorama import init
@@ -31,6 +32,9 @@ from scipy.constants import mu_0 as m0
from .utilities import get_host_info from .utilities import get_host_info
from .utilities import get_terminal_width from .utilities import get_terminal_width
log = logging.getLogger(__name__)
# Single instance of SimConfig to hold simulation configuration parameters. # Single instance of SimConfig to hold simulation configuration parameters.
sim_config = None sim_config = None
@@ -42,13 +46,22 @@ class ModelConfig:
N.B. Multiple models can exist within a simulation N.B. Multiple models can exist within a simulation
""" """
def __init__(self, i): def __init__(self, model_num):
""" """
Args: Args:
i (int): Current model number. model_num (int): Model number.
""" """
self.i = i # Indexed from 0 self.i = model_num # Indexed from 0
self.ompthreads = None # Number of OpenMP threads
# Store information for CUDA solver type
# gpu: GPU object
# snapsgpu2cpu: 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.cuda = {'gpu': None, 'snapsgpu2cpu': False}
self.memoryusage = 0 # Total memory usage for all grids in the model
self.reuse_geometry = False self.reuse_geometry = False
if not sim_config.single_model: if not sim_config.single_model:
@@ -65,9 +78,8 @@ class ModelConfig:
self.snapshot_dir = '_snaps' self.snapshot_dir = '_snaps'
# String to print at start of each model run # String to print at start of each model run
inputfilestr_f = '\n--- Model {}/{}, input file: {}' inputfilestr = f'\n--- Model {self.i + 1}/{sim_config.model_end}, input file: {sim_config.input_file_path}'
self.inputfilestr = inputfilestr_f.format(self.i + 1, sim_config.model_end, sim_config.input_file_path) self.next_model = Fore.GREEN + f"{inputfilestr} {'-' * (get_terminal_width() - 1 - len(inputfilestr))}\n" + Style.RESET_ALL
self.next_model = Fore.GREEN + '{} {}\n'.format(self.inputfilestr, '-' * (get_terminal_width() - 1 - len(self.inputfilestr))) + Style.RESET_ALL
# Numerical dispersion analysis parameters # Numerical dispersion analysis parameters
# highestfreqthres: threshold (dB) down from maximum power (0dB) of main frequency used # highestfreqthres: threshold (dB) down from maximum power (0dB) of main frequency used
@@ -86,6 +98,13 @@ class ModelConfig:
'dispersivedtype': None, 'dispersivedtype': None,
'dispersiveCdtype': None} 'dispersiveCdtype': None}
def memory_check(self):
"""Check if the required amount of memory (RAM) is available to build
and/or run model on the host.
"""
if self.memoryusage > config.sim_config.hostinfo['ram']:
raise GeneralError(f"Memory (RAM) required ~{human_size(self.memoryusage)} exceeds {human_size(config.hostinfo['ram'], a_kilobyte_is_1024_bytes=True)} detected!\n")
def get_scene(self): def get_scene(self):
if sim_config.scenes: if sim_config.scenes:
return sim_config.scenes[self.i] return sim_config.scenes[self.i]
@@ -113,6 +132,7 @@ class SimulationConfig:
""" """
self.args = args self.args = args
log.debug('Fix parsing args')
# General settings for the simulation # General settings for the simulation
# inputfilepath: path to inputfile location # inputfilepath: path to inputfile location
@@ -143,12 +163,8 @@ class SimulationConfig:
# Store information about host machine # Store information about host machine
self.hostinfo = get_host_info() self.hostinfo = get_host_info()
# Store information for CUDA solver type # Information about any GPUs as a list of GPU objects
# gpus: information about any GPUs as a list of GPU objects self.cuda_gpus = []
# snapsgpu2cpu: 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.cuda = {'gpus': None, 'snapsgpu2cpu': False}
# Data type (precision) for electromagnetic field output # Data type (precision) for electromagnetic field output
self.dtypes = None self.dtypes = None

查看文件

@@ -17,6 +17,7 @@
# along with gprMax. If not, see <http://www.gnu.org/licenses/>. # along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import datetime import datetime
import logging
import gprMax.config as config import gprMax.config as config
from ._version import __version__, codename from ._version import __version__, codename
@@ -25,9 +26,12 @@ from .model_build_run import ModelBuildRun
from .solvers import create_solver from .solvers import create_solver
from .solvers import create_G from .solvers import create_G
from .utilities import get_terminal_width from .utilities import get_terminal_width
from .utilities import human_size
from .utilities import logo from .utilities import logo
from .utilities import timer from .utilities import timer
log = logging.getLogger(__name__)
class Context: class Context:
"""Generic context for the model to run in. Sub-class with specific contexts """Generic context for the model to run in. Sub-class with specific contexts
@@ -42,14 +46,21 @@ class Context:
def run(self): def run(self):
"""Run the simulation in the correct context.""" """Run the simulation in the correct context."""
self.print_logo_copyright() self.print_logo_copyright()
self.print_hostinfo()
self.tsimstart = timer() self.tsimstart = timer()
self._run() self._run()
self.tsimend = timer() self.tsimend = timer()
self.print_time_report()
def print_logo_copyright(self): def print_logo_copyright(self):
"""Print gprMax logo, version, and copyright/licencing information.""" """Print gprMax logo, version, and copyright/licencing information."""
logo(__version__ + ' (' + codename + ')') logo(__version__ + ' (' + codename + ')')
def print_hostinfo(self):
"""Print information about the host machine."""
hyperthreadingstr = f", {config.sim_config.hostinfo['logicalcores']} cores with Hyper-Threading" if config.sim_config.hostinfo['hyperthreading'] else ''
log.info(f"\nHost: {config.sim_config.hostinfo['hostname']} | {config.sim_config.hostinfo['machineID']} | {config.sim_config.hostinfo['sockets']} x {config.sim_config.hostinfo['cpuID']} ({config.sim_config.hostinfo['physicalcores']} cores{hyperthreadingstr}) | {human_size(config.sim_config.hostinfo['ram'], a_kilobyte_is_1024_bytes=True)} RAM | {config.sim_config.hostinfo['osversion']}")
def print_time_report(self): def print_time_report(self):
"""Print the total simulation time based on context.""" """Print the total simulation time based on context."""
s = self.make_time_report() s = self.make_time_report()
@@ -57,7 +68,8 @@ class Context:
def make_time_report(self): def make_time_report(self):
"""Generate a string for the total simulation time.""" """Generate a string for the total simulation time."""
pass s = f"\n=== Simulation on {config.sim_config.hostinfo['hostname']} completed in [HH:MM:SS]: {datetime.timedelta(seconds=self.tsimend - self.tsimstart)}"
return f"{s} {'=' * (get_terminal_width() - 1 - len(s))}\n"
class NoMPIContext(Context): class NoMPIContext(Context):
@@ -75,26 +87,17 @@ class NoMPIContext(Context):
# The next model to run only gets a new solver if the # The next model to run only gets a new solver if the
# geometry is not re-used. # geometry is not re-used.
if i != 0 and config.sim_config.args.geometry_fixed: if i != 0 and config.sim_config.args.geometry_fixed:
config.model_config[i].reuse_geometry = True config.model_configs[i].reuse_geometry = True
else: else:
G = create_G() G = create_G(i)
model = ModelBuildRun(i, G) model = ModelBuildRun(G)
model.build() model.build()
solver = create_solver(G) solver = create_solver(G)
if not config.sim_config.args.geometry_only: if not config.sim_config.args.geometry_only:
model.solve(solver) model.solve(solver)
def make_time_report(self):
"""Function to specialise the time reporting for the standard simulation
context.
"""
sim_time = datetime.timedelta(seconds=self.tsimend - self.tsimstart)
s = f"\n=== Simulation on {self.simconfig.hostinfo['hostname']} completed in [HH:MM:SS]: {sim_time}"
return f"{s} {'=' * (get_terminal_width() - 1 - len(s))}\n"
class MPIContext(Context): class MPIContext(Context):
"""Mixed mode MPI/OpenMP/CUDA context - MPI task farm is used to distribute """Mixed mode MPI/OpenMP/CUDA context - MPI task farm is used to distribute
@@ -109,9 +112,6 @@ class MPIContext(Context):
def _run(self): def _run(self):
pass pass
def make_time_report(self):
pass
class MPINoSpawnContext(Context): class MPINoSpawnContext(Context):
@@ -122,9 +122,6 @@ class MPINoSpawnContext(Context):
def _run(self): def _run(self):
pass pass
def make_time_report(self):
pass
def create_context(): def create_context():
"""Create a context in which to run the simulation. i.e MPI. """Create a context in which to run the simulation. i.e MPI.

查看文件

@@ -17,6 +17,7 @@
# along with gprMax. If not, see <http://www.gnu.org/licenses/>. # along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import os import os
from pathlib import Path
import sys import sys
import h5py import h5py
@@ -114,8 +115,9 @@ class GeometryView:
appendmodelnumber (str): Text to append to filename. appendmodelnumber (str): Text to append to filename.
""" """
self.filename = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(config.general['inputfilepath'])), self.basefilename + appendmodelnumber)) parts = config.sim_config.input_file_path.parts
self.filename += self.fileext self.filename = Path(*parts[:-1], parts[-1] + appendmodelnumber)
self.filename = self.filename.with_suffix(self.fileext)
def write_vtk(self, G, pbar): def write_vtk(self, G, pbar):
""" """

查看文件

@@ -140,4 +140,3 @@ def run_main(args):
write_simulation_config(args) write_simulation_config(args)
context = create_context() context = create_context()
context.run() context.run()
context.print_time_report()

查看文件

@@ -41,13 +41,15 @@ class FDTDGrid:
accessing regularly used parameters. accessing regularly used parameters.
""" """
def __init__(self): def __init__(self, model_num):
"""
Args:
model_num (int): Model number.
"""
self.title = '' self.title = ''
self.memoryusage = 0
self.name = 'Main' self.name = 'Main'
self.mode = '' # 2D TMx, 2D TMy, 2D TMz, or 3D self.model_num = model_num
self.gpu = None
self.outputdirectory = ''
self.nx = 0 self.nx = 0
self.ny = 0 self.ny = 0
@@ -206,14 +208,6 @@ class FDTDGrid:
self.memoryusage = int(stdoverhead + fieldarrays + solidarray + rigidarrays + pmlarrays) self.memoryusage = int(stdoverhead + fieldarrays + solidarray + rigidarrays + pmlarrays)
def memory_check(self):
"""Check if the required amount of memory (RAM) is available to build
and/or run model on the host.
"""
if self.memoryusage > config.sim_config.hostinfo['ram']:
raise GeneralError(f"Memory (RAM) required ~{human_size(self.memoryusage)} \
exceeds {human_size(config.hostinfo['ram'], a_kilobyte_is_1024_bytes=True)} detected!\n")
def tmx(self): def tmx(self):
"""Add PEC boundaries to invariant direction in 2D TMx mode. """Add PEC boundaries to invariant direction in 2D TMx mode.
N.B. 2D modes are a single cell slice of 3D grid. N.B. 2D modes are a single cell slice of 3D grid.
@@ -370,7 +364,7 @@ def dispersion_analysis(G):
# Set maximum frequency to a threshold drop from maximum power, ignoring DC value # Set maximum frequency to a threshold drop from maximum power, ignoring DC value
try: try:
freqthres = np.where(power[freqmaxpower:] < -config.numdispersion['highestfreqthres'])[0][0] + freqmaxpower freqthres = np.where(power[freqmaxpower:] < -config.model_configs[G.model_num].numdispersion['highestfreqthres'])[0][0] + freqmaxpower
results['maxfreq'].append(freqs[freqthres]) results['maxfreq'].append(freqs[freqthres])
except ValueError: except ValueError:
results['error'] = 'unable to calculate maximum power from waveform, most likely due to undersampling.' results['error'] = 'unable to calculate maximum power from waveform, most likely due to undersampling.'
@@ -411,9 +405,9 @@ def dispersion_analysis(G):
minwavelength = minvelocity / results['maxfreq'] minwavelength = minvelocity / results['maxfreq']
# Maximum spatial step # Maximum spatial step
if '3D' in config.general['mode']: if '3D' in config.sim_config.general['mode']:
delta = max(G.dx, G.dy, G.dz) delta = max(G.dx, G.dy, G.dz)
elif '2D' in config.general['mode']: elif '2D' in config.sim_config.general['mode']:
if G.nx == 1: if G.nx == 1:
delta = max(G.dy, G.dz) delta = max(G.dy, G.dz)
elif G.ny == 1: elif G.ny == 1:
@@ -428,7 +422,7 @@ def dispersion_analysis(G):
results['N'] = minwavelength / delta results['N'] = minwavelength / delta
# Check grid sampling will result in physical wave propagation # Check grid sampling will result in physical wave propagation
if int(np.floor(results['N'])) >= config.numdispersion['mingridsampling']: if int(np.floor(results['N'])) >= config.model_configs[G.model_num].numdispersion['mingridsampling']:
# Numerical phase velocity # Numerical phase velocity
vp = np.pi / (results['N'] * np.arcsin((1 / S) * np.sin((np.pi * S) / results['N']))) vp = np.pi / (results['N'] * np.arcsin((1 / S) * np.sin((np.pi * S) / results['N'])))

查看文件

@@ -131,12 +131,18 @@ class DispersiveMaterial(Material):
# The implementation of the dispersive material modelling comes from the # The implementation of the dispersive material modelling comes from the
# derivation in: http://dx.doi.org/10.1109/TAP.2014.2308549 # derivation in: http://dx.doi.org/10.1109/TAP.2014.2308549
self.w = np.zeros(config.materials['maxpoles'], dtype=config.materials['dispersivedtype']) self.w = np.zeros(config.model_configs[G.model_num].materials['maxpoles'],
self.q = np.zeros(config.materials['maxpoles'], dtype=config.materials['dispersivedtype']) dtype=config.materials['dispersivedtype'])
self.zt = np.zeros(config.materials['maxpoles'], dtype=config.materials['dispersivedtype']) self.q = np.zeros(config.model_configs[G.model_num].materials['maxpoles'],
self.zt2 = np.zeros(config.materials['maxpoles'], dtype=config.materials['dispersivedtype']) dtype=config.materials['dispersivedtype'])
self.eqt = np.zeros(config.materials['maxpoles'], dtype=config.materials['dispersivedtype']) self.zt = np.zeros(config.model_configs[G.model_num].materials['maxpoles'],
self.eqt2 = np.zeros(config.materials['maxpoles'], dtype=config.materials['dispersivedtype']) dtype=config.materials['dispersivedtype'])
self.zt2 = np.zeros(config.model_configs[G.model_num].materials['maxpoles'],
dtype=config.materials['dispersivedtype'])
self.eqt = np.zeros(config.model_configs[G.model_num].materials['maxpoles'],
dtype=config.materials['dispersivedtype'])
self.eqt2 = np.zeros(config.model_configs[G.model_num].materials['maxpoles'],
dtype=config.materials['dispersivedtype'])
for x in range(self.poles): for x in range(self.poles):
if 'debye' in self.type: if 'debye' in self.type:
@@ -217,7 +223,7 @@ def process_materials(G):
print a table. print a table.
""" """
if config.materials['maxpoles'] == 0: if config.model_configs[G.model_num].materials['maxpoles'] == 0:
materialsdata = [['\nID', '\nName', '\nType', '\neps_r', 'sigma\n[S/m]', materialsdata = [['\nID', '\nName', '\nType', '\neps_r', 'sigma\n[S/m]',
'\nmu_r', 'sigma*\n[Ohm/m]', 'Dielectric\nsmoothable']] '\nmu_r', 'sigma*\n[Ohm/m]', 'Dielectric\nsmoothable']]
else: else:
@@ -237,7 +243,7 @@ def process_materials(G):
# Add update coefficients to overall storage for dispersive materials # Add update coefficients to overall storage for dispersive materials
if hasattr(material, 'poles'): if hasattr(material, 'poles'):
z = 0 z = 0
for pole in range(config.materials['maxpoles']): for pole in range(config.model_configs[G.model_num].materials['maxpoles']):
G.updatecoeffsdispersive[material.numID, z:z + 3] = (config.sim_config.em_consts['e0'] * G.updatecoeffsdispersive[material.numID, z:z + 3] = (config.sim_config.em_consts['e0'] *
material.eqt2[pole], material.eqt[pole], material.zt[pole]) material.eqt2[pole], material.eqt[pole], material.zt[pole])
z += 3 z += 3
@@ -249,7 +255,7 @@ def process_materials(G):
materialtext.append(material.type) materialtext.append(material.type)
materialtext.append('{:g}'.format(material.er)) materialtext.append('{:g}'.format(material.er))
materialtext.append('{:g}'.format(material.se)) materialtext.append('{:g}'.format(material.se))
if config.materials['maxpoles'] > 0: if config.model_configs[G.model_num].materials['maxpoles'] > 0:
if 'debye' in material.type: if 'debye' in material.type:
materialtext.append('\n'.join('{:g}'.format(deltaer) for deltaer in material.deltaer)) materialtext.append('\n'.join('{:g}'.format(deltaer) for deltaer in material.deltaer))
materialtext.append('\n'.join('{:g}'.format(tau) for tau in material.tau)) materialtext.append('\n'.join('{:g}'.format(tau) for tau in material.tau))
@@ -361,8 +367,8 @@ class PeplinskiSoil:
m.type = 'debye' m.type = 'debye'
m.averagable = False m.averagable = False
m.poles = 1 m.poles = 1
if m.poles > config.materials['maxpoles']: if m.poles > config.model_configs[G.model_num].materials['maxpoles']:
config.materials['maxpoles'] = m.poles config.model_configs[G.model_num].materials['maxpoles'] = m.poles
m.er = eri m.er = eri
m.se = sig m.se = sig
m.deltaer.append(er - eri) m.deltaer.append(er - eri)

查看文件

@@ -71,6 +71,7 @@ from .utilities import get_terminal_width
from .utilities import human_size from .utilities import human_size
from .utilities import open_path_file from .utilities import open_path_file
from .utilities import round32 from .utilities import round32
from .utilities import set_omp_threads
from .utilities import timer from .utilities import timer
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -79,8 +80,7 @@ log = logging.getLogger(__name__)
class ModelBuildRun: class ModelBuildRun:
"""Builds and runs (solves) a model.""" """Builds and runs (solves) a model."""
def __init__(self, i, G): def __init__(self, G):
self.i = i
self.G = G self.G = G
# Monitor memory usage # Monitor memory usage
self.p = None self.p = None
@@ -88,55 +88,67 @@ class ModelBuildRun:
def build(self): def build(self):
"""Builds the Yee cells for a model.""" """Builds the Yee cells for a model."""
G = self.G
# Monitor memory usage # Monitor memory usage
self.p = psutil.Process() self.p = psutil.Process()
# Normal model reading/building process; bypassed if geometry information to be reused # Normal model reading/building process; bypassed if geometry information to be reused
self.reuse_geometry() if config.model_configs[self.i].reuse_geometry else self.build_geometry() self.reuse_geometry() if config.model_configs[G.model_num].reuse_geometry else self.build_geometry()
G = self.G
# Adjust position of simple sources and receivers if required # Adjust position of simple sources and receivers if required
if G.srcsteps[0] != 0 or G.srcsteps[1] != 0 or G.srcsteps[2] != 0: if G.srcsteps[0] != 0 or G.srcsteps[1] != 0 or G.srcsteps[2] != 0:
for source in itertools.chain(G.hertziandipoles, G.magneticdipoles): for source in itertools.chain(G.hertziandipoles, G.magneticdipoles):
if config.model_configs[self.i].i == 0: if config.model_configs[G.model_num] == 0:
if source.xcoord + G.srcsteps[0] * self.sim_config.model_end < 0 or source.xcoord + G.srcsteps[0] * self.sim_config.model_end > G.nx or source.ycoord + G.srcsteps[1] * self.sim_config.model_end < 0 or source.ycoord + G.srcsteps[1] * self.sim_config.model_end > G.ny or source.zcoord + G.srcsteps[2] * self.sim_config.model_end < 0 or source.zcoord + G.srcsteps[2] * self.sim_config.model_end > G.nz: if (source.xcoord + G.srcsteps[0] * self.sim_config.model_end < 0 or
source.xcoord + G.srcsteps[0] * self.sim_config.model_end > G.nx or
source.ycoord + G.srcsteps[1] * self.sim_config.model_end < 0 or
source.ycoord + G.srcsteps[1] * self.sim_config.model_end > G.ny or
source.zcoord + G.srcsteps[2] * self.sim_config.model_end < 0 or
source.zcoord + G.srcsteps[2] * self.sim_config.model_end > G.nz):
raise GeneralError('Source(s) will be stepped to a position outside the domain.') raise GeneralError('Source(s) will be stepped to a position outside the domain.')
source.xcoord = source.xcoordorigin + (config.model_configs[self.i].i) * G.srcsteps[0] source.xcoord = source.xcoordorigin + config.model_configs[G.model_num] * G.srcsteps[0]
source.ycoord = source.ycoordorigin + (config.model_configs[self.i].i) * G.srcsteps[1] source.ycoord = source.ycoordorigin + config.model_configs[G.model_num] * G.srcsteps[1]
source.zcoord = source.zcoordorigin + (config.model_configs[self.i].i) * G.srcsteps[2] source.zcoord = source.zcoordorigin + config.model_configs[G.model_num] * G.srcsteps[2]
if G.rxsteps[0] != 0 or G.rxsteps[1] != 0 or G.rxsteps[2] != 0: if G.rxsteps[0] != 0 or G.rxsteps[1] != 0 or G.rxsteps[2] != 0:
for receiver in G.rxs: for receiver in G.rxs:
if config.model_configs[self.i].i == 0: if config.model_configs[G.model_num] == 0:
if receiver.xcoord + G.rxsteps[0] * self.sim_config.model_end < 0 or receiver.xcoord + G.rxsteps[0] * self.sim_config.model_end > G.nx or receiver.ycoord + G.rxsteps[1] * self.sim_config.model_end < 0 or receiver.ycoord + G.rxsteps[1] * self.sim_config.model_end > G.ny or receiver.zcoord + G.rxsteps[2] * self.sim_config.model_end < 0 or receiver.zcoord + G.rxsteps[2] * self.sim_config.model_end > G.nz: if (receiver.xcoord + G.rxsteps[0] * self.sim_config.model_end < 0 or
receiver.xcoord + G.rxsteps[0] * self.sim_config.model_end > G.nx or
receiver.ycoord + G.rxsteps[1] * self.sim_config.model_end < 0 or
receiver.ycoord + G.rxsteps[1] * self.sim_config.model_end > G.ny or
receiver.zcoord + G.rxsteps[2] * self.sim_config.model_end < 0 or
receiver.zcoord + G.rxsteps[2] * self.sim_config.model_end > G.nz):
raise GeneralError('Receiver(s) will be stepped to a position outside the domain.') raise GeneralError('Receiver(s) will be stepped to a position outside the domain.')
receiver.xcoord = receiver.xcoordorigin + (config.model_configs[self.i].i) * G.rxsteps[0] receiver.xcoord = receiver.xcoordorigin + config.model_configs[G.model_num] * G.rxsteps[0]
receiver.ycoord = receiver.ycoordorigin + (config.model_configs[self.i].i) * G.rxsteps[1] receiver.ycoord = receiver.ycoordorigin + config.model_configs[G.model_num] * G.rxsteps[1]
receiver.zcoord = receiver.zcoordorigin + (config.model_configs[self.i].i) * G.rxsteps[2] receiver.zcoord = receiver.zcoordorigin + config.model_configs[G.model_num] * G.rxsteps[2]
# Write files for any geometry views and geometry object outputs # Write files for any geometry views and geometry object outputs
if not (G.geometryviews or G.geometryobjectswrite) and self.sim_config.geometry_only and config.is_messages(): if not (G.geometryviews or G.geometryobjectswrite) and self.sim_config.geometry_only and config.is_messages():
log.warning(Fore.RED + f'\nNo geometry views or geometry objects to output found.' + Style.RESET_ALL) log.warning(Fore.RED + f'\nNo geometry views or geometry objects to output found.' + Style.RESET_ALL)
if config.is_messages(): print() if config.sim_config.is_messages(): log.info('')
for i, geometryview in enumerate(G.geometryviews): for i, geometryview in enumerate(G.geometryviews):
geometryview.set_filename(config.model_configs[self.i].appendmodelnumber) geometryview.set_filename(config.model_configs[G.model_num].appendmodelnumber)
pbar = tqdm(total=geometryview.datawritesize, unit='byte', unit_scale=True, desc='Writing geometry view file {}/{}, {}'.format(i + 1, len(G.geometryviews), os.path.split(geometryview.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars']) pbar = tqdm(total=geometryview.datawritesize, unit='byte', unit_scale=True,
desc=f'Writing geometry view file {i + 1}/{len(G.geometryviews)}, {geometryview.filename.name}',
ncols=get_terminal_width() - 1, file=sys.stdout,
disable=not config.sim_config.general['progressbars'])
geometryview.write_vtk(G, pbar) geometryview.write_vtk(G, pbar)
pbar.close() pbar.close()
for i, geometryobject in enumerate(G.geometryobjectswrite): for i, geometryobject in enumerate(G.geometryobjectswrite):
pbar = tqdm(total=geometryobject.datawritesize, unit='byte', unit_scale=True, desc='Writing geometry object file {}/{}, {}'.format(i + 1, len(G.geometryobjectswrite), os.path.split(geometryobject.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars']) pbar = tqdm(total=geometryobject.datawritesize, unit='byte', unit_scale=True,
desc=f'Writing geometry object file {i + 1}/{len(G.geometryobjectswrite)}, {geometryobject.filename.name}',
ncols=get_terminal_width() - 1, file=sys.stdout,
disable=not config.sim_config.general['progressbars'])
geometryobject.write_hdf5(G, pbar) geometryobject.write_hdf5(G, pbar)
pbar.close() pbar.close()
if config.is_messages(): print() if config.sim_config.is_messages(): log.info('')
# # If only writing geometry information
# if self.sim_config.geometry_only:
# tsolve = 0
def build_geometry(self): def build_geometry(self):
G = self.G G = self.G
log.info(config.model_configs[self.i].next_model) log.info(config.model_configs[G.model_num].next_model)
scene = self.build_scene() scene = self.build_scene()
@@ -153,7 +165,7 @@ class ModelBuildRun:
gb.grid.initialise_std_update_coeff_arrays() gb.grid.initialise_std_update_coeff_arrays()
# Set datatype for dispersive arrays if there are any dispersive materials. # Set datatype for dispersive arrays if there are any dispersive materials.
if config.model_configs[self.i].materials['maxpoles'] != 0: if config.model_configs[G.model_num].materials['maxpoles'] != 0:
drudelorentz = any([m for m in G.materials if 'drude' in m.type or 'lorentz' in m.type]) drudelorentz = any([m for m in G.materials if 'drude' in m.type or 'lorentz' in m.type])
if drudelorentz: if drudelorentz:
config.materials['dispersivedtype'] = config.dtypes['complex'] config.materials['dispersivedtype'] = config.dtypes['complex']
@@ -163,11 +175,12 @@ class ModelBuildRun:
config.materials['dispersiveCdtype'] = config.dtypes['C_float_or_double'] config.materials['dispersiveCdtype'] = config.dtypes['C_float_or_double']
# Update estimated memory (RAM) usage # Update estimated memory (RAM) usage
G.memoryusage += int(3 * config.materials['maxpoles'] * config.model_configs[G.model_num].memoryusage += int(3 *
(G.nx + 1) * (G.ny + 1) * (G.nz + 1) * config.materials['maxpoles'] *
np.dtype(config.materials['dispersivedtype']).itemsize) (G.nx + 1) * (G.ny + 1) * (G.nz + 1) *
np.dtype(config.materials['dispersivedtype']).itemsize)
G.memory_check() G.memory_check()
log.info(f'\nMemory (RAM) required - updated (dispersive): ~{human_size(G.memoryusage)}\n') log.info(f'\nMemory (RAM) required - updated (dispersive): ~{human_size(config.model_configs[G.model_num].memoryusage)}\n')
for gb in gridbuilders: for gb in gridbuilders:
gb.grid.initialise_dispersive_arrays(config.materials['dispersivedtype']) gb.grid.initialise_dispersive_arrays(config.materials['dispersivedtype'])
@@ -190,103 +203,93 @@ class ModelBuildRun:
# Check to see if numerical dispersion might be a problem # Check to see if numerical dispersion might be a problem
results = dispersion_analysis(G) results = dispersion_analysis(G)
if results['error']: if results['error']:
log.warning(Fore.RED + f"\nNumerical dispersion analysis not carried \ log.warning(Fore.RED + f"\nNumerical dispersion analysis not carried out as {results['error']}" + Style.RESET_ALL)
out as {results['error']}" + Style.RESET_ALL) elif results['N'] < config.model_configs[G.model_num].numdispersion['mingridsampling']:
elif results['N'] < config.numdispersion['mingridsampling']: raise GeneralError(f"Non-physical wave propagation: Material '{results['material'].ID}' has wavelength sampled by {results['N']} cells, less than required minimum for physical wave propagation. Maximum significant frequency estimated as {results['maxfreq']:g}Hz")
raise GeneralError(f"Non-physical wave propagation: Material \ elif (results['deltavp'] and np.abs(results['deltavp']) >
'{results['material'].ID}' has wavelength sampled \ config.model_configs[G.model_num].numdispersion['maxnumericaldisp']):
by {results['N']} cells, less than required \ log.warning(Fore.RED + f"\nPotentially significant numerical dispersion. Estimated largest physical phase-velocity error is {results['deltavp']:.2f}% in material '{results['material'].ID}' whose wavelength sampled by {results['N']} cells. Maximum significant frequency estimated as {results['maxfreq']:g}Hz" + Style.RESET_ALL)
minimum for physical wave propagation. Maximum \
significant frequency estimated as {results['maxfreq']:g}Hz")
elif results['deltavp'] and np.abs(results['deltavp']) > config.numdispersion['maxnumericaldisp']:
log.warning(Fore.RED + f"\nPotentially significant numerical dispersion. \
Estimated largest physical phase-velocity error is \
{results['deltavp']:.2f}% in material '{results['material'].ID}' \
whose wavelength sampled by {results['N']} cells. Maximum \
significant frequency estimated as {results['maxfreq']:g}Hz" + Style.RESET_ALL)
elif results['deltavp']: elif results['deltavp']:
log.info(f"\nNumerical dispersion analysis: estimated largest physical \ log.info(f"\nNumerical dispersion analysis: estimated largest physical phase-velocity error is {results['deltavp']:.2f}% in material '{results['material'].ID}' whose wavelength sampled by {results['N']} cells. Maximum significant frequency estimated as {results['maxfreq']:g}Hz")
phase-velocity error is {results['deltavp']:.2f}% in material \
'{results['material'].ID}' whose wavelength sampled by {results['N']} \
cells. Maximum significant frequency estimated as {results['maxfreq']:g}Hz")
def reuse_geometry(self): def reuse_geometry(self):
G = self.G
# Reset iteration number # Reset iteration number
G.iteration = 0 self.G.iteration = 0
config.model_configs[self.i].inputfilestr = f'\n--- Model {config.model_configs[self.i].appendmodelnumber}/{self.sim_config.model_end}, \ config.model_configs[self.G.model_num].inputfilestr = f'\n--- Model {config.model_configs[self.G.model_num].appendmodelnumber}/{self.sim_config.model_end}, input file (not re-processed, i.e. geometry fixed): {self.sim_config.input_file_path}'
input file (not re-processed, i.e. geometry fixed): {self.sim_config.input_file_path}' log.info(Fore.GREEN + f"{config.model_configs[self.G.model_num].inputfilestr} {'-' * (get_terminal_width() - 1 - len(config.model_configs[self.G.model_num].inputfilestr))}" + Style.RESET_ALL)
log.info(Fore.GREEN + f"{config.model_configs[self.i].inputfilestr} {'-' * (get_terminal_width() - 1 - len(config.model_configs[self.i].inputfilestr))}" + Style.RESET_ALL) for grid in [self.G] + self.G.subgrids:
for grid in [G] + G.subgrids:
grid.reset_fields() grid.reset_fields()
def build_scene(self): def build_scene(self):
G = self.G
# API for multiple scenes / model runs # API for multiple scenes / model runs
scene = config.model_configs[self.i].get_scene() scene = config.model_configs[self.G.model_num].get_scene()
# If there is no scene - process the hash commands instead # If there is no scene - process the hash commands instead
if not scene: if not scene:
scene = Scene() scene = Scene()
# Parse the input file into user objects and add them to the scene # Parse the input file into user objects and add them to the scene
scene = parse_hash_commands(config.model_configs[self.i], G, scene) scene = parse_hash_commands(config.model_configs[self.G.model_num], self.G, scene)
# Creates the internal simulation objects. # Creates the internal simulation objects.
scene.create_internal_objects(G) scene.create_internal_objects(self.G)
return scene return scene
def create_output_directory(self): def create_output_directory(self):
if self.G.outputdirectory: log.debug('Fix output directory path setting')
# Check and set output directory and filename # if self.G.outputdirectory:
try: # # Check and set output directory and filename
os.mkdir(self.G.outputdirectory) # try:
log.info(f'\nCreated output directory: {self.G.outputdirectory}') # os.mkdir(self.G.outputdirectory)
except FileExistsError: # log.info(f'\nCreated output directory: {self.G.outputdirectory}')
pass # except FileExistsError:
# Modify the output path (hack) # pass
config.model_configs[self.i].output_file_path_ext = Path(self.G.outputdirectory, config.model_configs[self.i].output_file_path_ext) # # Modify the output path (hack)
# config.model_configs[G.model_num].output_file_path_ext = Path(self.G.outputdirectory, config.model_configs[G.model_num].output_file_path_ext)
def write_output_data(self): def write_output_data(self):
"""Write output data, i.e. field data for receivers and snapshots """Write output data, i.e. field data for receivers and snapshots
to file(s). to file(s).
""" """
G = self.G
# Write an output file in HDF5 format # Write an output file in HDF5 format
write_hdf5_outputfile(config.model_configs[self.i].output_file_path_ext, G) write_hdf5_outputfile(config.model_configs[self.G.model_num].output_file_path_ext, self.G)
# Write any snapshots to file # Write any snapshots to file
if G.snapshots: if self.G.snapshots:
# Create directory and construct filename from user-supplied name # Create directory and construct filename from user-supplied name
# and model run number # and model run number
snapshotdir = config.model_configs[self.i].snapshot_dir snapshotdir = config.model_configs[self.G.model_num].snapshot_dir
if not os.path.exists(snapshotdir): if not os.path.exists(snapshotdir):
os.mkdir(snapshotdir) os.mkdir(snapshotdir)
log.info('') log.info('')
for i, snap in enumerate(G.snapshots): for i, snap in enumerate(self.G.snapshots):
fn = snapshotdir / Path(config.model_configs[self.i].output_file_path.stem + '_' + snap.basefilename) fn = snapshotdir / Path(config.model_configs[self.G.model_num].output_file_path.stem + '_' + snap.basefilename)
snap.filename = fn.with_suffix('.vti') snap.filename = fn.with_suffix('.vti')
pbar = tqdm(total=snap.vtkdatawritesize, leave=True, unit='byte', unit_scale=True, desc='Writing snapshot file {} of {}, {}'.format(i + 1, len(G.snapshots), os.path.split(snap.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars']) pbar = tqdm(total=snap.vtkdatawritesize, leave=True, unit='byte',
snap.write_vtk_imagedata(pbar, G) unit_scale=True, desc=f'Writing snapshot file {i + 1} of {len(self.G.snapshots)}, {os.path.split(snap.filename)[1]}', ncols=get_terminal_width() - 1, file=sys.stdout,
disable=not config.general['progressbars'])
snap.write_vtk_imagedata(pbar, self.G)
pbar.close() pbar.close()
log.info('') log.info('')
def print_resource_info(self): def print_resource_info(self, tsolve):
"""Print resource information on runtime and memory usage.""" """Print resource information on runtime and memory usage.
Args:
tsolve (float): time taken to execute solving (seconds).
"""
memGPU = '' memGPU = ''
if config.cuda['gpus']: if config.sim_config.general['cuda']:
memGPU = f' host + ~{human_size(self.solver.get_memsolve())} GPU' memGPU = f' host + ~{human_size(self.solver.get_memsolve())} GPU'
log.info(f'\nMemory (RAM) used: ~{human_size(self.p.memory_full_info().uss)}{memGPU}') log.info(f'\nMemory (RAM) used: ~{human_size(self.p.memory_full_info().uss)}{memGPU}')
log.info(f'Solving time [HH:MM:SS]: {datetime.timedelta(seconds=tsolve)}') log.info(f'Solving time [HH:MM:SS]: {datetime.timedelta(seconds=tsolve)}')
def solve(self, solver): def solve(self, solver):
""" """Solve using FDTD method.
Solve using FDTD method.
Args: Args:
solver (Solver): solver object. solver (Solver): solver object.
@@ -295,25 +298,34 @@ class ModelBuildRun:
tsolve (float): time taken to execute solving (seconds). tsolve (float): time taken to execute solving (seconds).
""" """
G = self.G
if config.is_messages():
iterator = tqdm(range(G.iterations), desc='Running simulation, model ' + str(config.model_configs[self.i]
.i + 1) + '/' + str(self.sim_config.model_end), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
else:
iterator = range(0, G.iterations)
self.create_output_directory() self.create_output_directory()
log.info(f'Output file: {config.model_configs[self.i].output_file_path_ext}\n') log.info(f'Output file: {config.model_configs[self.G.model_num].output_file_path_ext}')
# Set and check number of OpenMP threads
if config.sim_config.general['cpu']:
config.model_configs[self.G.model_num].ompthreads = set_omp_threads(config.model_configs[self.G.model_num].ompthreads)
log.info(f'CPU (OpenMP) threads for solving: {config.model_configs[self.G.model_num].ompthreads}')
if config.model_configs[self.G.model_num].ompthreads > config.sim_config.hostinfo['physicalcores']:
log.warning(Fore.RED + f"You have specified more threads ({config.model_configs[self.G.model_num].ompthreads}) than available physical CPU cores ({config.sim_config.hostinfo['physicalcores']}). This may lead to degraded performance." + Style.RESET_ALL)
# Print information about any GPU in use
if config.sim_config.general['cuda']:
log.info(f"GPU for solving: {config.model_configs[self.G.model_num].cuda['gpu'].deviceID} - {config.model_configs[self.G.model_num].cuda['gpu'].name}")
# Prepare iterator
if config.sim_config.is_messages():
iterator = tqdm(range(self.G.iterations), desc='\nRunning simulation, model ' + str(self.G.model_num + 1) + '/' + str(config.sim_config.model_end), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.sim_config.general['progressbars'])
else:
iterator = range(0, self.G.iterations)
# Run solver # Run solver
tsolve = self.solve(solver) tsolve = solver.solve(iterator)
# Write output data, i.e. field data for receivers and snapshots to file(s) # Write output data, i.e. field data for receivers and snapshots to file(s)
self.write_output_data self.write_output_data()
# Print resource information on runtime and memory usage # Print resource information on runtime and memory usage
self.print_resource_info self.print_resource_info(tsolve)
return tsolve return tsolve
@@ -326,7 +338,10 @@ class GridBuilder:
grid = self.grid grid = self.grid
# Build the PMLS # Build the PMLS
pbar = tqdm(total=sum(1 for value in grid.pmlthickness.values() if value > 0), desc='Building {} Grid PML boundaries'.format(self.grid.name), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.sim_config.general['progressbars']) pbar = tqdm(total=sum(1 for value in grid.pmlthickness.values() if value > 0),
desc=f'Building {self.grid.name} Grid PML boundaries',
ncols=get_terminal_width() - 1, file=sys.stdout,
disable=not config.sim_config.general['progressbars'])
for pml_id, thickness in grid.pmlthickness.items(): for pml_id, thickness in grid.pmlthickness.items():
if thickness > 0: if thickness > 0:
build_pml(grid, pml_id, thickness) build_pml(grid, pml_id, thickness)
@@ -337,7 +352,9 @@ class GridBuilder:
# Build the model, i.e. set the material properties (ID) for every edge # Build the model, i.e. set the material properties (ID) for every edge
# of every Yee cell # of every Yee cell
log.info('') log.info('')
pbar = tqdm(total=2, desc='Building {} Grid'.format(self.grid.name), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.sim_config.general['progressbars']) pbar = tqdm(total=2, desc=f'Building {self.grid.name} Grid',
ncols=get_terminal_width() - 1, file=sys.stdout,
disable=not config.sim_config.general['progressbars'])
build_electric_components(self.grid.solid, self.grid.rigidE, self.grid.ID, self.grid) build_electric_components(self.grid.solid, self.grid.rigidE, self.grid.ID, self.grid)
pbar.update() pbar.update()
build_magnetic_components(self.grid.solid, self.grid.rigidH, self.grid.ID, self.grid) build_magnetic_components(self.grid.solid, self.grid.rigidH, self.grid.ID, self.grid)

查看文件

@@ -207,7 +207,7 @@ class PML:
self.CFS = G.cfs self.CFS = G.cfs
if G.gpu is None: if not config.sim_config.general['cuda']:
self.initialise_field_arrays() self.initialise_field_arrays()
def initialise_field_arrays(self): def initialise_field_arrays(self):

查看文件

@@ -16,6 +16,8 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>. # along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import logging
from .cmds_geometry.cmds_geometry import UserObjectGeometry from .cmds_geometry.cmds_geometry import UserObjectGeometry
from .cmds_geometry.fractal_box_builder import FractalBoxBuilder from .cmds_geometry.fractal_box_builder import FractalBoxBuilder
from .cmds_multiple import UserObjectMulti from .cmds_multiple import UserObjectMulti
@@ -29,6 +31,8 @@ from .subgrids.user_objects import SubGridBase as SubGridUserBase
from .user_inputs import create_user_input_points from .user_inputs import create_user_input_points
from .utilities import human_size from .utilities import human_size
log = logging.getLogger(__name__)
class Scene: class Scene:
"""Scene stores all of the user created objects.""" """Scene stores all of the user created objects."""
@@ -90,12 +94,11 @@ class Scene:
try: try:
obj.create(grid, uip) obj.create(grid, uip)
except CmdInputError: except CmdInputError:
logger.exception('Error creating user input object') log.exception('Error creating user input object')
return self return self
def process_singlecmds(self, G): def process_singlecmds(self, G):
# Check for duplicate commands and warn user if they exist # Check for duplicate commands and warn user if they exist
cmds_unique = list(set(self.single_cmds)) cmds_unique = list(set(self.single_cmds))
if len(cmds_unique) != len(self.single_cmds): if len(cmds_unique) != len(self.single_cmds):
@@ -131,7 +134,8 @@ class Scene:
self.process_cmds(self.multiple_cmds, G) self.process_cmds(self.multiple_cmds, G)
# Estimate and check memory (RAM) usage # Estimate and check memory (RAM) usage
G.memory_check() log.debug('Fix memory checks')
# G.memory_check()
#snapshot_memory_check(G) #snapshot_memory_check(G)
# Initialise an array for volumetric material IDs (solid), boolean # Initialise an array for volumetric material IDs (solid), boolean

查看文件

@@ -24,19 +24,22 @@ from .updates import CPUUpdates
from .updates import CUDAUpdates from .updates import CUDAUpdates
def create_G(): def create_G(model_num):
"""Create grid object according to solver. """Create grid object according to solver.
Args:
model_num (int): Model number.
Returns: Returns:
G (FDTDGrid): Holds essential parameters describing the model. G (FDTDGrid): Holds essential parameters describing the model.
""" """
if config.sim_config.general['cuda']: if config.sim_config.general['cpu']:
G = CUDAGrid() G = FDTDGrid(model_num)
elif config.sim_config.general['cuda']:
G = CUDAGrid(model_num)
elif config.sim_config.subgrid: elif config.sim_config.subgrid:
G = FDTDGrid() G = FDTDGrid(model_num)
else:
G = FDTDGrid()
return G return G
@@ -51,7 +54,12 @@ def create_solver(G):
solver (Solver): solver object. solver (Solver): solver object.
""" """
if config.sim_config.gpu: if config.sim_config.general['cpu']:
updates = CPUUpdates(G)
solver = Solver(updates)
props = updates.adapt_dispersive_config()
updates.set_dispersive_updates(props)
elif config.sim_config.general['cuda']:
updates = CUDAUpdates(G) updates = CUDAUpdates(G)
solver = Solver(updates) solver = Solver(updates)
elif config.sim_config.subgrid: elif config.sim_config.subgrid:
@@ -60,12 +68,7 @@ def create_solver(G):
# A large range of different functions exist to advance the time step for # A large range of different functions exist to advance the time step for
# dispersive materials. The correct function is set here based on the # dispersive materials. The correct function is set here based on the
# the required numerical precision and dispersive material type. # the required numerical precision and dispersive material type.
props = updates.adapt_dispersive_config(config) props = updates.adapt_dispersive_config()
updates.set_dispersive_updates(props)
else:
updates = CPUUpdates(G)
solver = Solver(updates)
props = updates.adapt_dispersive_config(config)
updates.set_dispersive_updates(props) updates.set_dispersive_updates(props)
return solver return solver
@@ -84,9 +87,6 @@ class Solver:
self.updates = updates self.updates = updates
self.hsg = hsg self.hsg = hsg
def get_G(self):
return self.updates.G
def solve(self, iterator): def solve(self, iterator):
"""Time step the FDTD model. """Time step the FDTD model.

查看文件

@@ -69,7 +69,7 @@ class CPUUpdates:
update_magnetic(self.grid.nx, update_magnetic(self.grid.nx,
self.grid.ny, self.grid.ny,
self.grid.nz, self.grid.nz,
config.hostinfo['ompthreads'], config.sim_config.hostinfo['ompthreads'],
self.grid.updatecoeffsH, self.grid.updatecoeffsH,
self.grid.ID, self.grid.ID,
self.grid.Ex, self.grid.Ex,
@@ -98,11 +98,11 @@ class CPUUpdates:
def update_electric_a(self): def update_electric_a(self):
"""Update electric field components.""" """Update electric field components."""
# All materials are non-dispersive so do standard update. # All materials are non-dispersive so do standard update.
if config.materials['maxpoles'] == 0: if config.model_configs[self.grid.model_num].materials['maxpoles'] == 0:
update_electric(self.grid.nx, update_electric(self.grid.nx,
self.grid.ny, self.grid.ny,
self.grid.nz, self.grid.nz,
config.hostinfo['ompthreads'], config.sim_config.hostinfo['ompthreads'],
self.grid.updatecoeffsE, self.grid.updatecoeffsE,
self.grid.ID, self.grid.ID,
self.grid.Ex, self.grid.Ex,
@@ -118,8 +118,8 @@ class CPUUpdates:
self.dispersive_update_a(self.grid.nx, self.dispersive_update_a(self.grid.nx,
self.grid.ny, self.grid.ny,
self.grid.nz, self.grid.nz,
config.hostinfo['ompthreads'], config.sim_config.hostinfo['ompthreads'],
config.materials['maxpoles'], config.model_configs[self.grid.model_num].materials['maxpoles'],
self.grid.updatecoeffsE, self.grid.updatecoeffsE,
self.grid.updatecoeffsdispersive, self.grid.updatecoeffsdispersive,
self.grid.ID, self.grid.ID,
@@ -159,12 +159,12 @@ class CPUUpdates:
updated after the electric field has been updated by the PML and updated after the electric field has been updated by the PML and
source updates. source updates.
""" """
if config.materials['maxpoles'] != 0: if config.model_configs[self.grid.model_num].materials['maxpoles'] != 0:
self.dispersive_update_b(self.grid.nx, self.dispersive_update_b(self.grid.nx,
self.grid.ny, self.grid.ny,
self.grid.nz, self.grid.nz,
config.hostinfo['ompthreads'], config.sim_config.hostinfo['ompthreads'],
config.materials['maxpoles'], config.model_configs[self.grid.model_num].materials['maxpoles'],
self.grid.updatecoeffsdispersive, self.grid.updatecoeffsdispersive,
self.grid.ID, self.grid.ID,
self.grid.Tx, self.grid.Tx,
@@ -174,26 +174,23 @@ class CPUUpdates:
self.grid.Ey, self.grid.Ey,
self.grid.Ez) self.grid.Ez)
def adapt_dispersive_config(self, config): def adapt_dispersive_config(self):
"""Set properties for disperive materials. """Set properties for disperive materials.
Args:
config ():
Returns: Returns:
props (Props): Dispersive material properties. props (Props): Dispersive material properties.
""" """
if config.materials['maxpoles'] > 1: if config.model_configs[self.grid.model_num].materials['maxpoles'] > 1:
poles = 'multi' poles = 'multi'
else: else:
poles = '1' poles = '1'
if config.precision == 'single': if config.sim_config.general['precision'] == 'single':
type = 'float' type = 'float'
else: else:
type = 'double' type = 'double'
if config.materials['dispersivedtype'] == config.dtypes['complex']: if config.model_configs[self.grid.model_num].materials['dispersivedtype'] == config.sim_config.dtypes['complex']:
dispersion = 'complex' dispersion = 'complex'
else: else:
dispersion = 'real' dispersion = 'real'
@@ -276,7 +273,7 @@ class CUDAUpdates:
"""Electric and magnetic field updates - prepare kernels, and """Electric and magnetic field updates - prepare kernels, and
get kernel functions. get kernel functions.
""" """
if config.materials['maxpoles'] > 0: if config.model_configs[self.grid.model_num].materials['maxpoles'] > 0:
kernels_fields = SourceModule(kernels_template_fields.substitute( kernels_fields = SourceModule(kernels_template_fields.substitute(
REAL=cudafloattype, REAL=cudafloattype,
COMPLEX=cudacomplextype, COMPLEX=cudacomplextype,
@@ -319,7 +316,7 @@ class CUDAUpdates:
self.copy_mat_coeffs() self.copy_mat_coeffs()
# Electric and magnetic field updates - dispersive materials - get kernel functions and initialise array on GPU # Electric and magnetic field updates - dispersive materials - get kernel functions and initialise array on GPU
if config.materials['maxpoles'] > 0: # If there are any dispersive materials (updates are split into two parts as they require present and updated electric field values). if config.model_configs[self.grid.model_num].materials['maxpoles'] > 0: # If there are any dispersive materials (updates are split into two parts as they require present and updated electric field values).
self.dispersive_update_a = kernels_fields.get_function("update_electric_dispersive_A") self.dispersive_update_a = kernels_fields.get_function("update_electric_dispersive_A")
self.dispersive_update_b = kernels_fields.get_function("update_electric_dispersive_B") self.dispersive_update_b = kernels_fields.get_function("update_electric_dispersive_B")
self.grid.gpu_initialise_dispersive_arrays() self.grid.gpu_initialise_dispersive_arrays()
@@ -543,7 +540,7 @@ class CUDAUpdates:
def update_electric_a(self): def update_electric_a(self):
"""Update electric field components.""" """Update electric field components."""
# All materials are non-dispersive so do standard update. # All materials are non-dispersive so do standard update.
if config.materials['maxpoles'] == 0: if config.model_configs[self.grid.model_num].materials['maxpoles'] == 0:
self.update_electric(np.int32(self.grid.nx), self.update_electric(np.int32(self.grid.nx),
np.int32(self.grid.ny), np.int32(self.grid.ny),
np.int32(self.grid.nz), np.int32(self.grid.nz),
@@ -563,7 +560,7 @@ class CUDAUpdates:
self.dispersive_update_a(np.int32(self.grid.nx), self.dispersive_update_a(np.int32(self.grid.nx),
np.int32(self.grid.ny), np.int32(self.grid.ny),
np.int32(self.grid.nz), np.int32(self.grid.nz),
np.int32(config.materials['maxpoles']), np.int32(config.model_configs[self.grid.model_num].materials['maxpoles']),
self.grid.updatecoeffsdispersive_gpu.gpudata, self.grid.updatecoeffsdispersive_gpu.gpudata,
self.grid.Tx_gpu, self.grid.Tx_gpu,
self.grid.Ty_gpu, self.grid.Ty_gpu,
@@ -628,11 +625,11 @@ class CUDAUpdates:
updated after the electric field has been updated by the PML and updated after the electric field has been updated by the PML and
source updates. source updates.
""" """
if config.materials['maxpoles'] != 0: if config.model_configs[self.grid.model_num].materials['maxpoles'] != 0:
self.dispersive_update_b(np.int32(self.grid.nx), self.dispersive_update_b(np.int32(self.grid.nx),
np.int32(self.grid.ny), np.int32(self.grid.ny),
np.int32(self.grid.nz), np.int32(self.grid.nz),
np.int32(config.materials['maxpoles']), np.int32(config.model_configs[self.grid.model_num].materials['maxpoles']),
self.grid.updatecoeffsdispersive_gpu.gpudata, self.grid.updatecoeffsdispersive_gpu.gpudata,
self.grid.Tx_gpu, self.grid.Tx_gpu,
self.grid.Ty_gpu, self.grid.Ty_gpu,

查看文件

@@ -340,7 +340,7 @@ def get_host_info():
return hostinfo return hostinfo
def set_openmp_threads(): def set_omp_threads(nthreads=None):
"""Sets the number of OpenMP CPU threads for parallelised parts of code. """Sets the number of OpenMP CPU threads for parallelised parts of code.
Returns: Returns:
@@ -367,7 +367,9 @@ def set_openmp_threads():
del os.environ['OMP_PLACES'] del os.environ['OMP_PLACES']
del os.environ['OMP_PROC_BIND'] del os.environ['OMP_PROC_BIND']
if os.environ.get('OMP_NUM_THREADS'): if nthreads:
os.environ['OMP_NUM_THREADS'] = str(nthreads)
elif os.environ.get('OMP_NUM_THREADS'):
nthreads = int(os.environ.get('OMP_NUM_THREADS')) nthreads = int(os.environ.get('OMP_NUM_THREADS'))
else: else:
# Set number of threads to number of physical CPU cores # Set number of threads to number of physical CPU cores