Work on rotate method for geometry objects.

这个提交包含在:
craig-warren
2020-05-18 15:53:51 +01:00
父节点 50890d4b10
当前提交 6ebbe8a730
共有 17 个文件被更改,包括 188 次插入77 次删除

查看文件

@@ -22,12 +22,12 @@ from .cmds_geometry.geometry_objects_read import GeometryObjectsRead
from .cmds_geometry.plate import Plate from .cmds_geometry.plate import Plate
from .cmds_geometry.sphere import Sphere from .cmds_geometry.sphere import Sphere
from .cmds_geometry.triangle import Triangle from .cmds_geometry.triangle import Triangle
from .cmds_multiple import (PMLCFS, AddDebyeDispersion, AddDrudeDispersion, from .cmds_multiuse import (PMLCFS, AddDebyeDispersion, AddDrudeDispersion,
AddLorentzDispersion, GeometryObjectsWrite, AddLorentzDispersion, GeometryObjectsWrite,
GeometryView, HertzianDipole, MagneticDipole, GeometryView, HertzianDipole, MagneticDipole,
Material, Rx, RxArray, Snapshot, SoilPeplinski, Material, Rx, RxArray, Snapshot, SoilPeplinski,
TransmissionLine, VoltageSource, Waveform) TransmissionLine, VoltageSource, Waveform)
from .cmds_single_use import (Discretisation, Domain, ExcitationFile, from .cmds_singleuse import (Discretisation, Domain, ExcitationFile,
NumThreads, PMLCells, RxSteps, SrcSteps, NumThreads, PMLCells, RxSteps, SrcSteps,
TimeStepStabilityFactor, TimeWindow, Title) TimeStepStabilityFactor, TimeWindow, Title)
from .gprMax import run as run from .gprMax import run as run

查看文件

@@ -1,4 +1,4 @@
# This is where the version number is set and read by setup.py and conf.py (for the docs) # This is where the version number is set and read by setup.py and conf.py (for the docs)
__version__ = '3.2.0b0' __version__ = '4.0.0b0'
codename = 'Johns whisky choice!' codename = 'Johns whisky choice!'

查看文件

@@ -46,6 +46,13 @@ class Box(UserObjectGeometry):
super().__init__(**kwargs) super().__init__(**kwargs)
self.hash = '#box' self.hash = '#box'
def rotate(self, pts, axis, angle, origin=None):
pts = np.array([[self.kwargs['p1']], [self.kwargs['p1']]])
rotation = UserObjectGeometry.rotate_2point_object
rot_pts = rotation(self, pts, axis, angle, origin)
self.kwargs['p1'] = tuple(rot_pts[0, :])
self.kwargs['p1'] = tuple(rot_pts[1, :])
def create(self, grid, uip): def create(self, grid, uip):
try: try:
p1 = self.kwargs['p1'] p1 = self.kwargs['p1']

查看文件

