Trying to resolve all the merge conflicts.

这个提交包含在:
Sai-Suraj-27
2023-06-27 16:34:09 +05:30
当前提交 64b3068aa3
共有 18 个文件被更改,包括 81 次插入106 次删除

查看文件

@@ -111,7 +111,7 @@ class AddGrass(UserObjectGeometry):
if ys == yf or zs == zf: if ys == yf or zs == zf:
logger.exception(f"{self.__str__()} dimensions are not specified correctly") logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError raise ValueError
if xs != volume.xs and xs != volume.xf: if xs not in [volume.xs and volume.xf]:
logger.exception(f"{self.__str__()} must specify external surfaces on a fractal box") logger.exception(f"{self.__str__()} must specify external surfaces on a fractal box")
raise ValueError raise ValueError
fractalrange = (round_value(limits[0] / grid.dx), round_value(limits[1] / grid.dx)) fractalrange = (round_value(limits[0] / grid.dx), round_value(limits[1] / grid.dx))
@@ -133,10 +133,10 @@ class AddGrass(UserObjectGeometry):
requestedsurface = "xplus" requestedsurface = "xplus"
elif ys == yf: elif ys == yf:
if xs == xf or zs == zf: if zs == zf:
logger.exception(f"{self.__str__()} dimensions are not specified correctly") logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError raise ValueError
if ys != volume.ys and ys != volume.yf: if ys not in [volume.ys and volume.yf]:
logger.exception(f"{self.__str__()} must specify external surfaces on a fractal box") logger.exception(f"{self.__str__()} must specify external surfaces on a fractal box")
raise ValueError raise ValueError
fractalrange = (round_value(limits[0] / grid.dy), round_value(limits[1] / grid.dy)) fractalrange = (round_value(limits[0] / grid.dy), round_value(limits[1] / grid.dy))
@@ -158,10 +158,7 @@ class AddGrass(UserObjectGeometry):
requestedsurface = "yplus" requestedsurface = "yplus"
elif zs == zf: elif zs == zf:
if xs == xf or ys == yf: if zs not in [volume.zs and volume.zf]:
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError
if zs != volume.zs and zs != volume.zf:
logger.exception(f"{self.__str__()} must specify external surfaces on a fractal box") logger.exception(f"{self.__str__()} must specify external surfaces on a fractal box")
raise ValueError raise ValueError
fractalrange = (round_value(limits[0] / grid.dz), round_value(limits[1] / grid.dz)) fractalrange = (round_value(limits[0] / grid.dz), round_value(limits[1] / grid.dz))
@@ -238,7 +235,7 @@ class AddGrass(UserObjectGeometry):
surface.grass.append(g) surface.grass.append(g)
# Check to see if grass has been already defined as a material # Check to see if grass has been already defined as a material
if not any(x.ID == "grass" for x in grid.materials): if all(x.ID == "grass" for x in grid.materials):
create_grass(grid) create_grass(grid)
# Check if time step for model is suitable for using grass # Check if time step for model is suitable for using grass

查看文件

@@ -123,7 +123,7 @@ class AddSurfaceRoughness(UserObjectGeometry):
if ys == yf or zs == zf: if ys == yf or zs == zf:
logger.exception(f"{self.__str__()} dimensions are not specified correctly") logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError raise ValueError
if xs != volume.xs and xs != volume.xf: 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") logger.exception(f"{self.__str__()} can only be used on the external " + "surfaces of a fractal box")
raise ValueError raise ValueError
fractalrange = (round_value(limits[0] / grid.dx), round_value(limits[1] / grid.dx)) fractalrange = (round_value(limits[0] / grid.dx), round_value(limits[1] / grid.dx))
@@ -151,10 +151,10 @@ class AddSurfaceRoughness(UserObjectGeometry):
requestedsurface = "xplus" requestedsurface = "xplus"
elif ys == yf: elif ys == yf:
if xs == xf or zs == zf: if zs == zf:
logger.exception(f"{self.__str__()} dimensions are not specified correctly") logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError raise ValueError
if ys != volume.ys and ys != volume.yf: if ys not in [volume.ys and volume.yf]:
logger.exception(f"{self.__str__()} can only be used on the external " + "surfaces of a fractal box") logger.exception(f"{self.__str__()} can only be used on the external " + "surfaces of a fractal box")
raise ValueError raise ValueError
fractalrange = (round_value(limits[0] / grid.dy), round_value(limits[1] / grid.dy)) fractalrange = (round_value(limits[0] / grid.dy), round_value(limits[1] / grid.dy))
@@ -182,10 +182,7 @@ class AddSurfaceRoughness(UserObjectGeometry):
requestedsurface = "yplus" requestedsurface = "yplus"
elif zs == zf: elif zs == zf:
if xs == xf or ys == yf: if zs not in [volume.zs and volume.zf]:
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError
if zs != volume.zs and zs != volume.zf:
logger.exception(f"{self.__str__()} can only be used on the external " + "surfaces of a fractal box") logger.exception(f"{self.__str__()} can only be used on the external " + "surfaces of a fractal box")
raise ValueError raise ValueError
fractalrange = (round_value(limits[0] / grid.dz), round_value(limits[1] / grid.dz)) fractalrange = (round_value(limits[0] / grid.dz), round_value(limits[1] / grid.dz))

