你已经派生过 gprMax
镜像自地址
https://gitee.com/sunhf/gprMax.git
已同步 2025-08-08 07:24:19 +08:00
More docstring cleaning.
这个提交包含在:
@@ -39,12 +39,12 @@ def process_python_include_code(inputfile, usernamespace):
|
|||||||
and insert the contents of the included file at that location.
|
and insert the contents of the included file at that location.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
inputfile (object): File object for input file.
|
inputfile: file object for input file.
|
||||||
usernamespace (dict): Namespace that can be accessed by user
|
usernamespace: namespace that can be accessed by user in any Python code
|
||||||
in any Python code blocks in input file.
|
blocks in input file.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
processedlines (list): Input commands after Python processing.
|
processedlines: list of input commands after Python processing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Strip out any newline characters and comments that must begin with double hashes
|
# Strip out any newline characters and comments that must begin with double hashes
|
||||||
@@ -61,7 +61,9 @@ def process_python_include_code(inputfile, usernamespace):
|
|||||||
|
|
||||||
# Process any Python code
|
# Process any Python code
|
||||||
if(inputlines[x].startswith('#python:')):
|
if(inputlines[x].startswith('#python:')):
|
||||||
logger.warning('#python blocks are deprecated and will be removed in the next release of gprMax. Please convert your model to use our Python API instead.\n')
|
logger.warning('#python blocks are deprecated and will be removed in ' +
|
||||||
|
'the next release of gprMax. Please convert your ' +
|
||||||
|
'model to use our Python API instead.\n')
|
||||||
# String to hold Python code to be executed
|
# String to hold Python code to be executed
|
||||||
pythoncode = ''
|
pythoncode = ''
|
||||||
x += 1
|
x += 1
|
||||||
@@ -70,7 +72,8 @@ def process_python_include_code(inputfile, usernamespace):
|
|||||||
pythoncode += inputlines[x] + '\n'
|
pythoncode += inputlines[x] + '\n'
|
||||||
x += 1
|
x += 1
|
||||||
if x == len(inputlines):
|
if x == len(inputlines):
|
||||||
logger.exception('Cannot find the end of the Python code block, i.e. missing #end_python: command.')
|
logger.exception('Cannot find the end of the Python code ' +
|
||||||
|
'block, i.e. missing #end_python: command.')
|
||||||
raise SyntaxError
|
raise SyntaxError
|
||||||
# Compile code for faster execution
|
# Compile code for faster execution
|
||||||
pythoncompiledcode = compile(pythoncode, '<string>', 'exec')
|
pythoncompiledcode = compile(pythoncode, '<string>', 'exec')
|
||||||
@@ -110,22 +113,21 @@ def process_python_include_code(inputfile, usernamespace):
|
|||||||
x += 1
|
x += 1
|
||||||
|
|
||||||
# Process any include file commands
|
# Process any include file commands
|
||||||
processedlines = process_include_files(processedlines, inputfile)
|
processedlines = process_include_files(processedlines)
|
||||||
|
|
||||||
return processedlines
|
return processedlines
|
||||||
|
|
||||||
|
|
||||||
def process_include_files(hashcmds, inputfile):
|
def process_include_files(hashcmds):
|
||||||
"""Looks for and processes any include file commands and insert
|
"""Looks for and processes any include file commands and insert
|
||||||
the contents of the included file at that location.
|
the contents of the included file at that location.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
hashcmds (list): Input commands.
|
hashcmds: list of input commands.
|
||||||
inputfile (object): File object for input file.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
processedincludecmds (list): Input commands after processing
|
processedincludecmds: list of input commands after processing any
|
||||||
any include file commands.
|
include file commands.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
processedincludecmds = []
|
processedincludecmds = []
|
||||||
@@ -160,14 +162,13 @@ def process_include_files(hashcmds, inputfile):
|
|||||||
return processedincludecmds
|
return processedincludecmds
|
||||||
|
|
||||||
|
|
||||||
def write_processed_file(processedlines, G):
|
def write_processed_file(processedlines):
|
||||||
"""Writes an input file after any Python code and include commands
|
"""Writes an input file after any Python code and include commands
|
||||||
in the original input file have been processed.
|
in the original input file have been processed.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
processedlines (list): Input commands after after processing any
|
processedlines: list of input commands after after processing any
|
||||||
Python code and include commands.
|
Python code and include commands.
|
||||||
G (FDTDGrid): Parameters describing a grid in a model.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
parts = config.get_model_config().output_file_path.parts
|
parts = config.get_model_config().output_file_path.parts
|
||||||
@@ -177,7 +178,8 @@ def write_processed_file(processedlines, G):
|
|||||||
for item in processedlines:
|
for item in processedlines:
|
||||||
f.write(f'{item}')
|
f.write(f'{item}')
|
||||||
|
|
||||||
logger.info(f'Written input commands, after processing any Python code and include commands, to file: {processedfile}\n')
|
logger.info(f'Written input commands, after processing any Python code and ' +
|
||||||
|
f'include commands, to file: {processedfile}\n')
|
||||||
|
|
||||||
|
|
||||||
def check_cmd_names(processedlines, checkessential=True):
|
def check_cmd_names(processedlines, checkessential=True):
|
||||||
@@ -185,13 +187,14 @@ def check_cmd_names(processedlines, checkessential=True):
|
|||||||
and that all essential commands are present.
|
and that all essential commands are present.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
processedlines (list): Input commands after Python processing.
|
processedlines: list of input commands after Python processing.
|
||||||
checkessential (boolean): Perform check to see that all essential commands are present.
|
checkessential: boolean to check for essential commands or not.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
singlecmds (dict): Commands that can only occur once in the model.
|
singlecmds: dict of commands that can only occur once in the model.
|
||||||
multiplecmds (dict): Commands that can have multiple instances in the model.
|
multiplecmds: dict of commands that can have multiple instances in the
|
||||||
geometry (list): Geometry commands in the model.
|
model.
|
||||||
|
geometry: list of geometry commands in the model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Dictionaries of available commands
|
# Dictionaries of available commands
|
||||||
@@ -240,12 +243,17 @@ def check_cmd_names(processedlines, checkessential=True):
|
|||||||
# check first character of parameter string. Ignore case when there
|
# check first character of parameter string. Ignore case when there
|
||||||
# are no parameters for a command, e.g. for #taguchi:
|
# are no parameters for a command, e.g. for #taguchi:
|
||||||
if ' ' not in cmdparams[0] and len(cmdparams.strip('\n')) != 0:
|
if ' ' not in cmdparams[0] and len(cmdparams.strip('\n')) != 0:
|
||||||
logger.exception('There must be a space between the command name and parameters in ' + processedlines[lindex])
|
logger.exception('There must be a space between the command name ' +
|
||||||
|
'and parameters in ' + processedlines[lindex])
|
||||||
raise SyntaxError
|
raise SyntaxError
|
||||||
|
|
||||||
# Check if command name is valid
|
# Check if command name is valid
|
||||||
if cmdname not in essentialcmds and cmdname not in singlecmds and cmdname not in multiplecmds and cmdname not in geometrycmds:
|
if (cmdname not in essentialcmds and
|
||||||
logger.exception('Your input file contains an invalid command: ' + cmdname)
|
cmdname not in singlecmds and
|
||||||
|
cmdname not in multiplecmds and
|
||||||
|
cmdname not in geometrycmds):
|
||||||
|
logger.exception('Your input file contains an invalid command: ' +
|
||||||
|
cmdname)
|
||||||
raise SyntaxError
|
raise SyntaxError
|
||||||
|
|
||||||
# Count essential commands
|
# Count essential commands
|
||||||
@@ -257,7 +265,8 @@ def check_cmd_names(processedlines, checkessential=True):
|
|||||||
if singlecmds[cmdname] is None:
|
if singlecmds[cmdname] is None:
|
||||||
singlecmds[cmdname] = cmd[1].strip(' \t\n')
|
singlecmds[cmdname] = cmd[1].strip(' \t\n')
|
||||||
else:
|
else:
|
||||||
logger.exception('You can only have a single instance of ' + cmdname + ' in your model')
|
logger.exception('You can only have a single instance of ' +
|
||||||
|
cmdname + ' in your model')
|
||||||
raise SyntaxError
|
raise SyntaxError
|
||||||
|
|
||||||
elif cmdname in multiplecmds:
|
elif cmdname in multiplecmds:
|
||||||
@@ -270,25 +279,27 @@ def check_cmd_names(processedlines, checkessential=True):
|
|||||||
|
|
||||||
if checkessential:
|
if checkessential:
|
||||||
if (countessentialcmds < len(essentialcmds)):
|
if (countessentialcmds < len(essentialcmds)):
|
||||||
logger.exception('Your input file is missing essential commands required to run a model. Essential commands are: ' + ', '.join(essentialcmds))
|
logger.exception('Your input file is missing essential commands ' +
|
||||||
|
'required to run a model. Essential commands are: ' +
|
||||||
|
', '.join(essentialcmds))
|
||||||
raise SyntaxError
|
raise SyntaxError
|
||||||
|
|
||||||
return singlecmds, multiplecmds, geometry
|
return singlecmds, multiplecmds, geometry
|
||||||
|
|
||||||
|
|
||||||
def get_user_objects(processedlines, check=True):
|
def get_user_objects(processedlines, checkessential=True):
|
||||||
"""Make a list of all user objects.
|
"""Make a list of all user objects.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
processedlines (list): Input commands after Python processing.
|
processedlines: list of input commands after Python processing.
|
||||||
check (bool): Whether to check for essential commands or not.
|
checkessential: boolean to check for essential commands or not.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
user_objs (list): All user objects.
|
user_objs: list of all user objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Check validity of command names and that essential commands are present
|
# Check validity of command names and that essential commands are present
|
||||||
parsed_commands = check_cmd_names(processedlines, checkessential=check)
|
parsed_commands = check_cmd_names(processedlines, checkessential=checkessential)
|
||||||
|
|
||||||
# Process parameters for commands that can only occur once in the model
|
# Process parameters for commands that can only occur once in the model
|
||||||
single_user_objs = process_singlecmds(parsed_commands[0])
|
single_user_objs = process_singlecmds(parsed_commands[0])
|
||||||
@@ -305,15 +316,14 @@ def get_user_objects(processedlines, check=True):
|
|||||||
return user_objs
|
return user_objs
|
||||||
|
|
||||||
|
|
||||||
def parse_hash_commands(scene, G):
|
def parse_hash_commands(scene):
|
||||||
"""Parse user hash commands and add them to the scene.
|
"""Parse user hash commands and add them to the scene.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
scene (Scene): Scene object.
|
scene: Scene object.
|
||||||
G (FDTDGrid): Parameters describing a grid in a model.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
scene (Scene): Scene object.
|
scene: Scene object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with open(config.sim_config.input_file_path) as inputfile:
|
with open(config.sim_config.input_file_path) as inputfile:
|
||||||
@@ -328,14 +338,15 @@ def parse_hash_commands(scene, G):
|
|||||||
for key, value in sorted(usernamespace.items()):
|
for key, value in sorted(usernamespace.items()):
|
||||||
if key != '__builtins__':
|
if key != '__builtins__':
|
||||||
uservars += f'{key}: {value}, '
|
uservars += f'{key}: {value}, '
|
||||||
logger.info(f'Constants/variables used/available for Python scripting: {{{uservars[:-2]}}}\n')
|
logger.info(f'Constants/variables used/available for Python scripting: ' +
|
||||||
|
f'{{{uservars[:-2]}}}\n')
|
||||||
|
|
||||||
# Write a file containing the input commands after Python or include
|
# Write a file containing the input commands after Python or include
|
||||||
# file commands have been processed
|
# file commands have been processed
|
||||||
if config.sim_config.args.write_processed:
|
if config.sim_config.args.write_processed:
|
||||||
write_processed_file(processedlines, G)
|
write_processed_file(processedlines)
|
||||||
|
|
||||||
user_objs = get_user_objects(processedlines, check=True)
|
user_objs = get_user_objects(processedlines, checkessential==True)
|
||||||
for user_obj in user_objs:
|
for user_obj in user_objs:
|
||||||
scene.add(user_obj)
|
scene.add(user_obj)
|
||||||
|
|
||||||
@@ -365,6 +376,6 @@ def user_libs_fn_to_scene_obj(f, *args, **kwargs):
|
|||||||
with Capturing() as str_cmds:
|
with Capturing() as str_cmds:
|
||||||
f(*args, **kwargs)
|
f(*args, **kwargs)
|
||||||
|
|
||||||
user_objects = get_user_objects(str_cmds, check=False)
|
user_objects = get_user_objects(str_cmds, checkessential=False)
|
||||||
|
|
||||||
return user_objects
|
return user_objects
|
||||||
|
@@ -39,11 +39,11 @@ def check_averaging(averaging):
|
|||||||
"""Check and set material averaging value.
|
"""Check and set material averaging value.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
averaging (string): Input value from hash command - should be 'y'
|
averaging: string for input value from hash command - should be 'y'
|
||||||
or 'n'
|
or 'n'.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
averaging (bool): geometry object material averaging
|
averaging: boolean for geometry object material averaging.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if averaging == 'y':
|
if averaging == 'y':
|
||||||
@@ -51,22 +51,21 @@ def check_averaging(averaging):
|
|||||||
elif averaging == 'n':
|
elif averaging == 'n':
|
||||||
averaging = False
|
averaging = False
|
||||||
else:
|
else:
|
||||||
logger.exception(self.__str__() + f' requires averaging to be either y or n')
|
logger.exception('Averaging should be either y or n')
|
||||||
|
|
||||||
return averaging
|
return averaging
|
||||||
|
|
||||||
|
|
||||||
def process_geometrycmds(geometry):
|
def process_geometrycmds(geometry):
|
||||||
"""
|
"""Checks the validity of command parameters, creates instances of classes
|
||||||
This function checks the validity of command parameters, creates instances
|
of parameters, and calls functions to directly set arrays solid, rigid
|
||||||
of classes of parameters, and calls functions to directly set arrays
|
and ID.
|
||||||
solid, rigid and ID.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
geometry (list): Geometry commands in the model,
|
geometry: list of geometry commands in the model.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
scene_objects (list): Holds objects in scene.
|
scene_objects: list that holds objects in scene.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
scene_objects = []
|
scene_objects = []
|
||||||
@@ -78,7 +77,8 @@ def process_geometrycmds(geometry):
|
|||||||
from .cmds_geometry.geometry_objects_read import GeometryObjectsRead
|
from .cmds_geometry.geometry_objects_read import GeometryObjectsRead
|
||||||
|
|
||||||
if len(tmp) != 6:
|
if len(tmp) != 6:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' requires exactly five parameters')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' requires exactly five parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||||
@@ -88,7 +88,8 @@ def process_geometrycmds(geometry):
|
|||||||
|
|
||||||
elif tmp[0] == '#edge:':
|
elif tmp[0] == '#edge:':
|
||||||
if len(tmp) != 8:
|
if len(tmp) != 8:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' requires exactly seven parameters')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' requires exactly seven parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
edge = Edge(p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
|
edge = Edge(p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
|
||||||
@@ -99,7 +100,8 @@ def process_geometrycmds(geometry):
|
|||||||
|
|
||||||
elif tmp[0] == '#plate:':
|
elif tmp[0] == '#plate:':
|
||||||
if len(tmp) < 8:
|
if len(tmp) < 8:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least seven parameters')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' requires at least seven parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
# Isotropic case
|
# Isotropic case
|
||||||
@@ -115,14 +117,16 @@ def process_geometrycmds(geometry):
|
|||||||
material_ids=tmp[7:])
|
material_ids=tmp[7:])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' too many parameters have been given')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
scene_objects.append(plate)
|
scene_objects.append(plate)
|
||||||
|
|
||||||
elif tmp[0] == '#triangle:':
|
elif tmp[0] == '#triangle:':
|
||||||
if len(tmp) < 12:
|
if len(tmp) < 12:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least eleven parameters')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' requires at least eleven parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||||
@@ -132,26 +136,31 @@ def process_geometrycmds(geometry):
|
|||||||
|
|
||||||
# Isotropic case with no user specified averaging
|
# Isotropic case with no user specified averaging
|
||||||
if len(tmp) == 12:
|
if len(tmp) == 12:
|
||||||
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, material_id=tmp[11])
|
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness,
|
||||||
|
material_id=tmp[11])
|
||||||
|
|
||||||
# Isotropic case with user specified averaging
|
# Isotropic case with user specified averaging
|
||||||
elif len(tmp) == 13:
|
elif len(tmp) == 13:
|
||||||
averaging = check_averaging(tmp[12].lower())
|
averaging = check_averaging(tmp[12].lower())
|
||||||
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, material_id=tmp[11], averaging=averaging)
|
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness,
|
||||||
|
material_id=tmp[11], averaging=averaging)
|
||||||
|
|
||||||
# Uniaxial anisotropic case
|
# Uniaxial anisotropic case
|
||||||
elif len(tmp) == 14:
|
elif len(tmp) == 14:
|
||||||
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, material_ids=tmp[11:])
|
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness,
|
||||||
|
material_ids=tmp[11:])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' too many parameters have been given')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
scene_objects.append(triangle)
|
scene_objects.append(triangle)
|
||||||
|
|
||||||
elif tmp[0] == '#box:':
|
elif tmp[0] == '#box:':
|
||||||
if len(tmp) < 8:
|
if len(tmp) < 8:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least seven parameters')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' requires at least seven parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||||
@@ -171,14 +180,16 @@ def process_geometrycmds(geometry):
|
|||||||
box = Box(p1=p1, p2=p2, material_ids=tmp[7:])
|
box = Box(p1=p1, p2=p2, material_ids=tmp[7:])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' too many parameters have been given')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
scene_objects.append(box)
|
scene_objects.append(box)
|
||||||
|
|
||||||
elif tmp[0] == '#cylinder:':
|
elif tmp[0] == '#cylinder:':
|
||||||
if len(tmp) < 9:
|
if len(tmp) < 9:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least eight parameters')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' requires at least eight parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||||
@@ -192,21 +203,24 @@ def process_geometrycmds(geometry):
|
|||||||
# Isotropic case with user specified averaging
|
# Isotropic case with user specified averaging
|
||||||
elif len(tmp) == 10:
|
elif len(tmp) == 10:
|
||||||
averaging = check_averaging(tmp[9].lower())
|
averaging = check_averaging(tmp[9].lower())
|
||||||
cylinder = Cylinder(p1=p1, p2=p2, r=r, material_id=tmp[8], averaging=averaging)
|
cylinder = Cylinder(p1=p1, p2=p2, r=r, material_id=tmp[8],
|
||||||
|
averaging=averaging)
|
||||||
|
|
||||||
# Uniaxial anisotropic case
|
# Uniaxial anisotropic case
|
||||||
elif len(tmp) == 11:
|
elif len(tmp) == 11:
|
||||||
cylinder = Cylinder(p1=p1, p2=p2, r=r, material_ids=tmp[8:])
|
cylinder = Cylinder(p1=p1, p2=p2, r=r, material_ids=tmp[8:])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' too many parameters have been given')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
scene_objects.append(cylinder)
|
scene_objects.append(cylinder)
|
||||||
|
|
||||||
elif tmp[0] == '#cylindrical_sector:':
|
elif tmp[0] == '#cylindrical_sector:':
|
||||||
if len(tmp) < 10:
|
if len(tmp) < 10:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least nine parameters')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' requires at least nine parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
normal = tmp[1].lower()
|
normal = tmp[1].lower()
|
||||||
@@ -220,29 +234,41 @@ def process_geometrycmds(geometry):
|
|||||||
|
|
||||||
# Isotropic case with no user specified averaging
|
# Isotropic case with no user specified averaging
|
||||||
if len(tmp) == 10:
|
if len(tmp) == 10:
|
||||||
cylindrical_sector = CylindricalSector(normal=normal, ctr1=ctr1, ctr2=ctr2, extent1=extent1,
|
cylindrical_sector = CylindricalSector(normal=normal, ctr1=ctr1,
|
||||||
extent2=extent2, r=r, start=start, end=end, msterial_id=tmp[9])
|
ctr2=ctr2, extent1=extent1,
|
||||||
|
extent2=extent2, r=r,
|
||||||
|
start=start, end=end,
|
||||||
|
material_id=tmp[9])
|
||||||
|
|
||||||
# Isotropic case with user specified averaging
|
# Isotropic case with user specified averaging
|
||||||
elif len(tmp) == 11:
|
elif len(tmp) == 11:
|
||||||
averaging = check_averaging(tmp[10].lower())
|
averaging = check_averaging(tmp[10].lower())
|
||||||
cylindrical_sector = CylindricalSector(normal=normal, ctr1=ctr1, ctr2=ctr2, extent1=extent1, extent2=extent2,
|
cylindrical_sector = CylindricalSector(normal=normal, ctr1=ctr1,
|
||||||
r=r, start=start, end=end, averaging=averaging, material_id=tmp[9])
|
ctr2=ctr2, extent1=extent1,
|
||||||
|
extent2=extent2, r=r,
|
||||||
|
start=start, end=end,
|
||||||
|
averaging=averaging,
|
||||||
|
material_id=tmp[9])
|
||||||
|
|
||||||
# Uniaxial anisotropic case
|
# Uniaxial anisotropic case
|
||||||
elif len(tmp) == 12:
|
elif len(tmp) == 12:
|
||||||
cylindrical_sector = CylindricalSector(normal=normal, ctr1=ctr1, ctr2=ctr2, extent1=extent1,
|
cylindrical_sector = CylindricalSector(normal=normal, ctr1=ctr1,
|
||||||
extent2=extent2, r=r, start=start, end=end, material_ids=tmp[9:])
|
ctr2=ctr2, extent1=extent1,
|
||||||
|
extent2=extent2, r=r,
|
||||||
|
start=start, end=end,
|
||||||
|
material_ids=tmp[9:])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' too many parameters have been given')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
scene_objects.append(cylindrical_sector)
|
scene_objects.append(cylindrical_sector)
|
||||||
|
|
||||||
elif tmp[0] == '#sphere:':
|
elif tmp[0] == '#sphere:':
|
||||||
if len(tmp) < 6:
|
if len(tmp) < 6:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least five parameters')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' requires at least five parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||||
@@ -255,14 +281,16 @@ def process_geometrycmds(geometry):
|
|||||||
# Isotropic case with user specified averaging
|
# Isotropic case with user specified averaging
|
||||||
elif len(tmp) == 7:
|
elif len(tmp) == 7:
|
||||||
averaging = check_averaging(tmp[6].lower())
|
averaging = check_averaging(tmp[6].lower())
|
||||||
sphere = Sphere(p1=p1, r=r, material_id=tmp[5], averaging=averaging)
|
sphere = Sphere(p1=p1, r=r, material_id=tmp[5],
|
||||||
|
averaging=averaging)
|
||||||
|
|
||||||
# Uniaxial anisotropic case
|
# Uniaxial anisotropic case
|
||||||
elif len(tmp) == 8:
|
elif len(tmp) == 8:
|
||||||
sphere = Sphere(p1=p1, r=r, material_id=tmp[5:])
|
sphere = Sphere(p1=p1, r=r, material_id=tmp[5:])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' too many parameters have been given')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
scene_objects.append(sphere)
|
scene_objects.append(sphere)
|
||||||
@@ -271,7 +299,8 @@ def process_geometrycmds(geometry):
|
|||||||
# Default is no dielectric smoothing for a fractal box
|
# Default is no dielectric smoothing for a fractal box
|
||||||
|
|
||||||
if len(tmp) < 14:
|
if len(tmp) < 14:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least thirteen parameters')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' requires at least thirteen parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||||
@@ -283,13 +312,21 @@ def process_geometrycmds(geometry):
|
|||||||
ID = tmp[13]
|
ID = tmp[13]
|
||||||
|
|
||||||
if len(tmp) == 14:
|
if len(tmp) == 14:
|
||||||
fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, mixing_model_id=mixing_model_id, id=ID, n_materials=n_materials)
|
fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim,
|
||||||
|
weighting=weighting, mixing_model_id=mixing_model_id,
|
||||||
|
id=ID, n_materials=n_materials)
|
||||||
elif len(tmp) == 15:
|
elif len(tmp) == 15:
|
||||||
fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, mixing_model_id=mixing_model_id, id=ID, n_materials=n_materials, seed=tmp[14])
|
fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim,
|
||||||
|
weighting=weighting, mixing_model_id=mixing_model_id,
|
||||||
|
id=ID, n_materials=n_materials, seed=tmp[14])
|
||||||
elif len(tmp) == 16:
|
elif len(tmp) == 16:
|
||||||
fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, mixing_model_id=mixing_model_id, id=ID, n_materials=n_materials, seed=tmp[14], averaging=tmp[15].lower())
|
fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim,
|
||||||
|
weighting=weighting, mixing_model_id=mixing_model_id,
|
||||||
|
id=ID, n_materials=n_materials, seed=tmp[14],
|
||||||
|
averaging=tmp[15].lower())
|
||||||
else:
|
else:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' too many parameters have been given')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
scene_objects.append(fb)
|
scene_objects.append(fb)
|
||||||
@@ -300,7 +337,8 @@ def process_geometrycmds(geometry):
|
|||||||
|
|
||||||
if tmp[0] == '#add_surface_roughness:':
|
if tmp[0] == '#add_surface_roughness:':
|
||||||
if len(tmp) < 13:
|
if len(tmp) < 13:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least twelve parameters')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' requires at least twelve parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||||
@@ -311,18 +349,25 @@ def process_geometrycmds(geometry):
|
|||||||
fractal_box_id = tmp[12]
|
fractal_box_id = tmp[12]
|
||||||
|
|
||||||
if len(tmp) == 13:
|
if len(tmp) == 13:
|
||||||
asr = AddSurfaceRoughness(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, limits=limits, fractal_box_id=fractal_box_id)
|
asr = AddSurfaceRoughness(p1=p1, p2=p2, frac_dim=frac_dim,
|
||||||
|
weighting=weighting, limits=limits,
|
||||||
|
fractal_box_id=fractal_box_id)
|
||||||
elif len(tmp) == 14:
|
elif len(tmp) == 14:
|
||||||
asr = AddSurfaceRoughness(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, limits=limits, fractal_box_id=fractal_box_id, seed=int(tmp[13]))
|
asr = AddSurfaceRoughness(p1=p1, p2=p2, frac_dim=frac_dim,
|
||||||
|
weighting=weighting, limits=limits,
|
||||||
|
fractal_box_id=fractal_box_id,
|
||||||
|
seed=int(tmp[13]))
|
||||||
else:
|
else:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' too many parameters have been given')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
scene_objects.append(asr)
|
scene_objects.append(asr)
|
||||||
|
|
||||||
if tmp[0] == '#add_surface_water:':
|
if tmp[0] == '#add_surface_water:':
|
||||||
if len(tmp) != 9:
|
if len(tmp) != 9:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' requires exactly eight parameters')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' requires exactly eight parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||||
@@ -330,12 +375,14 @@ def process_geometrycmds(geometry):
|
|||||||
depth = float(tmp[7])
|
depth = float(tmp[7])
|
||||||
fractal_box_id = tmp[8]
|
fractal_box_id = tmp[8]
|
||||||
|
|
||||||
asf = AddSurfaceWater(p1=p1, p2=p2, depth=depth, fractal_box_id=fractal_box_id)
|
asf = AddSurfaceWater(p1=p1, p2=p2, depth=depth,
|
||||||
|
fractal_box_id=fractal_box_id)
|
||||||
scene_objects.append(asf)
|
scene_objects.append(asf)
|
||||||
|
|
||||||
if tmp[0] == '#add_grass:':
|
if tmp[0] == '#add_grass:':
|
||||||
if len(tmp) < 12:
|
if len(tmp) < 12:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least eleven parameters')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' requires at least eleven parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||||
@@ -346,11 +393,17 @@ def process_geometrycmds(geometry):
|
|||||||
fractal_box_id = tmp[11]
|
fractal_box_id = tmp[11]
|
||||||
|
|
||||||
if len(tmp) == 12:
|
if len(tmp) == 12:
|
||||||
grass = AddGrass(p1=p1, p2=p2, frac_dim=frac_dim, limits=limits, n_blades=n_blades, fractal_box_id=fractal_box_id)
|
grass = AddGrass(p1=p1, p2=p2, frac_dim=frac_dim,
|
||||||
|
limits=limits, n_blades=n_blades,
|
||||||
|
fractal_box_id=fractal_box_id)
|
||||||
elif len(tmp) == 13:
|
elif len(tmp) == 13:
|
||||||
grass = AddGrass(p1=p1, p2=p2, frac_dim=frac_dim, limits=limits, n_blades=n_blades, fractal_box_id=fractal_box_id, seed=int(tmp[12]))
|
grass = AddGrass(p1=p1, p2=p2, frac_dim=frac_dim,
|
||||||
|
limits=limits, n_blades=n_blades,
|
||||||
|
fractal_box_id=fractal_box_id,
|
||||||
|
seed=int(tmp[12]))
|
||||||
else:
|
else:
|
||||||
logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
|
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||||
|
' too many parameters have been given')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
scene_objects.append(grass)
|
scene_objects.append(grass)
|
||||||
|
@@ -28,15 +28,14 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def process_multicmds(multicmds):
|
def process_multicmds(multicmds):
|
||||||
"""
|
"""Checks the validity of command parameters and creates instances of
|
||||||
Checks the validity of command parameters and creates instances of
|
|
||||||
classes of parameters.
|
classes of parameters.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
multicmds (dict): Commands that can have multiple instances in the model.
|
multicmds: dict of commands that can have multiple instances in the model.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
scene_objects (list): Holds objects in scene.
|
scene_objects: list that holds objects in scene.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
scene_objects = []
|
scene_objects = []
|
||||||
@@ -46,10 +45,12 @@ def process_multicmds(multicmds):
|
|||||||
for cmdinstance in multicmds[cmdname]:
|
for cmdinstance in multicmds[cmdname]:
|
||||||
tmp = cmdinstance.split()
|
tmp = cmdinstance.split()
|
||||||
if len(tmp) != 4:
|
if len(tmp) != 4:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly four parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' requires exactly four parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
waveform = Waveform(wave_type=tmp[0], amp=float(tmp[1]), freq=float(tmp[2]), id=tmp[3])
|
waveform = Waveform(wave_type=tmp[0], amp=float(tmp[1]),
|
||||||
|
freq=float(tmp[2]), id=tmp[3])
|
||||||
scene_objects.append(waveform)
|
scene_objects.append(waveform)
|
||||||
|
|
||||||
cmdname = '#voltage_source'
|
cmdname = '#voltage_source'
|
||||||
@@ -57,11 +58,19 @@ def process_multicmds(multicmds):
|
|||||||
for cmdinstance in multicmds[cmdname]:
|
for cmdinstance in multicmds[cmdname]:
|
||||||
tmp = cmdinstance.split()
|
tmp = cmdinstance.split()
|
||||||
if len(tmp) == 6:
|
if len(tmp) == 6:
|
||||||
voltage_source = VoltageSource(polarisation=tmp[0].lower(), p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), resistance=float(tmp[4]), waveform_id=tmp[5])
|
voltage_source = VoltageSource(polarisation=tmp[0].lower(),
|
||||||
|
p1=(float(tmp[1]), float(tmp[2]),
|
||||||
|
float(tmp[3])), resistance=float(tmp[4]),
|
||||||
|
waveform_id=tmp[5])
|
||||||
elif len(tmp) == 8:
|
elif len(tmp) == 8:
|
||||||
voltage_source = VoltageSource(polarisation=tmp[0].lower(), p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), resistance=float(tmp[4]), waveform_id=tmp[5], start=float(tmp[6]), end=float(tmp[7]))
|
voltage_source = VoltageSource(polarisation=tmp[0].lower(),
|
||||||
|
p1=(float(tmp[1]), float(tmp[2]),
|
||||||
|
float(tmp[3])), resistance=float(tmp[4]),
|
||||||
|
waveform_id=tmp[5], start=float(tmp[6]),
|
||||||
|
end=float(tmp[7]))
|
||||||
else:
|
else:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least six parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' requires at least six parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
scene_objects.append(voltage_source)
|
scene_objects.append(voltage_source)
|
||||||
@@ -71,14 +80,21 @@ def process_multicmds(multicmds):
|
|||||||
for cmdinstance in multicmds[cmdname]:
|
for cmdinstance in multicmds[cmdname]:
|
||||||
tmp = cmdinstance.split()
|
tmp = cmdinstance.split()
|
||||||
if len(tmp) < 5:
|
if len(tmp) < 5:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' requires at least five parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
if len(tmp) == 5:
|
if len(tmp) == 5:
|
||||||
hertzian_dipole = HertzianDipole(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4])
|
hertzian_dipole = HertzianDipole(polarisation=tmp[0], p1=(float(tmp[1]),
|
||||||
|
float(tmp[2]), float(tmp[3])),
|
||||||
|
waveform_id=tmp[4])
|
||||||
elif len(tmp) == 7:
|
elif len(tmp) == 7:
|
||||||
hertzian_dipole = HertzianDipole(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4], start=float(tmp[5]), end=float(tmp[6]))
|
hertzian_dipole = HertzianDipole(polarisation=tmp[0], p1=(float(tmp[1]),
|
||||||
|
float(tmp[2]), float(tmp[3])),
|
||||||
|
waveform_id=tmp[4], start=float(tmp[5]),
|
||||||
|
end=float(tmp[6]))
|
||||||
else:
|
else:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' too many parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' too many parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
scene_objects.append(hertzian_dipole)
|
scene_objects.append(hertzian_dipole)
|
||||||
@@ -88,14 +104,21 @@ def process_multicmds(multicmds):
|
|||||||
for cmdinstance in multicmds[cmdname]:
|
for cmdinstance in multicmds[cmdname]:
|
||||||
tmp = cmdinstance.split()
|
tmp = cmdinstance.split()
|
||||||
if len(tmp) < 5:
|
if len(tmp) < 5:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' requires at least five parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
if len(tmp) == 5:
|
if len(tmp) == 5:
|
||||||
magnetic_dipole = MagneticDipole(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4])
|
magnetic_dipole = MagneticDipole(polarisation=tmp[0], p1=(float(tmp[1]),
|
||||||
|
float(tmp[2]), float(tmp[3])),
|
||||||
|
waveform_id=tmp[4])
|
||||||
elif len(tmp) == 7:
|
elif len(tmp) == 7:
|
||||||
magnetic_dipole = MagneticDipole(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4], start=float(tmp[5]), end=float(tmp[6]))
|
magnetic_dipole = MagneticDipole(polarisation=tmp[0], p1=(float(tmp[1]),
|
||||||
|
float(tmp[2]), float(tmp[3])),
|
||||||
|
waveform_id=tmp[4], start=float(tmp[5]),
|
||||||
|
end=float(tmp[6]))
|
||||||
else:
|
else:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' too many parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' too many parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
scene_objects.append(magnetic_dipole)
|
scene_objects.append(magnetic_dipole)
|
||||||
@@ -105,15 +128,22 @@ def process_multicmds(multicmds):
|
|||||||
for cmdinstance in multicmds[cmdname]:
|
for cmdinstance in multicmds[cmdname]:
|
||||||
tmp = cmdinstance.split()
|
tmp = cmdinstance.split()
|
||||||
if len(tmp) < 6:
|
if len(tmp) < 6:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least six parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' requires at least six parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
if len(tmp) == 6:
|
if len(tmp) == 6:
|
||||||
tl = TransmissionLine(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), resistance=float(tmp[4]), waveform_id=tmp[5])
|
tl = TransmissionLine(polarisation=tmp[0], p1=(float(tmp[1]),
|
||||||
|
float(tmp[2]), float(tmp[3])),
|
||||||
|
resistance=float(tmp[4]), waveform_id=tmp[5])
|
||||||
elif len(tmp) == 8:
|
elif len(tmp) == 8:
|
||||||
tl = TransmissionLine(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), resistance=float(tmp[4]), waveform_id=tmp[5], start=tmp[6], end=tmp[7])
|
tl = TransmissionLine(polarisation=tmp[0], p1=(float(tmp[1]),
|
||||||
|
float(tmp[2]), float(tmp[3])),
|
||||||
|
resistance=float(tmp[4]), waveform_id=tmp[5],
|
||||||
|
start=tmp[6], end=tmp[7])
|
||||||
else:
|
else:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' too many parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' too many parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
scene_objects.append(tl)
|
scene_objects.append(tl)
|
||||||
@@ -123,12 +153,14 @@ def process_multicmds(multicmds):
|
|||||||
for cmdinstance in multicmds[cmdname]:
|
for cmdinstance in multicmds[cmdname]:
|
||||||
tmp = cmdinstance.split()
|
tmp = cmdinstance.split()
|
||||||
if len(tmp) != 3 and len(tmp) < 5:
|
if len(tmp) != 3 and len(tmp) < 5:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' has an incorrect number of parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' has an incorrect number of parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
if len(tmp) == 3:
|
if len(tmp) == 3:
|
||||||
rx = Rx(p1=(float(tmp[0]), float(tmp[1]), float(tmp[2])))
|
rx = Rx(p1=(float(tmp[0]), float(tmp[1]), float(tmp[2])))
|
||||||
else:
|
else:
|
||||||
rx = Rx(p1=(float(tmp[0]), float(tmp[1]), float(tmp[2])), id=tmp[3], outputs=' '.join(tmp[4:]))
|
rx = Rx(p1=(float(tmp[0]), float(tmp[1]), float(tmp[2])),
|
||||||
|
id=tmp[3], outputs=' '.join(tmp[4:]))
|
||||||
|
|
||||||
scene_objects.append(rx)
|
scene_objects.append(rx)
|
||||||
|
|
||||||
@@ -137,7 +169,8 @@ def process_multicmds(multicmds):
|
|||||||
for cmdinstance in multicmds[cmdname]:
|
for cmdinstance in multicmds[cmdname]:
|
||||||
tmp = cmdinstance.split()
|
tmp = cmdinstance.split()
|
||||||
if len(tmp) != 9:
|
if len(tmp) != 9:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly nine parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' requires exactly nine parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
p1 = (float(tmp[0]), float(tmp[1]), float(tmp[2]))
|
p1 = (float(tmp[0]), float(tmp[1]), float(tmp[2]))
|
||||||
@@ -152,7 +185,8 @@ def process_multicmds(multicmds):
|
|||||||
for cmdinstance in multicmds[cmdname]:
|
for cmdinstance in multicmds[cmdname]:
|
||||||
tmp = cmdinstance.split()
|
tmp = cmdinstance.split()
|
||||||
if len(tmp) != 11:
|
if len(tmp) != 11:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly eleven parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' requires exactly eleven parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
p1 = (float(tmp[0]), float(tmp[1]), float(tmp[2]))
|
p1 = (float(tmp[0]), float(tmp[1]), float(tmp[2]))
|
||||||
@@ -162,11 +196,13 @@ def process_multicmds(multicmds):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
iterations = int(tmp[9])
|
iterations = int(tmp[9])
|
||||||
snapshot = Snapshot(p1=p1, p2=p2, dl=dl, iterations=iterations, filename=filename)
|
snapshot = Snapshot(p1=p1, p2=p2, dl=dl, iterations=iterations,
|
||||||
|
filename=filename)
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
time = float(tmp[9])
|
time = float(tmp[9])
|
||||||
snapshot = Snapshot(p1=p1, p2=p2, dl=dl, time=time, filename=filename)
|
snapshot = Snapshot(p1=p1, p2=p2, dl=dl, time=time,
|
||||||
|
filename=filename)
|
||||||
|
|
||||||
scene_objects.append(snapshot)
|
scene_objects.append(snapshot)
|
||||||
|
|
||||||
@@ -175,10 +211,12 @@ def process_multicmds(multicmds):
|
|||||||
for cmdinstance in multicmds[cmdname]:
|
for cmdinstance in multicmds[cmdname]:
|
||||||
tmp = cmdinstance.split()
|
tmp = cmdinstance.split()
|
||||||
if len(tmp) != 5:
|
if len(tmp) != 5:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly five parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' requires exactly five parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
material = Material(er=float(tmp[0]), se=float(tmp[1]), mr=float(tmp[2]), sm=float(tmp[3]), id=tmp[4])
|
material = Material(er=float(tmp[0]), se=float(tmp[1]),
|
||||||
|
mr=float(tmp[2]), sm=float(tmp[3]), id=tmp[4])
|
||||||
scene_objects.append(material)
|
scene_objects.append(material)
|
||||||
|
|
||||||
cmdname = '#add_dispersion_debye'
|
cmdname = '#add_dispersion_debye'
|
||||||
@@ -187,7 +225,8 @@ def process_multicmds(multicmds):
|
|||||||
tmp = cmdinstance.split()
|
tmp = cmdinstance.split()
|
||||||
|
|
||||||
if len(tmp) < 4:
|
if len(tmp) < 4:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least four parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' requires at least four parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
poles = int(tmp[0])
|
poles = int(tmp[0])
|
||||||
@@ -199,7 +238,8 @@ def process_multicmds(multicmds):
|
|||||||
er_delta.append(float(tmp[pole]))
|
er_delta.append(float(tmp[pole]))
|
||||||
tau.append(float(tmp[pole + 1]))
|
tau.append(float(tmp[pole + 1]))
|
||||||
|
|
||||||
debye_dispersion = AddDebyeDispersion(poles=poles, er_delta=er_delta, tau=tau, material_ids=material_ids)
|
debye_dispersion = AddDebyeDispersion(poles=poles, er_delta=er_delta,
|
||||||
|
tau=tau, material_ids=material_ids)
|
||||||
scene_objects.append(debye_dispersion)
|
scene_objects.append(debye_dispersion)
|
||||||
|
|
||||||
cmdname = '#add_dispersion_lorentz'
|
cmdname = '#add_dispersion_lorentz'
|
||||||
@@ -208,7 +248,8 @@ def process_multicmds(multicmds):
|
|||||||
tmp = cmdinstance.split()
|
tmp = cmdinstance.split()
|
||||||
|
|
||||||
if len(tmp) < 5:
|
if len(tmp) < 5:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' requires at least five parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
poles = int(tmp[0])
|
poles = int(tmp[0])
|
||||||
@@ -222,7 +263,10 @@ def process_multicmds(multicmds):
|
|||||||
tau.append(float(tmp[pole + 1]))
|
tau.append(float(tmp[pole + 1]))
|
||||||
alpha.append(float(tmp[pole + 2]))
|
alpha.append(float(tmp[pole + 2]))
|
||||||
|
|
||||||
lorentz_dispersion = AddLorentzDispersion(poles=poles, material_ids=material_ids, er_delta=er_delta, tau=tau, alpha=alpha)
|
lorentz_dispersion = AddLorentzDispersion(poles=poles,
|
||||||
|
material_ids=material_ids,
|
||||||
|
er_delta=er_delta, tau=tau,
|
||||||
|
alpha=alpha)
|
||||||
scene_objects.append(lorentz_dispersion)
|
scene_objects.append(lorentz_dispersion)
|
||||||
|
|
||||||
cmdname = '#add_dispersion_drude'
|
cmdname = '#add_dispersion_drude'
|
||||||
@@ -231,7 +275,8 @@ def process_multicmds(multicmds):
|
|||||||
tmp = cmdinstance.split()
|
tmp = cmdinstance.split()
|
||||||
|
|
||||||
if len(tmp) < 5:
|
if len(tmp) < 5:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' requires at least five parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
poles = int(tmp[0])
|
poles = int(tmp[0])
|
||||||
@@ -243,7 +288,9 @@ def process_multicmds(multicmds):
|
|||||||
tau.append(float(tmp[pole]))
|
tau.append(float(tmp[pole]))
|
||||||
alpha.append(float(tmp[pole + 1]))
|
alpha.append(float(tmp[pole + 1]))
|
||||||
|
|
||||||
drude_dispersion = AddDrudeDispersion(poles=poles, material_ids=material_ids, tau=tau, alpha=alpha)
|
drude_dispersion = AddDrudeDispersion(poles=poles,
|
||||||
|
material_ids=material_ids,
|
||||||
|
tau=tau, alpha=alpha)
|
||||||
scene_objects.append(drude_dispersion)
|
scene_objects.append(drude_dispersion)
|
||||||
|
|
||||||
cmdname = '#soil_peplinski'
|
cmdname = '#soil_peplinski'
|
||||||
@@ -252,7 +299,8 @@ def process_multicmds(multicmds):
|
|||||||
tmp = cmdinstance.split()
|
tmp = cmdinstance.split()
|
||||||
|
|
||||||
if len(tmp) != 7:
|
if len(tmp) != 7:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at exactly seven parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' requires at exactly seven parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
soil = SoilPeplinski(sand_fraction=float(tmp[0]),
|
soil = SoilPeplinski(sand_fraction=float(tmp[0]),
|
||||||
clay_fraction=float(tmp[1]),
|
clay_fraction=float(tmp[1]),
|
||||||
@@ -268,14 +316,16 @@ def process_multicmds(multicmds):
|
|||||||
for cmdinstance in multicmds[cmdname]:
|
for cmdinstance in multicmds[cmdname]:
|
||||||
tmp = cmdinstance.split()
|
tmp = cmdinstance.split()
|
||||||
if len(tmp) != 11:
|
if len(tmp) != 11:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly eleven parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' requires exactly eleven parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
p1 = float(tmp[0]), float(tmp[1]), float(tmp[2])
|
p1 = float(tmp[0]), float(tmp[1]), float(tmp[2])
|
||||||
p2 = float(tmp[3]), float(tmp[4]), float(tmp[5])
|
p2 = float(tmp[3]), float(tmp[4]), float(tmp[5])
|
||||||
dl = float(tmp[6]), float(tmp[7]), float(tmp[8])
|
dl = float(tmp[6]), float(tmp[7]), float(tmp[8])
|
||||||
|
|
||||||
geometry_view = GeometryView(p1=p1, p2=p2, dl=dl, filename=tmp[9], output_type=tmp[10])
|
geometry_view = GeometryView(p1=p1, p2=p2, dl=dl, filename=tmp[9],
|
||||||
|
output_type=tmp[10])
|
||||||
scene_objects.append(geometry_view)
|
scene_objects.append(geometry_view)
|
||||||
|
|
||||||
cmdname = '#geometry_objects_write'
|
cmdname = '#geometry_objects_write'
|
||||||
@@ -283,7 +333,8 @@ def process_multicmds(multicmds):
|
|||||||
for cmdinstance in multicmds[cmdname]:
|
for cmdinstance in multicmds[cmdname]:
|
||||||
tmp = cmdinstance.split()
|
tmp = cmdinstance.split()
|
||||||
if len(tmp) != 7:
|
if len(tmp) != 7:
|
||||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly seven parameters')
|
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||||
|
' requires exactly seven parameters')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
p1 = float(tmp[0]), float(tmp[1]), float(tmp[2])
|
p1 = float(tmp[0]), float(tmp[1]), float(tmp[2])
|
||||||
|
@@ -27,15 +27,14 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def process_singlecmds(singlecmds):
|
def process_singlecmds(singlecmds):
|
||||||
"""
|
"""Checks the validity of command parameters and creates instances of
|
||||||
Checks the validity of command parameters and creates instances of
|
|
||||||
classes of parameters.
|
classes of parameters.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
singlecmds (dict): Commands that can only occur once in the model.
|
singlecmds: dict of commands that can only occur once in the model.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
scene_objects (list): Holds objects in scene.
|
scene_objects: list that holds objects in scene.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
scene_objects = []
|
scene_objects = []
|
||||||
@@ -56,7 +55,8 @@ def process_singlecmds(singlecmds):
|
|||||||
if singlecmds[cmd] is not None:
|
if singlecmds[cmd] is not None:
|
||||||
tmp = tuple(int(x) for x in singlecmds[cmd].split())
|
tmp = tuple(int(x) for x in singlecmds[cmd].split())
|
||||||
if len(tmp) != 1:
|
if len(tmp) != 1:
|
||||||
logger.exception(cmd + ' requires exactly one parameter to specify the number of CPU OpenMP threads to use')
|
logger.exception(cmd + ' requires exactly one parameter to specify ' +
|
||||||
|
'the number of CPU OpenMP threads to use')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
omp_threads = OMPThreads(n=tmp[0])
|
omp_threads = OMPThreads(n=tmp[0])
|
||||||
@@ -94,7 +94,8 @@ def process_singlecmds(singlecmds):
|
|||||||
if singlecmds[cmd] is not None:
|
if singlecmds[cmd] is not None:
|
||||||
tmp = singlecmds[cmd].split()
|
tmp = singlecmds[cmd].split()
|
||||||
if len(tmp) != 1:
|
if len(tmp) != 1:
|
||||||
logger.exception(cmd + ' requires exactly one parameter to specify the time window. Either in seconds or number of iterations.')
|
logger.exception(cmd + ' requires exactly one parameter to specify ' +
|
||||||
|
'the time window. Either in seconds or number of iterations.')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
tmp = tmp[0].lower()
|
tmp = tmp[0].lower()
|
||||||
|
|
||||||
|
@@ -29,8 +29,8 @@ class Material:
|
|||||||
def __init__(self, numID, ID):
|
def __init__(self, numID, ID):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
numID (int): Numeric identifier of the material.
|
numID: int for numeric I of the material.
|
||||||
ID (str): Name of the material.
|
ID: string for name of the material.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.numID = numID
|
self.numID = numID
|
||||||
@@ -49,7 +49,7 @@ class Material:
|
|||||||
"""Calculates the magnetic update coefficients of the material.
|
"""Calculates the magnetic update coefficients of the material.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G (FDTDGrid): Parameters describing a grid in a model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
HA = (config.m0 * self.mr / G.dt) + 0.5 * self.sm
|
HA = (config.m0 * self.mr / G.dt) + 0.5 * self.sm
|
||||||
@@ -64,7 +64,7 @@ class Material:
|
|||||||
"""Calculates the electric update coefficients of the material.
|
"""Calculates the electric update coefficients of the material.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G (FDTDGrid): Parameters describing a grid in a model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
EA = (config.sim_config.em_consts['e0'] * self.er / G.dt) + 0.5 * self.se
|
EA = (config.sim_config.em_consts['e0'] * self.er / G.dt) + 0.5 * self.se
|
||||||
@@ -88,11 +88,11 @@ class Material:
|
|||||||
specific frequency.
|
specific frequency.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
freq (float): Frequency used to calculate complex relative
|
freq: float for frequency used to calculate complex relative
|
||||||
permittivity.
|
permittivity.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
er (float): Complex relative permittivity.
|
er: float for complex relative permittivity.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.er
|
return self.er
|
||||||
@@ -126,7 +126,7 @@ class DispersiveMaterial(Material):
|
|||||||
"""Calculates the electric update coefficients of the material.
|
"""Calculates the electric update coefficients of the material.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G (FDTDGrid): Parameters describing a grid in a model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# The implementation of the dispersive material modelling comes from the
|
# The implementation of the dispersive material modelling comes from the
|
||||||
@@ -183,11 +183,11 @@ class DispersiveMaterial(Material):
|
|||||||
specific frequency.
|
specific frequency.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
freq (float): Frequency used to calculate complex relative
|
freq: float for frequency used to calculate complex relative
|
||||||
permittivity.
|
permittivity.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
er (float): Complex relative permittivity.
|
er: float for complex relative permittivity.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Permittivity at infinite frequency if the material is dispersive
|
# Permittivity at infinite frequency if the material is dispersive
|
||||||
@@ -212,15 +212,15 @@ class DispersiveMaterial(Material):
|
|||||||
|
|
||||||
|
|
||||||
def process_materials(G):
|
def process_materials(G):
|
||||||
"""Process complete list of materials - calculate update coefficients,
|
"""Processes complete list of materials - calculates update coefficients,
|
||||||
store in arrays, and build text list of materials/properties
|
stores in arrays, and builds text list of materials/properties
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G (FDTDGrid): Parameters describing a grid in a model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
materialsdata (list): List of material IDs, names, and properties to
|
materialsdata: list of material IDs, names, and properties to
|
||||||
print a table.
|
print a table.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if config.get_model_config().materials['maxpoles'] == 0:
|
if config.get_model_config().materials['maxpoles'] == 0:
|
||||||
@@ -290,14 +290,15 @@ class PeplinskiSoil:
|
|||||||
def __init__(self, ID, sandfraction, clayfraction, bulkdensity, sandpartdensity, watervolfraction):
|
def __init__(self, ID, sandfraction, clayfraction, bulkdensity, sandpartdensity, watervolfraction):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
ID (str): Name of the soil.
|
ID: string for name of the soil.
|
||||||
sandfraction (float): Sand fraction of the soil.
|
sandfraction: float of sand fraction of the soil.
|
||||||
clayfraction (float): Clay fraction of the soil.
|
clayfraction: float of clay fraction of the soil.
|
||||||
bulkdensity (float): Bulk density of the soil (g/cm3).
|
bulkdensity: float of bulk density of the soil (g/cm3).
|
||||||
sandpartdensity (float): Density of the sand particles in the
|
sandpartdensity: float of density of the sand particles in the
|
||||||
soil (g/cm3).
|
soil (g/cm3).
|
||||||
watervolfraction (float): Two numbers that specify a range for the
|
watervolfraction: tuple of floats of two numbers that specify a
|
||||||
volumetric water fraction of the soil.
|
range for the volumetric water fraction of the
|
||||||
|
soil.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.ID = ID
|
self.ID = ID
|
||||||
@@ -314,8 +315,8 @@ class PeplinskiSoil:
|
|||||||
model (http://dx.doi.org/10.1109/36.387598).
|
model (http://dx.doi.org/10.1109/36.387598).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
nbins (int): Number of bins to use to create the different materials.
|
nbins: int for number of bins to use to create the different materials.
|
||||||
G (FDTDGrid): Parameters describing a grid in a model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Debye model properties of water at 25C & zero salinity
|
# Debye model properties of water at 25C & zero salinity
|
||||||
@@ -384,7 +385,7 @@ def create_built_in_materials(G):
|
|||||||
"""Create pre-defined (built-in) materials.
|
"""Create pre-defined (built-in) materials.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G (FDTDGrid): Parameters describing a grid in a model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
G.n_built_in_materials = len(G.materials)
|
G.n_built_in_materials = len(G.materials)
|
||||||
@@ -406,23 +407,25 @@ def calculate_water_properties(T=25, S=0):
|
|||||||
"""Get extended Debye model properties for water.
|
"""Get extended Debye model properties for water.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
T (float): Temperature of water (degrees centigrade)
|
T: float for emperature of water (degrees centigrade).
|
||||||
S (float): Salinity of water (part per thousand)
|
S: float for salinity of water (part per thousand).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
eri (float): Relative permittivity at infinite frequency.
|
eri: float for relative permittivity at infinite frequency.
|
||||||
er (float): Static relative permittivity.
|
er: float for static relative permittivity.
|
||||||
tau (float): Relaxation time (s).
|
tau: float for relaxation time (s).
|
||||||
sig (float): Conductivity (S/m)
|
sig: float for conductivity (Siemens/m).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Properties of water from: https://doi.org/10.1109/JOE.1977.1145319
|
# Properties of water from: https://doi.org/10.1109/JOE.1977.1145319
|
||||||
eri = 4.9
|
eri = 4.9
|
||||||
er = 88.045 - 0.4147 * T + 6.295e-4 * T**2 + 1.075e-5 * T**3
|
er = 88.045 - 0.4147 * T + 6.295e-4 * T**2 + 1.075e-5 * T**3
|
||||||
tau = (1 / (2 * np.pi)) * (1.1109e-10 - 3.824e-12 * T + 6.938e-14 * T**2 - 5.096e-16 * T**3)
|
tau = (1 / (2 * np.pi)) * (1.1109e-10 - 3.824e-12 * T + 6.938e-14 * T**2 -
|
||||||
|
5.096e-16 * T**3)
|
||||||
|
|
||||||
delta = 25 - T
|
delta = 25 - T
|
||||||
beta = 2.033e-2 + 1.266e-4 * delta + 2.464e-6 * delta**2 - S * (1.849e-5 - 2.551e-7 * delta + 2.551e-8 * delta**2)
|
beta = (2.033e-2 + 1.266e-4 * delta + 2.464e-6 * delta**2 - S *
|
||||||
|
(1.849e-5 - 2.551e-7 * delta + 2.551e-8 * delta**2))
|
||||||
sig_25s = S * (0.182521 - 1.46192e-3 * S + 2.09324e-5 * S**2 - 1.28205e-7 * S**3)
|
sig_25s = S * (0.182521 - 1.46192e-3 * S + 2.09324e-5 * S**2 - 1.28205e-7 * S**3)
|
||||||
sig = sig_25s * np.exp(-delta * beta)
|
sig = sig_25s * np.exp(-delta * beta)
|
||||||
|
|
||||||
@@ -434,9 +437,9 @@ def create_water(G, T=25, S=0):
|
|||||||
salinity.
|
salinity.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
T (float): Temperature of water (degrees centigrade)
|
T: float for temperature of water (degrees centigrade).
|
||||||
S (float): Salinity of water (part per thousand)
|
S: float for salinity of water (part per thousand).
|
||||||
G (FDTDGrid): Parameters describing a grid in a model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
eri, er, tau, sig = calculate_water_properties(T, S)
|
eri, er, tau, sig = calculate_water_properties(T, S)
|
||||||
@@ -462,7 +465,7 @@ def create_grass(G):
|
|||||||
"""Create single-pole Debye model for grass
|
"""Create single-pole Debye model for grass
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G (FDTDGrid): Parameters describing a grid in a model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Properties of grass from: http://dx.doi.org/10.1007/BF00902994
|
# Properties of grass from: http://dx.doi.org/10.1007/BF00902994
|
||||||
|
@@ -117,7 +117,8 @@ class ModelBuildRun:
|
|||||||
logger.info('')
|
logger.info('')
|
||||||
for i, go in enumerate(G.geometryobjectswrite):
|
for i, go in enumerate(G.geometryobjectswrite):
|
||||||
pbar = tqdm(total=go.datawritesize, unit='byte', unit_scale=True,
|
pbar = tqdm(total=go.datawritesize, unit='byte', unit_scale=True,
|
||||||
desc=f'Writing geometry object file {i + 1}/{len(G.geometryobjectswrite)}, {go.filename_hdf5.name}',
|
desc=f'Writing geometry object file {i + 1}/{len(G.geometryobjectswrite)}, ' +
|
||||||
|
f'{go.filename_hdf5.name}',
|
||||||
ncols=get_terminal_width() - 1, file=sys.stdout,
|
ncols=get_terminal_width() - 1, file=sys.stdout,
|
||||||
disable=not config.sim_config.general['progressbars'])
|
disable=not config.sim_config.general['progressbars'])
|
||||||
go.write_hdf5(G, pbar)
|
go.write_hdf5(G, pbar)
|
||||||
|
108
gprMax/mpi.py
108
gprMax/mpi.py
@@ -122,18 +122,16 @@ class MPIExecutor(object):
|
|||||||
|
|
||||||
def __init__(self, func, master=0, comm=None):
|
def __init__(self, func, master=0, comm=None):
|
||||||
"""Initializes a new executor instance.
|
"""Initializes a new executor instance.
|
||||||
Parameters
|
|
||||||
----------
|
Attributes:
|
||||||
func: callable
|
func: callable worker function. Jobs will be passed as keyword
|
||||||
The worker function. Jobs will be passed as keyword arguments,
|
arguments, so `func` must support this. This is usually the
|
||||||
so `func` must support this. This is usually the case, but
|
case, but can be a problem when builtin functions are used,
|
||||||
can be a problem when builtin functions are used, e.g. `abs()`.
|
e.g. `abs()`.
|
||||||
master: int
|
master: int of the rank of the master. Must be in `comm`. All other
|
||||||
The rank of the master. Must be in `comm`. All other
|
ranks in `comm` will be treated as workers.
|
||||||
ranks in `comm` will be treated as workers.
|
comm: MPI.Intracomm communicator used for communication between the
|
||||||
comm: MPI.Intracomm
|
master and workers.
|
||||||
The MPI communicator used for communication between the
|
|
||||||
master and workers.
|
|
||||||
"""
|
"""
|
||||||
if comm is None:
|
if comm is None:
|
||||||
self.comm = MPI.COMM_WORLD
|
self.comm = MPI.COMM_WORLD
|
||||||
@@ -170,9 +168,8 @@ class MPIExecutor(object):
|
|||||||
logger.basic(f'\n({self.comm.name}) - Master: {self.master}, Workers: {self.workers}')
|
logger.basic(f'\n({self.comm.name}) - Master: {self.master}, Workers: {self.workers}')
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
"""Context manager enter.
|
"""Context manager enter. Only the master returns an executor, all other
|
||||||
Only the master returns an executor,
|
ranks return None.
|
||||||
all other ranks return None.
|
|
||||||
"""
|
"""
|
||||||
self.start()
|
self.start()
|
||||||
if self.is_master():
|
if self.is_master():
|
||||||
@@ -180,8 +177,7 @@ class MPIExecutor(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
"""Context manager exit.
|
"""Context manager exit."""
|
||||||
"""
|
|
||||||
if exc_type is not None:
|
if exc_type is not None:
|
||||||
logger.exception(exc_val)
|
logger.exception(exc_val)
|
||||||
return False
|
return False
|
||||||
@@ -192,30 +188,26 @@ class MPIExecutor(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def is_idle(self):
|
def is_idle(self):
|
||||||
"""Returns a bool indicating whether the executor is idle.
|
"""Returns a bool indicating whether the executor is idle. The executor
|
||||||
The executor is considered to be not idle if *any* worker
|
is considered to be not idle if *any* worker process is busy with a
|
||||||
process is busy with a job. That means, it is idle only
|
job. That means, it is idle only if *all* workers are idle.
|
||||||
if *all* workers are idle.
|
Note: This member must not be called on a worker.
|
||||||
Note: This member must not be called on a worker.
|
|
||||||
"""
|
"""
|
||||||
assert self.is_master()
|
assert self.is_master()
|
||||||
return not any(self.busy)
|
return not any(self.busy)
|
||||||
|
|
||||||
def is_master(self):
|
def is_master(self):
|
||||||
"""Returns a bool indicating whether `self` is the master.
|
"""Returns a bool indicating whether `self` is the master."""
|
||||||
"""
|
|
||||||
return self.rank == self.master
|
return self.rank == self.master
|
||||||
|
|
||||||
def is_worker(self):
|
def is_worker(self):
|
||||||
"""Returns a bool indicating whether `self` is a worker.
|
"""Returns a bool indicating whether `self` is a worker."""
|
||||||
"""
|
|
||||||
return not self.is_master()
|
return not self.is_master()
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""Starts up workers.
|
"""Starts up workers. A check is performed on the master whether the
|
||||||
A check is performed on the master whether the executor
|
executor has already been terminated, in which case a RuntimeError
|
||||||
has already been terminated, in which case a RuntimeError
|
is raised on the master.
|
||||||
is raised on the master.
|
|
||||||
"""
|
"""
|
||||||
if self.is_master():
|
if self.is_master():
|
||||||
if self._up:
|
if self._up:
|
||||||
@@ -227,8 +219,7 @@ class MPIExecutor(object):
|
|||||||
self.__wait()
|
self.__wait()
|
||||||
|
|
||||||
def join(self):
|
def join(self):
|
||||||
"""Joins the workers.
|
"""Joins the workers."""
|
||||||
"""
|
|
||||||
if self.is_master():
|
if self.is_master():
|
||||||
|
|
||||||
logger.debug(f'({self.comm.name}) - Terminating. Sending sentinel to all workers.')
|
logger.debug(f'({self.comm.name}) - Terminating. Sending sentinel to all workers.')
|
||||||
@@ -252,21 +243,18 @@ class MPIExecutor(object):
|
|||||||
|
|
||||||
def submit(self, jobs, sleep=0.0):
|
def submit(self, jobs, sleep=0.0):
|
||||||
"""Submits a list of jobs to the workers and returns the results.
|
"""Submits a list of jobs to the workers and returns the results.
|
||||||
Parameters
|
|
||||||
----------
|
Args:
|
||||||
jobs: list
|
jobs: list of keyword argument dicts. Each dict describes a job and
|
||||||
A list of keyword argument dicts. Each dict describes
|
will be unpacked and supplied to the work function.
|
||||||
a job and will be unpacked and supplied to the work function.
|
sleep: float of number of seconds the master will sleep for when
|
||||||
sleep: float
|
trying to find an idle worker. The default value is 0.0,
|
||||||
The number of seconds the master will sleep for when trying
|
which means the master will not sleep at all.
|
||||||
to find an idle worker. The default value is 0.0, which means
|
|
||||||
the master will not sleep at all.
|
Returns:
|
||||||
Returns
|
results: list of results, i.e. the return values of the work
|
||||||
-------
|
function, received from the workers. The order of
|
||||||
results: list
|
results is identical to the order of `jobs`.
|
||||||
A list of results, i.e. the return values of the work function,
|
|
||||||
received from the workers. The order of results is identical to
|
|
||||||
the order of `jobs`.
|
|
||||||
"""
|
"""
|
||||||
if not self._up:
|
if not self._up:
|
||||||
raise RuntimeError('Cannot run jobs without a call to start()')
|
raise RuntimeError('Cannot run jobs without a call to start()')
|
||||||
@@ -303,11 +291,10 @@ class MPIExecutor(object):
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
def __wait(self):
|
def __wait(self):
|
||||||
"""The worker main loop.
|
"""The worker main loop. The worker will enter the loop after `start()`
|
||||||
The worker will enter the loop after `start()` has been called
|
has been called and stay here until it receives the sentinel,
|
||||||
and stay here until it receives the sentinel, e.g. by calling
|
e.g. by calling `join()` on the master. In the mean time, the worker
|
||||||
`join()` on the master. In the mean time, the worker is
|
is accepting work.
|
||||||
accepting work.
|
|
||||||
"""
|
"""
|
||||||
assert self.is_worker()
|
assert self.is_worker()
|
||||||
|
|
||||||
@@ -337,16 +324,13 @@ class MPIExecutor(object):
|
|||||||
|
|
||||||
def __guarded_work(self, work):
|
def __guarded_work(self, work):
|
||||||
"""Executes work safely on the workers.
|
"""Executes work safely on the workers.
|
||||||
Parameters
|
N.B. All exceptions that occur in the work function `func` are caught
|
||||||
----------
|
and logged. The worker returns `None` to the master in that case
|
||||||
work: dict
|
instead of the actual result.
|
||||||
Keyword arguments that are unpacked and given to the
|
|
||||||
work function.
|
Args:
|
||||||
Notes
|
work: dict ofeyword arguments that are unpacked and given to the
|
||||||
-----
|
work function.
|
||||||
All exceptions that occur in the work function `func` are caught
|
|
||||||
and logged. The worker returns `None` to the master in that case
|
|
||||||
instead of the actual result.
|
|
||||||
"""
|
"""
|
||||||
assert self.is_worker()
|
assert self.is_worker()
|
||||||
try:
|
try:
|
||||||
|
@@ -77,8 +77,7 @@ class CFS:
|
|||||||
d: float for dx, dy, or dz in direction of PML.
|
d: float for dx, dy, or dz in direction of PML.
|
||||||
er: float for average permittivity of underlying material.
|
er: float for average permittivity of underlying material.
|
||||||
mr: float for average permeability of underlying material.
|
mr: float for average permeability of underlying material.
|
||||||
G: FDTDGrid object that holds essential parameters describing the
|
G: FDTDGrid class describing a grid in a model.
|
||||||
model.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Calculation of the maximum value of sigma from http://dx.doi.org/10.1109/8.546249
|
# Calculation of the maximum value of sigma from http://dx.doi.org/10.1109/8.546249
|
||||||
@@ -184,8 +183,7 @@ class PML:
|
|||||||
def __init__(self, G, ID=None, direction=None, xs=0, xf=0, ys=0, yf=0, zs=0, zf=0):
|
def __init__(self, G, ID=None, direction=None, xs=0, xf=0, ys=0, yf=0, zs=0, zf=0):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
G: FDTDGrid object that holds essential parameters describing the
|
G: FDTDGrid class describing a grid in a model.
|
||||||
model.
|
|
||||||
ID: string identifier for PML slab.
|
ID: string identifier for PML slab.
|
||||||
direction: string for direction of increasing absorption.
|
direction: string for direction of increasing absorption.
|
||||||
xs, xf, ys, yf, zs, zf: floats of extent of the PML slab.
|
xs, xf, ys, yf, zs, zf: floats of extent of the PML slab.
|
||||||
@@ -577,7 +575,7 @@ def print_pml_info(G):
|
|||||||
"""Information about PMLs.
|
"""Information about PMLs.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G: FDTDGrid objects that holds parameters describing a grid in a model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
"""
|
"""
|
||||||
# No PML
|
# No PML
|
||||||
if all(value == 0 for value in G.pmlthickness.values()):
|
if all(value == 0 for value in G.pmlthickness.values()):
|
||||||
@@ -601,7 +599,7 @@ def build_pml(G, key, value):
|
|||||||
(based on underlying material er and mr from solid array).
|
(based on underlying material er and mr from solid array).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G: FDTDGrid objects that holds parameters describing a grid in a model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
key: string dentifier of PML slab.
|
key: string dentifier of PML slab.
|
||||||
value: int with thickness of PML slab in cells.
|
value: int with thickness of PML slab in cells.
|
||||||
"""
|
"""
|
||||||
|
@@ -46,7 +46,7 @@ def htod_rx_arrays(G, queue=None):
|
|||||||
components for receivers.
|
components for receivers.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G: FDTDGrid object that holds essential parameters describing the model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
queue: pyopencl queue.
|
queue: pyopencl queue.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -92,7 +92,7 @@ def dtoh_rx_array(rxs_dev, rxcoords_dev, G):
|
|||||||
rxcoords_dev: int array of receiver coordinates on compute device.
|
rxcoords_dev: int array of receiver coordinates on compute device.
|
||||||
rxs_dev: float array of receiver data on compute device - rows are field
|
rxs_dev: float array of receiver data on compute device - rows are field
|
||||||
components; columns are iterations; pages are receivers.
|
components; columns are iterations; pages are receivers.
|
||||||
G: FDTDGrid object that holds essential parameters describing the model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@@ -42,8 +42,9 @@ class Scene:
|
|||||||
def add(self, user_object):
|
def add(self, user_object):
|
||||||
"""Add the user object to the scene.
|
"""Add the user object to the scene.
|
||||||
|
|
||||||
:param user_object: User object to add to the scene. For example, :class:`gprMax.cmds_single_use.Domain`
|
Args:
|
||||||
:type user_object: UserObjectMulti/UserObjectGeometry/UserObjectSingle
|
user_object: user object to add to the scene. For example,
|
||||||
|
:class:`gprMax.cmds_single_use.Domain`
|
||||||
"""
|
"""
|
||||||
if isinstance(user_object, UserObjectMulti):
|
if isinstance(user_object, UserObjectMulti):
|
||||||
self.multiple_cmds.append(user_object)
|
self.multiple_cmds.append(user_object)
|
||||||
@@ -55,7 +56,7 @@ class Scene:
|
|||||||
logger.exception('This object is unknown to gprMax')
|
logger.exception('This object is unknown to gprMax')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
def process_subgrid_commands(self, subgrids):
|
def process_subgrid_commands(self):
|
||||||
# Check for subgrid user objects
|
# Check for subgrid user objects
|
||||||
def func(obj):
|
def func(obj):
|
||||||
if isinstance(obj, SubGridUserBase):
|
if isinstance(obj, SubGridUserBase):
|
||||||
@@ -106,7 +107,9 @@ class Scene:
|
|||||||
for cmd_type in self.essential_cmds:
|
for cmd_type in self.essential_cmds:
|
||||||
d = any([isinstance(cmd, cmd_type) for cmd in cmds_unique])
|
d = any([isinstance(cmd, cmd_type) for cmd in cmds_unique])
|
||||||
if not d:
|
if not d:
|
||||||
logger.exception('Your input file is missing essential commands required to run a model. Essential commands are: Domain, Discretisation, Time Window')
|
logger.exception('Your input file is missing essential commands ' +
|
||||||
|
'required to run a model. Essential commands ' +
|
||||||
|
'are: Domain, Discretisation, Time Window')
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
self.process_cmds(cmds_unique, G)
|
self.process_cmds(cmds_unique, G)
|
||||||
@@ -139,6 +142,6 @@ class Scene:
|
|||||||
self.process_cmds(self.geometry_cmds, G, sort=False)
|
self.process_cmds(self.geometry_cmds, G, sort=False)
|
||||||
|
|
||||||
# Process all the commands for the subgrid
|
# Process all the commands for the subgrid
|
||||||
self.process_subgrid_commands(G.subgrids)
|
self.process_subgrid_commands()
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
@@ -49,11 +49,11 @@ class Snapshot:
|
|||||||
dx=None, dy=None, dz=None, time=None, filename=None, fileext=None):
|
dx=None, dy=None, dz=None, time=None, filename=None, fileext=None):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
xs, xf, ys, yf, zs, zf (int): Extent of the volume in cells.
|
xs, xf, ys, yf, zs, zf: ints for the extent of the volume in cells.
|
||||||
dx, dy, dz (int): Spatial discretisation in cells.
|
dx, dy, dz: ints for the spatial discretisation in cells.
|
||||||
time (int): Iteration number to take the snapshot on.
|
time: int for the iteration number to take the snapshot on.
|
||||||
filename (str): Filename to save to.
|
filename: string for the filename to save to.
|
||||||
fileext (str): File extension.
|
fileext: string for the file extension.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.fileext = fileext
|
self.fileext = fileext
|
||||||
@@ -90,7 +90,7 @@ class Snapshot:
|
|||||||
"""Store (in memory) electric and magnetic field values for snapshot.
|
"""Store (in memory) electric and magnetic field values for snapshot.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G (FDTDGrid): Parameters describing a grid in a model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Memory views of field arrays to dimensions required for the snapshot
|
# Memory views of field arrays to dimensions required for the snapshot
|
||||||
@@ -132,8 +132,8 @@ class Snapshot:
|
|||||||
or HDF5 format (.h5) files
|
or HDF5 format (.h5) files
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
pbar (class): Progress bar class instance.
|
pbar: Progress bar class instance.
|
||||||
G (FDTDGrid): Parameters describing a grid in a model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.fileext == '.vti':
|
if self.fileext == '.vti':
|
||||||
@@ -147,8 +147,8 @@ class Snapshot:
|
|||||||
N.B. No Python 3 support for VTK at time of writing (03/2015)
|
N.B. No Python 3 support for VTK at time of writing (03/2015)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
pbar (class): Progress bar class instance.
|
pbar: Progress bar class instance.
|
||||||
G (FDTDGrid): Parameters describing a grid in a model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
hfield_offset = (3 * np.dtype(config.sim_config.dtypes['float_or_double']).itemsize
|
hfield_offset = (3 * np.dtype(config.sim_config.dtypes['float_or_double']).itemsize
|
||||||
@@ -197,8 +197,8 @@ class Snapshot:
|
|||||||
"""Write snapshot file in HDF5 (.h5) format.
|
"""Write snapshot file in HDF5 (.h5) format.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
pbar (class): Progress bar class instance.
|
pbar: Progress bar class instance.
|
||||||
G (FDTDGrid): Parameters describing a grid in a model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
f = h5py.File(self.filename, 'w')
|
f = h5py.File(self.filename, 'w')
|
||||||
@@ -227,7 +227,7 @@ def htod_snapshot_array(G, queue=None):
|
|||||||
"""Initialise array on compute device for to store field data for snapshots.
|
"""Initialise array on compute device for to store field data for snapshots.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G: FDTDGrid object with parameters describing a grid in a model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
queue: pyopencl queue.
|
queue: pyopencl queue.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@@ -27,7 +27,7 @@ def create_G():
|
|||||||
"""Create grid object according to solver.
|
"""Create grid object according to solver.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
G: FDTDGrid that holds essential parameters describing the model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if config.sim_config.general['solver'] == 'cpu':
|
if config.sim_config.general['solver'] == 'cpu':
|
||||||
@@ -44,7 +44,7 @@ def create_solver(G):
|
|||||||
"""Create configured solver object.
|
"""Create configured solver object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G: FDTDGrid that holds essential parameters describing the model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
solver: Solver object.
|
solver: Solver object.
|
||||||
@@ -80,7 +80,7 @@ class Solver:
|
|||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
updates: Updates contains methods to run FDTD algorithm.
|
updates: Updates contains methods to run FDTD algorithm.
|
||||||
hsg: bool to use sub-gridding.
|
hsg: boolean to use sub-gridding.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.updates = updates
|
self.updates = updates
|
||||||
|
@@ -45,8 +45,7 @@ class Source:
|
|||||||
"""Calculates all waveform values for source for duration of simulation.
|
"""Calculates all waveform values for source for duration of simulation.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G: FDTDGrid object that olds essential parameters describing the
|
G: FDTDGrid class describing a grid in a model.
|
||||||
model.
|
|
||||||
"""
|
"""
|
||||||
# Waveform values for electric sources - calculated half a timestep later
|
# Waveform values for electric sources - calculated half a timestep later
|
||||||
self.waveformvaluesJ = np.zeros((G.iterations),
|
self.waveformvaluesJ = np.zeros((G.iterations),
|
||||||
@@ -89,8 +88,7 @@ class VoltageSource(Source):
|
|||||||
ID: memory view of array of numeric IDs corresponding to materials
|
ID: memory view of array of numeric IDs corresponding to materials
|
||||||
in the model.
|
in the model.
|
||||||
Ex, Ey, Ez: memory view of array of electric field values.
|
Ex, Ey, Ez: memory view of array of electric field values.
|
||||||
G: FDTDGrid object that olds essential parameters describing the
|
G: FDTDGrid class describing a grid in a model.
|
||||||
model.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
|
if iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
|
||||||
@@ -128,8 +126,7 @@ class VoltageSource(Source):
|
|||||||
voltage source conductivity to the underlying parameters.
|
voltage source conductivity to the underlying parameters.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G: FDTDGrid object that olds essential parameters describing the
|
G: FDTDGrid class describing a grid in a model.
|
||||||
model.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.resistance != 0:
|
if self.resistance != 0:
|
||||||
@@ -175,8 +172,7 @@ class HertzianDipole(Source):
|
|||||||
ID: memory view of array of numeric IDs corresponding to materials
|
ID: memory view of array of numeric IDs corresponding to materials
|
||||||
in the model.
|
in the model.
|
||||||
Ex, Ey, Ez: memory view of array of electric field values.
|
Ex, Ey, Ez: memory view of array of electric field values.
|
||||||
G: FDTDGrid object that olds essential parameters describing the
|
G: FDTDGrid class describing a grid in a model.
|
||||||
model.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
|
if iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
|
||||||
@@ -213,8 +209,7 @@ class MagneticDipole(Source):
|
|||||||
ID: memory view of array of numeric IDs corresponding to materials
|
ID: memory view of array of numeric IDs corresponding to materials
|
||||||
in the model.
|
in the model.
|
||||||
Hx, Hy, Hz: memory view of array of magnetic field values.
|
Hx, Hy, Hz: memory view of array of magnetic field values.
|
||||||
G: FDTDGrid object that olds essential parameters describing the
|
G: FDTDGrid class describing a grid in a model.
|
||||||
model.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
|
if iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
|
||||||
@@ -245,7 +240,7 @@ def htod_src_arrays(sources, G, queue=None):
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
sources: list of sources of one type, e.g. HertzianDipole
|
sources: list of sources of one type, e.g. HertzianDipole
|
||||||
G: FDTDGrid object that holds essential parameters describing the model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
queue: pyopencl queue.
|
queue: pyopencl queue.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -303,8 +298,7 @@ class TransmissionLine(Source):
|
|||||||
def __init__(self, G):
|
def __init__(self, G):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
G: FDTDGrid object that holds essential parameters describing the
|
G: FDTDGrid class describing a grid in a model.
|
||||||
model.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -341,8 +335,7 @@ class TransmissionLine(Source):
|
|||||||
from: http://dx.doi.org/10.1002/mop.10415
|
from: http://dx.doi.org/10.1002/mop.10415
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G: FDTDGrid object that holds essential parameters describing the
|
G: FDTDGrid class describing a grid in a model.
|
||||||
model.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for iteration in range(G.iterations):
|
for iteration in range(G.iterations):
|
||||||
@@ -358,8 +351,7 @@ class TransmissionLine(Source):
|
|||||||
"""Updates absorbing boundary condition at end of the transmission line.
|
"""Updates absorbing boundary condition at end of the transmission line.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G: FDTDGrid object that holds essential parameters describing the
|
G: FDTDGrid class describing a grid in a model.
|
||||||
model.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
h = (config.c * G.dt - self.dl) / (config.c * G.dt + self.dl)
|
h = (config.c * G.dt - self.dl) / (config.c * G.dt + self.dl)
|
||||||
@@ -373,8 +365,7 @@ class TransmissionLine(Source):
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
iteration: int of current iteration (timestep).
|
iteration: int of current iteration (timestep).
|
||||||
G: FDTDGrid object that holds essential parameters describing the
|
G: FDTDGrid class describing a grid in a model.
|
||||||
model.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Update all the voltage values along the line
|
# Update all the voltage values along the line
|
||||||
@@ -392,8 +383,7 @@ class TransmissionLine(Source):
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
iteration: int of current iteration (timestep).
|
iteration: int of current iteration (timestep).
|
||||||
G: FDTDGrid object that holds essential parameters describing the
|
G: FDTDGrid class describing a grid in a model.
|
||||||
model.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Update all the current values along the line
|
# Update all the current values along the line
|
||||||
@@ -416,8 +406,7 @@ class TransmissionLine(Source):
|
|||||||
ID: memory view of array of numeric IDs corresponding to materials
|
ID: memory view of array of numeric IDs corresponding to materials
|
||||||
in the model.
|
in the model.
|
||||||
Ex, Ey, Ez: memory view of array of electric field values.
|
Ex, Ey, Ez: memory view of array of electric field values.
|
||||||
G: FDTDGrid object that olds essential parameters describing the
|
G: FDTDGrid class describing a grid in a model.
|
||||||
model.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
|
if iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
|
||||||
@@ -447,8 +436,7 @@ class TransmissionLine(Source):
|
|||||||
ID: memory view of array of numeric IDs corresponding to materials
|
ID: memory view of array of numeric IDs corresponding to materials
|
||||||
in the model.
|
in the model.
|
||||||
Hx, Hy, Hz: memory view of array of magnetic field values.
|
Hx, Hy, Hz: memory view of array of magnetic field values.
|
||||||
G: FDTDGrid object that olds essential parameters describing the
|
G: FDTDGrid class describing a grid in a model.
|
||||||
model.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
|
if iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
|
||||||
|
@@ -45,7 +45,7 @@ class CPUUpdates:
|
|||||||
def __init__(self, G):
|
def __init__(self, G):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
G (FDTDGrid): Parameters describing a grid in a model.
|
G: FDTDGrid class describing a grid in a model.
|
||||||
"""
|
"""
|
||||||
self.grid = G
|
self.grid = G
|
||||||
self.dispersive_update_a = None
|
self.dispersive_update_a = None
|
||||||
@@ -201,7 +201,7 @@ class CPUUpdates:
|
|||||||
"""Set dispersive update functions.
|
"""Set dispersive update functions.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
props (Props): Dispersive material properties.
|
props: dispersive material properties.
|
||||||
"""
|
"""
|
||||||
update_f = 'update_electric_dispersive_{}pole_{}_{}_{}'
|
update_f = 'update_electric_dispersive_{}pole_{}_{}_{}'
|
||||||
disp_a = update_f.format(props.poles, 'A', props.precision, props.dispersion_type)
|
disp_a = update_f.format(props.poles, 'A', props.precision, props.dispersion_type)
|
||||||
@@ -234,7 +234,7 @@ class CUDAUpdates:
|
|||||||
def __init__(self, G):
|
def __init__(self, G):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
G: CUDAGrid of parameters describing a grid in a model.
|
G: CUDAGrid class describing a grid in a model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.grid = G
|
self.grid = G
|
||||||
@@ -754,7 +754,7 @@ class OpenCLUpdates:
|
|||||||
def __init__(self, G):
|
def __init__(self, G):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
G: OpenCLGrid of parameters describing a grid in a model.
|
G: OpenCLGrid class describing a grid in a model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.grid = G
|
self.grid = G
|
||||||
|
@@ -37,7 +37,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def create_user_input_points(grid, user_obj):
|
def create_user_input_points(grid, user_obj):
|
||||||
"""Return a point checker class based on the grid supplied."""
|
"""Returns a point checker class based on the grid supplied."""
|
||||||
|
|
||||||
if isinstance(grid, SubGridBase):
|
if isinstance(grid, SubGridBase):
|
||||||
# Local object configuration trumps. User can turn of autotranslate for
|
# Local object configuration trumps. User can turn of autotranslate for
|
||||||
@@ -54,7 +54,7 @@ def create_user_input_points(grid, user_obj):
|
|||||||
|
|
||||||
|
|
||||||
class UserInput:
|
class UserInput:
|
||||||
"""Base class to handle (x, y, z) points supplied by the user."""
|
"""Handles (x, y, z) points supplied by the user."""
|
||||||
|
|
||||||
def __init__(self, grid):
|
def __init__(self, grid):
|
||||||
self.grid = grid
|
self.grid = grid
|
||||||
@@ -69,34 +69,38 @@ class UserInput:
|
|||||||
# Incorrect index
|
# Incorrect index
|
||||||
i = p[v.index(err.args[0])]
|
i = p[v.index(err.args[0])]
|
||||||
if name:
|
if name:
|
||||||
s = f"\n'{cmd_str}' {err.args[0]} {name}-coordinate {i * dl:g} is not within the model domain"
|
s = (f"\n'{cmd_str}' {err.args[0]} {name}-coordinate {i * dl:g} " +
|
||||||
|
"is not within the model domain")
|
||||||
else:
|
else:
|
||||||
s = f"\n'{cmd_str}' {err.args[0]}-coordinate {i * dl:g} is not within the model domain"
|
s = (f"\n'{cmd_str}' {err.args[0]}-coordinate {i * dl:g} is not " +
|
||||||
|
"within the model domain")
|
||||||
logger.exception(s)
|
logger.exception(s)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def discretise_point(self, p):
|
def discretise_point(self, p):
|
||||||
"""Function to get the index of a continuous point with the grid."""
|
"""Gets the index of a continuous point with the grid."""
|
||||||
rv = np.vectorize(round_value)
|
rv = np.vectorize(round_value)
|
||||||
return rv(p / self.grid.dl)
|
return rv(p / self.grid.dl)
|
||||||
|
|
||||||
def round_to_grid(self, p):
|
def round_to_grid(self, p):
|
||||||
"""Function to get the nearest continuous point on the grid from a continuous point in space."""
|
"""Gets the nearest continuous point on the grid from a continuous point
|
||||||
|
in space.
|
||||||
|
"""
|
||||||
return self.discretise_point(p) * self.grid.dl
|
return self.discretise_point(p) * self.grid.dl
|
||||||
|
|
||||||
def descretised_to_continuous(self, p):
|
def descretised_to_continuous(self, p):
|
||||||
"""Function to return a point given as indices to a continous point in the real space."""
|
"""Returns a point given as indices to a continous point in the real space."""
|
||||||
return p * self.grid.dl
|
return p * self.grid.dl
|
||||||
|
|
||||||
|
|
||||||
class MainGridUserInput(UserInput):
|
class MainGridUserInput(UserInput):
|
||||||
"""Class to handle (x, y, z) points supplied by the user in the main grid."""
|
"""Handles (x, y, z) points supplied by the user in the main grid."""
|
||||||
|
|
||||||
def __init__(self, grid):
|
def __init__(self, grid):
|
||||||
super().__init__(grid)
|
super().__init__(grid)
|
||||||
|
|
||||||
def check_point(self, p, cmd_str, name=''):
|
def check_point(self, p, cmd_str, name=''):
|
||||||
"""Discretise point and check its within the domain"""
|
"""Discretises point and check its within the domain"""
|
||||||
p = self.discretise_point(p)
|
p = self.discretise_point(p)
|
||||||
self.point_within_bounds(p, cmd_str, name)
|
self.point_within_bounds(p, cmd_str, name)
|
||||||
return p
|
return p
|
||||||
@@ -105,7 +109,8 @@ class MainGridUserInput(UserInput):
|
|||||||
p = self.check_point(p, cmd_str, name)
|
p = self.check_point(p, cmd_str, name)
|
||||||
|
|
||||||
if self.grid.within_pml(p):
|
if self.grid.within_pml(p):
|
||||||
logger.warning(f"'{cmd_str}' sources and receivers should not normally be positioned within the PML.")
|
logger.warning(f"'{cmd_str}' sources and receivers should not " +
|
||||||
|
"normally be positioned within the PML.")
|
||||||
|
|
||||||
return p
|
return p
|
||||||
|
|
||||||
@@ -114,7 +119,8 @@ class MainGridUserInput(UserInput):
|
|||||||
p2 = self.check_point(p2, cmd_str, name='upper')
|
p2 = self.check_point(p2, cmd_str, name='upper')
|
||||||
|
|
||||||
if np.greater(p1, p2).any():
|
if np.greater(p1, p2).any():
|
||||||
logger.exception(f"'{cmd_str}' the lower coordinates should be less than the upper coordinates.")
|
logger.exception(f"'{cmd_str}' the lower coordinates should be less " +
|
||||||
|
"than the upper coordinates.")
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
return p1, p2
|
return p1, p2
|
||||||
@@ -127,16 +133,20 @@ class MainGridUserInput(UserInput):
|
|||||||
return p1, p2, p3
|
return p1, p2, p3
|
||||||
|
|
||||||
def discretise_static_point(self, p):
|
def discretise_static_point(self, p):
|
||||||
"""Function to get the index of a continuous point regardless of the point of origin of the grid."""
|
"""Gets the index of a continuous point regardless of the point of
|
||||||
|
origin of the grid.
|
||||||
|
"""
|
||||||
return super().discretise_point(p)
|
return super().discretise_point(p)
|
||||||
|
|
||||||
def round_to_grid_static_point(self, p):
|
def round_to_grid_static_point(self, p):
|
||||||
"""Function to get the index of a continuous point regardless of the point of origin of the grid."""
|
"""Gets the index of a continuous point regardless of the point of
|
||||||
|
origin of the grid.
|
||||||
|
"""
|
||||||
return super().discretise_point(p) * self.grid.dl
|
return super().discretise_point(p) * self.grid.dl
|
||||||
|
|
||||||
|
|
||||||
class SubgridUserInput(MainGridUserInput):
|
class SubgridUserInput(MainGridUserInput):
|
||||||
"""Class to handle (x, y, z) points supplied by the user in the sub grid.
|
"""Handles (x, y, z) points supplied by the user in the sub grid.
|
||||||
This class autotranslates points from main grid to subgrid equivalent
|
This class autotranslates points from main grid to subgrid equivalent
|
||||||
(within IS). Useful if material traverse is not required.
|
(within IS). Useful if material traverse is not required.
|
||||||
"""
|
"""
|
||||||
@@ -153,9 +163,7 @@ class SubgridUserInput(MainGridUserInput):
|
|||||||
self.inner_bound)
|
self.inner_bound)
|
||||||
|
|
||||||
def translate_to_gap(self, p):
|
def translate_to_gap(self, p):
|
||||||
"""Function to translate the user input point to the real point in the
|
"""Translates the user input point to the real point in the subgrid."""
|
||||||
subgrid.
|
|
||||||
"""
|
|
||||||
|
|
||||||
p1 = (p[0] - self.grid.i0 * self.grid.ratio) + self.grid.n_boundary_cells_x
|
p1 = (p[0] - self.grid.i0 * self.grid.ratio) + self.grid.n_boundary_cells_x
|
||||||
p2 = (p[1] - self.grid.j0 * self.grid.ratio) + self.grid.n_boundary_cells_y
|
p2 = (p[1] - self.grid.j0 * self.grid.ratio) + self.grid.n_boundary_cells_y
|
||||||
@@ -164,9 +172,9 @@ class SubgridUserInput(MainGridUserInput):
|
|||||||
return np.array([p1, p2, p3])
|
return np.array([p1, p2, p3])
|
||||||
|
|
||||||
def discretise_point(self, p):
|
def discretise_point(self, p):
|
||||||
"""Function to discretise a point. Does not provide any checks. The
|
"""Discretises a point. Does not provide any checks. The user enters
|
||||||
user enters coordinates relative to self.inner_bound. This function
|
coordinates relative to self.inner_bound. This function translate
|
||||||
translate the user point to the correct index for building objects.
|
the user point to the correct index for building objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
p = super().discretise_point(p)
|
p = super().discretise_point(p)
|
||||||
@@ -185,15 +193,18 @@ class SubgridUserInput(MainGridUserInput):
|
|||||||
# the OS non-working region.
|
# the OS non-working region.
|
||||||
if (np.less(p_t, self.inner_bound).any() or
|
if (np.less(p_t, self.inner_bound).any() or
|
||||||
np.greater(p_t, self.outer_bound).any()):
|
np.greater(p_t, self.outer_bound).any()):
|
||||||
logger.warning(f"'{cmd_str}' this object traverses the Outer Surface. This is an advanced feature.")
|
logger.warning(f"'{cmd_str}' this object traverses the Outer " +
|
||||||
|
"Surface. This is an advanced feature.")
|
||||||
return p_t
|
return p_t
|
||||||
|
|
||||||
def discretise_static_point(self, p):
|
def discretise_static_point(self, p):
|
||||||
"""Function to get the index of a continuous point regardless of the point of origin of the grid."""
|
"""Gets the index of a continuous point regardless of the point of
|
||||||
|
origin of the grid."""
|
||||||
return super().discretise_point(p)
|
return super().discretise_point(p)
|
||||||
|
|
||||||
def round_to_grid_static_point(self, p):
|
def round_to_grid_static_point(self, p):
|
||||||
"""Function to get the index of a continuous point regardless of the point of origin of the grid."""
|
"""Gets the index of a continuous point regardless of the point of
|
||||||
|
origin of the grid."""
|
||||||
return super().discretise_point(p) * self.grid.dl
|
return super().discretise_point(p) * self.grid.dl
|
||||||
|
|
||||||
|
|
||||||
|
@@ -54,10 +54,13 @@ class Waveform:
|
|||||||
waveforms.
|
waveforms.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.type == 'gaussian' or self.type == 'gaussiandot' or self.type == 'gaussiandotnorm' or self.type == 'gaussianprime' or self.type == 'gaussiandoubleprime':
|
if (self.type == 'gaussian' or self.type == 'gaussiandot' or
|
||||||
|
self.type == 'gaussiandotnorm' or self.type == 'gaussianprime' or
|
||||||
|
self.type == 'gaussiandoubleprime'):
|
||||||
self.chi = 1 / self.freq
|
self.chi = 1 / self.freq
|
||||||
self.zeta = 2 * np.pi**2 * self.freq**2
|
self.zeta = 2 * np.pi**2 * self.freq**2
|
||||||
elif self.type == 'gaussiandotdot' or self.type == 'gaussiandotdotnorm' or self.type == 'ricker':
|
elif (self.type == 'gaussiandotdot' or
|
||||||
|
self.type == 'gaussiandotdotnorm' or self.type == 'ricker':)
|
||||||
self.chi = np.sqrt(2) / self.freq
|
self.chi = np.sqrt(2) / self.freq
|
||||||
self.zeta = np.pi**2 * self.freq**2
|
self.zeta = np.pi**2 * self.freq**2
|
||||||
|
|
||||||
@@ -65,11 +68,11 @@ class Waveform:
|
|||||||
"""Calculates value of the waveform at a specific time.
|
"""Calculates value of the waveform at a specific time.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
time (float): Absolute time.
|
time: float for absolute time.
|
||||||
dt (float): Absolute time discretisation.
|
dt: float for absolute time discretisation.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
ampvalue (float): Calculated value for waveform.
|
ampvalue: float for calculated value for waveform.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.calculate_coefficients()
|
self.calculate_coefficients()
|
||||||
@@ -130,7 +133,3 @@ class Waveform:
|
|||||||
ampvalue *= self.amp
|
ampvalue *= self.amp
|
||||||
|
|
||||||
return ampvalue
|
return ampvalue
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
logger.debug('Do we need this?')
|
|
||||||
return f'Waveform: ID={self.ID}, type={self.type}, amp{self.amp}, freq={self.freq}'
|
|
||||||
|
@@ -23,16 +23,18 @@ from gprMax.waveforms import Waveform
|
|||||||
|
|
||||||
|
|
||||||
def hertzian_dipole_fs(iterations, dt, dxdydz, rx):
|
def hertzian_dipole_fs(iterations, dt, dxdydz, rx):
|
||||||
"""Analytical solution of a z-directed Hertzian dipole in free space with a Gaussian current waveform (http://dx.doi.org/10.1016/0021-9991(83)90103-1).
|
"""Analytical solution of a z-directed Hertzian dipole in free space with a
|
||||||
|
Gaussian current waveform (http://dx.doi.org/10.1016/0021-9991(83)90103-1).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
iterations (int): Number of time steps.
|
iterations: int for number of time steps.
|
||||||
dt (float): Time step (seconds).
|
dt: float for time step (seconds).
|
||||||
dxdydz (float): Tuple of spatial resolution (metres).
|
dxdydz: tuple of floats for spatial resolution (metres).
|
||||||
rx (float): Tuple of coordinates of receiver position relative to transmitter position (metres).
|
rx: tuple of floats for coordinates of receiver position relative to
|
||||||
|
transmitter position (metres).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
fields (float): Array contain electric and magnetic field components.
|
fields: float array containing electric and magnetic field components.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Waveform
|
# Waveform
|
||||||
@@ -111,7 +113,8 @@ def hertzian_dipole_fs(iterations, dt, dxdydz, rx):
|
|||||||
# Calculate fields
|
# Calculate fields
|
||||||
for timestep in range(iterations):
|
for timestep in range(iterations):
|
||||||
|
|
||||||
# Calculate values for waveform, I * dl (current multiplied by dipole length) to match gprMax behaviour
|
# Calculate values for waveform, I * dl (current multiplied by dipole
|
||||||
|
# length) to match gprMax behaviour
|
||||||
fint_Ex = wint.calculate_value((timestep * dt) - tau_Ex, dt) * dl
|
fint_Ex = wint.calculate_value((timestep * dt) - tau_Ex, dt) * dl
|
||||||
f_Ex = w.calculate_value((timestep * dt) - tau_Ex, dt) * dl
|
f_Ex = w.calculate_value((timestep * dt) - tau_Ex, dt) * dl
|
||||||
fdot_Ex = wdot.calculate_value((timestep * dt) - tau_Ex, dt) * dl
|
fdot_Ex = wdot.calculate_value((timestep * dt) - tau_Ex, dt) * dl
|
||||||
@@ -131,17 +134,21 @@ def hertzian_dipole_fs(iterations, dt, dxdydz, rx):
|
|||||||
fdot_Hy = wdot.calculate_value((timestep * dt) - tau_Hy, dt) * dl
|
fdot_Hy = wdot.calculate_value((timestep * dt) - tau_Hy, dt) * dl
|
||||||
|
|
||||||
# Ex
|
# Ex
|
||||||
fields[timestep, 0] = ((Ex_x * Ex_z) / (4 * np.pi * config.sim_config.em_consts['e0'] * Er_x**5)) * (3 * (fint_Ex + (tau_Ex * f_Ex)) + (tau_Ex**2 * fdot_Ex))
|
fields[timestep, 0] = (((Ex_x * Ex_z) / (4 * np.pi * config.sim_config.em_consts['e0'] * Er_x**5)) *
|
||||||
|
(3 * (fint_Ex + (tau_Ex * f_Ex)) + (tau_Ex**2 * fdot_Ex)))
|
||||||
|
|
||||||
# Ey
|
# Ey
|
||||||
try:
|
try:
|
||||||
tmp = Ey_y / Ey_x
|
tmp = Ey_y / Ey_x
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
tmp = 0
|
tmp = 0
|
||||||
fields[timestep, 1] = tmp * ((Ey_x * Ey_z) / (4 * np.pi * config.sim_config.em_consts['e0'] * Er_y**5)) * (3 * (fint_Ey + (tau_Ey * f_Ey)) + (tau_Ey**2 * fdot_Ey))
|
fields[timestep, 1] = (tmp * ((Ey_x * Ey_z) / (4 * np.pi * config.sim_config.em_consts['e0'] * Er_y**5)) *
|
||||||
|
(3 * (fint_Ey + (tau_Ey * f_Ey)) + (tau_Ey**2 * fdot_Ey)))
|
||||||
|
|
||||||
# Ez
|
# Ez
|
||||||
fields[timestep, 2] = (1 / (4 * np.pi * config.sim_config.em_consts['e0'] * Er_z**5)) * ((2 * Ez_z**2 - (Ez_x**2 + Ez_y**2)) * (fint_Ez + (tau_Ez * f_Ez)) - (Ez_x**2 + Ez_y**2) * tau_Ez**2 * fdot_Ez)
|
fields[timestep, 2] = ((1 / (4 * np.pi * config.sim_config.em_consts['e0'] * Er_z**5)) *
|
||||||
|
((2 * Ez_z**2 - (Ez_x**2 + Ez_y**2)) * (fint_Ez + (tau_Ez * f_Ez)) -
|
||||||
|
(Ez_x**2 + Ez_y**2) * tau_Ez**2 * fdot_Ez))
|
||||||
|
|
||||||
# Hx
|
# Hx
|
||||||
fields[timestep, 3] = - (Hx_y / (4 * np.pi * Hr_x**3)) * (f_Hx + (tau_Hx * fdot_Hx))
|
fields[timestep, 3] = - (Hx_y / (4 * np.pi * Hr_x**3)) * (f_Hx + (tau_Hx * fdot_Hx))
|
||||||
|
@@ -1,211 +0,0 @@
|
|||||||
# Copyright (C) 2015-2022: The University of Edinburgh, United Kingdom
|
|
||||||
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
|
|
||||||
#
|
|
||||||
# This file is part of gprMax.
|
|
||||||
#
|
|
||||||
# gprMax 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.
|
|
||||||
#
|
|
||||||
# gprMax 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 gprMax. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import itertools
|
|
||||||
import os
|
|
||||||
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
import matplotlib.gridspec as gridspec
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
from gprMax._version import __version__
|
|
||||||
from gprMax.utilities.host_info import get_host_info
|
|
||||||
from gprMax.utilities.utilities import human_size
|
|
||||||
|
|
||||||
|
|
||||||
"""Plots execution times and speedup factors from benchmarking models run with different numbers of CPU (OpenMP) threads. Can also benchmark GPU(s) if required. Results are read from a NumPy archive."""
|
|
||||||
|
|
||||||
# Parse command line arguments
|
|
||||||
parser = argparse.ArgumentParser(description='Plots execution times and speedup factors from benchmarking models run with different numbers of CPU (OpenMP) threads. Can also benchmark GPU(s) if required. Results are read from a NumPy archive.', usage='cd gprMax; python -m tests.benchmarking.plot_benchmark numpyfile')
|
|
||||||
parser.add_argument('baseresult', help='name of NumPy archive file including path')
|
|
||||||
parser.add_argument('--otherresults', default=None, help='list of NumPy archives file including path', nargs='+')
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
# Load base result
|
|
||||||
baseresult = dict(np.load(args.baseresult))
|
|
||||||
|
|
||||||
# Get machine/CPU/OS details
|
|
||||||
hostinfo = get_host_info()
|
|
||||||
try:
|
|
||||||
machineIDlong = str(baseresult['machineID'])
|
|
||||||
# machineIDlong = 'Dell PowerEdge R630; Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz; Linux (3.10.0-327.18.2.el7.x86_64)' # Use to manually describe machine
|
|
||||||
machineID = machineIDlong.split(';')[0]
|
|
||||||
cpuID = machineIDlong.split(';')[1]
|
|
||||||
cpuID = cpuID.split('GHz')[0].split('x')[1][1::] + 'GHz'
|
|
||||||
except KeyError:
|
|
||||||
hyperthreading = ', {} cores with Hyper-Threading'.format(hostinfo['logicalcores']) if hostinfo['hyperthreading'] else ''
|
|
||||||
machineIDlong = '{}; {} x {} ({} cores{}); {} RAM; {}'.format(hostinfo['machineID'], hostinfo['sockets'], hostinfo['cpuID'], hostinfo['physicalcores'], hyperthreading, human_size(hostinfo['ram'], a_kilobyte_is_1024_bytes=True), hostinfo['osversion'])
|
|
||||||
print('Host: {}'.format(machineIDlong))
|
|
||||||
|
|
||||||
# Base result - general info
|
|
||||||
print('Model: {}'.format(args.baseresult))
|
|
||||||
cells = np.array([baseresult['numcells'][0]]) # Length of cubic model side for cells per second metric
|
|
||||||
baseplotlabel = os.path.splitext(os.path.split(args.baseresult)[1])[0] + '.in'
|
|
||||||
|
|
||||||
# Base result - CPU threads and times info from Numpy archive
|
|
||||||
if baseresult['cputhreads'].size != 0:
|
|
||||||
for i in range(len(baseresult['cputhreads'])):
|
|
||||||
print('{} CPU (OpenMP) thread(s): {:g} s'.format(baseresult['cputhreads'][i], baseresult['cputimes'][i]))
|
|
||||||
cpucellspersec = np.array([(baseresult['numcells'][0] * baseresult['numcells'][1] * baseresult['numcells'][2] * baseresult['iterations']) / baseresult['cputimes'][0]])
|
|
||||||
|
|
||||||
# Base result - GPU time info
|
|
||||||
gpuIDs = baseresult['gpuIDs'].tolist()
|
|
||||||
if gpuIDs:
|
|
||||||
gpucellspersec = np.zeros((len(gpuIDs), 1))
|
|
||||||
for i in range(len(gpuIDs)):
|
|
||||||
print('NVIDIA {}: {:g} s'.format(gpuIDs[i], baseresult['gputimes'][i]))
|
|
||||||
gpucellspersec[i] = (baseresult['numcells'][0] * baseresult['numcells'][1] * baseresult['numcells'][2] * baseresult['iterations']) / baseresult['gputimes'][i]
|
|
||||||
|
|
||||||
# Load any other results and info
|
|
||||||
otherresults = []
|
|
||||||
otherplotlabels = []
|
|
||||||
if args.otherresults is not None:
|
|
||||||
for i, result in enumerate(args.otherresults):
|
|
||||||
otherresults.append(dict(np.load(result)))
|
|
||||||
print('\nModel: {}'.format(result))
|
|
||||||
cells = np.append(cells, otherresults[i]['numcells'][0]) # Length of cubic model side for cells per second metric
|
|
||||||
otherplotlabels.append(os.path.splitext(os.path.split(result)[1])[0] + '.in')
|
|
||||||
|
|
||||||
# CPU
|
|
||||||
if otherresults[i]['cputhreads'].size != 0:
|
|
||||||
for thread in range(len(otherresults[i]['cputhreads'])):
|
|
||||||
print('{} CPU (OpenMP) thread(s): {:g} s'.format(otherresults[i]['cputhreads'][thread], otherresults[i]['cputimes'][thread]))
|
|
||||||
cpucellspersec = np.append(cpucellspersec, (otherresults[i]['numcells'][0] * otherresults[i]['numcells'][1] * otherresults[i]['numcells'][2] * otherresults[i]['iterations']) / otherresults[i]['cputimes'][0])
|
|
||||||
|
|
||||||
# GPU
|
|
||||||
othergpuIDs = otherresults[i]['gpuIDs'].tolist()
|
|
||||||
if othergpuIDs:
|
|
||||||
# Array for cells per second metric
|
|
||||||
tmp = np.zeros((len(gpuIDs), len(args.otherresults) + 1))
|
|
||||||
tmp[:gpucellspersec.shape[0],:gpucellspersec.shape[1]] = gpucellspersec
|
|
||||||
gpucellspersec = tmp
|
|
||||||
for j in range(len(othergpuIDs)):
|
|
||||||
print('NVIDIA {}: {:g} s'.format(othergpuIDs[j], otherresults[i]['gputimes'][j]))
|
|
||||||
gpucellspersec[j,i+1] = (otherresults[i]['numcells'][0] * otherresults[i]['numcells'][1] * otherresults[i]['numcells'][2] * otherresults[i]['iterations']) / otherresults[i]['gputimes'][j]
|
|
||||||
|
|
||||||
# Get gprMax version
|
|
||||||
try:
|
|
||||||
version = str(baseresult['version'])
|
|
||||||
except KeyError:
|
|
||||||
version = __version__
|
|
||||||
|
|
||||||
# Create/setup plot figure
|
|
||||||
#colors = ['#E60D30', '#5CB7C6', '#A21797', '#A3B347'] # Plot colours from http://tools.medialab.sciences-po.fr/iwanthue/index.php
|
|
||||||
colorIDs = ['#015dbb', '#c23100', '#00a15a', '#c84cd0', '#ff9aa0']
|
|
||||||
colors = itertools.cycle(colorIDs)
|
|
||||||
lines = itertools.cycle(('--', ':', '-.', '-'))
|
|
||||||
markers = ['o', 'd', '^', 's', '*']
|
|
||||||
fig, ax = plt.subplots(num=machineID, figsize=(30, 10), facecolor='w', edgecolor='w')
|
|
||||||
fig.suptitle(machineIDlong + '\ngprMax v' + version)
|
|
||||||
gs = gridspec.GridSpec(1, 3, hspace=0.5)
|
|
||||||
plotcount = 0
|
|
||||||
|
|
||||||
###########################################
|
|
||||||
# Subplot of CPU (OpenMP) threads vs time #
|
|
||||||
###########################################
|
|
||||||
if baseresult['cputhreads'].size != 0:
|
|
||||||
ax = plt.subplot(gs[0, plotcount])
|
|
||||||
ax.plot(baseresult['cputhreads'], baseresult['cputimes'], color=next(colors), marker=markers[0], markeredgecolor='none', ms=8, lw=2, label=baseplotlabel)
|
|
||||||
|
|
||||||
if args.otherresults is not None:
|
|
||||||
for i, result in enumerate(otherresults):
|
|
||||||
ax.plot(result['cputhreads'], result['cputimes'], color=next(colors), marker=markers[0], markeredgecolor='none', ms=8, lw=2, ls=next(lines), label=otherplotlabels[i])
|
|
||||||
|
|
||||||
ax.set_xlabel('Number of CPU (OpenMP) threads')
|
|
||||||
ax.set_ylabel('Time [s]')
|
|
||||||
ax.grid()
|
|
||||||
legend = ax.legend(loc=1)
|
|
||||||
frame = legend.get_frame()
|
|
||||||
frame.set_edgecolor('white')
|
|
||||||
ax.set_xlim([0, baseresult['cputhreads'][0] * 1.1])
|
|
||||||
ax.set_xticks(np.append(baseresult['cputhreads'], 0))
|
|
||||||
ax.set_ylim(0, top=ax.get_ylim()[1] * 1.1)
|
|
||||||
plotcount += 1
|
|
||||||
|
|
||||||
######################################################
|
|
||||||
# Subplot of CPU (OpenMP) threads vs speed-up factor #
|
|
||||||
######################################################
|
|
||||||
colors = itertools.cycle(colorIDs) # Reset color iterator
|
|
||||||
if baseresult['cputhreads'].size != 0:
|
|
||||||
ax = plt.subplot(gs[0, plotcount])
|
|
||||||
ax.plot(baseresult['cputhreads'], baseresult['cputimes'][-1] / baseresult['cputimes'], color=next(colors), marker=markers[0], markeredgecolor='none', ms=8, lw=2, label=baseplotlabel)
|
|
||||||
|
|
||||||
if args.otherresults is not None:
|
|
||||||
for i, result in enumerate(otherresults):
|
|
||||||
ax.plot(result['cputhreads'], result['cputimes'][-1] / result['cputimes'], color=next(colors), marker=markers[0], markeredgecolor='none', ms=8, lw=2, ls=next(lines), label=otherplotlabels[i])
|
|
||||||
|
|
||||||
ax.set_xlabel('Number of CPU (OpenMP) threads')
|
|
||||||
ax.set_ylabel('Speed-up factor')
|
|
||||||
ax.grid()
|
|
||||||
legend = ax.legend(loc=2)
|
|
||||||
frame = legend.get_frame()
|
|
||||||
frame.set_edgecolor('white')
|
|
||||||
ax.set_xlim([0, baseresult['cputhreads'][0] * 1.1])
|
|
||||||
ax.set_xticks(np.append(baseresult['cputhreads'], 0))
|
|
||||||
ax.set_ylim(bottom=1, top=ax.get_ylim()[1] * 1.1)
|
|
||||||
plotcount += 1
|
|
||||||
|
|
||||||
###########################################
|
|
||||||
# Subplot of simulation size vs cells/sec #
|
|
||||||
###########################################
|
|
||||||
|
|
||||||
def autolabel(rects):
|
|
||||||
"""Attach a text label above each bar on a matplotlib bar chart displaying its height.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
rects: Handle to bar chart
|
|
||||||
"""
|
|
||||||
for rect in rects:
|
|
||||||
height = rect.get_height()
|
|
||||||
ax.text(rect.get_x() + rect.get_width()/2, height,
|
|
||||||
'%d' % int(height),
|
|
||||||
ha='center', va='bottom', fontsize=10, rotation=90)
|
|
||||||
|
|
||||||
colors = itertools.cycle(colorIDs) # Reset color iterator
|
|
||||||
ax = plt.subplot(gs[0, plotcount])
|
|
||||||
barwidth = 8 # the width of the bars
|
|
||||||
|
|
||||||
if baseresult['cputhreads'].size != 0:
|
|
||||||
cpu = ax.bar(cells - (1/2) * barwidth, cpucellspersec / 1e6, barwidth, color=next(colors), edgecolor='none', label=cpuID)
|
|
||||||
autolabel(cpu)
|
|
||||||
|
|
||||||
if gpuIDs:
|
|
||||||
positions = np.arange(-gpucellspersec.shape[0] / 2, gpucellspersec.shape[0] / 2, 1)
|
|
||||||
for i in range(gpucellspersec.shape[0]):
|
|
||||||
gpu = ax.bar(cells + positions[i] * barwidth, gpucellspersec[i,:] / 1e6, barwidth, color=next(colors), edgecolor='none', label='NVIDIA ' + gpuIDs[i])
|
|
||||||
autolabel(gpu)
|
|
||||||
|
|
||||||
ax.set_xlabel('Side length of cubic domain [cells]')
|
|
||||||
ax.set_ylabel('Performance [Mcells/s]')
|
|
||||||
ax.grid()
|
|
||||||
legend = ax.legend(loc=2)
|
|
||||||
frame = legend.get_frame()
|
|
||||||
frame.set_edgecolor('white')
|
|
||||||
ax.set_xticks(cells)
|
|
||||||
ax.set_xticklabels(cells)
|
|
||||||
ax.set_xlim([0, cells[-1] * 1.1])
|
|
||||||
ax.set_ylim(bottom=0, top=ax.get_ylim()[1] * 1.1)
|
|
||||||
|
|
||||||
##########################
|
|
||||||
# Save a png of the plot #
|
|
||||||
##########################
|
|
||||||
fig.savefig(os.path.join(os.path.dirname(args.baseresult), machineID.replace(' ', '_') + '.png'), dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
|
|
||||||
#fig.savefig(os.path.join(os.path.dirname(args.baseresult), machineID.replace(' ', '_') + '.pdf'), dpi='none', format='pdf', bbox_inches='tight', pad_inches=0.1)
|
|
||||||
|
|
||||||
plt.show()
|
|
@@ -17,16 +17,23 @@
|
|||||||
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
|
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import h5py
|
import h5py
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
"""Plots a comparison of fields between given simulation output and experimental data files."""
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
"""Plots a comparison of fields between given simulation output and experimental
|
||||||
|
data files.
|
||||||
|
"""
|
||||||
|
|
||||||
# Parse command line arguments
|
# Parse command line arguments
|
||||||
parser = argparse.ArgumentParser(description='Plots a comparison of fields between given simulation output and experimental data files.', usage='cd gprMax; python -m tests.test_experimental modelfile realfile output')
|
parser = argparse.ArgumentParser(description='Plots a comparison of fields between ' +
|
||||||
|
'given simulation output and experimental data files.',
|
||||||
|
usage='cd gprMax; python -m tests.test_experimental modelfile realfile output')
|
||||||
parser.add_argument('modelfile', help='name of model output file including path')
|
parser.add_argument('modelfile', help='name of model output file including path')
|
||||||
parser.add_argument('realfile', help='name of file containing experimental data including path')
|
parser.add_argument('realfile', help='name of file containing experimental data including path')
|
||||||
parser.add_argument('output', help='output to be plotted, i.e. Ex Ey Ez', nargs='+')
|
parser.add_argument('output', help='output to be plotted, i.e. Ex Ey Ez', nargs='+')
|
||||||
@@ -48,7 +55,8 @@ else:
|
|||||||
polarity = 1
|
polarity = 1
|
||||||
|
|
||||||
if args.output[0] not in availablecomponents:
|
if args.output[0] not in availablecomponents:
|
||||||
logger.exception(f"{args.output[0]} output requested to plot, but the available output for receiver 1 is {', '.join(availablecomponents)}")
|
logger.exception(f"{args.output[0]} output requested to plot, but the " +
|
||||||
|
f"available output for receiver 1 is {', '.join(availablecomponents)}")
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
floattype = f[path + args.output[0]].dtype
|
floattype = f[path + args.output[0]].dtype
|
||||||
@@ -73,7 +81,8 @@ realmax = np.where(np.abs(real[:, 1]) == 1)[0][0]
|
|||||||
difftime = - (timemodel[modelmax] - real[realmax, 0])
|
difftime = - (timemodel[modelmax] - real[realmax, 0])
|
||||||
|
|
||||||
# Plot modelled and real data
|
# Plot modelled and real data
|
||||||
fig, ax = plt.subplots(num=modelfile.stem + '_vs_' + realfile.stem, figsize=(20, 10), facecolor='w', edgecolor='w')
|
fig, ax = plt.subplots(num=modelfile.stem + '_vs_' + realfile.stem,
|
||||||
|
figsize=(20, 10), facecolor='w', edgecolor='w')
|
||||||
ax.plot(timemodel + difftime, model, 'r', lw=2, label='Model')
|
ax.plot(timemodel + difftime, model, 'r', lw=2, label='Model')
|
||||||
ax.plot(real[:, 0], real[:, 1], 'r', ls='--', lw=2, label='Experiment')
|
ax.plot(real[:, 0], real[:, 1], 'r', ls='--', lw=2, label='Experiment')
|
||||||
ax.set_xlabel('Time [s]')
|
ax.set_xlabel('Time [s]')
|
||||||
@@ -86,7 +95,9 @@ ax.grid()
|
|||||||
# Save a PDF/PNG of the figure
|
# Save a PDF/PNG of the figure
|
||||||
savename = modelfile.stem + '_vs_' + realfile.stem
|
savename = modelfile.stem + '_vs_' + realfile.stem
|
||||||
savename = modelfile.parent / savename
|
savename = modelfile.parent / savename
|
||||||
# fig.savefig(savename.with_suffix('.pdf'), dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1)
|
# fig.savefig(savename.with_suffix('.pdf'), dpi=None, format='pdf',
|
||||||
# fig.savefig(savename.with_suffix('.png'), dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
|
# bbox_inches='tight', pad_inches=0.1)
|
||||||
|
# fig.savefig(savename.with_suffix('.png'), dpi=150, format='png',
|
||||||
|
# bbox_inches='tight', pad_inches=0.1)
|
||||||
|
|
||||||
plt.show()
|
plt.show()
|
||||||
|
@@ -24,8 +24,7 @@ import gprMax
|
|||||||
import h5py
|
import h5py
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from colorama import Fore, Style, init
|
|
||||||
init()
|
|
||||||
from tests.analytical_solutions import hertzian_dipole_fs
|
from tests.analytical_solutions import hertzian_dipole_fs
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -50,7 +49,9 @@ basepath = Path(__file__).parents[0] / modelset
|
|||||||
|
|
||||||
|
|
||||||
# List of available basic test models
|
# List of available basic test models
|
||||||
testmodels = ['hertzian_dipole_fs_analytical', '2D_ExHyHz', '2D_EyHxHz', '2D_EzHxHy', 'cylinder_Ascan_2D', 'hertzian_dipole_fs', 'hertzian_dipole_hs', 'hertzian_dipole_dispersive', 'magnetic_dipole_fs']
|
testmodels = ['hertzian_dipole_fs_analytical', '2D_ExHyHz', '2D_EyHxHz', '2D_EzHxHy',
|
||||||
|
'cylinder_Ascan_2D', 'hertzian_dipole_fs', 'hertzian_dipole_hs',
|
||||||
|
'hertzian_dipole_dispersive', 'magnetic_dipole_fs']
|
||||||
|
|
||||||
# List of available advanced test models
|
# List of available advanced test models
|
||||||
# testmodels = ['antenna_GSSI_1500_fs', 'antenna_MALA_1200_fs']
|
# testmodels = ['antenna_GSSI_1500_fs', 'antenna_MALA_1200_fs']
|
||||||
@@ -86,11 +87,13 @@ for i, model in enumerate(testmodels):
|
|||||||
|
|
||||||
# Arrays for storing time
|
# Arrays for storing time
|
||||||
float_or_double = filetest[path + outputstest[0]].dtype
|
float_or_double = filetest[path + outputstest[0]].dtype
|
||||||
timetest = np.linspace(0, (filetest.attrs['Iterations'] - 1) * filetest.attrs['dt'], num=filetest.attrs['Iterations']) / 1e-9
|
timetest = np.linspace(0, (filetest.attrs['Iterations'] - 1) * filetest.attrs['dt'],
|
||||||
|
num=filetest.attrs['Iterations']) / 1e-9
|
||||||
timeref = timetest
|
timeref = timetest
|
||||||
|
|
||||||
# Arrays for storing field data
|
# Arrays for storing field data
|
||||||
datatest = np.zeros((filetest.attrs['Iterations'], len(outputstest)), dtype=float_or_double)
|
datatest = np.zeros((filetest.attrs['Iterations'], len(outputstest)),
|
||||||
|
dtype=float_or_double)
|
||||||
for ID, name in enumerate(outputstest):
|
for ID, name in enumerate(outputstest):
|
||||||
datatest[:, ID] = filetest[path + str(name)][:]
|
datatest[:, ID] = filetest[path + str(name)][:]
|
||||||
if np.any(np.isnan(datatest[:, ID])):
|
if np.any(np.isnan(datatest[:, ID])):
|
||||||
@@ -100,10 +103,14 @@ for i, model in enumerate(testmodels):
|
|||||||
# Tx/Rx position to feed to analytical solution
|
# Tx/Rx position to feed to analytical solution
|
||||||
rxpos = filetest[path].attrs['Position']
|
rxpos = filetest[path].attrs['Position']
|
||||||
txpos = filetest['/srcs/src1/'].attrs['Position']
|
txpos = filetest['/srcs/src1/'].attrs['Position']
|
||||||
rxposrelative = ((rxpos[0] - txpos[0]), (rxpos[1] - txpos[1]), (rxpos[2] - txpos[2]))
|
rxposrelative = ((rxpos[0] - txpos[0]),
|
||||||
|
(rxpos[1] - txpos[1]),
|
||||||
|
(rxpos[2] - txpos[2]))
|
||||||
|
|
||||||
# Analytical solution of a dipole in free space
|
# Analytical solution of a dipole in free space
|
||||||
dataref = hertzian_dipole_fs(filetest.attrs['Iterations'], filetest.attrs['dt'], filetest.attrs['dx_dy_dz'], rxposrelative)
|
dataref = hertzian_dipole_fs(filetest.attrs['Iterations'],
|
||||||
|
filetest.attrs['dt'],
|
||||||
|
filetest.attrs['dx_dy_dz'], rxposrelative)
|
||||||
|
|
||||||
filetest.close()
|
filetest.close()
|
||||||
|
|
||||||
@@ -125,19 +132,25 @@ for i, model in enumerate(testmodels):
|
|||||||
|
|
||||||
# Check that type of float used to store fields matches
|
# Check that type of float used to store fields matches
|
||||||
if filetest[path + outputstest[0]].dtype != fileref[path + outputsref[0]].dtype:
|
if filetest[path + outputstest[0]].dtype != fileref[path + outputsref[0]].dtype:
|
||||||
print(Fore.RED + f'WARNING: Type of floating point number in test model ({filetest[path + outputstest[0]].dtype}) does not match type in reference solution ({fileref[path + outputsref[0]].dtype})\n' + Style.RESET_ALL)
|
logger.warning(f'Type of floating point number in test model ' +
|
||||||
|
f'({filetest[path + outputstest[0]].dtype}) does not ' +
|
||||||
|
f'match type in reference solution ({fileref[path + outputsref[0]].dtype})\n')
|
||||||
float_or_doubleref = fileref[path + outputsref[0]].dtype
|
float_or_doubleref = fileref[path + outputsref[0]].dtype
|
||||||
float_or_doubletest = filetest[path + outputstest[0]].dtype
|
float_or_doubletest = filetest[path + outputstest[0]].dtype
|
||||||
|
|
||||||
# Arrays for storing time
|
# Arrays for storing time
|
||||||
timeref = np.zeros((fileref.attrs['Iterations']), dtype=float_or_doubleref)
|
timeref = np.zeros((fileref.attrs['Iterations']), dtype=float_or_doubleref)
|
||||||
timeref = np.linspace(0, (fileref.attrs['Iterations'] - 1) * fileref.attrs['dt'], num=fileref.attrs['Iterations']) / 1e-9
|
timeref = np.linspace(0, (fileref.attrs['Iterations'] - 1) * fileref.attrs['dt'],
|
||||||
|
num=fileref.attrs['Iterations']) / 1e-9
|
||||||
timetest = np.zeros((filetest.attrs['Iterations']), dtype=float_or_doubletest)
|
timetest = np.zeros((filetest.attrs['Iterations']), dtype=float_or_doubletest)
|
||||||
timetest = np.linspace(0, (filetest.attrs['Iterations'] - 1) * filetest.attrs['dt'], num=filetest.attrs['Iterations']) / 1e-9
|
timetest = np.linspace(0, (filetest.attrs['Iterations'] - 1) * filetest.attrs['dt'],
|
||||||
|
num=filetest.attrs['Iterations']) / 1e-9
|
||||||
|
|
||||||
# Arrays for storing field data
|
# Arrays for storing field data
|
||||||
dataref = np.zeros((fileref.attrs['Iterations'], len(outputsref)), dtype=float_or_doubleref)
|
dataref = np.zeros((fileref.attrs['Iterations'], len(outputsref)),
|
||||||
datatest = np.zeros((filetest.attrs['Iterations'], len(outputstest)), dtype=float_or_doubletest)
|
dtype=float_or_doubleref)
|
||||||
|
datatest = np.zeros((filetest.attrs['Iterations'], len(outputstest)),
|
||||||
|
dtype=float_or_doubletest)
|
||||||
for ID, name in enumerate(outputsref):
|
for ID, name in enumerate(outputsref):
|
||||||
dataref[:, ID] = fileref[path + str(name)][:]
|
dataref[:, ID] = fileref[path + str(name)][:]
|
||||||
datatest[:, ID] = filetest[path + str(name)][:]
|
datatest[:, ID] = filetest[path + str(name)][:]
|
||||||
@@ -152,7 +165,9 @@ for i, model in enumerate(testmodels):
|
|||||||
datadiffs = np.zeros(datatest.shape, dtype=np.float64)
|
datadiffs = np.zeros(datatest.shape, dtype=np.float64)
|
||||||
for i in range(len(outputstest)):
|
for i in range(len(outputstest)):
|
||||||
max = np.amax(np.abs(dataref[:, i]))
|
max = np.amax(np.abs(dataref[:, i]))
|
||||||
datadiffs[:, i] = np.divide(np.abs(dataref[:, i] - datatest[:, i]), max, out=np.zeros_like(dataref[:, i]), where=max != 0) # Replace any division by zero with zero
|
datadiffs[:, i] = np.divide(np.abs(dataref[:, i] - datatest[:, i]), max,
|
||||||
|
out=np.zeros_like(dataref[:, i]),
|
||||||
|
where=max != 0) # Replace any division by zero with zero
|
||||||
|
|
||||||
# Calculate power (ignore warning from taking a log of any zero values)
|
# Calculate power (ignore warning from taking a log of any zero values)
|
||||||
with np.errstate(divide='ignore'):
|
with np.errstate(divide='ignore'):
|
||||||
@@ -165,7 +180,13 @@ for i, model in enumerate(testmodels):
|
|||||||
testresults[model]['Max diff'] = maxdiff
|
testresults[model]['Max diff'] = maxdiff
|
||||||
|
|
||||||
# Plot datasets
|
# Plot datasets
|
||||||
fig1, ((ex1, hx1), (ey1, hy1), (ez1, hz1)) = plt.subplots(nrows=3, ncols=2, sharex=False, sharey='col', subplot_kw=dict(xlabel='Time [ns]'), num=model + '.in', figsize=(20, 10), facecolor='w', edgecolor='w')
|
fig1, ((ex1, hx1), (ey1, hy1), (ez1, hz1)) = plt.subplots(nrows=3, ncols=2,
|
||||||
|
sharex=False, sharey='col',
|
||||||
|
subplot_kw=dict(xlabel='Time [ns]'),
|
||||||
|
num=model + '.in',
|
||||||
|
figsize=(20, 10),
|
||||||
|
facecolor='w',
|
||||||
|
edgecolor='w')
|
||||||
ex1.plot(timetest, datatest[:, 0], 'r', lw=2, label=model)
|
ex1.plot(timetest, datatest[:, 0], 'r', lw=2, label=model)
|
||||||
ex1.plot(timeref, dataref[:, 0], 'g', lw=2, ls='--', label=model + '(Ref)')
|
ex1.plot(timeref, dataref[:, 0], 'g', lw=2, ls='--', label=model + '(Ref)')
|
||||||
ey1.plot(timetest, datatest[:, 1], 'r', lw=2, label=model)
|
ey1.plot(timetest, datatest[:, 1], 'r', lw=2, label=model)
|
||||||
@@ -178,7 +199,9 @@ for i, model in enumerate(testmodels):
|
|||||||
hy1.plot(timeref, dataref[:, 4], 'g', lw=2, ls='--', label=model + '(Ref)')
|
hy1.plot(timeref, dataref[:, 4], 'g', lw=2, ls='--', label=model + '(Ref)')
|
||||||
hz1.plot(timetest, datatest[:, 5], 'r', lw=2, label=model)
|
hz1.plot(timetest, datatest[:, 5], 'r', lw=2, label=model)
|
||||||
hz1.plot(timeref, dataref[:, 5], 'g', lw=2, ls='--', label=model + '(Ref)')
|
hz1.plot(timeref, dataref[:, 5], 'g', lw=2, ls='--', label=model + '(Ref)')
|
||||||
ylabels = ['$E_x$, field strength [V/m]', '$H_x$, field strength [A/m]', '$E_y$, field strength [V/m]', '$H_y$, field strength [A/m]', '$E_z$, field strength [V/m]', '$H_z$, field strength [A/m]']
|
ylabels = ['$E_x$, field strength [V/m]', '$H_x$, field strength [A/m]',
|
||||||
|
'$E_y$, field strength [V/m]', '$H_y$, field strength [A/m]',
|
||||||
|
'$E_z$, field strength [V/m]', '$H_z$, field strength [A/m]']
|
||||||
for i, ax in enumerate(fig1.axes):
|
for i, ax in enumerate(fig1.axes):
|
||||||
ax.set_ylabel(ylabels[i])
|
ax.set_ylabel(ylabels[i])
|
||||||
ax.set_xlim(0, np.amax(timetest))
|
ax.set_xlim(0, np.amax(timetest))
|
||||||
@@ -186,14 +209,22 @@ for i, model in enumerate(testmodels):
|
|||||||
ax.legend()
|
ax.legend()
|
||||||
|
|
||||||
# Plot diffs
|
# Plot diffs
|
||||||
fig2, ((ex2, hx2), (ey2, hy2), (ez2, hz2)) = plt.subplots(nrows=3, ncols=2, sharex=False, sharey='col', subplot_kw=dict(xlabel='Time [ns]'), num='Diffs: ' + model + '.in', figsize=(20, 10), facecolor='w', edgecolor='w')
|
fig2, ((ex2, hx2), (ey2, hy2), (ez2, hz2)) = plt.subplots(nrows=3, ncols=2,
|
||||||
|
sharex=False, sharey='col',
|
||||||
|
subplot_kw=dict(xlabel='Time [ns]'),
|
||||||
|
num='Diffs: ' + model + '.in',
|
||||||
|
figsize=(20, 10),
|
||||||
|
facecolor='w',
|
||||||
|
edgecolor='w')
|
||||||
ex2.plot(timeref, datadiffs[:, 0], 'r', lw=2, label='Ex')
|
ex2.plot(timeref, datadiffs[:, 0], 'r', lw=2, label='Ex')
|
||||||
ey2.plot(timeref, datadiffs[:, 1], 'r', lw=2, label='Ey')
|
ey2.plot(timeref, datadiffs[:, 1], 'r', lw=2, label='Ey')
|
||||||
ez2.plot(timeref, datadiffs[:, 2], 'r', lw=2, label='Ez')
|
ez2.plot(timeref, datadiffs[:, 2], 'r', lw=2, label='Ez')
|
||||||
hx2.plot(timeref, datadiffs[:, 3], 'r', lw=2, label='Hx')
|
hx2.plot(timeref, datadiffs[:, 3], 'r', lw=2, label='Hx')
|
||||||
hy2.plot(timeref, datadiffs[:, 4], 'r', lw=2, label='Hy')
|
hy2.plot(timeref, datadiffs[:, 4], 'r', lw=2, label='Hy')
|
||||||
hz2.plot(timeref, datadiffs[:, 5], 'r', lw=2, label='Hz')
|
hz2.plot(timeref, datadiffs[:, 5], 'r', lw=2, label='Hz')
|
||||||
ylabels = ['$E_x$, difference [dB]', '$H_x$, difference [dB]', '$E_y$, difference [dB]', '$H_y$, difference [dB]', '$E_z$, difference [dB]', '$H_z$, difference [dB]']
|
ylabels = ['$E_x$, difference [dB]', '$H_x$, difference [dB]',
|
||||||
|
'$E_y$, difference [dB]', '$H_y$, difference [dB]',
|
||||||
|
'$E_z$, difference [dB]', '$H_z$, difference [dB]']
|
||||||
for i, ax in enumerate(fig2.axes):
|
for i, ax in enumerate(fig2.axes):
|
||||||
ax.set_ylabel(ylabels[i])
|
ax.set_ylabel(ylabels[i])
|
||||||
ax.set_xlim(0, np.amax(timetest))
|
ax.set_xlim(0, np.amax(timetest))
|
||||||
@@ -203,14 +234,21 @@ for i, model in enumerate(testmodels):
|
|||||||
# Save a PDF/PNG of the figure
|
# Save a PDF/PNG of the figure
|
||||||
filediffs = file.stem + '_diffs'
|
filediffs = file.stem + '_diffs'
|
||||||
filediffs = file.parent / Path(filediffs)
|
filediffs = file.parent / Path(filediffs)
|
||||||
# fig1.savefig(file.with_suffix('.pdf'), dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1)
|
# fig1.savefig(file.with_suffix('.pdf'), dpi=None, format='pdf',
|
||||||
# fig2.savefig(savediffs.with_suffix('.pdf'), dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1)
|
# bbox_inches='tight', pad_inches=0.1)
|
||||||
fig1.savefig(file.with_suffix('.png'), dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
|
# fig2.savefig(savediffs.with_suffix('.pdf'), dpi=None, format='pdf',
|
||||||
fig2.savefig(filediffs.with_suffix('.png'), dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
|
# bbox_inches='tight', pad_inches=0.1)
|
||||||
|
fig1.savefig(file.with_suffix('.png'), dpi=150, format='png',
|
||||||
|
bbox_inches='tight', pad_inches=0.1)
|
||||||
|
fig2.savefig(filediffs.with_suffix('.png'), dpi=150, format='png',
|
||||||
|
bbox_inches='tight', pad_inches=0.1)
|
||||||
|
|
||||||
# Summary of results
|
# Summary of results
|
||||||
for name, data in sorted(testresults.items()):
|
for name, data in sorted(testresults.items()):
|
||||||
if 'analytical' in name:
|
if 'analytical' in name:
|
||||||
print(Fore.CYAN + f"Test '{name}.in' using v.{data['Test version']} compared to analytical solution. Max difference {data['Max diff']:.2f}dB." + Style.RESET_ALL)
|
logger.info(f"Test '{name}.in' using v.{data['Test version']} compared " +
|
||||||
|
f"to analytical solution. Max difference {data['Max diff']:.2f}dB.")
|
||||||
else:
|
else:
|
||||||
print(Fore.CYAN + f"Test '{name}.in' using v.{data['Test version']} compared to reference solution using v.{data['Ref version']}. Max difference {data['Max diff']:.2f}dB." + Style.RESET_ALL)
|
logger.info(f"Test '{name}.in' using v.{data['Test version']} compared to " +
|
||||||
|
f"reference solution using v.{data['Ref version']}. Max difference " +
|
||||||
|
f"{data['Max diff']:.2f}dB.")
|
||||||
|
在新工单中引用
屏蔽一个用户