@@ -47,20 +47,54 @@ class UserObjectGeometry:
"""Create the object and add it to the grid.""" """Create the object and add it to the grid."""
pass pass
def rotate(self, axis, angle): def rotate_point(self, p, axis, angle, origin=(0, 0, 0)):
"""Rotate geometry object. """Rotate a point.
Args: Args:
p (array): coordinates of point (x, y, z)
axis (str): axis about which to perform rotation (x, y, or z) axis (str): axis about which to perform rotation (x, y, or z)
angle (int): angle of rotation (degrees) angle (int): angle of rotation (degrees)
origin (tuple): point about which to perform rotation (x, y, z)
Returns:
p (array): coordinates of rotated point (x, y, z)
""" """
orig_p1 = self.kwargs['p1'] origin = np.array([origin])
orig_p2 = self.kwargs['p2']
p1 = np.array([self.kwargs['p1']]) # Move point to axis of rotation
p2 = np.array([self.kwargs['p2']]) p -= origin
# Calculate rotation matrix
r = R.from_euler(axis, angle, degrees=True)
# Apply rotation
p = r.apply(p)
# Move object back to original axis
p += origin
return p
def rotate_2point_object(self, pts, axis, angle, origin=None):
"""Rotate a geometry object that is defined by 2 points.
Args:
pts (array): coordinates of points of object to be rotated
axis (str): axis about which to perform rotation (x, y, or z)
angle (int): angle of rotation (degrees)
origin (tuple): point about which to perform rotation (x, y, z)
Returns:
new_pts (array): coordinates of points of rotated object
"""
# Use origin at centre of object if not given
if not origin:
origin = pts[0,:] + (pts[1,:] - pts[0,:]) / 2
# Check angle value is suitable # Check angle value is suitable
angle = int(angle)
if angle < 0 or angle > 360: if angle < 0 or angle > 360:
logger.exception(self.__str__() + ' angle of rotation must be between 0-360 degrees') logger.exception(self.__str__() + ' angle of rotation must be between 0-360 degrees')
raise ValueError raise ValueError
@@ -68,59 +102,33 @@ class UserObjectGeometry:
logger.exception(self.__str__() + ' angle of rotation must be a multiple of 90 degrees') logger.exception(self.__str__() + ' angle of rotation must be a multiple of 90 degrees')
raise ValueError raise ValueError
# Check axis is valid
if axis != 'x' and axis != 'y' and axis != 'z': if axis != 'x' and axis != 'y' and axis != 'z':
logger.exception(self.__str__() + ' axis of rotation must be x, y, or z') logger.exception(self.__str__() + ' axis of rotation must be x, y, or z')
raise ValueError raise ValueError
# Coordinates for axis of rotation (centre of object) # Save original points
offset = p1 + (p2 - p1) / 2 orig_pts = pts
# Move object to axis of rotation # Rotate points that define object
p1 -= offset pts[0, :] = self.rotate_point(pts[0, :], axis, angle, origin)
p2 -= offset pts[1, :] = self.rotate_point(pts[1, :], axis, angle, origin)
# Calculate rotation matrix
r = R.from_euler(axis, angle, degrees=True)
# Apply rotation
p1 = r.apply(p1)
p2 = r.apply(p2)
# Move object back to original axis
p1 += offset
p2 += offset
# Get lower left and upper right coordinates to define new object # Get lower left and upper right coordinates to define new object
tmp = np.concatenate((p1, p2), axis=0) new_pts = np.zeros(pts.shape)
p1 = np.min(tmp, axis=0) new_pts[0, :] = np.min(pts, axis=0)
p2 = np.max(tmp, axis=0) new_pts[1, :] = np.max(pts, axis=0)
# For 2D modes check axis of rotation against mode # Reset coordinates of invariant direction
# and correct invariant coordinate # - only needed for 2D models, has no effect on 3D models.
# mode = config.get_model_config().mode if axis =='x':
mode = 'TMz' new_pts[0, 0] = orig_pts[0, 0]
if mode == 'TMx': new_pts[1, 0] = orig_pts[1, 0]
if axis == 'y' or axis =='z': elif axis == 'y':
logger.exception(self.__str__() + new_pts[0, 1] = orig_pts[0, 1]
' axis of rotation must be x for TMx mode models') new_pts[1, 1] = orig_pts[1, 1]
raise ValueError elif axis == 'z':
p1[2] = orig_p1[0] new_pts[0, 2] = orig_pts[0, 2]
p2[2] = orig_p2[0] new_pts[1, 2] = orig_pts[1, 2]
elif mode == 'TMy':
if axis == 'x' or axis == 'z':
logger.exception(self.__str__() +
' axis of rotation must be x for TMy mode models')
raise ValueError
p1[2] = orig_p1[1]
p2[2] = orig_p2[1]
elif mode == 'TMz':
if axis == 'x' or axis == 'y':
logger.exception(self.__str__() +
' axis of rotation must be x for TMz mode models')
raise ValueError
p1[2] = orig_p1[2]
p2[2] = orig_p2[2]
# Write points back to original tuple return new_pts
self.kwargs['p1'] = tuple(p1)
self.kwargs['p2'] = tuple(p2)

查看文件

@@ -58,6 +58,9 @@ class CylindricalSector(UserObjectGeometry):
super().__init__(**kwargs) super().__init__(**kwargs)
self.hash = '#cylindrical_sector' self.hash = '#cylindrical_sector'
def rotate(self, axis, angle, origin=None):
pass
def create(self, grid, uip): def create(self, grid, uip):
try: try:

查看文件

@@ -36,6 +36,9 @@ class GeometryObjectsRead(UserObjectGeometry):
super().__init__(**kwargs) super().__init__(**kwargs)
self.hash = '#geometry_objects_read' self.hash = '#geometry_objects_read'
def rotate(self, axis, angle, origin=None):
pass
def create(self, G, uip): def create(self, G, uip):
"""Create the object and add it to the grid.""" """Create the object and add it to the grid."""
try: try:

查看文件

@@ -46,6 +46,9 @@ class Sphere(UserObjectGeometry):
super().__init__(**kwargs) super().__init__(**kwargs)
self.hash = '#sphere' self.hash = '#sphere'
def rotate(self, axis, angle, origin=None):
pass
def create(self, grid, uip): def create(self, grid, uip):
try: try:
p1 = self.kwargs['p1'] p1 = self.kwargs['p1']

查看文件

@@ -50,6 +50,10 @@ class Triangle(UserObjectGeometry):
super().__init__(**kwargs) super().__init__(**kwargs)
self.hash = '#triangle' self.hash = '#triangle'
def rotate(self, axis, angle, origin=None):
logger.debug('Add rotate method.')
pass
def create(self, grid, uip): def create(self, grid, uip):
try: try:
up1 = self.kwargs['p1'] up1 = self.kwargs['p1']

查看文件

@@ -48,6 +48,7 @@ class UserObjectMulti:
self.order = None self.order = None
self.hash = '#example' self.hash = '#example'
self.autotranslate = True self.autotranslate = True
self.rotate_point = UserObjectGeometry.rotate_point
def __str__(self): def __str__(self):
"""Readable user string as per hash commands.""" """Readable user string as per hash commands."""
@@ -63,11 +64,11 @@ class UserObjectMulti:
"""Create the object and add it to the grid.""" """Create the object and add it to the grid."""
pass pass
def rotate(self, axis, angle): def rotate(self, axis, angle, origin=None):
pass pass
def params_str(self): def params_str(self):
"""Readble string of parameters given to object.""" """Readable string of parameters given to object."""
return self.hash + ': ' + str(self.kwargs) return self.hash + ': ' + str(self.kwargs)
@@ -140,6 +141,70 @@ class VoltageSource(UserObjectMulti):
self.order = 2 self.order = 2
self.hash = '#voltage_source' self.hash = '#voltage_source'
def rotate(self, axis, angle, origin=(0, 0, 0)):
"""Rotate geometry object.
Args:
axis (str): axis about which to perform rotation (x, y, or z)
angle (int): angle of rotation (degrees)
origin (tuple): point about which to perform rotation (x, y, z)
"""
# Check angle value is suitable
angle = int(angle)
if angle < 0 or angle > 360:
logger.exception(
self.__str__() + ' angle of rotation must be between 0-360 degrees')
raise ValueError
if angle % 90 != 0:
logger.exception(
self.__str__() + ' angle of rotation must be a multiple of 90 degrees')
raise ValueError
# Check axis is valid
if axis != 'x' and axis != 'y' and axis != 'z':
logger.exception(self.__str__() +
' axis of rotation must be x, y, or z')
raise ValueError
# Save original point
origp = self.kwargs['p1']
# Rotate point
p = self.rotate_point(self, origp, axis, angle, origin)
p = np.array([p])
# Reset coordinates of invariant direction
# - only needed for 2D models, has no effect on 3D models.
# Set polarisation depending on rotation angle
if axis == 'x':
p[0] = origp[0]
if self.kwargs['polarisation'].lower() == 'y':
if angle == 90 or angle == 270:
self.kwargs['polarisation'] = 'z'
elif self.kwargs['polarisation'].lower() =='z':
if angle == 90 or angle == 270:
self.kwargs['polarisation'] = 'y'
elif axis == 'y':
p[1] = origp[1]
if self.kwargs['polarisation'].lower() == 'x':
if angle == 90 or angle == 270:
self.kwargs['polarisation'] = 'z'
elif self.kwargs['polarisation'].lower() == 'z':
if angle == 90 or angle == 270:
self.kwargs['polarisation'] = 'x'
elif axis == 'z':
p[2] = origp[2]
if self.kwargs['polarisation'].lower() == 'x':
if angle == 90 or angle == 270:
self.kwargs['polarisation'] = 'y'
elif self.kwargs['polarisation'].lower() == 'y':
if angle == 90 or angle == 270:
self.kwargs['polarisation'] = 'x'
# Write point back to original tuple
self.kwargs['p1'] = tuple(p)
def create(self, grid, uip): def create(self, grid, uip):
try: try:
p1 = self.kwargs['p1'] p1 = self.kwargs['p1']
@@ -552,7 +617,7 @@ class Rx(UserObjectMulti):
try: try:
r.ID = self.kwargs['id'] r.ID = self.kwargs['id']
outputs = self.kwargs['outputs'] outputs = [self.kwargs['outputs']]
except KeyError: except KeyError:
# If no ID or outputs are specified, use default # If no ID or outputs are specified, use default
r.ID = r.__class__.__name__ + '(' + str(r.xcoord) + ',' + str(r.ycoord) + ',' + str(r.zcoord) + ')' r.ID = r.__class__.__name__ + '(' + str(r.xcoord) + ',' + str(r.ycoord) + ',' + str(r.zcoord) + ')'