查看文件

@@ -144,14 +144,13 @@ class FractalBox(UserObjectGeometry):
f"{self.__str__()} must be used with more than " + "one material from the mixing model." f"{self.__str__()} must be used with more than " + "one material from the mixing model."
) )
raise ValueError raise ValueError
if isinstance(mixingmodel, ListMaterial): if isinstance(mixingmodel, ListMaterial) and nbins > len(mixingmodel.mat):
if nbins > len(mixingmodel.mat): logger.exception(
logger.exception( f"{self.__str__()} too many materials/bins "
f"{self.__str__()} too many materials/bins " + "requested compared to materials in "
+ "requested compared to materials in " + "mixing model."
+ "mixing model." )
) raise ValueError
raise ValueError
# Create materials from mixing model as number of bins now known # Create materials from mixing model as number of bins now known
# from fractal_box command. # from fractal_box command.
mixingmodel.calculate_properties(nbins, grid) mixingmodel.calculate_properties(nbins, grid)

查看文件

@@ -385,7 +385,7 @@ class HertzianDipole(UserObjectMulti):
p2 = uip.round_to_grid_static_point(p1) p2 = uip.round_to_grid_static_point(p1)
# Check if there is a waveformID in the waveforms list # Check if there is a waveformID in the waveforms list
if not any(x.ID == waveform_id for x in grid.waveforms): if all(x.ID == waveform_id for x in grid.waveforms):
logger.exception(f"{self.params_str()} there is no waveform " + f"with the identifier {waveform_id}.") logger.exception(f"{self.params_str()} there is no waveform " + f"with the identifier {waveform_id}.")
raise ValueError raise ValueError
@@ -526,7 +526,7 @@ class MagneticDipole(UserObjectMulti):
p2 = uip.round_to_grid_static_point(p1) p2 = uip.round_to_grid_static_point(p1)
# Check if there is a waveformID in the waveforms list # Check if there is a waveformID in the waveforms list
if not any(x.ID == waveform_id for x in grid.waveforms): if all(x.ID == waveform_id for x in grid.waveforms):
logger.exception(f"{self.params_str()} there is no waveform " + f"with the identifier {waveform_id}.") logger.exception(f"{self.params_str()} there is no waveform " + f"with the identifier {waveform_id}.")
raise ValueError raise ValueError

查看文件

@@ -73,7 +73,7 @@ class ModelConfig:
# N.B. This will happen if the requested snapshots are too large to # N.B. This will happen if the requested snapshots are too large to
# fit on the memory of the GPU. If True this will slow # fit on the memory of the GPU. If True this will slow
# performance significantly. # performance significantly.
if sim_config.general["solver"] == "cuda" or sim_config.general["solver"] == "opencl": if sim_config.general["solver"] in ["cuda", "opencl"]:
if sim_config.general["solver"] == "cuda": if sim_config.general["solver"] == "cuda":
devs = sim_config.args.gpu devs = sim_config.args.gpu
elif sim_config.general["solver"] == "opencl": elif sim_config.general["solver"] == "opencl":

查看文件

