Merge pull request #491 from gprMax/mpi

Add fractal objects to MPI implementation
这个提交包含在:
Craig Warren
2025-06-26 09:16:58 +01:00
提交者 GitHub
当前提交 0256f1bdd2
共有 107 个文件被更改,包括 4017 次插入1680 次删除

5
.gitattributes vendored
查看文件

@@ -1,6 +1 @@
tools/Jupyter_notebooks/* linguist-vendored
reframe_tests/regression_checks/TestGeometryView_5176823e/partial_volume.vtkhdf filter=lfs diff=lfs merge=lfs -text
reframe_tests/regression_checks/TestGeometryView_5176823e/full_volume.vtkhdf filter=lfs diff=lfs merge=lfs -text
reframe_tests/regression_checks/TestGeometryView_77980202/full_volume.vtkhdf filter=lfs diff=lfs merge=lfs -text
reframe_tests/regression_checks/TestGeometryObject_a6a096cb/full_volume.h5 filter=lfs diff=lfs merge=lfs -text
reframe_tests/regression_checks/TestGeometryObject_a6a096cb/partial_volume.h5 filter=lfs diff=lfs merge=lfs -text

查看文件

@@ -6,6 +6,9 @@
.. image:: images_shared/gprMax_logo_small.png
:target: http://www.gprmax.com
:alt: gprMax
.. include_in_docs_after_this_label
***************
Getting Started
@@ -208,6 +211,6 @@ Periodically you should update conda and the required Python packages. With the
Thanks To Our Contributors ✨🔗
==========================
===============================
.. image:: https://contrib.rocks/image?repo=gprMax/gprMax
:target: https://github.com/gprMax/gprMax/graphs/contributors

查看文件

@@ -5,7 +5,7 @@
SPHINXOPTS = -aE
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = ./
BUILDDIR = ./build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)

查看文件

@@ -6,9 +6,13 @@
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
import os
import re
import sys
import time
sys.path.insert(0, os.path.abspath("../.."))
project = "gprMax"
copyright = f'2015-{time.strftime("%Y")}, The University of Edinburgh, United Kingdom. Authors: Craig Warren, Antonis Giannopoulos, and John Hartley'
author = "Craig Warren, Antonis Giannopoulos, and John Hartley"

查看文件

@@ -1 +1,6 @@
.. figure:: ../../images_shared/gprMax_logo_small.png
:target: http://www.gprmax.com
:alt: gprMax
.. include:: ../../README.rst
:start-after: .. include_in_docs_after_this_label

查看文件

@@ -69,11 +69,12 @@ The essential functions are:
Running model(s)
----------------
.. autofunction:: gprMax.gprMax.run
.. autofunction:: gprMax.run
Creating a model scene
----------------------
.. autoclass:: gprMax.scene.Scene
.. autoclass:: gprMax.Scene
:members: add
Domain
------
@@ -193,11 +194,11 @@ Add Surface Water
Geometry View
-------------
.. autoclass:: gprMax.user_objects.cmds_multiuse.GeometryView
.. autoclass:: gprMax.user_objects.cmds_output.GeometryView
Geometry Objects Write
----------------------
.. autoclass:: gprMax.user_objects.cmds_multiuse.GeometryObjectsWrite
.. autoclass:: gprMax.user_objects.cmds_output.GeometryObjectsWrite
Source and output functions
===========================
@@ -248,11 +249,11 @@ Receiver Steps
Snapshot
--------
.. autoclass:: gprMax.user_objects.cmds_multiuse.Snapshot
.. autoclass:: gprMax.user_objects.cmds_output.Snapshot
Subgrid
-------
.. autoclass:: gprMax.subgrids.user_objects.SubGridHSG
.. autoclass:: gprMax.SubGridHSG
.. _pml-tuning:
@@ -262,7 +263,15 @@ PML functions
The default behaviour for the absorbing boundary conditions (ABC) is first order Complex Frequency Shifted (CFS) Perfectly Matched Layers (PML), with thicknesses of 10 cells on each of the six sides of the model domain. The PML can be customised using the following commands:
PML properties
PML Formulation
---------------
.. autoclass:: gprMax.user_objects.cmds_singleuse.PMLFormulation
PML Thickness
-------------
.. autoclass:: gprMax.user_objects.cmds_singleuse.PMLThickness
PML Properties
--------------
.. autoclass:: gprMax.user_objects.cmds_singleuse.PMLProps

查看文件

@@ -359,6 +359,8 @@ For example for a soil with sand fraction 0.5, clay fraction 0.5, bulk density :
Further information on the Peplinski soil model and our implementation can be found in 'Giannakis, I. (2016). Realistic numerical modelling of Ground Penetrating Radar for landmine detection. The University of Edinburgh, United Kingdom. (http://hdl.handle.net/1842/20449)'
.. _object-construction-commands:
Object construction commands
============================

查看文件

@@ -1,5 +1,5 @@
# Copyright (C) 2015-2025: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, John Hartley,
# Authors: Craig Warren, Antonis Giannopoulos, John Hartley,
# and Nathan Mannall
#
# This file is part of gprMax.
@@ -187,7 +187,11 @@ class MPIContext(Context):
def run(self) -> Dict:
try:
return super().run()
result = super().run()
logger.debug("Waiting for all ranks to finish.")
self.comm.Barrier()
logger.debug("Completed.")
return result
except:
logger.exception(f"Rank {self.rank} encountered an error. Aborting...")
self.comm.Abort()

查看文件

@@ -27,6 +27,10 @@ from gprMax.config cimport float_or_double_complex
cpdef void generate_fractal2D(
int nx,
int ny,
int ox,
int oy,
int gx,
int gy,
int nthreads,
float D,
np.float64_t[:] weighting,
@@ -51,12 +55,16 @@ cpdef void generate_fractal2D(
cdef Py_ssize_t i, j
cdef double v2x, v2y, rr, B
cdef int sx, sy
sx = gx // 2
sy = gy // 2
for i in prange(nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(ny):
# Positional vector for current position
v2x = weighting[0] * i
v2y = weighting[1] * j
v2x = weighting[0] * ((i + ox + sx) % gx)
v2y = weighting[1] * ((j + oy + sy) % gy)
# Calulate norm of v2 - v1
rr = ((v2x - v1[0])**2 + (v2y - v1[1])**2)**(1/2)
@@ -71,6 +79,12 @@ cpdef void generate_fractal3D(
int nx,
int ny,
int nz,
int ox,
int oy,
int oz,
int gx,
int gy,
int gz,
int nthreads,
float D,
np.float64_t[:] weighting,
@@ -95,14 +109,19 @@ cpdef void generate_fractal3D(
cdef Py_ssize_t i, j, k
cdef double v2x, v2y, v2z, rr, B
cdef int sx, sy, sz
sx = gx // 2
sy = gy // 2
sz = gz // 2
for i in prange(nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(ny):
for k in range(nz):
# Positional vector for current position
v2x = weighting[0] * i
v2y = weighting[1] * j
v2z = weighting[2] * k
v2x = weighting[0] * ((i + ox + sx) % gx)
v2y = weighting[1] * ((j + oy + sy) % gy)
v2z = weighting[2] * ((k + oz + sz) % gz)
# Calulate norm of v2 - v1
rr = ((v2x - v1[0])**2 + (v2y - v1[1])**2 + (v2z - v1[2])**2)**(1/2)

查看文件

@@ -1,341 +0,0 @@
# Copyright (C) 2015-2025: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, John Hartley,
# and Nathan Mannall
#
# 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 numpy as np
from scipy import fftpack
import gprMax.config as config
from .cython.fractals_generate import generate_fractal2D, generate_fractal3D
from .utilities.utilities import round_value
np.seterr(divide="raise")
class FractalSurface:
"""Fractal surfaces."""
surfaceIDs = ["xminus", "xplus", "yminus", "yplus", "zminus", "zplus"]
def __init__(self, xs, xf, ys, yf, zs, zf, dimension, seed):
"""
Args:
xs, xf, ys, yf, zs, zf: floats for the extent of the fractal surface
(one pair of coordinates must be equal
to correctly define a surface).
dimension: float for the fractal dimension that controls the fractal
distribution.
seed: int for seed value for random number generator.
"""
self.ID = None
self.surfaceID = None
self.xs = xs
self.xf = xf
self.ys = ys
self.yf = yf
self.zs = zs
self.zf = zf
self.nx = xf - xs
self.ny = yf - ys
self.nz = zf - zs
self.dtype = np.dtype(np.complex128)
self.seed = seed
self.dimension = (
dimension # Fractal dimension from: http://dx.doi.org/10.1017/CBO9781139174695
)
self.weighting = np.array([1, 1], dtype=np.float64)
self.fractalrange = (0, 0)
self.filldepth = 0
self.grass = []
def get_surface_dims(self):
"""Gets the dimensions of the fractal surface based on surface plane."""
if self.xs == self.xf:
surfacedims = (self.ny, self.nz)
elif self.ys == self.yf:
surfacedims = (self.nx, self.nz)
elif self.zs == self.zf:
surfacedims = (self.nx, self.ny)
return surfacedims
def generate_fractal_surface(self):
"""Generate a 2D array with a fractal distribution."""
surfacedims = self.get_surface_dims()
self.fractalsurface = np.zeros(surfacedims, dtype=self.dtype)
# Positional vector at centre of array, scaled by weighting
v1 = np.array(
[
self.weighting[0] * (surfacedims[0]) / 2,
self.weighting[1] * (surfacedims[1]) / 2,
]
)
# 2D array of random numbers to be convolved with the fractal function
rng = np.random.default_rng(seed=self.seed)
A = rng.standard_normal(size=(surfacedims[0], surfacedims[1]))
# 2D FFT
A = fftpack.fftn(A)
# Shift the zero frequency component to the centre of the array
A = fftpack.fftshift(A)
# Generate fractal
generate_fractal2D(
surfacedims[0],
surfacedims[1],
config.get_model_config().ompthreads,
self.dimension,
self.weighting,
v1,
A,
self.fractalsurface,
)
# Shift the zero frequency component to start of the array
self.fractalsurface = fftpack.ifftshift(self.fractalsurface)
# Set DC component of FFT to zero
self.fractalsurface[0, 0] = 0
# Take the real part (numerical errors can give rise to an imaginary part)
# of the IFFT, and convert type to floattype. N.B calculation of fractals
# must always be carried out at double precision, i.e. float64, complex128
self.fractalsurface = np.real(fftpack.ifftn(self.fractalsurface)).astype(
config.sim_config.dtypes["float_or_double"], copy=False
)
# Scale the fractal volume according to requested range
fractalmin = np.amin(self.fractalsurface)
fractalmax = np.amax(self.fractalsurface)
fractalrange = fractalmax - fractalmin
self.fractalsurface = (
self.fractalsurface * ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange)
+ self.fractalrange[0]
- ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange) * fractalmin
)
class FractalVolume:
"""Fractal volumes."""
def __init__(self, xs, xf, ys, yf, zs, zf, dimension, seed):
"""
Args:
xs, xf, ys, yf, zs, zf: floats for the extent of the fractal volume.
dimension: float for the fractal dimension that controls the fractal
distribution.
seed: int for seed value for random number generator.
"""
self.ID = None
self.operatingonID = None
self.xs = xs
self.xf = xf
self.ys = ys
self.yf = yf
self.zs = zs
self.zf = zf
self.nx = xf - xs
self.ny = yf - ys
self.nz = zf - zs
self.originalxs = xs
self.originalxf = xf
self.originalys = ys
self.originalyf = yf
self.originalzs = zs
self.originalzf = zf
self.averaging = False
self.dtype = np.dtype(np.complex128)
self.seed = seed
self.dimension = (
dimension # Fractal dimension from: http://dx.doi.org/10.1017/CBO9781139174695
)
self.weighting = np.array([1, 1, 1], dtype=np.float64)
self.nbins = 0
self.fractalsurfaces = []
def generate_fractal_volume(self):
"""Generate a 3D volume with a fractal distribution."""
# Scale filter according to size of fractal volume
if self.nx == 1:
filterscaling = np.amin(np.array([self.ny, self.nz])) / np.array([self.ny, self.nz])
filterscaling = np.insert(filterscaling, 0, 1)
elif self.ny == 1:
filterscaling = np.amin(np.array([self.nx, self.nz])) / np.array([self.nx, self.nz])
filterscaling = np.insert(filterscaling, 1, 1)
elif self.nz == 1:
filterscaling = np.amin(np.array([self.nx, self.ny])) / np.array([self.nx, self.ny])
filterscaling = np.insert(filterscaling, 2, 1)
else:
filterscaling = np.amin(np.array([self.nx, self.ny, self.nz])) / np.array(
[self.nx, self.ny, self.nz]
)
# Adjust weighting to account for filter scaling
self.weighting = np.multiply(self.weighting, filterscaling)
self.fractalvolume = np.zeros((self.nx, self.ny, self.nz), dtype=self.dtype)
# Positional vector at centre of array, scaled by weighting
v1 = np.array(
[
self.weighting[0] * self.nx / 2,
self.weighting[1] * self.ny / 2,
self.weighting[2] * self.nz / 2,
]
)
# 3D array of random numbers to be convolved with the fractal function
rng = np.random.default_rng(seed=self.seed)
A = rng.standard_normal(size=(self.nx, self.ny, self.nz))
# 3D FFT
A = fftpack.fftn(A)
# Shift the zero frequency component to the centre of the array
A = fftpack.fftshift(A)
# Generate fractal
generate_fractal3D(
self.nx,
self.ny,
self.nz,
config.get_model_config().ompthreads,
self.dimension,
self.weighting,
v1,
A,
self.fractalvolume,
)
# Shift the zero frequency component to the start of the array
self.fractalvolume = fftpack.ifftshift(self.fractalvolume)
# Set DC component of FFT to zero
self.fractalvolume[0, 0, 0] = 0
# Take the real part (numerical errors can give rise to an imaginary part)
# of the IFFT, and convert type to floattype. N.B calculation of fractals
# must always be carried out at double precision, i.e. float64, complex128
self.fractalvolume = np.real(fftpack.ifftn(self.fractalvolume)).astype(
config.sim_config.dtypes["float_or_double"], copy=False
)
# Bin fractal values
bins = np.linspace(np.amin(self.fractalvolume), np.amax(self.fractalvolume), self.nbins)
for j in range(self.ny):
for k in range(self.nz):
self.fractalvolume[:, j, k] = np.digitize(
self.fractalvolume[:, j, k], bins, right=True
)
def generate_volume_mask(self):
"""Generate a 3D volume to use as a mask for adding rough surfaces,
water and grass/roots. Zero signifies the mask is not set, one
signifies the mask is set.
"""
self.mask = np.zeros((self.nx, self.ny, self.nz), dtype=np.int8)
maskxs = self.originalxs - self.xs
maskxf = (self.originalxf - self.originalxs) + maskxs
maskys = self.originalys - self.ys
maskyf = (self.originalyf - self.originalys) + maskys
maskzs = self.originalzs - self.zs
maskzf = (self.originalzf - self.originalzs) + maskzs
self.mask[maskxs:maskxf, maskys:maskyf, maskzs:maskzf] = 1
class Grass:
"""Geometry information for blades of grass."""
def __init__(self, numblades, seed):
"""
Args:
numblades: int for the number of blades of grass.
seed: int for seed value for random number generator.
"""
self.numblades = numblades
self.geometryparams = np.zeros(
(self.numblades, 6), dtype=config.sim_config.dtypes["float_or_double"]
)
self.seed = seed
self.set_geometry_parameters()
def set_geometry_parameters(self):
"""Sets randomly defined parameters that will be used to calculate
blade and root geometries.
"""
self.R1 = np.random.default_rng(seed=self.seed)
self.R2 = np.random.default_rng(seed=self.seed)
self.R3 = np.random.default_rng(seed=self.seed)
self.R4 = np.random.default_rng(seed=self.seed)
self.R5 = np.random.default_rng(seed=self.seed)
self.R6 = np.random.default_rng(seed=self.seed)
for i in range(self.numblades):
self.geometryparams[i, 0] = 10 + 20 * self.R1.random()
self.geometryparams[i, 1] = 10 + 20 * self.R2.random()
self.geometryparams[i, 2] = self.R3.choice([-1, 1])
self.geometryparams[i, 3] = self.R4.choice([-1, 1])
def calculate_blade_geometry(self, blade, height):
"""Calculates the x and y coordinates for a given height of grass blade.
Args:
blade: int for the numeric ID of grass blade.
height: float for the height of grass blade.
Returns:
x, y: floats for the x and y coordinates of grass blade.
"""
x = (
self.geometryparams[blade, 2]
* (height / self.geometryparams[blade, 0])
* (height / self.geometryparams[blade, 0])
)
y = (
self.geometryparams[blade, 3]
* (height / self.geometryparams[blade, 1])
* (height / self.geometryparams[blade, 1])
)
x = round_value(x)
y = round_value(y)
return x, y
def calculate_root_geometry(self, root, depth):
"""Calculates the x and y coordinates for a given depth of grass root.
Args:
root: int for the umeric ID of grass root.
depth: float for the depth of grass root.
Returns:
x, y: floats for the x and y coordinates of grass root.
"""
self.geometryparams[root, 4] += -1 + 2 * self.R5.random()
self.geometryparams[root, 5] += -1 + 2 * self.R6.random()
x = round(self.geometryparams[root, 4])
y = round(self.geometryparams[root, 5])
return x, y

查看文件

@@ -0,0 +1,460 @@
# Copyright (C) 2015-2025: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, John Hartley,
# and Nathan Mannall
#
# 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 logging
from typing import List, Optional, Tuple
import numpy as np
import numpy.typing as npt
from mpi4py import MPI
from mpi4py_fft import PFFT, DistArray, newDistArray
from mpi4py_fft.pencil import Subcomm
from scipy import fftpack
from gprMax import config
from gprMax.cython.fractals_generate import generate_fractal2D
from gprMax.fractals.grass import Grass
from gprMax.fractals.mpi_utilities import calculate_starts_and_subshape, create_mpi_type
from gprMax.utilities.mpi import Dim, Dir, get_relative_neighbour
logger = logging.getLogger(__name__)
np.seterr(divide="raise")
class FractalSurface:
"""Fractal surfaces."""
surfaceIDs = ["xminus", "xplus", "yminus", "yplus", "zminus", "zplus"]
def __init__(self, xs, xf, ys, yf, zs, zf, dimension, seed):
"""
Args:
xs, xf, ys, yf, zs, zf: floats for the extent of the fractal surface
(one pair of coordinates must be equal
to correctly define a surface).
dimension: float for the fractal dimension that controls the fractal
distribution.
seed: int for seed value for random number generator.
"""
self.ID = None
self.surfaceID = None
self.start = np.array([xs, ys, zs], dtype=np.int32)
self.stop = np.array([xf, yf, zf], dtype=np.int32)
self.dtype = np.dtype(np.complex128)
self.seed = seed
self.dimension = (
dimension # Fractal dimension from: http://dx.doi.org/10.1017/CBO9781139174695
)
self.weighting = np.array([1, 1], dtype=np.float64)
self.fractalrange: Tuple[int, int] = (0, 0)
self.filldepth = 0
self.grass: List[Grass] = []
@property
def xs(self) -> int:
return self.start[0]
@xs.setter
def xs(self, value: int):
self.start[0] = value
@property
def ys(self) -> int:
return self.start[1]
@ys.setter
def ys(self, value: int):
self.start[1] = value
@property
def zs(self) -> int:
return self.start[2]
@zs.setter
def zs(self, value: int):
self.start[2] = value
@property
def xf(self) -> int:
return self.stop[0]
@xf.setter
def xf(self, value: int):
self.stop[0] = value
@property
def yf(self) -> int:
return self.stop[1]
@yf.setter
def yf(self, value: int):
self.stop[1] = value
@property
def zf(self) -> int:
return self.stop[2]
@zf.setter
def zf(self, value: int):
self.stop[2] = value
@property
def size(self) -> npt.NDArray[np.int32]:
return self.stop - self.start
@property
def nx(self) -> int:
return self.xf - self.xs
@property
def ny(self) -> int:
return self.yf - self.ys
@property
def nz(self) -> int:
return self.zf - self.zs
def get_surface_dims(self):
"""Gets the dimensions of the fractal surface based on surface plane."""
if self.xs == self.xf:
surfacedims = (self.ny, self.nz)
elif self.ys == self.yf:
surfacedims = (self.nx, self.nz)
elif self.zs == self.zf:
surfacedims = (self.nx, self.ny)
return surfacedims
def generate_fractal_surface(self) -> bool:
"""Generate a 2D array with a fractal distribution."""
surfacedims = self.get_surface_dims()
self.fractalsurface = np.zeros(surfacedims, dtype=self.dtype)
# Positional vector at centre of array, scaled by weighting
v1 = np.array(
[
self.weighting[0] * (surfacedims[0]) / 2,
self.weighting[1] * (surfacedims[1]) / 2,
]
)
# 2D array of random numbers to be convolved with the fractal function
rng = np.random.default_rng(seed=self.seed)
A = rng.standard_normal(size=(surfacedims[0], surfacedims[1]))
# 2D FFT
A = fftpack.fftn(A)
# Generate fractal
generate_fractal2D(
surfacedims[0],
surfacedims[1],
0,
0,
surfacedims[0],
surfacedims[1],
config.get_model_config().ompthreads,
self.dimension,
self.weighting,
v1,
A,
self.fractalsurface,
)
# Set DC component of FFT to zero
self.fractalsurface[0, 0] = 0
# Take the real part (numerical errors can give rise to an imaginary part)
# of the IFFT, and convert type to floattype. N.B calculation of fractals
# must always be carried out at double precision, i.e. float64, complex128
self.fractalsurface = np.real(fftpack.ifftn(self.fractalsurface)).astype(
config.sim_config.dtypes["float_or_double"], copy=False
)
# Scale the fractal volume according to requested range
fractalmin = np.amin(self.fractalsurface)
fractalmax = np.amax(self.fractalsurface)
fractalrange = fractalmax - fractalmin
self.fractalsurface = (
self.fractalsurface * ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange)
+ self.fractalrange[0]
- ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange) * fractalmin
)
return True
class MPIFractalSurface(FractalSurface):
def __init__(
self,
xs: int,
xf: int,
ys: int,
yf: int,
zs: int,
zf: int,
dimension: float,
seed: Optional[int],
comm: MPI.Cartcomm,
upper_bound: npt.NDArray[np.int32],
):
super().__init__(xs, xf, ys, yf, zs, zf, dimension, seed)
self.comm = comm
self.upper_bound = upper_bound
def generate_fractal_surface(self) -> bool:
"""Generate a 2D array with a fractal distribution."""
if self.xs == self.xf:
color = self.xs
static_dimension = Dim.X
dims = [Dim.Y, Dim.Z]
elif self.ys == self.yf:
color = self.ys
static_dimension = Dim.Y
dims = [Dim.X, Dim.Z]
elif self.zs == self.zf:
color = self.zs
static_dimension = Dim.Z
dims = [Dim.X, Dim.Y]
# Exit early if this rank does not contain the Fractal Surface
if (
any(self.stop[dims] <= 0)
or any(self.start[dims] >= self.upper_bound[dims])
or self.fractalrange[1] <= 0
or self.fractalrange[0] >= self.upper_bound[static_dimension]
):
self.comm.Split(MPI.UNDEFINED)
# Update start and stop to local bounds
self.start = np.maximum(self.start, 0)
self.start = np.minimum(self.start, self.upper_bound)
self.stop = np.maximum(self.stop, 0)
self.stop = np.minimum(self.stop, self.upper_bound)
return False
else:
# Create new cartsesian communicator for the Fractal Surface
comm = self.comm.Split(color=color)
assert isinstance(comm, MPI.Intracomm)
min_coord = np.array(self.comm.coords, dtype=np.int32)[dims]
max_coord = min_coord + 1
comm.Allreduce(MPI.IN_PLACE, min_coord, MPI.MIN)
comm.Allreduce(MPI.IN_PLACE, max_coord, MPI.MAX)
cart_dims = (max_coord - min_coord).tolist()
self.comm = comm.Create_cart(cart_dims)
# Check domain decomosition is valid for the Fractal Volume
if all([dim > 1 for dim in self.comm.dims]):
raise ValueError(
"Fractal surface must be positioned such that its MPI decomposition is 1 in at"
f" least 1 dimension. Current decompostion is: {self.comm.dims}"
)
# Check domain decomosition is valid for the Fractal Volume
if len(self.grass) > 0 and self.comm.size > 1:
raise ValueError(
"Grass cannot currently be split across multiple MPI rank. Either change the MPI "
" decomposition such that the grass object is contained within a single MPI rank,"
" or divide the grass object into multiple sections. Current decompostion is:"
f" {self.comm.dims}"
)
surfacedims = self.get_surface_dims()
# Positional vector at centre of array, scaled by weighting
v1 = self.weighting * surfacedims / 2
subcomm = Subcomm(self.comm)
A = DistArray(self.size[dims], subcomm, dtype=self.dtype)
fft = PFFT(
None,
axes=tuple(np.argsort(self.comm.dims)[::-1]),
darray=A,
collapse=False,
backend="fftw",
)
# Decomposition of A may be different to the MPIGrid
A_shape = np.array(A.shape)
A_substart = np.array(A.substart)
# 3D array of random numbers to be convolved with the fractal function
rng = np.random.default_rng(seed=self.seed)
for index in np.ndindex(*A.global_shape):
index = np.array(index)
if any(index < A_substart) or any(index >= A_substart + A_shape):
rng.standard_normal()
else:
index -= A_substart
A[index[0], index[1]] = rng.standard_normal()
A_hat = newDistArray(fft)
assert isinstance(A_hat, DistArray)
# 2D FFT
fft.forward(A, A_hat, normalize=False)
# Generate fractal
generate_fractal2D(
A_hat.shape[0],
A_hat.shape[1],
A_hat.substart[0],
A_hat.substart[1],
A_hat.global_shape[0],
A_hat.global_shape[1],
config.get_model_config().ompthreads,
self.dimension,
self.weighting,
v1,
A_hat,
A_hat,
)
# Set DC component of FFT to zero
if all(A_substart == 0):
A_hat[0, 0] = 0
# Inverse 2D FFT transform
fft.backward(A_hat, A, normalize=True)
# Take the real part (numerical errors can give rise to an imaginary part)
# of the IFFT, and convert type to floattype. N.B calculation of fractals
# must always be carried out at double precision, i.e. float64, complex128
A = np.real(A).astype(config.sim_config.dtypes["float_or_double"], copy=False)
# Allreduce to get min and max values in the fractal surface
min_value = np.array(np.amin(A), dtype=config.sim_config.dtypes["float_or_double"])
max_value = np.array(np.amax(A), dtype=config.sim_config.dtypes["float_or_double"])
self.comm.Allreduce(MPI.IN_PLACE, min_value, MPI.MIN)
self.comm.Allreduce(MPI.IN_PLACE, max_value, MPI.MAX)
# Scale the fractal volume according to requested range
fractalrange = max_value - min_value
A = (
A * ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange)
+ self.fractalrange[0]
- ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange) * min_value
)
# Distribute A (DistArray) to match the MPIGrid decomposition
local_shape = (np.minimum(self.stop, self.upper_bound) - np.maximum(self.start, 0))[dims]
self.fractalsurface = np.zeros(
local_shape,
dtype=config.sim_config.dtypes["float_or_double"],
)
# Negative means send to negative neighbour
# Positive means receive from negative neighbour
negative_offset = np.where(self.start[dims] >= 0, A_substart, self.start[dims] + A_substart)
# Negative means send to positive neighbour
# Positive means receive from positive neighbour
positive_offset = np.minimum(self.stop, self.upper_bound)[dims] - (
self.start[dims] + A_substart + A_shape
)
dirs = np.full(2, Dir.NONE)
starts, subshape = calculate_starts_and_subshape(
A_shape, -negative_offset, -positive_offset, dirs, sending=True
)
ends = starts + subshape
A_local = A[starts[0] : ends[0], starts[1] : ends[1]]
starts, subshape = calculate_starts_and_subshape(
local_shape, negative_offset, positive_offset, dirs
)
ends = starts + subshape
self.fractalsurface[starts[0] : ends[0], starts[1] : ends[1]] = A_local
requests: List[MPI.Request] = []
# Need to check neighbours in each direction (2D plane)
sections = [
(Dir.NEG, Dir.NONE),
(Dir.POS, Dir.NONE),
(Dir.NONE, Dir.NEG),
(Dir.NONE, Dir.POS),
(Dir.NEG, Dir.NEG),
(Dir.NEG, Dir.POS),
(Dir.POS, Dir.NEG),
(Dir.POS, Dir.POS),
]
for section in sections:
dirs[0] = section[0]
dirs[1] = section[1]
rank = get_relative_neighbour(self.comm, dirs)
# Skip if no neighbour
if rank == -1:
continue
# Check if any data to send
if all(
np.select(
[dirs == Dir.NEG, dirs == Dir.POS],
[negative_offset <= 0, positive_offset <= 0],
dirs == Dir.NONE,
)
):
mpi_type = create_mpi_type(
A_shape, -negative_offset, -positive_offset, dirs, sending=True
)
logger.debug(
f"Sending fractal surface to rank {rank}, MPI type={mpi_type.decode()}"
)
self.comm.Isend([A, mpi_type], rank)
# Check if any data to receive
if all(
np.select(
[dirs == Dir.NEG, dirs == Dir.POS],
[negative_offset > 0, positive_offset > 0],
dirs == Dir.NONE,
)
):
mpi_type = create_mpi_type(local_shape, negative_offset, positive_offset, dirs)
logger.debug(
f"Receiving fractal surface from rank {rank}, MPI type={mpi_type.decode()}"
)
request = self.comm.Irecv([self.fractalsurface, mpi_type], rank)
requests.append(request)
if len(requests) > 0:
requests[0].Waitall(requests)
# Update start and stop to local bounds
self.start = np.maximum(self.start, 0)
self.start = np.minimum(self.start, self.upper_bound)
self.stop = np.maximum(self.stop, 0)
self.stop = np.minimum(self.stop, self.upper_bound)
logger.debug(
f"Generated fractal surface: start={self.start}, stop={self.stop}, size={self.size}, fractalrange={self.fractalrange}"
)
return True

查看文件

@@ -0,0 +1,525 @@
# Copyright (C) 2015-2025: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, John Hartley,
# and Nathan Mannall
#
# 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 logging
from typing import List, Optional, Tuple, Union
import numpy as np
import numpy.typing as npt
from mpi4py import MPI
from mpi4py_fft import PFFT, DistArray, newDistArray
from mpi4py_fft.pencil import Subcomm
from scipy import fftpack
from gprMax import config
from gprMax.cython.fractals_generate import generate_fractal3D
from gprMax.fractals.fractal_surface import FractalSurface
from gprMax.fractals.mpi_utilities import calculate_starts_and_subshape, create_mpi_type
from gprMax.materials import ListMaterial, PeplinskiSoil, RangeMaterial
from gprMax.utilities.mpi import Dim, Dir, get_relative_neighbour
from gprMax.utilities.utilities import round_value
logger = logging.getLogger(__name__)
np.seterr(divide="raise")
class FractalVolume:
"""Fractal volumes."""
def __init__(
self,
xs: int,
xf: int,
ys: int,
yf: int,
zs: int,
zf: int,
dimension: float,
seed: Optional[int],
):
"""
Args:
xs, xf, ys, yf, zs, zf: floats for the extent of the fractal volume.
dimension: float for the fractal dimension that controls the fractal
distribution.
seed: int for seed value for random number generator.
"""
self.ID = None
self.operatingonID = None
self.start = np.array([xs, ys, zs], dtype=np.int32)
self.stop = np.array([xf, yf, zf], dtype=np.int32)
self.original_start = self.start.copy()
self.original_stop = self.stop.copy()
self.averaging = False
self.dtype = np.dtype(np.complex128)
self.seed = seed
self.dimension = (
dimension # Fractal dimension from: http://dx.doi.org/10.1017/CBO9781139174695
)
self.weighting = np.array([1, 1, 1], dtype=np.float64)
self.nbins = 0
self.mixingmodel: Optional[Union[PeplinskiSoil, RangeMaterial, ListMaterial]] = None
self.fractalsurfaces: List[FractalSurface] = []
@property
def xs(self) -> int:
return self.start[0]
@xs.setter
def xs(self, value: int):
self.start[0] = value
@property
def ys(self) -> int:
return self.start[1]
@ys.setter
def ys(self, value: int):
self.start[1] = value
@property
def zs(self) -> int:
return self.start[2]
@zs.setter
def zs(self, value: int):
self.start[2] = value
@property
def xf(self) -> int:
return self.stop[0]
@xf.setter
def xf(self, value: int):
self.stop[0] = value
@property
def yf(self) -> int:
return self.stop[1]
@yf.setter
def yf(self, value: int):
self.stop[1] = value
@property
def zf(self) -> int:
return self.stop[2]
@zf.setter
def zf(self, value: int):
self.stop[2] = value
@property
def size(self) -> npt.NDArray[np.int32]:
return self.stop - self.start
@property
def nx(self) -> int:
return self.xf - self.xs
@property
def ny(self) -> int:
return self.yf - self.ys
@property
def nz(self) -> int:
return self.zf - self.zs
@property
def originalxs(self) -> int:
return self.original_start[0]
@originalxs.setter
def originalxs(self, value: int):
self.original_start[0] = value
@property
def originalys(self) -> int:
return self.original_start[1]
@originalys.setter
def originalys(self, value: int):
self.original_start[1] = value
@property
def originalzs(self) -> int:
return self.original_start[2]
@originalzs.setter
def originalzs(self, value: int):
self.original_start[2] = value
@property
def originalxf(self) -> int:
return self.original_stop[0]
@originalxf.setter
def originalxf(self, value: int):
self.original_stop[0] = value
@property
def originalyf(self) -> int:
return self.original_stop[1]
@originalyf.setter
def originalyf(self, value: int):
self.original_stop[1] = value
@property
def originalzf(self) -> int:
return self.original_stop[2]
@originalzf.setter
def originalzf(self, value: int):
self.original_stop[2] = value
def generate_fractal_volume(self) -> bool:
"""Generate a 3D volume with a fractal distribution."""
# Scale filter according to size of fractal volume
if self.nx == 1:
filterscaling = np.amin(np.array([self.ny, self.nz])) / np.array([self.ny, self.nz])
filterscaling = np.insert(filterscaling, 0, 1)
elif self.ny == 1:
filterscaling = np.amin(np.array([self.nx, self.nz])) / np.array([self.nx, self.nz])
filterscaling = np.insert(filterscaling, 1, 1)
elif self.nz == 1:
filterscaling = np.amin(np.array([self.nx, self.ny])) / np.array([self.nx, self.ny])
filterscaling = np.insert(filterscaling, 2, 1)
else:
filterscaling = np.amin(np.array([self.nx, self.ny, self.nz])) / np.array(
[self.nx, self.ny, self.nz]
)
# Adjust weighting to account for filter scaling
self.weighting = np.multiply(self.weighting, filterscaling)
self.fractalvolume = np.zeros((self.nx, self.ny, self.nz), dtype=self.dtype)
# Positional vector at centre of array, scaled by weighting
v1 = np.array(
[
self.weighting[0] * self.nx / 2,
self.weighting[1] * self.ny / 2,
self.weighting[2] * self.nz / 2,
]
)
# 3D array of random numbers to be convolved with the fractal function
rng = np.random.default_rng(seed=self.seed)
A = rng.standard_normal(size=(self.nx, self.ny, self.nz))
# 3D FFT
A = fftpack.fftn(A)
# Generate fractal
generate_fractal3D(
self.nx,
self.ny,
self.nz,
0,
0,
0,
self.nx,
self.ny,
self.nz,
config.get_model_config().ompthreads,
self.dimension,
self.weighting,
v1,
A,
self.fractalvolume,
)
# Set DC component of FFT to zero
self.fractalvolume[0, 0, 0] = 0
# Take the real part (numerical errors can give rise to an imaginary part)
# of the IFFT, and convert type to floattype. N.B calculation of fractals
# must always be carried out at double precision, i.e. float64, complex128
self.fractalvolume = np.real(fftpack.ifftn(self.fractalvolume)).astype(
config.sim_config.dtypes["float_or_double"], copy=False
)
# Bin fractal values
bins = np.linspace(np.amin(self.fractalvolume), np.amax(self.fractalvolume), self.nbins)
for j in range(self.ny):
for k in range(self.nz):
self.fractalvolume[:, j, k] = np.digitize(
self.fractalvolume[:, j, k], bins, right=True
)
return True
def generate_volume_mask(self):
"""Generate a 3D volume to use as a mask for adding rough surfaces,
water and grass/roots. Zero signifies the mask is not set, one
signifies the mask is set.
"""
self.mask = np.zeros((self.nx, self.ny, self.nz), dtype=np.int8)
maskxs = self.originalxs - self.xs
maskxf = (self.originalxf - self.originalxs) + maskxs
maskys = self.originalys - self.ys
maskyf = (self.originalyf - self.originalys) + maskys
maskzs = self.originalzs - self.zs
maskzf = (self.originalzf - self.originalzs) + maskzs
self.mask[maskxs:maskxf, maskys:maskyf, maskzs:maskzf] = 1
class MPIFractalVolume(FractalVolume):
def __init__(
self,
xs: int,
xf: int,
ys: int,
yf: int,
zs: int,
zf: int,
dimension: float,
seed: Optional[int],
comm: MPI.Cartcomm,
upper_bound: npt.NDArray[np.int32],
):
super().__init__(xs, xf, ys, yf, zs, zf, dimension, seed)
self.comm = comm
self.upper_bound = upper_bound
# Limit the original start and stop to within the local bounds
self.original_start = np.maximum(self.original_start, 0)
self.original_stop = np.minimum(self.original_stop, self.upper_bound)
# Ensure original_stop is not less than original_start
self.original_stop = np.where(
self.original_stop < self.original_start, self.original_start, self.original_stop
)
def generate_fractal_volume(self) -> bool:
"""Generate a 3D volume with a fractal distribution."""
# Exit early if this rank does not contain the Fractal Volume
# The size of a fractal volume can increase if a Fractal Surface
# is attached. Hence the check needs to happen here once that
# has happened.
if any(self.stop <= 0) or any(self.start >= self.upper_bound):
self.comm.Split(MPI.UNDEFINED)
return False
else:
# Create new cartsesian communicator for the Fractal Volume
comm = self.comm.Split()
assert isinstance(comm, MPI.Intracomm)
min_coord = np.array(self.comm.coords, dtype=np.int32)
max_coord = min_coord + 1
comm.Allreduce(MPI.IN_PLACE, min_coord, MPI.MIN)
comm.Allreduce(MPI.IN_PLACE, max_coord, MPI.MAX)
self.comm = comm.Create_cart((max_coord - min_coord).tolist())
# Check domain decomosition is valid for the Fractal Volume
if all([dim > 1 for dim in self.comm.dims]):
raise ValueError(
"Fractal volume must be positioned such that its MPI decomposition is 1 in at least"
f" 1 dimension. Current decompostion is: {self.comm.dims}"
)
# Scale filter according to size of fractal volume
sorted_size = np.sort(self.size)
min_size = sorted_size[1] if sorted_size[0] == 1 else sorted_size[0]
filterscaling = np.where(self.size == 1, 1, min_size / self.size)
# Adjust weighting to account for filter scaling
self.weighting = np.multiply(self.weighting, filterscaling)
# Positional vector at centre of array, scaled by weighting
v1 = self.weighting * self.size / 2
subcomm = Subcomm(self.comm)
A = DistArray(self.size, subcomm, dtype=self.dtype)
fft = PFFT(
None,
axes=tuple(np.argsort(self.comm.dims)[::-1]),
darray=A,
collapse=False,
backend="fftw",
)
# Decomposition of A may be different to the MPIGrid
A_shape = np.array(A.shape)
A_substart = np.array(A.substart)
static_dimension = Dim(A.alignment)
# 3D array of random numbers to be convolved with the fractal function
rng = np.random.default_rng(seed=self.seed)
for index in np.ndindex(*A.global_shape):
index = np.array(index)
if any(index < A_substart) or any(index >= A_substart + A_shape):
rng.standard_normal()
else:
index -= A_substart
A[index[0], index[1], index[2]] = rng.standard_normal()
A_hat = newDistArray(fft)
assert isinstance(A_hat, DistArray)
# 3D FFT
fft.forward(A, A_hat, normalize=False)
# Generate fractal
generate_fractal3D(
A_hat.shape[0],
A_hat.shape[1],
A_hat.shape[2],
A_hat.substart[0],
A_hat.substart[1],
A_hat.substart[2],
A_hat.global_shape[0],
A_hat.global_shape[1],
A_hat.global_shape[2],
config.get_model_config().ompthreads,
self.dimension,
self.weighting,
v1,
A_hat,
A_hat,
)
# Set DC component of FFT to zero
if all(A_substart == 0):
A_hat[0, 0, 0] = 0
# Inverse 3D FFT transform
fft.backward(A_hat, A, normalize=True)
# Take the real part (numerical errors can give rise to an imaginary part)
# of the IFFT, and convert type to floattype. N.B calculation of fractals
# must always be carried out at double precision, i.e. float64, complex128
A = np.real(A).astype(config.sim_config.dtypes["float_or_double"], copy=False)
# Allreduce to get min and max values in the fractal volume
min_value = np.array(np.amin(A), dtype=config.sim_config.dtypes["float_or_double"])
max_value = np.array(np.amax(A), dtype=config.sim_config.dtypes["float_or_double"])
self.comm.Allreduce(MPI.IN_PLACE, min_value, MPI.MIN)
self.comm.Allreduce(MPI.IN_PLACE, max_value, MPI.MAX)
# Bin fractal values
bins = np.linspace(min_value, max_value, self.nbins)
for j in range(A_shape[1]):
for k in range(A_shape[2]):
A[:, j, k] = np.digitize(A[:, j, k], bins, right=True)
# Distribute A (DistArray) to match the MPIGrid decomposition
local_shape = np.minimum(self.stop, self.upper_bound) - np.maximum(self.start, 0)
self.fractalvolume = np.zeros(
local_shape,
dtype=config.sim_config.dtypes["float_or_double"],
)
# Negative means send to negative neighbour
# Positive means receive from negative neighbour
negative_offset = np.where(self.start >= 0, A_substart, self.start + A_substart)
# Negative means send to positive neighbour
# Positive means receive from positive neighbour
positive_offset = np.minimum(self.stop, self.upper_bound) - (
self.start + A_substart + A_shape
)
dirs = np.full(3, Dir.NONE)
starts, subshape = calculate_starts_and_subshape(
A_shape, -negative_offset, -positive_offset, dirs, sending=True
)
ends = starts + subshape
A_local = A[starts[0] : ends[0], starts[1] : ends[1], starts[2] : ends[2]]
starts, subshape = calculate_starts_and_subshape(
local_shape, negative_offset, positive_offset, dirs
)
ends = starts + subshape
self.fractalvolume[starts[0] : ends[0], starts[1] : ends[1], starts[2] : ends[2]] = A_local
requests: List[MPI.Request] = []
# Need to check neighbours in each direction (2D plane)
sections = [
(Dir.NEG, Dir.NONE),
(Dir.POS, Dir.NONE),
(Dir.NONE, Dir.NEG),
(Dir.NONE, Dir.POS),
(Dir.NEG, Dir.NEG),
(Dir.NEG, Dir.POS),
(Dir.POS, Dir.NEG),
(Dir.POS, Dir.POS),
]
# Dimensions of the 2D plane
dims = [dim for dim in Dim if dim != static_dimension]
for section in sections:
dirs[dims[0]] = section[0]
dirs[dims[1]] = section[1]
rank = get_relative_neighbour(self.comm, dirs)
# Skip if no neighbour
if rank == -1:
continue
# Check if any data to send
if all(
np.select(
[dirs == Dir.NEG, dirs == Dir.POS],
[negative_offset <= 0, positive_offset <= 0],
dirs == Dir.NONE,
)
):
mpi_type = create_mpi_type(
A_shape, -negative_offset, -positive_offset, dirs, sending=True
)
logger.debug(f"Sending fractal volume to rank {rank}, MPI type={mpi_type.decode()}")
self.comm.Isend([A, mpi_type], rank)
# Check if any data to receive
if all(
np.select(
[dirs == Dir.NEG, dirs == Dir.POS],
[negative_offset > 0, positive_offset > 0],
dirs == Dir.NONE,
)
):
mpi_type = create_mpi_type(local_shape, negative_offset, positive_offset, dirs)
logger.debug(
f"Receiving fractal volume from rank {rank}, MPI type={mpi_type.decode()}"
)
request = self.comm.Irecv([self.fractalvolume, mpi_type], rank)
requests.append(request)
if len(requests) > 0:
requests[0].Waitall(requests)
# Update start and stop to local bounds
self.start = np.maximum(self.start, 0)
self.stop = np.minimum(self.stop, self.upper_bound)
logger.debug(
f"Generated fractal volume: start={self.start}, stop={self.stop}, size={self.size}"
)
return True

105
gprMax/fractals/grass.py 普通文件
查看文件

@@ -0,0 +1,105 @@
# Copyright (C) 2015-2025: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, John Hartley,
# and Nathan Mannall
#
# 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 numpy as np
from gprMax import config
from gprMax.utilities.utilities import round_value
np.seterr(divide="raise")
class Grass:
"""Geometry information for blades of grass."""
def __init__(self, numblades, seed):
"""
Args:
numblades: int for the number of blades of grass.
seed: int for seed value for random number generator.
"""
self.numblades = numblades
self.geometryparams = np.zeros(
(self.numblades, 6), dtype=config.sim_config.dtypes["float_or_double"]
)
self.seed = seed
self.set_geometry_parameters()
def set_geometry_parameters(self):
"""Sets randomly defined parameters that will be used to calculate
blade and root geometries.
"""
self.R1 = np.random.default_rng(seed=self.seed)
self.R2 = np.random.default_rng(seed=self.seed)
self.R3 = np.random.default_rng(seed=self.seed)
self.R4 = np.random.default_rng(seed=self.seed)
self.R5 = np.random.default_rng(seed=self.seed)
self.R6 = np.random.default_rng(seed=self.seed)
for i in range(self.numblades):
self.geometryparams[i, 0] = 10 + 20 * self.R1.random()
self.geometryparams[i, 1] = 10 + 20 * self.R2.random()
self.geometryparams[i, 2] = self.R3.choice([-1, 1])
self.geometryparams[i, 3] = self.R4.choice([-1, 1])
def calculate_blade_geometry(self, blade, height):
"""Calculates the x and y coordinates for a given height of grass blade.
Args:
blade: int for the numeric ID of grass blade.
height: float for the height of grass blade.
Returns:
x, y: floats for the x and y coordinates of grass blade.
"""
x = (
self.geometryparams[blade, 2]
* (height / self.geometryparams[blade, 0])
* (height / self.geometryparams[blade, 0])
)
y = (
self.geometryparams[blade, 3]
* (height / self.geometryparams[blade, 1])
* (height / self.geometryparams[blade, 1])
)
x = round_value(x)
y = round_value(y)
return x, y
def calculate_root_geometry(self, root, depth):
"""Calculates the x and y coordinates for a given depth of grass root.
Args:
root: int for the umeric ID of grass root.
depth: float for the depth of grass root.
Returns:
x, y: floats for the x and y coordinates of grass root.
"""
self.geometryparams[root, 4] += -1 + 2 * self.R5.random()
self.geometryparams[root, 5] += -1 + 2 * self.R6.random()
x = round(self.geometryparams[root, 4])
y = round(self.geometryparams[root, 5])
return x, y

查看文件

@@ -0,0 +1,75 @@
# Copyright (C) 2015-2025: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, John Hartley,
# and Nathan Mannall
#
# 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/>.
from typing import Tuple
import numpy as np
import numpy.typing as npt
from mpi4py import MPI
from gprMax.utilities.mpi import Dir
def calculate_starts_and_subshape(
shape: npt.NDArray[np.int32],
negative_offset: npt.NDArray[np.int32],
positive_offset: npt.NDArray[np.int32],
dirs: npt.NDArray[np.int32],
sending: bool = False,
) -> Tuple[npt.NDArray[np.int32], npt.NDArray[np.int32]]:
negative_offset = np.where(
dirs == Dir.NONE,
np.maximum(negative_offset, 0),
np.abs(negative_offset),
)
positive_offset = np.where(
dirs == Dir.NONE,
np.maximum(positive_offset, 0),
np.abs(positive_offset),
)
starts = np.select(
[dirs == Dir.NEG, dirs == Dir.POS],
[0, shape - positive_offset - sending],
default=negative_offset,
)
subshape = np.select(
[dirs == Dir.NEG, dirs == Dir.POS],
[negative_offset + sending, positive_offset + sending],
default=shape - negative_offset - positive_offset,
)
return starts, subshape
def create_mpi_type(
shape: npt.NDArray[np.int32],
negative_offset: npt.NDArray[np.int32],
positive_offset: npt.NDArray[np.int32],
dirs: npt.NDArray[np.int32],
sending: bool = False,
) -> MPI.Datatype:
starts, subshape = calculate_starts_and_subshape(
shape, negative_offset, positive_offset, dirs, sending
)
mpi_type = MPI.FLOAT.Create_subarray(shape.tolist(), subshape.tolist(), starts.tolist())
mpi_type.Commit()
return mpi_type

查看文件

@@ -1,658 +0,0 @@
# Copyright (C) 2015-2025: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, John Hartley,
# and Nathan Mannall
#
# 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 logging
import sys
from abc import ABC, abstractmethod
from io import TextIOWrapper
from itertools import chain
from pathlib import Path
from typing import Dict, List, Sequence, Tuple, Union
import h5py
import numpy as np
import numpy.typing as npt
from tqdm import tqdm
import gprMax.config as config
from gprMax.grid.fdtd_grid import FDTDGrid
from gprMax.grid.mpi_grid import MPIGrid
from gprMax.materials import Material
from gprMax.receivers import Rx
from gprMax.sources import Source
from gprMax.vtkhdf_filehandlers.vtk_image_data import VtkImageData
from gprMax.vtkhdf_filehandlers.vtk_unstructured_grid import VtkUnstructuredGrid
from gprMax.vtkhdf_filehandlers.vtkhdf import VtkCellType, VtkHdfFile
from ._version import __version__
from .cython.geometry_outputs import get_line_properties
from .subgrids.grid import SubGridBaseGrid
from .utilities.utilities import get_terminal_width
logger = logging.getLogger(__name__)
def save_geometry_views(gvs: "List[GeometryView]"):
"""Creates and saves geometryviews.
Args:
gvs: list of all GeometryViews.
"""
logger.info("")
for i, gv in enumerate(gvs):
gv.set_filename()
gv.prep_vtk()
pbar = tqdm(
total=gv.nbytes,
unit="byte",
unit_scale=True,
desc=f"Writing geometry view file {i + 1}/{len(gvs)}, {gv.filename.name}",
ncols=get_terminal_width() - 1,
file=sys.stdout,
disable=not config.sim_config.general["progressbars"],
)
gv.write_vtk()
pbar.update(gv.nbytes)
pbar.close()
logger.info("")
class GeometryView(ABC):
"""Base class for Geometry Views."""
FILE_EXTENSION = ".vtkhdf"
def __init__(
self,
xs: int,
ys: int,
zs: int,
xf: int,
yf: int,
zf: int,
dx: int,
dy: int,
dz: int,
filename: str,
grid: FDTDGrid,
):
"""
Args:
xs, xf, ys, yf, zs, zf: ints for extent of geometry view in cells.
dx, dy, dz: ints for spatial discretisation of geometry view in cells.
filename: string for filename.
grid: FDTDGrid class describing a grid in a model.
"""
self.start = np.array([xs, ys, zs], dtype=np.int32)
self.stop = np.array([xf, yf, zf], dtype=np.int32)
self.step = np.array([dx, dy, dz], dtype=np.int32)
self.size = (self.stop - self.start) // self.step
self.filename = Path(filename)
self.filenamebase = filename
self.grid = grid
self.nbytes = None
self.material_data = None
self.materials = None
# Properties for backwards compatibility
@property
def xs(self) -> int:
return self.start[0]
@property
def ys(self) -> int:
return self.start[1]
@property
def zs(self) -> int:
return self.start[2]
@property
def xf(self) -> int:
return self.stop[0]
@property
def yf(self) -> int:
return self.stop[1]
@property
def zf(self) -> int:
return self.stop[2]
@property
def dx(self) -> int:
return self.step[0]
@property
def dy(self) -> int:
return self.step[1]
@property
def dz(self) -> int:
return self.step[2]
@property
def nx(self) -> int:
return self.size[0]
@property
def ny(self) -> int:
return self.size[1]
@property
def nz(self) -> int:
return self.size[2]
def set_filename(self):
"""Construct filename from user-supplied name and model number."""
parts = config.get_model_config().output_file_path.parts
self.filename = Path(
*parts[:-1], self.filenamebase + config.get_model_config().appendmodelnumber
).with_suffix(self.FILE_EXTENSION)
@abstractmethod
def prep_vtk(self):
pass
@abstractmethod
def write_vtk(self):
pass
class GeometryViewLines(GeometryView):
"""Unstructured grid for a per-cell-edge geometry view."""
def prep_vtk(self):
"""Prepares data for writing to VTKHDF file."""
# Sample ID array according to geometry view spatial discretisation
# Only create a new array if subsampling is required
if (
self.grid.ID.shape != (self.xf, self.yf, self.zf)
or (self.dx, self.dy, self.dz) != (1, 1, 1)
or (self.xs, self.ys, self.zs) != (0, 0, 0)
):
# Require contiguous array
ID = np.ascontiguousarray(
self.grid.ID[
:,
self.xs : self.xf : self.dx,
self.ys : self.yf : self.dy,
self.zs : self.zf : self.dz,
]
)
else:
# This array is contiguous by design
ID = self.grid.ID
x = np.arange(self.nx + 1, dtype=np.float64)
y = np.arange(self.ny + 1, dtype=np.float64)
z = np.arange(self.nz + 1, dtype=np.float64)
coords = np.meshgrid(x, y, z, indexing="ij")
self.points = np.vstack(list(map(np.ravel, coords))).T
self.points += self.start
self.points *= self.step * self.grid.dl
# Add offset to subgrid geometry to correctly locate within main grid
if isinstance(self.grid, SubGridBaseGrid):
offset = [self.grid.i0, self.grid.j0, self.grid.k0]
self.points += offset * self.grid.dl * self.grid.ratio
# Each point is the 'source' for 3 lines.
# NB: Excluding points at the far edge of the geometry as those
# are the 'source' for no lines
n_lines = 3 * self.nx * self.ny * self.nz
self.cell_types = np.full(n_lines, VtkCellType.LINE)
self.cell_offsets = np.arange(0, 2 * n_lines + 2, 2, dtype=np.int32)
self.connectivity, self.material_data = get_line_properties(
n_lines, self.nx, self.ny, self.nz, ID
)
assert isinstance(self.connectivity, np.ndarray)
assert isinstance(self.material_data, np.ndarray)
# Write information about any PMLs, sources, receivers
self.metadata = Metadata(self.grid, self, averaged_materials=True, materials_only=True)
# Number of bytes of data to be written to file
self.nbytes = (
self.points.nbytes
+ self.cell_types.nbytes
+ self.connectivity.nbytes
+ self.cell_offsets.nbytes
+ self.material_data.nbytes
)
def write_vtk(self):
"""Writes geometry information to a VTKHDF file."""
# Write the VTK file
with VtkUnstructuredGrid(
self.filename,
self.points,
self.cell_types,
self.connectivity,
self.cell_offsets,
) as f:
f.add_cell_data("Material", self.material_data)
self.metadata.write_to_vtkhdf(f)
class GeometryViewVoxels(GeometryView):
"""Image data for a per-cell geometry view."""
def prep_vtk(self):
"""Prepares data for writing to VTKHDF file."""
# Sample solid array according to geometry view spatial discretisation
# Only create a new array if subsampling is required
if (
self.grid.solid.shape != (self.xf, self.yf, self.zf)
or (self.dx, self.dy, self.dz) != (1, 1, 1)
or (self.xs, self.ys, self.zs) != (0, 0, 0)
):
# Require contiguous array
self.material_data = np.ascontiguousarray(
self.grid.solid[
self.xs : self.xf : self.dx,
self.ys : self.yf : self.dy,
self.zs : self.zf : self.dz,
]
)
else:
# This array is contiguous by design
self.material_data = self.grid.solid
if isinstance(self.grid, SubGridBaseGrid):
self.origin = np.array(
[
(self.grid.i0 * self.grid.dx * self.grid.ratio),
(self.grid.j0 * self.grid.dy * self.grid.ratio),
(self.grid.k0 * self.grid.dz * self.grid.ratio),
]
)
else:
self.origin = self.start * self.grid.dl
self.spacing = self.step * self.grid.dl
# Write information about any PMLs, sources, receivers
self.metadata = Metadata(self.grid, self)
self.nbytes = self.material_data.nbytes
def write_vtk(self):
"""Writes geometry information to a VTKHDF file."""
with VtkImageData(self.filename, self.size, self.origin, self.spacing) as f:
f.add_cell_data("Material", self.material_data)
self.metadata.write_to_vtkhdf(f)
class Metadata:
"""Comments can be strings included in the header of XML VTK file, and are
used to hold extra (gprMax) information about the VTK data.
"""
def __init__(
self,
grid: FDTDGrid,
gv: GeometryView,
averaged_materials: bool = False,
materials_only: bool = False,
):
self.grid = grid
self.gv = gv
self.averaged_materials = averaged_materials
self.materials_only = materials_only
self.gprmax_version = __version__
self.dx_dy_dz = self.grid.dl
self.nx_ny_nz = np.array([self.grid.nx, self.grid.ny, self.grid.nz], dtype=np.int32)
self.materials = self.materials_comment()
# Write information on PMLs, sources, and receivers
if not self.materials_only:
# Information on PML thickness
if self.grid.pmls["slabs"]:
self.pml_thickness = self.pml_gv_comment()
else:
self.pml_thickness = None
srcs = (
self.grid.hertziandipoles
+ self.grid.magneticdipoles
+ self.grid.voltagesources
+ self.grid.transmissionlines
)
if srcs:
self.source_ids, self.source_positions = self.srcs_rx_gv_comment(srcs)
else:
self.source_ids = None
self.source_positions = None
if self.grid.rxs:
self.receiver_ids, self.receiver_positions = self.srcs_rx_gv_comment(self.grid.rxs)
else:
self.receiver_ids = None
self.receiver_positions = None
def write_to_vtkhdf(self, file_handler: VtkHdfFile):
file_handler.add_field_data(
"gprMax_version",
self.gprmax_version,
dtype=h5py.string_dtype(),
)
file_handler.add_field_data("dx_dy_dz", self.dx_dy_dz)
file_handler.add_field_data("nx_ny_nz", self.nx_ny_nz)
file_handler.add_field_data(
"material_ids",
self.materials,
dtype=h5py.string_dtype(),
)
if not self.materials_only:
if self.pml_thickness is not None:
file_handler.add_field_data("pml_thickness", self.pml_thickness)
if self.source_ids is not None and self.source_positions is not None:
file_handler.add_field_data(
"source_ids", self.source_ids, dtype=h5py.string_dtype()
)
file_handler.add_field_data("sources", self.source_positions)
if self.receiver_ids is not None and self.receiver_positions is not None:
file_handler.add_field_data(
"receiver_ids", self.receiver_ids, dtype=h5py.string_dtype()
)
file_handler.add_field_data("receivers", self.receiver_positions)
def pml_gv_comment(self) -> List[int]:
grid = self.grid
# Only render PMLs if they are in the geometry view
thickness: Dict[str, int] = grid.pmls["thickness"]
gv_pml_depth = dict.fromkeys(thickness, 0)
if self.gv.xs < thickness["x0"]:
gv_pml_depth["x0"] = thickness["x0"] - self.gv.xs
if self.gv.ys < thickness["y0"]:
gv_pml_depth["y0"] = thickness["y0"] - self.gv.ys
if thickness["z0"] - self.gv.zs > 0:
gv_pml_depth["z0"] = thickness["z0"] - self.gv.zs
if self.gv.xf > grid.nx - thickness["xmax"]:
gv_pml_depth["xmax"] = self.gv.xf - (grid.nx - thickness["xmax"])
if self.gv.yf > grid.ny - thickness["ymax"]:
gv_pml_depth["ymax"] = self.gv.yf - (grid.ny - thickness["ymax"])
if self.gv.zf > grid.nz - thickness["zmax"]:
gv_pml_depth["zmax"] = self.gv.zf - (grid.nz - thickness["zmax"])
return list(gv_pml_depth.values())
def srcs_rx_gv_comment(
self, srcs: Union[Sequence[Source], List[Rx]]
) -> Tuple[List[str], npt.NDArray[np.float32]]:
"""Used to name sources and/or receivers."""
names: List[str] = []
positions: npt.NDArray[np.float32] = np.empty((len(srcs), 3))
for index, src in enumerate(srcs):
position = src.coord * self.grid.dl
names.append(src.ID)
positions[index] = position
return names, positions
def dx_dy_dz_comment(self) -> npt.NDArray[np.float64]:
return self.grid.dl
def nx_ny_nz_comment(self) -> npt.NDArray[np.int32]:
return np.array([self.grid.nx, self.grid.ny, self.grid.nz], dtype=np.int32)
def materials_comment(self) -> List[str]:
if not self.averaged_materials:
return [m.ID for m in self.grid.materials if m.type != "dielectric-smoothed"]
else:
return [m.ID for m in self.grid.materials]
class GeometryObjects:
"""Geometry objects to be written to file."""
def __init__(self, xs=None, ys=None, zs=None, xf=None, yf=None, zf=None, basefilename=None):
"""
Args:
xs, xf, ys, yf, zs, zf: ints for extent of the volume in cells.
filename: string for filename.
"""
self.xs = xs
self.ys = ys
self.zs = zs
self.xf = xf
self.yf = yf
self.zf = zf
self.nx = self.xf - self.xs
self.ny = self.yf - self.ys
self.nz = self.zf - self.zs
self.basefilename = basefilename
# Set filenames
parts = config.sim_config.input_file_path.with_suffix("").parts
self.filename_hdf5 = Path(*parts[:-1], self.basefilename)
self.filename_hdf5 = self.filename_hdf5.with_suffix(".h5")
self.filename_materials = Path(*parts[:-1], f"{self.basefilename}_materials")
self.filename_materials = self.filename_materials.with_suffix(".txt")
# Sizes of arrays to write necessary to update progress bar
self.solidsize = (
(self.nx + 1) * (self.ny + 1) * (self.nz + 1) * np.dtype(np.uint32).itemsize
)
self.rigidsize = (
18 * (self.nx + 1) * (self.ny + 1) * (self.nz + 1) * np.dtype(np.int8).itemsize
)
self.IDsize = (
6 * (self.nx + 1) * (self.ny + 1) * (self.nz + 1) * np.dtype(np.uint32).itemsize
)
self.datawritesize = self.solidsize + self.rigidsize + self.IDsize
def write_hdf5(self, title: str, G: FDTDGrid, pbar: tqdm):
"""Writes a geometry objects file in HDF5 format.
Args:
G: FDTDGrid class describing a grid in a model.
pbar: Progress bar class instance.
"""
ID = G.ID[:, self.xs : self.xf + 1, self.ys : self.yf + 1, self.zs : self.zf + 1]
# Get materials present in subset of ID array and sort by material ID
material_ids, inverse_map = np.unique(ID, return_inverse=True)
get_material = np.vectorize(lambda id: G.materials[id])
materials = sorted(get_material(material_ids))
# Create map from material ID to 0 - number of materials
materials_map = {material.numID: index for index, material in enumerate(materials)}
map_materials = np.vectorize(lambda id: materials_map[id])
# Remap ID array to the reduced list of materials
ID = np.array(map_materials(material_ids))[inverse_map].reshape(ID.shape)
data = G.solid[self.xs : self.xf, self.ys : self.yf, self.zs : self.zf].astype("int16")
data = map_materials(data)
rigidE = G.rigidE[:, self.xs : self.xf, self.ys : self.yf, self.zs : self.zf]
rigidH = G.rigidH[:, self.xs : self.xf, self.ys : self.yf, self.zs : self.zf]
with h5py.File(self.filename_hdf5, "w") as fdata:
fdata.attrs["gprMax"] = __version__
fdata.attrs["Title"] = title
fdata.attrs["dx_dy_dz"] = (G.dx, G.dy, G.dz)
fdata["/data"] = data
pbar.update(self.solidsize)
fdata["/rigidE"] = rigidE
fdata["/rigidH"] = rigidH
pbar.update(self.rigidsize)
fdata["/ID"] = ID
pbar.update(self.IDsize)
# Write materials list to a text file
with open(self.filename_materials, "w") as fmaterials:
for material in materials:
self.output_material(material, fmaterials)
def output_material(self, material: Material, file: TextIOWrapper):
file.write(
f"#material: {material.er:g} {material.se:g} "
f"{material.mr:g} {material.sm:g} {material.ID}\n"
)
if hasattr(material, "poles"):
if "debye" in material.type:
dispersionstr = "#add_dispersion_debye: " f"{material.poles:g} "
for pole in range(material.poles):
dispersionstr += f"{material.deltaer[pole]:g} " f"{material.tau[pole]:g} "
elif "lorenz" in material.type:
dispersionstr = f"#add_dispersion_lorenz: " f"{material.poles:g} "
for pole in range(material.poles):
dispersionstr += (
f"{material.deltaer[pole]:g} "
f"{material.tau[pole]:g} "
f"{material.alpha[pole]:g} "
)
elif "drude" in material.type:
dispersionstr = f"#add_dispersion_drude: " f"{material.poles:g} "
for pole in range(material.poles):
dispersionstr += f"{material.tau[pole]:g} " f"{material.alpha[pole]:g} "
dispersionstr += material.ID
file.write(dispersionstr + "\n")
class MPIGeometryObjects(GeometryObjects):
# def __init__(self, start, stop, global_size, comm):
# super().__init__(...)
def write_hdf5(self, title: str, G: MPIGrid, pbar: tqdm):
"""Writes a geometry objects file in HDF5 format.
Args:
G: FDTDGrid class describing a grid in a model.
pbar: Progress bar class instance.
"""
# Get neighbours
self.neighbours = np.full((3, 2), -1, dtype=int)
self.neighbours[0] = self.comm.Shift(direction=0, disp=1)
self.neighbours[1] = self.comm.Shift(direction=1, disp=1)
self.neighbours[2] = self.comm.Shift(direction=2, disp=1)
# Make subsection of array one larger if no positive neighbour
slice_stop = np.where(
self.neighbours[:, 1] == -1,
self.stop + 1,
self.stop,
)
array_slice = list(map(slice, self.start, slice_stop))
ID = G.ID[:, array_slice[0], array_slice[1], array_slice[2]]
# Get materials present in subset of ID
local_material_num_ids, inverse_map = np.unique(ID, return_inverse=True)
get_material = np.vectorize(lambda id: G.materials[id])
local_materials = get_material(local_material_num_ids)
# Send all materials to the coordinating rank
materials = self.comm.gather(local_materials, root=0)
if self.comm.rank == 0:
# Filter out duplicate materials and sort by material ID
materials = np.fromiter(chain.from_iterable(materials), dtype=Material)
global_materials = np.unique(materials)
global_material_ids = [m.ID for m in global_materials]
else:
global_material_ids = None
global_material_ids = self.comm.bcast(global_material_ids, root=0)
# Create map from material ID (str) to global material numID
materials_map = {
material_id: index for index, material_id in enumerate(global_material_ids)
}
# Remap ID array to the global material numID
ID = np.array([materials_map[m.ID] for m in local_materials])[inverse_map].reshape(ID.shape)
# Other geometry arrays do not have halos, so adjustment to
# 'stop' is not necessary
array_slice = list(map(slice, self.start, self.stop))
data = G.solid[array_slice[0], array_slice[1], array_slice[2]]
map_local_materials = np.vectorize(lambda id: materials_map[G.materials[id].ID])
data = map_local_materials(data)
rigidE = G.rigidE[:, array_slice[0], array_slice[1], array_slice[2]]
rigidH = G.rigidH[:, array_slice[0], array_slice[1], array_slice[2]]
start = self.offset
stop = start + self.size
with h5py.File(self.filename_hdf5, "w", driver="mpio", comm=self.comm) as fdata:
fdata.attrs["gprMax"] = __version__
fdata.attrs["Title"] = title
fdata.attrs["dx_dy_dz"] = (G.dx, G.dy, G.dz)
dset = fdata.create_dataset("/data", self.global_size, dtype=data.dtype)
dset[start[0] : stop[0], start[1] : stop[1], start[2] : stop[2]] = data
pbar.update(self.solidsize)
rigid_E_dataset = fdata.create_dataset(
"/rigidE", (12, *self.global_size), dtype=rigidE.dtype
)
rigid_E_dataset[:, start[0] : stop[0], start[1] : stop[1], start[2] : stop[2]] = rigidE
rigid_H_dataset = fdata.create_dataset(
"/rigidH", (6, *self.global_size), dtype=rigidH.dtype
)
rigid_H_dataset[:, start[0] : stop[0], start[1] : stop[1], start[2] : stop[2]] = rigidH
pbar.update(self.rigidsize)
stop = np.where(
self.neighbours[:, 1] == -1,
stop + 1,
stop,
)
dset = fdata.create_dataset("/ID", (6, *(self.global_size + 1)), dtype=ID.dtype)
dset[:, start[0] : stop[0], start[1] : stop[1], start[2] : stop[2]] = ID
pbar.update(self.IDsize)
# Write materials list to a text file
if self.comm.rank == 0:
with open(self.filename_materials, "w") as materials_file:
for material in global_materials:
self.output_material(material, materials_file)

查看文件

@@ -28,10 +28,10 @@ from tqdm import tqdm
from gprMax import config
from gprMax._version import __version__
from gprMax.geometry_outputs.grid_view import GridType, GridView, MPIGridView
from gprMax.grid.fdtd_grid import FDTDGrid
from gprMax.grid.mpi_grid import MPIGrid
from gprMax.materials import Material
from gprMax.output_controllers.grid_view import GridType, GridView, MPIGridView
class GeometryObject(Generic[GridType]):

查看文件

@@ -0,0 +1,170 @@
# Copyright (C) 2015-2025: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, John Hartley,
# and Nathan Mannall
#
# 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/>.
from contextlib import AbstractContextManager
from os import PathLike
from types import TracebackType
from typing import Optional
import h5py
import numpy as np
import numpy.typing as npt
from mpi4py import MPI
from gprMax.geometry_outputs.grid_view import GridView, MPIGridView
from gprMax.grid.fdtd_grid import FDTDGrid
from gprMax.grid.mpi_grid import MPIGrid
class ReadGeometryObject(AbstractContextManager):
def __init__(
self,
filename: PathLike,
grid: FDTDGrid,
start: npt.NDArray[np.int32],
num_existing_materials: int,
) -> None:
self.file_handler = h5py.File(filename)
data = self.file_handler["/data"]
assert isinstance(data, h5py.Dataset)
stop = start + data.shape
if isinstance(grid, MPIGrid):
if grid.local_bounds_overlap_grid(start, stop):
self.grid_view = MPIGridView(
grid, start[0], start[1], start[2], stop[0], stop[1], stop[2]
)
else:
# The MPIGridView will create a new communicator using
# MPI_Split. Calling this here prevents deadlock if not
# all ranks need to read the geometry object.
grid.comm.Split(MPI.UNDEFINED)
self.grid_view = None
else:
self.grid_view = GridView(grid, start[0], start[1], start[2], stop[0], stop[1], stop[2])
self.num_existing_materials = num_existing_materials
def __enter__(self):
return self
def __exit__(
self,
exc_type: Optional[type[BaseException]],
exc_value: Optional[BaseException],
traceback: Optional[TracebackType],
) -> Optional[bool]:
"""Close the file when the context is exited.
The parameters describe the exception that caused the context to
be exited. If the context was exited without an exception, all
three arguments will be None. Any exception will be
processed normally upon exit from this method.
Returns:
suppress_exception (optional): Returns True if the exception
should be suppressed (i.e. not propagated). Otherwise,
the exception will be processed normally upon exit from
this method.
"""
self.close()
def close(self) -> None:
"""Close the file handler"""
self.file_handler.close()
def has_valid_discritisation(self) -> bool:
if self.grid_view is None:
return True
dx_dy_dz = self.file_handler.attrs["dx_dy_dz"]
return not isinstance(dx_dy_dz, h5py.Empty) and all(dx_dy_dz == self.grid_view.grid.dl)
def has_ID_array(self) -> bool:
ID_class = self.file_handler.get("ID", getclass=True)
return ID_class == h5py.Dataset
def has_rigid_arrays(self) -> bool:
rigidE_class = self.file_handler.get("rigidE", getclass=True)
rigidH_class = self.file_handler.get("rigidH", getclass=True)
return rigidE_class == h5py.Dataset and rigidH_class == h5py.Dataset
def read_data(self):
if self.grid_view is None:
return
data = self.file_handler["/data"]
assert isinstance(data, h5py.Dataset)
data = data[self.grid_view.get_3d_read_slice()]
# Should be int16 to allow for -1 which indicates background, i.e.
# don't build anything, but AustinMan/Woman maybe uint16
if data.dtype != "int16":
data = data.astype("int16")
self.grid_view.set_solid(data + self.num_existing_materials)
def get_data(self) -> Optional[npt.NDArray[np.int16]]:
if self.grid_view is None:
return None
data = self.file_handler["/data"]
assert isinstance(data, h5py.Dataset)
data = data[self.grid_view.get_3d_read_slice()]
# Should be int16 to allow for -1 which indicates background, i.e.
# don't build anything, but AustinMan/Woman maybe uint16
if data.dtype != "int16":
data = data.astype("int16")
return data + self.num_existing_materials
def read_rigidE(self):
if self.grid_view is None:
return
rigidE = self.file_handler["/rigidE"]
assert isinstance(rigidE, h5py.Dataset)
dset_slice = self.grid_view.get_3d_read_slice()
self.grid_view.set_rigidE(rigidE[:, dset_slice[0], dset_slice[1], dset_slice[2]])
def read_rigidH(self):
if self.grid_view is None:
return
rigidH = self.file_handler["/rigidH"]
assert isinstance(rigidH, h5py.Dataset)
dset_slice = self.grid_view.get_3d_read_slice()
self.grid_view.set_rigidH(rigidH[:, dset_slice[0], dset_slice[1], dset_slice[2]])
def read_ID(self):
if self.grid_view is None:
return
ID = self.file_handler["/ID"]
assert isinstance(ID, h5py.Dataset)
dset_slice = self.grid_view.get_3d_read_slice(upper_bound_exclusive=False)
self.grid_view.set_ID(
ID[:, dset_slice[0], dset_slice[1], dset_slice[2]] + self.num_existing_materials
)

查看文件

@@ -23,13 +23,13 @@ import numpy as np
from gprMax._version import __version__
from gprMax.cython.geometry_outputs import get_line_properties
from gprMax.geometry_outputs.geometry_views import GeometryView, Metadata, MPIMetadata
from gprMax.geometry_outputs.grid_view import GridType, MPIGridView
from gprMax.grid.fdtd_grid import FDTDGrid
from gprMax.grid.mpi_grid import MPIGrid
from gprMax.output_controllers.geometry_views import GeometryView, Metadata, MPIMetadata
from gprMax.output_controllers.grid_view import GridType, MPIGridView
from gprMax.subgrids.grid import SubGridBaseGrid
from gprMax.vtkhdf_filehandlers.vtk_unstructured_grid import VtkUnstructuredGrid
from gprMax.vtkhdf_filehandlers.vtkhdf import VtkCellType
from gprMax.vtkhdf_filehandlers.vtkhdf import VtkCellType, VtkHdfFile
logger = logging.getLogger(__name__)
@@ -131,18 +131,18 @@ class MPIGeometryViewLines(GeometryViewLines[MPIGrid]):
ID = self.grid_view.get_ID()
x = np.arange(self.grid_view.gx + 1, dtype=np.float64)
y = np.arange(self.grid_view.gy + 1, dtype=np.float64)
z = np.arange(self.grid_view.gz + 1, dtype=np.float64)
x = np.arange(self.grid_view.nx + 1, dtype=np.float64)
y = np.arange(self.grid_view.ny + 1, dtype=np.float64)
z = np.arange(self.grid_view.nz + 1, dtype=np.float64)
coords = np.meshgrid(x, y, z, indexing="ij")
self.points = np.vstack(list(map(np.ravel, coords))).T
self.points += self.grid_view.global_start
self.points += self.grid_view.global_start + self.grid_view.offset
self.points *= self.grid_view.step * self.grid.dl
# Each point is the 'source' for 3 lines.
# NB: Excluding points at the far edge of the geometry as those
# are the 'source' for no lines
n_lines = 3 * np.prod(self.grid_view.global_size)
n_lines = 3 * np.prod(self.grid_view.size)
self.cell_types = np.full(n_lines, VtkCellType.LINE)
self.cell_offsets = np.arange(0, 2 * n_lines + 2, 2, dtype=np.intc)
@@ -182,5 +182,11 @@ class MPIGeometryViewLines(GeometryViewLines[MPIGrid]):
self.cell_offsets,
comm=self.grid_view.comm,
) as f:
self.metadata.write_to_vtkhdf(f)
f.add_cell_data("Material", self.material_data, self.grid_view.offset)
f.add_cell_data("Material", self.material_data)
# Write metadata in serial as it contains variable length
# strings which currently cannot be written by HDF5 using
# parallel I/O
if self.grid_view.comm.rank == 0:
with VtkHdfFile(self.filename, VtkUnstructuredGrid.TYPE, mode="r+") as f:
self.metadata.write_to_vtkhdf(f)

查看文件

@@ -22,11 +22,12 @@ import logging
import numpy as np
from gprMax._version import __version__
from gprMax.geometry_outputs.geometry_views import GeometryView, Metadata, MPIMetadata
from gprMax.geometry_outputs.grid_view import GridType, MPIGridView
from gprMax.grid.mpi_grid import MPIGrid
from gprMax.output_controllers.geometry_views import GeometryView, Metadata, MPIMetadata
from gprMax.output_controllers.grid_view import GridType, MPIGridView
from gprMax.subgrids.grid import SubGridBaseGrid
from gprMax.vtkhdf_filehandlers.vtk_image_data import VtkImageData
from gprMax.vtkhdf_filehandlers.vtkhdf import VtkHdfFile
logger = logging.getLogger(__name__)
@@ -99,5 +100,11 @@ class MPIGeometryViewVoxels(GeometryViewVoxels[MPIGrid]):
self.spacing,
comm=self.grid_view.comm,
) as f:
self.metadata.write_to_vtkhdf(f)
f.add_cell_data("Material", self.material_data, self.grid_view.offset)
# Write metadata in serial as it contains variable length
# strings which currently cannot be written by HDF5 using
# parallel I/O
if self.grid_view.comm.rank == 0:
with VtkHdfFile(self.filename, VtkImageData.TYPE, mode="r+") as f:
self.metadata.write_to_vtkhdf(f)

查看文件

@@ -31,8 +31,8 @@ from tqdm import tqdm
import gprMax.config as config
from gprMax._version import __version__
from gprMax.geometry_outputs.grid_view import GridType, GridView, MPIGridView
from gprMax.grid.mpi_grid import MPIGrid
from gprMax.output_controllers.grid_view import GridType, GridView, MPIGridView
from gprMax.receivers import Rx
from gprMax.sources import Source
from gprMax.utilities.utilities import get_terminal_width
@@ -179,7 +179,7 @@ class Metadata(Generic[GridType]):
file_handler.add_field_data("dx_dy_dz", self.dx_dy_dz)
file_handler.add_field_data("nx_ny_nz", self.nx_ny_nz)
self.write_material_ids(file_handler)
file_handler.add_field_data("material_ids", self.materials)
if not self.materials_only:
if self.pml_thickness is not None:
@@ -193,9 +193,6 @@ class Metadata(Generic[GridType]):
file_handler.add_field_data("receiver_ids", self.receiver_ids)
file_handler.add_field_data("receivers", self.receiver_positions)
def write_material_ids(self, file_handler: VtkHdfFile):
file_handler.add_field_data("material_ids", self.materials)
def pml_gv_comment(self) -> Optional[npt.NDArray[np.int64]]:
grid = self.grid
@@ -292,22 +289,3 @@ class MPIMetadata(Metadata[MPIGrid]):
objects = dict(sorted(objects.items()))
return (list(objects.keys()), np.array(list(objects.values()))) if objects else None
def write_material_ids(self, file_handler: VtkHdfFile):
assert isinstance(self.grid_view, MPIGridView)
# Only rank 0 has all the material data. However, creating the
# 'material_ids' dataset is a collective operation, so all ranks
# need to know the shape and datatype of the dataset.
if self.materials is None:
buffer = np.empty(2, dtype=np.int32)
else:
shape = len(self.materials)
max_length = max([len(m) for m in self.materials])
buffer = np.array([shape, max_length], dtype=np.int32)
self.grid_view.comm.Bcast([buffer, MPI.INT32_T])
shape, max_length = buffer
dtype = h5py.string_dtype(length=int(max_length))
file_handler.add_field_data("material_ids", self.materials, shape=(shape,), dtype=dtype)

查看文件

@@ -19,7 +19,7 @@
import logging
from itertools import chain
from typing import Generic, Tuple
from typing import Generic, List, Tuple
import numpy as np
import numpy.typing as npt
@@ -117,9 +117,12 @@ class GridView(Generic[GridType]):
def nz(self) -> int:
return self.size[2]
def get_slice(self, dimension: int, upper_bound_exclusive: bool = True) -> slice:
def getter_slice(self, dimension: int, upper_bound_exclusive: bool = True) -> slice:
"""Create a slice object for the specified dimension.
This is used to slice and get a view of arrays owned by the
grid.
Args:
dimension: Dimension to create the slice object for. Values
0, 1, and 2 map to the x, y, and z dimensions
@@ -138,7 +141,150 @@ class GridView(Generic[GridType]):
return slice(self.start[dimension], stop, self.step[dimension])
def slice_array(self, array: npt.NDArray, upper_bound_exclusive: bool = True) -> npt.NDArray:
def setter_slice(self, dimension: int, upper_bound_exclusive: bool = True) -> slice:
"""Create a slice object for the specified dimension.
This is used to slice arrays owned by the grid in order to set
their value.
Args:
dimension: Dimension to create the slice object for. Values
0, 1, and 2 map to the x, y, and z dimensions
respectively.
upper_bound_exclusive: Optionally specify if the upper bound
of the slice should be exclusive or inclusive. Defaults
to True.
Returns:
slice: Slice object
"""
return self.getter_slice(dimension, upper_bound_exclusive)
def get_output_slice(self, dimension: int, upper_bound_exclusive: bool = True) -> slice:
"""Create an output slice object for the specified dimension.
This provides a slice of the grid view for the section of the
grid view managed by this process. This can be used when writing
out arrays provided by the grid view as part of a collective
operation.
For example:
```
dset_slice = (
grid_view.get_output_slice(0),
grid_view.get_output_slice(1),
grid_view.get_output_slice(2),
)
dset[dset_slice] = grid_view.get_solid()
```
Args:
dimension: Dimension to create the slice object for. Values
0, 1, and 2 map to the x, y, and z dimensions
respectively.
upper_bound_exclusive: Optionally specify if the upper bound
of the slice should be exclusive or inclusive. Defaults
to True.
Returns:
slice: Slice object
"""
if upper_bound_exclusive:
size = self.size[dimension]
else:
size = self.size[dimension] + 1
return slice(0, size)
def get_3d_output_slice(self, upper_bound_exclusive: bool = True) -> Tuple[slice, slice, slice]:
"""Create a 3D output slice object.
This provides a slice of the grid view for the section of the
grid view managed by this process. This can be used when writing
out arrays provided by the grid view as part of a collective
operation.
For example:
`dset[grid_view.get_3d_output_slice()] = grid_view.get_solid()`
Args:
upper_bound_exclusive: Optionally specify if the upper bound
of the slice should be exclusive or inclusive. Defaults
to True.
Returns:
slice: 3D Slice object
"""
return (
self.get_output_slice(0, upper_bound_exclusive),
self.get_output_slice(1, upper_bound_exclusive),
self.get_output_slice(2, upper_bound_exclusive),
)
def get_read_slice(self, dimension: int, upper_bound_exclusive: bool = True) -> slice:
"""Create a read slice object for the specified dimension.
This provides a slice of the grid view for the section of the
grid view managed by this rank. This can be used when reading
arrays provided by the grid view as part of a collective
operation.
For example:
```
dset_slice = (
grid_view.get_read_slice(0),
grid_view.get_read_slice(1),
grid_view.get_read_slice(2),
)
grid_view.set_solid(dset[dset_slice])
```
Args:
dimension: Dimension to create the slice object for. Values
0, 1, and 2 map to the x, y, and z dimensions
respectively.
upper_bound_exclusive: Optionally specify if the upper bound
of the slice should be exclusive or inclusive. Defaults
to True.
Returns:
slice: Slice object
"""
return self.get_output_slice(dimension, upper_bound_exclusive)
def get_3d_read_slice(self, upper_bound_exclusive: bool = True) -> Tuple[slice, slice, slice]:
"""Create a 3D read slice object.
This provides a slice of the grid view for the section of the
grid view managed by this rank. This can be used when reading
arrays provided by the grid view as part of a collective
operation.
For example:
```
solid = dset[grid_view.get_3d_read_slice()]
grid_view.set_solid(solid)
```
Args:
upper_bound_exclusive: Optionally specify if the upper bound
of the slice should be exclusive or inclusive. Defaults
to True.
Returns:
slice: 3D Slice object
"""
return (
self.get_read_slice(0, upper_bound_exclusive),
self.get_read_slice(1, upper_bound_exclusive),
self.get_read_slice(2, upper_bound_exclusive),
)
def get_array_slice(
self, array: npt.NDArray, upper_bound_exclusive: bool = True
) -> npt.NDArray:
"""Slice an array according to the dimensions of the grid view.
It is assumed the last 3 dimensions of the provided array
@@ -161,12 +307,41 @@ class GridView(Generic[GridType]):
return np.ascontiguousarray(
array[
...,
self.get_slice(0, upper_bound_exclusive),
self.get_slice(1, upper_bound_exclusive),
self.get_slice(2, upper_bound_exclusive),
self.getter_slice(0, upper_bound_exclusive),
self.getter_slice(1, upper_bound_exclusive),
self.getter_slice(2, upper_bound_exclusive),
]
)
def set_array_slice(
self, array: npt.NDArray, value: npt.NDArray, upper_bound_exclusive: bool = True
):
"""Set value of an array according to the dimensions of the grid view.
It is assumed the last 3 dimensions of the array represent the
x, y, z spacial information. Other dimensions will not be
sliced.
E.g. If setting the value of an array of shape (10, 100, 50, 50)
the new values should have shape (10, x, y, z) where x, y, and z
are specified by the size/shape of the grid view.
Args:
array: Array to set the values of. Must have at least 3
dimensions.
value: New values. Its shape must match 'array' after
'array' has been sliced.
upper_bound_exclusive: Optionally specify if the upper bound
of the slice should be exclusive or inclusive. Defaults
to True.
"""
array[
...,
self.setter_slice(0, upper_bound_exclusive),
self.setter_slice(1, upper_bound_exclusive),
self.setter_slice(2, upper_bound_exclusive),
] = value
def initialise_materials(self, filter_materials: bool = True):
"""Create a new ID map for materials in the grid view.
@@ -228,32 +403,64 @@ class GridView(Generic[GridType]):
ID: View of the ID array.
"""
if self._ID is None or force_refresh:
self._ID = self.slice_array(self.grid.ID, upper_bound_exclusive=False)
self._ID = self.get_array_slice(self.grid.ID, upper_bound_exclusive=False)
return self._ID
def set_ID(self, value: npt.NDArray[np.uint32]):
"""Set the value of the ID array.
Args:
value: Array of new values.
"""
self.set_array_slice(self.grid.ID, value, upper_bound_exclusive=False)
def get_solid(self) -> npt.NDArray[np.uint32]:
"""Get a view of the solid array.
Returns:
solid: View of the solid array
solid: View of the solid array.
"""
return self.slice_array(self.grid.solid)
return self.get_array_slice(self.grid.solid)
def set_solid(self, value: npt.NDArray[np.uint32]):
"""Set the value of the solid array.
Args:
value: Array of new values.
"""
self.set_array_slice(self.grid.solid, value)
def get_rigidE(self) -> npt.NDArray[np.int8]:
"""Get a view of the rigidE array.
Returns:
rigidE: View of the rigidE array
rigidE: View of the rigidE array.
"""
return self.slice_array(self.grid.rigidE)
return self.get_array_slice(self.grid.rigidE)
def set_rigidE(self, value: npt.NDArray[np.uint32]):
"""Set the value of the rigidE array.
Args:
value: Array of new values.
"""
self.set_array_slice(self.grid.rigidE, value)
def get_rigidH(self) -> npt.NDArray[np.int8]:
"""Get a view of the rigidH array.
Returns:
rigidH: View of the rigidH array
rigidH: View of the rigidH array.
"""
return self.slice_array(self.grid.rigidH)
return self.get_array_slice(self.grid.rigidH)
def set_rigidH(self, value: npt.NDArray[np.uint32]):
"""Set the value of the rigidH array.
Args:
value: Array of new values.
"""
self.set_array_slice(self.grid.rigidH, value)
def get_Ex(self) -> npt.NDArray[np.float32]:
"""Get a view of the Ex array.
@@ -261,7 +468,7 @@ class GridView(Generic[GridType]):
Returns:
Ex: View of the Ex array
"""
return self.slice_array(self.grid.Ex, upper_bound_exclusive=False)
return self.get_array_slice(self.grid.Ex, upper_bound_exclusive=False)
def get_Ey(self) -> npt.NDArray[np.float32]:
"""Get a view of the Ey array.
@@ -269,7 +476,7 @@ class GridView(Generic[GridType]):
Returns:
Ey: View of the Ey array
"""
return self.slice_array(self.grid.Ey, upper_bound_exclusive=False)
return self.get_array_slice(self.grid.Ey, upper_bound_exclusive=False)
def get_Ez(self) -> npt.NDArray[np.float32]:
"""Get a view of the Ez array.
@@ -277,7 +484,7 @@ class GridView(Generic[GridType]):
Returns:
Ez: View of the Ez array
"""
return self.slice_array(self.grid.Ez, upper_bound_exclusive=False)
return self.get_array_slice(self.grid.Ez, upper_bound_exclusive=False)
def get_Hx(self) -> npt.NDArray[np.float32]:
"""Get a view of the Hx array.
@@ -285,7 +492,7 @@ class GridView(Generic[GridType]):
Returns:
Hx: View of the Hx array
"""
return self.slice_array(self.grid.Hx, upper_bound_exclusive=False)
return self.get_array_slice(self.grid.Hx, upper_bound_exclusive=False)
def get_Hy(self) -> npt.NDArray[np.float32]:
"""Get a view of the Hy array.
@@ -293,7 +500,7 @@ class GridView(Generic[GridType]):
Returns:
Hy: View of the Hy array
"""
return self.slice_array(self.grid.Hy, upper_bound_exclusive=False)
return self.get_array_slice(self.grid.Hy, upper_bound_exclusive=False)
def get_Hz(self) -> npt.NDArray[np.float32]:
"""Get a view of the Hz array.
@@ -301,7 +508,7 @@ class GridView(Generic[GridType]):
Returns:
Hz: View of the Hz array
"""
return self.slice_array(self.grid.Hz, upper_bound_exclusive=False)
return self.get_array_slice(self.grid.Hz, upper_bound_exclusive=False)
class MPIGridView(GridView[MPIGrid]):
@@ -382,7 +589,9 @@ class MPIGridView(GridView[MPIGrid]):
)
# Calculate offset for the local grid view
self.offset = self.grid.local_to_global_coordinate(self.start) - self.global_start
self.offset = (
self.grid.local_to_global_coordinate(self.start) - self.global_start
) // self.step
# Update local size
self.size = np.ceil((self.stop - self.start) / self.step).astype(np.int32)
@@ -405,9 +614,12 @@ class MPIGridView(GridView[MPIGrid]):
def gz(self) -> int:
return self.global_size[2]
def get_slice(self, dimension: int, upper_bound_exclusive: bool = True) -> slice:
def getter_slice(self, dimension: int, upper_bound_exclusive: bool = True) -> slice:
"""Create a slice object for the specified dimension.
This is used to slice and get a view of arrays owned by the
grid.
Args:
dimension: Dimension to create the slice object for. Values
0, 1, and 2 map to the x, y, and z dimensions
@@ -428,6 +640,35 @@ class MPIGridView(GridView[MPIGrid]):
return slice(self.start[dimension], stop, self.step[dimension])
def setter_slice(self, dimension: int, upper_bound_exclusive: bool = True) -> slice:
"""Create a slice object for the specified dimension.
This is used to slice arrays owned by the grid in order to set
their value.
Args:
dimension: Dimension to create the slice object for. Values
0, 1, and 2 map to the x, y, and z dimensions
respectively.
upper_bound_exclusive: Optionally specify if the upper bound
of the slice should be exclusive or inclusive. Defaults
to True.
Returns:
slice: Slice object
"""
if upper_bound_exclusive:
stop = self.stop[dimension]
else:
stop = self.stop[dimension] + self.step[dimension]
if self.has_negative_neighbour[dimension]:
start = self.start[dimension] - self.step[dimension]
else:
start = self.start[dimension]
return slice(start, stop, self.step[dimension])
def get_output_slice(self, dimension: int, upper_bound_exclusive: bool = True) -> slice:
"""Create an output slice object for the specified dimension.
@@ -465,34 +706,52 @@ class MPIGridView(GridView[MPIGrid]):
# have a positive neighbour
size = self.size[dimension] + 1
offset = self.offset[dimension] // self.step[dimension]
offset = self.offset[dimension]
return slice(offset, offset + size)
def get_3d_output_slice(self, upper_bound_exclusive: bool = True) -> Tuple[slice, slice, slice]:
"""Create a 3D output slice object.
def get_read_slice(self, dimension: int, upper_bound_exclusive: bool = True) -> slice:
"""Create a read slice object for the specified dimension.
This provides a slice of the grid view for the section of the
grid view managed by this rank. This can be used when writing
out arrays provided by the grid view as part of a collective
grid view managed by this rank. This can be used when reading
arrays provided by the grid view as part of a collective
operation.
For example:
`dset[grid_view.get_3d_output_slice()] = grid_view.get_solid()`
```
dset_slice = (
grid_view.get_read_slice(0),
grid_view.get_read_slice(1),
grid_view.get_read_slice(2),
)
grid_view.get_solid()[:] = dset[dset_slice]
```
Args:
dimension: Dimension to create the slice object for. Values
0, 1, and 2 map to the x, y, and z dimensions
respectively.
upper_bound_exclusive: Optionally specify if the upper bound
of the slice should be exclusive or inclusive. Defaults
to True.
Returns:
slice: 3D Slice object
slice: Slice object
"""
return (
self.get_output_slice(0, upper_bound_exclusive),
self.get_output_slice(1, upper_bound_exclusive),
self.get_output_slice(2, upper_bound_exclusive),
)
if upper_bound_exclusive:
size = self.size[dimension]
else:
size = self.size[dimension] + 1
offset = self.offset[dimension]
if self.has_negative_neighbour[dimension]:
offset -= 1
size += 1
return slice(offset, offset + size)
def initialise_materials(self, filter_materials: bool = True):
"""Create a new ID map for materials in the grid view.
@@ -520,6 +779,8 @@ class MPIGridView(GridView[MPIGrid]):
# Send all materials to the coordinating rank
materials_by_rank = self.comm.gather(local_materials, root=0)
requests: List[MPI.Request] = []
if materials_by_rank is not None:
# Filter out duplicate materials and sort by material ID
all_materials = np.fromiter(chain.from_iterable(materials_by_rank), dtype=Material)
@@ -530,7 +791,10 @@ class MPIGridView(GridView[MPIGrid]):
# new IDs of each material it sent to send back
for rank in range(1, self.comm.size):
new_material_ids = np.where(np.isin(self.materials, materials_by_rank[rank]))[0]
self.comm.Isend([new_material_ids.astype(np.int32), MPI.INT], rank)
# astype() always returns a copy, so it should be safe to use Isend here
request = self.comm.Isend([new_material_ids.astype(np.int32), MPI.INT], rank)
requests.append(request)
new_material_ids = np.where(np.isin(self.materials, materials_by_rank[0]))[0]
new_material_ids = new_material_ids.astype(np.int32)
@@ -548,3 +812,6 @@ class MPIGridView(GridView[MPIGrid]):
# Create map from material ID to 0 - number of materials
self.map_materials_func = np.vectorize(lambda id: materials_map[id])
if len(requests) > 0:
requests[0].Waitall(requests)

查看文件

@@ -22,7 +22,7 @@ import itertools
import logging
import sys
from collections import OrderedDict
from typing import Any, Iterable, List, Tuple, Union
from typing import Any, Iterable, List, Optional, Tuple, Union
import numpy as np
import numpy.typing as npt
@@ -33,7 +33,8 @@ from typing_extensions import TypeVar
from gprMax import config
from gprMax.cython.pml_build import pml_average_er_mr
from gprMax.cython.yee_cell_build import build_electric_components, build_magnetic_components
from gprMax.fractals import FractalVolume
from gprMax.fractals.fractal_surface import FractalSurface
from gprMax.fractals.fractal_volume import FractalVolume
from gprMax.materials import ListMaterial, Material, PeplinskiSoil, RangeMaterial, process_materials
from gprMax.pml import CFS, PML, print_pml_info
from gprMax.receivers import Rx
@@ -186,6 +187,34 @@ class FDTDGrid:
self.pmls["thickness"]["ymax"] = int(thickness[4])
self.pmls["thickness"]["zmax"] = int(thickness[5])
def add_fractal_volume(
self,
xs: int,
xf: int,
ys: int,
yf: int,
zs: int,
zf: int,
frac_dim: float,
seed: Optional[int],
) -> FractalVolume:
volume = FractalVolume(xs, xf, ys, yf, zs, zf, frac_dim, seed)
self.fractalvolumes.append(volume)
return volume
def create_fractal_surface(
self,
xs: int,
xf: int,
ys: int,
yf: int,
zs: int,
zf: int,
frac_dim: float,
seed: Optional[int],
) -> FractalSurface:
return FractalSurface(xs, xf, ys, yf, zs, zf, frac_dim, seed)
def add_source(self, source: Source):
if isinstance(source, VoltageSource):
self.voltagesources.append(source)
@@ -748,7 +777,7 @@ class FDTDGrid:
mem_use = 0
for vol in self.fractalvolumes:
mem_use += vol.nx * vol.ny * vol.nz * vol.dtype.itemsize
mem_use += np.prod(vol.start) * vol.dtype.itemsize
for surface in vol.fractalsurfaces:
surfacedims = surface.get_surface_dims()
mem_use += surfacedims[0] * surfacedims[1] * surface.dtype.itemsize

查看文件

@@ -19,7 +19,6 @@
import itertools
import logging
from enum import IntEnum, unique
from typing import List, Optional, Tuple, TypeVar, Union
import numpy as np
@@ -29,29 +28,19 @@ from numpy import ndarray
from gprMax import config
from gprMax.cython.pml_build import pml_sum_er_mr
from gprMax.fractals.fractal_surface import MPIFractalSurface
from gprMax.fractals.fractal_volume import MPIFractalVolume
from gprMax.grid.fdtd_grid import FDTDGrid
from gprMax.pml import MPIPML, PML
from gprMax.receivers import Rx
from gprMax.sources import Source
from gprMax.utilities.mpi import Dim, Dir
logger = logging.getLogger(__name__)
CoordType = TypeVar("CoordType", bound=Union[Rx, Source])
@unique
class Dim(IntEnum):
X = 0
Y = 1
Z = 2
@unique
class Dir(IntEnum):
NEG = 0
POS = 1
class MPIGrid(FDTDGrid):
HALO_SIZE = 1
COORDINATOR_RANK = 0
@@ -70,13 +59,15 @@ class MPIGrid(FDTDGrid):
self.negative_halo_offset = np.zeros(3, dtype=np.bool_)
self.global_size = np.zeros(3, dtype=np.int32)
self.neighbours = np.full((3, 2), -1, dtype=int)
self.neighbours = np.full((3, 2), -1, dtype=np.int32)
self.neighbours[Dim.X] = self.comm.Shift(direction=Dim.X, disp=1)
self.neighbours[Dim.Y] = self.comm.Shift(direction=Dim.Y, disp=1)
self.neighbours[Dim.Z] = self.comm.Shift(direction=Dim.Z, disp=1)
self.send_halo_map = np.empty((3, 2), dtype=MPI.Datatype)
self.recv_halo_map = np.empty((3, 2), dtype=MPI.Datatype)
self.send_requests: List[MPI.Request] = []
self.recv_requests: List[MPI.Request] = []
super().__init__()
@@ -129,6 +120,34 @@ class MPIGrid(FDTDGrid):
if self.has_neighbour(Dim.Z, Dir.POS):
self.pmls["thickness"]["zmax"] = 0
def add_fractal_volume(
self,
xs: int,
xf: int,
ys: int,
yf: int,
zs: int,
zf: int,
frac_dim: float,
seed: Optional[int],
) -> MPIFractalVolume:
volume = MPIFractalVolume(xs, xf, ys, yf, zs, zf, frac_dim, seed, self.comm, self.size)
self.fractalvolumes.append(volume)
return volume
def create_fractal_surface(
self,
xs: int,
xf: int,
ys: int,
yf: int,
zs: int,
zf: int,
frac_dim: float,
seed: Optional[int],
) -> MPIFractalSurface:
return MPIFractalSurface(xs, xf, ys, yf, zs, zf, frac_dim, seed, self.comm, self.size)
def is_coordinator(self) -> bool:
"""Test if the current rank is the coordinator.
@@ -352,15 +371,10 @@ class MPIGrid(FDTDGrid):
"""
neighbour = self.neighbours[dim][dir]
if neighbour != -1:
self.comm.Sendrecv(
[array, self.send_halo_map[dim][dir]],
neighbour,
0,
[array, self.recv_halo_map[dim][dir]],
neighbour,
0,
None,
)
send_request = self.comm.Isend([array, self.send_halo_map[dim][dir]], neighbour)
recv_request = self.comm.Irecv([array, self.recv_halo_map[dim][dir]], neighbour)
self.send_requests.append(send_request)
self.recv_requests.append(recv_request)
def _halo_swap_by_dimension(self, array: ndarray, dim: Dim):
"""Perform halo swaps in the specifed dimension.
@@ -393,17 +407,45 @@ class MPIGrid(FDTDGrid):
def halo_swap_electric(self):
"""Perform halo swaps for electric field arrays."""
# Ensure send requests for the magnetic field have completed
# The magnetic field arrays may change after this halo swap in
# the magnetic update step
if len(self.send_requests) > 0:
self.send_requests[0].Waitall(self.send_requests)
self.send_requests = []
self._halo_swap_array(self.Ex)
self._halo_swap_array(self.Ey)
self._halo_swap_array(self.Ez)
# Wait for all receive requests to complete
# Don't need to wait for send requests yet as the electric
# field arrays won't be changed during the magnetic update steps
if len(self.recv_requests) > 0:
self.recv_requests[0].Waitall(self.recv_requests)
self.recv_requests = []
def halo_swap_magnetic(self):
"""Perform halo swaps for magnetic field arrays."""
# Ensure send requests for the electric field have completed
# The electric field arrays will change after this halo swap in
# the electric update step
if len(self.send_requests) > 0:
self.send_requests[0].Waitall(self.send_requests)
self.send_requests = []
self._halo_swap_array(self.Hx)
self._halo_swap_array(self.Hy)
self._halo_swap_array(self.Hz)
# Wait for all receive requests to complete
# Don't need to wait for send requests yet as the magnetic
# field arrays won't be changed during the electric update steps
if len(self.recv_requests) > 0:
self.recv_requests[0].Waitall(self.recv_requests)
self.recv_requests = []
def _construct_pml(self, pml_ID: str, thickness: int) -> MPIPML:
"""Build instance of MPIPML and set the MPI communicator.

查看文件

@@ -422,6 +422,10 @@ def process_geometrycmds(geometry):
)
raise ValueError
# Only build objects attached to the current fractal box
if tmp[12] != ID:
continue
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
frac_dim = float(tmp[7])
@@ -463,6 +467,10 @@ def process_geometrycmds(geometry):
)
raise ValueError
# Only build objects attached to the current fractal box
if tmp[8] != ID:
continue
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
depth = float(tmp[7])
@@ -478,6 +486,10 @@ def process_geometrycmds(geometry):
)
raise ValueError
# Only build objects attached to the current fractal box
if tmp[11] != ID:
continue
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
frac_dim = float(tmp[7])

查看文件

@@ -73,8 +73,10 @@ class Material:
)
elif self.is_compound_material() and value.is_compound_material():
return self.ID < value.ID
elif not self.is_compound_material() and not value.is_compound_material():
return self.numID < value.numID
else:
return value.is_compound_material() or self.numID < value.numID
return value.is_compound_material()
def __gt__(self, value: "Material") -> bool:
"""Greater than comparator for two Materials.
@@ -90,8 +92,10 @@ class Material:
)
elif self.is_compound_material() and value.is_compound_material():
return self.ID > value.ID
elif not self.is_compound_material() and not value.is_compound_material():
return self.numID > value.numID
else:
return self.is_compound_material() or self.numID > value.numID
return self.is_compound_material()
def is_compound_material(self) -> bool:
"""Check if a material is a compound material.

查看文件

@@ -27,12 +27,12 @@ import numpy.typing as npt
import psutil
from colorama import Fore, Style, init
from gprMax.geometry_outputs.geometry_objects import GeometryObject
from gprMax.geometry_outputs.geometry_view_lines import GeometryViewLines
from gprMax.geometry_outputs.geometry_view_voxels import GeometryViewVoxels
from gprMax.geometry_outputs.geometry_views import GeometryView, save_geometry_views
from gprMax.grid.cuda_grid import CUDAGrid
from gprMax.grid.opencl_grid import OpenCLGrid
from gprMax.output_controllers.geometry_objects import GeometryObject
from gprMax.output_controllers.geometry_view_lines import GeometryViewLines
from gprMax.output_controllers.geometry_view_voxels import GeometryViewVoxels
from gprMax.output_controllers.geometry_views import GeometryView, save_geometry_views
from gprMax.subgrids.grid import SubGridBaseGrid
init()
@@ -98,6 +98,10 @@ class Model:
def nz(self, value: int):
self.G.nz = value
@property
def cells(self) -> np.uint64:
return np.prod(self.G.size, dtype=np.uint64)
@property
def dx(self) -> float:
return self.G.dl[0]
@@ -472,10 +476,16 @@ class Model:
# Print information about and check OpenMP threads
if config.sim_config.general["solver"] == "cpu":
if config.sim_config.mpi:
backend = "MPI+OpenMP"
layout = f"{np.prod(config.sim_config.mpi)} MPI rank(s) and {config.get_model_config().ompthreads} thread(s) per rank"
else:
backend = "OpenMP"
layout = f"{config.get_model_config().ompthreads} thread(s)"
logger.basic(
f"Model {config.sim_config.current_model + 1}/{config.sim_config.model_end} "
f"on {config.sim_config.hostinfo['hostname']} "
f"with OpenMP backend using {config.get_model_config().ompthreads} thread(s)"
f"with {backend} backend using {layout}"
)
if config.get_model_config().ompthreads > config.sim_config.hostinfo["physicalcores"]:
logger.warning(

查看文件

@@ -7,11 +7,11 @@ from mpi4py import MPI
from gprMax import config
from gprMax.fields_outputs import write_hdf5_outputfile
from gprMax.geometry_outputs.geometry_objects import MPIGeometryObject
from gprMax.geometry_outputs.geometry_view_lines import MPIGeometryViewLines
from gprMax.geometry_outputs.geometry_view_voxels import MPIGeometryViewVoxels
from gprMax.grid.mpi_grid import MPIGrid
from gprMax.model import Model
from gprMax.output_controllers.geometry_objects import MPIGeometryObject
from gprMax.output_controllers.geometry_view_lines import MPIGeometryViewLines
from gprMax.output_controllers.geometry_view_voxels import MPIGeometryViewVoxels
from gprMax.snapshots import MPISnapshot, Snapshot, save_snapshots
logger = logging.getLogger(__name__)

查看文件

@@ -1,5 +1,5 @@
# Copyright (C) 2015-2025: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, John Hartley,
# Authors: Craig Warren, Antonis Giannopoulos, John Hartley,
# and Nathan Mannall
#
# This file is part of gprMax.
@@ -57,7 +57,7 @@ class Scene:
Args:
user_object: user object to add to the scene. For example,
`gprMax.user_objects.cmds_singleuse.Domain`
`gprMax.Domain`
"""
# Check for
if isinstance(user_object, SubGridUserBase):

查看文件

@@ -1,5 +1,5 @@
# Copyright (C) 2015-2025: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, John Hartley,
# Authors: Craig Warren, Antonis Giannopoulos, John Hartley,
# and Nathan Mannall
#
# This file is part of gprMax.
@@ -19,7 +19,6 @@
import logging
import sys
from enum import IntEnum, unique
from pathlib import Path
from typing import Dict, Generic, List
@@ -30,8 +29,9 @@ from mpi4py import MPI
from tqdm import tqdm
import gprMax.config as config
from gprMax.geometry_outputs.grid_view import GridType, GridView, MPIGridView
from gprMax.grid.mpi_grid import MPIGrid
from gprMax.output_controllers.grid_view import GridType, GridView, MPIGridView
from gprMax.utilities.mpi import Dim, Dir
from ._version import __version__
from .cython.snapshots import calculate_snapshot_fields
@@ -305,19 +305,6 @@ class Snapshot(Generic[GridType]):
f.close()
@unique
class Dim(IntEnum):
X = 0
Y = 1
Z = 2
@unique
class Dir(IntEnum):
NEG = 0
POS = 1
class MPISnapshot(Snapshot[MPIGrid]):
H_TAG = 0
EX_TAG = 1

查看文件

@@ -21,7 +21,8 @@ import logging
import numpy as np
from gprMax.fractals import FractalSurface, Grass
from gprMax.fractals.fractal_surface import FractalSurface
from gprMax.fractals.grass import Grass
from gprMax.grid.fdtd_grid import FDTDGrid
from gprMax.materials import create_grass
from gprMax.user_objects.rotatable import RotatableMixin
@@ -93,134 +94,106 @@ class AddGrass(RotatableMixin, GeometryUserObject):
# Get the correct fractal volume
volumes = [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id]
try:
if volumes:
volume = volumes[0]
except NameError:
logger.exception(f"{self.__str__()} cannot find FractalBox {fractal_box_id}")
raise
else:
raise ValueError(f"{self.__str__()} cannot find FractalBox {fractal_box_id}")
uip = self._create_uip(grid)
_, p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
discretised_p1, discretised_p2 = uip.check_output_object_bounds(p1, p2, self.__str__())
xs, ys, zs = discretised_p1
xf, yf, zf = discretised_p2
if frac_dim < 0:
logger.exception(
raise ValueError(
f"{self.__str__()} requires a positive value for the fractal dimension"
)
raise ValueError
if limits[0] < 0 or limits[1] < 0:
logger.exception(
raise ValueError(
f"{self.__str__()} requires a positive value for the minimum and maximum heights for grass blades"
)
raise ValueError
# Check for valid orientations
if np.count_nonzero(discretised_p1 == discretised_p2) != 1:
raise ValueError(f"{self.__str__()} dimensions are not specified correctly")
if xs == xf:
if ys == yf or zs == zf:
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError
if xs not in [volume.xs, volume.xf]:
logger.exception(
f"{self.__str__()} must specify external surfaces on a fractal box"
)
raise ValueError
fractalrange = (
round_value(limits[0] / grid.dx),
round_value(limits[1] / grid.dx),
)
# xminus surface
if xs == volume.xs:
logger.exception(
raise ValueError(
f"{self.__str__()} grass can only be specified on surfaces in the positive axis direction"
)
raise ValueError
# xplus surface
elif xf == volume.xf:
if fractalrange[1] > grid.nx:
logger.exception(
f"{self.__str__()} cannot apply grass to "
"fractal box as it would exceed the domain "
"size in the x direction"
)
raise ValueError
lower_bound = uip.discretise_point((limits[0], 0, 0))
upper_bound = uip.discretise_point((limits[1], p2[1], p2[2]))
uip.point_within_bounds(upper_bound, self.__str__())
fractalrange = (lower_bound[0], upper_bound[0])
requestedsurface = "xplus"
else:
raise ValueError(
f"{self.__str__()} must specify external surfaces on a fractal box"
)
elif ys == yf:
if zs == zf:
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError
if ys not in [volume.ys, volume.yf]:
logger.exception(
f"{self.__str__()} must specify external surfaces on a fractal box"
)
raise ValueError
fractalrange = (
round_value(limits[0] / grid.dy),
round_value(limits[1] / grid.dy),
)
# yminus surface
if ys == volume.ys:
logger.exception(
raise ValueError(
f"{self.__str__()} grass can only be specified on surfaces in the positive axis direction"
)
raise ValueError
# yplus surface
elif yf == volume.yf:
if fractalrange[1] > grid.ny:
logger.exception(
f"{self.__str__()} cannot apply grass to "
"fractal box as it would exceed the domain "
"size in the y direction"
)
raise ValueError
lower_bound = uip.discretise_point((0, limits[0], 0))
upper_bound = uip.discretise_point((p2[0], limits[1], p2[2]))
uip.point_within_bounds(upper_bound, self.__str__())
fractalrange = (lower_bound[1], upper_bound[1])
requestedsurface = "yplus"
elif zs == zf:
if zs not in [volume.zs, volume.zf]:
logger.exception(
else:
raise ValueError(
f"{self.__str__()} must specify external surfaces on a fractal box"
)
raise ValueError
fractalrange = (
round_value(limits[0] / grid.dz),
round_value(limits[1] / grid.dz),
)
elif zs == zf:
# zminus surface
if zs == volume.zs:
logger.exception(
raise ValueError(
f"{self.__str__()} grass can only be specified on surfaces in the positive axis direction"
)
raise ValueError
# zplus surface
elif zf == volume.zf:
if fractalrange[1] > grid.nz:
logger.exception(
f"{self.__str__()} cannot apply grass to "
"fractal box as it would exceed the domain "
"size in the z direction"
)
raise ValueError
lower_bound = uip.discretise_point((0, 0, limits[0]))
upper_bound = uip.discretise_point((p2[0], p2[1], limits[1]))
uip.point_within_bounds(upper_bound, self.__str__())
fractalrange = (lower_bound[2], upper_bound[2])
requestedsurface = "zplus"
else:
raise ValueError(
f"{self.__str__()} must specify external surfaces on a fractal box"
)
else:
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError
raise ValueError(f"{self.__str__()} dimensions are not specified correctly")
surface = FractalSurface(xs, xf, ys, yf, zs, zf, frac_dim, seed)
surface = grid.create_fractal_surface(xs, xf, ys, yf, zs, zf, frac_dim, seed)
surface.ID = "grass"
surface.surfaceID = requestedsurface
# Create grass geometry parameters
# Add grass to the surface here as an MPIFractalSurface needs to
# know if grass is present when generate_fractal_surface() is
# called
g = Grass(n_blades, surface.seed)
surface.grass.append(g)
# Set the fractal range to scale the fractal distribution between zero and one
surface.fractalrange = (0, 1)
surface.operatingonID = volume.ID
surface.generate_fractal_surface()
if not surface.generate_fractal_surface():
return
if n_blades > surface.fractalsurface.shape[0] * surface.fractalsurface.shape[1]:
logger.exception(
raise ValueError(
f"{self.__str__()} the specified surface is not large "
"enough for the number of grass blades/roots specified"
)
raise ValueError
# Scale the distribution so that the summation is equal to one,
# i.e. a probability distribution
@@ -255,10 +228,6 @@ class AddGrass(RotatableMixin, GeometryUserObject):
surface.fractalrange[0], surface.fractalrange[1], size=1
)
# Create grass geometry parameters
g = Grass(n_blades, surface.seed)
surface.grass.append(g)
# Check to see if grass has been already defined as a material
if not any(x.ID == "grass" for x in grid.materials):
create_grass(grid)
@@ -267,18 +236,20 @@ class AddGrass(RotatableMixin, GeometryUserObject):
grass = next((x for x in grid.materials if x.ID == "grass"))
testgrass = next((x for x in grass.tau if x < grid.dt), None)
if testgrass:
logger.exception(
raise ValueError(
f"{self.__str__()} requires the time step for the "
"model to be less than the relaxation time required to model grass."
)
raise ValueError
volume.fractalsurfaces.append(surface)
p3 = uip.round_to_grid_static_point(p1)
p4 = uip.round_to_grid_static_point(p2)
logger.info(
f"{self.grid_name(grid)}{n_blades} blades of grass on surface from "
f"{xs * grid.dx:g}m, {ys * grid.dy:g}m, {zs * grid.dz:g}m, "
f"to {xf * grid.dx:g}m, {yf * grid.dy:g}m, {zf * grid.dz:g}m "
f"{p3[0]:g}m, {p3[1]:g}m, {p3[2]:g}m, "
f"to {p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m "
f"with fractal dimension {surface.dimension:g}, fractal seeding "
f"{surface.seed}, and range {limits[0]:g}m to {limits[1]:g}m, "
f"added to {surface.operatingonID}."

查看文件

@@ -21,11 +21,9 @@ import logging
import numpy as np
from gprMax.fractals import FractalSurface
from gprMax.grid.fdtd_grid import FDTDGrid
from gprMax.user_objects.rotatable import RotatableMixin
from gprMax.user_objects.user_objects import GeometryUserObject
from gprMax.utilities.utilities import round_value
from .cmds_geometry import rotate_2point_object
@@ -38,18 +36,19 @@ class AddSurfaceRoughness(RotatableMixin, GeometryUserObject):
Attributes:
p1: list of the lower left (x,y,z) coordinates of a surface on a
FractalBox class.
p2: list of the upper right (x,y,z) coordinates of a surface on a
FractalBox class.
frac_dim: float for the fractal dimension which, for an orthogonal
parallelepiped, should take values between zero and three.
weighting: list with weightings in the first and second direction of
the surface.
limits: ist to define lower and upper limits for a range over which
the surface roughness can vary.
fractal_box_id: string identifier for the FractalBox class
that the surface roughness should be applied to.
seed: (optional) float parameter which controls the seeding of the random
number generator used to create the fractals.
p2: list of the upper right (x,y,z) coordinates of a surface on
a FractalBox class.
frac_dim: float for the fractal dimension which, for an
orthogonal parallelepiped, should take values between zero
and three.
weighting: list with weightings in the first and second
direction of the surface.
limits: list to define lower and upper limits for a range over
which the surface roughness can vary.
fractal_box_id: string identifier for the FractalBox class that
the surface roughness should be applied to.
seed: (optional) float parameter which controls the seeding of
the random number generator used to create the fractals.
"""
@property
@@ -96,145 +95,111 @@ class AddSurfaceRoughness(RotatableMixin, GeometryUserObject):
if volumes:
volume = volumes[0]
else:
logger.exception(f"{self.__str__()} cannot find FractalBox {fractal_box_id}")
raise ValueError
raise ValueError(f"{self.__str__()} cannot find FractalBox {fractal_box_id}")
uip = self._create_uip(grid)
_, p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
discretised_p1, discretised_p2 = uip.check_output_object_bounds(p1, p2, self.__str__())
xs, ys, zs = discretised_p1
xf, yf, zf = discretised_p2
if frac_dim < 0:
logger.exception(
raise ValueError(
f"{self.__str__()} requires a positive value for the fractal dimension"
)
raise ValueError
if weighting[0] < 0:
logger.exception(
f"{self.__str__()} requires a positive value for the "
"fractal weighting in the first direction of the surface"
raise ValueError(
f"{self.__str__()} requires a positive value for the fractal weighting in the first"
" direction of the surface"
)
raise ValueError
if weighting[1] < 0:
logger.exception(
f"{self.__str__()} requires a positive value for the "
"fractal weighting in the second direction of the surface"
raise ValueError(
f"{self.__str__()} requires a positive value for the fractal weighting in the"
" second direction of the surface"
)
raise ValueError
# Check for valid orientations
if np.count_nonzero(discretised_p1 == discretised_p2) != 1:
raise ValueError(f"{self.__str__()} dimensions are not specified correctly")
if xs == xf:
if ys == yf or zs == zf:
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError
if xs not in [volume.xs, volume.xf]:
logger.exception(
f"{self.__str__()} can only be used on the external surfaces of a fractal box"
)
raise ValueError
fractalrange = (
round_value(limits[0] / grid.dx),
round_value(limits[1] / grid.dx),
)
# xminus surface
if xs == volume.xs:
if fractalrange[0] < 0 or fractalrange[1] > volume.xf:
logger.exception(
f"{self.__str__()} cannot apply fractal surface "
"to fractal box as it would exceed either the "
"upper coordinates of the fractal box or the "
"domain in the x direction"
)
raise ValueError
lower_bound = discretised_p1
upper_bound = uip.discretise_point((limits[1], p2[1], p2[2]))
grid_bound = uip.discretise_point((limits[0], p1[1], p1[2]))
fractalrange = (grid_bound[0], upper_bound[0])
requestedsurface = "xminus"
# xplus surface
elif xf == volume.xf:
if fractalrange[0] < volume.xs or fractalrange[1] > grid.nx:
logger.exception(
f"{self.__str__()} cannot apply fractal surface "
"to fractal box as it would exceed either the "
"lower coordinates of the fractal box or the "
"domain in the x direction"
)
raise ValueError
lower_bound = uip.discretise_point((limits[0], p1[1], p1[2]))
upper_bound = discretised_p2
grid_bound = uip.discretise_point((limits[1], p2[1], p2[2]))
fractalrange = (lower_bound[0], grid_bound[0])
requestedsurface = "xplus"
elif ys == yf:
if zs == zf:
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError
if ys not in [volume.ys, volume.yf]:
logger.exception(
f"{self.__str__()} can only be used on the external "
+ "surfaces of a fractal box"
else:
raise ValueError(
f"{self.__str__()} can only be used on the external surfaces of a fractal box"
)
raise ValueError
fractalrange = (
round_value(limits[0] / grid.dy),
round_value(limits[1] / grid.dy),
)
elif ys == yf:
# yminus surface
if ys == volume.ys:
if fractalrange[0] < 0 or fractalrange[1] > volume.yf:
logger.exception(
f"{self.__str__()} cannot apply fractal surface "
"to fractal box as it would exceed either the "
"upper coordinates of the fractal box or the "
"domain in the y direction"
)
raise ValueError
lower_bound = discretised_p1
upper_bound = uip.discretise_point((p2[0], limits[1], p2[2]))
grid_bound = uip.discretise_point((p1[0], limits[0], p1[2]))
fractalrange = (grid_bound[1], upper_bound[1])
requestedsurface = "yminus"
# yplus surface
elif yf == volume.yf:
if fractalrange[0] < volume.ys or fractalrange[1] > grid.ny:
logger.exception(
f"{self.__str__()} cannot apply fractal surface "
"to fractal box as it would exceed either the "
"lower coordinates of the fractal box or the "
"domain in the y direction"
)
raise ValueError
lower_bound = uip.discretise_point((p1[0], limits[0], p1[2]))
upper_bound = discretised_p2
grid_bound = uip.discretise_point((p2[0], limits[1], p2[2]))
fractalrange = (lower_bound[1], grid_bound[1])
requestedsurface = "yplus"
elif zs == zf:
if zs not in [volume.zs, volume.zf]:
logger.exception(
f"{self.__str__()} can only be used on the external "
+ "surfaces of a fractal box"
else:
raise ValueError(
f"{self.__str__()} can only be used on the external surfaces of a fractal box"
)
raise ValueError
fractalrange = (
round_value(limits[0] / grid.dz),
round_value(limits[1] / grid.dz),
)
elif zs == zf:
# zminus surface
if zs == volume.zs:
if fractalrange[0] < 0 or fractalrange[1] > volume.zf:
logger.exception(
f"{self.__str__()} cannot apply fractal surface "
"to fractal box as it would exceed either the "
"upper coordinates of the fractal box or the "
"domain in the x direction"
)
raise ValueError
lower_bound = discretised_p1
upper_bound = uip.discretise_point((p2[0], p2[1], limits[1]))
grid_bound = uip.discretise_point((p1[0], p1[1], limits[0]))
fractalrange = (grid_bound[2], upper_bound[2])
requestedsurface = "zminus"
# zplus surface
elif zf == volume.zf:
if fractalrange[0] < volume.zs or fractalrange[1] > grid.nz:
logger.exception(
f"{self.__str__()} cannot apply fractal surface "
"to fractal box as it would exceed either the "
"lower coordinates of the fractal box or the "
"domain in the z direction"
)
raise ValueError
lower_bound = uip.discretise_point((p1[0], p1[1], limits[0]))
upper_bound = discretised_p2
grid_bound = uip.discretise_point((p2[0], p2[1], limits[1]))
fractalrange = (lower_bound[2], grid_bound[2])
requestedsurface = "zplus"
else:
raise ValueError(
f"{self.__str__()} can only be used on the external surfaces of a fractal box"
)
else:
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError
raise ValueError(f"{self.__str__()} dimensions are not specified correctly")
surface = FractalSurface(xs, xf, ys, yf, zs, zf, frac_dim, seed)
if any(lower_bound < volume.start):
raise ValueError(
f"{self.__str__()} cannot apply fractal surface to"
" fractal box as it would exceed the lower coordinates"
" of the fractal box."
)
elif any(upper_bound > volume.stop):
raise ValueError(
f"{self.__str__()} cannot apply fractal surface to"
" fractal box as it would exceed the upper coordinates"
" of the fractal box."
)
# Check lower or upper extent of the fractal surface (depending
# if the fractal surface has been applied in the minus or plus
# direction).
uip.point_within_bounds(grid_bound, f"{self.__str__()}")
surface = grid.create_fractal_surface(xs, xf, ys, yf, zs, zf, frac_dim, seed)
surface.surfaceID = requestedsurface
surface.fractalrange = fractalrange
surface.operatingonID = volume.ID
@@ -243,18 +208,21 @@ class AddSurfaceRoughness(RotatableMixin, GeometryUserObject):
# List of existing surfaces IDs
existingsurfaceIDs = [x.surfaceID for x in volume.fractalsurfaces]
if surface.surfaceID in existingsurfaceIDs:
logger.exception(
raise ValueError(
f"{self.__str__()} has already been used on the {surface.surfaceID} surface"
)
raise ValueError
surface.generate_fractal_surface()
volume.fractalsurfaces.append(surface)
p3 = uip.round_to_grid_static_point(p1)
p4 = uip.round_to_grid_static_point(p2)
logger.info(
f"{self.grid_name(grid)}Fractal surface from {xs * grid.dx:g}m, "
f"{ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, "
f"{yf * grid.dy:g}m, {zf * grid.dz:g}m with fractal dimension "
f"{self.grid_name(grid)}Fractal surface from {p3[0]:g}m, "
f"{p3[1]:g}m, {p3[2]:g}m, to {p4[0]:g}m, "
f"{p4[1]:g}m, {p4[2]:g}m with fractal dimension "
f"{surface.dimension:g}, fractal weightings {surface.weighting[0]:g}, "
f"{surface.weighting[1]:g}, fractal seeding {surface.seed}, "
f"and range {limits[0]:g}m to {limits[1]:g}m, added to "

查看文件

@@ -25,7 +25,6 @@ from gprMax.grid.fdtd_grid import FDTDGrid
from gprMax.materials import create_water
from gprMax.user_objects.rotatable import RotatableMixin
from gprMax.user_objects.user_objects import GeometryUserObject
from gprMax.utilities.utilities import round_value
from .cmds_geometry import rotate_2point_object
@@ -78,80 +77,70 @@ class AddSurfaceWater(RotatableMixin, GeometryUserObject):
if volumes := [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id]:
volume = volumes[0]
else:
logger.exception(f"{self.__str__()} cannot find FractalBox {fractal_box_id}")
raise ValueError
raise ValueError(f"{self.__str__()} cannot find FractalBox {fractal_box_id}")
uip = self._create_uip(grid)
_, p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
discretised_p1, discretised_p2 = uip.check_output_object_bounds(p1, p2, self.__str__())
xs, ys, zs = discretised_p1
xf, yf, zf = discretised_p2
if depth <= 0:
logger.exception(f"{self.__str__()} requires a positive value for the depth of water")
raise ValueError
raise ValueError(f"{self.__str__()} requires a positive value for the depth of water")
# Check for valid orientations
if np.count_nonzero(discretised_p1 == discretised_p2) != 1:
raise ValueError(f"{self.__str__()} dimensions are not specified correctly")
if xs == xf:
if ys == yf or zs == zf:
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError
if xs not in [volume.xs, volume.xf]:
logger.exception(
f"{self.__str__()} can only be used on the external surfaces of a fractal box"
)
raise ValueError
# xminus surface
if xs == volume.xs:
requestedsurface = "xminus"
# xplus surface
elif xf == volume.xf:
requestedsurface = "xplus"
filldepthcells = round_value(depth / grid.dx)
filldepth = filldepthcells * grid.dx
elif ys == yf:
if zs == zf:
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError
if ys not in [volume.ys, volume.yf]:
logger.exception(
else:
raise ValueError(
f"{self.__str__()} can only be used on the external surfaces of a fractal box"
)
raise ValueError
filldepthcells = uip.discretise_point((depth, 0, 0))[0]
filldepth = uip.round_to_grid_static_point((depth, 0, 0))[0]
elif ys == yf:
# yminus surface
if ys == volume.ys:
requestedsurface = "yminus"
# yplus surface
elif yf == volume.yf:
requestedsurface = "yplus"
filldepthcells = round_value(depth / grid.dy)
filldepth = filldepthcells * grid.dy
elif zs == zf:
if zs not in [volume.zs, volume.zf]:
logger.exception(
else:
raise ValueError(
f"{self.__str__()} can only be used on the external surfaces of a fractal box"
)
raise ValueError
filldepthcells = uip.discretise_point((0, depth, 0))[1]
filldepth = uip.round_to_grid_static_point((0, depth, 0))[1]
elif zs == zf:
# zminus surface
if zs == volume.zs:
requestedsurface = "zminus"
# zplus surface
elif zf == volume.zf:
requestedsurface = "zplus"
filldepthcells = round_value(depth / grid.dz)
filldepth = filldepthcells * grid.dz
else:
raise ValueError(
f"{self.__str__()} can only be used on the external surfaces of a fractal box"
)
filldepthcells = uip.discretise_point((0, 0, depth))[2]
filldepth = uip.round_to_grid_static_point((0, 0, depth))[2]
else:
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError
raise ValueError(f"{self.__str__()} dimensions are not specified correctly")
surface = next((x for x in volume.fractalsurfaces if x.surfaceID == requestedsurface), None)
if not surface:
logger.exception(
raise ValueError(
f"{self.__str__()} specified surface {requestedsurface} does not have a rough surface applied"
)
raise ValueError
surface.filldepth = filldepthcells
@@ -160,11 +149,10 @@ class AddSurfaceWater(RotatableMixin, GeometryUserObject):
surface.filldepth < surface.fractalrange[0]
or surface.filldepth > surface.fractalrange[1]
):
logger.exception(
raise ValueError(
f"{self.__str__()} requires a value for the depth of water that lies with the "
f"range of the requested surface roughness"
)
raise ValueError
# Check to see if water has been already defined as a material
if not any(x.ID == "water" for x in grid.materials):
@@ -173,15 +161,17 @@ class AddSurfaceWater(RotatableMixin, GeometryUserObject):
# Check if time step for model is suitable for using water
water = next((x for x in grid.materials if x.ID == "water"))
if testwater := next((x for x in water.tau if x < grid.dt), None):
logger.exception(
raise ValueError(
f"{self.__str__()} requires the time step for the model "
f"to be less than the relaxation time required to model water."
)
raise ValueError
p3 = uip.round_to_grid_static_point(p1)
p4 = uip.round_to_grid_static_point(p2)
logger.info(
f"{self.grid_name(grid)}Water on surface from {xs * grid.dx:g}m, "
f"{ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, "
f"{yf * grid.dy:g}m, {zf * grid.dz:g}m with depth {filldepth:g}m, "
f"added to {surface.operatingonID}."
f"{self.grid_name(grid)}Water on surface from {p3[0]:g}m,"
f" {p3[1]:g}m, {p3[2]:g}m, to {p4[0]:g}m, {p4[1]:g}m,"
f" {p4[2]:g}m with depth {filldepth:g}m, added to"
f" {surface.operatingonID}."
)

查看文件

@@ -23,7 +23,6 @@ import numpy as np
import gprMax.config as config
from gprMax.cython.geometry_primitives import build_voxels_from_array, build_voxels_from_array_mask
from gprMax.fractals import FractalVolume
from gprMax.grid.fdtd_grid import FDTDGrid
from gprMax.materials import ListMaterial
from gprMax.user_objects.cmds_geometry.cmds_geometry import check_averaging, rotate_2point_object
@@ -111,38 +110,29 @@ class FractalBox(RotatableMixin, GeometryUserObject):
p3 = uip.round_to_grid_static_point(p1)
p4 = uip.round_to_grid_static_point(p2)
grid_contains_fractal_box, p1, p2 = uip.check_box_points(p1, p2, self.__str__())
# Exit early if none of the fractal box is in this grid as there
# is nothing else to do.
if not grid_contains_fractal_box:
return
p1, p2 = uip.check_output_object_bounds(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
if frac_dim < 0:
logger.exception(
raise ValueError(
f"{self.__str__()} requires a positive value for the fractal dimension"
)
raise ValueError
if weighting[0] < 0:
logger.exception(
raise ValueError(
f"{self.__str__()} requires a positive value for the fractal weighting in the x direction"
)
raise ValueError
if weighting[1] < 0:
logger.exception(
raise ValueError(
f"{self.__str__()} requires a positive value for the fractal weighting in the y direction"
)
raise ValueError
if weighting[2] < 0:
logger.exception(
raise ValueError(
f"{self.__str__()} requires a positive value for the fractal weighting in the z direction"
)
if n_materials < 0:
logger.exception(f"{self.__str__()} requires a positive value for the number of bins")
raise ValueError
raise ValueError(f"{self.__str__()} requires a positive value for the number of bins")
# Find materials to use to build fractal volume, either from mixing
# models or normal materials.
@@ -152,28 +142,25 @@ class FractalBox(RotatableMixin, GeometryUserObject):
if mixingmodel:
if nbins == 1:
logger.exception(
raise ValueError(
f"{self.__str__()} must be used with more than one material from the mixing model."
)
raise ValueError
if isinstance(mixingmodel, ListMaterial) and nbins > len(mixingmodel.mat):
logger.exception(
raise ValueError(
f"{self.__str__()} too many materials/bins "
"requested compared to materials in "
"mixing model."
)
raise ValueError
# Create materials from mixing model as number of bins now known
# from fractal_box command.
mixingmodel.calculate_properties(nbins, grid)
elif not material:
logger.exception(
raise ValueError(
f"{self.__str__()} mixing model or material with "
+ "ID {mixing_model_id} does not exist"
)
raise ValueError
self.volume = FractalVolume(xs, xf, ys, yf, zs, zf, frac_dim, seed)
self.volume = grid.add_fractal_volume(xs, xf, ys, yf, zs, zf, frac_dim, seed)
self.volume.ID = ID
self.volume.operatingonID = mixing_model_id
self.volume.nbins = nbins
@@ -200,39 +187,26 @@ class FractalBox(RotatableMixin, GeometryUserObject):
self.do_pre_build = False
else:
if self.volume.fractalsurfaces:
self.volume.originalxs = self.volume.xs
self.volume.originalxf = self.volume.xf
self.volume.originalys = self.volume.ys
self.volume.originalyf = self.volume.yf
self.volume.originalzs = self.volume.zs
self.volume.originalzf = self.volume.zf
# Extend the volume to accomodate any rough surfaces, grass,
# or roots
for surface in self.volume.fractalsurfaces:
if surface.surfaceID == "xminus":
if surface.fractalrange[0] < self.volume.xs:
self.volume.nx += self.volume.xs - surface.fractalrange[0]
self.volume.xs = surface.fractalrange[0]
elif surface.surfaceID == "xplus":
if surface.fractalrange[1] > self.volume.xf:
self.volume.nx += surface.fractalrange[1] - self.volume.xf
self.volume.xf = surface.fractalrange[1]
elif surface.surfaceID == "yminus":
if surface.fractalrange[0] < self.volume.ys:
self.volume.ny += self.volume.ys - surface.fractalrange[0]
self.volume.ys = surface.fractalrange[0]
elif surface.surfaceID == "yplus":
if surface.fractalrange[1] > self.volume.yf:
self.volume.ny += surface.fractalrange[1] - self.volume.yf
self.volume.yf = surface.fractalrange[1]
elif surface.surfaceID == "zminus":
if surface.fractalrange[0] < self.volume.zs:
self.volume.nz += self.volume.zs - surface.fractalrange[0]
self.volume.zs = surface.fractalrange[0]
elif surface.surfaceID == "zplus":
if surface.fractalrange[1] > self.volume.zf:
self.volume.nz += surface.fractalrange[1] - self.volume.zf
self.volume.zf = surface.fractalrange[1]
# If there is only 1 bin then a normal material is being used,
@@ -247,7 +221,8 @@ class FractalBox(RotatableMixin, GeometryUserObject):
)
self.volume.fractalvolume *= materialnumID
else:
self.volume.generate_fractal_volume()
if not self.volume.generate_fractal_volume():
return
for i in range(0, self.volume.nx):
for j in range(0, self.volume.ny):
for k in range(0, self.volume.nz):
@@ -263,6 +238,10 @@ class FractalBox(RotatableMixin, GeometryUserObject):
# TODO: Allow extract of rough surface profile (to print/file?)
for surface in self.volume.fractalsurfaces:
if surface.surfaceID == "xminus":
surface.fractalrange = (
max(surface.fractalrange[0], 0),
min(surface.fractalrange[1], grid.nx),
)
for i in range(surface.fractalrange[0], surface.fractalrange[1]):
for j in range(surface.ys, surface.yf):
for k in range(surface.zs, surface.zf):
@@ -286,6 +265,10 @@ class FractalBox(RotatableMixin, GeometryUserObject):
] = 0
elif surface.surfaceID == "xplus":
surface.fractalrange = (
max(surface.fractalrange[0], 0),
min(surface.fractalrange[1], grid.nx),
)
if not surface.ID:
for i in range(surface.fractalrange[0], surface.fractalrange[1]):
for j in range(surface.ys, surface.yf):
@@ -394,6 +377,10 @@ class FractalBox(RotatableMixin, GeometryUserObject):
root += 1
elif surface.surfaceID == "yminus":
surface.fractalrange = (
max(surface.fractalrange[0], 0),
min(surface.fractalrange[1], grid.ny),
)
for i in range(surface.xs, surface.xf):
for j in range(surface.fractalrange[0], surface.fractalrange[1]):
for k in range(surface.zs, surface.zf):
@@ -417,6 +404,10 @@ class FractalBox(RotatableMixin, GeometryUserObject):
] = 0
elif surface.surfaceID == "yplus":
surface.fractalrange = (
max(surface.fractalrange[0], 0),
min(surface.fractalrange[1], grid.ny),
)
if not surface.ID:
for i in range(surface.xs, surface.xf):
for j in range(surface.fractalrange[0], surface.fractalrange[1]):
@@ -525,6 +516,10 @@ class FractalBox(RotatableMixin, GeometryUserObject):
root += 1
elif surface.surfaceID == "zminus":
surface.fractalrange = (
max(surface.fractalrange[0], 0),
min(surface.fractalrange[1], grid.nz),
)
for i in range(surface.xs, surface.xf):
for j in range(surface.ys, surface.yf):
for k in range(surface.fractalrange[0], surface.fractalrange[1]):
@@ -548,6 +543,10 @@ class FractalBox(RotatableMixin, GeometryUserObject):
] = 0
elif surface.surfaceID == "zplus":
surface.fractalrange = (
max(surface.fractalrange[0], 0),
min(surface.fractalrange[1], grid.nz),
)
if not surface.ID:
for i in range(surface.xs, surface.xf):
for j in range(surface.ys, surface.yf):
@@ -679,14 +678,14 @@ class FractalBox(RotatableMixin, GeometryUserObject):
else:
if self.volume.nbins == 1:
logger.exception(
raise ValueError(
f"{self.__str__()} is being used with a "
"single material and no modifications, "
"therefore please use a #box command instead."
)
raise ValueError
else:
self.volume.generate_fractal_volume()
if not self.volume.generate_fractal_volume():
return
for i in range(0, self.volume.nx):
for j in range(0, self.volume.ny):
for k in range(0, self.volume.nz):

查看文件

@@ -24,10 +24,10 @@ import h5py
import gprMax.config as config
from gprMax.cython.geometry_primitives import build_voxels_from_array
from gprMax.geometry_outputs.geometry_objects_read import ReadGeometryObject
from gprMax.grid.fdtd_grid import FDTDGrid
from gprMax.hash_cmds_file import get_user_objects
from gprMax.user_objects.user_objects import GeometryUserObject
from gprMax.utilities.utilities import round_value
logger = logging.getLogger(__name__)
@@ -50,12 +50,6 @@ class GeometryObjectsRead(GeometryUserObject):
logger.exception(f"{self.__str__()} requires exactly five parameters")
raise
# Discretise the point using uip object. This has different behaviour
# depending on the type of uip object. So we can use it for
# the main grid or the subgrid.
uip = self._create_uip(grid)
xs, ys, zs = uip.discretise_point(p1)
# See if material file exists at specified path and if not try input
# file directory
matfile = Path(matfile)
@@ -76,9 +70,21 @@ class GeometryObjectsRead(GeometryUserObject):
if (line.startswith("#") and not line.startswith("##") and line.rstrip("\n"))
]
# Avoid redefining default builtin materials
pec = f"#material: 1 inf 1 0 pec{{{matstr}}}\n"
free_space = f"#material: 1 0 1 0 free_space{{{matstr}}}\n"
if materials[0] == pec and materials[1] == free_space:
materials.pop(0)
materials.pop(1)
numexistmaterials -= 2
elif materials[0] == pec or materials[0] == free_space:
materials.pop(0)
numexistmaterials -= 1
# Build scene
# API for multiple scenes / model runs
scene = config.get_model_config().get_scene()
assert scene is not None
material_objs = get_user_objects(materials, checkessential=False)
for material_obj in material_objs:
scene.add(material_obj)
@@ -100,69 +106,53 @@ class GeometryObjectsRead(GeometryUserObject):
if not geofile.exists():
geofile = Path(config.sim_config.input_file_path.parent, geofile)
# Open geometry object file and read/check spatial resolution attribute
f = h5py.File(geofile, "r")
dx_dy_dz = f.attrs["dx_dy_dz"]
if round_value(
(dx_dy_dz[0] / grid.dx) != 1
or round_value(dx_dy_dz[1] / grid.dy) != 1
or round_value(dx_dy_dz[2] / grid.dz) != 1
):
logger.exception(
f"{self.__str__()} requires the spatial resolution "
"of the geometry objects file to match the spatial "
"resolution of the model"
)
raise ValueError
# Discretise the point using uip object. This has different behaviour
# depending on the type of uip object. So we can use it for
# the main grid, MPI grids or the subgrid.
uip = self._create_uip(grid)
discretised_p1 = uip.discretise_point(p1)
p2 = uip.round_to_grid_static_point(p1)
data = f["/data"][:]
with ReadGeometryObject(geofile, grid, discretised_p1, numexistmaterials) as f:
# Check spatial resolution attribute
if not f.has_valid_discritisation():
raise ValueError(
f"{self.__str__()} requires the spatial resolution "
"of the geometry objects file to match the spatial "
"resolution of the model"
)
# Should be int16 to allow for -1 which indicates background, i.e.
# don't build anything, but AustinMan/Woman maybe uint16
if data.dtype != "int16":
data = data.astype("int16")
if f.has_rigid_arrays() and f.has_ID_array():
f.read_data()
f.read_ID()
f.read_rigidE()
f.read_rigidH()
# Look to see if rigid and ID arrays are present (these should be
# present if the original geometry objects were written from gprMax)
try:
rigidE = f["/rigidE"][:]
rigidH = f["/rigidH"][:]
ID = f["/ID"][:]
grid.solid[
xs : xs + data.shape[0], ys : ys + data.shape[1], zs : zs + data.shape[2]
] = (data + numexistmaterials)
grid.rigidE[
:, xs : xs + rigidE.shape[1], ys : ys + rigidE.shape[2], zs : zs + rigidE.shape[3]
] = rigidE
grid.rigidH[
:, xs : xs + rigidH.shape[1], ys : ys + rigidH.shape[2], zs : zs + rigidH.shape[3]
] = rigidH
grid.ID[:, xs : xs + ID.shape[1], ys : ys + ID.shape[2], zs : zs + ID.shape[3]] = (
ID + numexistmaterials
)
logger.info(
f"{self.grid_name(grid)}Geometry objects from file {geofile} "
f"inserted at {xs * grid.dx:g}m, {ys * grid.dy:g}m, "
f"{zs * grid.dz:g}m, with corresponding materials file "
f"{matfile}."
)
except KeyError:
averaging = False
build_voxels_from_array(
xs,
ys,
zs,
numexistmaterials,
averaging,
data,
grid.solid,
grid.rigidE,
grid.rigidH,
grid.ID,
)
logger.info(
f"{self.grid_name(grid)}Geometry objects from file "
f"(voxels only){geofile} inserted at {xs * grid.dx:g}m, "
f"{ys * grid.dy:g}m, {zs * grid.dz:g}m, with corresponding "
f"materials file {matfile}."
)
logger.info(
f"{self.grid_name(grid)}Geometry objects from file {geofile}"
f" inserted at {p2[0]:g}m, {p2[1]:g}m, {p2[2]:g}m,"
f" with corresponding materials file"
f" {matfile}."
)
else:
data = f.get_data()
if data is not None:
averaging = False
build_voxels_from_array(
discretised_p1[0],
discretised_p1[1],
discretised_p1[2],
numexistmaterials,
averaging,
data,
grid.solid,
grid.rigidE,
grid.rigidH,
grid.ID,
)
logger.info(
f"{self.grid_name(grid)}Geometry objects from file "
f"(voxels only){geofile} inserted at {p2[0]:g}m, "
f"{p2[1]:g}m, {p2[2]:g}m, with corresponding "
f"materials file {matfile}."
)

查看文件

@@ -890,7 +890,7 @@ class DiscretePlaneWave(GridUserObject):
rational angle.
p1: tuple required for the lower left position (x, y, z) of the total
field, scattered field (TFSF) box.
p1: tuple required for the upper right position (x, y, z) of the total
p2: tuple required for the upper right position (x, y, z) of the total
field, scattered field (TFSF) box.
waveform_id: string required for identifier of waveform used with source.
material_id: string optional of material identifier to use as the

查看文件

@@ -274,7 +274,7 @@ class GeometryView(OutputUserObject):
p2: tuple required for upper right (x,y,z) coordinates of volume of
geometry view in metres.
dl: tuple required for spatial discretisation of geometry view in metres.
output_tuple: string required for per-cell 'n' (normal) or per-cell-edge
output_type: string required for per-cell 'n' (normal) or per-cell-edge
'f' (fine) geometry views.
filename: string required for filename where geometry view will be
stored in the same directory as input file.
@@ -303,18 +303,6 @@ class GeometryView(OutputUserObject):
self.filename = filename
self.output_type = output_type
def geometry_view_constructor(self, output_type):
"""Selects appropriate class for geometry view dependent on geometry
view type, i.e. normal or fine.
"""
if output_type == "n":
from gprMax.geometry_outputs import GeometryViewVoxels as GeometryViewUser
else:
from gprMax.geometry_outputs import GeometryViewLines as GeometryViewUser
return GeometryViewUser
def build(self, model: Model, grid: FDTDGrid):
uip = self._create_uip(grid)
discretised_lower_bound, discretised_upper_bound = uip.check_output_object_bounds(

查看文件

@@ -1,5 +1,5 @@
# Copyright (C) 2015-2025: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, John Hartley,
# Authors: Craig Warren, Antonis Giannopoulos, John Hartley,
# and Nathan Mannall
#
# This file is part of gprMax.
@@ -21,10 +21,8 @@ import logging
from typing import Optional, Tuple, Union
import numpy as np
import numpy.typing as npt
from gprMax import config
from gprMax.grid.mpi_grid import MPIGrid
from gprMax.model import Model
from gprMax.pml import PML
from gprMax.user_objects.user_objects import ModelUserObject
@@ -66,7 +64,7 @@ class Discretisation(ModelUserObject):
"""Spatial discretisation of the model in the x, y, and z dimensions.
Attributes:
discretisation (np.array): Spatial discretisation of the model
discretisation (tuple): Spatial discretisation of the model
(x, y, z)
"""
@@ -135,7 +133,7 @@ class Domain(ModelUserObject):
logger.info(
f"Domain size: {self.domain_size[0]:g} x {self.domain_size[1]:g} x "
+ f"{self.domain_size[2]:g}m ({model.nx:d} x {model.ny:d} x {model.nz:d} = "
+ f"{(model.nx * model.ny * model.nz):g} cells)"
+ f"{(model.cells):g} cells)"
)
# Set mode and switch off appropriate PMLs for 2D models
@@ -172,7 +170,7 @@ class TimeStepStabilityFactor(ModelUserObject):
"""Factor by which to reduce the time step from the CFL limit.
Attributes:
stability_factor (flaot): Factor to multiply time step by.
stability_factor (float): Factor to multiply time step by.
"""
@property
@@ -212,8 +210,8 @@ class TimeWindow(ModelUserObject):
time takes precedence.
Attributes:
time: float of required simulated time in seconds.
iterations: int of required number of iterations.
time (float | None): Simulated time in seconds.
iterations (int | None): Number of iterations.
"""
@property

47
gprMax/utilities/mpi.py 普通文件
查看文件

@@ -0,0 +1,47 @@
from enum import IntEnum, unique
from typing import Union
import numpy as np
import numpy.typing as npt
from mpi4py import MPI
@unique
class Dim(IntEnum):
X = 0
Y = 1
Z = 2
@unique
class Dir(IntEnum):
NONE = -1
NEG = 0
POS = 1
def get_neighbours(comm: MPI.Cartcomm) -> npt.NDArray[np.int32]:
neighbours = np.full((3, 2), -1, dtype=np.int32)
neighbours[Dim.X] = comm.Shift(direction=Dim.X, disp=1)
neighbours[Dim.Y] = comm.Shift(direction=Dim.Y, disp=1)
neighbours[Dim.Z] = comm.Shift(direction=Dim.Z, disp=1)
return neighbours
def get_neighbour(comm: MPI.Cartcomm, dim: Dim, dir: Dir) -> int:
neighbours = comm.Shift(direction=dim, disp=1)
return neighbours[dir]
def get_relative_neighbour(
comm: MPI.Cartcomm, dirs: npt.NDArray[np.int32], disp: Union[int, npt.NDArray[np.int32]] = 1
) -> int:
offset = np.select([dirs == Dir.NEG, dirs == Dir.POS], [-disp, disp], default=0)
coord = comm.coords + offset
if any(coord < 0) or any(coord >= comm.dims):
return -1
return comm.Get_cart_rank(coord.tolist())

查看文件

@@ -18,13 +18,13 @@
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from os import PathLike
from typing import Literal, Optional, Union
from typing import Optional, Union
import numpy as np
import numpy.typing as npt
from mpi4py import MPI
from mpi4py.MPI import Intracomm
from gprMax.vtkhdf_filehandlers.vtkhdf import VtkHdfFile
from gprMax.vtkhdf_filehandlers.vtkhdf import VtkFileType, VtkHdfFile
class VtkImageData(VtkHdfFile):
@@ -34,6 +34,8 @@ class VtkImageData(VtkHdfFile):
https://docs.vtk.org/en/latest/design_documents/VTKFileFormats.html#image-data
"""
TYPE = VtkFileType.IMAGE_DATA
DIRECTION_ATTR = "Direction"
ORIGIN_ATTR = "Origin"
SPACING_ATTR = "Spacing"
@@ -41,10 +43,6 @@ class VtkImageData(VtkHdfFile):
DIMENSIONS = 3
@property
def TYPE(self) -> Literal["ImageData"]:
return "ImageData"
def __init__(
self,
filename: Union[str, PathLike],
@@ -52,7 +50,7 @@ class VtkImageData(VtkHdfFile):
origin: Optional[npt.NDArray[np.float32]] = None,
spacing: Optional[npt.NDArray[np.float32]] = None,
direction: Optional[npt.NDArray[np.float32]] = None,
comm: Optional[MPI.Comm] = None,
comm: Optional[Intracomm] = None,
):
"""Create a new VtkImageData file.
@@ -80,7 +78,7 @@ class VtkImageData(VtkHdfFile):
comm (optional): MPI communicator containing all ranks that
want to write to the file.
"""
super().__init__(filename, comm)
super().__init__(filename, self.TYPE, "w", comm)
if len(shape) == 0:
raise ValueError(f"Shape must not be empty.")
@@ -179,7 +177,7 @@ class VtkImageData(VtkHdfFile):
"If no offset is specified, data.shape must be one larger in each dimension than"
f" this vtkImageData object. {data.shape} != {points_shape}"
)
return super().add_point_data(name, data, points_shape, offset)
return super()._add_point_data(name, data, points_shape, offset)
def add_cell_data(
self, name: str, data: npt.NDArray, offset: Optional[npt.NDArray[np.int32]] = None
@@ -200,4 +198,4 @@ class VtkImageData(VtkHdfFile):
"If no offset is specified, data.shape must match the dimensions of this"
f" VtkImageData object. {data.shape} != {self.shape}"
)
return super().add_cell_data(name, data, self.shape, offset)
return super()._add_cell_data(name, data, self.shape, offset)

查看文件

@@ -19,13 +19,13 @@
import logging
from os import PathLike
from typing import Literal, Optional, Union
from typing import Optional, Union
import numpy as np
import numpy.typing as npt
from mpi4py import MPI
from gprMax.vtkhdf_filehandlers.vtkhdf import VtkCellType, VtkHdfFile
from gprMax.vtkhdf_filehandlers.vtkhdf import VtkCellType, VtkFileType, VtkHdfFile
logger = logging.getLogger(__name__)
@@ -37,6 +37,8 @@ class VtkUnstructuredGrid(VtkHdfFile):
https://docs.vtk.org/en/latest/design_documents/VTKFileFormats.html#unstructured-grid
"""
TYPE = VtkFileType.UNSTRUCTURED_GRID
class Dataset(VtkHdfFile.Dataset):
CONNECTIVITY = "Connectivity"
NUMBER_OF_CELLS = "NumberOfCells"
@@ -46,10 +48,6 @@ class VtkUnstructuredGrid(VtkHdfFile):
POINTS = "Points"
TYPES = "Types"
@property
def TYPE(self) -> Literal["UnstructuredGrid"]:
return "UnstructuredGrid"
def __init__(
self,
filename: Union[str, PathLike],
@@ -57,7 +55,7 @@ class VtkUnstructuredGrid(VtkHdfFile):
cell_types: npt.NDArray[VtkCellType],
connectivity: npt.NDArray,
cell_offsets: npt.NDArray,
comm: Optional[MPI.Comm] = None,
comm: Optional[MPI.Intracomm] = None,
) -> None:
"""Create a new VtkUnstructuredGrid file.
@@ -88,7 +86,7 @@ class VtkUnstructuredGrid(VtkHdfFile):
Raises:
Value Error: Raised if argument dimensions are invalid.
"""
super().__init__(filename, comm)
super().__init__(filename, self.TYPE, "w", comm)
if len(cell_offsets) != len(cell_types) + 1:
raise ValueError(
@@ -109,39 +107,140 @@ class VtkUnstructuredGrid(VtkHdfFile):
" Some connectivity data will be ignored"
)
self._write_root_dataset(self.Dataset.CONNECTIVITY, connectivity)
self._write_root_dataset(self.Dataset.NUMBER_OF_CELLS, len(cell_types))
self._write_root_dataset(self.Dataset.NUMBER_OF_CONNECTIVITY_IDS, len(connectivity))
self._write_root_dataset(self.Dataset.NUMBER_OF_POINTS, len(points))
self._write_root_dataset(self.Dataset.OFFSETS, cell_offsets)
self._write_root_dataset(self.Dataset.POINTS, points, xyz_data_ordering=False)
self._write_root_dataset(self.Dataset.TYPES, cell_types)
self._number_of_cells = len(cell_types)
self._number_of_connectivity_ids = len(connectivity)
self._number_of_points = len(points)
if self.comm is None:
self.partition = 0
self._global_number_of_cells = self._number_of_cells
self._global_number_of_points = self._number_of_points
self._cells_offset = np.zeros(1, dtype=np.int32)
self._points_offsets = np.zeros(2, dtype=np.int32)
self._write_root_dataset(self.Dataset.NUMBER_OF_CELLS, self._number_of_cells)
self._write_root_dataset(
self.Dataset.NUMBER_OF_CONNECTIVITY_IDS, self._number_of_connectivity_ids
)
self._write_root_dataset(self.Dataset.NUMBER_OF_POINTS, self._number_of_points)
self._write_root_dataset(self.Dataset.TYPES, cell_types)
self._write_root_dataset(self.Dataset.POINTS, points, xyz_data_ordering=False)
self._write_root_dataset(self.Dataset.CONNECTIVITY, connectivity)
self._write_root_dataset(self.Dataset.OFFSETS, cell_offsets)
else:
# Write partition information for each rank
self.partition = self.comm.rank
partition_offset = np.array([self.partition], dtype=np.int32)
partition_shape = np.array([self.comm.size], dtype=np.int32)
self._write_root_dataset(
self.Dataset.NUMBER_OF_CELLS,
self._number_of_cells,
shape=partition_shape,
offset=partition_offset,
)
self._write_root_dataset(
self.Dataset.NUMBER_OF_CONNECTIVITY_IDS,
self._number_of_connectivity_ids,
shape=partition_shape,
offset=partition_offset,
)
self._write_root_dataset(
self.Dataset.NUMBER_OF_POINTS,
self._number_of_points,
shape=partition_shape,
offset=partition_offset,
)
self._global_number_of_cells = self.comm.allreduce(self._number_of_cells, MPI.SUM)
self._global_number_of_points = self.comm.allreduce(self._number_of_points, MPI.SUM)
self._cells_offset = np.full(1, self._number_of_cells, dtype=np.int32)
self.comm.Exscan(MPI.IN_PLACE, self._cells_offset)
self._points_offsets = np.array([self._number_of_points, 0], dtype=np.int32)
self.comm.Exscan(MPI.IN_PLACE, self._points_offsets)
connectivity_shape = np.array([self._number_of_connectivity_ids], dtype=np.int32)
self.comm.Allreduce(MPI.IN_PLACE, connectivity_shape)
connectivity_offset = np.array([self._number_of_connectivity_ids], dtype=np.int32)
self.comm.Exscan(MPI.IN_PLACE, connectivity_offset)
# Exscan leaves these undefined for rank 0
if self.comm.rank == 0:
connectivity_offset = np.zeros(1, dtype=np.int32)
self._cells_offset = np.zeros(1, dtype=np.int32)
self._points_offsets = np.zeros(2, dtype=np.int32)
cells_shape = np.array([self.global_number_of_cells], dtype=np.int32)
self._write_root_dataset(
self.Dataset.TYPES, cell_types, shape=cells_shape, offset=self.cells_offset
)
points_shape = np.array([self.global_number_of_points, 3], dtype=np.int32)
self._write_root_dataset(
self.Dataset.POINTS,
points,
shape=points_shape,
offset=self.points_offset,
xyz_data_ordering=False,
)
self._write_root_dataset(
self.Dataset.CONNECTIVITY,
connectivity,
shape=connectivity_shape,
offset=connectivity_offset,
)
# The OFFSETS dataset has size C + 1 for each partition.
# Account for the +1 by addding the partition index to the
# offset and MPI communicator size to the shape
cell_offsets_offset = self.cells_offset + self.partition
cell_offsets_shape = cells_shape + self.comm.size
self._write_root_dataset(
self.Dataset.OFFSETS,
cell_offsets,
shape=cell_offsets_shape,
offset=cell_offsets_offset,
)
@property
def number_of_cells(self) -> int:
number_of_cells = self._get_root_dataset(self.Dataset.NUMBER_OF_CELLS)
return np.sum(number_of_cells, dtype=np.int32)
return self._number_of_cells
@property
def global_number_of_cells(self) -> int:
return self._global_number_of_cells
@property
def cells_offset(self) -> npt.NDArray[np.int32]:
return self._cells_offset
@property
def number_of_connectivity_ids(self) -> int:
number_of_connectivity_ids = self._get_root_dataset(self.Dataset.NUMBER_OF_CONNECTIVITY_IDS)
return np.sum(number_of_connectivity_ids, dtype=np.int32)
return self._number_of_connectivity_ids
@property
def number_of_points(self) -> int:
number_of_points = self._get_root_dataset(self.Dataset.NUMBER_OF_POINTS)
return np.sum(number_of_points, dtype=np.int32)
return self._number_of_points
def add_point_data(
self, name: str, data: npt.NDArray, offset: Optional[npt.NDArray[np.int32]] = None
):
@property
def global_number_of_points(self) -> int:
return self._global_number_of_points
@property
def points_offset(self) -> npt.NDArray[np.int32]:
return self._points_offsets
def add_point_data(self, name: str, data: npt.NDArray):
"""Add point data to the VTKHDF file.
Args:
name: Name of the dataset.
data: Data to be saved.
offset (optional): Offset to store the provided data at. Can
be omitted if data provides the full dataset.
data: Data to be saved. The length of the date must match
the number of points in the partition owned by this
rank.
Raises:
ValueError: Raised if data has invalid dimensions.
@@ -159,18 +258,17 @@ class VtkUnstructuredGrid(VtkHdfFile):
elif number_of_dimensions == 2 and shape[1] != 1 and shape[1] != 3:
raise ValueError(f"The second dimension should have shape 1 or 3, not {shape[1]}")
return super().add_point_data(name, data, shape, offset)
shape[0] = self.global_number_of_points
def add_cell_data(
self, name: str, data: npt.NDArray, offset: Optional[npt.NDArray[np.int32]] = None
):
return super()._add_point_data(name, data, shape, self.points_offset)
def add_cell_data(self, name: str, data: npt.NDArray):
"""Add cell data to the VTKHDF file.
Args:
name: Name of the dataset.
data: Data to be saved.
offset (optional): Offset to store the provided data at. Can
be omitted if data provides the full dataset.
data: Data to be saved. The length of the date must match
the number of cells in the partition owned by this rank.
Raises:
ValueError: Raised if data has invalid dimensions.
@@ -182,10 +280,12 @@ class VtkUnstructuredGrid(VtkHdfFile):
raise ValueError(f"Data must have 1 or 2 dimensions, not {number_of_dimensions}.")
elif len(data) != self.number_of_cells:
raise ValueError(
"Length of data must match the number of cells in the vtkUnstructuredGrid."
f" {len(data)} != {self.number_of_cells}"
f"Length of data must match the number of cells in partition {self.partition} of the"
f" vtkUnstructuredGrid. {len(data)} != {self.number_of_cells}"
)
elif number_of_dimensions == 2 and shape[1] != 1 and shape[1] != 3:
raise ValueError(f"The second dimension should have shape 1 or 3, not {shape[1]}")
return super().add_cell_data(name, data, shape, offset)
shape[0] = self.global_number_of_cells
return super()._add_cell_data(name, data, shape, self.cells_offset)

查看文件

@@ -29,11 +29,16 @@ from typing import Optional, Tuple, Union
import h5py
import numpy as np
import numpy.typing as npt
from mpi4py import MPI
from mpi4py.MPI import Intracomm
logger = logging.getLogger(__name__)
class VtkFileType(str, Enum):
IMAGE_DATA = "ImageData"
UNSTRUCTURED_GRID = "UnstructuredGrid"
class VtkHdfFile(AbstractContextManager):
VERSION = [2, 2]
FILE_EXTENSION = ".vtkhdf"
@@ -48,15 +53,16 @@ class VtkHdfFile(AbstractContextManager):
class Dataset(str, Enum):
pass
@property
@abstractmethod
def TYPE(self) -> str:
pass
def __enter__(self):
return self
def __init__(self, filename: Union[str, PathLike], comm: Optional[MPI.Comm] = None) -> None:
def __init__(
self,
filename: Union[str, PathLike],
vtk_file_type: VtkFileType,
mode: str = "r",
comm: Optional[Intracomm] = None,
) -> None:
"""Create a new VtkHdfFile.
If the file already exists, it will be overriden. Required
@@ -68,6 +74,12 @@ class VtkHdfFile(AbstractContextManager):
Args:
filename: Name of the file (can be a file path). The file
extension will be set to '.vtkhdf'.
mode (optional): Mode to open the file. Valid modes are
- r Readonly, file must exist (default)
- r+ Read/write, file must exist
- w Create file, truncate if exists
- w- or x Create file, fail if exists
- a Read/write if exists, create otherwise
comm (optional): MPI communicator containing all ranks that
want to write to the file.
@@ -85,18 +97,20 @@ class VtkHdfFile(AbstractContextManager):
# Check if the filehandler should use an MPI driver
if self.comm is None:
self.file_handler = h5py.File(self.filename, "w")
self.file_handler = h5py.File(self.filename, mode)
else:
self.file_handler = h5py.File(self.filename, "w", driver="mpio", comm=self.comm)
self.file_handler = h5py.File(self.filename, mode, driver="mpio", comm=self.comm)
self.root_group = self.file_handler.create_group(self.ROOT_GROUP)
logger.debug(f"Opened file '{self.filename}'")
self.root_group = self.file_handler.require_group(self.ROOT_GROUP)
# Set required Version and Type root attributes
self._set_root_attribute(self.VERSION_ATTR, self.VERSION)
self._check_root_attribute(self.VERSION_ATTR, self.VERSION)
type_as_ascii = self.TYPE.encode("ascii")
self._set_root_attribute(
self.TYPE_ATTR, type_as_ascii, h5py.string_dtype("ascii", len(type_as_ascii))
type_as_ascii = vtk_file_type.encode("ascii")
self._check_root_attribute(
self.TYPE_ATTR, type_as_ascii, dtype=h5py.string_dtype("ascii", len(type_as_ascii))
)
def __exit__(
@@ -122,8 +136,58 @@ class VtkHdfFile(AbstractContextManager):
def close(self) -> None:
"""Close the file handler"""
logger.debug(f"Closed file '{self.filename}'")
self.file_handler.close()
def _check_root_attribute(
self,
attribute: str,
expected_value: npt.ArrayLike,
dtype: npt.DTypeLike = None,
):
"""Check root attribute is present and warn if not valid.
If the attribute exists in the file that has been opened, the
value will be checked against the expected value with a warning
produced if they differ. If the attribute has not been set, it
will be set to the expected value unless the file has been
opened in readonly mode.
Args:
attribute: Name of the attribute.
expected_value: Expected value of the attribute.
dtype (optional): Data type of the attribute. Overrides
expected_value.dtype if the attribute is set by
this function.
Returns:
value: True if the attribute is present in the root VTKHDF
group. False otherwise.
"""
if self._has_root_attribute(attribute):
value = self._get_root_attribute(attribute)
if np.any(value != expected_value):
logger.warning(
f"VTKHDF version mismatch. Expected '{expected_value}', but found '{value}'."
)
elif self.file_handler.mode == "r+":
self._set_root_attribute(attribute, expected_value, dtype=dtype)
else:
logger.warning(f"Required VTKHDF attribute '{attribute}' not found.")
def _has_root_attribute(self, attribute: str) -> bool:
"""Check if attribute is present in the root VTKHDF group.
Args:
attribute: Name of the attribute.
Returns:
value: True if the attribute is present in the root VTKHDF
group. False otherwise.
"""
value = self.root_group.attrs.get(attribute)
return value is not None
def _get_root_attribute(self, attribute: str) -> npt.NDArray:
"""Get attribute from the root VTKHDF group if it exists.
@@ -136,8 +200,8 @@ class VtkHdfFile(AbstractContextManager):
Raises:
KeyError: Raised if the attribute is not present as a key.
"""
value = self.root_group.attrs[attribute]
if isinstance(value, h5py.Empty):
value = self.root_group.attrs.get(attribute)
if value is None:
raise KeyError(f"Attribute '{attribute}' not present in /{self.ROOT_GROUP} group")
return value
@@ -258,48 +322,71 @@ class VtkHdfFile(AbstractContextManager):
and offset are invalid.
"""
# If dtype is a string and using parallel I/O, ensure using
# fixed length strings
if isinstance(dtype, np.dtype) and self.comm is not None:
string_info = h5py.check_string_dtype(dtype)
if string_info is not None and string_info.length is None:
logger.warning(
"HDF5 does not support variable length strings with parallel I/O."
" Using fixed length strings instead."
)
dtype = h5py.string_dtype(encoding="ascii", length=0)
# Ensure data is a numpy array
if not isinstance(data, np.ndarray):
data = np.array(data, dtype=dtype)
if data.ndim < 1:
data = np.expand_dims(data, axis=-1)
if data.dtype.kind == "U":
if dtype is not None: # Only log warning if user specified a data type
string_info = None
# Warn if string data type will be converted from unicode to a
# byte array (ascii). Only output a warning if the user
# specified dtype
if dtype is not None and np.dtype(dtype).kind == "U":
logger.warning(
"NumPy UTF-32 ('U' dtype) is not supported by HDF5."
" Converting to bytes array ('S' dtype)."
)
# Ensure dtype is a numpy dtype
if dtype is None:
dtype = data.dtype
elif isinstance(dtype, np.dtype):
string_info = h5py.check_string_dtype(dtype)
# Warn if user specified h5py string data type is invalid
if string_info is not None and string_info.encoding == "utf-8":
logger.warning(
"NumPy UTF-32 ('U' dtype) is not supported by HDF5."
" Converting to bytes array ('S' dtype)."
"utf-8 encoding is not supported by VTKHDF. Converting to ascii encoding."
)
data = data.astype("S")
if string_info is not None and string_info.length is not None:
if self.comm is None:
logger.warning(
"Fixed length strings are not supported by VTKHDF."
" Converting to variable length strings."
)
else:
logger.warning(
"HDF5 does not support variable length strings with parallel I/O."
" Using fixed length strings instead."
)
logger.warning(
"VTKHDF does not support fixed length strings. File readers may generate"
" error messages when reading in a VTKHDF file containing fixed length"
" strings."
)
else:
dtype = np.dtype(dtype)
# Explicitly define string datatype
# VTKHDF only supports ascii strings (not UTF-8)
if data.dtype.kind == "S":
dtype = h5py.string_dtype(encoding="ascii", length=data.dtype.itemsize)
# VTKHDF only supports variable length ascii strings (not UTF-8)
if dtype.kind == "U" or dtype.kind == "S" or string_info is not None:
# If using parallel I/O, use fixed length strings
length = None if self.comm is None else 0
dtype = h5py.string_dtype(encoding="ascii", length=length)
data = data.astype(dtype)
elif dtype is None:
dtype = data.dtype
# VTKHDF stores datasets using ZYX ordering rather than XYZ
if xyz_data_ordering:
data = data.transpose()
if shape is not None:
shape = np.flip(shape)
if shape is not None:
shape = np.flip(shape)
if offset is not None:
offset = np.flip(offset)
if offset is not None:
offset = np.flip(offset)
logger.debug(
f"Writing dataset '{path}', shape: {shape}, data.shape: {data.shape}, dtype: {dtype}"
@@ -367,25 +454,36 @@ class VtkHdfFile(AbstractContextManager):
if string_info is not None and string_info.length is None:
raise TypeError(
"HDF5 does not support variable length strings with parallel I/O."
" Use fixed length strings instead."
" Use a serial driver or fixed length strings instead."
)
string_info = h5py.check_string_dtype(dtype)
if dtype.kind == "U":
logger.warning(
"NumPy UTF-32 ('U' dtype) is not supported by HDF5."
" Converting to bytes array ('S' dtype)."
)
if string_info is not None and string_info.encoding == "utf-8":
logger.warning(
"utf-8 encoding is not supported by VTKHDF. Converting to ascii encoding."
)
# Explicitly define string datatype
# VTKHDF only supports ascii strings (not UTF-8)
if dtype.kind == "U" or dtype.kind == "S":
dtype = h5py.string_dtype(encoding="ascii", length=dtype.itemsize)
# VTKHDF only supports variable length ascii strings (not UTF-8)
if (
dtype.kind == "U"
or dtype.kind == "S"
or (string_info is not None and string_info.encoding == "utf-8")
):
dtype = h5py.string_dtype(encoding="ascii", length=None)
logger.debug(f"Creating dataset '{path}', shape: {shape}, dtype: {dtype}")
self.file_handler.create_dataset(path, shape=shape, dtype=dtype)
def add_point_data(
def _add_point_data(
self,
name: str,
data: npt.NDArray,
@@ -405,7 +503,7 @@ class VtkHdfFile(AbstractContextManager):
dataset_path = self._build_dataset_path("PointData", name)
self._write_dataset(dataset_path, data, shape=shape, offset=offset)
def add_cell_data(
def _add_cell_data(
self,
name: str,
data: npt.NDArray,

0
reframe_tests/__init__.py 普通文件
查看文件

查看文件

@@ -0,0 +1,4 @@
#material: 1 0 1 0 free_space
#material: 8 0 1 0 half_space
#material: 12.7174 0 1 0 grass
#add_dispersion_debye: 1 5.7913 1.0793e-11 grass

查看文件

@@ -0,0 +1,4 @@
#material: 1 0 1 0 free_space
#material: 8 0 1 0 half_space
#material: 12.7174 0 1 0 grass
#add_dispersion_debye: 1 5.7913 1.0793e-11 grass

查看文件

@@ -0,0 +1,95 @@
#material: 1 0 1 0 free_space
#material: 10.1009 0.0737926 1 0 |10.1009+0.0738+1.0000+0.0000|
#add_dispersion_debye: 1 7.57011 8.0994e-12 |10.1009+0.0738+1.0000+0.0000|
#material: 10.224 0.0745944 1 0 |10.2240+0.0746+1.0000+0.0000|
#add_dispersion_debye: 1 7.83158 8.0994e-12 |10.2240+0.0746+1.0000+0.0000|
#material: 10.3465 0.0753865 1 0 |10.3465+0.0754+1.0000+0.0000|
#add_dispersion_debye: 1 8.09586 8.0994e-12 |10.3465+0.0754+1.0000+0.0000|
#material: 10.4684 0.0761693 1 0 |10.4684+0.0762+1.0000+0.0000|
#add_dispersion_debye: 1 8.36291 8.0994e-12 |10.4684+0.0762+1.0000+0.0000|
#material: 10.5897 0.0769429 1 0 |10.5897+0.0769+1.0000+0.0000|
#add_dispersion_debye: 1 8.6327 8.0994e-12 |10.5897+0.0769+1.0000+0.0000|
#material: 10.7105 0.0777078 1 0 |10.7105+0.0777+1.0000+0.0000|
#add_dispersion_debye: 1 8.9052 8.0994e-12 |10.7105+0.0777+1.0000+0.0000|
#material: 10.8308 0.0784642 1 0 |10.8308+0.0785+1.0000+0.0000|
#add_dispersion_debye: 1 9.18039 8.0994e-12 |10.8308+0.0785+1.0000+0.0000|
#material: 10.9505 0.0792123 1 0 |10.9505+0.0792+1.0000+0.0000|
#add_dispersion_debye: 1 9.45822 8.0994e-12 |10.9505+0.0792+1.0000+0.0000|
#material: 4.35657 0.0235572 1 0 |4.3566+0.0236+1.0000+0.0000|
#add_dispersion_debye: 1 0.209447 8.0994e-12 |4.3566+0.0236+1.0000+0.0000|
#material: 4.55835 0.0263403 1 0 |4.5583+0.0263+1.0000+0.0000|
#add_dispersion_debye: 1 0.297473 8.0994e-12 |4.5583+0.0263+1.0000+0.0000|
#material: 4.75224 0.0288223 1 0 |4.7522+0.0288+1.0000+0.0000|
#add_dispersion_debye: 1 0.394747 8.0994e-12 |4.7522+0.0288+1.0000+0.0000|
#material: 4.93963 0.0310813 1 0 |4.9396+0.0311+1.0000+0.0000|
#add_dispersion_debye: 1 0.500357 8.0994e-12 |4.9396+0.0311+1.0000+0.0000|
#material: 5.1215 0.0331667 1 0 |5.1215+0.0332+1.0000+0.0000|
#add_dispersion_debye: 1 0.61361 8.0994e-12 |5.1215+0.0332+1.0000+0.0000|
#material: 5.2986 0.0351122 1 0 |5.2986+0.0351+1.0000+0.0000|
#add_dispersion_debye: 1 0.733958 8.0994e-12 |5.2986+0.0351+1.0000+0.0000|
#material: 5.4715 0.0369417 1 0 |5.4715+0.0369+1.0000+0.0000|
#add_dispersion_debye: 1 0.860952 8.0994e-12 |5.4715+0.0369+1.0000+0.0000|
#material: 5.64067 0.0386732 1 0 |5.6407+0.0387+1.0000+0.0000|
#add_dispersion_debye: 1 0.994215 8.0994e-12 |5.6407+0.0387+1.0000+0.0000|
#material: 5.80649 0.0403204 1 0 |5.8065+0.0403+1.0000+0.0000|
#add_dispersion_debye: 1 1.13343 8.0994e-12 |5.8065+0.0403+1.0000+0.0000|
#material: 5.96926 0.0418941 1 0 |5.9693+0.0419+1.0000+0.0000|
#add_dispersion_debye: 1 1.27831 8.0994e-12 |5.9693+0.0419+1.0000+0.0000|
#material: 6.12926 0.043403 1 0 |6.1293+0.0434+1.0000+0.0000|
#add_dispersion_debye: 1 1.42863 8.0994e-12 |6.1293+0.0434+1.0000+0.0000|
#material: 6.28671 0.0448543 1 0 |6.2867+0.0449+1.0000+0.0000|
#add_dispersion_debye: 1 1.58416 8.0994e-12 |6.2867+0.0449+1.0000+0.0000|
#material: 6.44181 0.0462539 1 0 |6.4418+0.0463+1.0000+0.0000|
#add_dispersion_debye: 1 1.74471 8.0994e-12 |6.4418+0.0463+1.0000+0.0000|
#material: 6.59474 0.0476066 1 0 |6.5947+0.0476+1.0000+0.0000|
#add_dispersion_debye: 1 1.91011 8.0994e-12 |6.5947+0.0476+1.0000+0.0000|
#material: 6.74564 0.0489169 1 0 |6.7456+0.0489+1.0000+0.0000|
#add_dispersion_debye: 1 2.0802 8.0994e-12 |6.7456+0.0489+1.0000+0.0000|
#material: 6.89465 0.0501882 1 0 |6.8946+0.0502+1.0000+0.0000|
#add_dispersion_debye: 1 2.25484 8.0994e-12 |6.8946+0.0502+1.0000+0.0000|
#material: 7.04188 0.0514238 1 0 |7.0419+0.0514+1.0000+0.0000|
#add_dispersion_debye: 1 2.4339 8.0994e-12 |7.0419+0.0514+1.0000+0.0000|
#material: 7.18745 0.0526264 1 0 |7.1875+0.0526+1.0000+0.0000|
#add_dispersion_debye: 1 2.61725 8.0994e-12 |7.1875+0.0526+1.0000+0.0000|
#material: 7.33146 0.0537984 1 0 |7.3315+0.0538+1.0000+0.0000|
#add_dispersion_debye: 1 2.80478 8.0994e-12 |7.3315+0.0538+1.0000+0.0000|
#material: 7.47398 0.0549419 1 0 |7.4740+0.0549+1.0000+0.0000|
#add_dispersion_debye: 1 2.99639 8.0994e-12 |7.4740+0.0549+1.0000+0.0000|
#material: 7.61511 0.0560589 1 0 |7.6151+0.0561+1.0000+0.0000|
#add_dispersion_debye: 1 3.19199 8.0994e-12 |7.6151+0.0561+1.0000+0.0000|
#material: 7.7549 0.0571511 1 0 |7.7549+0.0572+1.0000+0.0000|
#add_dispersion_debye: 1 3.39148 8.0994e-12 |7.7549+0.0572+1.0000+0.0000|
#material: 7.89344 0.0582199 1 0 |7.8934+0.0582+1.0000+0.0000|
#add_dispersion_debye: 1 3.59477 8.0994e-12 |7.8934+0.0582+1.0000+0.0000|
#material: 8.03078 0.0592667 1 0 |8.0308+0.0593+1.0000+0.0000|
#add_dispersion_debye: 1 3.8018 8.0994e-12 |8.0308+0.0593+1.0000+0.0000|
#material: 8.16697 0.0602929 1 0 |8.1670+0.0603+1.0000+0.0000|
#add_dispersion_debye: 1 4.01247 8.0994e-12 |8.1670+0.0603+1.0000+0.0000|
#material: 8.30207 0.0612995 1 0 |8.3021+0.0613+1.0000+0.0000|
#add_dispersion_debye: 1 4.22673 8.0994e-12 |8.3021+0.0613+1.0000+0.0000|
#material: 8.43613 0.0622875 1 0 |8.4361+0.0623+1.0000+0.0000|
#add_dispersion_debye: 1 4.4445 8.0994e-12 |8.4361+0.0623+1.0000+0.0000|
#material: 8.5692 0.063258 1 0 |8.5692+0.0633+1.0000+0.0000|
#add_dispersion_debye: 1 4.66572 8.0994e-12 |8.5692+0.0633+1.0000+0.0000|
#material: 8.7013 0.0642118 1 0 |8.7013+0.0642+1.0000+0.0000|
#add_dispersion_debye: 1 4.89034 8.0994e-12 |8.7013+0.0642+1.0000+0.0000|
#material: 8.83249 0.0651496 1 0 |8.8325+0.0651+1.0000+0.0000|
#add_dispersion_debye: 1 5.11828 8.0994e-12 |8.8325+0.0651+1.0000+0.0000|
#material: 8.9628 0.0660723 1 0 |8.9628+0.0661+1.0000+0.0000|
#add_dispersion_debye: 1 5.3495 8.0994e-12 |8.9628+0.0661+1.0000+0.0000|
#material: 9.09226 0.0669805 1 0 |9.0923+0.0670+1.0000+0.0000|
#add_dispersion_debye: 1 5.58395 8.0994e-12 |9.0923+0.0670+1.0000+0.0000|
#material: 9.22091 0.0678749 1 0 |9.2209+0.0679+1.0000+0.0000|
#add_dispersion_debye: 1 5.82158 8.0994e-12 |9.2209+0.0679+1.0000+0.0000|
#material: 9.34877 0.068756 1 0 |9.3488+0.0688+1.0000+0.0000|
#add_dispersion_debye: 1 6.06233 8.0994e-12 |9.3488+0.0688+1.0000+0.0000|
#material: 9.47588 0.0696244 1 0 |9.4759+0.0696+1.0000+0.0000|
#add_dispersion_debye: 1 6.30617 8.0994e-12 |9.4759+0.0696+1.0000+0.0000|
#material: 9.60226 0.0704806 1 0 |9.6023+0.0705+1.0000+0.0000|
#add_dispersion_debye: 1 6.55304 8.0994e-12 |9.6023+0.0705+1.0000+0.0000|
#material: 9.72793 0.0713251 1 0 |9.7279+0.0713+1.0000+0.0000|
#add_dispersion_debye: 1 6.80292 8.0994e-12 |9.7279+0.0713+1.0000+0.0000|
#material: 9.85292 0.0721583 1 0 |9.8529+0.0722+1.0000+0.0000|
#add_dispersion_debye: 1 7.05574 8.0994e-12 |9.8529+0.0722+1.0000+0.0000|
#material: 9.97725 0.0729807 1 0 |9.9772+0.0730+1.0000+0.0000|
#add_dispersion_debye: 1 7.31149 8.0994e-12 |9.9772+0.0730+1.0000+0.0000|

查看文件

@@ -0,0 +1,97 @@
#material: 1 0 1 0 free_space
#material: 10.1009 0.0737926 1 0 |10.1009+0.0738+1.0000+0.0000|
#add_dispersion_debye: 1 7.57011 8.0994e-12 |10.1009+0.0738+1.0000+0.0000|
#material: 10.224 0.0745944 1 0 |10.2240+0.0746+1.0000+0.0000|
#add_dispersion_debye: 1 7.83158 8.0994e-12 |10.2240+0.0746+1.0000+0.0000|
#material: 10.3465 0.0753865 1 0 |10.3465+0.0754+1.0000+0.0000|
#add_dispersion_debye: 1 8.09586 8.0994e-12 |10.3465+0.0754+1.0000+0.0000|
#material: 10.4684 0.0761693 1 0 |10.4684+0.0762+1.0000+0.0000|
#add_dispersion_debye: 1 8.36291 8.0994e-12 |10.4684+0.0762+1.0000+0.0000|
#material: 10.5897 0.0769429 1 0 |10.5897+0.0769+1.0000+0.0000|
#add_dispersion_debye: 1 8.6327 8.0994e-12 |10.5897+0.0769+1.0000+0.0000|
#material: 10.7105 0.0777078 1 0 |10.7105+0.0777+1.0000+0.0000|
#add_dispersion_debye: 1 8.9052 8.0994e-12 |10.7105+0.0777+1.0000+0.0000|
#material: 10.8308 0.0784642 1 0 |10.8308+0.0785+1.0000+0.0000|
#add_dispersion_debye: 1 9.18039 8.0994e-12 |10.8308+0.0785+1.0000+0.0000|
#material: 10.9505 0.0792123 1 0 |10.9505+0.0792+1.0000+0.0000|
#add_dispersion_debye: 1 9.45822 8.0994e-12 |10.9505+0.0792+1.0000+0.0000|
#material: 3.67252 0.0108321 1 0 |3.6725+0.0108+1.0000+0.0000|
#add_dispersion_debye: 1 0.0182374 8.0994e-12 |3.6725+0.0108+1.0000+0.0000|
#material: 4.35657 0.0235572 1 0 |4.3566+0.0236+1.0000+0.0000|
#add_dispersion_debye: 1 0.209447 8.0994e-12 |4.3566+0.0236+1.0000+0.0000|
#material: 4.55835 0.0263403 1 0 |4.5583+0.0263+1.0000+0.0000|
#add_dispersion_debye: 1 0.297473 8.0994e-12 |4.5583+0.0263+1.0000+0.0000|
#material: 4.75224 0.0288223 1 0 |4.7522+0.0288+1.0000+0.0000|
#add_dispersion_debye: 1 0.394747 8.0994e-12 |4.7522+0.0288+1.0000+0.0000|
#material: 4.93963 0.0310813 1 0 |4.9396+0.0311+1.0000+0.0000|
#add_dispersion_debye: 1 0.500357 8.0994e-12 |4.9396+0.0311+1.0000+0.0000|
#material: 5.1215 0.0331667 1 0 |5.1215+0.0332+1.0000+0.0000|
#add_dispersion_debye: 1 0.61361 8.0994e-12 |5.1215+0.0332+1.0000+0.0000|
#material: 5.2986 0.0351122 1 0 |5.2986+0.0351+1.0000+0.0000|
#add_dispersion_debye: 1 0.733958 8.0994e-12 |5.2986+0.0351+1.0000+0.0000|
#material: 5.4715 0.0369417 1 0 |5.4715+0.0369+1.0000+0.0000|
#add_dispersion_debye: 1 0.860952 8.0994e-12 |5.4715+0.0369+1.0000+0.0000|
#material: 5.64067 0.0386732 1 0 |5.6407+0.0387+1.0000+0.0000|
#add_dispersion_debye: 1 0.994215 8.0994e-12 |5.6407+0.0387+1.0000+0.0000|
#material: 5.80649 0.0403204 1 0 |5.8065+0.0403+1.0000+0.0000|
#add_dispersion_debye: 1 1.13343 8.0994e-12 |5.8065+0.0403+1.0000+0.0000|
#material: 5.96926 0.0418941 1 0 |5.9693+0.0419+1.0000+0.0000|
#add_dispersion_debye: 1 1.27831 8.0994e-12 |5.9693+0.0419+1.0000+0.0000|
#material: 6.12926 0.043403 1 0 |6.1293+0.0434+1.0000+0.0000|
#add_dispersion_debye: 1 1.42863 8.0994e-12 |6.1293+0.0434+1.0000+0.0000|
#material: 6.28671 0.0448543 1 0 |6.2867+0.0449+1.0000+0.0000|
#add_dispersion_debye: 1 1.58416 8.0994e-12 |6.2867+0.0449+1.0000+0.0000|
#material: 6.44181 0.0462539 1 0 |6.4418+0.0463+1.0000+0.0000|
#add_dispersion_debye: 1 1.74471 8.0994e-12 |6.4418+0.0463+1.0000+0.0000|
#material: 6.59474 0.0476066 1 0 |6.5947+0.0476+1.0000+0.0000|
#add_dispersion_debye: 1 1.91011 8.0994e-12 |6.5947+0.0476+1.0000+0.0000|
#material: 6.74564 0.0489169 1 0 |6.7456+0.0489+1.0000+0.0000|
#add_dispersion_debye: 1 2.0802 8.0994e-12 |6.7456+0.0489+1.0000+0.0000|
#material: 6.89465 0.0501882 1 0 |6.8946+0.0502+1.0000+0.0000|
#add_dispersion_debye: 1 2.25484 8.0994e-12 |6.8946+0.0502+1.0000+0.0000|
#material: 7.04188 0.0514238 1 0 |7.0419+0.0514+1.0000+0.0000|
#add_dispersion_debye: 1 2.4339 8.0994e-12 |7.0419+0.0514+1.0000+0.0000|
#material: 7.18745 0.0526264 1 0 |7.1875+0.0526+1.0000+0.0000|
#add_dispersion_debye: 1 2.61725 8.0994e-12 |7.1875+0.0526+1.0000+0.0000|
#material: 7.33146 0.0537984 1 0 |7.3315+0.0538+1.0000+0.0000|
#add_dispersion_debye: 1 2.80478 8.0994e-12 |7.3315+0.0538+1.0000+0.0000|
#material: 7.47398 0.0549419 1 0 |7.4740+0.0549+1.0000+0.0000|
#add_dispersion_debye: 1 2.99639 8.0994e-12 |7.4740+0.0549+1.0000+0.0000|
#material: 7.61511 0.0560589 1 0 |7.6151+0.0561+1.0000+0.0000|
#add_dispersion_debye: 1 3.19199 8.0994e-12 |7.6151+0.0561+1.0000+0.0000|
#material: 7.7549 0.0571511 1 0 |7.7549+0.0572+1.0000+0.0000|
#add_dispersion_debye: 1 3.39148 8.0994e-12 |7.7549+0.0572+1.0000+0.0000|
#material: 7.89344 0.0582199 1 0 |7.8934+0.0582+1.0000+0.0000|
#add_dispersion_debye: 1 3.59477 8.0994e-12 |7.8934+0.0582+1.0000+0.0000|
#material: 8.03078 0.0592667 1 0 |8.0308+0.0593+1.0000+0.0000|
#add_dispersion_debye: 1 3.8018 8.0994e-12 |8.0308+0.0593+1.0000+0.0000|
#material: 8.16697 0.0602929 1 0 |8.1670+0.0603+1.0000+0.0000|
#add_dispersion_debye: 1 4.01247 8.0994e-12 |8.1670+0.0603+1.0000+0.0000|
#material: 8.30207 0.0612995 1 0 |8.3021+0.0613+1.0000+0.0000|
#add_dispersion_debye: 1 4.22673 8.0994e-12 |8.3021+0.0613+1.0000+0.0000|
#material: 8.43613 0.0622875 1 0 |8.4361+0.0623+1.0000+0.0000|
#add_dispersion_debye: 1 4.4445 8.0994e-12 |8.4361+0.0623+1.0000+0.0000|
#material: 8.5692 0.063258 1 0 |8.5692+0.0633+1.0000+0.0000|
#add_dispersion_debye: 1 4.66572 8.0994e-12 |8.5692+0.0633+1.0000+0.0000|
#material: 8.7013 0.0642118 1 0 |8.7013+0.0642+1.0000+0.0000|
#add_dispersion_debye: 1 4.89034 8.0994e-12 |8.7013+0.0642+1.0000+0.0000|
#material: 8.83249 0.0651496 1 0 |8.8325+0.0651+1.0000+0.0000|
#add_dispersion_debye: 1 5.11828 8.0994e-12 |8.8325+0.0651+1.0000+0.0000|
#material: 8.9628 0.0660723 1 0 |8.9628+0.0661+1.0000+0.0000|
#add_dispersion_debye: 1 5.3495 8.0994e-12 |8.9628+0.0661+1.0000+0.0000|
#material: 9.09226 0.0669805 1 0 |9.0923+0.0670+1.0000+0.0000|
#add_dispersion_debye: 1 5.58395 8.0994e-12 |9.0923+0.0670+1.0000+0.0000|
#material: 9.22091 0.0678749 1 0 |9.2209+0.0679+1.0000+0.0000|
#add_dispersion_debye: 1 5.82158 8.0994e-12 |9.2209+0.0679+1.0000+0.0000|
#material: 9.34877 0.068756 1 0 |9.3488+0.0688+1.0000+0.0000|
#add_dispersion_debye: 1 6.06233 8.0994e-12 |9.3488+0.0688+1.0000+0.0000|
#material: 9.47588 0.0696244 1 0 |9.4759+0.0696+1.0000+0.0000|
#add_dispersion_debye: 1 6.30617 8.0994e-12 |9.4759+0.0696+1.0000+0.0000|
#material: 9.60226 0.0704806 1 0 |9.6023+0.0705+1.0000+0.0000|
#add_dispersion_debye: 1 6.55304 8.0994e-12 |9.6023+0.0705+1.0000+0.0000|
#material: 9.72793 0.0713251 1 0 |9.7279+0.0713+1.0000+0.0000|
#add_dispersion_debye: 1 6.80292 8.0994e-12 |9.7279+0.0713+1.0000+0.0000|
#material: 9.85292 0.0721583 1 0 |9.8529+0.0722+1.0000+0.0000|
#add_dispersion_debye: 1 7.05574 8.0994e-12 |9.8529+0.0722+1.0000+0.0000|
#material: 9.97725 0.0729807 1 0 |9.9772+0.0730+1.0000+0.0000|
#add_dispersion_debye: 1 7.31149 8.0994e-12 |9.9772+0.0730+1.0000+0.0000|

查看文件

@@ -0,0 +1,91 @@
#material: 1 0 1 0 free_space
#material: 10.1009 0.0737926 1 0 |10.1009+0.0738+1.0000+0.0000|
#add_dispersion_debye: 1 7.57011 8.0994e-12 |10.1009+0.0738+1.0000+0.0000|
#material: 10.224 0.0745944 1 0 |10.2240+0.0746+1.0000+0.0000|
#add_dispersion_debye: 1 7.83158 8.0994e-12 |10.2240+0.0746+1.0000+0.0000|
#material: 10.3465 0.0753865 1 0 |10.3465+0.0754+1.0000+0.0000|
#add_dispersion_debye: 1 8.09586 8.0994e-12 |10.3465+0.0754+1.0000+0.0000|
#material: 10.9505 0.0792123 1 0 |10.9505+0.0792+1.0000+0.0000|
#add_dispersion_debye: 1 9.45822 8.0994e-12 |10.9505+0.0792+1.0000+0.0000|
#material: 3.67252 0.0108321 1 0 |3.6725+0.0108+1.0000+0.0000|
#add_dispersion_debye: 1 0.0182374 8.0994e-12 |3.6725+0.0108+1.0000+0.0000|
#material: 3.91944 0.0163865 1 0 |3.9194+0.0164+1.0000+0.0000|
#add_dispersion_debye: 1 0.0669568 8.0994e-12 |3.9194+0.0164+1.0000+0.0000|
#material: 4.35657 0.0235572 1 0 |4.3566+0.0236+1.0000+0.0000|
#add_dispersion_debye: 1 0.209447 8.0994e-12 |4.3566+0.0236+1.0000+0.0000|
#material: 4.55835 0.0263403 1 0 |4.5583+0.0263+1.0000+0.0000|
#add_dispersion_debye: 1 0.297473 8.0994e-12 |4.5583+0.0263+1.0000+0.0000|
#material: 4.75224 0.0288223 1 0 |4.7522+0.0288+1.0000+0.0000|
#add_dispersion_debye: 1 0.394747 8.0994e-12 |4.7522+0.0288+1.0000+0.0000|
#material: 4.93963 0.0310813 1 0 |4.9396+0.0311+1.0000+0.0000|
#add_dispersion_debye: 1 0.500357 8.0994e-12 |4.9396+0.0311+1.0000+0.0000|
#material: 5.1215 0.0331667 1 0 |5.1215+0.0332+1.0000+0.0000|
#add_dispersion_debye: 1 0.61361 8.0994e-12 |5.1215+0.0332+1.0000+0.0000|
#material: 5.2986 0.0351122 1 0 |5.2986+0.0351+1.0000+0.0000|
#add_dispersion_debye: 1 0.733958 8.0994e-12 |5.2986+0.0351+1.0000+0.0000|
#material: 5.4715 0.0369417 1 0 |5.4715+0.0369+1.0000+0.0000|
#add_dispersion_debye: 1 0.860952 8.0994e-12 |5.4715+0.0369+1.0000+0.0000|
#material: 5.64067 0.0386732 1 0 |5.6407+0.0387+1.0000+0.0000|
#add_dispersion_debye: 1 0.994215 8.0994e-12 |5.6407+0.0387+1.0000+0.0000|
#material: 5.80649 0.0403204 1 0 |5.8065+0.0403+1.0000+0.0000|
#add_dispersion_debye: 1 1.13343 8.0994e-12 |5.8065+0.0403+1.0000+0.0000|
#material: 5.96926 0.0418941 1 0 |5.9693+0.0419+1.0000+0.0000|
#add_dispersion_debye: 1 1.27831 8.0994e-12 |5.9693+0.0419+1.0000+0.0000|
#material: 6.12926 0.043403 1 0 |6.1293+0.0434+1.0000+0.0000|
#add_dispersion_debye: 1 1.42863 8.0994e-12 |6.1293+0.0434+1.0000+0.0000|
#material: 6.28671 0.0448543 1 0 |6.2867+0.0449+1.0000+0.0000|
#add_dispersion_debye: 1 1.58416 8.0994e-12 |6.2867+0.0449+1.0000+0.0000|
#material: 6.44181 0.0462539 1 0 |6.4418+0.0463+1.0000+0.0000|
#add_dispersion_debye: 1 1.74471 8.0994e-12 |6.4418+0.0463+1.0000+0.0000|
#material: 6.59474 0.0476066 1 0 |6.5947+0.0476+1.0000+0.0000|
#add_dispersion_debye: 1 1.91011 8.0994e-12 |6.5947+0.0476+1.0000+0.0000|
#material: 6.74564 0.0489169 1 0 |6.7456+0.0489+1.0000+0.0000|
#add_dispersion_debye: 1 2.0802 8.0994e-12 |6.7456+0.0489+1.0000+0.0000|
#material: 6.89465 0.0501882 1 0 |6.8946+0.0502+1.0000+0.0000|
#add_dispersion_debye: 1 2.25484 8.0994e-12 |6.8946+0.0502+1.0000+0.0000|
#material: 7.04188 0.0514238 1 0 |7.0419+0.0514+1.0000+0.0000|
#add_dispersion_debye: 1 2.4339 8.0994e-12 |7.0419+0.0514+1.0000+0.0000|
#material: 7.18745 0.0526264 1 0 |7.1875+0.0526+1.0000+0.0000|
#add_dispersion_debye: 1 2.61725 8.0994e-12 |7.1875+0.0526+1.0000+0.0000|
#material: 7.33146 0.0537984 1 0 |7.3315+0.0538+1.0000+0.0000|
#add_dispersion_debye: 1 2.80478 8.0994e-12 |7.3315+0.0538+1.0000+0.0000|
#material: 7.47398 0.0549419 1 0 |7.4740+0.0549+1.0000+0.0000|
#add_dispersion_debye: 1 2.99639 8.0994e-12 |7.4740+0.0549+1.0000+0.0000|
#material: 7.61511 0.0560589 1 0 |7.6151+0.0561+1.0000+0.0000|
#add_dispersion_debye: 1 3.19199 8.0994e-12 |7.6151+0.0561+1.0000+0.0000|
#material: 7.7549 0.0571511 1 0 |7.7549+0.0572+1.0000+0.0000|
#add_dispersion_debye: 1 3.39148 8.0994e-12 |7.7549+0.0572+1.0000+0.0000|
#material: 7.89344 0.0582199 1 0 |7.8934+0.0582+1.0000+0.0000|
#add_dispersion_debye: 1 3.59477 8.0994e-12 |7.8934+0.0582+1.0000+0.0000|
#material: 8.03078 0.0592667 1 0 |8.0308+0.0593+1.0000+0.0000|
#add_dispersion_debye: 1 3.8018 8.0994e-12 |8.0308+0.0593+1.0000+0.0000|
#material: 8.16697 0.0602929 1 0 |8.1670+0.0603+1.0000+0.0000|
#add_dispersion_debye: 1 4.01247 8.0994e-12 |8.1670+0.0603+1.0000+0.0000|
#material: 8.30207 0.0612995 1 0 |8.3021+0.0613+1.0000+0.0000|
#add_dispersion_debye: 1 4.22673 8.0994e-12 |8.3021+0.0613+1.0000+0.0000|
#material: 8.43613 0.0622875 1 0 |8.4361+0.0623+1.0000+0.0000|
#add_dispersion_debye: 1 4.4445 8.0994e-12 |8.4361+0.0623+1.0000+0.0000|
#material: 8.5692 0.063258 1 0 |8.5692+0.0633+1.0000+0.0000|
#add_dispersion_debye: 1 4.66572 8.0994e-12 |8.5692+0.0633+1.0000+0.0000|
#material: 8.7013 0.0642118 1 0 |8.7013+0.0642+1.0000+0.0000|
#add_dispersion_debye: 1 4.89034 8.0994e-12 |8.7013+0.0642+1.0000+0.0000|
#material: 8.83249 0.0651496 1 0 |8.8325+0.0651+1.0000+0.0000|
#add_dispersion_debye: 1 5.11828 8.0994e-12 |8.8325+0.0651+1.0000+0.0000|
#material: 8.9628 0.0660723 1 0 |8.9628+0.0661+1.0000+0.0000|
#add_dispersion_debye: 1 5.3495 8.0994e-12 |8.9628+0.0661+1.0000+0.0000|
#material: 9.09226 0.0669805 1 0 |9.0923+0.0670+1.0000+0.0000|
#add_dispersion_debye: 1 5.58395 8.0994e-12 |9.0923+0.0670+1.0000+0.0000|
#material: 9.22091 0.0678749 1 0 |9.2209+0.0679+1.0000+0.0000|
#add_dispersion_debye: 1 5.82158 8.0994e-12 |9.2209+0.0679+1.0000+0.0000|
#material: 9.34877 0.068756 1 0 |9.3488+0.0688+1.0000+0.0000|
#add_dispersion_debye: 1 6.06233 8.0994e-12 |9.3488+0.0688+1.0000+0.0000|
#material: 9.47588 0.0696244 1 0 |9.4759+0.0696+1.0000+0.0000|
#add_dispersion_debye: 1 6.30617 8.0994e-12 |9.4759+0.0696+1.0000+0.0000|
#material: 9.60226 0.0704806 1 0 |9.6023+0.0705+1.0000+0.0000|
#add_dispersion_debye: 1 6.55304 8.0994e-12 |9.6023+0.0705+1.0000+0.0000|
#material: 9.72793 0.0713251 1 0 |9.7279+0.0713+1.0000+0.0000|
#add_dispersion_debye: 1 6.80292 8.0994e-12 |9.7279+0.0713+1.0000+0.0000|
#material: 9.85292 0.0721583 1 0 |9.8529+0.0722+1.0000+0.0000|
#add_dispersion_debye: 1 7.05574 8.0994e-12 |9.8529+0.0722+1.0000+0.0000|
#material: 9.97725 0.0729807 1 0 |9.9772+0.0730+1.0000+0.0000|
#add_dispersion_debye: 1 7.31149 8.0994e-12 |9.9772+0.0730+1.0000+0.0000|

查看文件

@@ -0,0 +1,99 @@
#material: 1 0 1 0 free_space
#material: 10.1009 0.0737926 1 0 |10.1009+0.0738+1.0000+0.0000|
#add_dispersion_debye: 1 7.57011 8.0994e-12 |10.1009+0.0738+1.0000+0.0000|
#material: 10.224 0.0745944 1 0 |10.2240+0.0746+1.0000+0.0000|
#add_dispersion_debye: 1 7.83158 8.0994e-12 |10.2240+0.0746+1.0000+0.0000|
#material: 10.3465 0.0753865 1 0 |10.3465+0.0754+1.0000+0.0000|
#add_dispersion_debye: 1 8.09586 8.0994e-12 |10.3465+0.0754+1.0000+0.0000|
#material: 10.4684 0.0761693 1 0 |10.4684+0.0762+1.0000+0.0000|
#add_dispersion_debye: 1 8.36291 8.0994e-12 |10.4684+0.0762+1.0000+0.0000|
#material: 10.5897 0.0769429 1 0 |10.5897+0.0769+1.0000+0.0000|
#add_dispersion_debye: 1 8.6327 8.0994e-12 |10.5897+0.0769+1.0000+0.0000|
#material: 10.7105 0.0777078 1 0 |10.7105+0.0777+1.0000+0.0000|
#add_dispersion_debye: 1 8.9052 8.0994e-12 |10.7105+0.0777+1.0000+0.0000|
#material: 10.8308 0.0784642 1 0 |10.8308+0.0785+1.0000+0.0000|
#add_dispersion_debye: 1 9.18039 8.0994e-12 |10.8308+0.0785+1.0000+0.0000|
#material: 10.9505 0.0792123 1 0 |10.9505+0.0792+1.0000+0.0000|
#add_dispersion_debye: 1 9.45822 8.0994e-12 |10.9505+0.0792+1.0000+0.0000|
#material: 3.67252 0.0108321 1 0 |3.6725+0.0108+1.0000+0.0000|
#add_dispersion_debye: 1 0.0182374 8.0994e-12 |3.6725+0.0108+1.0000+0.0000|
#material: 4.1448 0.0203355 1 0 |4.1448+0.0203+1.0000+0.0000|
#add_dispersion_debye: 1 0.131947 8.0994e-12 |4.1448+0.0203+1.0000+0.0000|
#material: 4.35657 0.0235572 1 0 |4.3566+0.0236+1.0000+0.0000|
#add_dispersion_debye: 1 0.209447 8.0994e-12 |4.3566+0.0236+1.0000+0.0000|
#material: 4.55835 0.0263403 1 0 |4.5583+0.0263+1.0000+0.0000|
#add_dispersion_debye: 1 0.297473 8.0994e-12 |4.5583+0.0263+1.0000+0.0000|
#material: 4.75224 0.0288223 1 0 |4.7522+0.0288+1.0000+0.0000|
#add_dispersion_debye: 1 0.394747 8.0994e-12 |4.7522+0.0288+1.0000+0.0000|
#material: 4.93963 0.0310813 1 0 |4.9396+0.0311+1.0000+0.0000|
#add_dispersion_debye: 1 0.500357 8.0994e-12 |4.9396+0.0311+1.0000+0.0000|
#material: 5.1215 0.0331667 1 0 |5.1215+0.0332+1.0000+0.0000|
#add_dispersion_debye: 1 0.61361 8.0994e-12 |5.1215+0.0332+1.0000+0.0000|
#material: 5.2986 0.0351122 1 0 |5.2986+0.0351+1.0000+0.0000|
#add_dispersion_debye: 1 0.733958 8.0994e-12 |5.2986+0.0351+1.0000+0.0000|
#material: 5.4715 0.0369417 1 0 |5.4715+0.0369+1.0000+0.0000|
#add_dispersion_debye: 1 0.860952 8.0994e-12 |5.4715+0.0369+1.0000+0.0000|
#material: 5.64067 0.0386732 1 0 |5.6407+0.0387+1.0000+0.0000|
#add_dispersion_debye: 1 0.994215 8.0994e-12 |5.6407+0.0387+1.0000+0.0000|
#material: 5.80649 0.0403204 1 0 |5.8065+0.0403+1.0000+0.0000|
#add_dispersion_debye: 1 1.13343 8.0994e-12 |5.8065+0.0403+1.0000+0.0000|
#material: 5.96926 0.0418941 1 0 |5.9693+0.0419+1.0000+0.0000|
#add_dispersion_debye: 1 1.27831 8.0994e-12 |5.9693+0.0419+1.0000+0.0000|
#material: 6.12926 0.043403 1 0 |6.1293+0.0434+1.0000+0.0000|
#add_dispersion_debye: 1 1.42863 8.0994e-12 |6.1293+0.0434+1.0000+0.0000|
#material: 6.28671 0.0448543 1 0 |6.2867+0.0449+1.0000+0.0000|
#add_dispersion_debye: 1 1.58416 8.0994e-12 |6.2867+0.0449+1.0000+0.0000|
#material: 6.44181 0.0462539 1 0 |6.4418+0.0463+1.0000+0.0000|
#add_dispersion_debye: 1 1.74471 8.0994e-12 |6.4418+0.0463+1.0000+0.0000|
#material: 6.59474 0.0476066 1 0 |6.5947+0.0476+1.0000+0.0000|
#add_dispersion_debye: 1 1.91011 8.0994e-12 |6.5947+0.0476+1.0000+0.0000|
#material: 6.74564 0.0489169 1 0 |6.7456+0.0489+1.0000+0.0000|
#add_dispersion_debye: 1 2.0802 8.0994e-12 |6.7456+0.0489+1.0000+0.0000|
#material: 6.89465 0.0501882 1 0 |6.8946+0.0502+1.0000+0.0000|
#add_dispersion_debye: 1 2.25484 8.0994e-12 |6.8946+0.0502+1.0000+0.0000|
#material: 7.04188 0.0514238 1 0 |7.0419+0.0514+1.0000+0.0000|
#add_dispersion_debye: 1 2.4339 8.0994e-12 |7.0419+0.0514+1.0000+0.0000|
#material: 7.18745 0.0526264 1 0 |7.1875+0.0526+1.0000+0.0000|
#add_dispersion_debye: 1 2.61725 8.0994e-12 |7.1875+0.0526+1.0000+0.0000|
#material: 7.33146 0.0537984 1 0 |7.3315+0.0538+1.0000+0.0000|
#add_dispersion_debye: 1 2.80478 8.0994e-12 |7.3315+0.0538+1.0000+0.0000|
#material: 7.47398 0.0549419 1 0 |7.4740+0.0549+1.0000+0.0000|
#add_dispersion_debye: 1 2.99639 8.0994e-12 |7.4740+0.0549+1.0000+0.0000|
#material: 7.61511 0.0560589 1 0 |7.6151+0.0561+1.0000+0.0000|
#add_dispersion_debye: 1 3.19199 8.0994e-12 |7.6151+0.0561+1.0000+0.0000|
#material: 7.7549 0.0571511 1 0 |7.7549+0.0572+1.0000+0.0000|
#add_dispersion_debye: 1 3.39148 8.0994e-12 |7.7549+0.0572+1.0000+0.0000|
#material: 7.89344 0.0582199 1 0 |7.8934+0.0582+1.0000+0.0000|
#add_dispersion_debye: 1 3.59477 8.0994e-12 |7.8934+0.0582+1.0000+0.0000|
#material: 8.03078 0.0592667 1 0 |8.0308+0.0593+1.0000+0.0000|
#add_dispersion_debye: 1 3.8018 8.0994e-12 |8.0308+0.0593+1.0000+0.0000|
#material: 8.16697 0.0602929 1 0 |8.1670+0.0603+1.0000+0.0000|
#add_dispersion_debye: 1 4.01247 8.0994e-12 |8.1670+0.0603+1.0000+0.0000|
#material: 8.30207 0.0612995 1 0 |8.3021+0.0613+1.0000+0.0000|
#add_dispersion_debye: 1 4.22673 8.0994e-12 |8.3021+0.0613+1.0000+0.0000|
#material: 8.43613 0.0622875 1 0 |8.4361+0.0623+1.0000+0.0000|
#add_dispersion_debye: 1 4.4445 8.0994e-12 |8.4361+0.0623+1.0000+0.0000|
#material: 8.5692 0.063258 1 0 |8.5692+0.0633+1.0000+0.0000|
#add_dispersion_debye: 1 4.66572 8.0994e-12 |8.5692+0.0633+1.0000+0.0000|
#material: 8.7013 0.0642118 1 0 |8.7013+0.0642+1.0000+0.0000|
#add_dispersion_debye: 1 4.89034 8.0994e-12 |8.7013+0.0642+1.0000+0.0000|
#material: 8.83249 0.0651496 1 0 |8.8325+0.0651+1.0000+0.0000|
#add_dispersion_debye: 1 5.11828 8.0994e-12 |8.8325+0.0651+1.0000+0.0000|
#material: 8.9628 0.0660723 1 0 |8.9628+0.0661+1.0000+0.0000|
#add_dispersion_debye: 1 5.3495 8.0994e-12 |8.9628+0.0661+1.0000+0.0000|
#material: 9.09226 0.0669805 1 0 |9.0923+0.0670+1.0000+0.0000|
#add_dispersion_debye: 1 5.58395 8.0994e-12 |9.0923+0.0670+1.0000+0.0000|
#material: 9.22091 0.0678749 1 0 |9.2209+0.0679+1.0000+0.0000|
#add_dispersion_debye: 1 5.82158 8.0994e-12 |9.2209+0.0679+1.0000+0.0000|
#material: 9.34877 0.068756 1 0 |9.3488+0.0688+1.0000+0.0000|
#add_dispersion_debye: 1 6.06233 8.0994e-12 |9.3488+0.0688+1.0000+0.0000|
#material: 9.47588 0.0696244 1 0 |9.4759+0.0696+1.0000+0.0000|
#add_dispersion_debye: 1 6.30617 8.0994e-12 |9.4759+0.0696+1.0000+0.0000|
#material: 9.60226 0.0704806 1 0 |9.6023+0.0705+1.0000+0.0000|
#add_dispersion_debye: 1 6.55304 8.0994e-12 |9.6023+0.0705+1.0000+0.0000|
#material: 9.72793 0.0713251 1 0 |9.7279+0.0713+1.0000+0.0000|
#add_dispersion_debye: 1 6.80292 8.0994e-12 |9.7279+0.0713+1.0000+0.0000|
#material: 9.85292 0.0721583 1 0 |9.8529+0.0722+1.0000+0.0000|
#add_dispersion_debye: 1 7.05574 8.0994e-12 |9.8529+0.0722+1.0000+0.0000|
#material: 9.97725 0.0729807 1 0 |9.9772+0.0730+1.0000+0.0000|
#add_dispersion_debye: 1 7.31149 8.0994e-12 |9.9772+0.0730+1.0000+0.0000|

查看文件

@@ -0,0 +1,95 @@
#material: 1 0 1 0 free_space
#material: 4.9 0 1 0 water
#add_dispersion_debye: 1 73.3389 8.0994e-12 water
#material: 10.1009 0.0737926 1 0 |10.1009+0.0738+1.0000+0.0000|
#add_dispersion_debye: 1 7.57011 8.0994e-12 |10.1009+0.0738+1.0000+0.0000|
#material: 10.224 0.0745944 1 0 |10.2240+0.0746+1.0000+0.0000|
#add_dispersion_debye: 1 7.83158 8.0994e-12 |10.2240+0.0746+1.0000+0.0000|
#material: 10.3465 0.0753865 1 0 |10.3465+0.0754+1.0000+0.0000|
#add_dispersion_debye: 1 8.09586 8.0994e-12 |10.3465+0.0754+1.0000+0.0000|
#material: 10.4684 0.0761693 1 0 |10.4684+0.0762+1.0000+0.0000|
#add_dispersion_debye: 1 8.36291 8.0994e-12 |10.4684+0.0762+1.0000+0.0000|
#material: 10.5897 0.0769429 1 0 |10.5897+0.0769+1.0000+0.0000|
#add_dispersion_debye: 1 8.6327 8.0994e-12 |10.5897+0.0769+1.0000+0.0000|
#material: 10.7105 0.0777078 1 0 |10.7105+0.0777+1.0000+0.0000|
#add_dispersion_debye: 1 8.9052 8.0994e-12 |10.7105+0.0777+1.0000+0.0000|
#material: 10.9505 0.0792123 1 0 |10.9505+0.0792+1.0000+0.0000|
#add_dispersion_debye: 1 9.45822 8.0994e-12 |10.9505+0.0792+1.0000+0.0000|
#material: 3.67252 0.0108321 1 0 |3.6725+0.0108+1.0000+0.0000|
#add_dispersion_debye: 1 0.0182374 8.0994e-12 |3.6725+0.0108+1.0000+0.0000|
#material: 4.55835 0.0263403 1 0 |4.5583+0.0263+1.0000+0.0000|
#add_dispersion_debye: 1 0.297473 8.0994e-12 |4.5583+0.0263+1.0000+0.0000|
#material: 4.75224 0.0288223 1 0 |4.7522+0.0288+1.0000+0.0000|
#add_dispersion_debye: 1 0.394747 8.0994e-12 |4.7522+0.0288+1.0000+0.0000|
#material: 4.93963 0.0310813 1 0 |4.9396+0.0311+1.0000+0.0000|
#add_dispersion_debye: 1 0.500357 8.0994e-12 |4.9396+0.0311+1.0000+0.0000|
#material: 5.1215 0.0331667 1 0 |5.1215+0.0332+1.0000+0.0000|
#add_dispersion_debye: 1 0.61361 8.0994e-12 |5.1215+0.0332+1.0000+0.0000|
#material: 5.2986 0.0351122 1 0 |5.2986+0.0351+1.0000+0.0000|
#add_dispersion_debye: 1 0.733958 8.0994e-12 |5.2986+0.0351+1.0000+0.0000|
#material: 5.4715 0.0369417 1 0 |5.4715+0.0369+1.0000+0.0000|
#add_dispersion_debye: 1 0.860952 8.0994e-12 |5.4715+0.0369+1.0000+0.0000|
#material: 5.64067 0.0386732 1 0 |5.6407+0.0387+1.0000+0.0000|
#add_dispersion_debye: 1 0.994215 8.0994e-12 |5.6407+0.0387+1.0000+0.0000|
#material: 5.80649 0.0403204 1 0 |5.8065+0.0403+1.0000+0.0000|
#add_dispersion_debye: 1 1.13343 8.0994e-12 |5.8065+0.0403+1.0000+0.0000|
#material: 5.96926 0.0418941 1 0 |5.9693+0.0419+1.0000+0.0000|
#add_dispersion_debye: 1 1.27831 8.0994e-12 |5.9693+0.0419+1.0000+0.0000|
#material: 6.12926 0.043403 1 0 |6.1293+0.0434+1.0000+0.0000|
#add_dispersion_debye: 1 1.42863 8.0994e-12 |6.1293+0.0434+1.0000+0.0000|
#material: 6.28671 0.0448543 1 0 |6.2867+0.0449+1.0000+0.0000|
#add_dispersion_debye: 1 1.58416 8.0994e-12 |6.2867+0.0449+1.0000+0.0000|
#material: 6.44181 0.0462539 1 0 |6.4418+0.0463+1.0000+0.0000|
#add_dispersion_debye: 1 1.74471 8.0994e-12 |6.4418+0.0463+1.0000+0.0000|
#material: 6.59474 0.0476066 1 0 |6.5947+0.0476+1.0000+0.0000|
#add_dispersion_debye: 1 1.91011 8.0994e-12 |6.5947+0.0476+1.0000+0.0000|
#material: 6.74564 0.0489169 1 0 |6.7456+0.0489+1.0000+0.0000|
#add_dispersion_debye: 1 2.0802 8.0994e-12 |6.7456+0.0489+1.0000+0.0000|
#material: 6.89465 0.0501882 1 0 |6.8946+0.0502+1.0000+0.0000|
#add_dispersion_debye: 1 2.25484 8.0994e-12 |6.8946+0.0502+1.0000+0.0000|
#material: 7.04188 0.0514238 1 0 |7.0419+0.0514+1.0000+0.0000|
#add_dispersion_debye: 1 2.4339 8.0994e-12 |7.0419+0.0514+1.0000+0.0000|
#material: 7.18745 0.0526264 1 0 |7.1875+0.0526+1.0000+0.0000|
#add_dispersion_debye: 1 2.61725 8.0994e-12 |7.1875+0.0526+1.0000+0.0000|
#material: 7.33146 0.0537984 1 0 |7.3315+0.0538+1.0000+0.0000|
#add_dispersion_debye: 1 2.80478 8.0994e-12 |7.3315+0.0538+1.0000+0.0000|
#material: 7.47398 0.0549419 1 0 |7.4740+0.0549+1.0000+0.0000|
#add_dispersion_debye: 1 2.99639 8.0994e-12 |7.4740+0.0549+1.0000+0.0000|
#material: 7.61511 0.0560589 1 0 |7.6151+0.0561+1.0000+0.0000|
#add_dispersion_debye: 1 3.19199 8.0994e-12 |7.6151+0.0561+1.0000+0.0000|
#material: 7.7549 0.0571511 1 0 |7.7549+0.0572+1.0000+0.0000|
#add_dispersion_debye: 1 3.39148 8.0994e-12 |7.7549+0.0572+1.0000+0.0000|
#material: 7.89344 0.0582199 1 0 |7.8934+0.0582+1.0000+0.0000|
#add_dispersion_debye: 1 3.59477 8.0994e-12 |7.8934+0.0582+1.0000+0.0000|
#material: 8.03078 0.0592667 1 0 |8.0308+0.0593+1.0000+0.0000|
#add_dispersion_debye: 1 3.8018 8.0994e-12 |8.0308+0.0593+1.0000+0.0000|
#material: 8.16697 0.0602929 1 0 |8.1670+0.0603+1.0000+0.0000|
#add_dispersion_debye: 1 4.01247 8.0994e-12 |8.1670+0.0603+1.0000+0.0000|
#material: 8.30207 0.0612995 1 0 |8.3021+0.0613+1.0000+0.0000|
#add_dispersion_debye: 1 4.22673 8.0994e-12 |8.3021+0.0613+1.0000+0.0000|
#material: 8.43613 0.0622875 1 0 |8.4361+0.0623+1.0000+0.0000|
#add_dispersion_debye: 1 4.4445 8.0994e-12 |8.4361+0.0623+1.0000+0.0000|
#material: 8.5692 0.063258 1 0 |8.5692+0.0633+1.0000+0.0000|
#add_dispersion_debye: 1 4.66572 8.0994e-12 |8.5692+0.0633+1.0000+0.0000|
#material: 8.7013 0.0642118 1 0 |8.7013+0.0642+1.0000+0.0000|
#add_dispersion_debye: 1 4.89034 8.0994e-12 |8.7013+0.0642+1.0000+0.0000|
#material: 8.83249 0.0651496 1 0 |8.8325+0.0651+1.0000+0.0000|
#add_dispersion_debye: 1 5.11828 8.0994e-12 |8.8325+0.0651+1.0000+0.0000|
#material: 8.9628 0.0660723 1 0 |8.9628+0.0661+1.0000+0.0000|
#add_dispersion_debye: 1 5.3495 8.0994e-12 |8.9628+0.0661+1.0000+0.0000|
#material: 9.09226 0.0669805 1 0 |9.0923+0.0670+1.0000+0.0000|
#add_dispersion_debye: 1 5.58395 8.0994e-12 |9.0923+0.0670+1.0000+0.0000|
#material: 9.22091 0.0678749 1 0 |9.2209+0.0679+1.0000+0.0000|
#add_dispersion_debye: 1 5.82158 8.0994e-12 |9.2209+0.0679+1.0000+0.0000|
#material: 9.34877 0.068756 1 0 |9.3488+0.0688+1.0000+0.0000|
#add_dispersion_debye: 1 6.06233 8.0994e-12 |9.3488+0.0688+1.0000+0.0000|
#material: 9.47588 0.0696244 1 0 |9.4759+0.0696+1.0000+0.0000|
#add_dispersion_debye: 1 6.30617 8.0994e-12 |9.4759+0.0696+1.0000+0.0000|
#material: 9.60226 0.0704806 1 0 |9.6023+0.0705+1.0000+0.0000|
#add_dispersion_debye: 1 6.55304 8.0994e-12 |9.6023+0.0705+1.0000+0.0000|
#material: 9.72793 0.0713251 1 0 |9.7279+0.0713+1.0000+0.0000|
#add_dispersion_debye: 1 6.80292 8.0994e-12 |9.7279+0.0713+1.0000+0.0000|
#material: 9.85292 0.0721583 1 0 |9.8529+0.0722+1.0000+0.0000|
#add_dispersion_debye: 1 7.05574 8.0994e-12 |9.8529+0.0722+1.0000+0.0000|
#material: 9.97725 0.0729807 1 0 |9.9772+0.0730+1.0000+0.0000|
#add_dispersion_debye: 1 7.31149 8.0994e-12 |9.9772+0.0730+1.0000+0.0000|

查看文件

@@ -0,0 +1,97 @@
#material: 1 0 1 0 free_space
#material: 4.9 0 1 0 water
#add_dispersion_debye: 1 73.3389 8.0994e-12 water
#material: 10.1009 0.0737926 1 0 |10.1009+0.0738+1.0000+0.0000|
#add_dispersion_debye: 1 7.57011 8.0994e-12 |10.1009+0.0738+1.0000+0.0000|
#material: 10.224 0.0745944 1 0 |10.2240+0.0746+1.0000+0.0000|
#add_dispersion_debye: 1 7.83158 8.0994e-12 |10.2240+0.0746+1.0000+0.0000|
#material: 10.3465 0.0753865 1 0 |10.3465+0.0754+1.0000+0.0000|
#add_dispersion_debye: 1 8.09586 8.0994e-12 |10.3465+0.0754+1.0000+0.0000|
#material: 10.4684 0.0761693 1 0 |10.4684+0.0762+1.0000+0.0000|
#add_dispersion_debye: 1 8.36291 8.0994e-12 |10.4684+0.0762+1.0000+0.0000|
#material: 10.5897 0.0769429 1 0 |10.5897+0.0769+1.0000+0.0000|
#add_dispersion_debye: 1 8.6327 8.0994e-12 |10.5897+0.0769+1.0000+0.0000|
#material: 10.7105 0.0777078 1 0 |10.7105+0.0777+1.0000+0.0000|
#add_dispersion_debye: 1 8.9052 8.0994e-12 |10.7105+0.0777+1.0000+0.0000|
#material: 10.8308 0.0784642 1 0 |10.8308+0.0785+1.0000+0.0000|
#add_dispersion_debye: 1 9.18039 8.0994e-12 |10.8308+0.0785+1.0000+0.0000|
#material: 10.9505 0.0792123 1 0 |10.9505+0.0792+1.0000+0.0000|
#add_dispersion_debye: 1 9.45822 8.0994e-12 |10.9505+0.0792+1.0000+0.0000|
#material: 4.35657 0.0235572 1 0 |4.3566+0.0236+1.0000+0.0000|
#add_dispersion_debye: 1 0.209447 8.0994e-12 |4.3566+0.0236+1.0000+0.0000|
#material: 4.55835 0.0263403 1 0 |4.5583+0.0263+1.0000+0.0000|
#add_dispersion_debye: 1 0.297473 8.0994e-12 |4.5583+0.0263+1.0000+0.0000|
#material: 4.75224 0.0288223 1 0 |4.7522+0.0288+1.0000+0.0000|
#add_dispersion_debye: 1 0.394747 8.0994e-12 |4.7522+0.0288+1.0000+0.0000|
#material: 4.93963 0.0310813 1 0 |4.9396+0.0311+1.0000+0.0000|
#add_dispersion_debye: 1 0.500357 8.0994e-12 |4.9396+0.0311+1.0000+0.0000|
#material: 5.1215 0.0331667 1 0 |5.1215+0.0332+1.0000+0.0000|
#add_dispersion_debye: 1 0.61361 8.0994e-12 |5.1215+0.0332+1.0000+0.0000|
#material: 5.2986 0.0351122 1 0 |5.2986+0.0351+1.0000+0.0000|
#add_dispersion_debye: 1 0.733958 8.0994e-12 |5.2986+0.0351+1.0000+0.0000|
#material: 5.4715 0.0369417 1 0 |5.4715+0.0369+1.0000+0.0000|
#add_dispersion_debye: 1 0.860952 8.0994e-12 |5.4715+0.0369+1.0000+0.0000|
#material: 5.64067 0.0386732 1 0 |5.6407+0.0387+1.0000+0.0000|
#add_dispersion_debye: 1 0.994215 8.0994e-12 |5.6407+0.0387+1.0000+0.0000|
#material: 5.80649 0.0403204 1 0 |5.8065+0.0403+1.0000+0.0000|
#add_dispersion_debye: 1 1.13343 8.0994e-12 |5.8065+0.0403+1.0000+0.0000|
#material: 5.96926 0.0418941 1 0 |5.9693+0.0419+1.0000+0.0000|
#add_dispersion_debye: 1 1.27831 8.0994e-12 |5.9693+0.0419+1.0000+0.0000|
#material: 6.12926 0.043403 1 0 |6.1293+0.0434+1.0000+0.0000|
#add_dispersion_debye: 1 1.42863 8.0994e-12 |6.1293+0.0434+1.0000+0.0000|
#material: 6.28671 0.0448543 1 0 |6.2867+0.0449+1.0000+0.0000|
#add_dispersion_debye: 1 1.58416 8.0994e-12 |6.2867+0.0449+1.0000+0.0000|
#material: 6.44181 0.0462539 1 0 |6.4418+0.0463+1.0000+0.0000|
#add_dispersion_debye: 1 1.74471 8.0994e-12 |6.4418+0.0463+1.0000+0.0000|
#material: 6.59474 0.0476066 1 0 |6.5947+0.0476+1.0000+0.0000|
#add_dispersion_debye: 1 1.91011 8.0994e-12 |6.5947+0.0476+1.0000+0.0000|
#material: 6.74564 0.0489169 1 0 |6.7456+0.0489+1.0000+0.0000|
#add_dispersion_debye: 1 2.0802 8.0994e-12 |6.7456+0.0489+1.0000+0.0000|
#material: 6.89465 0.0501882 1 0 |6.8946+0.0502+1.0000+0.0000|
#add_dispersion_debye: 1 2.25484 8.0994e-12 |6.8946+0.0502+1.0000+0.0000|
#material: 7.04188 0.0514238 1 0 |7.0419+0.0514+1.0000+0.0000|
#add_dispersion_debye: 1 2.4339 8.0994e-12 |7.0419+0.0514+1.0000+0.0000|
#material: 7.18745 0.0526264 1 0 |7.1875+0.0526+1.0000+0.0000|
#add_dispersion_debye: 1 2.61725 8.0994e-12 |7.1875+0.0526+1.0000+0.0000|
#material: 7.33146 0.0537984 1 0 |7.3315+0.0538+1.0000+0.0000|
#add_dispersion_debye: 1 2.80478 8.0994e-12 |7.3315+0.0538+1.0000+0.0000|
#material: 7.47398 0.0549419 1 0 |7.4740+0.0549+1.0000+0.0000|
#add_dispersion_debye: 1 2.99639 8.0994e-12 |7.4740+0.0549+1.0000+0.0000|
#material: 7.61511 0.0560589 1 0 |7.6151+0.0561+1.0000+0.0000|
#add_dispersion_debye: 1 3.19199 8.0994e-12 |7.6151+0.0561+1.0000+0.0000|
#material: 7.7549 0.0571511 1 0 |7.7549+0.0572+1.0000+0.0000|
#add_dispersion_debye: 1 3.39148 8.0994e-12 |7.7549+0.0572+1.0000+0.0000|
#material: 7.89344 0.0582199 1 0 |7.8934+0.0582+1.0000+0.0000|
#add_dispersion_debye: 1 3.59477 8.0994e-12 |7.8934+0.0582+1.0000+0.0000|
#material: 8.03078 0.0592667 1 0 |8.0308+0.0593+1.0000+0.0000|
#add_dispersion_debye: 1 3.8018 8.0994e-12 |8.0308+0.0593+1.0000+0.0000|
#material: 8.16697 0.0602929 1 0 |8.1670+0.0603+1.0000+0.0000|
#add_dispersion_debye: 1 4.01247 8.0994e-12 |8.1670+0.0603+1.0000+0.0000|
#material: 8.30207 0.0612995 1 0 |8.3021+0.0613+1.0000+0.0000|
#add_dispersion_debye: 1 4.22673 8.0994e-12 |8.3021+0.0613+1.0000+0.0000|
#material: 8.43613 0.0622875 1 0 |8.4361+0.0623+1.0000+0.0000|
#add_dispersion_debye: 1 4.4445 8.0994e-12 |8.4361+0.0623+1.0000+0.0000|
#material: 8.5692 0.063258 1 0 |8.5692+0.0633+1.0000+0.0000|
#add_dispersion_debye: 1 4.66572 8.0994e-12 |8.5692+0.0633+1.0000+0.0000|
#material: 8.7013 0.0642118 1 0 |8.7013+0.0642+1.0000+0.0000|
#add_dispersion_debye: 1 4.89034 8.0994e-12 |8.7013+0.0642+1.0000+0.0000|
#material: 8.83249 0.0651496 1 0 |8.8325+0.0651+1.0000+0.0000|
#add_dispersion_debye: 1 5.11828 8.0994e-12 |8.8325+0.0651+1.0000+0.0000|
#material: 8.9628 0.0660723 1 0 |8.9628+0.0661+1.0000+0.0000|
#add_dispersion_debye: 1 5.3495 8.0994e-12 |8.9628+0.0661+1.0000+0.0000|
#material: 9.09226 0.0669805 1 0 |9.0923+0.0670+1.0000+0.0000|
#add_dispersion_debye: 1 5.58395 8.0994e-12 |9.0923+0.0670+1.0000+0.0000|
#material: 9.22091 0.0678749 1 0 |9.2209+0.0679+1.0000+0.0000|
#add_dispersion_debye: 1 5.82158 8.0994e-12 |9.2209+0.0679+1.0000+0.0000|
#material: 9.34877 0.068756 1 0 |9.3488+0.0688+1.0000+0.0000|
#add_dispersion_debye: 1 6.06233 8.0994e-12 |9.3488+0.0688+1.0000+0.0000|
#material: 9.47588 0.0696244 1 0 |9.4759+0.0696+1.0000+0.0000|
#add_dispersion_debye: 1 6.30617 8.0994e-12 |9.4759+0.0696+1.0000+0.0000|
#material: 9.60226 0.0704806 1 0 |9.6023+0.0705+1.0000+0.0000|
#add_dispersion_debye: 1 6.55304 8.0994e-12 |9.6023+0.0705+1.0000+0.0000|
#material: 9.72793 0.0713251 1 0 |9.7279+0.0713+1.0000+0.0000|
#add_dispersion_debye: 1 6.80292 8.0994e-12 |9.7279+0.0713+1.0000+0.0000|
#material: 9.85292 0.0721583 1 0 |9.8529+0.0722+1.0000+0.0000|
#add_dispersion_debye: 1 7.05574 8.0994e-12 |9.8529+0.0722+1.0000+0.0000|
#material: 9.97725 0.0729807 1 0 |9.9772+0.0730+1.0000+0.0000|
#add_dispersion_debye: 1 7.31149 8.0994e-12 |9.9772+0.0730+1.0000+0.0000|

查看文件

@@ -0,0 +1,99 @@
#material: 1 0 1 0 free_space
#material: 10.1009 0.0737926 1 0 |10.1009+0.0738+1.0000+0.0000|
#add_dispersion_debye: 1 7.57011 8.0994e-12 |10.1009+0.0738+1.0000+0.0000|
#material: 10.224 0.0745944 1 0 |10.2240+0.0746+1.0000+0.0000|
#add_dispersion_debye: 1 7.83158 8.0994e-12 |10.2240+0.0746+1.0000+0.0000|
#material: 10.3465 0.0753865 1 0 |10.3465+0.0754+1.0000+0.0000|
#add_dispersion_debye: 1 8.09586 8.0994e-12 |10.3465+0.0754+1.0000+0.0000|
#material: 10.4684 0.0761693 1 0 |10.4684+0.0762+1.0000+0.0000|
#add_dispersion_debye: 1 8.36291 8.0994e-12 |10.4684+0.0762+1.0000+0.0000|
#material: 10.5897 0.0769429 1 0 |10.5897+0.0769+1.0000+0.0000|
#add_dispersion_debye: 1 8.6327 8.0994e-12 |10.5897+0.0769+1.0000+0.0000|
#material: 10.7105 0.0777078 1 0 |10.7105+0.0777+1.0000+0.0000|
#add_dispersion_debye: 1 8.9052 8.0994e-12 |10.7105+0.0777+1.0000+0.0000|
#material: 10.8308 0.0784642 1 0 |10.8308+0.0785+1.0000+0.0000|
#add_dispersion_debye: 1 9.18039 8.0994e-12 |10.8308+0.0785+1.0000+0.0000|
#material: 10.9505 0.0792123 1 0 |10.9505+0.0792+1.0000+0.0000|
#add_dispersion_debye: 1 9.45822 8.0994e-12 |10.9505+0.0792+1.0000+0.0000|
#material: 3.67252 0.0108321 1 0 |3.6725+0.0108+1.0000+0.0000|
#add_dispersion_debye: 1 0.0182374 8.0994e-12 |3.6725+0.0108+1.0000+0.0000|
#material: 4.1448 0.0203355 1 0 |4.1448+0.0203+1.0000+0.0000|
#add_dispersion_debye: 1 0.131947 8.0994e-12 |4.1448+0.0203+1.0000+0.0000|
#material: 4.35657 0.0235572 1 0 |4.3566+0.0236+1.0000+0.0000|
#add_dispersion_debye: 1 0.209447 8.0994e-12 |4.3566+0.0236+1.0000+0.0000|
#material: 4.55835 0.0263403 1 0 |4.5583+0.0263+1.0000+0.0000|
#add_dispersion_debye: 1 0.297473 8.0994e-12 |4.5583+0.0263+1.0000+0.0000|
#material: 4.75224 0.0288223 1 0 |4.7522+0.0288+1.0000+0.0000|
#add_dispersion_debye: 1 0.394747 8.0994e-12 |4.7522+0.0288+1.0000+0.0000|
#material: 4.93963 0.0310813 1 0 |4.9396+0.0311+1.0000+0.0000|
#add_dispersion_debye: 1 0.500357 8.0994e-12 |4.9396+0.0311+1.0000+0.0000|
#material: 5.1215 0.0331667 1 0 |5.1215+0.0332+1.0000+0.0000|
#add_dispersion_debye: 1 0.61361 8.0994e-12 |5.1215+0.0332+1.0000+0.0000|
#material: 5.2986 0.0351122 1 0 |5.2986+0.0351+1.0000+0.0000|
#add_dispersion_debye: 1 0.733958 8.0994e-12 |5.2986+0.0351+1.0000+0.0000|
#material: 5.4715 0.0369417 1 0 |5.4715+0.0369+1.0000+0.0000|
#add_dispersion_debye: 1 0.860952 8.0994e-12 |5.4715+0.0369+1.0000+0.0000|
#material: 5.64067 0.0386732 1 0 |5.6407+0.0387+1.0000+0.0000|
#add_dispersion_debye: 1 0.994215 8.0994e-12 |5.6407+0.0387+1.0000+0.0000|
#material: 5.80649 0.0403204 1 0 |5.8065+0.0403+1.0000+0.0000|
#add_dispersion_debye: 1 1.13343 8.0994e-12 |5.8065+0.0403+1.0000+0.0000|
#material: 5.96926 0.0418941 1 0 |5.9693+0.0419+1.0000+0.0000|
#add_dispersion_debye: 1 1.27831 8.0994e-12 |5.9693+0.0419+1.0000+0.0000|
#material: 6.12926 0.043403 1 0 |6.1293+0.0434+1.0000+0.0000|
#add_dispersion_debye: 1 1.42863 8.0994e-12 |6.1293+0.0434+1.0000+0.0000|
#material: 6.28671 0.0448543 1 0 |6.2867+0.0449+1.0000+0.0000|
#add_dispersion_debye: 1 1.58416 8.0994e-12 |6.2867+0.0449+1.0000+0.0000|
#material: 6.44181 0.0462539 1 0 |6.4418+0.0463+1.0000+0.0000|
#add_dispersion_debye: 1 1.74471 8.0994e-12 |6.4418+0.0463+1.0000+0.0000|
#material: 6.59474 0.0476066 1 0 |6.5947+0.0476+1.0000+0.0000|
#add_dispersion_debye: 1 1.91011 8.0994e-12 |6.5947+0.0476+1.0000+0.0000|
#material: 6.74564 0.0489169 1 0 |6.7456+0.0489+1.0000+0.0000|
#add_dispersion_debye: 1 2.0802 8.0994e-12 |6.7456+0.0489+1.0000+0.0000|
#material: 6.89465 0.0501882 1 0 |6.8946+0.0502+1.0000+0.0000|
#add_dispersion_debye: 1 2.25484 8.0994e-12 |6.8946+0.0502+1.0000+0.0000|
#material: 7.04188 0.0514238 1 0 |7.0419+0.0514+1.0000+0.0000|
#add_dispersion_debye: 1 2.4339 8.0994e-12 |7.0419+0.0514+1.0000+0.0000|
#material: 7.18745 0.0526264 1 0 |7.1875+0.0526+1.0000+0.0000|
#add_dispersion_debye: 1 2.61725 8.0994e-12 |7.1875+0.0526+1.0000+0.0000|
#material: 7.33146 0.0537984 1 0 |7.3315+0.0538+1.0000+0.0000|
#add_dispersion_debye: 1 2.80478 8.0994e-12 |7.3315+0.0538+1.0000+0.0000|
#material: 7.47398 0.0549419 1 0 |7.4740+0.0549+1.0000+0.0000|
#add_dispersion_debye: 1 2.99639 8.0994e-12 |7.4740+0.0549+1.0000+0.0000|
#material: 7.61511 0.0560589 1 0 |7.6151+0.0561+1.0000+0.0000|
#add_dispersion_debye: 1 3.19199 8.0994e-12 |7.6151+0.0561+1.0000+0.0000|
#material: 7.7549 0.0571511 1 0 |7.7549+0.0572+1.0000+0.0000|
#add_dispersion_debye: 1 3.39148 8.0994e-12 |7.7549+0.0572+1.0000+0.0000|
#material: 7.89344 0.0582199 1 0 |7.8934+0.0582+1.0000+0.0000|
#add_dispersion_debye: 1 3.59477 8.0994e-12 |7.8934+0.0582+1.0000+0.0000|
#material: 8.03078 0.0592667 1 0 |8.0308+0.0593+1.0000+0.0000|
#add_dispersion_debye: 1 3.8018 8.0994e-12 |8.0308+0.0593+1.0000+0.0000|
#material: 8.16697 0.0602929 1 0 |8.1670+0.0603+1.0000+0.0000|
#add_dispersion_debye: 1 4.01247 8.0994e-12 |8.1670+0.0603+1.0000+0.0000|
#material: 8.30207 0.0612995 1 0 |8.3021+0.0613+1.0000+0.0000|
#add_dispersion_debye: 1 4.22673 8.0994e-12 |8.3021+0.0613+1.0000+0.0000|
#material: 8.43613 0.0622875 1 0 |8.4361+0.0623+1.0000+0.0000|
#add_dispersion_debye: 1 4.4445 8.0994e-12 |8.4361+0.0623+1.0000+0.0000|
#material: 8.5692 0.063258 1 0 |8.5692+0.0633+1.0000+0.0000|
#add_dispersion_debye: 1 4.66572 8.0994e-12 |8.5692+0.0633+1.0000+0.0000|
#material: 8.7013 0.0642118 1 0 |8.7013+0.0642+1.0000+0.0000|
#add_dispersion_debye: 1 4.89034 8.0994e-12 |8.7013+0.0642+1.0000+0.0000|
#material: 8.83249 0.0651496 1 0 |8.8325+0.0651+1.0000+0.0000|
#add_dispersion_debye: 1 5.11828 8.0994e-12 |8.8325+0.0651+1.0000+0.0000|
#material: 8.9628 0.0660723 1 0 |8.9628+0.0661+1.0000+0.0000|
#add_dispersion_debye: 1 5.3495 8.0994e-12 |8.9628+0.0661+1.0000+0.0000|
#material: 9.09226 0.0669805 1 0 |9.0923+0.0670+1.0000+0.0000|
#add_dispersion_debye: 1 5.58395 8.0994e-12 |9.0923+0.0670+1.0000+0.0000|
#material: 9.22091 0.0678749 1 0 |9.2209+0.0679+1.0000+0.0000|
#add_dispersion_debye: 1 5.82158 8.0994e-12 |9.2209+0.0679+1.0000+0.0000|
#material: 9.34877 0.068756 1 0 |9.3488+0.0688+1.0000+0.0000|
#add_dispersion_debye: 1 6.06233 8.0994e-12 |9.3488+0.0688+1.0000+0.0000|
#material: 9.47588 0.0696244 1 0 |9.4759+0.0696+1.0000+0.0000|
#add_dispersion_debye: 1 6.30617 8.0994e-12 |9.4759+0.0696+1.0000+0.0000|
#material: 9.60226 0.0704806 1 0 |9.6023+0.0705+1.0000+0.0000|
#add_dispersion_debye: 1 6.55304 8.0994e-12 |9.6023+0.0705+1.0000+0.0000|
#material: 9.72793 0.0713251 1 0 |9.7279+0.0713+1.0000+0.0000|
#add_dispersion_debye: 1 6.80292 8.0994e-12 |9.7279+0.0713+1.0000+0.0000|
#material: 9.85292 0.0721583 1 0 |9.8529+0.0722+1.0000+0.0000|
#add_dispersion_debye: 1 7.05574 8.0994e-12 |9.8529+0.0722+1.0000+0.0000|
#material: 9.97725 0.0729807 1 0 |9.9772+0.0730+1.0000+0.0000|
#add_dispersion_debye: 1 7.31149 8.0994e-12 |9.9772+0.0730+1.0000+0.0000|

查看文件

@@ -0,0 +1,97 @@
#material: 1 0 1 0 free_space
#material: 10.1009 0.0737926 1 0 |10.1009+0.0738+1.0000+0.0000|
#add_dispersion_debye: 1 7.57011 8.0994e-12 |10.1009+0.0738+1.0000+0.0000|
#material: 10.224 0.0745944 1 0 |10.2240+0.0746+1.0000+0.0000|
#add_dispersion_debye: 1 7.83158 8.0994e-12 |10.2240+0.0746+1.0000+0.0000|
#material: 10.3465 0.0753865 1 0 |10.3465+0.0754+1.0000+0.0000|
#add_dispersion_debye: 1 8.09586 8.0994e-12 |10.3465+0.0754+1.0000+0.0000|
#material: 10.4684 0.0761693 1 0 |10.4684+0.0762+1.0000+0.0000|
#add_dispersion_debye: 1 8.36291 8.0994e-12 |10.4684+0.0762+1.0000+0.0000|
#material: 10.5897 0.0769429 1 0 |10.5897+0.0769+1.0000+0.0000|
#add_dispersion_debye: 1 8.6327 8.0994e-12 |10.5897+0.0769+1.0000+0.0000|
#material: 10.7105 0.0777078 1 0 |10.7105+0.0777+1.0000+0.0000|
#add_dispersion_debye: 1 8.9052 8.0994e-12 |10.7105+0.0777+1.0000+0.0000|
#material: 10.8308 0.0784642 1 0 |10.8308+0.0785+1.0000+0.0000|
#add_dispersion_debye: 1 9.18039 8.0994e-12 |10.8308+0.0785+1.0000+0.0000|
#material: 10.9505 0.0792123 1 0 |10.9505+0.0792+1.0000+0.0000|
#add_dispersion_debye: 1 9.45822 8.0994e-12 |10.9505+0.0792+1.0000+0.0000|
#material: 3.67252 0.0108321 1 0 |3.6725+0.0108+1.0000+0.0000|
#add_dispersion_debye: 1 0.0182374 8.0994e-12 |3.6725+0.0108+1.0000+0.0000|
#material: 4.35657 0.0235572 1 0 |4.3566+0.0236+1.0000+0.0000|
#add_dispersion_debye: 1 0.209447 8.0994e-12 |4.3566+0.0236+1.0000+0.0000|
#material: 4.55835 0.0263403 1 0 |4.5583+0.0263+1.0000+0.0000|
#add_dispersion_debye: 1 0.297473 8.0994e-12 |4.5583+0.0263+1.0000+0.0000|
#material: 4.75224 0.0288223 1 0 |4.7522+0.0288+1.0000+0.0000|
#add_dispersion_debye: 1 0.394747 8.0994e-12 |4.7522+0.0288+1.0000+0.0000|
#material: 4.93963 0.0310813 1 0 |4.9396+0.0311+1.0000+0.0000|
#add_dispersion_debye: 1 0.500357 8.0994e-12 |4.9396+0.0311+1.0000+0.0000|
#material: 5.1215 0.0331667 1 0 |5.1215+0.0332+1.0000+0.0000|
#add_dispersion_debye: 1 0.61361 8.0994e-12 |5.1215+0.0332+1.0000+0.0000|
#material: 5.2986 0.0351122 1 0 |5.2986+0.0351+1.0000+0.0000|
#add_dispersion_debye: 1 0.733958 8.0994e-12 |5.2986+0.0351+1.0000+0.0000|
#material: 5.4715 0.0369417 1 0 |5.4715+0.0369+1.0000+0.0000|
#add_dispersion_debye: 1 0.860952 8.0994e-12 |5.4715+0.0369+1.0000+0.0000|
#material: 5.64067 0.0386732 1 0 |5.6407+0.0387+1.0000+0.0000|
#add_dispersion_debye: 1 0.994215 8.0994e-12 |5.6407+0.0387+1.0000+0.0000|
#material: 5.80649 0.0403204 1 0 |5.8065+0.0403+1.0000+0.0000|
#add_dispersion_debye: 1 1.13343 8.0994e-12 |5.8065+0.0403+1.0000+0.0000|
#material: 5.96926 0.0418941 1 0 |5.9693+0.0419+1.0000+0.0000|
#add_dispersion_debye: 1 1.27831 8.0994e-12 |5.9693+0.0419+1.0000+0.0000|
#material: 6.12926 0.043403 1 0 |6.1293+0.0434+1.0000+0.0000|
#add_dispersion_debye: 1 1.42863 8.0994e-12 |6.1293+0.0434+1.0000+0.0000|
#material: 6.28671 0.0448543 1 0 |6.2867+0.0449+1.0000+0.0000|
#add_dispersion_debye: 1 1.58416 8.0994e-12 |6.2867+0.0449+1.0000+0.0000|
#material: 6.44181 0.0462539 1 0 |6.4418+0.0463+1.0000+0.0000|
#add_dispersion_debye: 1 1.74471 8.0994e-12 |6.4418+0.0463+1.0000+0.0000|
#material: 6.59474 0.0476066 1 0 |6.5947+0.0476+1.0000+0.0000|
#add_dispersion_debye: 1 1.91011 8.0994e-12 |6.5947+0.0476+1.0000+0.0000|
#material: 6.74564 0.0489169 1 0 |6.7456+0.0489+1.0000+0.0000|
#add_dispersion_debye: 1 2.0802 8.0994e-12 |6.7456+0.0489+1.0000+0.0000|
#material: 6.89465 0.0501882 1 0 |6.8946+0.0502+1.0000+0.0000|
#add_dispersion_debye: 1 2.25484 8.0994e-12 |6.8946+0.0502+1.0000+0.0000|
#material: 7.04188 0.0514238 1 0 |7.0419+0.0514+1.0000+0.0000|
#add_dispersion_debye: 1 2.4339 8.0994e-12 |7.0419+0.0514+1.0000+0.0000|
#material: 7.18745 0.0526264 1 0 |7.1875+0.0526+1.0000+0.0000|
#add_dispersion_debye: 1 2.61725 8.0994e-12 |7.1875+0.0526+1.0000+0.0000|
#material: 7.33146 0.0537984 1 0 |7.3315+0.0538+1.0000+0.0000|
#add_dispersion_debye: 1 2.80478 8.0994e-12 |7.3315+0.0538+1.0000+0.0000|
#material: 7.47398 0.0549419 1 0 |7.4740+0.0549+1.0000+0.0000|
#add_dispersion_debye: 1 2.99639 8.0994e-12 |7.4740+0.0549+1.0000+0.0000|
#material: 7.61511 0.0560589 1 0 |7.6151+0.0561+1.0000+0.0000|
#add_dispersion_debye: 1 3.19199 8.0994e-12 |7.6151+0.0561+1.0000+0.0000|
#material: 7.7549 0.0571511 1 0 |7.7549+0.0572+1.0000+0.0000|
#add_dispersion_debye: 1 3.39148 8.0994e-12 |7.7549+0.0572+1.0000+0.0000|
#material: 7.89344 0.0582199 1 0 |7.8934+0.0582+1.0000+0.0000|
#add_dispersion_debye: 1 3.59477 8.0994e-12 |7.8934+0.0582+1.0000+0.0000|
#material: 8.03078 0.0592667 1 0 |8.0308+0.0593+1.0000+0.0000|
#add_dispersion_debye: 1 3.8018 8.0994e-12 |8.0308+0.0593+1.0000+0.0000|
#material: 8.16697 0.0602929 1 0 |8.1670+0.0603+1.0000+0.0000|
#add_dispersion_debye: 1 4.01247 8.0994e-12 |8.1670+0.0603+1.0000+0.0000|
#material: 8.30207 0.0612995 1 0 |8.3021+0.0613+1.0000+0.0000|
#add_dispersion_debye: 1 4.22673 8.0994e-12 |8.3021+0.0613+1.0000+0.0000|
#material: 8.43613 0.0622875 1 0 |8.4361+0.0623+1.0000+0.0000|
#add_dispersion_debye: 1 4.4445 8.0994e-12 |8.4361+0.0623+1.0000+0.0000|
#material: 8.5692 0.063258 1 0 |8.5692+0.0633+1.0000+0.0000|
#add_dispersion_debye: 1 4.66572 8.0994e-12 |8.5692+0.0633+1.0000+0.0000|
#material: 8.7013 0.0642118 1 0 |8.7013+0.0642+1.0000+0.0000|
#add_dispersion_debye: 1 4.89034 8.0994e-12 |8.7013+0.0642+1.0000+0.0000|
#material: 8.83249 0.0651496 1 0 |8.8325+0.0651+1.0000+0.0000|
#add_dispersion_debye: 1 5.11828 8.0994e-12 |8.8325+0.0651+1.0000+0.0000|
#material: 8.9628 0.0660723 1 0 |8.9628+0.0661+1.0000+0.0000|
#add_dispersion_debye: 1 5.3495 8.0994e-12 |8.9628+0.0661+1.0000+0.0000|
#material: 9.09226 0.0669805 1 0 |9.0923+0.0670+1.0000+0.0000|
#add_dispersion_debye: 1 5.58395 8.0994e-12 |9.0923+0.0670+1.0000+0.0000|
#material: 9.22091 0.0678749 1 0 |9.2209+0.0679+1.0000+0.0000|
#add_dispersion_debye: 1 5.82158 8.0994e-12 |9.2209+0.0679+1.0000+0.0000|
#material: 9.34877 0.068756 1 0 |9.3488+0.0688+1.0000+0.0000|
#add_dispersion_debye: 1 6.06233 8.0994e-12 |9.3488+0.0688+1.0000+0.0000|
#material: 9.47588 0.0696244 1 0 |9.4759+0.0696+1.0000+0.0000|
#add_dispersion_debye: 1 6.30617 8.0994e-12 |9.4759+0.0696+1.0000+0.0000|
#material: 9.60226 0.0704806 1 0 |9.6023+0.0705+1.0000+0.0000|
#add_dispersion_debye: 1 6.55304 8.0994e-12 |9.6023+0.0705+1.0000+0.0000|
#material: 9.72793 0.0713251 1 0 |9.7279+0.0713+1.0000+0.0000|
#add_dispersion_debye: 1 6.80292 8.0994e-12 |9.7279+0.0713+1.0000+0.0000|
#material: 9.85292 0.0721583 1 0 |9.8529+0.0722+1.0000+0.0000|
#add_dispersion_debye: 1 7.05574 8.0994e-12 |9.8529+0.0722+1.0000+0.0000|
#material: 9.97725 0.0729807 1 0 |9.9772+0.0730+1.0000+0.0000|
#add_dispersion_debye: 1 7.31149 8.0994e-12 |9.9772+0.0730+1.0000+0.0000|

查看文件

@@ -0,0 +1,97 @@
#material: 1 0 1 0 free_space
#material: 10.1009 0.0737926 1 0 |10.1009+0.0738+1.0000+0.0000|
#add_dispersion_debye: 1 7.57011 8.0994e-12 |10.1009+0.0738+1.0000+0.0000|
#material: 10.224 0.0745944 1 0 |10.2240+0.0746+1.0000+0.0000|
#add_dispersion_debye: 1 7.83158 8.0994e-12 |10.2240+0.0746+1.0000+0.0000|
#material: 10.3465 0.0753865 1 0 |10.3465+0.0754+1.0000+0.0000|
#add_dispersion_debye: 1 8.09586 8.0994e-12 |10.3465+0.0754+1.0000+0.0000|
#material: 10.4684 0.0761693 1 0 |10.4684+0.0762+1.0000+0.0000|
#add_dispersion_debye: 1 8.36291 8.0994e-12 |10.4684+0.0762+1.0000+0.0000|
#material: 10.5897 0.0769429 1 0 |10.5897+0.0769+1.0000+0.0000|
#add_dispersion_debye: 1 8.6327 8.0994e-12 |10.5897+0.0769+1.0000+0.0000|
#material: 10.7105 0.0777078 1 0 |10.7105+0.0777+1.0000+0.0000|
#add_dispersion_debye: 1 8.9052 8.0994e-12 |10.7105+0.0777+1.0000+0.0000|
#material: 10.8308 0.0784642 1 0 |10.8308+0.0785+1.0000+0.0000|
#add_dispersion_debye: 1 9.18039 8.0994e-12 |10.8308+0.0785+1.0000+0.0000|
#material: 10.9505 0.0792123 1 0 |10.9505+0.0792+1.0000+0.0000|
#add_dispersion_debye: 1 9.45822 8.0994e-12 |10.9505+0.0792+1.0000+0.0000|
#material: 3.67252 0.0108321 1 0 |3.6725+0.0108+1.0000+0.0000|
#add_dispersion_debye: 1 0.0182374 8.0994e-12 |3.6725+0.0108+1.0000+0.0000|
#material: 4.35657 0.0235572 1 0 |4.3566+0.0236+1.0000+0.0000|
#add_dispersion_debye: 1 0.209447 8.0994e-12 |4.3566+0.0236+1.0000+0.0000|
#material: 4.55835 0.0263403 1 0 |4.5583+0.0263+1.0000+0.0000|
#add_dispersion_debye: 1 0.297473 8.0994e-12 |4.5583+0.0263+1.0000+0.0000|
#material: 4.75224 0.0288223 1 0 |4.7522+0.0288+1.0000+0.0000|
#add_dispersion_debye: 1 0.394747 8.0994e-12 |4.7522+0.0288+1.0000+0.0000|
#material: 4.93963 0.0310813 1 0 |4.9396+0.0311+1.0000+0.0000|
#add_dispersion_debye: 1 0.500357 8.0994e-12 |4.9396+0.0311+1.0000+0.0000|
#material: 5.1215 0.0331667 1 0 |5.1215+0.0332+1.0000+0.0000|
#add_dispersion_debye: 1 0.61361 8.0994e-12 |5.1215+0.0332+1.0000+0.0000|
#material: 5.2986 0.0351122 1 0 |5.2986+0.0351+1.0000+0.0000|
#add_dispersion_debye: 1 0.733958 8.0994e-12 |5.2986+0.0351+1.0000+0.0000|
#material: 5.4715 0.0369417 1 0 |5.4715+0.0369+1.0000+0.0000|
#add_dispersion_debye: 1 0.860952 8.0994e-12 |5.4715+0.0369+1.0000+0.0000|
#material: 5.64067 0.0386732 1 0 |5.6407+0.0387+1.0000+0.0000|
#add_dispersion_debye: 1 0.994215 8.0994e-12 |5.6407+0.0387+1.0000+0.0000|
#material: 5.80649 0.0403204 1 0 |5.8065+0.0403+1.0000+0.0000|
#add_dispersion_debye: 1 1.13343 8.0994e-12 |5.8065+0.0403+1.0000+0.0000|
#material: 5.96926 0.0418941 1 0 |5.9693+0.0419+1.0000+0.0000|
#add_dispersion_debye: 1 1.27831 8.0994e-12 |5.9693+0.0419+1.0000+0.0000|
#material: 6.12926 0.043403 1 0 |6.1293+0.0434+1.0000+0.0000|
#add_dispersion_debye: 1 1.42863 8.0994e-12 |6.1293+0.0434+1.0000+0.0000|
#material: 6.28671 0.0448543 1 0 |6.2867+0.0449+1.0000+0.0000|
#add_dispersion_debye: 1 1.58416 8.0994e-12 |6.2867+0.0449+1.0000+0.0000|
#material: 6.44181 0.0462539 1 0 |6.4418+0.0463+1.0000+0.0000|
#add_dispersion_debye: 1 1.74471 8.0994e-12 |6.4418+0.0463+1.0000+0.0000|
#material: 6.59474 0.0476066 1 0 |6.5947+0.0476+1.0000+0.0000|
#add_dispersion_debye: 1 1.91011 8.0994e-12 |6.5947+0.0476+1.0000+0.0000|
#material: 6.74564 0.0489169 1 0 |6.7456+0.0489+1.0000+0.0000|
#add_dispersion_debye: 1 2.0802 8.0994e-12 |6.7456+0.0489+1.0000+0.0000|
#material: 6.89465 0.0501882 1 0 |6.8946+0.0502+1.0000+0.0000|
#add_dispersion_debye: 1 2.25484 8.0994e-12 |6.8946+0.0502+1.0000+0.0000|
#material: 7.04188 0.0514238 1 0 |7.0419+0.0514+1.0000+0.0000|
#add_dispersion_debye: 1 2.4339 8.0994e-12 |7.0419+0.0514+1.0000+0.0000|
#material: 7.18745 0.0526264 1 0 |7.1875+0.0526+1.0000+0.0000|
#add_dispersion_debye: 1 2.61725 8.0994e-12 |7.1875+0.0526+1.0000+0.0000|
#material: 7.33146 0.0537984 1 0 |7.3315+0.0538+1.0000+0.0000|
#add_dispersion_debye: 1 2.80478 8.0994e-12 |7.3315+0.0538+1.0000+0.0000|
#material: 7.47398 0.0549419 1 0 |7.4740+0.0549+1.0000+0.0000|
#add_dispersion_debye: 1 2.99639 8.0994e-12 |7.4740+0.0549+1.0000+0.0000|
#material: 7.61511 0.0560589 1 0 |7.6151+0.0561+1.0000+0.0000|
#add_dispersion_debye: 1 3.19199 8.0994e-12 |7.6151+0.0561+1.0000+0.0000|
#material: 7.7549 0.0571511 1 0 |7.7549+0.0572+1.0000+0.0000|
#add_dispersion_debye: 1 3.39148 8.0994e-12 |7.7549+0.0572+1.0000+0.0000|
#material: 7.89344 0.0582199 1 0 |7.8934+0.0582+1.0000+0.0000|
#add_dispersion_debye: 1 3.59477 8.0994e-12 |7.8934+0.0582+1.0000+0.0000|
#material: 8.03078 0.0592667 1 0 |8.0308+0.0593+1.0000+0.0000|
#add_dispersion_debye: 1 3.8018 8.0994e-12 |8.0308+0.0593+1.0000+0.0000|
#material: 8.16697 0.0602929 1 0 |8.1670+0.0603+1.0000+0.0000|
#add_dispersion_debye: 1 4.01247 8.0994e-12 |8.1670+0.0603+1.0000+0.0000|
#material: 8.30207 0.0612995 1 0 |8.3021+0.0613+1.0000+0.0000|
#add_dispersion_debye: 1 4.22673 8.0994e-12 |8.3021+0.0613+1.0000+0.0000|
#material: 8.43613 0.0622875 1 0 |8.4361+0.0623+1.0000+0.0000|
#add_dispersion_debye: 1 4.4445 8.0994e-12 |8.4361+0.0623+1.0000+0.0000|
#material: 8.5692 0.063258 1 0 |8.5692+0.0633+1.0000+0.0000|
#add_dispersion_debye: 1 4.66572 8.0994e-12 |8.5692+0.0633+1.0000+0.0000|
#material: 8.7013 0.0642118 1 0 |8.7013+0.0642+1.0000+0.0000|
#add_dispersion_debye: 1 4.89034 8.0994e-12 |8.7013+0.0642+1.0000+0.0000|
#material: 8.83249 0.0651496 1 0 |8.8325+0.0651+1.0000+0.0000|
#add_dispersion_debye: 1 5.11828 8.0994e-12 |8.8325+0.0651+1.0000+0.0000|
#material: 8.9628 0.0660723 1 0 |8.9628+0.0661+1.0000+0.0000|
#add_dispersion_debye: 1 5.3495 8.0994e-12 |8.9628+0.0661+1.0000+0.0000|
#material: 9.09226 0.0669805 1 0 |9.0923+0.0670+1.0000+0.0000|
#add_dispersion_debye: 1 5.58395 8.0994e-12 |9.0923+0.0670+1.0000+0.0000|
#material: 9.22091 0.0678749 1 0 |9.2209+0.0679+1.0000+0.0000|
#add_dispersion_debye: 1 5.82158 8.0994e-12 |9.2209+0.0679+1.0000+0.0000|
#material: 9.34877 0.068756 1 0 |9.3488+0.0688+1.0000+0.0000|
#add_dispersion_debye: 1 6.06233 8.0994e-12 |9.3488+0.0688+1.0000+0.0000|
#material: 9.47588 0.0696244 1 0 |9.4759+0.0696+1.0000+0.0000|
#add_dispersion_debye: 1 6.30617 8.0994e-12 |9.4759+0.0696+1.0000+0.0000|
#material: 9.60226 0.0704806 1 0 |9.6023+0.0705+1.0000+0.0000|
#add_dispersion_debye: 1 6.55304 8.0994e-12 |9.6023+0.0705+1.0000+0.0000|
#material: 9.72793 0.0713251 1 0 |9.7279+0.0713+1.0000+0.0000|
#add_dispersion_debye: 1 6.80292 8.0994e-12 |9.7279+0.0713+1.0000+0.0000|
#material: 9.85292 0.0721583 1 0 |9.8529+0.0722+1.0000+0.0000|
#add_dispersion_debye: 1 7.05574 8.0994e-12 |9.8529+0.0722+1.0000+0.0000|
#material: 9.97725 0.0729807 1 0 |9.9772+0.0730+1.0000+0.0000|
#add_dispersion_debye: 1 7.31149 8.0994e-12 |9.9772+0.0730+1.0000+0.0000|

查看文件

@@ -0,0 +1,101 @@
#material: 1 0 1 0 free_space
#material: 10.1009 0.0737926 1 0 |10.1009+0.0738+1.0000+0.0000|
#add_dispersion_debye: 1 7.57011 8.0994e-12 |10.1009+0.0738+1.0000+0.0000|
#material: 10.224 0.0745944 1 0 |10.2240+0.0746+1.0000+0.0000|
#add_dispersion_debye: 1 7.83158 8.0994e-12 |10.2240+0.0746+1.0000+0.0000|
#material: 10.3465 0.0753865 1 0 |10.3465+0.0754+1.0000+0.0000|
#add_dispersion_debye: 1 8.09586 8.0994e-12 |10.3465+0.0754+1.0000+0.0000|
#material: 10.4684 0.0761693 1 0 |10.4684+0.0762+1.0000+0.0000|
#add_dispersion_debye: 1 8.36291 8.0994e-12 |10.4684+0.0762+1.0000+0.0000|
#material: 10.5897 0.0769429 1 0 |10.5897+0.0769+1.0000+0.0000|
#add_dispersion_debye: 1 8.6327 8.0994e-12 |10.5897+0.0769+1.0000+0.0000|
#material: 10.7105 0.0777078 1 0 |10.7105+0.0777+1.0000+0.0000|
#add_dispersion_debye: 1 8.9052 8.0994e-12 |10.7105+0.0777+1.0000+0.0000|
#material: 10.8308 0.0784642 1 0 |10.8308+0.0785+1.0000+0.0000|
#add_dispersion_debye: 1 9.18039 8.0994e-12 |10.8308+0.0785+1.0000+0.0000|
#material: 10.9505 0.0792123 1 0 |10.9505+0.0792+1.0000+0.0000|
#add_dispersion_debye: 1 9.45822 8.0994e-12 |10.9505+0.0792+1.0000+0.0000|
#material: 3.67252 0.0108321 1 0 |3.6725+0.0108+1.0000+0.0000|
#add_dispersion_debye: 1 0.0182374 8.0994e-12 |3.6725+0.0108+1.0000+0.0000|
#material: 3.91944 0.0163865 1 0 |3.9194+0.0164+1.0000+0.0000|
#add_dispersion_debye: 1 0.0669568 8.0994e-12 |3.9194+0.0164+1.0000+0.0000|
#material: 4.1448 0.0203355 1 0 |4.1448+0.0203+1.0000+0.0000|
#add_dispersion_debye: 1 0.131947 8.0994e-12 |4.1448+0.0203+1.0000+0.0000|
#material: 4.35657 0.0235572 1 0 |4.3566+0.0236+1.0000+0.0000|
#add_dispersion_debye: 1 0.209447 8.0994e-12 |4.3566+0.0236+1.0000+0.0000|
#material: 4.55835 0.0263403 1 0 |4.5583+0.0263+1.0000+0.0000|
#add_dispersion_debye: 1 0.297473 8.0994e-12 |4.5583+0.0263+1.0000+0.0000|
#material: 4.75224 0.0288223 1 0 |4.7522+0.0288+1.0000+0.0000|
#add_dispersion_debye: 1 0.394747 8.0994e-12 |4.7522+0.0288+1.0000+0.0000|
#material: 4.93963 0.0310813 1 0 |4.9396+0.0311+1.0000+0.0000|
#add_dispersion_debye: 1 0.500357 8.0994e-12 |4.9396+0.0311+1.0000+0.0000|
#material: 5.1215 0.0331667 1 0 |5.1215+0.0332+1.0000+0.0000|
#add_dispersion_debye: 1 0.61361 8.0994e-12 |5.1215+0.0332+1.0000+0.0000|
#material: 5.2986 0.0351122 1 0 |5.2986+0.0351+1.0000+0.0000|
#add_dispersion_debye: 1 0.733958 8.0994e-12 |5.2986+0.0351+1.0000+0.0000|
#material: 5.4715 0.0369417 1 0 |5.4715+0.0369+1.0000+0.0000|
#add_dispersion_debye: 1 0.860952 8.0994e-12 |5.4715+0.0369+1.0000+0.0000|
#material: 5.64067 0.0386732 1 0 |5.6407+0.0387+1.0000+0.0000|
#add_dispersion_debye: 1 0.994215 8.0994e-12 |5.6407+0.0387+1.0000+0.0000|
#material: 5.80649 0.0403204 1 0 |5.8065+0.0403+1.0000+0.0000|
#add_dispersion_debye: 1 1.13343 8.0994e-12 |5.8065+0.0403+1.0000+0.0000|
#material: 5.96926 0.0418941 1 0 |5.9693+0.0419+1.0000+0.0000|
#add_dispersion_debye: 1 1.27831 8.0994e-12 |5.9693+0.0419+1.0000+0.0000|
#material: 6.12926 0.043403 1 0 |6.1293+0.0434+1.0000+0.0000|
#add_dispersion_debye: 1 1.42863 8.0994e-12 |6.1293+0.0434+1.0000+0.0000|
#material: 6.28671 0.0448543 1 0 |6.2867+0.0449+1.0000+0.0000|
#add_dispersion_debye: 1 1.58416 8.0994e-12 |6.2867+0.0449+1.0000+0.0000|
#material: 6.44181 0.0462539 1 0 |6.4418+0.0463+1.0000+0.0000|
#add_dispersion_debye: 1 1.74471 8.0994e-12 |6.4418+0.0463+1.0000+0.0000|
#material: 6.59474 0.0476066 1 0 |6.5947+0.0476+1.0000+0.0000|
#add_dispersion_debye: 1 1.91011 8.0994e-12 |6.5947+0.0476+1.0000+0.0000|
#material: 6.74564 0.0489169 1 0 |6.7456+0.0489+1.0000+0.0000|
#add_dispersion_debye: 1 2.0802 8.0994e-12 |6.7456+0.0489+1.0000+0.0000|
#material: 6.89465 0.0501882 1 0 |6.8946+0.0502+1.0000+0.0000|
#add_dispersion_debye: 1 2.25484 8.0994e-12 |6.8946+0.0502+1.0000+0.0000|
#material: 7.04188 0.0514238 1 0 |7.0419+0.0514+1.0000+0.0000|
#add_dispersion_debye: 1 2.4339 8.0994e-12 |7.0419+0.0514+1.0000+0.0000|
#material: 7.18745 0.0526264 1 0 |7.1875+0.0526+1.0000+0.0000|
#add_dispersion_debye: 1 2.61725 8.0994e-12 |7.1875+0.0526+1.0000+0.0000|
#material: 7.33146 0.0537984 1 0 |7.3315+0.0538+1.0000+0.0000|
#add_dispersion_debye: 1 2.80478 8.0994e-12 |7.3315+0.0538+1.0000+0.0000|
#material: 7.47398 0.0549419 1 0 |7.4740+0.0549+1.0000+0.0000|
#add_dispersion_debye: 1 2.99639 8.0994e-12 |7.4740+0.0549+1.0000+0.0000|
#material: 7.61511 0.0560589 1 0 |7.6151+0.0561+1.0000+0.0000|
#add_dispersion_debye: 1 3.19199 8.0994e-12 |7.6151+0.0561+1.0000+0.0000|
#material: 7.7549 0.0571511 1 0 |7.7549+0.0572+1.0000+0.0000|
#add_dispersion_debye: 1 3.39148 8.0994e-12 |7.7549+0.0572+1.0000+0.0000|
#material: 7.89344 0.0582199 1 0 |7.8934+0.0582+1.0000+0.0000|
#add_dispersion_debye: 1 3.59477 8.0994e-12 |7.8934+0.0582+1.0000+0.0000|
#material: 8.03078 0.0592667 1 0 |8.0308+0.0593+1.0000+0.0000|
#add_dispersion_debye: 1 3.8018 8.0994e-12 |8.0308+0.0593+1.0000+0.0000|
#material: 8.16697 0.0602929 1 0 |8.1670+0.0603+1.0000+0.0000|
#add_dispersion_debye: 1 4.01247 8.0994e-12 |8.1670+0.0603+1.0000+0.0000|
#material: 8.30207 0.0612995 1 0 |8.3021+0.0613+1.0000+0.0000|
#add_dispersion_debye: 1 4.22673 8.0994e-12 |8.3021+0.0613+1.0000+0.0000|
#material: 8.43613 0.0622875 1 0 |8.4361+0.0623+1.0000+0.0000|
#add_dispersion_debye: 1 4.4445 8.0994e-12 |8.4361+0.0623+1.0000+0.0000|
#material: 8.5692 0.063258 1 0 |8.5692+0.0633+1.0000+0.0000|
#add_dispersion_debye: 1 4.66572 8.0994e-12 |8.5692+0.0633+1.0000+0.0000|
#material: 8.7013 0.0642118 1 0 |8.7013+0.0642+1.0000+0.0000|
#add_dispersion_debye: 1 4.89034 8.0994e-12 |8.7013+0.0642+1.0000+0.0000|
#material: 8.83249 0.0651496 1 0 |8.8325+0.0651+1.0000+0.0000|
#add_dispersion_debye: 1 5.11828 8.0994e-12 |8.8325+0.0651+1.0000+0.0000|
#material: 8.9628 0.0660723 1 0 |8.9628+0.0661+1.0000+0.0000|
#add_dispersion_debye: 1 5.3495 8.0994e-12 |8.9628+0.0661+1.0000+0.0000|
#material: 9.09226 0.0669805 1 0 |9.0923+0.0670+1.0000+0.0000|
#add_dispersion_debye: 1 5.58395 8.0994e-12 |9.0923+0.0670+1.0000+0.0000|
#material: 9.22091 0.0678749 1 0 |9.2209+0.0679+1.0000+0.0000|
#add_dispersion_debye: 1 5.82158 8.0994e-12 |9.2209+0.0679+1.0000+0.0000|
#material: 9.34877 0.068756 1 0 |9.3488+0.0688+1.0000+0.0000|
#add_dispersion_debye: 1 6.06233 8.0994e-12 |9.3488+0.0688+1.0000+0.0000|
#material: 9.47588 0.0696244 1 0 |9.4759+0.0696+1.0000+0.0000|
#add_dispersion_debye: 1 6.30617 8.0994e-12 |9.4759+0.0696+1.0000+0.0000|
#material: 9.60226 0.0704806 1 0 |9.6023+0.0705+1.0000+0.0000|
#add_dispersion_debye: 1 6.55304 8.0994e-12 |9.6023+0.0705+1.0000+0.0000|
#material: 9.72793 0.0713251 1 0 |9.7279+0.0713+1.0000+0.0000|
#add_dispersion_debye: 1 6.80292 8.0994e-12 |9.7279+0.0713+1.0000+0.0000|
#material: 9.85292 0.0721583 1 0 |9.8529+0.0722+1.0000+0.0000|
#add_dispersion_debye: 1 7.05574 8.0994e-12 |9.8529+0.0722+1.0000+0.0000|
#material: 9.97725 0.0729807 1 0 |9.9772+0.0730+1.0000+0.0000|
#add_dispersion_debye: 1 7.31149 8.0994e-12 |9.9772+0.0730+1.0000+0.0000|

查看文件

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c7c7303daffcd4debb8df4c0c5e6cd62a6fd07c6e4b33e8c47c9e190c42d75cf
size 75736481

查看文件

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a0a195bfeec73f72083e6dbeb5e3713259721d2d4e9118bf019ee9905f02f5a8
size 4927361

查看文件

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:67c7c15c0025feac5b5899c3a691929ae1d7338c68714708c7a41e51a7f71441
size 4009216

查看文件

@@ -161,9 +161,11 @@ class GprMaxBaseTest(RunOnlyRegressionTest):
if self.test_dependency is None:
return None
# Always filter by the model parameter, but allow child classes
# Always filter by the model parameter (unless the test
# dependency only runs a single model), but allow child classes
# (or mixins) to override how models are filtered.
kwargs.setdefault("model", self.model)
if len(self.test_dependency.model.values) > 1:
kwargs.setdefault("model", self.model)
variant_nums = self.test_dependency.get_variant_nums(**kwargs)
@@ -246,6 +248,20 @@ class GprMaxBaseTest(RunOnlyRegressionTest):
# Set the matplotlib cache to the work filesystem
self.env_vars["MPLCONFIGDIR"] = "${HOME/home/work}/.config/matplotlib"
def build_output_file_path(self, filename: str) -> Path:
"""Build output file Path object from filename.
Using a function to build this allows mixins to reuse or
override it if needed.
Args:
filename: Name of output file with no file extension.
Returns:
Path: Output file Path object
"""
return Path(f"{filename}.h5")
@run_after("init")
def set_file_paths(self):
"""Set default test input and output files.
@@ -254,7 +270,7 @@ class GprMaxBaseTest(RunOnlyRegressionTest):
later in the pipeline.
"""
self.input_file = Path(f"{self.model}.in")
self.output_file = Path(f"{self.model}.h5")
self.output_file = self.build_output_file_path(self.model)
@run_before("run")
def configure_test_run(self):

查看文件

@@ -1,10 +1,12 @@
from pathlib import Path
from shutil import copyfile
from typing import Optional
import reframe.utility.sanity as sn
import reframe.utility.typecheck as typ
from numpy import prod
from reframe import RegressionMixin
from reframe.core.builtins import parameter, required, run_after, variable
from reframe.core.builtins import parameter, run_after, variable
from typing_extensions import TYPE_CHECKING
from reframe_tests.tests.base_tests import GprMaxBaseTest
@@ -29,9 +31,14 @@ else:
class ReceiverMixin(GprMaxMixin):
number_of_receivers = variable(int, value=-1)
@run_after("setup")
@run_after("setup", always_last=True)
def add_receiver_regression_checks(self):
reference_file = self.build_reference_filepath(self.output_file)
test_dependency = self.get_test_dependency()
if test_dependency is not None:
output_file = self.build_output_file_path(test_dependency.model)
reference_file = self.build_reference_filepath(output_file)
else:
reference_file = self.build_reference_filepath(self.output_file)
if self.number_of_receivers > 0:
for i in range(self.number_of_receivers):
@@ -88,16 +95,7 @@ class GeometryOnlyMixin(GprMaxMixin):
self.executable_opts += ["--geometry-only"]
class GeometryObjectMixin(GprMaxMixin):
"""Add regression tests for geometry objects.
Attributes:
geometry_objects (list[str]): List of geometry objects to run
regression checks on.
"""
geometry_objects = variable(typ.List[str], value=[])
class GeometryObjectMixinBase(GprMaxMixin):
def build_geometry_object_filepath(self, geometry_object: str) -> Path:
"""Build filepath to the specified geometry object.
@@ -114,6 +112,63 @@ class GeometryObjectMixin(GprMaxMixin):
"""
return Path(f"{geometry_object}_materials").with_suffix(".txt")
class GeometryObjectsReadMixin(GeometryObjectMixinBase):
geometry_objects_read = variable(typ.Dict[str, str], value={})
@run_after("setup")
def copy_geometry_objects_from_test_dependency(self):
self.skip_if(
len(self.geometry_objects_read) < 0,
f"Must provide a list of geometry objects being read by the test.",
)
test_dependency = self.get_test_dependency()
self.skip_if(
test_dependency is None,
f"GeometryObjectsReadMixin must be used with a test dependency.",
)
for geometry_object_input, geometry_object_output in self.geometry_objects_read.items():
geometry_object_input_file = self.build_geometry_object_filepath(geometry_object_input)
geometry_object_input_file = Path(test_dependency.stagedir, geometry_object_input_file)
materials_input_file = self.build_materials_filepath(geometry_object_input)
materials_input_file = Path(test_dependency.stagedir, materials_input_file)
self.skip_if(
not sn.path_isfile(geometry_object_input_file),
f"Test dependency did not create geometry object file.",
)
self.skip_if(
not sn.path_isfile(materials_input_file),
f"Test dependency did not create geometry object materials file.",
)
geometry_object_output_file = self.build_geometry_object_filepath(
geometry_object_output
)
geometry_object_output_file = Path(self.stagedir, geometry_object_output_file)
materials_output_file = self.build_materials_filepath(geometry_object_output)
materials_output_file = Path(self.stagedir, materials_output_file)
copyfile(geometry_object_input_file, geometry_object_output_file)
copyfile(materials_input_file, materials_output_file)
class GeometryObjectsWriteMixin(GeometryObjectMixinBase):
"""Add regression tests for geometry objects.
Attributes:
geometry_objects_write (list[str]): List of geometry objects to
run regression checks on.
"""
geometry_objects_write = variable(typ.List[str], value=[])
@run_after("setup")
def add_geometry_object_regression_checks(self):
"""Add a regression check for each geometry object.
@@ -121,11 +176,11 @@ class GeometryObjectMixin(GprMaxMixin):
The test will be skipped if no geometry objects have been specified.
"""
self.skip_if(
len(self.geometry_objects) < 0,
f"Must provide a list of geometry objects.",
len(self.geometry_objects_write) < 0,
f"Must provide a list of geometry objects being written by the test.",
)
for geometry_object in self.geometry_objects:
for geometry_object in self.geometry_objects_write:
# Add materials regression check first as if this fails,
# checking the .h5 file will almost definitely fail.
materials_file = self.build_materials_filepath(geometry_object)
@@ -138,7 +193,9 @@ class GeometryObjectMixin(GprMaxMixin):
self.regression_checks.append(materials_regression_check)
geometry_object_file = self.build_geometry_object_filepath(geometry_object)
reference_file = self.build_reference_filepath(geometry_object)
reference_file = self.build_reference_filepath(
geometry_object, suffix=geometry_object_file.suffix
)
regression_check = GeometryObjectRegressionCheck(geometry_object_file, reference_file)
self.regression_checks.append(regression_check)

查看文件

@@ -0,0 +1,14 @@
#title: Hertzian dipole in free-space
#domain: 0.100 0.100 0.100
#dx_dy_dz: 0.002 0.005 0.005
#time_window: 3e-9
#pml_cells: 5 2 2 5 2 2
#waveform: gaussiandot 1 1e9 myWave
#hertzian_dipole: z 0.050 0.050 0.050 myWave
#rx: 0.08 0.08 0.08
#geometry_objects_read: 0.03 0.03 0.03 full_volume_read.h5 full_volume_read_materials.txt
#geometry_objects_write: 0.05 0.05 0.05 0.09 0.09 0.09 partial_volume

查看文件

@@ -0,0 +1,13 @@
#title: Hertzian dipole in free-space
#domain: 0.100 0.100 0.100
#dx_dy_dz: 0.002 0.005 0.005
#time_window: 3e-9
#pml_cells: 5 2 2 5 2 2
#waveform: gaussiandot 1 1e9 myWave
#hertzian_dipole: z 0.050 0.050 0.050 myWave
#rx: 0.08 0.08 0.08
#geometry_objects_read: 0 0 0 full_volume_read.h5 full_volume_read_materials.txt

查看文件

@@ -0,0 +1,15 @@
#title: Hertzian dipole in free-space
#domain: 0.100 0.100 0.100
#dx_dy_dz: 0.002 0.005 0.005
#time_window: 3e-9
#pml_cells: 5 2 2 5 2 2
#waveform: gaussiandot 1 1e9 myWave
#hertzian_dipole: z 0.050 0.050 0.050 myWave
#rx: 0.08 0.08 0.08
#geometry_objects_read: 0 0 0 full_volume_read.h5 full_volume_read_materials.txt
#geometry_objects_write: 0.02 0.02 0.02 0.06 0.06 0.06 partial_volume
#geometry_objects_write: 0 0 0 0.1 0.1 0.1 full_volume

查看文件

@@ -1,11 +1,15 @@
#title: Hertzian dipole in free-space
#domain: 0.100 0.100 0.100
#dx_dy_dz: 0.001 0.001 0.001
#dx_dy_dz: 0.002 0.005 0.005
#time_window: 3e-9
#pml_cells: 5 2 2 5 2 2
#waveform: gaussiandot 1 1e9 myWave
#hertzian_dipole: z 0.050 0.050 0.050 myWave
#rx: 0.08 0.08 0.08
#material: 4.9 0 1 0 myWater
#material: 2 0 1.4 0 unusedMaterial
#material: 3 0 2 0 boxMaterial

查看文件

@@ -0,0 +1,14 @@
#title: Heterogeneous soil using a stochastic distribution of dielectric properties given by a mixing model from Peplinski
#domain: 0.100 0.100 0.100
#dx_dy_dz: 0.001 0.001 0.001
#time_window: 3e-9
#waveform: ricker 1 1.5e9 my_ricker
#hertzian_dipole: y 0.02 0.02 0.02 my_ricker
#rx: 0.080 0.080 0.080
#material: 8 0 1 0 half_space
#fractal_box: 0 0 0 0.1 0.1 0.05 1.5 1 1 1 1 half_space my_fractal_box 42
#add_grass: 0 0 0.05 0.1 0.1 0.05 1.5 0.05 0.1 100 my_fractal_box 22
#geometry_objects_write: 0 0 0 0.1 0.1 0.1 full_volume

查看文件

@@ -0,0 +1,15 @@
#title: Heterogeneous soil using a stochastic distribution of dielectric properties given by a mixing model from Peplinski
#domain: 0.100 0.100 0.100
#dx_dy_dz: 0.001 0.001 0.001
#time_window: 3e-9
#waveform: ricker 1 1.5e9 my_ricker
#hertzian_dipole: y 0.02 0.02 0.02 my_ricker
#rx: 0.080 0.080 0.080
#material: 8 0 1 0 half_space
#fractal_box: 0.06 0.06 0.02 0.08 0.08 0.045 1.5 1 1 1 1 half_space my_fractal_box 42
#add_surface_roughness: 0.06 0.08 0.02 0.08 0.08 0.045 1 1 1 0.07 0.08 my_fractal_box 22
#add_grass: 0.06 0.08 0.02 0.08 0.08 0.045 1.5 0.075 0.09 100 my_fractal_box 17
#geometry_objects_write: 0 0 0 0.1 0.1 0.1 full_volume

查看文件

@@ -0,0 +1,19 @@
#title: Heterogeneous soil using a stochastic distribution of dielectric properties given by a mixing model from Peplinski
#domain: 0.100 0.100 0.100
#dx_dy_dz: 0.001 0.001 0.001
#time_window: 3e-9
#waveform: ricker 1 1.5e9 my_ricker
#hertzian_dipole: y 0.02 0.02 0.02 my_ricker
#rx: 0.080 0.080 0.080
#soil_peplinski: 0.5 0.5 2.0 2.66 0.001 0.25 my_soil
#fractal_box: 0 0 0 0.049 0.05 0.051 1.5 1 1 1 50 my_soil box_1 42
#fractal_box: 0.049 0.05 0.051 0.1 0.1 0.1 1.5 1 1 1 50 my_soil box_2 22
#add_surface_roughness: 0.049 0 0 0.049 0.05 0.05 1 1 1 0.025 0.05 box_1 43
#add_surface_roughness: 0.049 0.05 0.051 0.049 0.1 0.1 1 1 1 0.049 0.074 box_2 23
#geometry_objects_write: 0 0 0 0.1 0.1 0.1 full_volume

查看文件

@@ -0,0 +1,14 @@
#title: Heterogeneous soil using a stochastic distribution of dielectric properties given by a mixing model from Peplinski
#domain: 0.100 0.100 0.100
#dx_dy_dz: 0.001 0.001 0.001
#time_window: 3e-9
#waveform: ricker 1 1.5e9 my_ricker
#hertzian_dipole: y 0.02 0.02 0.02 my_ricker
#rx: 0.080 0.080 0.080
#soil_peplinski: 0.5 0.5 2.0 2.66 0.001 0.25 my_soil
#fractal_box: 0 0 0 0.1 0.1 0.07 1.5 1 1 1 50 my_soil my_soil_box 42
#add_surface_roughness: 0 0 0.07 0.1 0.1 0.07 1 1 1 0.05 0.1 my_soil_box 22
#geometry_objects_write: 0 0 0 0.1 0.1 0.1 full_volume

查看文件

@@ -0,0 +1,14 @@
#title: Heterogeneous soil using a stochastic distribution of dielectric properties given by a mixing model from Peplinski
#domain: 0.100 0.100 0.100
#dx_dy_dz: 0.001 0.001 0.001
#time_window: 3e-9
#waveform: ricker 1 1.5e9 my_ricker
#hertzian_dipole: y 0.02 0.02 0.02 my_ricker
#rx: 0.080 0.080 0.080
#soil_peplinski: 0.5 0.5 2.0 2.66 0.001 0.25 my_soil
#fractal_box: 0.03 0.03 0.03 0.045 0.045 0.045 1.5 1 1 1 50 my_soil my_soil_box 42
#add_surface_roughness: 0.03 0.03 0.03 0.045 0.03 0.045 1 1 1 0.01 0.02 my_soil_box 22
#geometry_objects_write: 0 0 0 0.1 0.1 0.1 full_volume

查看文件

@@ -0,0 +1,14 @@
#title: Heterogeneous soil using a stochastic distribution of dielectric properties given by a mixing model from Peplinski
#domain: 0.100 0.100 0.100
#dx_dy_dz: 0.001 0.001 0.001
#time_window: 3e-9
#waveform: ricker 1 1.5e9 my_ricker
#hertzian_dipole: y 0.02 0.02 0.02 my_ricker
#rx: 0.080 0.080 0.080
#soil_peplinski: 0.5 0.5 2.0 2.66 0.001 0.25 my_soil
#fractal_box: 0 0 0 0.1 0.1 0.07 1.5 1 1 1 50 my_soil my_soil_box 42
#add_surface_roughness: 0 0 0.07 0.1 0.1 0.07 5 1.2 0.2 0.05 0.1 my_soil_box 22
#geometry_objects_write: 0 0 0 0.1 0.1 0.1 full_volume

查看文件

@@ -0,0 +1,15 @@
#title: Heterogeneous soil using a stochastic distribution of dielectric properties given by a mixing model from Peplinski
#domain: 0.100 0.100 0.100
#dx_dy_dz: 0.001 0.001 0.001
#time_window: 3e-9
#waveform: ricker 1 1.5e9 my_ricker
#hertzian_dipole: y 0.02 0.02 0.02 my_ricker
#rx: 0.080 0.080 0.080
#soil_peplinski: 0.5 0.5 2.0 2.66 0.001 0.25 my_soil
#fractal_box: 0 0 0 0.1 0.1 0.07 1.5 1 1 1 50 my_soil my_soil_box 42
#add_surface_roughness: 0 0 0.07 0.1 0.1 0.07 1 1 1 0.05 0.1 my_soil_box 22
#add_surface_water: 0 0 0.07 0.1 0.1 0.07 0.1 my_soil_box
#geometry_objects_write: 0 0 0 0.1 0.1 0.1 full_volume

查看文件

@@ -0,0 +1,15 @@
#title: Heterogeneous soil using a stochastic distribution of dielectric properties given by a mixing model from Peplinski
#domain: 0.100 0.100 0.100
#dx_dy_dz: 0.001 0.001 0.001
#time_window: 3e-9
#waveform: ricker 1 1.5e9 my_ricker
#hertzian_dipole: y 0.02 0.02 0.02 my_ricker
#rx: 0.080 0.080 0.080
#soil_peplinski: 0.5 0.5 2.0 2.66 0.001 0.25 my_soil
#fractal_box: 0.015 0.015 0.015 0.03 0.03 0.03 1.5 1 1 1 50 my_soil my_soil_box 42
#add_surface_roughness: 0.03 0.015 0.015 0.03 0.03 0.03 1 1 1 0.035 0.045 my_soil_box 22
#add_surface_water: 0.03 0.015 0.015 0.03 0.03 0.03 0.04 my_soil_box
#geometry_objects_write: 0 0 0 0.1 0.1 0.1 full_volume

查看文件

@@ -0,0 +1,14 @@
#title: Heterogeneous soil using a stochastic distribution of dielectric properties given by a mixing model from Peplinski
#domain: 0.100 0.100 0.100
#dx_dy_dz: 0.001 0.001 0.001
#time_window: 3e-9
#waveform: ricker 1 1.5e9 my_ricker
#hertzian_dipole: y 0.02 0.02 0.02 my_ricker
#rx: 0.080 0.080 0.080
#soil_peplinski: 0.5 0.5 2.0 2.66 0.001 0.25 my_soil
#fractal_box: 0 0 0 0.049 0.05 0.051 1.5 1 1 1 50 my_soil my_soil_box 42
#fractal_box: 0.049 0.05 0.051 0.1 0.1 0.1 1.5 1 1 1 50 my_soil my_soil_box 22
#geometry_objects_write: 0 0 0 0.1 0.1 0.1 full_volume

查看文件

@@ -0,0 +1,13 @@
#title: Heterogeneous soil using a stochastic distribution of dielectric properties given by a mixing model from Peplinski
#domain: 0.100 0.100 0.100
#dx_dy_dz: 0.001 0.001 0.001
#time_window: 3e-9
#waveform: ricker 1 1.5e9 my_ricker
#hertzian_dipole: y 0.02 0.02 0.02 my_ricker
#rx: 0.080 0.080 0.080
#soil_peplinski: 0.5 0.5 2.0 2.66 0.001 0.25 my_soil
#fractal_box: 0.015 0.015 0.015 0.045 0.045 0.045 1.5 1 1 1 50 my_soil my_soil_box 42
#geometry_objects_write: 0 0 0 0.1 0.1 0.1 full_volume

查看文件

@@ -0,0 +1,13 @@
#title: Heterogeneous soil using a stochastic distribution of dielectric properties given by a mixing model from Peplinski
#domain: 0.100 0.100 0.100
#dx_dy_dz: 0.001 0.001 0.001
#time_window: 3e-9
#waveform: ricker 1 1.5e9 my_ricker
#hertzian_dipole: y 0.02 0.02 0.02 my_ricker
#rx: 0.080 0.080 0.080
#soil_peplinski: 0.5 0.5 2.0 2.66 0.001 0.25 my_soil
#fractal_box: 0 0 0 0.1 0.1 0.1 1.5 1.5 1 0.8 50 my_soil my_soil_box 42
#geometry_objects_write: 0 0 0 0.1 0.1 0.1 full_volume

查看文件

@@ -13,5 +13,7 @@
#sphere: 0.05 0.05 0.05 0.03 myWater y
#box: 0.01 0.01 0.01 0.09 0.025 0.025 boxMaterial y
#geometry_view: 0.02 0.02 0.02 0.06 0.06 0.06 0.001 0.001 0.001 partial_volume f
#geometry_view: 0 0 0 0.1 0.1 0.1 0.001 0.001 0.001 full_volume f
#geometry_view: 0.04 0.04 0.04 0.06 0.06 0.06 0.001 0.001 0.001 partial_volume f
#geometry_view: 0 0 0.048 0.1 0.1 0.049 0.001 0.001 0.001 z_plane_48 f
#geometry_view: 0 0 0.049 0.1 0.1 0.05 0.001 0.001 0.001 z_plane_49 f
#geometry_view: 0 0 0.05 0.1 0.1 0.051 0.001 0.001 0.001 z_plane_50 f

查看文件

@@ -14,4 +14,7 @@
#box: 0.01 0.01 0.01 0.09 0.025 0.025 boxMaterial y
#geometry_view: 0.02 0.02 0.02 0.06 0.06 0.06 0.001 0.001 0.001 partial_volume n
#geometry_view: 0 0 0 0.1 0.1 0.1 0.001 0.001 0.001 full_volume n
#geometry_view: 0 0 0 0.1 0.1 0.1 0.002 0.002 0.002 full_volume n
#geometry_view: 0 0 0.048 0.1 0.1 0.049 0.001 0.001 0.001 z_plane_48 n
#geometry_view: 0 0 0.049 0.1 0.1 0.05 0.001 0.001 0.001 z_plane_49 n
#geometry_view: 0 0 0.05 0.1 0.1 0.051 0.001 0.001 0.001 z_plane_50 n

某些文件未显示,因为此 diff 中更改的文件太多 显示更多