查看文件

@@ -55,7 +55,7 @@ class UserObjectSingle:
def create(self, grid, uip): def create(self, grid, uip):
pass pass
def rotate(self, axis, angle): def rotate(self, axis, angle, origin=None):
pass pass

查看文件

@@ -285,7 +285,24 @@ class GeometryView:
if not materialsonly: if not materialsonly:
# Information on PML thickness # Information on PML thickness
if G.pmls: if G.pmls:
root.set('PMLthickness', list(G.pmlthickness.values())) # Only render PMLs if they are in the geometry view
pmlstorender = dict.fromkeys(G.pmlthickness, 0)
xmax = G.nx - self.vtk_xfcells
ymax = G.ny - self.vtk_yfcells
zmax = G.nz - self.vtk_zfcells
if G.pmlthickness['x0'] - self.vtk_xscells > 0:
pmlstorender['x0'] = G.pmlthickness['x0']
if G.pmlthickness['y0'] - self.vtk_yscells > 0:
pmlstorender['y0'] = G.pmlthickness['y0']
if G.pmlthickness['z0'] - self.vtk_zscells > 0:
pmlstorender['z0'] = G.pmlthickness['z0']
if self.vtk_xfcells > G.nx - G.pmlthickness['xmax']:
pmlstorender['xmax'] = G.pmlthickness['xmax']
if self.vtk_yfcells > G.ny - G.pmlthickness['ymax']:
pmlstorender['ymax'] = G.pmlthickness['ymax']
if self.vtk_zfcells > G.nz - G.pmlthickness['zmax']:
pmlstorender['zmax'] = G.pmlthickness['zmax']
root.set('PMLthickness', list(pmlstorender.values()))
# Location of sources and receivers # Location of sources and receivers
srcs = G.hertziandipoles + G.magneticdipoles + G.voltagesources + G.transmissionlines srcs = G.hertziandipoles + G.magneticdipoles + G.voltagesources + G.transmissionlines
if srcs: if srcs:

查看文件