@@ -51,7 +51,6 @@ class Context:
results: dict that can contain useful results/data from simulation. results: dict that can contain useful results/data from simulation.
""" """
results = {}
self.tsimstart = timer() self.tsimstart = timer()
self.print_logo_copyright() self.print_logo_copyright()
print_host_info(config.sim_config.hostinfo) print_host_info(config.sim_config.hostinfo)
@@ -86,7 +85,7 @@ class Context:
self.tsimend = timer() self.tsimend = timer()
self.print_sim_time_taken() self.print_sim_time_taken()
return results return {}
def print_logo_copyright(self): def print_logo_copyright(self):
"""Prints gprMax logo, version, and copyright/licencing information.""" """Prints gprMax logo, version, and copyright/licencing information."""

查看文件

@@ -217,10 +217,9 @@ def run_main(args):
# MPI running with (OpenMP/CUDA/OpenCL) # MPI running with (OpenMP/CUDA/OpenCL)
if config.sim_config.args.mpi: if config.sim_config.args.mpi:
context = MPIContext() context = MPIContext()
results = context.run()
# Standard running (OpenMP/CUDA/OpenCL) # Standard running (OpenMP/CUDA/OpenCL)
else: else:
context = Context() context = Context()
results = context.run()
results = context.run()
return results return results

查看文件

@@ -417,7 +417,7 @@ def dispersion_analysis(G):
# Find maximum significant frequency # Find maximum significant frequency
if G.waveforms: if G.waveforms:
for waveform in G.waveforms: for waveform in G.waveforms:
if waveform.type == "sine" or waveform.type == "contsine": if waveform.type in ["sine", "contsine"]:
results["maxfreq"].append(4 * waveform.freq) results["maxfreq"].append(4 * waveform.freq)
elif waveform.type == "impulse": elif waveform.type == "impulse":

查看文件

@@ -322,14 +322,13 @@ def check_cmd_names(processedlines, checkessential=True):
lindex += 1 lindex += 1
if checkessential: if checkessential and countessentialcmds < len(essentialcmds):
if countessentialcmds < len(essentialcmds): logger.exception(
logger.exception( "Your input file is missing essential commands "
"Your input file is missing essential commands " + "required to run a model. Essential commands are: "
+ "required to run a model. Essential commands are: " + ", ".join(essentialcmds)
+ ", ".join(essentialcmds) )
) raise SyntaxError
raise SyntaxError
return singlecmds, multiplecmds, geometry return singlecmds, multiplecmds, geometry

查看文件

@@ -1209,7 +1209,6 @@ class OpenCLUpdates:
self.grid.Hy_dev, self.grid.Hy_dev,
self.grid.Hz_dev, self.grid.Hz_dev,
) )
event.wait()
# If there are any dispersive materials do 1st part of dispersive update # If there are any dispersive materials do 1st part of dispersive update
# (it is split into two parts as it requires present and updated electric field values). # (it is split into two parts as it requires present and updated electric field values).
@@ -1231,7 +1230,8 @@ class OpenCLUpdates:
self.grid.Hy_dev, self.grid.Hy_dev,
self.grid.Hz_dev, self.grid.Hz_dev,
) )
event.wait()
event.wait()
def update_electric_pml(self): def update_electric_pml(self):
"""Updates electric field components with the PML correction.""" """Updates electric field components with the PML correction."""
@@ -1319,7 +1319,6 @@ class OpenCLUpdates:
# if iteration == self.grid.iterations - 1: # if iteration == self.grid.iterations - 1:
# return self.drv.mem_get_info()[1] - self.drv.mem_get_info()[0] # return self.drv.mem_get_info()[1] - self.drv.mem_get_info()[0]
logger.debug("Look at memory estimate for pyopencl") logger.debug("Look at memory estimate for pyopencl")
pass
def calculate_solve_time(self): def calculate_solve_time(self):
"""Calculates solving time for model.""" """Calculates solving time for model."""

查看文件

