你已经派生过 gprMax
镜像自地址
https://gitee.com/sunhf/gprMax.git
已同步 2025-08-08 07:24:19 +08:00
Work on rotate method for geometry objects.
这个提交包含在:
@@ -22,14 +22,14 @@ from .cmds_geometry.geometry_objects_read import GeometryObjectsRead
|
||||
from .cmds_geometry.plate import Plate
|
||||
from .cmds_geometry.sphere import Sphere
|
||||
from .cmds_geometry.triangle import Triangle
|
||||
from .cmds_multiple import (PMLCFS, AddDebyeDispersion, AddDrudeDispersion,
|
||||
from .cmds_multiuse import (PMLCFS, AddDebyeDispersion, AddDrudeDispersion,
|
||||
AddLorentzDispersion, GeometryObjectsWrite,
|
||||
GeometryView, HertzianDipole, MagneticDipole,
|
||||
Material, Rx, RxArray, Snapshot, SoilPeplinski,
|
||||
TransmissionLine, VoltageSource, Waveform)
|
||||
from .cmds_single_use import (Discretisation, Domain, ExcitationFile,
|
||||
NumThreads, PMLCells, RxSteps, SrcSteps,
|
||||
TimeStepStabilityFactor, TimeWindow, Title)
|
||||
from .cmds_singleuse import (Discretisation, Domain, ExcitationFile,
|
||||
NumThreads, PMLCells, RxSteps, SrcSteps,
|
||||
TimeStepStabilityFactor, TimeWindow, Title)
|
||||
from .gprMax import run as run
|
||||
from .hash_cmds_file import user_libs_fn_to_scene_obj
|
||||
from .scene import Scene
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# 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!'
|
||||
|
@@ -46,6 +46,13 @@ class Box(UserObjectGeometry):
|
||||
super().__init__(**kwargs)
|
||||
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):
|
||||
try:
|
||||
p1 = self.kwargs['p1']
|
||||
|
@@ -47,20 +47,54 @@ class UserObjectGeometry:
|
||||
"""Create the object and add it to the grid."""
|
||||
pass
|
||||
|
||||
def rotate(self, axis, angle):
|
||||
"""Rotate geometry object.
|
||||
def rotate_point(self, p, axis, angle, origin=(0, 0, 0)):
|
||||
"""Rotate a point.
|
||||
|
||||
Args:
|
||||
p (array): coordinates of point (x, y, z)
|
||||
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:
|
||||
p (array): coordinates of rotated point (x, y, z)
|
||||
"""
|
||||
|
||||
orig_p1 = self.kwargs['p1']
|
||||
orig_p2 = self.kwargs['p2']
|
||||
p1 = np.array([self.kwargs['p1']])
|
||||
p2 = np.array([self.kwargs['p2']])
|
||||
origin = np.array([origin])
|
||||
|
||||
# Move point to axis of rotation
|
||||
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
|
||||
angle = int(angle)
|
||||
if angle < 0 or angle > 360:
|
||||
logger.exception(self.__str__() + ' angle of rotation must be between 0-360 degrees')
|
||||
raise ValueError
|
||||
@@ -68,59 +102,33 @@ class UserObjectGeometry:
|
||||
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
|
||||
|
||||
# Coordinates for axis of rotation (centre of object)
|
||||
offset = p1 + (p2 - p1) / 2
|
||||
# Save original points
|
||||
orig_pts = pts
|
||||
|
||||
# Move object to axis of rotation
|
||||
p1 -= offset
|
||||
p2 -= offset
|
||||
|
||||
# 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
|
||||
# Rotate points that define object
|
||||
pts[0, :] = self.rotate_point(pts[0, :], axis, angle, origin)
|
||||
pts[1, :] = self.rotate_point(pts[1, :], axis, angle, origin)
|
||||
|
||||
# Get lower left and upper right coordinates to define new object
|
||||
tmp = np.concatenate((p1, p2), axis=0)
|
||||
p1 = np.min(tmp, axis=0)
|
||||
p2 = np.max(tmp, axis=0)
|
||||
new_pts = np.zeros(pts.shape)
|
||||
new_pts[0, :] = np.min(pts, axis=0)
|
||||
new_pts[1, :] = np.max(pts, axis=0)
|
||||
|
||||
# For 2D modes check axis of rotation against mode
|
||||
# and correct invariant coordinate
|
||||
# mode = config.get_model_config().mode
|
||||
mode = 'TMz'
|
||||
if mode == 'TMx':
|
||||
if axis == 'y' or axis =='z':
|
||||
logger.exception(self.__str__() +
|
||||
' axis of rotation must be x for TMx mode models')
|
||||
raise ValueError
|
||||
p1[2] = orig_p1[0]
|
||||
p2[2] = orig_p2[0]
|
||||
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]
|
||||
# Reset coordinates of invariant direction
|
||||
# - only needed for 2D models, has no effect on 3D models.
|
||||
if axis =='x':
|
||||
new_pts[0, 0] = orig_pts[0, 0]
|
||||
new_pts[1, 0] = orig_pts[1, 0]
|
||||
elif axis == 'y':
|
||||
new_pts[0, 1] = orig_pts[0, 1]
|
||||
new_pts[1, 1] = orig_pts[1, 1]
|
||||
elif axis == 'z':
|
||||
new_pts[0, 2] = orig_pts[0, 2]
|
||||
new_pts[1, 2] = orig_pts[1, 2]
|
||||
|
||||
# Write points back to original tuple
|
||||
self.kwargs['p1'] = tuple(p1)
|
||||
self.kwargs['p2'] = tuple(p2)
|
||||
return new_pts
|
||||
|
@@ -58,6 +58,9 @@ class CylindricalSector(UserObjectGeometry):
|
||||
super().__init__(**kwargs)
|
||||
self.hash = '#cylindrical_sector'
|
||||
|
||||
def rotate(self, axis, angle, origin=None):
|
||||
pass
|
||||
|
||||
def create(self, grid, uip):
|
||||
|
||||
try:
|
||||
|
@@ -36,6 +36,9 @@ class GeometryObjectsRead(UserObjectGeometry):
|
||||
super().__init__(**kwargs)
|
||||
self.hash = '#geometry_objects_read'
|
||||
|
||||
def rotate(self, axis, angle, origin=None):
|
||||
pass
|
||||
|
||||
def create(self, G, uip):
|
||||
"""Create the object and add it to the grid."""
|
||||
try:
|
||||
|
@@ -46,6 +46,9 @@ class Sphere(UserObjectGeometry):
|
||||
super().__init__(**kwargs)
|
||||
self.hash = '#sphere'
|
||||
|
||||
def rotate(self, axis, angle, origin=None):
|
||||
pass
|
||||
|
||||
def create(self, grid, uip):
|
||||
try:
|
||||
p1 = self.kwargs['p1']
|
||||
|
@@ -50,6 +50,10 @@ class Triangle(UserObjectGeometry):
|
||||
super().__init__(**kwargs)
|
||||
self.hash = '#triangle'
|
||||
|
||||
def rotate(self, axis, angle, origin=None):
|
||||
logger.debug('Add rotate method.')
|
||||
pass
|
||||
|
||||
def create(self, grid, uip):
|
||||
try:
|
||||
up1 = self.kwargs['p1']
|
||||
|
@@ -48,6 +48,7 @@ class UserObjectMulti:
|
||||
self.order = None
|
||||
self.hash = '#example'
|
||||
self.autotranslate = True
|
||||
self.rotate_point = UserObjectGeometry.rotate_point
|
||||
|
||||
def __str__(self):
|
||||
"""Readable user string as per hash commands."""
|
||||
@@ -63,11 +64,11 @@ class UserObjectMulti:
|
||||
"""Create the object and add it to the grid."""
|
||||
pass
|
||||
|
||||
def rotate(self, axis, angle):
|
||||
def rotate(self, axis, angle, origin=None):
|
||||
pass
|
||||
|
||||
def params_str(self):
|
||||
"""Readble string of parameters given to object."""
|
||||
"""Readable string of parameters given to object."""
|
||||
return self.hash + ': ' + str(self.kwargs)
|
||||
|
||||
|
||||
@@ -140,6 +141,70 @@ class VoltageSource(UserObjectMulti):
|
||||
self.order = 2
|
||||
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):
|
||||
try:
|
||||
p1 = self.kwargs['p1']
|
||||
@@ -552,7 +617,7 @@ class Rx(UserObjectMulti):
|
||||
|
||||
try:
|
||||
r.ID = self.kwargs['id']
|
||||
outputs = self.kwargs['outputs']
|
||||
outputs = [self.kwargs['outputs']]
|
||||
except KeyError:
|
||||
# If no ID or outputs are specified, use default
|
||||
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):
|
||||
pass
|
||||
|
||||
def rotate(self, axis, angle):
|
||||
def rotate(self, axis, angle, origin=None):
|
||||
pass
|
||||
|
||||
|
@@ -285,7 +285,24 @@ class GeometryView:
|
||||
if not materialsonly:
|
||||
# Information on PML thickness
|
||||
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
|
||||
srcs = G.hertziandipoles + G.magneticdipoles + G.voltagesources + G.transmissionlines
|
||||
if srcs:
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
import logging
|
||||
|
||||
from .cmds_multiple import (PMLCFS, AddDebyeDispersion, AddDrudeDispersion,
|
||||
from .cmds_multiuse import (PMLCFS, AddDebyeDispersion, AddDrudeDispersion,
|
||||
AddLorentzDispersion, GeometryObjectsWrite,
|
||||
GeometryView, HertzianDipole, MagneticDipole,
|
||||
Material, Rx, RxArray, Snapshot, SoilPeplinski,
|
||||
@@ -128,7 +128,7 @@ def process_multicmds(multicmds):
|
||||
if len(tmp) == 3:
|
||||
rx = Rx(p1=(float(tmp[0]), float(tmp[1]), float(tmp[2])))
|
||||
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)
|
||||
|
||||
|
@@ -18,10 +18,10 @@
|
||||
|
||||
import logging
|
||||
|
||||
from .cmds_single_use import (Discretisation, Domain, ExcitationFile,
|
||||
NumThreads, OutputDir, PMLCells, RxSteps,
|
||||
SrcSteps, TimeStepStabilityFactor, TimeWindow,
|
||||
Title)
|
||||
from .cmds_singleuse import (Discretisation, Domain, ExcitationFile,
|
||||
NumThreads, OutputDir, PMLCells, RxSteps,
|
||||
SrcSteps, TimeStepStabilityFactor, TimeWindow,
|
||||
Title)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@@ -20,9 +20,9 @@ import logging
|
||||
|
||||
from .cmds_geometry.cmds_geometry import UserObjectGeometry
|
||||
from .cmds_geometry.fractal_box_builder import FractalBoxBuilder
|
||||
from .cmds_multiple import UserObjectMulti
|
||||
from .cmds_single_use import (Discretisation, Domain, TimeWindow,
|
||||
UserObjectSingle)
|
||||
from .cmds_multiuse import UserObjectMulti
|
||||
from .cmds_singleuse import (Discretisation, Domain, TimeWindow,
|
||||
UserObjectSingle)
|
||||
from .materials import create_built_in_materials
|
||||
from .subgrids.user_objects import SubGridBase as SubGridUserBase
|
||||
from .user_inputs import create_user_input_points
|
||||
|
@@ -32,7 +32,8 @@ class SubGridBase(FDTDGrid):
|
||||
self.ratio = kwargs['ratio']
|
||||
|
||||
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
|
||||
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'
|
||||
|
||||
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
|
||||
if x.ID == h.waveformID][0]) + '\n'
|
||||
for r in self.rxs:
|
||||
|
@@ -23,7 +23,7 @@ import numpy as np
|
||||
from gprMax import config
|
||||
|
||||
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 .subgrid_hsg import SubGridHSG as SubGridHSGUser
|
||||
|
||||
@@ -129,13 +129,13 @@ class SubGridBase(UserObjectMulti):
|
||||
# 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)]
|
||||
|
||||
# Dont mix and match different subgrids
|
||||
# Don't mix and match different subgrid types
|
||||
for sg_made in grid.subgrids:
|
||||
if type(sg) != type(sg_made):
|
||||
logger.exception(self.__str__() + ' please only use one type of subgrid')
|
||||
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)
|
||||
|
||||
|
||||
|
在新工单中引用
屏蔽一个用户