你已经派生过 gprMax
镜像自地址
https://gitee.com/sunhf/gprMax.git
已同步 2025-08-04 11:36:52 +08:00
Merge branch '53-create-mpi-fractal-objects' into mpi
这个提交包含在:
@@ -143,7 +143,7 @@ class FractalSurface:
|
||||
|
||||
return surfacedims
|
||||
|
||||
def generate_fractal_surface(self):
|
||||
def generate_fractal_surface(self) -> bool:
|
||||
"""Generate a 2D array with a fractal distribution."""
|
||||
|
||||
surfacedims = self.get_surface_dims()
|
||||
@@ -199,6 +199,8 @@ class FractalSurface:
|
||||
- ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange) * fractalmin
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class MPIFractalSurface(FractalSurface):
|
||||
def __init__(
|
||||
@@ -218,7 +220,7 @@ class MPIFractalSurface(FractalSurface):
|
||||
self.comm = comm
|
||||
self.upper_bound = upper_bound
|
||||
|
||||
def generate_fractal_surface(self):
|
||||
def generate_fractal_surface(self) -> bool:
|
||||
"""Generate a 2D array with a fractal distribution."""
|
||||
|
||||
if self.xs == self.xf:
|
||||
@@ -247,7 +249,7 @@ class MPIFractalSurface(FractalSurface):
|
||||
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
|
||||
return False
|
||||
else:
|
||||
# Create new cartsesian communicator for the Fractal Surface
|
||||
comm = self.comm.Split(color=color)
|
||||
@@ -262,8 +264,17 @@ class MPIFractalSurface(FractalSurface):
|
||||
# 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 least"
|
||||
f" 1 dimension. Current decompostion is: {self.comm.dims}"
|
||||
"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()
|
||||
@@ -445,3 +456,5 @@ class MPIFractalSurface(FractalSurface):
|
||||
logger.debug(
|
||||
f"Generated fractal surface: start={self.start}, stop={self.stop}, size={self.size}, fractalrange={self.fractalrange}"
|
||||
)
|
||||
|
||||
return True
|
||||
|
@@ -173,14 +173,22 @@ class AddGrass(RotatableMixin, GeometryUserObject):
|
||||
else:
|
||||
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]:
|
||||
raise ValueError(
|
||||
f"{self.__str__()} the specified surface is not large "
|
||||
@@ -220,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)
|
||||
|
@@ -116,27 +116,23 @@ class FractalBox(RotatableMixin, GeometryUserObject):
|
||||
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.
|
||||
@@ -146,26 +142,23 @@ 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 = grid.add_fractal_volume(xs, xf, ys, yf, zs, zf, frac_dim, seed)
|
||||
self.volume.ID = ID
|
||||
@@ -685,12 +678,11 @@ 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:
|
||||
if not self.volume.generate_fractal_volume():
|
||||
return
|
||||
|
@@ -248,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.
|
||||
@@ -256,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):
|
||||
|
@@ -35,7 +35,8 @@ class ReceiverMixin(GprMaxMixin):
|
||||
def add_receiver_regression_checks(self):
|
||||
test_dependency = self.get_test_dependency()
|
||||
if test_dependency is not None:
|
||||
reference_file = self.build_reference_filepath(test_dependency.output_file)
|
||||
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)
|
||||
|
||||
|
@@ -168,7 +168,8 @@ class TestTriangleGeometry(GprMaxRegressionTest):
|
||||
@rfm.simple_test
|
||||
class TestAddGrassMpi(MpiMixin, TestAddGrass):
|
||||
tags = {"test", "mpi", "geometery", "fractal", "surface", "grass"}
|
||||
mpi_layout = parameter([[1, 2, 2], [3, 1, 3], [4, 1, 4]])
|
||||
mpi_layout = parameter([[2, 2, 2]])
|
||||
model = parameter(["add_grass_small"])
|
||||
test_dependency = TestAddGrass
|
||||
|
||||
|
||||
|
在新工单中引用
屏蔽一个用户