@@ -3,9 +3,12 @@
receiver at the centre. receiver at the centre.
""" """
from pathlib import Path from pathlib import Path
import gprMax import gprMax
import itertools
# File path for output # File path for output
fn = Path(__file__) fn = Path(__file__)
@@ -17,35 +20,34 @@ ompthreads = [1, 2, 4, 8, 16, 32, 64, 128]
scenes = [] scenes = []
for d in domains: # Discretisation
for threads in ompthreads: dl = 0.001
# Discretisation
dl = 0.001
# Domain for d, threads in itertools.product(domains, ompthreads):
x = d # Domain
y = x x = d
z = x y = x
z = x
scene = gprMax.Scene() scene = gprMax.Scene()
title = gprMax.Title(name=fn.with_suffix("").name) title = gprMax.Title(name=fn.with_suffix("").name)
domain = gprMax.Domain(p1=(x, y, z)) domain = gprMax.Domain(p1=(x, y, z))
dxdydz = gprMax.Discretisation(p1=(dl, dl, dl)) dxdydz = gprMax.Discretisation(p1=(dl, dl, dl))
time_window = gprMax.TimeWindow(time=3e-9) time_window = gprMax.TimeWindow(time=3e-9)
wv = gprMax.Waveform(wave_type="gaussiandotnorm", amp=1, freq=900e6, id="MySource") wv = gprMax.Waveform(wave_type="gaussiandotnorm", amp=1, freq=900e6, id="MySource")
src = gprMax.HertzianDipole(p1=(x / 2, y / 2, z / 2), polarisation="x", waveform_id="MySource") src = gprMax.HertzianDipole(p1=(x / 2, y / 2, z / 2), polarisation="x", waveform_id="MySource")
omp = gprMax.OMPThreads(n=threads) omp = gprMax.OMPThreads(n=threads)
scene.add(title) scene.add(title)
scene.add(domain) scene.add(domain)
scene.add(dxdydz) scene.add(dxdydz)
scene.add(time_window) scene.add(time_window)
scene.add(wv) scene.add(wv)
scene.add(src) scene.add(src)
scene.add(omp) scene.add(omp)
scenes.append(scene) scenes.append(scene)
# Run model # Run model
gprMax.run(scenes=scenes, n=len(scenes), geometry_only=False, outputfile=fn, gpu=None) gprMax.run(scenes=scenes, n=len(scenes), geometry_only=False, outputfile=fn, gpu=None)

查看文件