@@ -18,7 +18,7 @@
import logging import logging
from .cmds_multiple import (PMLCFS, AddDebyeDispersion, AddDrudeDispersion, from .cmds_multiuse import (PMLCFS, AddDebyeDispersion, AddDrudeDispersion,
AddLorentzDispersion, GeometryObjectsWrite, AddLorentzDispersion, GeometryObjectsWrite,
GeometryView, HertzianDipole, MagneticDipole, GeometryView, HertzianDipole, MagneticDipole,
Material, Rx, RxArray, Snapshot, SoilPeplinski, Material, Rx, RxArray, Snapshot, SoilPeplinski,
@@ -128,7 +128,7 @@ def process_multicmds(multicmds):
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=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)

查看文件

@@ -18,7 +18,7 @@
import logging import logging
from .cmds_single_use import (Discretisation, Domain, ExcitationFile, from .cmds_singleuse import (Discretisation, Domain, ExcitationFile,
NumThreads, OutputDir, PMLCells, RxSteps, NumThreads, OutputDir, PMLCells, RxSteps,
SrcSteps, TimeStepStabilityFactor, TimeWindow, SrcSteps, TimeStepStabilityFactor, TimeWindow,
Title) Title)

查看文件

@@ -20,8 +20,8 @@ import logging
from .cmds_geometry.cmds_geometry import UserObjectGeometry from .cmds_geometry.cmds_geometry import UserObjectGeometry
from .cmds_geometry.fractal_box_builder import FractalBoxBuilder from .cmds_geometry.fractal_box_builder import FractalBoxBuilder
from .cmds_multiple import UserObjectMulti from .cmds_multiuse import UserObjectMulti
from .cmds_single_use import (Discretisation, Domain, TimeWindow, from .cmds_singleuse import (Discretisation, Domain, TimeWindow,
UserObjectSingle) UserObjectSingle)
from .materials import create_built_in_materials from .materials import create_built_in_materials
from .subgrids.user_objects import SubGridBase as SubGridUserBase from .subgrids.user_objects import SubGridBase as SubGridUserBase

查看文件

@@ -32,7 +32,8 @@ class SubGridBase(FDTDGrid):
self.ratio = kwargs['ratio'] self.ratio = kwargs['ratio']
if self.ratio % 2 == 0: if self.ratio % 2 == 0:
raise ValueError('Subgrid Error: Only odd ratios are supported') logger.exception('Subgrid Error: Only odd ratios are supported')
raise ValueError
# Name of the grid # Name of the grid
self.name = kwargs['id'] self.name = kwargs['id']

查看文件

@@ -151,7 +151,7 @@ class SubGridHSG(SubGridBase):
s += f' Working region cells: {self.nwx} x {self.nwy} x {self.nwz}\n' s += f' Working region cells: {self.nwx} x {self.nwy} x {self.nwz}\n'
for h in self.hertziandipoles: for h in self.hertziandipoles:
s += f' Hertizian dipole at {h.xcoord} {h.ycoord} {h.zcoord}\n' s += f' Hertzian dipole at {h.xcoord} {h.ycoord} {h.zcoord}\n'
s += ' ' + str([x for x in self.waveforms s += ' ' + str([x for x in self.waveforms
if x.ID == h.waveformID][0]) + '\n' if x.ID == h.waveformID][0]) + '\n'
for r in self.rxs: for r in self.rxs:

查看文件

@@ -23,7 +23,7 @@ import numpy as np
from gprMax import config from gprMax import config
from ..cmds_geometry.cmds_geometry import UserObjectGeometry from ..cmds_geometry.cmds_geometry import UserObjectGeometry
from ..cmds_multiple import Rx, UserObjectMulti from ..cmds_multiuse import Rx, UserObjectMulti
from .multi import ReferenceRx as ReferenceRxUser from .multi import ReferenceRx as ReferenceRxUser
from .subgrid_hsg import SubGridHSG as SubGridHSGUser from .subgrid_hsg import SubGridHSG as SubGridHSGUser
@@ -129,13 +129,13 @@ class SubGridBase(UserObjectMulti):
# Copy over built in materials # Copy over built in materials
sg.materials = [copy(m) for m in grid.materials if m.numID in range(0, grid.n_built_in_materials + 1)] sg.materials = [copy(m) for m in grid.materials if m.numID in range(0, grid.n_built_in_materials + 1)]
# Dont mix and match different subgrids # Don't mix and match different subgrid types
for sg_made in grid.subgrids: for sg_made in grid.subgrids:
if type(sg) != type(sg_made): if type(sg) != type(sg_made):
logger.exception(self.__str__() + ' please only use one type of subgrid') logger.exception(self.__str__() + ' please only use one type of subgrid')
raise ValueError raise ValueError
# Reference the sub grid under the main grid to which it belongs. # Reference the subgrid under the main grid to which it belongs
grid.subgrids.append(sg) grid.subgrids.append(sg)