@@ -61,9 +61,9 @@ if epsr:
wavelength = v1 / f wavelength = v1 / f
# Print some useful information # Print some useful information
logger.info("Centre frequency: {} GHz".format(f / 1e9)) logger.info(f"Centre frequency: {f / 1000000000.0} GHz")
if epsr: if epsr:
logger.info("Critical angle for Er {} is {} degrees".format(epsr, thetac)) logger.info(f"Critical angle for Er {epsr} is {thetac} degrees")
logger.info("Wavelength: {:.3f} m".format(wavelength)) logger.info("Wavelength: {:.3f} m".format(wavelength))
logger.info( logger.info(
"Observation distance(s) from {:.3f} m ({:.1f} wavelengths) to {:.3f} m ({:.1f} wavelengths)".format( "Observation distance(s) from {:.3f} m ({:.1f} wavelengths) to {:.3f} m ({:.1f} wavelengths)".format(
@@ -139,7 +139,7 @@ leg = ax.legend(
[legobj.set_linewidth(2) for legobj in leg.legendHandles] [legobj.set_linewidth(2) for legobj in leg.legendHandles]
# Save a pdf of the plot # Save a pdf of the plot
savename = os.path.splitext(args.numpyfile)[0] + ".pdf" savename = f"{os.path.splitext(args.numpyfile)[0]}.pdf"
fig.savefig(savename, dpi=None, format="pdf", bbox_inches="tight", pad_inches=0.1) fig.savefig(savename, dpi=None, format="pdf", bbox_inches="tight", pad_inches=0.1)
# savename = os.path.splitext(args.numpyfile)[0] + '.png' # savename = os.path.splitext(args.numpyfile)[0] + '.png'
# fig.savefig(savename, dpi=150, format='png', bbox_inches='tight', pad_inches=0.1) # fig.savefig(savename, dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)

查看文件

@@ -123,9 +123,7 @@ class Relaxation(object):
""" """
print(f"Approximating {self.name}" f" using {self.number_of_debye_poles} Debye poles") print(f"Approximating {self.name}" f" using {self.number_of_debye_poles} Debye poles")
print(f"{self.name} parameters: ") print(f"{self.name} parameters: ")
s = "" s = "".join(f"{k:10s} = {v}\n" for k, v in self.params.items())
for k, v in self.params.items():
s += f"{k:10s} = {v}\n"
print(s) print(s)
return f"{self.name}:\n{s}" return f"{self.name}:\n{s}"
@@ -232,10 +230,10 @@ class Relaxation(object):
"#material: {} {} {} {} {}\n".format(ee, self.sigma, self.mu, self.mu_sigma, self.material_name) "#material: {} {} {} {} {}\n".format(ee, self.sigma, self.mu, self.mu_sigma, self.material_name)
) )
print(material_prop[0], end="") print(material_prop[0], end="")
dispersion_prop = "#add_dispersion_debye: {}".format(len(tau)) dispersion_prop = f"#add_dispersion_debye: {len(tau)}"
for i in range(len(tau)): for i in range(len(tau)):
dispersion_prop += " {} {}".format(weights[i], 10 ** tau[i]) dispersion_prop += f" {weights[i]} {10**tau[i]}"
dispersion_prop += " {}".format(self.material_name) dispersion_prop += f" {self.material_name}"
print(dispersion_prop) print(dispersion_prop)
material_prop.append(dispersion_prop + "\n") material_prop.append(dispersion_prop + "\n")
return material_prop return material_prop
@@ -312,11 +310,10 @@ class Relaxation(object):
file_path = os.path.join("user_libs", "materials", "my_materials.txt") file_path = os.path.join("user_libs", "materials", "my_materials.txt")
else: else:
sys.exit("Cannot save material properties " f"in {os.path.join(fdir, 'my_materials.txt')}!") sys.exit("Cannot save material properties " f"in {os.path.join(fdir, 'my_materials.txt')}!")
fileH = open(file_path, "a") with open(file_path, "a") as fileH:
fileH.write(f"## {output[0].split(' ')[-1]}") fileH.write(f"## {output[0].split(' ')[-1]}")
fileH.writelines(output) fileH.writelines(output)
fileH.write("\n") fileH.write("\n")
fileH.close()
print(f"Material properties save at: {file_path}") print(f"Material properties save at: {file_path}")
@@ -613,7 +610,7 @@ class Crim(Relaxation):
print(f"Approximating Complex Refractive Index Model (CRIM)" f" using {self.number_of_debye_poles} Debye poles") print(f"Approximating Complex Refractive Index Model (CRIM)" f" using {self.number_of_debye_poles} Debye poles")
print("CRIM parameters: ") print("CRIM parameters: ")
for i in range(len(self.volumetric_fractions)): for i in range(len(self.volumetric_fractions)):
print("Material {}.:".format(i + 1)) print(f"Material {i + 1}.:")
print("---------------------------------") print("---------------------------------")
print(f"{'Vol. fraction':>27s} = {self.volumetric_fractions[i]}") print(f"{'Vol. fraction':>27s} = {self.volumetric_fractions[i]}")
print(f"{'e_inf':>27s} = {self.materials[i][0]}") print(f"{'e_inf':>27s} = {self.materials[i][0]}")

查看文件

@@ -474,7 +474,6 @@ def DLS(logt, rl, im, freq):
rp, ip = np.matmul(d.real, x[np.newaxis].T).T[0], np.matmul(d.imag, x[np.newaxis].T).T[0] rp, ip = np.matmul(d.real, x[np.newaxis].T).T[0], np.matmul(d.imag, x[np.newaxis].T).T[0]
cost_i = np.sum(np.abs(ip - im)) / len(im) cost_i = np.sum(np.abs(ip - im)) / len(im)
ee = np.mean(rl - rp) ee = np.mean(rl - rp)
if ee < 1: ee = max(ee, 1)
ee = 1
cost_r = np.sum(np.abs(rp + ee - rl)) / len(im) cost_r = np.sum(np.abs(rp + ee - rl)) / len(im)
return cost_i, cost_r, x, ee, rp, ip return cost_i, cost_r, x, ee, rp, ip

查看文件

@@ -79,10 +79,9 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False):
time = np.linspace(0, (iterations - 1) * dt, num=iterations) time = np.linspace(0, (iterations - 1) * dt, num=iterations)
# Check for single output component when doing a FFT # Check for single output component when doing a FFT
if fft: if fft and not len(outputs) == 1:
if not len(outputs) == 1: logger.exception("A single output must be specified when using " + "the -fft option")
logger.exception("A single output must be specified when using " + "the -fft option") raise ValueError
raise ValueError
# New plot for each receiver # New plot for each receiver
for rx in range(1, nrx + 1): for rx in range(1, nrx + 1):

查看文件

@@ -86,19 +86,13 @@ def mpl_plot(w, timewindow, dt, iterations, fft=False, save=False):
logging.info(f"Type: {w.type}") logging.info(f"Type: {w.type}")
logging.info(f"Maximum (absolute) amplitude: {np.max(np.abs(waveform)):g}") logging.info(f"Maximum (absolute) amplitude: {np.max(np.abs(waveform)):g}")
if w.freq and not w.type == "gaussian" and not w.type == "impulse": if w.freq and w.type != "gaussian" and w.type != "impulse":
logging.info(f"Centre frequency: {w.freq:g} Hz") logging.info(f"Centre frequency: {w.freq:g} Hz")
if ( if w.type in ["gaussian", "gaussiandot", "gaussiandotnorm", "gaussianprime", "gaussiandoubleprime"]:
w.type == "gaussian"
or w.type == "gaussiandot"
or w.type == "gaussiandotnorm"
or w.type == "gaussianprime"
or w.type == "gaussiandoubleprime"
):
delay = 1 / w.freq delay = 1 / w.freq
logging.info(f"Time to centre of pulse: {delay:g} s") logging.info(f"Time to centre of pulse: {delay:g} s")
elif w.type == "gaussiandotdot" or w.type == "gaussiandotdotnorm" or w.type == "ricker": elif w.type in ["gaussiandotdot", "gaussiandotdotnorm", "ricker"]:
delay = np.sqrt(2) / w.freq delay = np.sqrt(2) / w.freq
logging.info(f"Time to centre of pulse: {delay:g} s") logging.info(f"Time to centre of pulse: {delay:g} s")

查看文件

@@ -36,12 +36,9 @@ def generate_y(p1, p2, x):
def paint_y_axis(lines, pixels, x): def paint_y_axis(lines, pixels, x):
is_black = False is_black = False
target_ys = list(map(lambda line: int(generate_y(line[0], line[1], x)), lines)) target_ys = sorted(map(lambda line: int(generate_y(line[0], line[1], x)), lines))
target_ys.sort()
if len(target_ys) % 2: if len(target_ys) % 2:
distances = [] distances = [target_ys[i + 1] - target_ys[i] for i in range(len(target_ys) - 1)]
for i in range(len(target_ys) - 1):
distances.append(target_ys[i + 1] - target_ys[i])
# https://stackoverflow.com/a/17952763 # https://stackoverflow.com/a/17952763
min_idx = -min((x, -i) for i, x in enumerate(distances))[1] min_idx = -min((x, -i) for i, x in enumerate(distances))[1]
del target_ys[min_idx] del target_ys[min_idx]
@@ -54,7 +51,7 @@ def paint_y_axis(lines, pixels, x):
pixels[target_y][x] = True pixels[target_y][x] = True
is_black = not is_black is_black = not is_black
yi = target_y yi = target_y
assert is_black is False, "an error has occured at x%s" % x assert is_black is False, f"an error has occured at x{x}"
def generate_line_events(line_list): def generate_line_events(line_list):

查看文件

@@ -1,3 +1,4 @@
import itertools
import multiprocessing as mp import multiprocessing as mp
import sys import sys
@@ -90,10 +91,7 @@ def triangle_to_intersecting_lines(triangle, height, pixels, lines):
y = int(same[0][1]) y = int(same[0][1])
pixels[y][x] = True pixels[y][x] = True
else: else:
cross_lines = [] cross_lines = [(b, a) for a, b in itertools.product(above, below)]
for a in above:
for b in below:
cross_lines.append((b, a))
side1 = where_line_crosses_z(cross_lines[0][0], cross_lines[0][1], height) side1 = where_line_crosses_z(cross_lines[0][0], cross_lines[0][1], height)
side2 = where_line_crosses_z(cross_lines[1][0], cross_lines[1][1], height) side2 = where_line_crosses_z(cross_lines[1][0], cross_lines[1][1], height)
lines.append((side1, side2)) lines.append((side1, side2))