Beispiel #1
0
def antenna_like_GSSI_1500(x, y, z, resolution=0.001):
    """Inserts a description of an antenna similar to the GSSI 1.5GHz antenna. Can be used with 1mm (default) or 2mm spatial resolution. The external dimensions of the antenna are 170mm x 108mm x 45mm. One output point is defined between the arms of the receiever bowtie. The bowties are aligned with the y axis so the output is the y component of the electric field.
        
    Args:
        x, y, z (float): Coordinates of a location in the model to insert the antenna. Coordinates are relative to the geometric centre of the antenna in the x-y plane and the bottom of the antenna skid in the z direction.
        resolution (float): Spatial resolution for the antenna model.
    """
    
    # Antenna geometry properties
    casesize = (0.170, 0.108, 0.043)
    casethickness = 0.002
    shieldthickness = 0.002
    foamsurroundthickness = 0.003
    pcbthickness = 0.002
    skidthickness = 0.004
    bowtiebase = 0.022
    bowtieheight = 0.014
    patchheight = 0.015
    
    excitationfreq = 1.5e9 # GHz
    # excitationfreq = 1.71e9 # Value from http://hdl.handle.net/1842/4074
    sourceresistance = 50 # Ohms
    # sourceresistance = 4 # Value from http://hdl.handle.net/1842/4074
    
    x = x - (casesize[0] / 2)
    y = y - (casesize[1] / 2)
    
    # Coordinates of source excitation point in antenna
    tx = x + 0.114, y + 0.053, z + skidthickness
    
    if resolution == 0.001:
        dx = 0.001
        dy = 0.001
        dz = 0.001
    elif resolution == 0.002:
        dx = 0.002
        dy = 0.002
        dz = 0.002
        foamsurroundthickness = 0.002
        patchheight = 0.016
        tx = x + 0.112, y + 0.052, z + skidthickness
    else:
        raise CmdInputError('This antenna module can only be used with a spatial discretisation of 1mm or 2mm')

    # Material definitions
    print('#material: 1.7 0.59 1.0 0.0 absorber')
    # print('#material: 1.58 0.428 1.0 0.0 absorber') # Value from http://hdl.handle.net/1842/4074
    print('#material: 3.0 0.0 1.0 0.0 pcb')
    print('#material: 2.35 0.0 1.0 0.0 hdpe')

    # Antenna geometry
    # Plastic case
    print('#box: {} {} {} {} {} {} hdpe'.format(x, y, z + skidthickness, x + casesize[0], y + casesize[1], z + skidthickness + casesize[2]))
    print('#box: {} {} {} {} {} {} free_space'.format(x + casethickness, y + casethickness, z + skidthickness, x + casesize[0] - casethickness, y + casesize[1] - casethickness, z + skidthickness + casesize[2] - casethickness))
    
    # Metallic enclosure
    print('#box: {} {} {} {} {} {} pec'.format(x + 0.025, y + casethickness, z + skidthickness, x + casesize[0] - 0.025, y + casesize[1] - casethickness, z + skidthickness + 0.027))
    
    # Absorber material, and foam (modelled as PCB material) around edge of absorber
    print('#box: {} {} {} {} {} {} pcb'.format(x + 0.025 + shieldthickness, y + casethickness + shieldthickness, z + skidthickness, x + 0.025 + shieldthickness + 0.057, y + casesize[1] - casethickness - shieldthickness, z + skidthickness + 0.027 - shieldthickness - 0.001))
    print('#box: {} {} {} {} {} {} absorber'.format(x + 0.025 + shieldthickness + foamsurroundthickness, y + casethickness + shieldthickness + foamsurroundthickness, z + skidthickness, x + 0.025 + shieldthickness + 0.057 - foamsurroundthickness, y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness, z + skidthickness + 0.027 - shieldthickness))
    print('#box: {} {} {} {} {} {} pcb'.format(x + 0.086, y + casethickness + shieldthickness, z + skidthickness, x + 0.086 + 0.057, y + casesize[1] - casethickness - shieldthickness, z + skidthickness + 0.027 - shieldthickness - 0.001))
    print('#box: {} {} {} {} {} {} absorber'.format(x + 0.086 + foamsurroundthickness, y + casethickness + shieldthickness + foamsurroundthickness, z + skidthickness, x + 0.086 + 0.057 - foamsurroundthickness, y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness, z + skidthickness + 0.027 - shieldthickness))
    
    # PCB
    print('#box: {} {} {} {} {} {} pcb'.format(x + 0.025 + shieldthickness + foamsurroundthickness, y + casethickness + shieldthickness + foamsurroundthickness, z + skidthickness, x + 0.086 - shieldthickness - foamsurroundthickness, y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness, z + skidthickness + pcbthickness))
    print('#box: {} {} {} {} {} {} pcb'.format(x + 0.086 + foamsurroundthickness, y + casethickness + shieldthickness + foamsurroundthickness, z + skidthickness, x + 0.086 + 0.057 - foamsurroundthickness, y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness, z + skidthickness + pcbthickness))
    
    # PCB components
    if resolution == 0.001:
        # Rx & Tx bowties
        a = 0
        b = 0
        while b < 13:
            print('#plate: {} {} {} {} {} {} pec'.format(x + 0.045 + a*dx, y + 0.039 + b*dx, z + skidthickness, x + 0.065 - a*dx, y + 0.039 + b*dx + dy, z + skidthickness))
            print('#plate: {} {} {} {} {} {} pec'.format(x + 0.045 + a*dx, y + 0.067 - b*dx, z + skidthickness, x + 0.065 - a*dx, y + 0.067 - b*dx + dy, z + skidthickness))
            print('#plate: {} {} {} {} {} {} pec'.format(x + 0.104 + a*dx, y + 0.039 + b*dx, z + skidthickness, x + 0.124 - a*dx, y + 0.039 + b*dx + dy, z + skidthickness))
            print('#plate: {} {} {} {} {} {} pec'.format(x + 0.104 + a*dx, y + 0.067 - b*dx, z + skidthickness, x + 0.124 - a*dx, y + 0.067 - b*dx + dy, z + skidthickness))
            b += 1
            if a == 2 or a == 4 or a == 7:
                print('#plate: {} {} {} {} {} {} pec'.format(x + 0.045 + a*dx, y + 0.039 + b*dx, z + skidthickness, x + 0.065 - a*dx, y + 0.039 + b*dx + dy, z + skidthickness))
                print('#plate: {} {} {} {} {} {} pec'.format(x + 0.045 + a*dx, y + 0.067 - b*dx, z + skidthickness, x + 0.065 - a*dx, y + 0.067 - b*dx + dy, z + skidthickness))
                print('#plate: {} {} {} {} {} {} pec'.format(x + 0.104 + a*dx, y + 0.039 + b*dx, z + skidthickness, x + 0.124 - a*dx, y + 0.039 + b*dx + dy, z + skidthickness))
                print('#plate: {} {} {} {} {} {} pec'.format(x + 0.104 + a*dx, y + 0.067 - b*dx, z + skidthickness, x + 0.124 - a*dx, y + 0.067 - b*dx + dy, z + skidthickness))
                b += 1
            a += 1
        # Rx extension section (upper y)
        print('#plate: {} {} {} {} {} {} pec'.format(x + 0.044, y + 0.068, z + skidthickness, x + 0.044 + bowtiebase, y + 0.068 + patchheight, z + skidthickness))
        # Tx extension section (upper y)
        print('#plate: {} {} {} {} {} {} pec'.format(x + 0.103, y + 0.068, z + skidthickness, x + 0.103 + bowtiebase, y + 0.068 + patchheight, z + skidthickness))
        
        # Edges that represent wire between bowtie halves in 1mm model
        print('#edge: {} {} {} {} {} {} pec'.format(tx[0] - 0.059, tx[1] - dy, tx[2], tx[0] - 0.059, tx[1], tx[2]))
        print('#edge: {} {} {} {} {} {} pec'.format(tx[0] - 0.059, tx[1] + dy, tx[2], tx[0] - 0.059, tx[1] + 0.002, tx[2]))
        print('#edge: {} {} {} {} {} {} pec'.format(tx[0], tx[1] - dy, tx[2], tx[0], tx[1], tx[2]))
        print('#edge: {} {} {} {} {} {} pec'.format(tx[0], tx[1] + dz, tx[2], tx[0], tx[1] + 0.002, tx[2]))

    elif resolution == 0.002:
    # Rx & Tx bowties
        for a in range(0,6):
            print('#plate: {} {} {} {} {} {} pec'.format(x + 0.044 + a*dx, y + 0.040 + a*dx, z + skidthickness, x + 0.066 - a*dx, y + 0.040 + a*dx + dy, z + skidthickness))
            print('#plate: {} {} {} {} {} {} pec'.format(x + 0.044 + a*dx, y + 0.064 - a*dx, z + skidthickness, x + 0.066 - a*dx, y + 0.064 - a*dx + dy, z + skidthickness))
            print('#plate: {} {} {} {} {} {} pec'.format(x + 0.103 + a*dx, y + 0.040 + a*dx, z + skidthickness, x + 0.125 - a*dx, y + 0.040 + a*dx + dy, z + skidthickness))
            print('#plate: {} {} {} {} {} {} pec'.format(x + 0.103 + a*dx, y + 0.064 - a*dx, z + skidthickness, x + 0.125 - a*dx, y + 0.064 - a*dx + dy, z + skidthickness))
            # Rx extension section (upper y)
            print('#plate: {} {} {} {} {} {} pec'.format(x + 0.044, y + 0.066, z + skidthickness, x + 0.044 + bowtiebase, y + 0.066 + patchheight, z + skidthickness))
            # Tx extension section (upper y)
            print('#plate: {} {} {} {} {} {} pec'.format(x + 0.103, y + 0.066, z + skidthickness, x + 0.103 + bowtiebase, y + 0.066 + patchheight, z + skidthickness))

    # Rx extension section (lower y)
    print('#plate: {} {} {} {} {} {} pec'.format(x + 0.044, y + 0.024, z + skidthickness, x + 0.044 + bowtiebase, y + 0.024 + patchheight, z + skidthickness))
    # Tx extension section (lower y)
    print('#plate: {} {} {} {} {} {} pec'.format(x + 0.103, y + 0.024, z + skidthickness, x + 0.103 + bowtiebase, y + 0.024 + patchheight, z + skidthickness))

    # Skid
    print('#box: {} {} {} {} {} {} hdpe'.format(x, y, z, x + casesize[0], y + casesize[1], z + skidthickness))
    
    # Geometry views
    #print('#geometry_view: {} {} {} {} {} {} {} {} {} antenna_like_GSSI_1500 n'.format(x - dx, y - dy, z - dz, x + casesize[0] + dx, y + casesize[1] + dy, z + skidthickness + casesize[2] + dz, dx, dy, dz))
    #print('#geometry_view: {} {} {} {} {} {} {} {} {} antenna_like_GSSI_1500_pcb f'.format(x, y, z, x + casesize[0], y + casesize[1], z + 0.010, dx, dy, dz))
    
    # Excitation
    print('#waveform: gaussian 1.0 {} myGaussian'.format(excitationfreq))
    print('#voltage_source: y {} {} {} {} myGaussian'.format(tx[0], tx[1], tx[2], sourceresistance))
    
    # Output point - transmitter bowtie
    #print('#rx: {} {} {}'.format(tx[0], tx[1], tx[2]))
    # Output point - receiver bowtie
    print('#rx: {} {} {}'.format(tx[0] - 0.059, tx[1], tx[2]))
Beispiel #2
0
"""Plots a B-scan image."""

# Parse command line arguments
parser = argparse.ArgumentParser(description='Plots a B-scan image.', usage='cd gprMax; python -m tools.plot_Bscan outputfile output')
parser.add_argument('outputfile', help='name of output file including path')
parser.add_argument('output', help='name of output component to be plotted (Ex, Ey, Ez, Hx, Hy, Hz, Ix, Iy or Iz)')
args = parser.parse_args()

# Open output file and read some attributes
f = h5py.File(args.outputfile, 'r')
nrx = f.attrs['nrx']

# Check there are any receivers
if nrx == 0:
    raise CmdInputError('No receivers found in {}'.format(args.outputfile))

for rx in range(1, nrx + 1):
    path = '/rxs/rx' + str(rx) + '/'
    availableoutputs = list(f[path].keys())

    # Check if requested output is in file
    if args.output not in availableoutputs:
        raise CmdInputError('{} output requested to plot, but the available output for receiver 1 is {}'.format(args.output, ', '.join(availableoutputs)))

    outputdata = f[path + '/' + args.output]

    # Check that there is more than one A-scan present
    if outputdata.shape[1] == 1:
        raise CmdInputError('{} contains only a single A-scan.'.format(args.outputfile))
Beispiel #3
0
def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False):
    """Plots electric and magnetic fields and currents from all receiver points in the given output file. Each receiver point is plotted in a new figure window.

    Args:
        filename (string): Filename (including path) of output file.
        outputs (list): List of field/current components to plot.
        fft (boolean): Plot FFT switch.

    Returns:
        plt (object): matplotlib plot object.
    """

    # Open output file and read some attributes
    f = h5py.File(filename, 'r')
    nrx = f.attrs['nrx']
    dt = f.attrs['dt']
    iterations = f.attrs['Iterations']
    time = np.linspace(0, (iterations - 1) * dt, num=iterations)

    # Check there are any receivers
    if nrx == 0:
        raise CmdInputError('No receivers found in {}'.format(filename))

    # Check for single output component when doing a FFT
    if fft:
        if not len(outputs) == 1:
            raise CmdInputError('A single output must be specified when using the -fft option')

    # New plot for each receiver
    for rx in range(1, nrx + 1):
        path = '/rxs/rx' + str(rx) + '/'
        availableoutputs = list(f[path].keys())

        # If only a single output is required, create one subplot
        if len(outputs) == 1:

            # Check for polarity of output and if requested output is in file
            if outputs[0][-1] == '-':
                polarity = -1
                outputtext = '-' + outputs[0][0:-1]
                output = outputs[0][0:-1]
            else:
                polarity = 1
                outputtext = outputs[0]
                output = outputs[0]

            if output not in availableoutputs:
                raise CmdInputError('{} output requested to plot, but the available output for receiver 1 is {}'.format(output, ', '.join(availableoutputs)))

            outputdata = f[path + output][:] * polarity

            # Plotting if FFT required
            if fft:
                # FFT
                freqs, power = fft_power(outputdata, dt)
                freqmaxpower = np.where(np.isclose(power, 0))[0][0]

                # Set plotting range to -60dB from maximum power or 4 times
                # frequency at maximum power
                try:
                    pltrange = np.where(power[freqmaxpower:] < -60)[0][0] + freqmaxpower + 1
                except:
                    pltrange = freqmaxpower * 4

                pltrange = np.s_[0:pltrange]

                # Plot time history of output component
                fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, num='rx' + str(rx), figsize=(20, 10), facecolor='w', edgecolor='w')
                line1 = ax1.plot(time, outputdata, 'r', lw=2, label=outputtext)
                ax1.set_xlabel('Time [s]')
                ax1.set_ylabel(outputtext + ' field strength [V/m]')
                ax1.set_xlim([0, np.amax(time)])
                ax1.grid(which='both', axis='both', linestyle='-.')

                # Plot frequency spectra
                markerline, stemlines, baseline = ax2.stem(freqs[pltrange], power[pltrange], '-.')
                plt.setp(baseline, 'linewidth', 0)
                plt.setp(stemlines, 'color', 'r')
                plt.setp(markerline, 'markerfacecolor', 'r', 'markeredgecolor', 'r')
                line2 = ax2.plot(freqs[pltrange], power[pltrange], 'r', lw=2)
                ax2.set_xlabel('Frequency [Hz]')
                ax2.set_ylabel('Power [dB]')
                ax2.grid(which='both', axis='both', linestyle='-.')

                # Change colours and labels for magnetic field components or currents
                if 'H' in outputs[0]:
                    plt.setp(line1, color='g')
                    plt.setp(line2, color='g')
                    plt.setp(ax1, ylabel=outputtext + ' field strength [A/m]')
                    plt.setp(stemlines, 'color', 'g')
                    plt.setp(markerline, 'markerfacecolor', 'g', 'markeredgecolor', 'g')
                elif 'I' in outputs[0]:
                    plt.setp(line1, color='b')
                    plt.setp(line2, color='b')
                    plt.setp(ax1, ylabel=outputtext + ' current [A]')
                    plt.setp(stemlines, 'color', 'b')
                    plt.setp(markerline, 'markerfacecolor', 'b', 'markeredgecolor', 'b')

                plt.show()

            # Plotting if no FFT required
            else:
                fig, ax = plt.subplots(subplot_kw=dict(xlabel='Time [s]', ylabel=outputtext + ' field strength [V/m]'), num='rx' + str(rx), figsize=(20, 10), facecolor='w', edgecolor='w')
                line = ax.plot(time, outputdata, 'r', lw=2, label=outputtext)
                ax.set_xlim([0, np.amax(time)])
                # ax.set_ylim([-15, 20])
                ax.grid(which='both', axis='both', linestyle='-.')

                if 'H' in output:
                    plt.setp(line, color='g')
                    plt.setp(ax, ylabel=outputtext + ', field strength [A/m]')
                elif 'I' in output:
                    plt.setp(line, color='b')
                    plt.setp(ax, ylabel=outputtext + ', current [A]')

        # If multiple outputs required, create all nine subplots and populate only the specified ones
        else:
            fig, ax = plt.subplots(subplot_kw=dict(xlabel='Time [s]'), num='rx' + str(rx), figsize=(20, 10), facecolor='w', edgecolor='w')
            if len(outputs) == 9:
                gs = gridspec.GridSpec(3, 3, hspace=0.3, wspace=0.3)
            else:
                gs = gridspec.GridSpec(3, 2, hspace=0.3, wspace=0.3)

            for output in outputs:
                # Check for polarity of output and if requested output is in file
                if output[-1] == 'm':
                    polarity = -1
                    outputtext = '-' + output[0:-1]
                    output = output[0:-1]
                else:
                    polarity = 1
                    outputtext = output

                # Check if requested output is in file
                if output not in availableoutputs:
                    raise CmdInputError('Output(s) requested to plot: {}, but available output(s) for receiver {} in the file: {}'.format(', '.join(outputs), rx, ', '.join(availableoutputs)))

                outputdata = f[path + output][:] * polarity

                if output == 'Ex':
                    ax = plt.subplot(gs[0, 0])
                    ax.plot(time, outputdata, 'r', lw=2, label=outputtext)
                    ax.set_ylabel(outputtext + ', field strength [V/m]')
                # ax.set_ylim([-15, 20])
                elif output == 'Ey':
                    ax = plt.subplot(gs[1, 0])
                    ax.plot(time, outputdata, 'r', lw=2, label=outputtext)
                    ax.set_ylabel(outputtext + ', field strength [V/m]')
                # ax.set_ylim([-15, 20])
                elif output == 'Ez':
                    ax = plt.subplot(gs[2, 0])
                    ax.plot(time, outputdata, 'r', lw=2, label=outputtext)
                    ax.set_ylabel(outputtext + ', field strength [V/m]')
                # ax.set_ylim([-15, 20])
                elif output == 'Hx':
                    ax = plt.subplot(gs[0, 1])
                    ax.plot(time, outputdata, 'g', lw=2, label=outputtext)
                    ax.set_ylabel(outputtext + ', field strength [A/m]')
                # ax.set_ylim([-0.03, 0.03])
                elif output == 'Hy':
                    ax = plt.subplot(gs[1, 1])
                    ax.plot(time, outputdata, 'g', lw=2, label=outputtext)
                    ax.set_ylabel(outputtext + ', field strength [A/m]')
                # ax.set_ylim([-0.03, 0.03])
                elif output == 'Hz':
                    ax = plt.subplot(gs[2, 1])
                    ax.plot(time, outputdata, 'g', lw=2, label=outputtext)
                    ax.set_ylabel(outputtext + ', field strength [A/m]')
                # ax.set_ylim([-0.03, 0.03])
                elif output == 'Ix':
                    ax = plt.subplot(gs[0, 2])
                    ax.plot(time, outputdata, 'b', lw=2, label=outputtext)
                    ax.set_ylabel(outputtext + ', current [A]')
                elif output == 'Iy':
                    ax = plt.subplot(gs[1, 2])
                    ax.plot(time, outputdata, 'b', lw=2, label=outputtext)
                    ax.set_ylabel(outputtext + ', current [A]')
                elif output == 'Iz':
                    ax = plt.subplot(gs[2, 2])
                    ax.plot(time, outputdata, 'b', lw=2, label=outputtext)
                    ax.set_ylabel(outputtext + ', current [A]')
            for ax in fig.axes:
                ax.set_xlim([0, np.amax(time)])
                ax.grid(which='both', axis='both', linestyle='-.')

        # Save a PDF/PNG of the figure
        # fig.savefig(os.path.splitext(os.path.abspath(filename))[0] + '_rx' + str(rx) + '.pdf', dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1)
        # fig.savefig(os.path.splitext(os.path.abspath(filename))[0] + '_rx' + str(rx) + '.png', dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)

    return plt
Beispiel #4
0
def run_opt_sim(args, inputfile, usernamespace):
    """Run a simulation using Taguchi's optmisation process.

    Args:
        args (dict): Namespace with command line arguments
        inputfile (object): File object for the input file.
        usernamespace (dict): Namespace that can be accessed by user
                in any Python code blocks in input file.
    """

    tsimstart = perf_counter()

    if args.n > 1:
        raise CmdInputError('When a Taguchi optimisation is being carried out the number of model runs argument is not required')

    inputfileparts = os.path.splitext(inputfile.name)

    # Default maximum number of iterations of optimisation to perform (used
    # if the stopping criterion is not achieved)
    maxiterations = 20

    # Process Taguchi code blocks in the input file; pass in ordered
    # dictionary to hold parameters to optimise
    tmp = usernamespace.copy()
    tmp.update({'optparams': OrderedDict()})
    taguchinamespace = taguchi_code_blocks(inputfile, tmp)

    # Extract dictionaries and variables containing initialisation parameters
    optparams = taguchinamespace['optparams']
    fitness = taguchinamespace['fitness']
    if 'maxiterations' in taguchinamespace:
        maxiterations = taguchinamespace['maxiterations']

    # Store initial parameter ranges
    optparamsinit = list(optparams.items())

    # Dictionary to hold history of optmised values of parameters
    optparamshist = OrderedDict((key, list()) for key in optparams)

    # Import specified fitness function
    fitness_metric = getattr(import_module('user_libs.optimisation_taguchi.fitness_functions'), fitness['name'])

    # Select OA
    OA, N, cols, k, s, t = construct_OA(optparams)

    taguchistr = '\n--- Taguchi optimisation'
    print('{} {}\n'.format(taguchistr, '-' * (get_terminal_width() - 1 - len(taguchistr))))
    print('Orthogonal array: {:g} experiments per iteration, {:g} parameters ({:g} will be used), {:g} levels, and strength {:g}'.format(N, cols, k, s, t))
    tmp = [(k, v) for k, v in optparams.items()]
    print('Parameters to optimise with ranges: {}'.format(str(tmp).strip('[]')))
    print('Output name(s) from model: {}'.format(fitness['args']['outputs']))
    print('Fitness function "{}" with stopping criterion {:g}'.format(fitness['name'], fitness['stop']))
    print('Maximum iterations: {:g}'.format(maxiterations))

    # Initialise arrays and lists to store parameters required throughout optimisation
    # Lower, central, and upper values for each parameter
    levels = np.zeros((s, k), dtype=floattype)
    # Optimal lower, central, or upper value for each parameter
    levelsopt = np.zeros(k, dtype=np.uint8)
    # Difference used to set values for levels
    levelsdiff = np.zeros(k, dtype=floattype)
    # History of fitness values from each confirmation experiment
    fitnessvalueshist = []

    iteration = 0
    while iteration < maxiterations:
        # Reset number of model runs to number of experiments
        args.n = N
        usernamespace['number_model_runs'] = N

        # Fitness values for each experiment
        fitnessvalues = []

        # Set parameter ranges and define experiments
        optparams, levels, levelsdiff = calculate_ranges_experiments(optparams, optparamsinit, levels, levelsopt, levelsdiff, OA, N, k, s, iteration)

        # Run model for each experiment
        # Mixed mode MPI with OpenMP or CUDA - MPI task farm for models with
        # each model parallelised with OpenMP (CPU) or CUDA (GPU)
        if args.mpi:
            run_mpi_sim(args, inputfile, usernamespace, optparams)
        # Standard behaviour - models run serially with each model parallelised
        # with OpenMP (CPU) or CUDA (GPU)
        else:
            run_std_sim(args, inputfile, usernamespace, optparams)

        # Calculate fitness value for each experiment
        for experiment in range(1, N + 1):
            outputfile = inputfileparts[0] + str(experiment) + '.out'
            fitnessvalues.append(fitness_metric(outputfile, fitness['args']))
            os.remove(outputfile)

        taguchistr = '\n--- Taguchi optimisation, iteration {}: {} initial experiments with fitness values {}.'.format(iteration + 1, N, fitnessvalues)
        print('{} {}\n'.format(taguchistr, '-' * (get_terminal_width() - 1 - len(taguchistr))))

        # Calculate optimal levels from fitness values by building a response
        # table; update dictionary of parameters with optimal values
        optparams, levelsopt = calculate_optimal_levels(optparams, levels, levelsopt, fitnessvalues, OA, N, k)

        # Update dictionary with history of parameters with optimal values
        for key, value in optparams.items():
            optparamshist[key].append(value[0])

        # Run a confirmation experiment with optimal values
        args.n = 1
        usernamespace['number_model_runs'] = 1
        # Mixed mode MPI with OpenMP or CUDA - MPI task farm for models with
        # each model parallelised with OpenMP (CPU) or CUDA (GPU)
        if args.mpi:
            run_mpi_sim(args, inputfile, usernamespace, optparams)
        # Standard behaviour - models run serially with each model parallelised
        # with OpenMP (CPU) or CUDA (GPU)
        else:
            run_std_sim(args, inputfile, usernamespace, optparams)

        # Calculate fitness value for confirmation experiment
        outputfile = inputfileparts[0] + '.out'
        fitnessvalueshist.append(fitness_metric(outputfile, fitness['args']))

        # Rename confirmation experiment output file so that it is retained for each iteraction
        os.rename(outputfile, os.path.splitext(outputfile)[0] + '_final' + str(iteration + 1) + '.out')

        taguchistr = '\n--- Taguchi optimisation, iteration {} completed. History of optimal parameter values {} and of fitness values {}'.format(iteration + 1, dict(optparamshist), fitnessvalueshist)
        print('{} {}\n'.format(taguchistr, '-' * (get_terminal_width() - 1 - len(taguchistr))))
        iteration += 1

        # Stop optimisation if stopping criterion has been reached
        if fitnessvalueshist[iteration - 1] > fitness['stop']:
            taguchistr = '\n--- Taguchi optimisation stopped as fitness criteria reached: {:g} > {:g}'.format(fitnessvalueshist[iteration - 1], fitness['stop'])
            print('{} {}\n'.format(taguchistr, '-' * (get_terminal_width() - 1 - len(taguchistr))))
            break

        # Stop optimisation if successive fitness values are within a percentage threshold
        fitnessvaluesthres = 0.1
        if iteration > 2:
            fitnessvaluesclose = (np.abs(fitnessvalueshist[iteration - 2] - fitnessvalueshist[iteration - 1]) / fitnessvalueshist[iteration - 1]) * 100
            if fitnessvaluesclose < fitnessvaluesthres:
                taguchistr = '\n--- Taguchi optimisation stopped as successive fitness values within {}%'.format(fitnessvaluesthres)
                print('{} {}\n'.format(taguchistr, '-' * (get_terminal_width() - 1 - len(taguchistr))))
                break

    tsimend = perf_counter()

    # Save optimisation parameters history and fitness values history to file
    opthistfile = inputfileparts[0] + '_hist.pickle'
    with open(opthistfile, 'wb') as f:
        pickle.dump(optparamshist, f)
        pickle.dump(fitnessvalueshist, f)
        pickle.dump(optparamsinit, f)

    taguchistr = '\n=== Taguchi optimisation completed in [HH:MM:SS]: {} after {} iteration(s)'.format(datetime.timedelta(seconds=int(tsimend - tsimstart)), iteration)
    print('{} {}\n'.format(taguchistr, '=' * (get_terminal_width() - 1 - len(taguchistr))))
    print('History of optimal parameter values {} and of fitness values {}\n'.format(dict(optparamshist), fitnessvalueshist))
Beispiel #5
0
if not pmlcheck:
    cmd1 = '#abc_type: pml'
    cmd2 = '#pml_layers: 10'
    print("Commands '{}' and '{}' added.".format(cmd1, cmd2))
    inputlines.append(cmd1)
    inputlines.append(cmd2)

# Process materials
for position, material in materials.items():
    params = material.split()
    debye = next((debye for debye in debyes if debye.split()[-1] == params[5]),
                 None)
    if debye:
        if len(debye.split()) > 5:
            raise CmdInputError(
                "Command '{}' cannot be used in the old version of gprMax as it only supports materials with a single Debye pole."
                .format(''.join(debye)))
        medium = '#medium: {} {} {} {} {} {} {}'.format(
            float(params[1]) + float(debye.split()[2]), params[1],
            float(debye.split()[3]), params[2], params[3], params[4],
            params[5])
        print("Commands '{}' and '{}', replaced with '{}'".format(
            material, debye, medium))
    else:
        medium = '#medium: {} 0 0 {} {} {} {}'.format(params[1], params[2],
                                                      params[3], params[4],
                                                      params[5])
        print("Command '{}', replaced with '{}'".format(material, medium))
    inputlines[position] = medium

# Create #analysis block
Beispiel #6
0
    parser.add_argument('amp', type=float, help='amplitude of waveform')
    parser.add_argument('freq',
                        type=float,
                        help='centre frequency of waveform')
    parser.add_argument('timewindow', help='time window to view waveform')
    parser.add_argument('dt', type=float, help='time step to view waveform')
    parser.add_argument('-fft',
                        action='store_true',
                        help='plot FFT of waveform',
                        default=False)
    args = parser.parse_args()

    # Check waveform parameters
    if args.type.lower() not in Waveform.types:
        raise CmdInputError(
            'The waveform must have one of the following types {}'.format(
                ', '.join(Waveform.types)))
    if args.freq <= 0:
        raise CmdInputError(
            'The waveform requires an excitation frequency value of greater than zero'
        )

    # Create waveform instance
    w = Waveform()
    w.type = args.type
    w.amp = args.amp
    w.freq = args.freq

    timewindow, iterations = check_timewindow(args.timewindow, args.dt)
    plthandle = mpl_plot(w, timewindow, args.dt, iterations, args.fft)
    plthandle.show()
Beispiel #7
0
def antenna_like_GSSI_1500(x, y, z, resolution=0.001, rotate90=False):
    """Inserts a description of an antenna similar to the GSSI 1.5GHz antenna. Can be used with 1mm (default) or 2mm spatial resolution. The external dimensions of the antenna are 170x108x45mm. One output point is defined between the arms of the receiver bowtie. The bowties are aligned with the y axis so the output is the y component of the electric field (x component if the antenna is rotated 90 degrees).

    Args:
        x, y, z (float): Coordinates of a location in the model to insert the antenna. Coordinates are relative to the geometric centre of the antenna in the x-y plane and the bottom of the antenna skid in the z direction.
        resolution (float): Spatial resolution for the antenna model.
        rotate90 (bool): Rotate model 90 degrees CCW in xy plane.
    """

    # Antenna geometry properties
    casesize = (0.170, 0.108, 0.043)
    casethickness = 0.002
    shieldthickness = 0.002
    foamsurroundthickness = 0.003
    pcbthickness = 0.002
    skidthickness = 0.004
    bowtiebase = 0.022
    bowtieheight = 0.014
    patchheight = 0.015

    # Set origin for rotation to geometric centre of antenna in x-y plane if required, and set output component for receiver
    if rotate90:
        rotate90origin = (x, y)
        output = 'Ex'
    else:
        rotate90origin = ()
        output = 'Ey'

    x = x - (casesize[0] / 2)
    y = y - (casesize[1] / 2)

    # Coordinates of source excitation point in antenna
    tx = x + 0.114, y + 0.053, z + skidthickness

    if resolution == 0.001:
        dx = 0.001
        dy = 0.001
        dz = 0.001
    elif resolution == 0.002:
        dx = 0.002
        dy = 0.002
        dz = 0.002
        foamsurroundthickness = 0.002
        patchheight = 0.016
        tx = x + 0.114, y + 0.052, z + skidthickness
    else:
        raise CmdInputError('This antenna module can only be used with a spatial discretisation of 1mm or 2mm')

    # Specify optimisation state of antenna model
    optstate = ['WarrenThesis', 'DebyeAbsorber', 'GiannakisPaper']
    optstate = optstate[0]

    if optstate == 'WarrenThesis':
        # Original optimised values from http://hdl.handle.net/1842/4074
        excitationfreq = 1.71e9
        sourceresistance = 230  # Correction for old (< 123) GprMax3D bug (optimised to 4)
        rxres = 925  # Resistance at Rx bowtie
        material(1.58, 0.428, 1, 0, 'absorber1')
        material(3, 0, 1, 0, 'absorber2') # Foam modelled as PCB material
        material(3, 0, 1, 0, 'pcb')
        material(2.35, 0, 1, 0, 'hdpe')
        material(3, (1 / rxres) * (dy / (dx * dz)), 1, 0, 'rxres')

    elif optstate == 'DebyeAbsorber':
        # Same values as WarrenThesis but uses dispersive absorber properties for Eccosorb LS22
        excitationfreq = 1.71e9
        sourceresistance = 230  # Correction for old (< 123) GprMax3D bug (optimised to 4)
        rxres = 925  # Resistance at Rx bowtie
        material(1, 0, 1, 0, 'absorber1')
        print('#add_dispersion_debye: 3 3.7733 1.00723e-11 3.14418 1.55686e-10 20.2441 3.44129e-10 absorber1') # Eccosorb LS22 3-pole Debye model (https://bitbucket.org/uoyaeg/aegboxts/wiki/Home)
        material(3, 0, 1, 0, 'absorber2') # Foam modelled as PCB material
        material(3, 0, 1, 0, 'pcb')
        material(2.35, 0, 1, 0, 'hdpe')
        material(3, (1 / rxres) * (dy / (dx * dz)), 1, 0, 'rxres')

    elif optstate == 'GiannakisPaper':
        # Further optimised values from https://doi.org/10.1109/TGRS.2018.2869027
        sourceresistance = 195
        material(3.96, 0.31, 1, 0, 'absorber1')
        material(1.05, 1.01, 1, 0, 'absorber2')
        material(1.37, 0.0002, 1, 0, 'pcb')
        material(1.99, 0.013, 1, 0, 'hdpe')

    # Antenna geometry
    # Plastic case
    box(x, y, z + skidthickness, x + casesize[0], y + casesize[1], z + skidthickness + casesize[2], 'hdpe', rotate90origin=rotate90origin)
    box(x + casethickness, y + casethickness, z + skidthickness, x + casesize[0] - casethickness, y + casesize[1] - casethickness, z + skidthickness + casesize[2] - casethickness, 'free_space', rotate90origin=rotate90origin)

    # Metallic enclosure
    box(x + 0.025, y + casethickness, z + skidthickness, x + casesize[0] - 0.025, y + casesize[1] - casethickness, z + skidthickness + 0.027, 'pec', rotate90origin=rotate90origin)

    # Absorber material (absorber1) and foam (absorber2) around edge of absorber
    box(x + 0.025 + shieldthickness, y + casethickness + shieldthickness, z + skidthickness, x + 0.025 + shieldthickness + 0.057, y + casesize[1] - casethickness - shieldthickness, z + skidthickness + 0.027 - shieldthickness - 0.001, 'absorber2', rotate90origin=rotate90origin)
    box(x + 0.025 + shieldthickness + foamsurroundthickness, y + casethickness + shieldthickness + foamsurroundthickness, z + skidthickness, x + 0.025 + shieldthickness + 0.057 - foamsurroundthickness, y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness, z + skidthickness + 0.027 - shieldthickness, 'absorber1', rotate90origin=rotate90origin)
    box(x + 0.086, y + casethickness + shieldthickness, z + skidthickness, x + 0.086 + 0.057, y + casesize[1] - casethickness - shieldthickness, z + skidthickness + 0.027 - shieldthickness - 0.001, 'absorber2', rotate90origin=rotate90origin)
    box(x + 0.086 + foamsurroundthickness, y + casethickness + shieldthickness + foamsurroundthickness, z + skidthickness, x + 0.086 + 0.057 - foamsurroundthickness, y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness, z + skidthickness + 0.027 - shieldthickness, 'absorber1', rotate90origin=rotate90origin)

    # PCB
    box(x + 0.025 + shieldthickness + foamsurroundthickness, y + casethickness + shieldthickness + foamsurroundthickness, z + skidthickness, x + 0.086 - shieldthickness - foamsurroundthickness, y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness, z + skidthickness + pcbthickness, 'pcb', rotate90origin=rotate90origin)
    box(x + 0.086 + foamsurroundthickness, y + casethickness + shieldthickness + foamsurroundthickness, z + skidthickness, x + 0.086 + 0.057 - foamsurroundthickness, y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness, z + skidthickness + pcbthickness, 'pcb', rotate90origin=rotate90origin)

    # PCB components
    if resolution == 0.001:
        # Rx & Tx bowties
        a = 0
        b = 0
        while b < 13:
            plate(x + 0.045 + a * dx, y + 0.039 + b * dx, z + skidthickness, x + 0.065 - a * dx, y + 0.039 + b * dx + dy, z + skidthickness, 'pec', rotate90origin=rotate90origin)
            plate(x + 0.045 + a * dx, y + 0.067 - b * dx, z + skidthickness, x + 0.065 - a * dx, y + 0.067 - b * dx + dy, z + skidthickness, 'pec', rotate90origin=rotate90origin)
            plate(x + 0.104 + a * dx, y + 0.039 + b * dx, z + skidthickness, x + 0.124 - a * dx, y + 0.039 + b * dx + dy, z + skidthickness, 'pec', rotate90origin=rotate90origin)
            plate(x + 0.104 + a * dx, y + 0.067 - b * dx, z + skidthickness, x + 0.124 - a * dx, y + 0.067 - b * dx + dy, z + skidthickness, 'pec', rotate90origin=rotate90origin)
            b += 1
            if a == 2 or a == 4 or a == 7:
                plate(x + 0.045 + a * dx, y + 0.039 + b * dx, z + skidthickness, x + 0.065 - a * dx, y + 0.039 + b * dx + dy, z + skidthickness, 'pec', rotate90origin=rotate90origin)
                plate(x + 0.045 + a * dx, y + 0.067 - b * dx, z + skidthickness, x + 0.065 - a * dx, y + 0.067 - b * dx + dy, z + skidthickness, 'pec', rotate90origin=rotate90origin)
                plate(x + 0.104 + a * dx, y + 0.039 + b * dx, z + skidthickness, x + 0.124 - a * dx, y + 0.039 + b * dx + dy, z + skidthickness, 'pec', rotate90origin=rotate90origin)
                plate(x + 0.104 + a * dx, y + 0.067 - b * dx, z + skidthickness, x + 0.124 - a * dx, y + 0.067 - b * dx + dy, z + skidthickness, 'pec', rotate90origin=rotate90origin)
                b += 1
            a += 1
        # Rx extension section (upper y)
        plate(x + 0.044, y + 0.068, z + skidthickness, x + 0.044 + bowtiebase, y + 0.068 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin)
        # Tx extension section (upper y)
        plate(x + 0.103, y + 0.068, z + skidthickness, x + 0.103 + bowtiebase, y + 0.068 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin)

        # Edges that represent wire between bowtie halves in 1mm model
        edge(tx[0] - 0.059, tx[1] - dy, tx[2], tx[0] - 0.059, tx[1], tx[2], 'pec', rotate90origin=rotate90origin)
        edge(tx[0] - 0.059, tx[1] + dy, tx[2], tx[0] - 0.059, tx[1] + 0.002, tx[2], 'pec', rotate90origin=rotate90origin)
        edge(tx[0], tx[1] - dy, tx[2], tx[0], tx[1], tx[2], 'pec', rotate90origin=rotate90origin)
        edge(tx[0], tx[1] + dz, tx[2], tx[0], tx[1] + 0.002, tx[2], 'pec', rotate90origin=rotate90origin)

    elif resolution == 0.002:
        # Rx & Tx bowties
        for a in range(0, 6):
            plate(x + 0.044 + a * dx, y + 0.040 + a * dx, z + skidthickness, x + 0.066 - a * dx, y + 0.040 + a * dx + dy, z + skidthickness, 'pec', rotate90origin=rotate90origin)
            plate(x + 0.044 + a * dx, y + 0.064 - a * dx, z + skidthickness, x + 0.066 - a * dx, y + 0.064 - a * dx + dy, z + skidthickness, 'pec', rotate90origin=rotate90origin)
            plate(x + 0.103 + a * dx, y + 0.040 + a * dx, z + skidthickness, x + 0.125 - a * dx, y + 0.040 + a * dx + dy, z + skidthickness, 'pec', rotate90origin=rotate90origin)
            plate(x + 0.103 + a * dx, y + 0.064 - a * dx, z + skidthickness, x + 0.125 - a * dx, y + 0.064 - a * dx + dy, z + skidthickness, 'pec', rotate90origin=rotate90origin)
            # Rx extension section (upper y)
            plate(x + 0.044, y + 0.066, z + skidthickness, x + 0.044 + bowtiebase, y + 0.066 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin)
            # Tx extension section (upper y)
            plate(x + 0.103, y + 0.066, z + skidthickness, x + 0.103 + bowtiebase, y + 0.066 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin)

    # Rx extension section (lower y)
    plate(x + 0.044, y + 0.024, z + skidthickness, x + 0.044 + bowtiebase, y + 0.024 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin)
    # Tx extension section (lower y)
    plate(x + 0.103, y + 0.024, z + skidthickness, x + 0.103 + bowtiebase, y + 0.024 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin)

    # Skid
    box(x, y, z, x + casesize[0], y + casesize[1], z + skidthickness, 'hdpe', rotate90origin=rotate90origin)

    # Geometry views
    # geometry_view(x - dx, y - dy, z - dz, x + casesize[0] + dx, y + casesize[1] + dy, z + skidthickness + casesize[2] + dz, dx, dy, dz, 'antenna_like_GSSI_1500')
    # geometry_view(x, y, z, x + casesize[0], y + casesize[1], z + 0.010, dx, dy, dz, 'antenna_like_GSSI_1500_pcb', type='f')

    # Excitation
    if optstate == 'WarrenThesis' or optstate == 'DebyeAbsorber':
        # Gaussian pulse
        print('#waveform: gaussian 1 {} myGaussian'.format(excitationfreq))
        voltage_source('y', tx[0], tx[1], tx[2], sourceresistance, 'myGaussian', dxdy=(resolution, resolution), rotate90origin=rotate90origin)

    elif optstate == 'GiannakisPaper':
        # Optimised custom pulse
        print('#excitation_file: {} linear extrapolate'.format(os.path.join(userlibdir, 'GSSI1p5optpulse.txt')))
        voltage_source('y', tx[0], tx[1], tx[2], sourceresistance, 'GSSI1p5optpulse', dxdy=(resolution, resolution), rotate90origin=rotate90origin)

    # Output point - receiver bowtie
    if resolution == 0.001:
        if optstate == 'WarrenThesis' or optstate == 'DebyeAbsorber':
            edge(tx[0] - 0.059, tx[1], tx[2], tx[0] - 0.059, tx[1] + dy, tx[2], 'rxres', rotate90origin=rotate90origin)
        rx(tx[0] - 0.059, tx[1], tx[2], identifier='rxbowtie', to_save=[output], polarisation='y', dxdy=(resolution, resolution), rotate90origin=rotate90origin)
    elif resolution == 0.002:
        if optstate == 'WarrenThesis' or optstate == 'DebyeAbsorber':
            edge(tx[0] - 0.060, tx[1], tx[2], tx[0] - 0.060, tx[1] + dy, tx[2], 'rxres', rotate90origin=rotate90origin)
        rx(tx[0] - 0.060, tx[1], tx[2], identifier='rxbowtie', to_save=[output], polarisation='y', dxdy=(resolution, resolution), rotate90origin=rotate90origin)
Beispiel #8
0
def calculate_antenna_params(filename,
                             tltxnumber=1,
                             tlrxnumber=None,
                             rxnumber=None,
                             rxcomponent=None):
    """Calculates antenna parameters - incident, reflected and total volatges and currents; s11, (s21) and input impedance.
            
    Args:
        filename (string): Filename (including path) of output file.
        tltxnumber (int): Transmitter antenna - transmission line number
        tlrxnumber (int): Receiver antenna - transmission line number
        rxnumber (int): Receiver antenna - output number
        rxcomponent (str): Receiver antenna - output electric field component
        
    Returns:
        antennaparams (dict): Antenna parameters.
    """

    # Open output file and read some attributes
    f = h5py.File(filename, 'r')
    dxdydz = f.attrs['dx, dy, dz']
    dt = f.attrs['dt']
    iterations = f.attrs['Iterations']

    # Calculate time array and frequency bin spacing
    time = np.linspace(0, 1, iterations)
    time *= (iterations * dt)
    df = 1 / np.amax(time)

    print('Time window: {:g} s ({} iterations)'.format(np.amax(time),
                                                       iterations))
    print('Time step: {:g} s'.format(dt))
    print('Frequency bin spacing: {:g} Hz'.format(df))

    # Read/calculate voltages and currents from transmitter antenna
    tltxpath = '/tls/tl' + str(tltxnumber) + '/'

    # Incident voltages/currents
    Vinc = f[tltxpath + 'Vinc'][:]
    Iinc = f[tltxpath + 'Iinc'][:]

    # Total (incident + reflected) voltages/currents
    Vtotal = f[tltxpath + 'Vtotal'][:]
    Itotal = f[tltxpath + 'Itotal'][:]

    # Reflected voltages/currents
    Vref = Vtotal - Vinc
    Iref = Itotal - Iinc

    # If a receiver antenna is used (with a transmission line or receiver), get received voltage for s21
    if tlrxnumber:
        tlrxpath = '/tls/tl' + str(tlrxnumber) + '/'
        Vrec = f[tlrxpath + 'Vtotal'][:]

    elif rxnumber:
        rxpath = '/rxs/rx' + str(rxnumber) + '/'
        availableoutputs = list(f[rxpath].keys())

        if rxcomponent not in availableoutputs:
            raise CmdInputError(
                '{} output requested, but the available output for receiver {} is {}'
                .format(rxcomponent, rxnumber, ', '.join(availableoutputs)))

        rxpath += rxcomponent

        # Received voltage
        if rxcomponent == 'Ex':
            Vrec = f[rxpath][:] * -1 * dxdydz[0]
        elif rxcomponent == 'Ey':
            Vrec = f[rxpath][:] * -1 * dxdydz[1]
        elif rxcomponent == 'Ez':
            Vrec = f[rxpath][:] * -1 * dxdydz[2]
    f.close()

    # Frequency bins
    freqs = np.fft.fftfreq(Vinc.size, d=dt)

    # Delay correction - current lags voltage, so delay voltage to match current timestep
    delaycorrection = np.exp(1j * 2 * np.pi * freqs * (dt / 2))

    # Calculate s11 and (optionally) s21
    s11 = np.abs(np.fft.fft(Vref) / np.fft.fft(Vinc))
    if tlrxnumber or rxnumber:
        s21 = np.abs(np.fft.fft(Vrec) / np.fft.fft(Vinc))

    # Calculate input impedance
    zin = (np.fft.fft(Vtotal) * delaycorrection) / np.fft.fft(Itotal)

    # Calculate input admittance
    yin = np.fft.fft(Itotal) / (np.fft.fft(Vtotal) * delaycorrection)

    # Convert to decibels
    Vincp = 20 * np.log10(np.abs((np.fft.fft(Vinc) * delaycorrection)))
    Iincp = 20 * np.log10(np.abs(np.fft.fft(Iinc)))
    Vrefp = 20 * np.log10(np.abs((np.fft.fft(Vref) * delaycorrection)))
    Irefp = 20 * np.log10(np.abs(np.fft.fft(Iref)))
    Vtotalp = 20 * np.log10(np.abs((np.fft.fft(Vtotal) * delaycorrection)))
    Itotalp = 20 * np.log10(np.abs(np.fft.fft(Itotal)))
    s11 = 20 * np.log10(s11)

    # Create dictionary of antenna parameters
    antennaparams = {
        'time': time,
        'freqs': freqs,
        'Vinc': Vinc,
        'Vincp': Vincp,
        'Iinc': Iinc,
        'Iincp': Iincp,
        'Vref': Vref,
        'Vrefp': Vrefp,
        'Iref': Iref,
        'Irefp': Irefp,
        'Vtotal': Vtotal,
        'Vtotalp': Vtotalp,
        'Itotal': Itotal,
        'Itotalp': Itotalp,
        's11': s11,
        'zin': zin,
        'yin': yin
    }
    if tlrxnumber or rxnumber:
        s21 = 20 * np.log10(s21)
        antennaparams['s21'] = s21

    return antennaparams
Beispiel #9
0
    usage='cd gprMax; python -m tools.plot_hdf5_Bscan outputfile field')
parser.add_argument('outputfile', help='name of output file including path')
parser.add_argument('field',
                    help='name of field to be plotted, i.e. Ex, Ey, Ez')
args = parser.parse_args()

file = args.outputfile
field = args.field
path = '/rxs/rx1'

f = h5py.File(file, 'r')
data = f[path + '/' + field]

# Check that there is more than one A-scan present
if data.shape[1] == 1:
    raise CmdInputError('{} contains only a single A-scan.'.format(file))

# Plot B-scan image
fig = plt.figure(num=file, figsize=(20, 10), facecolor='w', edgecolor='w')
plt.imshow(data,
           extent=[0, data.shape[1], data.shape[0] * f.attrs['dt'], 0],
           interpolation='nearest',
           aspect='auto',
           cmap='seismic',
           vmin=-np.amax(np.abs(data)),
           vmax=np.amax(np.abs(data)))
plt.xlabel('Trace number')
plt.ylabel('Time [s]')
plt.grid()
cb = plt.colorbar()
cb.set_label('Field strength [V/m]')
Beispiel #10
0
def run_opt_sim(args, numbermodelruns, inputfile, usernamespace):
    """Run a simulation using Taguchi's optmisation process.
        
    Args:
        args (dict): Namespace with command line arguments
        numbermodelruns (int): Total number of model runs.
        inputfile (str): Name of the input file to open.
        usernamespace (dict): Namespace that can be accessed by user in any Python code blocks in input file.
    """

    if numbermodelruns > 1:
        raise CmdInputError(
            'When a Taguchi optimisation is being carried out the number of model runs argument is not required'
        )

    inputfileparts = os.path.splitext(inputfile)

    # Default maximum number of iterations of optimisation to perform (used if the stopping criterion is not achieved)
    maxiterations = 20

    # Process Taguchi code blocks in the input file; pass in ordered dictionary to hold parameters to optimise
    tmp = usernamespace.copy()
    tmp.update({'optparams': OrderedDict()})
    taguchinamespace = taguchi_code_blocks(inputfile, tmp)

    # Extract dictionaries and variables containing initialisation parameters
    optparams = taguchinamespace['optparams']
    fitness = taguchinamespace['fitness']
    if 'maxiterations' in taguchinamespace:
        maxiterations = taguchinamespace['maxiterations']

    # Store initial parameter ranges
    optparamsinit = list(optparams.items())

    # Dictionary to hold history of optmised values of parameters
    optparamshist = OrderedDict((key, list()) for key in optparams)

    # Import specified fitness function
    fitness_metric = getattr(
        importlib.import_module('user_libs.optimisation_taguchi_fitness'),
        fitness['name'])

    # Select OA
    OA, N, cols, k, s, t = construct_OA(optparams)
    print(
        '\n{}\n\nTaguchi optimisation: orthogonal array with {} experiments, {} parameters ({} used), {} levels, and strength {} will be used.'
        .format(68 * '*', N, cols, k, s, t))

    # Initialise arrays and lists to store parameters required throughout optimisation
    # Lower, central, and upper values for each parameter
    levels = np.zeros((s, k), dtype=floattype)
    # Optimal lower, central, or upper value for each parameter
    levelsopt = np.zeros(k, dtype=floattype)
    # Difference used to set values for levels
    levelsdiff = np.zeros(k, dtype=floattype)
    # History of fitness values from each confirmation experiment
    fitnessvalueshist = []

    iteration = 0
    while iteration < maxiterations:
        # Reset number of model runs to number of experiments
        numbermodelruns = N
        usernamespace['number_model_runs'] = numbermodelruns

        # Fitness values for each experiment
        fitnessvalues = []

        # Set parameter ranges and define experiments
        optparams, levels, levelsdiff = calculate_ranges_experiments(
            optparams, optparamsinit, levels, levelsopt, levelsdiff, OA, N, k,
            s, iteration)

        # Run model for each experiment
        if args.mpi:  # Mixed mode MPI/OpenMP - MPI task farm for models with each model parallelised with OpenMP
            run_mpi_sim(args, numbermodelruns, inputfile, usernamespace,
                        optparams)
        else:  # Standard behaviour - models run serially with each model parallelised with OpenMP
            run_std_sim(args, numbermodelruns, inputfile, usernamespace,
                        optparams)

        # Calculate fitness value for each experiment
        for experiment in range(1, numbermodelruns + 1):
            outputfile = inputfileparts[0] + str(experiment) + '.out'
            fitnessvalues.append(fitness_metric(outputfile, fitness['args']))
            os.remove(outputfile)

        print(
            '\nTaguchi optimisation, iteration {}: {} initial experiments with fitness values {}.'
            .format(iteration + 1, numbermodelruns, fitnessvalues))

        # Calculate optimal levels from fitness values by building a response table; update dictionary of parameters with optimal values
        optparams, levelsopt = calculate_optimal_levels(
            optparams, levels, levelsopt, fitnessvalues, OA, N, k)

        # Run a confirmation experiment with optimal values
        numbermodelruns = 1
        usernamespace['number_model_runs'] = numbermodelruns
        run_std_sim(args, numbermodelruns, inputfile, usernamespace, optparams)

        # Calculate fitness value for confirmation experiment
        outputfile = inputfileparts[0] + '.out'
        fitnessvalueshist.append(fitness_metric(outputfile, fitness['args']))

        # Rename confirmation experiment output file so that it is retained for each iteraction
        os.rename(
            outputfile,
            os.path.splitext(outputfile)[0] + '_final' + str(iteration + 1) +
            '.out')

        print(
            '\nTaguchi optimisation, iteration {} completed. History of optimal parameter values {} and of fitness values {}'
            .format(iteration + 1, dict(optparamshist), fitnessvalueshist,
                    68 * '*'))
        iteration += 1

        # Stop optimisation if stopping criterion has been reached
        if fitnessvalueshist[iteration - 1] > fitness['stop']:
            print('\nTaguchi optimisation stopped as fitness criteria reached')
            break

        # Stop optimisation if successive fitness values are within a percentage threshold
        if iteration > 2:
            fitnessvaluesclose = (np.abs(fitnessvalueshist[iteration - 2] -
                                         fitnessvalueshist[iteration - 1]) /
                                  fitnessvalueshist[iteration - 1]) * 100
            fitnessvaluesthres = 0.1
            if fitnessvaluesclose < fitnessvaluesthres:
                print(
                    '\nTaguchi optimisation stopped as successive fitness values within {}%'
                    .format(fitnessvaluesthres))
                break

    # Save optimisation parameters history and fitness values history to file
    opthistfile = inputfileparts[0] + '_hist'
    np.savez(opthistfile, dict(optparamshist), fitnessvalueshist)

    print(
        '\n{}\nTaguchi optimisation completed after {} iteration(s).\nHistory of optimal parameter values {} and of fitness values {}\n{}\n'
        .format(68 * '*', iteration, dict(optparamshist), fitnessvalueshist,
                68 * '*'))

    # Plot the history of fitness values and each optimised parameter values for the optimisation
    plot_optimisation_history(fitnessvalueshist, optparamshist, optparamsinit)
def process_multicmds(multicmds, G):
    """Checks the validity of command parameters and creates instances of classes of parameters.
        
    Args:
        multicmds (dict): Commands that can have multiple instances in the model.
        G (class): Grid class instance - holds essential parameters describing the model.
    """

    # Waveform definitions
    cmdname = '#waveform'
    if multicmds[cmdname] != 'None':
        for cmdinstance in multicmds[cmdname]:
            tmp = cmdinstance.split()
            if len(tmp) != 4:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly four parameters')
            if tmp[0].lower() not in Waveform.types:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' must have one of the following types {}'.format(','.join(Waveform.types)))
            if float(tmp[2]) <= 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires an excitation frequency value of greater than zero')
            if any(x.ID == tmp[3] for x in G.waveforms):
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' with ID {} already exists'.format(tmp[3]))
            
            w = Waveform()
            w.ID = tmp[3]
            w.type = tmp[0].lower()
            w.amp = float(tmp[1])
            w.freq = float(tmp[2])
            
            if G.messages:
                print('Waveform {} of type {} with amplitude {:g}, frequency {:g}Hz created.'.format(w.ID, w.type, w.amp, w.freq))
            
            G.waveforms.append(w)


    # Voltage source
    cmdname = '#voltage_source'
    if multicmds[cmdname] != 'None':
        for cmdinstance in multicmds[cmdname]:
            tmp = cmdinstance.split()
            if len(tmp) < 6:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least six parameters')
            
            # Check polarity & position parameters
            if tmp[0].lower() not in ('x', 'y', 'z'):
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' polarisation must be x, y, or z')
            xcoord = round_value(float(tmp[1])/G.dx)
            ycoord = round_value(float(tmp[2])/G.dy)
            zcoord = round_value(float(tmp[3])/G.dz)
            resistance = float(tmp[4])
            if xcoord < 0 or xcoord >= G.nx:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' x-coordinate is not within the model domain')
            if ycoord < 0 or ycoord >= G.ny:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' y-coordinate is not within the model domain')
            if zcoord < 0 or zcoord >= G.nz:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' z-coordinate is not within the model domain')
            if xcoord < G.pmlthickness[0] or xcoord > G.nx - G.pmlthickness[3] or ycoord < G.pmlthickness[1] or ycoord > G.ny - G.pmlthickness[4] or zcoord < G.pmlthickness[2] or zcoord > G.nz - G.pmlthickness[5]:
                print("WARNING: '" + cmdname + ': ' + ' '.join(tmp) + "'" + ' sources and receivers should not normally be positioned within the PML.')
            if resistance < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a source resistance of zero or greater')
                    
            # Check if there is a waveformID in the waveforms list
            if not any(x.ID == tmp[5] for x in G.waveforms):
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' there is no waveform with the identifier {}'.format(tmp[5]))
            
            v = VoltageSource()
            v.polarisation= tmp[0]
            v.xcoord = xcoord
            v.ycoord = ycoord
            v.zcoord = zcoord
            v.resistance = resistance
            v.waveformID = tmp[5]
            
            if len(tmp) > 6:
                # Check source start & source remove time parameters
                start = float(tmp[6])
                stop = float(tmp[7])
                if start < 0:
                    raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' delay of the initiation of the source should not be less than zero')
                if stop < 0:
                    raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' time to remove the source should not be less than zero')
                if stop - start <= 0:
                    raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' duration of the source should not be zero or less')
                v.start = start
                if stop > G.timewindow:
                    v.stop = G.timewindow
                startstop = ' start time {:g} secs, finish time {:g} secs '.format(v.start, v.stop)
            else:
                v.start = 0
                v.stop = G.timewindow
                startstop = ' '
            
            if G.messages:
                print('Voltage source with polarity {} at {:g}m, {:g}m, {:g}m, resistance {:.1f} Ohms,'.format(v.polarisation, v.xcoord * G.dx, v.ycoord * G.dy, v.zcoord * G.dz, v.resistance) + startstop + 'using waveform {} created.'.format(v.waveformID))
            
            G.voltagesources.append(v)


    # Hertzian dipole
    cmdname = '#hertzian_dipole'
    if multicmds[cmdname] != 'None':
        for cmdinstance in multicmds[cmdname]:
            tmp = cmdinstance.split()
            if len(tmp) < 5:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
            
            # Check polarity & position parameters
            if tmp[0].lower() not in ('x', 'y', 'z'):
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' polarisation must be x, y, or z')
            xcoord = round_value(float(tmp[1])/G.dx)
            ycoord = round_value(float(tmp[2])/G.dy)
            zcoord = round_value(float(tmp[3])/G.dz)
            if xcoord < 0 or xcoord >= G.nx:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' x-coordinate is not within the model domain')
            if ycoord < 0 or ycoord >= G.ny:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' y-coordinate is not within the model domain')
            if zcoord < 0 or zcoord >= G.nz:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' z-coordinate is not within the model domain')
            if xcoord < G.pmlthickness[0] or xcoord > G.nx - G.pmlthickness[3] or ycoord < G.pmlthickness[1] or ycoord > G.ny - G.pmlthickness[4] or zcoord < G.pmlthickness[2] or zcoord > G.nz - G.pmlthickness[5]:
                print("WARNING: '" + cmdname + ': ' + ' '.join(tmp) + "'" + ' sources and receivers should not normally be positioned within the PML.')
                    
            # Check if there is a waveformID in the waveforms list
            if not any(x.ID == tmp[4] for x in G.waveforms):
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' there is no waveform with the identifier {}'.format(tmp[4]))
            
            h = HertzianDipole()
            h.polarisation = tmp[0]
            h.xcoord = xcoord
            h.ycoord = ycoord
            h.zcoord = zcoord
            h.waveformID = tmp[4]
            
            if len(tmp) > 5:
                # Check source start & source remove time parameters
                start = float(tmp[5])
                stop = float(tmp[6])
                if start < 0:
                    raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' delay of the initiation of the source should not be less than zero')
                if stop < 0:
                    raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' time to remove the source should not be less than zero')
                if stop - start <= 0:
                    raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' duration of the source should not be zero or less')
                h.start = start
                if stop > G.timewindow:
                    h.stop = G.timewindow
                startstop = ' start time {:g} secs, finish time {:g} secs '.format(h.start, h.stop)
            else:
                h.start = 0
                h.stop = G.timewindow
                startstop = ' '
            
            if G.messages:
                print('Hertzian dipole with polarity {} at {:g}m, {:g}m, {:g}m,'.format(h.polarisation, h.xcoord * G.dx, h.ycoord * G.dy, h.zcoord * G.dz) + startstop + 'using waveform {} created.'.format(h.waveformID))
            
            G.hertziandipoles.append(h)


    # Magnetic dipole
    cmdname = '#magnetic_dipole'
    if multicmds[cmdname] != 'None':
        for cmdinstance in multicmds[cmdname]:
            tmp = cmdinstance.split()
            if len(tmp) < 5:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
            
            # Check polarity & position parameters
            if tmp[0].lower() not in ('x', 'y', 'z'):
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' polarisation must be x, y, or z')
            xcoord = round_value(float(tmp[1])/G.dx)
            ycoord = round_value(float(tmp[2])/G.dy)
            zcoord = round_value(float(tmp[3])/G.dz)
            if xcoord < 0 or xcoord >= G.nx:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' x-coordinate is not within the model domain')
            if ycoord < 0 or ycoord >= G.ny:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' y-coordinate is not within the model domain')
            if zcoord < 0 or zcoord >= G.nz:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' z-coordinate is not within the model domain')
            if xcoord < G.pmlthickness[0] or xcoord > G.nx - G.pmlthickness[3] or ycoord < G.pmlthickness[1] or ycoord > G.ny - G.pmlthickness[4] or zcoord < G.pmlthickness[2] or zcoord > G.nz - G.pmlthickness[5]:
                print("WARNING: '" + cmdname + ': ' + ' '.join(tmp) + "'" + ' sources and receivers should not normally be positioned within the PML.')
                    
            # Check if there is a waveformID in the waveforms list
            if not any(x.ID == tmp[4] for x in G.waveforms):
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' there is no waveform with the identifier {}'.format(tmp[4]))
            
            m = MagneticDipole()
            m.polarisation = tmp[0]
            m.xcoord = xcoord
            m.ycoord = ycoord
            m.zcoord = zcoord
            m.waveformID = tmp[4]
            
            if len(tmp) > 5:
                # Check source start & source remove time parameters
                start = float(tmp[5])
                stop = float(tmp[6])
                if start < 0:
                    raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' delay of the initiation of the source should not be less than zero')
                if stop < 0:
                    raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' time to remove the source should not be less than zero')
                if stop - start <= 0:
                    raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' duration of the source should not be zero or less')
                m.start = start
                if stop > G.timewindow:
                    m.stop = G.timewindow
                startstop = ' start time {:g} secs, finish time {:g} secs '.format(m.start, m.stop)
            else:
                m.start = 0
                m.stop = G.timewindow
                startstop = ' '
            
            if G.messages:
                print('Magnetic dipole with polarity {} at {:g}m, {:g}m, {:g}m,'.format(m.polarisation, m.xcoord * G.dx, m.ycoord * G.dy, m.zcoord * G.dz) + startstop + 'using waveform {} created.'.format(m.waveformID))
            
            G.magneticdipoles.append(m)


    # Transmission line
    cmdname = '#transmission_line'
    if multicmds[cmdname] != 'None':
        for cmdinstance in multicmds[cmdname]:
            tmp = cmdinstance.split()
            if len(tmp) < 6:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least six parameters')
            
            # Check polarity & position parameters
            if tmp[0].lower() not in ('x', 'y', 'z'):
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' polarisation must be x, y, or z')
            xcoord = round_value(float(tmp[1])/G.dx)
            ycoord = round_value(float(tmp[2])/G.dy)
            zcoord = round_value(float(tmp[3])/G.dz)
            resistance = float(tmp[4])
            if xcoord < 0 or xcoord >= G.nx:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' x-coordinate is not within the model domain')
            if ycoord < 0 or ycoord >= G.ny:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' y-coordinate is not within the model domain')
            if zcoord < 0 or zcoord >= G.nz:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' z-coordinate is not within the model domain')
            if xcoord < G.pmlthickness[0] or xcoord > G.nx - G.pmlthickness[3] or ycoord < G.pmlthickness[1] or ycoord > G.ny - G.pmlthickness[4] or zcoord < G.pmlthickness[2] or zcoord > G.nz - G.pmlthickness[5]:
                print("WARNING: '" + cmdname + ': ' + ' '.join(tmp) + "'" + ' sources and receivers should not normally be positioned within the PML.')
            if resistance <= 0 or resistance > z0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a resistance greater than zero and less than the impedance of free space, i.e. 376.73 Ohms')
                    
            # Check if there is a waveformID in the waveforms list
            if not any(x.ID == tmp[5] for x in G.waveforms):
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' there is no waveform with the identifier {}'.format(tmp[4]))
            
            t = TransmissionLine(G)
            t.polarisation = tmp[0]
            t.xcoord = xcoord
            t.ycoord = ycoord
            t.zcoord = zcoord
            t.resistance = resistance
            t.waveformID = tmp[5]
            t.calculate_incident_V_I(G)
            
            if len(tmp) > 6:
                # Check source start & source remove time parameters
                start = float(tmp[6])
                stop = float(tmp[7])
                if start < 0:
                    raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' delay of the initiation of the source should not be less than zero')
                if stop < 0:
                    raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' time to remove the source should not be less than zero')
                if stop - start <= 0:
                    raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' duration of the source should not be zero or less')
                t.start = start
                if stop > G.timewindow:
                    t.stop = G.timewindow
                startstop = ' start time {:g} secs, finish time {:g} secs '.format(t.start, t.stop)
            else:
                t.start = 0
                t.stop = G.timewindow
                startstop = ' '
            
            if G.messages:
                print('Transmission line with polarity {} at {:g}m, {:g}m, {:g}m, resistance {:.1f} Ohms,'.format(t.polarisation, t.xcoord * G.dx, t.ycoord * G.dy, t.zcoord * G.dz, t.resistance) + startstop + 'using waveform {} created.'.format(t.waveformID))
            
            G.transmissionlines.append(t)


    # Receiver
    cmdname = '#rx'
    if multicmds[cmdname] != 'None':
        for cmdinstance in multicmds[cmdname]:
            tmp = cmdinstance.split()
            if len(tmp) != 3 and len(tmp) < 5:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' has an incorrect number of parameters')

            # Check position parameters
            xcoord = round_value(float(tmp[0])/G.dx)
            ycoord = round_value(float(tmp[1])/G.dy)
            zcoord = round_value(float(tmp[2])/G.dz)
            if xcoord < 0 or xcoord >= G.nx:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' x-coordinate is not within the model domain')
            if ycoord < 0 or ycoord >= G.ny:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' y-coordinate is not within the model domain')
            if zcoord < 0 or zcoord >= G.nz:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' z-coordinate is not within the model domain')
            if xcoord < G.pmlthickness[0] or xcoord > G.nx - G.pmlthickness[3] or ycoord < G.pmlthickness[1] or ycoord > G.ny - G.pmlthickness[4] or zcoord < G.pmlthickness[2] or zcoord > G.nz - G.pmlthickness[5]:
                print("WARNING: '" + cmdname + ': ' + ' '.join(tmp) + "'" + ' sources and receivers should not normally be positioned within the PML.')
            
            r = Rx(xcoord=xcoord, ycoord=ycoord, zcoord=zcoord)
            
            # If no ID or outputs are specified, use default i.e Ex, Ey, Ez, Hx, Hy, Hz, Ix, Iy, Iz
            if len(tmp) == 3:
                r.outputs = Rx.availableoutputs[0:9]
            else:
                r.ID = tmp[3]
                # Check and add field output names
                for field in tmp[4::]:
                    if field in Rx.availableoutputs:
                        r.outputs.append(field)
                    else:
                        raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' contains an output type that is not available')
            
            if G.messages:
                print('Receiver at {:g}m, {:g}m, {:g}m with output(s) {} created.'.format(r.xcoord * G.dx, r.ycoord * G.dy, r.zcoord * G.dz, ', '.join(r.outputs)))
            
            G.rxs.append(r)


    # Receiver box
    cmdname = '#rx_box'
    if multicmds[cmdname] != 'None':
        for cmdinstance in multicmds[cmdname]:
            tmp = cmdinstance.split()
            if len(tmp) != 9:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly nine parameters')

            xs = round_value(float(tmp[0])/G.dx)
            xf = round_value(float(tmp[3])/G.dx)
            ys = round_value(float(tmp[1])/G.dy)
            yf = round_value(float(tmp[4])/G.dy)
            zs = round_value(float(tmp[2])/G.dz)
            zf = round_value(float(tmp[5])/G.dz)
            dx = round_value(float(tmp[6])/G.dx)
            dy = round_value(float(tmp[7])/G.dy)
            dz = round_value(float(tmp[8])/G.dz)

            if xs < 0 or xs >= G.nx:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower x-coordinate {:g}m is not within the model domain'.format(xs))
            if xf < 0 or xf >= G.nx:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the upper x-coordinate {:g}m is not within the model domain'.format(xf))
            if ys < 0 or ys >= G.ny:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower y-coordinate {:g}m is not within the model domain'.format(ys))
            if yf < 0 or yf >= G.ny:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the upper y-coordinate {:g}m is not within the model domain'.format(yf))
            if zs < 0 or zs >= G.nz:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower z-coordinate {:g}m is not within the model domain'.format(zs))
            if zf < 0 or zf >= G.nz:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the upper z-coordinate {:g}m is not within the model domain'.format(zf))
            if xcoord < G.pmlthickness[0] or xcoord > G.nx - G.pmlthickness[3] or ycoord < G.pmlthickness[1] or ycoord > G.ny - G.pmlthickness[4] or zcoord < G.pmlthickness[2] or zcoord > G.nz - G.pmlthickness[5]:
                print("WARNING: '" + cmdname + ': ' + ' '.join(tmp) + "'" + ' sources and receivers should not normally be positioned within the PML.')
            if xs >= xf or ys >= yf or zs >= zf:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower coordinates should be less than the upper coordinates')
            if dx < 0 or dy < 0 or dz < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should not be less than zero')
            if dx < G.dx or dy < G.dy or dz < G.dz:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should not be less than the spatial discretisation')

            for x in range(xs, xf, dx):
                for y in range(ys, yf, dy):
                    for z in range(zs, zf, dz):
                        r = Rx(xcoord=x, ycoord=y, zcoord=z)
                        G.rxs.append(r)

            if G.messages:
                print('Receiver box {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m with steps {:g}m, {:g}m, {:g} created.'.format(xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, dx * G.dx, dy * G.dy, dz * G.dz))
                    
                    
    # Snapshot
    cmdname = '#snapshot'
    if multicmds[cmdname] != 'None':
        for cmdinstance in multicmds[cmdname]:
            tmp = cmdinstance.split()
            if len(tmp) != 11:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly eleven parameters')

            xs = round_value(float(tmp[0])/G.dx)
            xf = round_value(float(tmp[3])/G.dx)
            ys = round_value(float(tmp[1])/G.dy)
            yf = round_value(float(tmp[4])/G.dy)
            zs = round_value(float(tmp[2])/G.dz)
            zf = round_value(float(tmp[5])/G.dz)
            dx = round_value(float(tmp[6])/G.dx)
            dy = round_value(float(tmp[7])/G.dy)
            dz = round_value(float(tmp[8])/G.dz)
            
            # If real floating point value given
            if '.' in tmp[9] or 'e' in tmp[9]:
                if float(tmp[9]) > 0:
                    time = round_value((float(tmp[9]) / G.dt)) + 1
                else:
                    raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' time value must be greater than zero')
            # If number of iterations given
            else:
                time = int(tmp[9])
            
            if xs < 0 or xs > G.nx:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower x-coordinate {:g}m is not within the model domain'.format(xs * G.dx))
            if xf < 0 or xf > G.nx:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the upper x-coordinate {:g}m is not within the model domain'.format(xf * G.dx))
            if ys < 0 or ys > G.ny:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower y-coordinate {:g}m is not within the model domain'.format(ys * G.dy))
            if yf < 0 or yf > G.ny:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the upper y-coordinate {:g}m is not within the model domain'.format(yf * G.dy))
            if zs < 0 or zs > G.nz:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower z-coordinate {:g}m is not within the model domain'.format(zs * G.dz))
            if zf < 0 or zf > G.nz:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the upper z-coordinate {:g}m is not within the model domain'.format(zf * G.dz))
            if xs >= xf or ys >= yf or zs >= zf:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower coordinates should be less than the upper coordinates')
            if dx < 0 or dy < 0 or dz < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should not be less than zero')
            if dx < G.dx or dy < G.dy or dz < G.dz:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should not be less than the spatial discretisation')
            if time <= 0 or time > G.iterations:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' time value is not valid')
                    
            s = Snapshot(xs, ys, zs, xf, yf, zf, dx, dy, dz, time, tmp[10])

            if G.messages:
                print('Snapshot from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m, discretisation {:g}m, {:g}m, {:g}m, at {:g} secs with filename {} created.'.format(xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, dx * G.dx, dx * G.dy, dx * G.dz, s.time * G.dt, s.filename))
                    
            G.snapshots.append(s)


    # Materials
    # Create built-in materials
    m = Material(0, 'pec', G)
    m.average = False
    G.materials.append(m)
    
    m = Material(1, 'free_space', G)
    m.average = True
    G.materials.append(m)

    cmdname = '#material'
    if multicmds[cmdname] != 'None':
        for cmdinstance in multicmds[cmdname]:
            tmp = cmdinstance.split()
            if len(tmp) != 5:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly five parameters')
            if float(tmp[0]) < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for static (DC) permittivity')
            if float(tmp[1]) < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for conductivity')
            if float(tmp[2]) < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for permeability')
            if float(tmp[3]) < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for magnetic conductivity')
            if any(x.ID == tmp[4] for x in G.materials):
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' with ID {} already exists'.format(tmp[4]))
            
            # Create a new instance of the Material class material (start index after pec & free_space)
            m = Material(len(G.materials), tmp[4], G)
            m.er = float(tmp[0])
            m.se = float(tmp[1])
            m.mr = float(tmp[2])
            m.sm = float(tmp[3])
            
            if G.messages:
                print('Material {} with epsr={:g}, sig={:g} S/m; mur={:g}, sig*={:g} S/m created.'.format(m.ID, m.er, m.se, m.mr, m.sm))
            
            # Append the new material object to the materials list
            G.materials.append(m)


    cmdname = '#add_dispersion_debye'
    if multicmds[cmdname] != 'None':
        for cmdinstance in multicmds[cmdname]:
            tmp = cmdinstance.split()

            if len(tmp) < 4:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least four parameters')
            if int(tmp[0]) < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for number of poles')
            poles = int(tmp[0])
            materialsrequested = tmp[(2 * poles) + 1:len(tmp)]

            # Look up requested materials in existing list of material instances
            materials = [y for x in materialsrequested for y in G.materials if y.ID == x]

            if len(materials) != len(materialsrequested):
                notfound = [x for x in materialsrequested if x not in materials]
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' material(s) {} do not exist'.format(notfound))
            
            for material in materials:
                material.type = 'debye'
                material.poles = poles
                material.average = False
                for pole in range(1, 2 * poles, 2):
                    if float(tmp[pole]) > 0 and float(tmp[pole + 1]) > G.dt:
                        material.deltaer.append(float(tmp[pole]))
                        material.tau.append(float(tmp[pole + 1]))
                    else:
                        raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires positive values for the permittivity difference and relaxation times, and relaxation times that are greater than the time step for the model.')
                if material.poles > Material.maxpoles:
                    Material.maxpoles = material.poles

                if G.messages:
                    print('Debye-type disperion added to {} with delta_epsr={}, and tau={} secs created.'.format(material.ID, ','.join('%4.2f' % deltaer for deltaer in material.deltaer), ','.join('%4.3e' % tau for tau in material.tau)))

    cmdname = '#add_dispersion_lorentz'
    if multicmds[cmdname] != 'None':
        for cmdinstance in multicmds[cmdname]:
            tmp = cmdinstance.split()

            if len(tmp) < 5:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
            if int(tmp[0]) < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for number of poles')
            poles = int(tmp[0])
            materialsrequested = tmp[(3 * poles) + 1:len(tmp)]

            # Look up requested materials in existing list of material instances
            materials = [y for x in materialsrequested for y in G.materials if y.ID == x]

            if len(materials) != len(materialsrequested):
                notfound = [x for x in materialsrequested if x not in materials]
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' material(s) {} do not exist'.format(notfound))
            
            for material in materials:
                material.type = 'lorentz'
                material.poles = poles
                material.average = False
                for pole in range(1, 3 * poles, 3):
                    if float(tmp[pole]) > 0 and float(tmp[pole + 1]) > G.dt and float(tmp[pole + 2]) > G.dt:
                        material.deltaer.append(float(tmp[pole]))
                        material.tau.append(float(tmp[pole + 1]))
                        material.alpha.append(float(tmp[pole + 2]))
                    else:
                        raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires positive values for the permittivity difference and frequencies, and associated times that are greater than the time step for the model.')
                if material.poles > Material.maxpoles:
                    Material.maxpoles = material.poles

                if G.messages:
                    print('Lorentz-type disperion added to {} with delta_epsr={}, omega={} secs, and gamma={} created.'.format(material.ID, ','.join('%4.2f' % deltaer for deltaer in material.deltaer), ','.join('%4.3e' % tau for tau in material.tau), ','.join('%4.3e' % alpha for alpha in material.alpha)))


    cmdname = '#add_dispersion_drude'
    if multicmds[cmdname] != 'None':
        for cmdinstance in multicmds[cmdname]:
            tmp = cmdinstance.split()

            if len(tmp) < 5:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
            if int(tmp[0]) < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for number of poles')
            poles = int(tmp[0])
            materialsrequested = tmp[(3 * poles) + 1:len(tmp)]

            # Look up requested materials in existing list of material instances
            materials = [y for x in materialsrequested for y in G.materials if y.ID == x]

            if len(materials) != len(materialsrequested):
                notfound = [x for x in materialsrequested if x not in materials]
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' material(s) {} do not exist'.format(notfound))
            
            for material in materials:
                material.type = 'drude'
                material.poles = poles
                material.average = False
                for pole in range(1, 2 * poles, 2):
                    if float(tmp[pole]) > 0 and float(tmp[pole + 1]) > G.dt:
                        material.tau.append(float(tmp[pole ]))
                        material.alpha.append(float(tmp[pole + 1]))
                    else:
                        raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires positive values for the frequencies, and associated times that are greater than the time step for the model.')
                if material.poles > Material.maxpoles:
                    Material.maxpoles = material.poles

                if G.messages:
                    print('Drude-type disperion added to {} with omega={} secs, and gamma={} secs created.'.format(material.ID, ','.join('%4.3e' % tau for tau in material.tau), ','.join('%4.3e' % alpha for alpha in material.alpha)))


    cmdname = '#soil_peplinski'
    if multicmds[cmdname] != 'None':
        for cmdinstance in multicmds[cmdname]:
            tmp = cmdinstance.split()
            if len(tmp) != 7:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at exactly seven parameters')
            if float(tmp[0]) < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for the sand fraction')
            if float(tmp[1]) < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for the clay fraction')
            if float(tmp[2]) < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for the bulk density')
            if float(tmp[3]) < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for the sand particle density')
            if float(tmp[4]) < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for the lower limit of the water volumetric fraction')
            if float(tmp[5]) < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for the upper limit of the water volumetric fraction')
            if any(x.ID == tmp[6] for x in G.mixingmodels):
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' with ID {} already exists'.format(tmp[6]))
            
            # Create a new instance of the Material class material (start index after pec & free_space)
            s = PeplinskiSoil(tmp[6], float(tmp[0]), float(tmp[1]), float(tmp[2]), float(tmp[3]), (float(tmp[4]), float(tmp[5])))
            
            if G.messages:
                print('Mixing model (Peplinski) used to create {} with sand fraction {:g}, clay fraction {:g}, bulk density {:g}g/cm3, sand particle density {:g}g/cm3, and water volumetric fraction {:g} to {:g} created.'.format(s.ID, s.S, s.C, s.rb, s.rs, s.mu[0], s.mu[1]))
            
            # Append the new material object to the materials list
            G.mixingmodels.append(s)


    # Geometry views (creates VTK-based geometry files)
    cmdname = '#geometry_view'
    if multicmds[cmdname] != 'None':
        for cmdinstance in multicmds[cmdname]:
            tmp = cmdinstance.split()
            if len(tmp) != 11:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly eleven parameters')

            xs = round_value(float(tmp[0])/G.dx)
            xf = round_value(float(tmp[3])/G.dx)
            ys = round_value(float(tmp[1])/G.dy)
            yf = round_value(float(tmp[4])/G.dy)
            zs = round_value(float(tmp[2])/G.dz)
            zf = round_value(float(tmp[5])/G.dz)
            dx = round_value(float(tmp[6])/G.dx)
            dy = round_value(float(tmp[7])/G.dy)
            dz = round_value(float(tmp[8])/G.dz)

            if xs < 0 or xs > G.nx:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower x-coordinate {:g}m is not within the model domain'.format(xs * G.dx))
            if xf < 0 or xf > G.nx:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the upper x-coordinate {:g}m is not within the model domain'.format(xf * G.dx))
            if ys < 0 or ys > G.ny:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower y-coordinate {:g}m is not within the model domain'.format(ys * G.dy))
            if yf < 0 or yf > G.ny:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the upper y-coordinate {:g}m is not within the model domain'.format(yf * G.dy))
            if zs < 0 or zs > G.nz:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower z-coordinate {:g}m is not within the model domain'.format(zs * G.dz))
            if zf < 0 or zf > G.nz:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the upper z-coordinate {:g}m is not within the model domain'.format(zf * G.dz))
            if xs >= xf or ys >= yf or zs >= zf:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower coordinates should be less than the upper coordinates')
            if dx < 0 or dy < 0 or dz < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should not be less than zero')
            if dx > G.nx or dy > G.ny or dz > G.nz:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should be less than the domain size')
            if dx < G.dx or dy < G.dy or dz < G.dz:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should not be less than the spatial discretisation')
            if tmp[10].lower() != 'n' and tmp[10].lower() != 'f':
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires type to be either n (normal) or f (fine)')
            
            g = GeometryView(xs, ys, zs, xf, yf, zf, dx, dy, dz, tmp[9], tmp[10].lower())

            if G.messages:
                print('Geometry view from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m, discretisation {:g}m, {:g}m, {:g}m, filename {} created.'.format(xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, dx * G.dx, dy * G.dy, dz * G.dz, g.filename))

            # Append the new GeometryView object to the geometry views list
            G.geometryviews.append(g)


    # Complex frequency shifted (CFS) PML parameter
    cmdname = '#pml_cfs'
    if multicmds[cmdname] != 'None':
        if len(multicmds[cmdname]) > 2:
            raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' can only be used up to two times, for up to a 2nd order PML')
        for cmdinstance in multicmds[cmdname]:
            tmp = cmdinstance.split()
            if len(tmp) != 12:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly twelve parameters')
            if tmp[0] not in CFSParameter.scalingprofiles.keys() or tmp[4] not in CFSParameter.scalingprofiles.keys() or tmp[8] not in CFSParameter.scalingprofiles.keys():
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' must have scaling type {}'.format(','.join(CFSParameter.scalingprofiles.keys())))
            if tmp[1] not in CFSParameter.scalingdirections or tmp[5] not in CFSParameter.scalingdirections or tmp[9] not in CFSParameter.scalingdirections:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' must have scaling type {}'.format(','.join(CFSParameter.scalingprofiles.keys())))
            if float(tmp[2]) < 0 or float(tmp[3]) < 0 or float(tmp[6]) < 0 or float(tmp[7]) < 0 or float(tmp[10]) < 0:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' minimum and maximum scaling values must be greater than zero')
            if float(tmp[6]) < 1:
                raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' minimum scaling value for kappa must be greater than zero')
            
            cfs = CFS()
            cfsalpha = CFSParameter()
            cfsalpha.ID = 'alpha'
            cfsalpha.scalingprofile = tmp[0]
            cfsalpha.scalingdirection = tmp[1]
            cfsalpha.min = float(tmp[2])
            cfsalpha.max = float(tmp[3])
            cfskappa = CFSParameter()
            cfskappa.ID = 'kappa'
            cfskappa.scalingprofile = tmp[4]
            cfskappa.scalingdirection = tmp[5]
            cfskappa.min = float(tmp[6])
            cfskappa.max = float(tmp[7])
            cfssigma = CFSParameter()
            cfssigma.ID = 'sigma'
            cfssigma.scalingprofile = tmp[8]
            cfssigma.scalingdirection = tmp[9]
            cfssigma.min = float(tmp[10])
            if tmp[11] == 'None':
                cfssigma.max = None
            else:
                cfssigma.max = float(tmp[11])
            cfs = CFS()
            cfs.alpha = cfsalpha
            cfs.kappa = cfskappa
            cfs.sigma = cfssigma
    
            if G.messages:
                print('PML CFS parameters: alpha (scaling: {}, scaling direction: {}, min: {:g}, max: {:g}), kappa (scaling: {}, scaling direction: {}, min: {:g}, max: {:g}), sigma (scaling: {}, scaling direction: {}, min: {:g}, max: {:g}) created.'.format(cfsalpha.scalingprofile, cfsalpha.scalingdirection, cfsalpha.min, cfsalpha.max, cfskappa.scalingprofile, cfskappa.scalingdirection, cfskappa.min, cfskappa.max, cfssigma.scalingprofile, cfssigma.scalingdirection, cfssigma.min, cfssigma.max))
            
            G.cfs.append(cfs)
def process_singlecmds(singlecmds, multicmds, G):
    """Checks the validity of command parameters and creates instances of classes of parameters.
        
    Args:
        singlecmds (dict): Commands that can only occur once in the model.
        multicmds (dict): Commands that can have multiple instances in the model (required to pass to process_materials_file function).
        G (class): Grid class instance - holds essential parameters describing the model.
    """

    # Check validity of command parameters in order needed
    # messages
    cmd = '#messages'
    if singlecmds[cmd] != 'None':
        tmp = singlecmds[cmd].split()
        if len(tmp) != 1:
            raise CmdInputError(cmd + ' requires exactly one parameter')
        if singlecmds[cmd].lower() == 'y':
            G.messages = True
        elif singlecmds[cmd].lower() == 'n':
            G.messages = False
        else:
            raise CmdInputError(cmd +
                                ' requires input values of either y or n')

    # Title
    cmd = '#title'
    if singlecmds[cmd] != 'None':
        G.title = singlecmds[cmd]
        if G.messages:
            print('Model title: {}'.format(G.title))

    # Number of processors to run on (OpenMP)
    cmd = '#num_threads'
    ompthreads = os.environ.get('OMP_NUM_THREADS')
    if singlecmds[cmd] != 'None':
        tmp = tuple(int(x) for x in singlecmds[cmd].split())
        if len(tmp) != 1:
            raise CmdInputError(
                cmd +
                ' requires exactly one parameter to specify the number of threads to use'
            )
        if tmp[0] < 1:
            raise CmdInputError(
                cmd + ' requires the value to be an integer not less than one')
        G.nthreads = tmp[0]
    elif ompthreads:
        G.nthreads = int(ompthreads)
    else:
        # Set number of threads to number of physical CPU cores, i.e. avoid hyperthreading with OpenMP
        G.nthreads = psutil.cpu_count(logical=False)
    if G.messages:
        print('Number of threads: {}'.format(G.nthreads))

    # Spatial discretisation
    cmd = '#dx_dy_dz'
    tmp = [float(x) for x in singlecmds[cmd].split()]
    if len(tmp) != 3:
        raise CmdInputError(cmd + ' requires exactly three parameters')
    if tmp[0] <= 0:
        raise CmdInputError(
            cmd +
            ' requires the x-direction spatial step to be greater than zero')
    if tmp[1] <= 0:
        raise CmdInputError(
            cmd +
            ' requires the y-direction spatial step to be greater than zero')
    if tmp[2] <= 0:
        raise CmdInputError(
            cmd +
            ' requires the z-direction spatial step to be greater than zero')
    G.dx = tmp[0]
    G.dy = tmp[1]
    G.dz = tmp[2]
    if G.messages:
        print('Spatial discretisation: {:g} x {:g} x {:g}m'.format(
            G.dx, G.dy, G.dz))

    # Domain
    cmd = '#domain'
    tmp = [float(x) for x in singlecmds[cmd].split()]
    if len(tmp) != 3:
        raise CmdInputError(cmd + ' requires exactly three parameters')
    G.nx = round_value(tmp[0] / G.dx)
    G.ny = round_value(tmp[1] / G.dy)
    G.nz = round_value(tmp[2] / G.dz)
    if G.nx == 0 or G.ny == 0 or G.nz == 0:
        raise CmdInputError(cmd +
                            ' requires at least one cell in every dimension')
    if G.messages:
        print(
            'Domain size: {:g} x {:g} x {:g}m ({:d} x {:d} x {:d} = {:g} cells)'
            .format(tmp[0], tmp[1], tmp[2], G.nx, G.ny, G.nz,
                    (G.nx * G.ny * G.nz)))
        # Guesstimate at memory usage
        mem = (((G.nx + 1) * (G.ny + 1) *
                (G.nz + 1) * 13 * np.dtype(floattype).itemsize + (G.nx + 1) *
                (G.ny + 1) * (G.nz + 1) * 18) * 1.1) + 30e6
        print('Memory (RAM) usage: ~{} required, {} available'.format(
            human_size(mem), human_size(psutil.virtual_memory().total)))

    # Time step CFL limit - use either 2D or 3D (default)
    cmd = '#time_step_limit_type'
    if singlecmds[cmd] != 'None':
        tmp = singlecmds[cmd].split()
        if len(tmp) != 1:
            raise CmdInputError(cmd + ' requires exactly one parameter')
        if singlecmds[cmd].lower() == '2d':
            if G.nx == 1:
                G.dt = 1 / (c * np.sqrt((1 / G.dy) * (1 / G.dy) + (1 / G.dz) *
                                        (1 / G.dz)))
            elif G.ny == 1:
                G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dz) *
                                        (1 / G.dz)))
            elif G.nz == 1:
                G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dy) *
                                        (1 / G.dy)))
            else:
                raise CmdInputError(
                    cmd +
                    ' 2D CFL limit can only be used when one dimension of the domain is one cell'
                )
        elif singlecmds[cmd].lower() == '3d':
            G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dy) *
                                    (1 / G.dy) + (1 / G.dz) * (1 / G.dz)))
        else:
            raise CmdInputError(cmd +
                                ' requires input values of either 2D or 3D')
    else:
        G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dy) *
                                (1 / G.dy) + (1 / G.dz) * (1 / G.dz)))

    # Round down time step to nearest float with precision one less than hardware maximum. Avoids inadvertently exceeding the CFL due to binary representation of floating point number.
    G.dt = round_value(G.dt, decimalplaces=d.getcontext().prec - 1)

    if G.messages:
        print('Time step: {:g} secs'.format(G.dt))

    # Time step stability factor
    cmd = '#time_step_stability_factor'
    if singlecmds[cmd] != 'None':
        tmp = tuple(float(x) for x in singlecmds[cmd].split())
        if len(tmp) != 1:
            raise CmdInputError(cmd + ' requires exactly one parameter')
        if tmp[0] <= 0 or tmp[0] > 1:
            raise CmdInputError(
                cmd +
                ' requires the value of the time step stability factor to be between zero and one'
            )
        G.dt = G.dt * tmp[0]
        if G.messages:
            print('Time step (modified): {:g} secs'.format(G.dt))

    # Time window
    cmd = '#time_window'
    tmp = singlecmds[cmd].split()
    if len(tmp) != 1:
        raise CmdInputError(
            cmd +
            ' requires exactly one parameter to specify the time window. Either in seconds or number of iterations.'
        )
    tmp = tmp[0].lower()
    # If real floating point value given
    if '.' in tmp or 'e' in tmp:
        if float(tmp) > 0:
            G.timewindow = float(tmp)
            G.iterations = round_value((float(tmp) / G.dt)) + 1
        else:
            raise CmdInputError(cmd + ' must have a value greater than zero')
    # If number of iterations given
    else:
        G.timewindow = (int(tmp) - 1) * G.dt
        G.iterations = int(tmp)
    if G.messages:
        print('Time window: {:g} secs ({} iterations)'.format(
            G.timewindow, G.iterations))

    # PML
    cmd = '#pml_cells'
    if singlecmds[cmd] != 'None':
        tmp = singlecmds[cmd].split()
        if len(tmp) != 1 and len(tmp) != 6:
            raise CmdInputError(cmd + ' requires either one or six parameters')
        if len(tmp) == 1:
            G.pmlthickness = (int(tmp[0]), int(tmp[0]), int(tmp[0]),
                              int(tmp[0]), int(tmp[0]), int(tmp[0]))
        else:
            G.pmlthickness = (int(tmp[0]), int(tmp[1]), int(tmp[2]),
                              int(tmp[3]), int(tmp[4]), int(tmp[5]))
    if 2 * G.pmlthickness[0] >= G.nx or 2 * G.pmlthickness[
            1] >= G.ny or 2 * G.pmlthickness[2] >= G.nz or 2 * G.pmlthickness[
                3] >= G.nx or 2 * G.pmlthickness[
                    4] >= G.ny or 2 * G.pmlthickness[5] >= G.nz:
        raise CmdInputError(cmd + ' has too many cells for the domain size')

    # src_steps
    cmd = '#src_steps'
    if singlecmds[cmd] != 'None':
        tmp = singlecmds[cmd].split()
        if len(tmp) != 3:
            raise CmdInputError(cmd + ' requires exactly three parameters')
        G.srcstepx = round_value(float(tmp[0]) / G.dx)
        G.srcstepy = round_value(float(tmp[1]) / G.dy)
        G.srcstepz = round_value(float(tmp[2]) / G.dz)
        if G.messages:
            print(
                'All sources will step {:g}m, {:g}m, {:g}m for each model run.'
                .format(G.srcstepx * G.dx, G.srcstepy * G.dy,
                        G.srcstepz * G.dz))

    # rx_steps
    cmd = '#rx_steps'
    if singlecmds[cmd] != 'None':
        tmp = singlecmds[cmd].split()
        if len(tmp) != 3:
            raise CmdInputError(cmd + ' requires exactly three parameters')
        G.rxstepx = round_value(float(tmp[0]) / G.dx)
        G.rxstepy = round_value(float(tmp[1]) / G.dy)
        G.rxstepz = round_value(float(tmp[2]) / G.dz)
        if G.messages:
            print(
                'All receivers will step {:g}m, {:g}m, {:g}m for each model run.'
                .format(G.rxstepx * G.dx, G.rxstepy * G.dy, G.rxstepz * G.dz))

    # Excitation file for user-defined source waveforms
    cmd = '#excitation_file'
    if singlecmds[cmd] != 'None':
        tmp = singlecmds[cmd].split()
        if len(tmp) != 1:
            raise CmdInputError(cmd + ' requires exactly one parameter')
        excitationfile = tmp[0]

        # See if file exists at specified path and if not try input file directory
        if not os.path.isfile(excitationfile):
            excitationfile = os.path.join(G.inputdirectory, excitationfile)

        # Get waveform names
        with open(excitationfile, 'r') as f:
            waveformIDs = f.readline().split()

        # Read all waveform values into an array
        waveformvalues = np.loadtxt(excitationfile,
                                    skiprows=1,
                                    dtype=floattype)

        for waveform in range(len(waveformIDs)):
            if any(x.ID == waveformIDs[waveform] for x in G.waveforms):
                raise CmdInputError(
                    'Waveform with ID {} already exists'.format(
                        waveformIDs[waveform]))
            w = Waveform()
            w.ID = waveformIDs[waveform]
            w.type = 'user'
            if len(waveformvalues.shape) == 1:
                w.uservalues = waveformvalues[:]
            else:
                w.uservalues = waveformvalues[:, waveform]

            if G.messages:
                print('User waveform {} created.'.format(w.ID))

            G.waveforms.append(w)
Beispiel #13
0
def antenna_like_MALA_1200(x, y, z, resolution=0.001):
    """Inserts a description of an antenna similar to the MALA 1.2GHz antenna. Can be used with 1mm (default) or 2mm spatial resolution. The external dimensions of the antenna are 184mm x 109mm x 46mm. One output point is defined between the arms of the receiever bowtie. The bowties are aligned with the y axis so the output is the y component of the electric field.
        
    Args:
        x, y, z (float): Coordinates of a location in the model to insert the antenna. Coordinates are relative to the geometric centre of the antenna in the x-y plane and the bottom of the antenna skid in the z direction.
        resolution (float): Spatial resolution for the antenna model.
    """
    
    # Antenna geometry properties
    casesize = (0.184, 0.109, 0.040)
    casethickness = 0.002
    cavitysize = (0.062, 0.062, 0.037)
    cavitythickness = 0.001
    pcbthickness = 0.002
    polypropylenethickness = 0.003;
    hdpethickness = 0.003;
    skidthickness = 0.006
    bowtieheight = 0.025
    
    excitationfreq = 0.978e9 # GHz
    sourceresistance = 1000 # Ohms
    
    x = x - (casesize[0] / 2)
    y = y - (casesize[1] / 2)
    
    # Coordinates of source excitation point in antenna
    tx = x + 0.063, y + 0.052, z + skidthickness
    
    if resolution == 0.001:
        dx = 0.001
        dy = 0.001
        dz = 0.001
    elif resolution == 0.002:
        dx = 0.002
        dy = 0.002
        dz = 0.002
        cavitysize = (0.062, 0.062, 0.036)
        cavitythickness = 0.002
        polypropylenethickness = 0.002;
        hdpethickness = 0.004;
        bowtieheight = 0.024
        tx = x + 0.062, y + 0.052, z + skidthickness
    else:
        raise CmdInputError('This antenna module can only be used with a spatial resolution of 1mm or 2mm')
    
    # SMD resistors - 3 on each Tx & Rx bowtie arm
    txres = 470 # Ohms
    txrescellupper = txres / 3 # Resistor over 3 cells
    txsigupper = ((1 / txrescellupper) * (dy / (dx * dz))) / 2 # Divide by number of parallel edges per resistor
    txrescelllower = txres / 4 # Resistor over 4 cells
    txsiglower = ((1 / txrescelllower) * (dy / (dx * dz))) / 2 # Divide by number of parallel edges per resistor
    rxres = 150 # Ohms
    rxrescellupper = rxres / 3 # Resistor over 3 cells
    rxsigupper = ((1 / rxrescellupper) * (dy / (dx * dz))) / 2 # Divide by number of parallel edges per resistor
    rxrescelllower = rxres / 4 # Resistor over 4 cells
    rxsiglower = ((1 / rxrescelllower) * (dy / (dx * dz))) / 2 # Divide by number of parallel edges per resistor
    
    # Material definitions
    print('#material: 6.49 0.252 1.0 0.0 absorber')
    print('#material: 3.0 0.0 1.0 0.0 pcb')
    print('#material: 2.35 0.0 1.0 0.0 hdpe')
    print('#material: 2.26 0.0 1.0 0.0 polypropylene')
    print('#material: 3.0 {:.3f} 1.0 0.0 txreslower'.format(txsiglower))
    print('#material: 3.0 {:.3f} 1.0 0.0 txresupper'.format(txsigupper))
    print('#material: 3.0 {:.3f} 1.0 0.0 rxreslower'.format(rxsiglower))
    print('#material: 3.0 {:.3f} 1.0 0.0 rxresupper'.format(rxsigupper))
    
    # Antenna geometry
    # Shield - metallic enclosure
    print('#box: {} {} {} {} {} {} pec'.format(x, y, z + skidthickness, x + casesize[0], y + casesize[1], z + skidthickness + casesize[2]))
    print('#box: {} {} {} {} {} {} free_space'.format(x + 0.020, y + casethickness, z + skidthickness, x + 0.100, y + casesize[1] - casethickness, z + skidthickness + casethickness))
    print('#box: {} {} {} {} {} {} free_space'.format(x + 0.100, y + casethickness, z + skidthickness, x + casesize[0] - casethickness, y + casesize[1] - casethickness, z + skidthickness + casethickness))
    
    # Absorber material
    print('#box: {} {} {} {} {} {} absorber'.format(x + 0.020, y + casethickness, z + skidthickness, x + 0.100, y + casesize[1] - casethickness, z + skidthickness + casesize[2] - casethickness))
    print('#box: {} {} {} {} {} {} absorber'.format(x + 0.100, y + casethickness, z + skidthickness, x + casesize[0] - casethickness, y + casesize[1] - casethickness, z + skidthickness + casesize[2] - casethickness))
    
    # Shield - cylindrical sections
    print('#cylinder: {} {} {} {} {} {} {} pec'.format(x + 0.055, y + casesize[1] - 0.008, z + skidthickness, x + 0.055, y + casesize[1] - 0.008, z + skidthickness + casesize[2] - casethickness, 0.008))
    print('#cylinder: {} {} {} {} {} {} {} pec'.format(x + 0.055, y + 0.008, z + skidthickness, x + 0.055, y + 0.008, z + skidthickness + casesize[2] - casethickness, 0.008))
    print('#cylinder: {} {} {} {} {} {} {} pec'.format(x + 0.147, y + casesize[1] - 0.008, z + skidthickness, x + 0.147, y + casesize[1] - 0.008, z + skidthickness + casesize[2] - casethickness, 0.008))
    print('#cylinder: {} {} {} {} {} {} {} pec'.format(x + 0.147, y + 0.008, z + skidthickness, x + 0.147, y + 0.008, z + skidthickness + casesize[2] - casethickness, 0.008))
    print('#cylinder: {} {} {} {} {} {} {} free_space'.format(x + 0.055, y + casesize[1] - 0.008, z + skidthickness, x + 0.055, y + casesize[1] - 0.008, z + skidthickness + casesize[2] - casethickness, 0.007))
    print('#cylinder: {} {} {} {} {} {} {} free_space'.format(x + 0.055, y + 0.008, z + skidthickness, x + 0.055, y + 0.008, z + skidthickness + casesize[2] - casethickness, 0.007))
    print('#cylinder: {} {} {} {} {} {} {} free_space'.format(x + 0.147, y + casesize[1] - 0.008, z + skidthickness, x + 0.147, y + casesize[1] - 0.008, z + skidthickness + casesize[2] - casethickness, 0.007))
    print('#cylinder: {} {} {} {} {} {} {} free_space'.format(x + 0.147, y + 0.008, z + skidthickness, x + 0.147, y + 0.008, z + skidthickness + casesize[2] - casethickness, 0.007))
    print('#box: {} {} {} {} {} {} free_space'.format(x + 0.054, y + casesize[1] - 0.016, z + skidthickness, x + 0.056, y + casesize[1] - 0.014, z + skidthickness + casesize[2] - casethickness))
    print('#box: {} {} {} {} {} {} free_space'.format(x + 0.054, y + 0.014, z + skidthickness, x + 0.056, y + 0.016, z + skidthickness + casesize[2] - casethickness))
    print('#box: {} {} {} {} {} {} free_space'.format(x + 0.146, y + casesize[1] - 0.016, z + skidthickness, x + 0.148, y + casesize[1] - 0.014, z + skidthickness + casesize[2] - casethickness))
    print('#box: {} {} {} {} {} {} free_space'.format(x + 0.146, y + 0.014, z + skidthickness, x + 0.148, y + 0.016, z + skidthickness + casesize[2] - casethickness))
    
    # PCB
    print('#box: {} {} {} {} {} {} pcb'.format(x + 0.020, y + 0.018, z + skidthickness, x + casesize[0] - casethickness, y + casesize[1] - 0.018, z + skidthickness + pcbthickness))
    
    # Shield - Tx & Rx cavities
    print('#box: {} {} {} {} {} {} pec'.format(x + 0.032, y + 0.022, z + skidthickness, x + 0.032 + cavitysize[0], y + 0.022 + cavitysize[1], z + skidthickness + cavitysize[2]))
    print('#box: {} {} {} {} {} {} absorber'.format(x + 0.032 + cavitythickness, y + 0.022 + cavitythickness, z + skidthickness, x + 0.032 + cavitysize[0] - cavitythickness, y + 0.022 + cavitysize[1] - cavitythickness, z + skidthickness + cavitysize[2]))
    print('#box: {} {} {} {} {} {} pec'.format(x + 0.108, y + 0.022, z + skidthickness, x + 0.108 + cavitysize[0], y + 0.022 + cavitysize[1], z + skidthickness + cavitysize[2]))
    print('#box: {} {} {} {} {} {} free_space'.format(x + 0.108 + cavitythickness, y + 0.022 + cavitythickness, z + skidthickness, x + 0.108 + cavitysize[0] - cavitythickness, y + 0.022 + cavitysize[1] - cavitythickness, z + skidthickness + cavitysize[2]))
    
    # Shield - Tx & Rx cavities - joining strips
    print('#box: {} {} {} {} {} {} pec'.format(x + 0.032 + cavitysize[0], y + 0.022 + cavitysize[1] - 0.006, z + skidthickness + cavitysize[2] - casethickness, x + 0.108, y + 0.022 + cavitysize[1], z + skidthickness + cavitysize[2]))
    print('#box: {} {} {} {} {} {} pec'.format(x + 0.032 + cavitysize[0], y + 0.022, z + skidthickness + cavitysize[2] - casethickness, x + 0.108, y + 0.022 + 0.006, z + skidthickness + cavitysize[2]))
    
    # PCB - replace bits chopped by TX & Rx cavities
    print('#box: {} {} {} {} {} {} pcb'.format(x + 0.032 + cavitythickness, y + 0.022 + cavitythickness, z + skidthickness, x + 0.032 + cavitysize[0] - cavitythickness, y + 0.022 + cavitysize[1] - cavitythickness, z + skidthickness + pcbthickness))
    print('#box: {} {} {} {} {} {} pcb'.format(x + 0.108 + cavitythickness, y + 0.022 + cavitythickness, z + skidthickness, x + 0.108 + cavitysize[0] - cavitythickness, y + 0.022 + cavitysize[1] - cavitythickness, z + skidthickness + pcbthickness))
    
    # PCB components
    # Tx bowtie
    print('#triangle: {} {} {} {} {} {} {} {} {} 0 pec'.format(tx[0], tx[1] - 0.001, tx[2], tx[0] - 0.026, tx[1] - bowtieheight - 0.001, tx[2], tx[0] + 0.026, tx[1] - bowtieheight - 0.001, tx[2]))
    print('#edge: {} {} {} {} {} {} pec'.format(tx[0], tx[1] - 0.001, tx[2], tx[0], tx[1], tx[2]))
    print('#triangle: {} {} {} {} {} {} {} {} {} 0 pec'.format(tx[0], tx[1] + 0.002, tx[2], tx[0] - 0.026, tx[1] + bowtieheight + 0.002, tx[2], tx[0] + 0.026, tx[1] + bowtieheight + 0.002, tx[2]))
    print('#edge: {} {} {} {} {} {} pec'.format(tx[0], tx[1] + 0.001, tx[2], tx[0], tx[1] + 0.002, tx[2]))
    
    # Rx bowtie
    print('#triangle: {} {} {} {} {} {} {} {} {} 0 pec'.format(tx[0] + 0.076, tx[1] - 0.001, tx[2], tx[0] + 0.076 - 0.026, tx[1] - bowtieheight - 0.001, tx[2], tx[0] + 0.076 + 0.026, tx[1] - bowtieheight - 0.001, tx[2]))
    print('#edge: {} {} {} {} {} {} pec'.format(tx[0] + 0.076, tx[1] - 0.001, tx[2], tx[0] + 0.076, tx[1], tx[2]))
    print('#triangle: {} {} {} {} {} {} {} {} {} 0 pec'.format(tx[0] + 0.076, tx[1] + 0.002, tx[2], tx[0] + 0.076 - 0.026, tx[1] + bowtieheight + 0.002, tx[2], tx[0] + 0.076 + 0.026, tx[1] + bowtieheight + 0.002, tx[2]))
    print('#edge: {} {} {} {} {} {} pec'.format(tx[0] + 0.076, tx[1] + 0.001, tx[2], tx[0] + 0.076, tx[1] + 0.002, tx[2]))
    
    # Tx surface mount resistors (lower y coordinate)
    if resolution == 0.001:
        print('#edge: {} {} {} {} {} {} txreslower'.format(tx[0] - 0.023, tx[1] - bowtieheight - 0.004, tx[2], tx[0] - 0.023, tx[1] - bowtieheight - dy, tx[2]))
        print('#edge: {} {} {} {} {} {} txreslower'.format(tx[0] - 0.023 + dx, tx[1] - bowtieheight - 0.004, tx[2], tx[0] - 0.023 + dx, tx[1] - bowtieheight - dy, tx[2]))
        print('#edge: {} {} {} {} {} {} txreslower'.format(tx[0], tx[1] - bowtieheight - 0.004, tx[2], tx[0], tx[1] - bowtieheight - dy, tx[2]))
        print('#edge: {} {} {} {} {} {} txreslower'.format(tx[0] + dx, tx[1] - bowtieheight - 0.004, tx[2], tx[0] + dx, tx[1] - bowtieheight - dy, tx[2]))
        print('#edge: {} {} {} {} {} {} txreslower'.format(tx[0] + 0.022, tx[1] - bowtieheight - 0.004, tx[2], tx[0] + 0.022, tx[1] - bowtieheight - dy, tx[2]))
        print('#edge: {} {} {} {} {} {} txreslower'.format(tx[0] + 0.022 + dx, tx[1] - bowtieheight - 0.004, tx[2], tx[0] + 0.022 + dx, tx[1] - bowtieheight - dy, tx[2]))
    elif resolution == 0.002:
        print('#edge: {} {} {} {} {} {} txreslower'.format(tx[0] - 0.023, tx[1] - bowtieheight - 0.004, tx[2], tx[0] - 0.023, tx[1] - bowtieheight, tx[2]))
        print('#edge: {} {} {} {} {} {} txreslower'.format(tx[0] - 0.023 + dx, tx[1] - bowtieheight - 0.004, tx[2], tx[0] - 0.023 + dx, tx[1] - bowtieheight, tx[2]))
        print('#edge: {} {} {} {} {} {} txreslower'.format(tx[0], tx[1] - bowtieheight - 0.004, tx[2], tx[0], tx[1] - bowtieheight, tx[2]))
        print('#edge: {} {} {} {} {} {} txreslower'.format(tx[0] + dx, tx[1] - bowtieheight - 0.004, tx[2], tx[0] + dx, tx[1] - bowtieheight, tx[2]))
        print('#edge: {} {} {} {} {} {} txreslower'.format(tx[0] + 0.020, tx[1] - bowtieheight - 0.004, tx[2], tx[0] + 0.020, tx[1] - bowtieheight, tx[2]))
        print('#edge: {} {} {} {} {} {} txreslower'.format(tx[0] + 0.020 + dx, tx[1] - bowtieheight - 0.004, tx[2], tx[0] + 0.020 + dx, tx[1] - bowtieheight, tx[2]))
    
    # Tx surface mount resistors (upper y coordinate)
    if resolution == 0.001:
        print('#edge: {} {} {} {} {} {} txresupper'.format(tx[0] - 0.023, tx[1] + bowtieheight + 0.002, tx[2], tx[0] - 0.023, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} txresupper'.format(tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.002, tx[2], tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} txresupper'.format(tx[0], tx[1] + bowtieheight + 0.002, tx[2], tx[0], tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} txresupper'.format(tx[0] + dx, tx[1] + bowtieheight + 0.002, tx[2], tx[0] + dx, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} txresupper'.format(tx[0] + 0.022, tx[1] + bowtieheight + 0.002, tx[2], tx[0] + 0.022, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} txresupper'.format(tx[0] + 0.022 + dx, tx[1] + bowtieheight + 0.002, tx[2], tx[0] + 0.022 + dx, tx[1] + bowtieheight + 0.006, tx[2]))
    elif resolution == 0.002:
        print('#edge: {} {} {} {} {} {} txresupper'.format(tx[0] - 0.023, tx[1] + bowtieheight + 0.002, tx[2], tx[0] - 0.023, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} txresupper'.format(tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.002, tx[2], tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} txresupper'.format(tx[0], tx[1] + bowtieheight + 0.002, tx[2], tx[0], tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} txresupper'.format(tx[0] + dx, tx[1] + bowtieheight + 0.002, tx[2], tx[0] + dx, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} txresupper'.format(tx[0] + 0.020, tx[1] + bowtieheight + 0.002, tx[2], tx[0] + 0.020, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} txresupper'.format(tx[0] + 0.020 + dx, tx[1] + bowtieheight + 0.002, tx[2], tx[0] + 0.020 + dx, tx[1] + bowtieheight + 0.006, tx[2]))
    
    # Rx surface mount resistors (lower y coordinate)
    if resolution == 0.001:
        print('#edge: {} {} {} {} {} {} rxreslower'.format(tx[0] - 0.023 + 0.076, tx[1] - bowtieheight - 0.004, tx[2], tx[0] - 0.023 + 0.076, tx[1] - bowtieheight - dy, tx[2]))
        print('#edge: {} {} {} {} {} {} rxreslower'.format(tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2], tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight - dy, tx[2]))
        print('#edge: {} {} {} {} {} {} rxreslower'.format(tx[0] + 0.076, tx[1] - bowtieheight - 0.004, tx[2], tx[0] + 0.076, tx[1] - bowtieheight - dy, tx[2]))
        print('#edge: {} {} {} {} {} {} rxreslower'.format(tx[0] + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2], tx[0] + dx + 0.076, tx[1] - bowtieheight - dy, tx[2]))
        print('#edge: {} {} {} {} {} {} rxreslower'.format(tx[0] + 0.022 + 0.076, tx[1] - bowtieheight - 0.004, tx[2], tx[0] + 0.022 + 0.076, tx[1] - bowtieheight - dy, tx[2]))
        print('#edge: {} {} {} {} {} {} rxreslower'.format(tx[0] + 0.022 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2], tx[0] + 0.022 + dx + 0.076, tx[1] - bowtieheight - dy, tx[2]))
    elif resolution == 0.002:
        print('#edge: {} {} {} {} {} {} rxreslower'.format(tx[0] - 0.023 + 0.076, tx[1] - bowtieheight - 0.004, tx[2], tx[0] - 0.023 + 0.076, tx[1] - bowtieheight, tx[2]))
        print('#edge: {} {} {} {} {} {} rxreslower'.format(tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2], tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight, tx[2]))
        print('#edge: {} {} {} {} {} {} rxreslower'.format(tx[0] + 0.076, tx[1] - bowtieheight - 0.004, tx[2], tx[0] + 0.076, tx[1] - bowtieheight, tx[2]))
        print('#edge: {} {} {} {} {} {} rxreslower'.format(tx[0] + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2], tx[0] + dx + 0.076, tx[1] - bowtieheight, tx[2]))
        print('#edge: {} {} {} {} {} {} rxreslower'.format(tx[0] + 0.020 + 0.076, tx[1] - bowtieheight - 0.004, tx[2], tx[0] + 0.020 + 0.076, tx[1] - bowtieheight, tx[2]))
        print('#edge: {} {} {} {} {} {} rxreslower'.format(tx[0] + 0.020 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2], tx[0] + 0.020 + dx + 0.076, tx[1] - bowtieheight, tx[2]))
    
    # Rx surface mount resistors (upper y coordinate)
    if resolution == 0.001:
        print('#edge: {} {} {} {} {} {} rxresupper'.format(tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.002, tx[2], tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} rxresupper'.format(tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2], tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} rxresupper'.format(tx[0] + 0.076, tx[1] + bowtieheight + 0.002, tx[2], tx[0] + 0.076, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} rxresupper'.format(tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2], tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} rxresupper'.format(tx[0] + 0.022 + 0.076, tx[1] + bowtieheight + 0.002, tx[2], tx[0] + 0.022 + 0.076, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} rxresupper'.format(tx[0] + 0.022 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2], tx[0] + 0.022 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]))
    elif resolution == 0.002:
        print('#edge: {} {} {} {} {} {} rxresupper'.format(tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.002, tx[2], tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} rxresupper'.format(tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2], tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} rxresupper'.format(tx[0] + 0.076, tx[1] + bowtieheight + 0.002, tx[2], tx[0] + 0.076, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} rxresupper'.format(tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2], tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} rxresupper'.format(tx[0] + 0.020 + 0.076, tx[1] + bowtieheight + 0.002, tx[2], tx[0] + 0.020 + 0.076, tx[1] + bowtieheight + 0.006, tx[2]))
        print('#edge: {} {} {} {} {} {} rxresupper'.format(tx[0] + 0.020 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2], tx[0] + 0.020 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]))
    
    # Skid
    print('#box: {} {} {} {} {} {} polypropylene'.format(x, y, z, x + casesize[0], y + casesize[1], z + polypropylenethickness))
    print('#box: {} {} {} {} {} {} hdpe'.format(x, y, z + polypropylenethickness, x + casesize[0], y + casesize[1], z + polypropylenethickness + hdpethickness))

    # Geometry views
    #print('#geometry_view: {} {} {} {} {} {} {} {} {} antenna_like_MALA_1200 n'.format(x - dx, y - dy, z - dz, x + casesize[0] + dx, y + casesize[1] + dy, z + casesize[2] + skidthickness + dz, dx, dy, dz))
    #print('#geometry_view: {} {} {} {} {} {} {} {} {} antenna_like_MALA_1200_pcb f'.format(x, y, z, x + casesize[0], y + casesize[1], z + 0.010, dx, dy, dz))
    
    # Excitation
    print('#waveform: gaussian 1.0 {} myGaussian'.format(excitationfreq))
    print('#voltage_source: y {} {} {} {} myGaussian'.format(tx[0], tx[1], tx[2], sourceresistance))
    
    # Output point - transmitter bowtie
    #print('#rx: {} {} {}'.format(tx[0], tx[1], tx[2]))
    # Output point - receiver bowtie
    print('#rx: {} {} {}'.format(tx[0] + 0.076, tx[1], tx[2]))
Beispiel #14
0
def check_cmd_names(processedlines, checkessential=True):
    """Checks the validity of commands, i.e. are they gprMax commands, and that all essential commands are present.

    Args:
        processedlines (list): Input commands after Python processing.
        checkessential (boolean): Perform check to see that all essential commands are present.

    Returns:
        singlecmds (dict): Commands that can only occur once in the model.
        multiplecmds (dict): Commands that can have multiple instances in the model.
        geometry (list): Geometry commands in the model.
    """

    # Dictionaries of available commands
    # Essential commands neccessary to run a gprMax model
    essentialcmds = ['#domain', '#dx_dy_dz', '#time_window']

    # Commands that there should only be one instance of in a model
    singlecmds = dict.fromkeys([
        '#domain', '#dx_dy_dz', '#time_window', '#title', '#messages',
        '#num_threads', '#time_step_stability_factor', '#pml_cells',
        '#excitation_file', '#src_steps', '#rx_steps', '#taguchi',
        '#end_taguchi'
    ], 'None')

    # Commands that there can be multiple instances of in a model - these will be lists within the dictionary
    multiplecmds = {
        key: []
        for key in [
            '#geometry_view', '#geometry_objects_write', '#material',
            '#soil_peplinski', '#add_dispersion_debye',
            '#add_dispersion_lorentz', '#add_dispersion_drude', '#waveform',
            '#voltage_source', '#hertzian_dipole', '#magnetic_dipole',
            '#transmission_line', '#rx', '#rx_array', '#snapshot', '#pml_cfs'
        ]
    }

    # Geometry object building commands that there can be multiple instances of in a model - these will be lists within the dictionary
    geometrycmds = [
        '#geometry_objects_read', '#edge', '#plate', '#triangle', '#box',
        '#sphere', '#cylinder', '#cylindrical_sector', '#fractal_box',
        '#add_surface_roughness', '#add_surface_water', '#add_grass'
    ]
    # List to store all geometry object commands in order from input file
    geometry = []

    # Check if command names are valid, if essential commands are present, and add command parameters to appropriate dictionary values or lists
    countessentialcmds = 0
    lindex = 0
    while (lindex < len(processedlines)):
        cmd = processedlines[lindex].split(':')
        cmdname = cmd[0]
        cmdparams = cmd[1]

        # Check if there is space between command name and parameters, i.e. check first character of parameter string
        if ' ' not in cmdparams[0]:
            raise CmdInputError(
                'There must be a space between the command name and parameters in '
                + processedlines[lindex])

        # Check if command name is valid
        if cmdname not in essentialcmds and cmdname not in singlecmds and cmdname not in multiplecmds and cmdname not in geometrycmds:
            raise CmdInputError(
                'Your input file contains an invalid command: ' + cmdname)

        # Count essential commands
        if cmdname in essentialcmds:
            countessentialcmds += 1

        # Assign command parameters as values to dictionary keys
        if cmdname in singlecmds:
            if singlecmds[cmdname] == 'None':
                singlecmds[cmdname] = cmd[1].strip(' \t\n')
            else:
                raise CmdInputError('You can only have instance of ' +
                                    cmdname + ' in your model')

        elif cmdname in multiplecmds:
            multiplecmds[cmdname].append(cmd[1].strip(' \t\n'))

        elif cmdname in geometrycmds:
            geometry.append(processedlines[lindex].strip(' \t\n'))

        lindex += 1

    if checkessential:
        if (countessentialcmds < len(essentialcmds)):
            raise CmdInputError(
                'Your input file is missing essential commands required to run a model. Essential commands are: '
                + ', '.join(essentialcmds))

    return singlecmds, multiplecmds, geometry
Beispiel #15
0
def antenna_like_GSSI_400(x, y, z, resolution=0.001, rotate90=False):
    """Inserts a description of an antenna similar to the GSSI 400MHz antenna. Can be used with 0.5mm, 1mm (default) or 2mm spatial resolution. The external dimensions of the antenna are 300x300x178mm. One output point is defined between the arms of the receiver bowtie. The bowties are aligned with the y axis so the output is the y component of the electric field.

    Args:
        x, y, z (float): Coordinates of a location in the model to insert the antenna. Coordinates are relative to the geometric centre of the antenna in the x-y plane and the bottom of the antenna skid in the z direction.
        resolution (float): Spatial resolution for the antenna model.
        rotate90 (bool): Rotate model 90 degrees CCW in xy plane.
    """

    # Antenna geometry properties
    casesize = (0.3, 0.3, 0.178) # original
    casethickness = 0.002
    shieldthickness = 0.002
    foamsurroundthickness = 0.003
    pcbthickness = 0.002
    bowtiebase = 0.06
    bowtieheight = 0.06 # original 0.056
    patchheight = 0.06 # original 0.056
    metalboxheight = 0.089
    metalmiddleplateheight = 0.11

    # Set origin for rotation to geometric centre of antenna in x-y plane if required, and set output component for receiver
    if rotate90:
        rotate90origin = (x, y)
        output = 'Ex'
    else:
        rotate90origin = ()
        output = 'Ey'

    smooth_dec = 'yes' # choose to use dielectric smoothing or not
    src_type = 'voltage_source' # # source type. "voltage_source" or "transmission_line"
    excitationfreq = 0.39239891e9 # GHz
    sourceresistance = 111.59927 # Ohms
    receiverresistance = sourceresistance # Ohms
    absorberEr = 1.1
    absorbersig = 0.062034689
    pcber = 2.35
    hdper = 2.35
    skidthickness = 0.01

    x = x - (casesize[0] / 2)
    y = y - (casesize[1] / 2)

    # Coordinates of source excitation point in antenna
    tx = x + 0.01 + 0.005 + 0.056, y + casethickness + 0.005 + 0.143, z + skidthickness

    if resolution == 0.0005:
        dx = 0.0005
        dy = 0.0005
        dz = 0.0005
        tx = x + 0.01 + 0.005 + 0.056, y + casethickness + 0.005 + 0.1435, z + skidthickness
    elif resolution == 0.001:
        dx = 0.001
        dy = 0.001
        dz = 0.001
    elif resolution == 0.002:
        dx = 0.002
        dy = 0.002
        dz = 0.002
        foamsurroundthickness = 0.002
        metalboxheight = 0.088
        tx = x + 0.01 + 0.004 + 0.056, y + casethickness + 0.005 + 0.143 - 0.002, z + skidthickness
    else:
        raise CmdInputError('This antenna module can only be used with a spatial discretisation of 0.5mm, 1mm, 2mm')

    # Material definitions
    material(absorberEr, absorbersig, 1, 0, 'absorber')
    material(pcber, 0, 1, 0, 'pcb')
    material(hdper, 0, 1, 0, 'hdpe')

    # Antenna geometry
    if smooth_dec == 'yes':
        # Plastic case
        box(x, y, z + skidthickness - 0.002, x + casesize[0], y + casesize[1], z + casesize[2], 'hdpe', rotate90origin=rotate90origin) # new new (0.300 x 0.300 x 0.170)
        box(x + casethickness, y + casethickness, z + skidthickness - 0.002, x + casesize[0] - casethickness, y + casesize[1] - casethickness, z + casesize[2] - casethickness, 'free_space', rotate90origin=rotate90origin) # (0.296 x 0.296 x 0.168)

        # Metallic enclosure
        box(x + casethickness, y + casethickness, z + skidthickness + (metalmiddleplateheight - metalboxheight), x + casesize[0] - casethickness, y + casesize[1] - casethickness, z + skidthickness + (metalmiddleplateheight - metalboxheight) + metalboxheight, 'pec', rotate90origin=rotate90origin) # new (0.296 x 0.296 x 0.088)

        # Absorber, and foam (modelled as PCB material) around edge of absorber
        box(x + casethickness, y + casethickness, z + skidthickness, x + casesize[0] - casethickness, y + casesize[1] - casethickness, z + skidthickness + (metalmiddleplateheight - metalboxheight), 'absorber', rotate90origin=rotate90origin)	# new 4 (0.296 x 0.296 x 0.022)
        box(x + casethickness + shieldthickness, y + casethickness + shieldthickness, z + skidthickness + (metalmiddleplateheight - metalboxheight), x + casesize[0] - casethickness - shieldthickness, y + casesize[1] - casethickness - shieldthickness, z + skidthickness - shieldthickness + metalmiddleplateheight, 'absorber', rotate90origin=rotate90origin)	# new 4 (0.292 x 0.292 x 0.086)

        # PCB
        if resolution == 0.0005:
            box(x + 0.01 + 0.005 + 0.018, y + casethickness + 0.005 + 0.0235, z + skidthickness, x + 0.01 + 0.005 + 0.034 + bowtiebase, y + casethickness + 0.005 + 0.204 + patchheight, z + skidthickness + pcbthickness, 'pcb', rotate90origin=rotate90origin)    # new
            box(x + 0.01 + 0.005 + 0.178, y + casethickness + 0.005 + 0.0235, z + skidthickness, x + 0.01 + 0.005 + 0.194 + bowtiebase, y + casethickness + 0.005 + 0.204 + patchheight, z + skidthickness + pcbthickness, 'pcb', rotate90origin=rotate90origin)   # new

        elif resolution == 0.001:
            box(x + 0.01 + 0.005 + 0.018, y + casethickness + 0.005 + 0.023, z + skidthickness, x + 0.01 + 0.005 + 0.034 + bowtiebase, y + casethickness + 0.005 + 0.204 + patchheight, z + skidthickness + pcbthickness, 'pcb', rotate90origin=rotate90origin)    # new
            box(x + 0.01 + 0.005 + 0.178, y + casethickness + 0.005 + 0.023, z + skidthickness, x + 0.01 + 0.005 + 0.194 + bowtiebase, y + casethickness + 0.005 + 0.204 + patchheight, z + skidthickness + pcbthickness, 'pcb', rotate90origin=rotate90origin)   # new

        elif resolution == 0.002:
            box(x + 0.01 + 0.005 + 0.017, y + casethickness + 0.005 + 0.021, z + skidthickness, x + 0.01 + 0.005 + 0.033 + bowtiebase, y + casethickness + 0.006 + 0.202 + patchheight, z + skidthickness + pcbthickness, 'pcb', rotate90origin=rotate90origin)    # new (for use with edges)
            box(x + 0.01 + 0.005 + 0.179, y + casethickness + 0.005 + 0.021, z + skidthickness, x + 0.01 + 0.005 + 0.195 + bowtiebase, y + casethickness + 0.006 + 0.202 + patchheight, z + skidthickness + pcbthickness, 'pcb', rotate90origin=rotate90origin)   # new (for use with edges)

    elif smooth_dec == 'no':
        # Plastic case
        box(x, y, z + skidthickness - 0.002, x + casesize[0], y + casesize[1], z + casesize[2], 'hdpe', 'n', rotate90origin=rotate90origin) # new new (0.300 x 0.300 x 0.170)
        box(x + casethickness, y + casethickness, z + skidthickness - 0.002, x + casesize[0] - casethickness, y + casesize[1] - casethickness, z + casesize[2] - casethickness, 'free_space', 'n', rotate90origin=rotate90origin) # (0.296 x 0.296 x 0.168)

        # Metallic enclosure
        box(x + casethickness, y + casethickness, z + skidthickness + (metalmiddleplateheight - metalboxheight), x + casesize[0] - casethickness, y + casesize[1] - casethickness, z + skidthickness + (metalmiddleplateheight - metalboxheight) + metalboxheight, 'pec', rotate90origin=rotate90origin) # new (0.296 x 0.296 x 0.088)

        # Absorber, and foam (modelled as PCB material) around edge of absorber
        box(x + casethickness, y + casethickness, z + skidthickness, x + casesize[0] - casethickness, y + casesize[1] - casethickness, z + skidthickness + (metalmiddleplateheight - metalboxheight), 'absorber', 'n', rotate90origin=rotate90origin)	# new 4 (0.296 x 0.296 x 0.022)
        box(x + casethickness + shieldthickness, y + casethickness + shieldthickness, z + skidthickness + (metalmiddleplateheight - metalboxheight), x + casesize[0] - casethickness - shieldthickness, y + casesize[1] - casethickness - shieldthickness, z + skidthickness - shieldthickness + metalmiddleplateheight, 'absorber', 'n', rotate90origin=rotate90origin)	# new 4 (0.292 x 0.292 x 0.086)

        # PCB
        if resolution == 0.0005:
            box(x + 0.01 + 0.005 + 0.018, y + casethickness + 0.005 + 0.0235, z + skidthickness, x + 0.01 + 0.005 + 0.034 + bowtiebase, y + casethickness + 0.005 + 0.204 + patchheight, z + skidthickness + pcbthickness, 'pcb', 'n', rotate90origin=rotate90origin)    # new
            box(x + 0.01 + 0.005 + 0.178, y + casethickness + 0.005 + 0.0235, z + skidthickness, x + 0.01 + 0.005 + 0.194 + bowtiebase, y + casethickness + 0.005 + 0.204 + patchheight, z + skidthickness + pcbthickness, 'pcb', 'n', rotate90origin=rotate90origin)   # new

        elif resolution == 0.001:
            box(x + 0.01 + 0.005 + 0.018, y + casethickness + 0.005 + 0.023, z + skidthickness, x + 0.01 + 0.005 + 0.034 + bowtiebase, y + casethickness + 0.005 + 0.204 + patchheight, z + skidthickness + pcbthickness, 'pcb', 'n', rotate90origin=rotate90origin)    # new
            box(x + 0.01 + 0.005 + 0.178, y + casethickness + 0.005 + 0.023, z + skidthickness, x + 0.01 + 0.005 + 0.194 + bowtiebase, y + casethickness + 0.005 + 0.204 + patchheight, z + skidthickness + pcbthickness, 'pcb', 'n', rotate90origin=rotate90origin)   # new

        elif resolution == 0.002:
            box(x + 0.01 + 0.005 + 0.017, y + casethickness + 0.005 + 0.021, z + skidthickness, x + 0.01 + 0.005 + 0.033 + bowtiebase, y + casethickness + 0.006 + 0.202 + patchheight, z + skidthickness + pcbthickness, 'pcb', 'n', rotate90origin=rotate90origin)    # new (for use with edges)
            box(x + 0.01 + 0.005 + 0.179, y + casethickness + 0.005 + 0.021, z + skidthickness, x + 0.01 + 0.005 + 0.195 + bowtiebase, y + casethickness + 0.006 + 0.202 + patchheight, z + skidthickness + pcbthickness, 'pcb', 'n', rotate90origin=rotate90origin)   # new (for use with edges)

    # PCB components
    # My own bowties with triangle commands
    if resolution == 0.0005:
        # "left" side
        # extension plates
        plate(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.0235, z + skidthickness, x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.0235 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin) # new
        # box(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.0235, z + skidthickness, x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.0235 + patchheight, z + skidthickness + 0.002, 'pec', rotate90origin=rotate90origin) # new new

        plate(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.204, z + skidthickness, x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.204 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin) # new
        # box(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.204, z + skidthickness, x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.204 + patchheight, z + skidthickness + 0.002, 'pec', rotate90origin=rotate90origin) # new new

        # triangles
        # triangle(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.0835, z + skidthickness,
                 # x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.0835, z + skidthickness,
                 # x + 0.01 + 0.005 + 0.026 + (bowtiebase/2), y + casethickness + 0.005 + 0.0835 + bowtieheight, z + skidthickness,
                 # 0.002,'pec', rotate90origin=rotate90origin)
        triangle(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.0835, z + skidthickness,
                 x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.0835, z + skidthickness,
                 x + 0.01 + 0.005 + 0.026 + (bowtiebase/2), y + casethickness + 0.005 + 0.0835 + bowtieheight, z + skidthickness,
                 0,'pec', rotate90origin=rotate90origin) # new
        # triangle(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.204, z + skidthickness,
                 # x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.204, z + skidthickness,
                 # x + 0.01 + 0.005 + 0.026 + (bowtiebase/2), y + casethickness + 0.005 + 0.204 - bowtieheight, z + skidthickness,
                 # 0.002,'pec', rotate90origin=rotate90origin)
        triangle(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.204, z + skidthickness,
                 x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.204, z + skidthickness,
                 x + 0.01 + 0.005 + 0.026 + (bowtiebase/2), y + casethickness + 0.005 + 0.204 - bowtieheight, z + skidthickness,
                 0,'pec', rotate90origin=rotate90origin) # new

        # "right" side
        plate(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.0235, z + skidthickness, x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.0235 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin) # new
        # box(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.0235, z + skidthickness, x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.0235 + patchheight, z + skidthickness + 0.002, 'pec', rotate90origin=rotate90origin) # new new

        plate(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.204, z + skidthickness, x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.204 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin) # new
        # box(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.204, z + skidthickness, x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.204 + patchheight, z + skidthickness + 0.002, 'pec', rotate90origin=rotate90origin) # new

        # triangles
        # triangle(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.0835, z + skidthickness,
                 # x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.0835, z + skidthickness,
                 # x + 0.01 + 0.005 + 0.186 + (bowtiebase/2), y + casethickness + 0.005 + 0.0835 + bowtieheight, z + skidthickness,
                 # 0.002,'pec', rotate90origin=rotate90origin)
        triangle(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.0835, z + skidthickness,
                 x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.0835, z + skidthickness,
                 x + 0.01 + 0.005 + 0.186 + (bowtiebase/2), y + casethickness + 0.005 + 0.0835 + bowtieheight, z + skidthickness,
                 0,'pec', rotate90origin=rotate90origin)
        # triangle(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.204, z + skidthickness,
                 # x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.204, z + skidthickness,
                 # x + 0.01 + 0.005 + 0.186 + (bowtiebase/2), y + casethickness + 0.005 + 0.204 - bowtieheight, z + skidthickness,
                 # 0.002,'pec', rotate90origin=rotate90origin)
        triangle(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.204, z + skidthickness,
                 x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.204, z + skidthickness,
                 x + 0.01 + 0.005 + 0.186 + (bowtiebase/2), y + casethickness + 0.005 + 0.204 - bowtieheight, z + skidthickness,
                 0,'pec', rotate90origin=rotate90origin)

        # Edges that represent wire between bowtie halves in 1mm model
        edge(tx[0] + 0.16, tx[1] - dy, tx[2], tx[0] + 0.16, tx[1], tx[2], 'pec', rotate90origin=rotate90origin)
        edge(tx[0] + 0.16, tx[1] + dy, tx[2], tx[0] + 0.16, tx[1] + 2*dy, tx[2], 'pec', rotate90origin=rotate90origin)
        edge(tx[0], tx[1] - dy, tx[2], tx[0], tx[1], tx[2], 'pec', rotate90origin=rotate90origin)
        edge(tx[0], tx[1] + dy, tx[2], tx[0], tx[1] + 2*dy, tx[2], 'pec', rotate90origin=rotate90origin)

    elif resolution == 0.001:
        # "left" side
        # extension plates
        plate(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.023, z + skidthickness, x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.023 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin) # new
        # box(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.023, z + skidthickness, x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.023 + patchheight, z + skidthickness + 0.002, 'pec', rotate90origin=rotate90origin) # new

        plate(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.204, z + skidthickness, x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.204 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin) # new
        # box(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.204, z + skidthickness, x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.204 + patchheight, z + skidthickness + 0.002, 'pec', rotate90origin=rotate90origin) # new

        # triangles
        # triangle(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.083, z + skidthickness,
                 # x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.083, z + skidthickness,
                 # x + 0.01 + 0.005 + 0.026 + (bowtiebase/2), y + casethickness + 0.005 + 0.083 + bowtieheight, z + skidthickness,
                 # 0.002,'pec', rotate90origin=rotate90origin)
        triangle(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.083, z + skidthickness,
                 x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.083, z + skidthickness,
                 x + 0.01 + 0.005 + 0.026 + (bowtiebase/2), y + casethickness + 0.005 + 0.083 + bowtieheight, z + skidthickness,
                 0,'pec', rotate90origin=rotate90origin) # new
        # triangle(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.204, z + skidthickness,
                 # x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.204, z + skidthickness,
                 # x + 0.01 + 0.005 + 0.026 + (bowtiebase/2), y + casethickness + 0.005 + 0.204 - bowtieheight, z + skidthickness,
                 # 0.002,'pec', rotate90origin=rotate90origin)
        triangle(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.204, z + skidthickness,
                 x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.204, z + skidthickness,
                 x + 0.01 + 0.005 + 0.026 + (bowtiebase/2), y + casethickness + 0.005 + 0.204 - bowtieheight, z + skidthickness,
                 0,'pec', rotate90origin=rotate90origin) # new

        # "right" side
        plate(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.023, z + skidthickness, x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.023 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin) # new
        # box(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.023, z + skidthickness, x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.023 + patchheight, z + skidthickness + 0.002, 'pec', rotate90origin=rotate90origin) # new

        plate(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.204, z + skidthickness, x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.204 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin) # new
        # box(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.204, z + skidthickness, x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.204 + patchheight, z + skidthickness + 0.002, 'pec', rotate90origin=rotate90origin) # new

        # triangles
        # triangle(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.083, z + skidthickness,
                 # x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.083, z + skidthickness,
                 # x + 0.01 + 0.005 + 0.186 + (bowtiebase/2), y + casethickness + 0.005 + 0.083 + bowtieheight, z + skidthickness,
                 # 0.002,'pec', rotate90origin=rotate90origin)
        triangle(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.083, z + skidthickness,
                 x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.083, z + skidthickness,
                 x + 0.01 + 0.005 + 0.186 + (bowtiebase/2), y + casethickness + 0.005 + 0.083 + bowtieheight, z + skidthickness,
                 0,'pec', rotate90origin=rotate90origin)
        # triangle(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.204, z + skidthickness,
                 # x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.204, z + skidthickness,
                 # x + 0.01 + 0.005 + 0.186 + (bowtiebase/2), y + casethickness + 0.005 + 0.204 - bowtieheight, z + skidthickness,
                 # 0.002,'pec', rotate90origin=rotate90origin)
        triangle(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.204, z + skidthickness,
                 x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.204, z + skidthickness,
                 x + 0.01 + 0.005 + 0.186 + (bowtiebase/2), y + casethickness + 0.005 + 0.204 - bowtieheight, z + skidthickness,
                 0,'pec', rotate90origin=rotate90origin)

        # Edges that represent wire between bowtie halves in 1mm model
        edge(tx[0] + 0.16, tx[1] - dy, tx[2], tx[0] + 0.16, tx[1], tx[2], 'pec', rotate90origin=rotate90origin)
        edge(tx[0] + 0.16, tx[1] + dy, tx[2], tx[0] + 0.16, tx[1] + 2*dy, tx[2], 'pec', rotate90origin=rotate90origin)
        edge(tx[0], tx[1] - dy, tx[2], tx[0], tx[1], tx[2], 'pec', rotate90origin=rotate90origin)
        edge(tx[0], tx[1] + dy, tx[2], tx[0], tx[1] + 2*dy, tx[2], 'pec', rotate90origin=rotate90origin)

    elif resolution == 0.002:
            # "left" side
            # extension plates
            plate(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.021, z + skidthickness, x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.021 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin) # new (for use with edges)
            # box(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.021, z + skidthickness, x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.021 + patchheight, z + skidthickness + 0.002, 'pec', rotate90origin=rotate90origin) # new (for use with edges)
            plate(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.203, z + skidthickness, x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.203 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin) # new (for use with edges)
            # box(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.203, z + skidthickness, x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.203 + patchheight, z + skidthickness + 0.002, 'pec', rotate90origin=rotate90origin) # new (for use with edges)

            # triangles
            # triangle(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.081, z + skidthickness,
                     # x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.081, z + skidthickness,
                     # x + 0.01 + 0.005 + 0.025 + (bowtiebase/2), y + casethickness + 0.005 + 0.081 + bowtieheight, z + skidthickness,
                     # 0.002,'pec', rotate90origin=rotate90origin) # new (for use with edges)
            triangle(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.081, z + skidthickness,
                     x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.081, z + skidthickness,
                     x + 0.01 + 0.005 + 0.025 + (bowtiebase/2), y + casethickness + 0.005 + 0.081 + bowtieheight, z + skidthickness,
                     0,'pec', rotate90origin=rotate90origin) # new (for use with edges)
            # triangle(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.203, z + skidthickness,
                     # x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.203, z + skidthickness,
                     # x + 0.01 + 0.005 + 0.025 + (bowtiebase/2), y + casethickness + 0.005 + 0.203 - bowtieheight, z + skidthickness,
                     # 0.002,'pec', rotate90origin=rotate90origin) # new (for use with edges)
            triangle(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.203, z + skidthickness,
                     x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.203, z + skidthickness,
                     x + 0.01 + 0.005 + 0.025 + (bowtiebase/2), y + casethickness + 0.005 + 0.203 - bowtieheight, z + skidthickness,
                     0,'pec', rotate90origin=rotate90origin) # new (for use with edges)

            # "right" side
            plate(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.021, z + skidthickness, x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.021 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin) # new (for use with edges)
            # box(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.021, z + skidthickness, x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.021 + patchheight, z + skidthickness + 0.002, 'pec', rotate90origin=rotate90origin) # new (for use with edges)
            plate(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.203, z + skidthickness, x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.203 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin) # new (for use with edges)
            # box(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.203, z + skidthickness, x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.203 + patchheight, z + skidthickness + 0.002, 'pec', rotate90origin=rotate90origin) # new (for use with edges)

            # triangles
            # triangle(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.081, z + skidthickness,
                     # x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.081, z + skidthickness,
                     # x + 0.01 + 0.005 + 0.187 + (bowtiebase/2), y + casethickness + 0.005 + 0.081 + bowtieheight, z + skidthickness,
                     # 0.002,'pec', rotate90origin=rotate90origin) # new (for use with edges)
            triangle(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.081, z + skidthickness,
                     x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.081, z + skidthickness,
                     x + 0.01 + 0.005 + 0.187 + (bowtiebase/2), y + casethickness + 0.005 + 0.081 + bowtieheight, z + skidthickness,
                     0,'pec', rotate90origin=rotate90origin) # new (for use with edges)
            # triangle(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.203, z + skidthickness,
                     # x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.203, z + skidthickness,
                     # x + 0.01 + 0.005 + 0.187 + (bowtiebase/2), y + casethickness + 0.005 + 0.203 - bowtieheight, z + skidthickness,
                     # 0.002,'pec', rotate90origin=rotate90origin) # new (for use with edges)
            triangle(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.203, z + skidthickness,
                     x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.203, z + skidthickness,
                     x + 0.01 + 0.005 + 0.187 + (bowtiebase/2), y + casethickness + 0.005 + 0.203 - bowtieheight, z + skidthickness,
                     0,'pec', rotate90origin=rotate90origin) # new (for use with edges)

            # Edges that represent wire between bowtie halves in 2mm model
            edge(tx[0] + 0.162, tx[1] - dy, tx[2], tx[0] + 0.162, tx[1], tx[2], 'pec', rotate90origin=rotate90origin)
            edge(tx[0] + 0.162, tx[1] + dy, tx[2], tx[0] + 0.162, tx[1] + 2*dy, tx[2], 'pec', rotate90origin=rotate90origin)
            edge(tx[0], tx[1] - dy, tx[2], tx[0], tx[1], tx[2], 'pec', rotate90origin=rotate90origin)
            edge(tx[0], tx[1] + dy, tx[2], tx[0], tx[1] + 2*dy, tx[2], 'pec', rotate90origin=rotate90origin)

            # "left" side
            # extension plates
            plate(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.021, z + skidthickness, x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.021 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin) # new (for use with edges)
            # box(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.021, z + skidthickness, x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.021 + patchheight, z + skidthickness + 0.002, 'pec', rotate90origin=rotate90origin) # new (for use with edges)
            plate(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.203, z + skidthickness, x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.203 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin) # new (for use with edges)
            # box(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.203, z + skidthickness, x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.203 + patchheight, z + skidthickness + 0.002, 'pec', rotate90origin=rotate90origin) # new (for use with edges)

            # triangles
            # triangle(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.081, z + skidthickness,
                     # x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.081, z + skidthickness,
                     # x + 0.01 + 0.005 + 0.025 + (bowtiebase/2), y + casethickness + 0.005 + 0.081 + bowtieheight, z + skidthickness,
                     # 0.002,'pec', rotate90origin=rotate90origin) # new (for use with edges)
            triangle(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.081, z + skidthickness,
                     x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.081, z + skidthickness,
                     x + 0.01 + 0.005 + 0.025 + (bowtiebase/2), y + casethickness + 0.005 + 0.081 + bowtieheight, z + skidthickness,
                     0,'pec', rotate90origin=rotate90origin) # new (for use with edges)
            # triangle(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.203, z + skidthickness,
                     # x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.203, z + skidthickness,
                     # x + 0.01 + 0.005 + 0.025 + (bowtiebase/2), y + casethickness + 0.005 + 0.203 - bowtieheight, z + skidthickness,
                     # 0.002,'pec', rotate90origin=rotate90origin) # new (for use with edges)
            triangle(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.203, z + skidthickness,
                     x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.203, z + skidthickness,
                     x + 0.01 + 0.005 + 0.025 + (bowtiebase/2), y + casethickness + 0.005 + 0.203 - bowtieheight, z + skidthickness,
                     0,'pec', rotate90origin=rotate90origin) # new (for use with edges)

            # "right" side
            plate(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.021, z + skidthickness, x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.021 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin) # new (for use with edges)
            # box(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.021, z + skidthickness, x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.021 + patchheight, z + skidthickness + 0.002, 'pec', rotate90origin=rotate90origin) # new (for use with edges)
            plate(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.203, z + skidthickness, x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.203 + patchheight, z + skidthickness, 'pec', rotate90origin=rotate90origin) # new (for use with edges)
            # box(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.203, z + skidthickness, x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.203 + patchheight, z + skidthickness + 0.002, 'pec', rotate90origin=rotate90origin) # new (for use with edges)

            # triangles
            # triangle(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.081, z + skidthickness,
                     # x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.081, z + skidthickness,
                     # x + 0.01 + 0.005 + 0.187 + (bowtiebase/2), y + casethickness + 0.005 + 0.081 + bowtieheight, z + skidthickness,
                     # 0.002,'pec', rotate90origin=rotate90origin) # new (for use with edges)
            triangle(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.081, z + skidthickness,
                     x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.081, z + skidthickness,
                     x + 0.01 + 0.005 + 0.187 + (bowtiebase/2), y + casethickness + 0.005 + 0.081 + bowtieheight, z + skidthickness,
                     0,'pec', rotate90origin=rotate90origin) # new (for use with edges)
            # triangle(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.203, z + skidthickness,
                     # x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.203, z + skidthickness,
                     # x + 0.01 + 0.005 + 0.187 + (bowtiebase/2), y + casethickness + 0.005 + 0.203 - bowtieheight, z + skidthickness,
                     # 0.002,'pec', rotate90origin=rotate90origin) # new (for use with edges)
            triangle(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.203, z + skidthickness,
                     x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.203, z + skidthickness,
                     x + 0.01 + 0.005 + 0.187 + (bowtiebase/2), y + casethickness + 0.005 + 0.203 - bowtieheight, z + skidthickness,
                     0,'pec', rotate90origin=rotate90origin) # new (for use with edges)

            # Edges that represent wire between bowtie halves in 2mm model
            edge(tx[0] + 0.162, tx[1] - dy, tx[2], tx[0] + 0.162, tx[1], tx[2], 'pec', rotate90origin=rotate90origin)
            edge(tx[0] + 0.162, tx[1] + dy, tx[2], tx[0] + 0.162, tx[1] + 2*dy, tx[2], 'pec', rotate90origin=rotate90origin)
            edge(tx[0], tx[1] - dy, tx[2], tx[0], tx[1], tx[2], 'pec', rotate90origin=rotate90origin)
            edge(tx[0], tx[1] + dy, tx[2], tx[0], tx[1] + 2*dy, tx[2], 'pec', rotate90origin=rotate90origin)

    # metallic plate extension
    box(x + (casesize[0] / 2), y + casethickness, z + skidthickness, x + (casesize[0] / 2) + shieldthickness, y + casesize[1] - casethickness, z + skidthickness + metalmiddleplateheight, 'pec', rotate90origin=rotate90origin) # new

    if smooth_dec == 'yes':
        # Skid
        box(x, y, z, x + casesize[0], y + casesize[1], z + skidthickness - 0.002, 'hdpe', rotate90origin=rotate90origin) # new

    elif smooth_dec == 'no':
        # Skid
        box(x, y, z, x + casesize[0], y + casesize[1], z + skidthickness - 0.002, 'hdpe', 'n', rotate90origin=rotate90origin) # new

    # Excitation - Gaussian pulse
    print('#waveform: gaussian 1 {} myGaussian'.format(excitationfreq))
    if src_type == 'voltage_source':
        print('#voltage_source: y {} {} {} {} myGaussian'.format(tx[0], tx[1], tx[2], sourceresistance))
    elif src_type == 'transmission_line':
        print('#transmission_line: y {} {} {} {} myGaussian'.format(tx[0], tx[1], tx[2], sourceresistance))

    # Output point - receiver bowtie
    print('#waveform: gaussian 0 4e8 my_zero_src')

    if resolution == 0.001 or resolution == 0.0005:
        if src_type == 'transmission_line':
            print('#transmission_line: y {} {} {} {} my_zero_src'.format(tx[0] + 0.16, tx[1], tx[2], receiverresistance))
        elif src_type == 'voltage_source':
            print('#voltage_source: y {} {} {} {} my_zero_src'.format(tx[0] + 0.16, tx[1], tx[2], receiverresistance))
        print('#rx: {} {} {} {} {}'.format(tx[0] + 0.16, tx[1], tx[2], 'rx1', 'Ey'))

    elif resolution == 0.002:
        if src_type == 'transmission_line':
            print('#transmission_line: y {} {} {} {} my_zero_src'.format(tx[0] + 0.162, tx[1], tx[2], receiverresistance))
        elif src_type == 'voltage_source':
            print('#rx: {} {} {} {} {}'.format(tx[0] + 0.162, tx[1], tx[2], 'rx1', 'Ey'))
Beispiel #16
0
def process_python_include_code(inputfile, usernamespace):
    """Looks for and processes any Python code found in the input file. It will ignore any lines that are comments, i.e. begin with a double hash (##), and any blank lines. It will also ignore any lines that do not begin with a hash (#) after it has processed Python commands. It will also process any include commands and insert the contents of the included file at that location.

    Args:
        inputfile (str): Name of the input file to open.
        usernamespace (dict): Namespace that can be accessed by user in any Python code blocks in input file.

    Returns:
        processedlines (list): Input commands after Python processing.
    """

    with open(inputfile, 'r') as f:
        # Strip out any newline characters and comments that must begin with double hashes
        inputlines = [
            line.rstrip() for line in f
            if (not line.startswith('##') and line.rstrip('\n'))
        ]

    # List to hold final processed commands
    processedlines = []

    x = 0
    while (x < len(inputlines)):

        # Process any Python code
        if (inputlines[x].startswith('#python:')):

            # String to hold Python code to be executed
            pythoncode = ''
            x += 1
            while not inputlines[x].startswith('#end_python:'):
                # Add all code in current code block to string
                pythoncode += inputlines[x] + '\n'
                x += 1
                if x == len(inputlines):
                    raise CmdInputError(
                        'Cannot find the end of the Python code block, i.e. missing #end_python: command.'
                    )
            # Compile code for faster execution
            pythoncompiledcode = compile(pythoncode, '<string>', 'exec')
            # Redirect stdout to a text stream
            sys.stdout = result = StringIO()
            # Execute code block & make available only usernamespace
            exec(pythoncompiledcode, usernamespace)
            # String containing buffer of executed code
            codeout = result.getvalue().split('\n')
            result.close()

            # Reset stdio
            sys.stdout = sys.__stdout__

            # Separate commands from any other generated output
            hashcmds = []
            pythonstdout = []
            for line in codeout:
                if line.startswith('#'):
                    hashcmds.append(line + '\n')
                elif line:
                    pythonstdout.append(line)

            # Add commands to a list
            processedlines.extend(hashcmds)

            # Print any generated output that is not commands
            if pythonstdout:
                print(
                    'Python messages (from stdout): {}\n'.format(pythonstdout))

        # Process any include commands
        elif (inputlines[x].startswith('#include_file:')):
            includefile = inputlines[x].split()

            if len(includefile) != 2:
                raise CmdInputError(
                    '#include_file requires exactly one parameter')

            includefile = includefile[1]

            # See if file exists at specified path and if not try input file directory
            if not os.path.isfile(includefile):
                includefile = os.path.join(usernamespace['inputdirectory'],
                                           includefile)

            with open(includefile, 'r') as f:
                # Strip out any newline characters and comments that must begin with double hashes
                includelines = [
                    includeline.rstrip() + '\n' for includeline in f
                    if (not includeline.startswith('##')
                        and includeline.rstrip('\n'))
                ]

            # Add lines from include file to list
            processedlines.extend(includelines)

        # Add any other commands to list
        elif (inputlines[x].startswith('#')):
            # Add gprMax command to list
            inputlines[x] += ('\n')
            processedlines.append(inputlines[x])

        x += 1

    return processedlines
Beispiel #17
0
def process_singlecmds(singlecmds, G):
    """Checks the validity of command parameters and creates instances of classes of parameters.

    Args:
        singlecmds (dict): Commands that can only occur once in the model.
        G (class): Grid class instance - holds essential parameters describing the model.
    """

    # Check validity of command parameters in order needed
    # messages
    cmd = '#messages'
    if singlecmds[cmd] != 'None':
        tmp = singlecmds[cmd].split()
        if len(tmp) != 1:
            raise CmdInputError(cmd + ' requires exactly one parameter')
        if singlecmds[cmd].lower() == 'y':
            G.messages = True
        elif singlecmds[cmd].lower() == 'n':
            G.messages = False
        else:
            raise CmdInputError(cmd + ' requires input values of either y or n')

    # Title
    cmd = '#title'
    if singlecmds[cmd] != 'None':
        G.title = singlecmds[cmd]
        if G.messages:
            print('Model title: {}'.format(G.title))

    # Number of threads (OpenMP) to use
    cmd = '#num_threads'
    if sys.platform == 'darwin':
        os.environ['OMP_WAIT_POLICY'] = 'ACTIVE'  # What to do with threads when they are waiting; can drastically effect performance
    os.environ['OMP_DYNAMIC'] = 'FALSE'
    os.environ['OMP_PROC_BIND'] = 'TRUE'  # Bind threads to physical cores

    if singlecmds[cmd] != 'None':
        tmp = tuple(int(x) for x in singlecmds[cmd].split())
        if len(tmp) != 1:
            raise CmdInputError(cmd + ' requires exactly one parameter to specify the number of threads to use')
        if tmp[0] < 1:
            raise CmdInputError(cmd + ' requires the value to be an integer not less than one')
        G.nthreads = tmp[0]
        os.environ['OMP_NUM_THREADS'] = str(G.nthreads)
    elif os.environ.get('OMP_NUM_THREADS'):
        G.nthreads = int(os.environ.get('OMP_NUM_THREADS'))
    else:
        # Set number of threads to number of physical CPU cores, i.e. avoid hyperthreading with OpenMP
        G.nthreads = psutil.cpu_count(logical=False)
        os.environ['OMP_NUM_THREADS'] = str(G.nthreads)

    if G.messages:
        machineID, cpuID, osversion = get_machine_cpu_os()
        print('Number of threads: {} ({})'.format(G.nthreads, cpuID))
    if G.nthreads > psutil.cpu_count(logical=False):
        print(Fore.RED + 'WARNING: You have specified more threads ({}) than available physical CPU cores ({}). This may lead to degraded performance.'.format(G.nthreads, psutil.cpu_count(logical=False)) + Style.RESET_ALL)

    # Spatial discretisation
    cmd = '#dx_dy_dz'
    tmp = [float(x) for x in singlecmds[cmd].split()]
    if len(tmp) != 3:
        raise CmdInputError(cmd + ' requires exactly three parameters')
    if tmp[0] <= 0:
        raise CmdInputError(cmd + ' requires the x-direction spatial step to be greater than zero')
    if tmp[1] <= 0:
        raise CmdInputError(cmd + ' requires the y-direction spatial step to be greater than zero')
    if tmp[2] <= 0:
        raise CmdInputError(cmd + ' requires the z-direction spatial step to be greater than zero')
    G.dx = tmp[0]
    G.dy = tmp[1]
    G.dz = tmp[2]
    if G.messages:
        print('Spatial discretisation: {:g} x {:g} x {:g}m'.format(G.dx, G.dy, G.dz))

    # Domain
    cmd = '#domain'
    tmp = [float(x) for x in singlecmds[cmd].split()]
    if len(tmp) != 3:
        raise CmdInputError(cmd + ' requires exactly three parameters')
    G.nx = round_value(tmp[0] / G.dx)
    G.ny = round_value(tmp[1] / G.dy)
    G.nz = round_value(tmp[2] / G.dz)
    if G.nx == 0 or G.ny == 0 or G.nz == 0:
        raise CmdInputError(cmd + ' requires at least one cell in every dimension')
    if G.messages:
        print('Domain size: {:g} x {:g} x {:g}m ({:d} x {:d} x {:d} = {:g} cells)'.format(tmp[0], tmp[1], tmp[2], G.nx, G.ny, G.nz, (G.nx * G.ny * G.nz)))

    # Estimate memory (RAM) usage
    stdoverhead = 70e6
    floatarrays = (6 + 6 + 1) * (G.nx + 1) * (G.ny + 1) * (G.nz + 1) * np.dtype(floattype).itemsize  # 6 x field arrays + 6 x ID arrays + 1 x solid array
    rigidarray = (12 + 6) * (G.nx + 1) * (G.ny + 1) * (G.nz + 1) * np.dtype(np.int8).itemsize
    memestimate = stdoverhead + floatarrays + rigidarray
    if memestimate > psutil.virtual_memory().total:
        print(Fore.RED + 'WARNING: Estimated memory (RAM) required ~{} exceeds {} detected!\n'.format(human_size(memestimate), human_size(psutil.virtual_memory().total, a_kilobyte_is_1024_bytes=True)) + Style.RESET_ALL)
    if G.messages:
        print('Estimated memory (RAM) required: ~{} ({} detected)'.format(human_size(memestimate), human_size(psutil.virtual_memory().total, a_kilobyte_is_1024_bytes=True)))

    # Time step CFL limit (use either 2D or 3D) and default PML thickness
    if G.nx == 1:
        G.dt = 1 / (c * np.sqrt((1 / G.dy) * (1 / G.dy) + (1 / G.dz) * (1 / G.dz)))
        G.dimension = '2D'
        G.pmlthickness['xminus'] = 0
        G.pmlthickness['xplus'] = 0
    elif G.ny == 1:
        G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dz) * (1 / G.dz)))
        G.dimension = '2D'
        G.pmlthickness['yminus'] = 0
        G.pmlthickness['yplus'] = 0
    elif G.nz == 1:
        G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dy) * (1 / G.dy)))
        G.dimension = '2D'
        G.pmlthickness['zminus'] = 0
        G.pmlthickness['zplus'] = 0
    else:
        G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dy) * (1 / G.dy) + (1 / G.dz) * (1 / G.dz)))
        G.dimension = '3D'

    # Round down time step to nearest float with precision one less than hardware maximum. Avoids inadvertently exceeding the CFL due to binary representation of floating point number.
    G.dt = round_value(G.dt, decimalplaces=d.getcontext().prec - 1)

    if G.messages:
        print('Time step (at {} CFL limit): {:g} secs'.format(G.dimension, G.dt))

    # Time step stability factor
    cmd = '#time_step_stability_factor'
    if singlecmds[cmd] != 'None':
        tmp = tuple(float(x) for x in singlecmds[cmd].split())
        if len(tmp) != 1:
            raise CmdInputError(cmd + ' requires exactly one parameter')
        if tmp[0] <= 0 or tmp[0] > 1:
            raise CmdInputError(cmd + ' requires the value of the time step stability factor to be between zero and one')
        G.dt = G.dt * tmp[0]
        if G.messages:
            print('Time step (modified): {:g} secs'.format(G.dt))

    # Time window
    cmd = '#time_window'
    tmp = singlecmds[cmd].split()
    if len(tmp) != 1:
        raise CmdInputError(cmd + ' requires exactly one parameter to specify the time window. Either in seconds or number of iterations.')
    tmp = tmp[0].lower()

    # If number of iterations given
    try:
        tmp = int(tmp)
        G.timewindow = (tmp - 1) * G.dt
        G.iterations = tmp
    # If real floating point value given
    except:
        tmp = float(tmp)
        if tmp > 0:
            G.timewindow = tmp
            G.iterations = round_value((tmp / G.dt)) + 1
        else:
            raise CmdInputError(cmd + ' must have a value greater than zero')
    if G.messages:
        print('Time window: {:g} secs ({} iterations)'.format(G.timewindow, G.iterations))

    # PML
    cmd = '#pml_cells'
    if singlecmds[cmd] != 'None':
        tmp = singlecmds[cmd].split()
        if len(tmp) != 1 and len(tmp) != 6:
            raise CmdInputError(cmd + ' requires either one or six parameters')
        if len(tmp) == 1:
            for key in G.pmlthickness.keys():
                G.pmlthickness[key] = int(tmp[0])
        else:
            G.pmlthickness['xminus'] = int(tmp[0])
            G.pmlthickness['yminus'] = int(tmp[1])
            G.pmlthickness['zminus'] = int(tmp[2])
            G.pmlthickness['xplus'] = int(tmp[3])
            G.pmlthickness['yplus'] = int(tmp[4])
            G.pmlthickness['zplus'] = int(tmp[5])
    if 2 * G.pmlthickness['xminus'] >= G.nx or 2 * G.pmlthickness['yminus'] >= G.ny or 2 * G.pmlthickness['zminus'] >= G.nz or 2 * G.pmlthickness['xplus'] >= G.nx or 2 * G.pmlthickness['yplus'] >= G.ny or 2 * G.pmlthickness['zplus'] >= G.nz:
        raise CmdInputError(cmd + ' has too many cells for the domain size')

    # src_steps
    cmd = '#src_steps'
    if singlecmds[cmd] != 'None':
        tmp = singlecmds[cmd].split()
        if len(tmp) != 3:
            raise CmdInputError(cmd + ' requires exactly three parameters')
        G.srcsteps[0] = round_value(float(tmp[0]) / G.dx)
        G.srcsteps[1] = round_value(float(tmp[1]) / G.dy)
        G.srcsteps[2] = round_value(float(tmp[2]) / G.dz)
        if G.messages:
            print('Simple sources will step {:g}m, {:g}m, {:g}m for each model run.'.format(G.srcsteps[0] * G.dx, G.srcsteps[1] * G.dy, G.srcsteps[2] * G.dz))

    # rx_steps
    cmd = '#rx_steps'
    if singlecmds[cmd] != 'None':
        tmp = singlecmds[cmd].split()
        if len(tmp) != 3:
            raise CmdInputError(cmd + ' requires exactly three parameters')
        G.rxsteps[0] = round_value(float(tmp[0]) / G.dx)
        G.rxsteps[1] = round_value(float(tmp[1]) / G.dy)
        G.rxsteps[2] = round_value(float(tmp[2]) / G.dz)
        if G.messages:
            print('All receivers will step {:g}m, {:g}m, {:g}m for each model run.'.format(G.rxsteps[0] * G.dx, G.rxsteps[1] * G.dy, G.rxsteps[2] * G.dz))

    # Excitation file for user-defined source waveforms
    cmd = '#excitation_file'
    if singlecmds[cmd] != 'None':
        tmp = singlecmds[cmd].split()
        if len(tmp) != 1:
            raise CmdInputError(cmd + ' requires exactly one parameter')
        excitationfile = tmp[0]

        # See if file exists at specified path and if not try input file directory
        if not os.path.isfile(excitationfile):
            excitationfile = os.path.abspath(os.path.join(G.inputdirectory, excitationfile))

        # Get waveform names
        with open(excitationfile, 'r') as f:
            waveformIDs = f.readline().split()

        # Read all waveform values into an array
        waveformvalues = np.loadtxt(excitationfile, skiprows=1, dtype=floattype)

        for waveform in range(len(waveformIDs)):
            if any(x.ID == waveformIDs[waveform] for x in G.waveforms):
                raise CmdInputError('Waveform with ID {} already exists'.format(waveformIDs[waveform]))
            w = Waveform()
            w.ID = waveformIDs[waveform]
            w.type = 'user'
            if len(waveformvalues.shape) == 1:
                w.uservalues = waveformvalues[:]
            else:
                w.uservalues = waveformvalues[:, waveform]

            if G.messages:
                print('User waveform {} created.'.format(w.ID))

            G.waveforms.append(w)
Beispiel #18
0
    # fig.savefig(path + os.sep + savefile + '.png', dpi=150, format='png', 
    #             bbox_inches='tight', pad_inches=0.1)

    return plt


if __name__ == "__main__":

    # Parse command line arguments
    parser = argparse.ArgumentParser(description='Plots a B-scan image.', 
                                     usage='cd gprMax; python -m tools.plot_Bscan outputfile output')
    parser.add_argument('outputfile', help='name of output file including path')
    parser.add_argument('rx_component', help='name of output component to be plotted', 
                        choices=['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz', 'Ix', 'Iy', 'Iz'])
    args = parser.parse_args()

    # Open output file and read number of outputs (receivers)
    f = h5py.File(args.outputfile, 'r')
    nrx = f.attrs['nrx']
    f.close()

    # Check there are any receivers
    if nrx == 0:
        raise CmdInputError('No receivers found in {}'.format(args.outputfile))

    for rx in range(1, nrx + 1):
        outputdata, dt = get_output_data(args.outputfile, rx, args.rx_component)
        plthandle = mpl_plot(args.outputfile, outputdata, dt, rx, args.rx_component)

    plthandle.show()
Beispiel #19
0
        elif cmdname == '#geometry_vtk':
            # Syntax of old command: #geometry_vtk: x1 y1 z1 x2 y2 z2 dx dy dz filename type
            replacement = '#geometry_view: {} {} {} {} {} {} {} {} {} {} {}'.format(
                params[0], params[1], params[2], params[3], params[4],
                params[5], params[6], params[7], params[8], params[9],
                params[10])
            print("Command '{}', replaced with '{}'".format(
                inputlines[lindex], replacement))
            inputlines.pop(lindex)
            inputlines.insert(lindex, replacement)
            lindex += 1

        elif cmdname in ['#plane_wave', '#thin_wire', '#huygens_surface']:
            raise CmdInputError(
                "Command '{}' has not yet implemented in the new version of gprMax. For now please continue to use the old version."
                .format(inputlines[lindex]))

        else:
            lindex += 1

    else:
        lindex += 1

# Convert separate #line_source and associated #tx to #waveform and #hertzian_dipole
for source in linesources:
    params = source.split()
    if params[3] is badwaveforms:
        raise CmdInputError(
            "Waveform types {} are not compatible between new and old versions of gprMax."
            .format(''.join(badwaveforms)))
def process_singlecmds(singlecmds, G):
    """Checks the validity of command parameters and creates instances of classes of parameters.

    Args:
        singlecmds (dict): Commands that can only occur once in the model.
        G (class): Grid class instance - holds essential parameters describing the model.
    """

    # Check validity of command parameters in order needed
    # messages
    cmd = '#messages'
    if singlecmds[cmd] is not None:
        tmp = singlecmds[cmd].split()
        if len(tmp) != 1:
            raise CmdInputError(cmd + ' requires exactly one parameter')
        if singlecmds[cmd].lower() == 'y':
            G.messages = True
        elif singlecmds[cmd].lower() == 'n':
            G.messages = False
        else:
            raise CmdInputError(cmd + ' requires input values of either y or n')

    # Title
    cmd = '#title'
    if singlecmds[cmd] is not None:
        G.title = singlecmds[cmd]
        if G.messages:
            print('Model title: {}'.format(G.title))

    # Get information about host machine
    hostinfo = get_host_info()

    # Number of threads (OpenMP) to use
    cmd = '#num_threads'
    if sys.platform == 'darwin':
        os.environ['OMP_WAIT_POLICY'] = 'ACTIVE'  # Should waiting threads consume CPU power (can drastically effect performance)
    os.environ['OMP_DYNAMIC'] = 'FALSE'  # Number of threads may be adjusted by the run time environment to best utilize system resources
    os.environ['OMP_PLACES'] = 'cores'  # Each place corresponds to a single core (having one or more hardware threads)
    os.environ['OMP_PROC_BIND'] = 'TRUE'  # Bind threads to physical cores
    # os.environ['OMP_DISPLAY_ENV'] = 'TRUE' # Prints OMP version and environment variables (useful for debug)

    # Catch bug with Windows Subsystem for Linux (https://github.com/Microsoft/BashOnWindows/issues/785)
    if 'Microsoft' in G.hostinfo['osversion']:
        os.environ['KMP_AFFINITY'] = 'disabled'
        del os.environ['OMP_PLACES']
        del os.environ['OMP_PROC_BIND']

    if singlecmds[cmd] is not None:
        tmp = tuple(int(x) for x in singlecmds[cmd].split())
        if len(tmp) != 1:
            raise CmdInputError(cmd + ' requires exactly one parameter to specify the number of threads to use')
        if tmp[0] < 1:
            raise CmdInputError(cmd + ' requires the value to be an integer not less than one')
        G.nthreads = tmp[0]
        os.environ['OMP_NUM_THREADS'] = str(G.nthreads)
    elif os.environ.get('OMP_NUM_THREADS'):
        G.nthreads = int(os.environ.get('OMP_NUM_THREADS'))
    else:
        # Set number of threads to number of physical CPU cores
        G.nthreads = hostinfo['physicalcores']
        os.environ['OMP_NUM_THREADS'] = str(G.nthreads)

    if G.messages:
        print('Number of CPU (OpenMP) threads: {}'.format(G.nthreads))
    if G.nthreads > G.hostinfo['physicalcores']:
        print(Fore.RED + 'WARNING: You have specified more threads ({}) than available physical CPU cores ({}). This may lead to degraded performance.'.format(G.nthreads, hostinfo['physicalcores']) + Style.RESET_ALL)

    # Print information about any GPU in use
    if G.messages:
        if G.gpu is not None:
            print('GPU solving using: {} - {}'.format(G.gpu.deviceID, G.gpu.name))

    # Spatial discretisation
    cmd = '#dx_dy_dz'
    tmp = [float(x) for x in singlecmds[cmd].split()]
    if len(tmp) != 3:
        raise CmdInputError(cmd + ' requires exactly three parameters')
    if tmp[0] <= 0:
        raise CmdInputError(cmd + ' requires the x-direction spatial step to be greater than zero')
    if tmp[1] <= 0:
        raise CmdInputError(cmd + ' requires the y-direction spatial step to be greater than zero')
    if tmp[2] <= 0:
        raise CmdInputError(cmd + ' requires the z-direction spatial step to be greater than zero')
    G.dx = tmp[0]
    G.dy = tmp[1]
    G.dz = tmp[2]
    if G.messages:
        print('Spatial discretisation: {:g} x {:g} x {:g}m'.format(G.dx, G.dy, G.dz))

    # Domain
    cmd = '#domain'
    tmp = [float(x) for x in singlecmds[cmd].split()]
    if len(tmp) != 3:
        raise CmdInputError(cmd + ' requires exactly three parameters')
    G.nx = round_value(tmp[0] / G.dx)
    G.ny = round_value(tmp[1] / G.dy)
    G.nz = round_value(tmp[2] / G.dz)
    if G.nx == 0 or G.ny == 0 or G.nz == 0:
        raise CmdInputError(cmd + ' requires at least one cell in every dimension')
    if G.messages:
        print('Domain size: {:g} x {:g} x {:g}m ({:d} x {:d} x {:d} = {:g} cells)'.format(tmp[0], tmp[1], tmp[2], G.nx, G.ny, G.nz, (G.nx * G.ny * G.nz)))

    # Time step CFL limit (either 2D or 3D); switch off appropriate PMLs for 2D
    if G.nx == 1:
        G.dt = 1 / (c * np.sqrt((1 / G.dy) * (1 / G.dy) + (1 / G.dz) * (1 / G.dz)))
        G.mode = '2D TMx'
        G.pmlthickness['x0'] = 0
        G.pmlthickness['xmax'] = 0
    elif G.ny == 1:
        G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dz) * (1 / G.dz)))
        G.mode = '2D TMy'
        G.pmlthickness['y0'] = 0
        G.pmlthickness['ymax'] = 0
    elif G.nz == 1:
        G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dy) * (1 / G.dy)))
        G.mode = '2D TMz'
        G.pmlthickness['z0'] = 0
        G.pmlthickness['zmax'] = 0
    else:
        G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dy) * (1 / G.dy) + (1 / G.dz) * (1 / G.dz)))
        G.mode = '3D'

    # Round down time step to nearest float with precision one less than hardware maximum.
    # Avoids inadvertently exceeding the CFL due to binary representation of floating point number.
    G.dt = round_value(G.dt, decimalplaces=d.getcontext().prec - 1)

    if G.messages:
        print('Mode: {}'.format(G.mode))
        print('Time step (at CFL limit): {:g} secs'.format(G.dt))

    # Time step stability factor
    cmd = '#time_step_stability_factor'
    if singlecmds[cmd] is not None:
        tmp = tuple(float(x) for x in singlecmds[cmd].split())
        if len(tmp) != 1:
            raise CmdInputError(cmd + ' requires exactly one parameter')
        if tmp[0] <= 0 or tmp[0] > 1:
            raise CmdInputError(cmd + ' requires the value of the time step stability factor to be between zero and one')
        G.dt = G.dt * tmp[0]
        if G.messages:
            print('Time step (modified): {:g} secs'.format(G.dt))

    # Time window
    cmd = '#time_window'
    tmp = singlecmds[cmd].split()
    if len(tmp) != 1:
        raise CmdInputError(cmd + ' requires exactly one parameter to specify the time window. Either in seconds or number of iterations.')
    tmp = tmp[0].lower()

    # If number of iterations given
    # The +/- 1 used in calculating the number of iterations is to account for
    # the fact that the solver (iterations) loop runs from 0 to < G.iterations
    try:
        tmp = int(tmp)
        G.timewindow = (tmp - 1) * G.dt
        G.iterations = tmp
    # If real floating point value given
    except ValueError:
        tmp = float(tmp)
        if tmp > 0:
            G.timewindow = tmp
            G.iterations = int(np.ceil(tmp / G.dt)) + 1
        else:
            raise CmdInputError(cmd + ' must have a value greater than zero')
    if G.messages:
        print('Time window: {:g} secs ({} iterations)'.format(G.timewindow, G.iterations))

    # PML cells
    cmd = '#pml_cells'
    if singlecmds[cmd] is not None:
        tmp = singlecmds[cmd].split()
        if len(tmp) != 1 and len(tmp) != 6:
            raise CmdInputError(cmd + ' requires either one or six parameter(s)')
        if len(tmp) == 1:
            for key in G.pmlthickness.keys():
                G.pmlthickness[key] = int(tmp[0])
        else:
            G.pmlthickness['x0'] = int(tmp[0])
            G.pmlthickness['y0'] = int(tmp[1])
            G.pmlthickness['z0'] = int(tmp[2])
            G.pmlthickness['xmax'] = int(tmp[3])
            G.pmlthickness['ymax'] = int(tmp[4])
            G.pmlthickness['zmax'] = int(tmp[5])
    if 2 * G.pmlthickness['x0'] >= G.nx or 2 * G.pmlthickness['y0'] >= G.ny or 2 * G.pmlthickness['z0'] >= G.nz or 2 * G.pmlthickness['xmax'] >= G.nx or 2 * G.pmlthickness['ymax'] >= G.ny or 2 * G.pmlthickness['zmax'] >= G.nz:
        raise CmdInputError(cmd + ' has too many cells for the domain size')

    # PML formulation
    cmd = '#pml_formulation'
    if singlecmds[cmd] is not None:
        tmp = singlecmds[cmd].split()
        if len(tmp) != 1:
            raise CmdInputError(cmd + ' requires exactly one parameter')
        if singlecmds[cmd].upper() in PML.formulations:
            G.pmlformulation = singlecmds[cmd].upper()
        else:
            raise CmdInputError(cmd + ' PML formulation is not found')

    # src_steps
    cmd = '#src_steps'
    if singlecmds[cmd] is not None:
        tmp = singlecmds[cmd].split()
        if len(tmp) != 3:
            raise CmdInputError(cmd + ' requires exactly three parameters')
        G.srcsteps[0] = round_value(float(tmp[0]) / G.dx)
        G.srcsteps[1] = round_value(float(tmp[1]) / G.dy)
        G.srcsteps[2] = round_value(float(tmp[2]) / G.dz)
        if G.messages:
            print('Simple sources will step {:g}m, {:g}m, {:g}m for each model run.'.format(G.srcsteps[0] * G.dx, G.srcsteps[1] * G.dy, G.srcsteps[2] * G.dz))

    # rx_steps
    cmd = '#rx_steps'
    if singlecmds[cmd] is not None:
        tmp = singlecmds[cmd].split()
        if len(tmp) != 3:
            raise CmdInputError(cmd + ' requires exactly three parameters')
        G.rxsteps[0] = round_value(float(tmp[0]) / G.dx)
        G.rxsteps[1] = round_value(float(tmp[1]) / G.dy)
        G.rxsteps[2] = round_value(float(tmp[2]) / G.dz)
        if G.messages:
            print('All receivers will step {:g}m, {:g}m, {:g}m for each model run.'.format(G.rxsteps[0] * G.dx, G.rxsteps[1] * G.dy, G.rxsteps[2] * G.dz))

    # Excitation file for user-defined source waveforms
    cmd = '#excitation_file'
    if singlecmds[cmd] is not None:
        tmp = singlecmds[cmd].split()
        if len(tmp) != 1 and len(tmp) != 3:
            raise CmdInputError(cmd + ' requires either one or three parameter(s)')
        excitationfile = tmp[0]

        # Optional parameters passed directly to scipy.interpolate.interp1d
        kwargs = dict()
        if len(tmp) > 1:
            kwargs['kind'] = tmp[1]
            kwargs['fill_value'] = tmp[2]
        else:
            args, varargs, keywords, defaults = inspect.getargspec(interpolate.interp1d)
            kwargs = dict(zip(reversed(args), reversed(defaults)))

        # See if file exists at specified path and if not try input file directory
        if not os.path.isfile(excitationfile):
            excitationfile = os.path.abspath(os.path.join(G.inputdirectory, excitationfile))

        if G.messages:
            print('\nExcitation file: {}'.format(excitationfile))

        # Get waveform names
        with open(excitationfile, 'r') as f:
            waveformIDs = f.readline().split()

        # Read all waveform values into an array
        waveformvalues = np.loadtxt(excitationfile, skiprows=1, dtype=floattype)

        # Time array (if specified) for interpolation, otherwise use simulation time
        if waveformIDs[0].lower() == 'time':
            waveformIDs = waveformIDs[1:]
            waveformtime = waveformvalues[:, 0]
            waveformvalues = waveformvalues[:, 1:]
            timestr = 'user-defined time array'
        else:
            waveformtime = np.arange(0, G.timewindow + G.dt, G.dt)
            timestr = 'simulation time array'

        for waveform in range(len(waveformIDs)):
            if any(x.ID == waveformIDs[waveform] for x in G.waveforms):
                raise CmdInputError('Waveform with ID {} already exists'.format(waveformIDs[waveform]))
            w = Waveform()
            w.ID = waveformIDs[waveform]
            w.type = 'user'

            # Select correct column of waveform values depending on array shape
            singlewaveformvalues = waveformvalues[:] if len(waveformvalues.shape) == 1 else waveformvalues[:, waveform]

            # Truncate waveform array if it is longer than time array
            if len(singlewaveformvalues) > len(waveformtime):
                singlewaveformvalues = singlewaveformvalues[:len(waveformtime)]
            # Zero-pad end of waveform array if it is shorter than time array
            elif len(singlewaveformvalues) < len(waveformtime):
                tmp = np.zeros(len(waveformtime))
                tmp[:len(singlewaveformvalues)] = singlewaveformvalues
                singlewaveformvalues = tmp

            # Interpolate waveform values
            w.userfunc = interpolate.interp1d(waveformtime, singlewaveformvalues, **kwargs)

            if G.messages:
                print('User waveform {} created using {} and, if required, interpolation parameters (kind: {}, fill value: {}).'.format(w.ID, timestr, kwargs['kind'], kwargs['fill_value']))

            G.waveforms.append(w)

    # Set the output directory
    cmd = '#output_dir'
    if singlecmds[cmd] is not None:
        outputdir = singlecmds[cmd]
        G.outputdirectory = outputdir
Beispiel #21
0
import argparse
import os
import sys

import h5py
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

from gprMax.exceptions import CmdInputError
from gprMax.receivers import Rx
from gprMax.utilities import fft_power

f = h5py.File(filename, 'r')
nrx = f.attrs['nrx']
dt = f.attrs['dt']
iterations = f.attrs['Iterations']
time = np.linspace(0, (iterations - 1) * dt, num=iterations)

# Check there are any receivers
if nrx == 0:
    raise CmdInputError('No receivers found in {}'.format(filename))

for rx in range(1, nrx + 1):
    path = '/rxs/rx' + str(rx) + '/'
    availableoutputs = list(f[path].keys())

f.close()

return plt
Beispiel #22
0
                    default=False,
                    help='plot FFT (single output must be specified)')
args = parser.parse_args()

# Open output file and read some attributes
file = args.outputfile
f = h5py.File(file, 'r')
nrx = f.attrs['nrx']
dt = f.attrs['dt']
iterations = f.attrs['Iterations']
time = np.linspace(0, 1, iterations)
time *= (iterations * dt)

# Check there are any receivers
if nrx == 0:
    raise CmdInputError('No receivers found in {}'.format(file))

# Check for single output component when doing a FFT
if args.fft:
    if not len(args.outputs) == 1:
        raise CmdInputError(
            'A single output must be specified when using the -fft option')

# New plot for each receiver
for rx in range(1, nrx + 1):
    path = '/rxs/rx' + str(rx) + '/'
    availableoutputs = list(f[path].keys())

    # If only a single output is required, create one subplot
    if len(args.outputs) == 1:
Beispiel #23
0
def antenna_like_GSSI_1500(x,
                           y,
                           z,
                           resolution=0.001,
                           rotate90=False,
                           **kwargs):
    """Inserts a description of an antenna similar to the GSSI 1.5GHz antenna. Can be used with 1mm (default) or 2mm spatial resolution. The external dimensions of the antenna are 170x108x45mm. One output point is defined between the arms of the receiever bowtie. The bowties are aligned with the y axis so the output is the y component of the electric field.

    Args:
        x, y, z (float): Coordinates of a location in the model to insert the antenna. Coordinates are relative to the geometric centre of the antenna in the x-y plane and the bottom of the antenna skid in the z direction.
        resolution (float): Spatial resolution for the antenna model.
        rotate90 (bool): Rotate model 90 degrees CCW in xy plane.
        kwargs (dict): Optional variables, e.g. can be fed from an optimisation process.
    """

    # Antenna geometry properties
    casesize = (0.170, 0.108, 0.043)
    casethickness = 0.002
    shieldthickness = 0.002
    foamsurroundthickness = 0.003
    pcbthickness = 0.002
    skidthickness = 0.004
    bowtiebase = 0.022
    bowtieheight = 0.014
    patchheight = 0.015

    # Set origin for rotation to geometric centre of antenna in x-y plane if required
    if rotate90:
        rotate90origin = (x, y)
        output = 'Ex'
    else:
        rotate90origin = ()
        output = 'Ey'

    # Unknown properties
    if kwargs:
        excitationfreq = kwargs['excitationfreq']
        sourceresistance = kwargs['sourceresistance']
        absorberEr = kwargs['absorberEr']
        absorbersig = kwargs['absorbersig']
        rxres = 50
    else:
        # excitationfreq = 1.5e9 # GHz
        # sourceresistance = 50 # Ohms
        # absorberEr = 1.7
        # absorbersig = 0.59

        # Values from http://hdl.handle.net/1842/4074
        excitationfreq = 1.71e9
        # sourceresistance = 4
        sourceresistance = 230  # Correction for old (< 123) GprMax3D bug
        absorberEr = 1.58
        absorbersig = 0.428
        rxres = 925  # Resistance at Rx bowtie

    x = x - (casesize[0] / 2)
    y = y - (casesize[1] / 2)

    # Coordinates of source excitation point in antenna
    tx = x + 0.114, y + 0.053, z + skidthickness

    if resolution == 0.001:
        dx = 0.001
        dy = 0.001
        dz = 0.001
    elif resolution == 0.002:
        dx = 0.002
        dy = 0.002
        dz = 0.002
        foamsurroundthickness = 0.002
        patchheight = 0.016
        tx = x + 0.112, y + 0.052, z + skidthickness
    else:
        raise CmdInputError(
            'This antenna module can only be used with a spatial discretisation of 1mm or 2mm'
        )

    # Material definitions
    material(absorberEr, absorbersig, 1, 0, 'absorber')
    material(3, 0, 1, 0, 'pcb')
    material(2.35, 0, 1, 0, 'hdpe')
    material(3, (1 / rxres) * (dy / (dx * dz)), 1, 0, 'rxres')

    # Antenna geometry
    # Plastic case
    box(x,
        y,
        z + skidthickness,
        x + casesize[0],
        y + casesize[1],
        z + skidthickness + casesize[2],
        'hdpe',
        rotate90origin=rotate90origin)
    box(x + casethickness,
        y + casethickness,
        z + skidthickness,
        x + casesize[0] - casethickness,
        y + casesize[1] - casethickness,
        z + skidthickness + casesize[2] - casethickness,
        'free_space',
        rotate90origin=rotate90origin)

    # Metallic enclosure
    box(x + 0.025,
        y + casethickness,
        z + skidthickness,
        x + casesize[0] - 0.025,
        y + casesize[1] - casethickness,
        z + skidthickness + 0.027,
        'pec',
        rotate90origin=rotate90origin)

    # Absorber material, and foam (modelled as PCB material) around edge of absorber
    box(x + 0.025 + shieldthickness,
        y + casethickness + shieldthickness,
        z + skidthickness,
        x + 0.025 + shieldthickness + 0.057,
        y + casesize[1] - casethickness - shieldthickness,
        z + skidthickness + 0.027 - shieldthickness - 0.001,
        'pcb',
        rotate90origin=rotate90origin)
    box(x + 0.025 + shieldthickness + foamsurroundthickness,
        y + casethickness + shieldthickness + foamsurroundthickness,
        z + skidthickness,
        x + 0.025 + shieldthickness + 0.057 - foamsurroundthickness,
        y + casesize[1] - casethickness - shieldthickness -
        foamsurroundthickness,
        z + skidthickness + 0.027 - shieldthickness,
        'absorber',
        rotate90origin=rotate90origin)
    box(x + 0.086,
        y + casethickness + shieldthickness,
        z + skidthickness,
        x + 0.086 + 0.057,
        y + casesize[1] - casethickness - shieldthickness,
        z + skidthickness + 0.027 - shieldthickness - 0.001,
        'pcb',
        rotate90origin=rotate90origin)
    box(x + 0.086 + foamsurroundthickness,
        y + casethickness + shieldthickness + foamsurroundthickness,
        z + skidthickness,
        x + 0.086 + 0.057 - foamsurroundthickness,
        y + casesize[1] - casethickness - shieldthickness -
        foamsurroundthickness,
        z + skidthickness + 0.027 - shieldthickness,
        'absorber',
        rotate90origin=rotate90origin)

    # PCB
    box(x + 0.025 + shieldthickness + foamsurroundthickness,
        y + casethickness + shieldthickness + foamsurroundthickness,
        z + skidthickness,
        x + 0.086 - shieldthickness - foamsurroundthickness,
        y + casesize[1] - casethickness - shieldthickness -
        foamsurroundthickness,
        z + skidthickness + pcbthickness,
        'pcb',
        rotate90origin=rotate90origin)
    box(x + 0.086 + foamsurroundthickness,
        y + casethickness + shieldthickness + foamsurroundthickness,
        z + skidthickness,
        x + 0.086 + 0.057 - foamsurroundthickness,
        y + casesize[1] - casethickness - shieldthickness -
        foamsurroundthickness,
        z + skidthickness + pcbthickness,
        'pcb',
        rotate90origin=rotate90origin)

    # PCB components
    if resolution == 0.001:
        # Rx & Tx bowties
        a = 0
        b = 0
        while b < 13:
            plate(x + 0.045 + a * dx,
                  y + 0.039 + b * dx,
                  z + skidthickness,
                  x + 0.065 - a * dx,
                  y + 0.039 + b * dx + dy,
                  z + skidthickness,
                  'pec',
                  rotate90origin=rotate90origin)
            plate(x + 0.045 + a * dx,
                  y + 0.067 - b * dx,
                  z + skidthickness,
                  x + 0.065 - a * dx,
                  y + 0.067 - b * dx + dy,
                  z + skidthickness,
                  'pec',
                  rotate90origin=rotate90origin)
            plate(x + 0.104 + a * dx,
                  y + 0.039 + b * dx,
                  z + skidthickness,
                  x + 0.124 - a * dx,
                  y + 0.039 + b * dx + dy,
                  z + skidthickness,
                  'pec',
                  rotate90origin=rotate90origin)
            plate(x + 0.104 + a * dx,
                  y + 0.067 - b * dx,
                  z + skidthickness,
                  x + 0.124 - a * dx,
                  y + 0.067 - b * dx + dy,
                  z + skidthickness,
                  'pec',
                  rotate90origin=rotate90origin)
            b += 1
            if a == 2 or a == 4 or a == 7:
                plate(x + 0.045 + a * dx,
                      y + 0.039 + b * dx,
                      z + skidthickness,
                      x + 0.065 - a * dx,
                      y + 0.039 + b * dx + dy,
                      z + skidthickness,
                      'pec',
                      rotate90origin=rotate90origin)
                plate(x + 0.045 + a * dx,
                      y + 0.067 - b * dx,
                      z + skidthickness,
                      x + 0.065 - a * dx,
                      y + 0.067 - b * dx + dy,
                      z + skidthickness,
                      'pec',
                      rotate90origin=rotate90origin)
                plate(x + 0.104 + a * dx,
                      y + 0.039 + b * dx,
                      z + skidthickness,
                      x + 0.124 - a * dx,
                      y + 0.039 + b * dx + dy,
                      z + skidthickness,
                      'pec',
                      rotate90origin=rotate90origin)
                plate(x + 0.104 + a * dx,
                      y + 0.067 - b * dx,
                      z + skidthickness,
                      x + 0.124 - a * dx,
                      y + 0.067 - b * dx + dy,
                      z + skidthickness,
                      'pec',
                      rotate90origin=rotate90origin)
                b += 1
            a += 1
        # Rx extension section (upper y)
        plate(x + 0.044,
              y + 0.068,
              z + skidthickness,
              x + 0.044 + bowtiebase,
              y + 0.068 + patchheight,
              z + skidthickness,
              'pec',
              rotate90origin=rotate90origin)
        # Tx extension section (upper y)
        plate(x + 0.103,
              y + 0.068,
              z + skidthickness,
              x + 0.103 + bowtiebase,
              y + 0.068 + patchheight,
              z + skidthickness,
              'pec',
              rotate90origin=rotate90origin)

        # Edges that represent wire between bowtie halves in 1mm model
        edge(tx[0] - 0.059,
             tx[1] - dy,
             tx[2],
             tx[0] - 0.059,
             tx[1],
             tx[2],
             'pec',
             rotate90origin=rotate90origin)
        edge(tx[0] - 0.059,
             tx[1] + dy,
             tx[2],
             tx[0] - 0.059,
             tx[1] + 0.002,
             tx[2],
             'pec',
             rotate90origin=rotate90origin)
        edge(tx[0],
             tx[1] - dy,
             tx[2],
             tx[0],
             tx[1],
             tx[2],
             'pec',
             rotate90origin=rotate90origin)
        edge(tx[0],
             tx[1] + dz,
             tx[2],
             tx[0],
             tx[1] + 0.002,
             tx[2],
             'pec',
             rotate90origin=rotate90origin)

    elif resolution == 0.002:
        # Rx & Tx bowties
        for a in range(0, 6):
            plate(x + 0.044 + a * dx,
                  y + 0.040 + a * dx,
                  z + skidthickness,
                  x + 0.066 - a * dx,
                  y + 0.040 + a * dx + dy,
                  z + skidthickness,
                  'pec',
                  rotate90origin=rotate90origin)
            plate(x + 0.044 + a * dx,
                  y + 0.064 - a * dx,
                  z + skidthickness,
                  x + 0.066 - a * dx,
                  y + 0.064 - a * dx + dy,
                  z + skidthickness,
                  'pec',
                  rotate90origin=rotate90origin)
            plate(x + 0.103 + a * dx,
                  y + 0.040 + a * dx,
                  z + skidthickness,
                  x + 0.125 - a * dx,
                  y + 0.040 + a * dx + dy,
                  z + skidthickness,
                  'pec',
                  rotate90origin=rotate90origin)
            plate(x + 0.103 + a * dx,
                  y + 0.064 - a * dx,
                  z + skidthickness,
                  x + 0.125 - a * dx,
                  y + 0.064 - a * dx + dy,
                  z + skidthickness,
                  'pec',
                  rotate90origin=rotate90origin)
            # Rx extension section (upper y)
            plate(x + 0.044,
                  y + 0.066,
                  z + skidthickness,
                  x + 0.044 + bowtiebase,
                  y + 0.066 + patchheight,
                  z + skidthickness,
                  'pec',
                  rotate90origin=rotate90origin)
            # Tx extension section (upper y)
            plate(x + 0.103,
                  y + 0.066,
                  z + skidthickness,
                  x + 0.103 + bowtiebase,
                  y + 0.066 + patchheight,
                  z + skidthickness,
                  'pec',
                  rotate90origin=rotate90origin)

    # Rx extension section (lower y)
    plate(x + 0.044,
          y + 0.024,
          z + skidthickness,
          x + 0.044 + bowtiebase,
          y + 0.024 + patchheight,
          z + skidthickness,
          'pec',
          rotate90origin=rotate90origin)
    # Tx extension section (lower y)
    plate(x + 0.103,
          y + 0.024,
          z + skidthickness,
          x + 0.103 + bowtiebase,
          y + 0.024 + patchheight,
          z + skidthickness,
          'pec',
          rotate90origin=rotate90origin)

    # Skid
    box(x,
        y,
        z,
        x + casesize[0],
        y + casesize[1],
        z + skidthickness,
        'hdpe',
        rotate90origin=rotate90origin)

    # Geometry views
    # geometry_view(x - dx, y - dy, z - dz, x + casesize[0] + dx, y + casesize[1] + dy, z + skidthickness + casesize[2] + dz, dx, dy, dz, 'antenna_like_GSSI_1500')
    # geometry_view(x, y, z, x + casesize[0], y + casesize[1], z + 0.010, dx, dy, dz, 'antenna_like_GSSI_1500_pcb', type='f')

    # Excitation - custom pulse
    # print('#excitation_file: {}'.format(os.path.join(moduledirectory, 'GSSIgausspulse1.txt')))
    # print('#transmission_line: y {} {} {} {} GSSIgausspulse1'.format(tx[0], tx[1], tx[2], sourceresistance))

    # Excitation - Gaussian pulse
    print('#waveform: gaussian 1 {} myGaussian'.format(excitationfreq))
    transmission_line('y',
                      tx[0],
                      tx[1],
                      tx[2],
                      sourceresistance,
                      'myGaussian',
                      dxdy=(resolution, resolution),
                      rotate90origin=rotate90origin)

    # Output point - receiver bowtie
    if resolution == 0.001:
        edge(tx[0] - 0.059,
             tx[1],
             tx[2],
             tx[0] - 0.059,
             tx[1] + dy,
             tx[2],
             'rxres',
             rotate90origin=rotate90origin)
        rx(tx[0] - 0.059,
           tx[1],
           tx[2],
           identifier='rxbowtie',
           to_save=[output],
           polarisation='y',
           dxdy=(resolution, resolution),
           rotate90origin=rotate90origin)
    elif resolution == 0.002:
        edge(tx[0] - 0.058,
             tx[1],
             tx[2],
             tx[0] - 0.058,
             tx[1] + dy,
             tx[2],
             'rxres',
             rotate90origin=rotate90origin)
        rx(tx[0] - 0.058,
           tx[1],
           tx[2],
           identifier='rxbowtie',
           to_save=[output],
           polarisation='y',
           dxdy=(resolution, resolution),
           rotate90origin=rotate90origin)
Beispiel #24
0
def process_singlecmds(singlecmds, G):
    """Checks the validity of command parameters and creates instances of classes of parameters.

    Args:
        singlecmds (dict): Commands that can only occur once in the model.
        G (class): Grid class instance - holds essential parameters describing the model.
    """

    # Check validity of command parameters in order needed
    # messages
    cmd = '#messages'
    if singlecmds[cmd] is not None:
        tmp = singlecmds[cmd].split()
        if len(tmp) != 1:
            raise CmdInputError(cmd + ' requires exactly one parameter')
        if singlecmds[cmd].lower() == 'y':
            G.messages = True
        elif singlecmds[cmd].lower() == 'n':
            G.messages = False
        else:
            raise CmdInputError(cmd +
                                ' requires input values of either y or n')

    # Title
    cmd = '#title'
    if singlecmds[cmd] is not None:
        G.title = singlecmds[cmd]
        if G.messages:
            print('Model title: {}'.format(G.title))

    # Get information about host machine
    hostinfo = get_host_info()

    # Number of threads (OpenMP) to use
    cmd = '#num_threads'
    if sys.platform == 'darwin':
        os.environ[
            'OMP_WAIT_POLICY'] = 'ACTIVE'  # Should waiting threads consume CPU power (can drastically effect performance)
    os.environ[
        'OMP_DYNAMIC'] = 'FALSE'  # Number of threads may be adjusted by the run time environment to best utilize system resources
    os.environ[
        'OMP_PLACES'] = 'cores'  # Each place corresponds to a single core (having one or more hardware threads)
    os.environ['OMP_PROC_BIND'] = 'TRUE'  # Bind threads to physical cores
    # os.environ['OMP_DISPLAY_ENV'] = 'TRUE' # Prints OMP version and environment variables (useful for debug)

    # Catch bug with Windows Subsystem for Linux (https://github.com/Microsoft/BashOnWindows/issues/785)
    if 'Microsoft' in hostinfo['osversion']:
        os.environ['KMP_AFFINITY'] = 'disabled'
        del os.environ['OMP_PLACES']
        del os.environ['OMP_PROC_BIND']

    if singlecmds[cmd] is not None:
        tmp = tuple(int(x) for x in singlecmds[cmd].split())
        if len(tmp) != 1:
            raise CmdInputError(
                cmd +
                ' requires exactly one parameter to specify the number of threads to use'
            )
        if tmp[0] < 1:
            raise CmdInputError(
                cmd + ' requires the value to be an integer not less than one')
        G.nthreads = tmp[0]
        os.environ['OMP_NUM_THREADS'] = str(G.nthreads)
    elif os.environ.get('OMP_NUM_THREADS'):
        G.nthreads = int(os.environ.get('OMP_NUM_THREADS'))
    else:
        # Set number of threads to number of physical CPU cores
        G.nthreads = hostinfo['physicalcores']
        os.environ['OMP_NUM_THREADS'] = str(G.nthreads)

    if G.messages:
        print('Number of CPU (OpenMP) threads: {}'.format(G.nthreads))
    if G.nthreads > hostinfo['physicalcores']:
        print(
            Fore.RED +
            'WARNING: You have specified more threads ({}) than available physical CPU cores ({}). This may lead to degraded performance.'
            .format(G.nthreads, hostinfo['physicalcores']) + Style.RESET_ALL)

    # Spatial discretisation
    cmd = '#dx_dy_dz'
    tmp = [float(x) for x in singlecmds[cmd].split()]
    if len(tmp) != 3:
        raise CmdInputError(cmd + ' requires exactly three parameters')
    if tmp[0] <= 0:
        raise CmdInputError(
            cmd +
            ' requires the x-direction spatial step to be greater than zero')
    if tmp[1] <= 0:
        raise CmdInputError(
            cmd +
            ' requires the y-direction spatial step to be greater than zero')
    if tmp[2] <= 0:
        raise CmdInputError(
            cmd +
            ' requires the z-direction spatial step to be greater than zero')
    G.dx = tmp[0]
    G.dy = tmp[1]
    G.dz = tmp[2]
    if G.messages:
        print('Spatial discretisation: {:g} x {:g} x {:g}m'.format(
            G.dx, G.dy, G.dz))

    # Domain
    cmd = '#domain'
    tmp = [float(x) for x in singlecmds[cmd].split()]
    if len(tmp) != 3:
        raise CmdInputError(cmd + ' requires exactly three parameters')
    G.nx = round_value(tmp[0] / G.dx)
    G.ny = round_value(tmp[1] / G.dy)
    G.nz = round_value(tmp[2] / G.dz)
    if G.nx == 0 or G.ny == 0 or G.nz == 0:
        raise CmdInputError(cmd +
                            ' requires at least one cell in every dimension')
    if G.messages:
        print(
            'Domain size: {:g} x {:g} x {:g}m ({:d} x {:d} x {:d} = {:g} cells)'
            .format(tmp[0], tmp[1], tmp[2], G.nx, G.ny, G.nz,
                    (G.nx * G.ny * G.nz)))

    # Estimate memory (RAM) usage
    memestimate = memory_usage(G)
    # Check if model can be built and/or run on host
    if memestimate > hostinfo['ram']:
        raise GeneralError(
            'Estimated memory (RAM) required ~{} exceeds {} detected!\n'.
            format(human_size(memestimate),
                   human_size(hostinfo['ram'], a_kilobyte_is_1024_bytes=True)))
    if G.messages:
        print('Estimated memory (RAM) required: ~{}'.format(
            human_size(memestimate)))

    # Time step CFL limit (use either 2D or 3D) and default PML thickness
    if G.nx == 1:
        G.dt = 1 / (c * np.sqrt((1 / G.dy) * (1 / G.dy) + (1 / G.dz) *
                                (1 / G.dz)))
        G.dimension = '2D'
        G.pmlthickness['x0'] = 0
        G.pmlthickness['xmax'] = 0
    elif G.ny == 1:
        G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dz) *
                                (1 / G.dz)))
        G.dimension = '2D'
        G.pmlthickness['y0'] = 0
        G.pmlthickness['ymax'] = 0
    elif G.nz == 1:
        G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dy) *
                                (1 / G.dy)))
        G.dimension = '2D'
        G.pmlthickness['z0'] = 0
        G.pmlthickness['zmax'] = 0
    else:
        G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dy) *
                                (1 / G.dy) + (1 / G.dz) * (1 / G.dz)))
        G.dimension = '3D'

    # Round down time step to nearest float with precision one less than hardware maximum. Avoids inadvertently exceeding the CFL due to binary representation of floating point number.
    G.dt = round_value(G.dt, decimalplaces=d.getcontext().prec - 1)

    if G.messages:
        print('Time step (at {} CFL limit): {:g} secs'.format(
            G.dimension, G.dt))

    # Time step stability factor
    cmd = '#time_step_stability_factor'
    if singlecmds[cmd] is not None:
        tmp = tuple(float(x) for x in singlecmds[cmd].split())
        if len(tmp) != 1:
            raise CmdInputError(cmd + ' requires exactly one parameter')
        if tmp[0] <= 0 or tmp[0] > 1:
            raise CmdInputError(
                cmd +
                ' requires the value of the time step stability factor to be between zero and one'
            )
        G.dt = G.dt * tmp[0]
        if G.messages:
            print('Time step (modified): {:g} secs'.format(G.dt))

    # Time window
    cmd = '#time_window'
    tmp = singlecmds[cmd].split()
    if len(tmp) != 1:
        raise CmdInputError(
            cmd +
            ' requires exactly one parameter to specify the time window. Either in seconds or number of iterations.'
        )
    tmp = tmp[0].lower()

    # If number of iterations given
    try:
        tmp = int(tmp)
        G.timewindow = (tmp - 1) * G.dt
        G.iterations = tmp
    # If real floating point value given
    except:
        tmp = float(tmp)
        if tmp > 0:
            G.timewindow = tmp
            G.iterations = round_value((tmp / G.dt)) + 1
        else:
            raise CmdInputError(cmd + ' must have a value greater than zero')
    if G.messages:
        print('Time window: {:g} secs ({} iterations)'.format(
            G.timewindow, G.iterations))

    # PML
    cmd = '#pml_cells'
    if singlecmds[cmd] is not None:
        tmp = singlecmds[cmd].split()
        if len(tmp) != 1 and len(tmp) != 6:
            raise CmdInputError(cmd + ' requires either one or six parameters')
        if len(tmp) == 1:
            for key in G.pmlthickness.keys():
                G.pmlthickness[key] = int(tmp[0])
        else:
            G.pmlthickness['x0'] = int(tmp[0])
            G.pmlthickness['y0'] = int(tmp[1])
            G.pmlthickness['z0'] = int(tmp[2])
            G.pmlthickness['xmax'] = int(tmp[3])
            G.pmlthickness['ymax'] = int(tmp[4])
            G.pmlthickness['zmax'] = int(tmp[5])
    if 2 * G.pmlthickness['x0'] >= G.nx or 2 * G.pmlthickness[
            'y0'] >= G.ny or 2 * G.pmlthickness[
                'z0'] >= G.nz or 2 * G.pmlthickness[
                    'xmax'] >= G.nx or 2 * G.pmlthickness[
                        'ymax'] >= G.ny or 2 * G.pmlthickness['zmax'] >= G.nz:
        raise CmdInputError(cmd + ' has too many cells for the domain size')

    # src_steps
    cmd = '#src_steps'
    if singlecmds[cmd] is not None:
        tmp = singlecmds[cmd].split()
        if len(tmp) != 3:
            raise CmdInputError(cmd + ' requires exactly three parameters')
        G.srcsteps[0] = round_value(float(tmp[0]) / G.dx)
        G.srcsteps[1] = round_value(float(tmp[1]) / G.dy)
        G.srcsteps[2] = round_value(float(tmp[2]) / G.dz)
        if G.messages:
            print(
                'Simple sources will step {:g}m, {:g}m, {:g}m for each model run.'
                .format(G.srcsteps[0] * G.dx, G.srcsteps[1] * G.dy,
                        G.srcsteps[2] * G.dz))

    # rx_steps
    cmd = '#rx_steps'
    if singlecmds[cmd] is not None:
        tmp = singlecmds[cmd].split()
        if len(tmp) != 3:
            raise CmdInputError(cmd + ' requires exactly three parameters')
        G.rxsteps[0] = round_value(float(tmp[0]) / G.dx)
        G.rxsteps[1] = round_value(float(tmp[1]) / G.dy)
        G.rxsteps[2] = round_value(float(tmp[2]) / G.dz)
        if G.messages:
            print(
                'All receivers will step {:g}m, {:g}m, {:g}m for each model run.'
                .format(G.rxsteps[0] * G.dx, G.rxsteps[1] * G.dy,
                        G.rxsteps[2] * G.dz))

    # Excitation file for user-defined source waveforms
    cmd = '#excitation_file'
    if singlecmds[cmd] is not None:
        tmp = singlecmds[cmd].split()
        if len(tmp) != 1:
            raise CmdInputError(cmd + ' requires exactly one parameter')
        excitationfile = tmp[0]

        # See if file exists at specified path and if not try input file directory
        if not os.path.isfile(excitationfile):
            excitationfile = os.path.abspath(
                os.path.join(G.inputdirectory, excitationfile))

        # Get waveform names
        with open(excitationfile, 'r') as f:
            waveformIDs = f.readline().split()

        # Read all waveform values into an array
        waveformvalues = np.loadtxt(excitationfile,
                                    skiprows=1,
                                    dtype=floattype)

        for waveform in range(len(waveformIDs)):
            if any(x.ID == waveformIDs[waveform] for x in G.waveforms):
                raise CmdInputError(
                    'Waveform with ID {} already exists'.format(
                        waveformIDs[waveform]))
            w = Waveform()
            w.ID = waveformIDs[waveform]
            w.type = 'user'
            if len(waveformvalues.shape) == 1:
                w.uservalues = waveformvalues[:]
            else:
                w.uservalues = waveformvalues[:, waveform]

            if G.messages:
                print('User waveform {} created.'.format(w.ID))

            G.waveforms.append(w)
Beispiel #25
0
def antenna_like_MALA_1200(x,
                           y,
                           z,
                           resolution=0.001,
                           rotate90=False,
                           **kwargs):
    """Inserts a description of an antenna similar to the MALA 1.2GHz antenna. Can be used with 1mm (default) or 2mm spatial resolution. The external dimensions of the antenna are 184x109x46mm. One output point is defined between the arms of the receiever bowtie. The bowties are aligned with the y axis so the output is the y component of the electric field.

    Args:
        x, y, z (float): Coordinates of a location in the model to insert the antenna. Coordinates are relative to the geometric centre of the antenna in the x-y plane and the bottom of the antenna skid in the z direction.
        resolution (float): Spatial resolution for the antenna model.
        rotate90 (bool): Rotate model 90 degrees CCW in xy plane.
        kwargs (dict): Optional variables, e.g. can be fed from an optimisation process.
    """

    # Antenna geometry properties
    casesize = (0.184, 0.109, 0.040)
    casethickness = 0.002
    cavitysize = (0.062, 0.062, 0.037)
    cavitythickness = 0.001
    pcbthickness = 0.002
    polypropylenethickness = 0.003
    hdpethickness = 0.003
    skidthickness = 0.006
    bowtieheight = 0.025

    # Set origin for rotation to geometric centre of antenna in x-y plane if required
    if rotate90:
        rotate90origin = (x, y)
        output = 'Ex'
    else:
        rotate90origin = ()
        output = 'Ey'

    # Unknown properties
    if kwargs:
        excitationfreq = kwargs['excitationfreq']
        sourceresistance = kwargs['sourceresistance']
        absorberEr = kwargs['absorberEr']
        absorbersig = kwargs['absorbersig']
    else:
        # Values from http://hdl.handle.net/1842/4074
        excitationfreq = 0.978e9
        sourceresistance = 1000
        absorberEr = 6.49
        absorbersig = 0.252

    x = x - (casesize[0] / 2)
    y = y - (casesize[1] / 2)

    # Coordinates of source excitation point in antenna
    tx = x + 0.063, y + 0.052, z + skidthickness

    if resolution == 0.001:
        dx = 0.001
        dy = 0.001
        dz = 0.001
    elif resolution == 0.002:
        dx = 0.002
        dy = 0.002
        dz = 0.002
        cavitysize = (0.062, 0.062, 0.036)
        cavitythickness = 0.002
        polypropylenethickness = 0.002
        hdpethickness = 0.004
        bowtieheight = 0.024
        tx = x + 0.062, y + 0.052, z + skidthickness
    else:
        raise CmdInputError(
            'This antenna module can only be used with a spatial resolution of 1mm or 2mm'
        )

    # SMD resistors - 3 on each Tx & Rx bowtie arm
    txres = 470  # Ohms
    txrescellupper = txres / 3  # Resistor over 3 cells
    txsigupper = (
        (1 / txrescellupper) *
        (dy /
         (dx * dz))) / 2  # Divide by number of parallel edges per resistor
    txrescelllower = txres / 4  # Resistor over 4 cells
    txsiglower = (
        (1 / txrescelllower) *
        (dy /
         (dx * dz))) / 2  # Divide by number of parallel edges per resistor
    rxres = 150  # Ohms
    rxrescellupper = rxres / 3  # Resistor over 3 cells
    rxsigupper = (
        (1 / rxrescellupper) *
        (dy /
         (dx * dz))) / 2  # Divide by number of parallel edges per resistor
    rxrescelllower = rxres / 4  # Resistor over 4 cells
    rxsiglower = (
        (1 / rxrescelllower) *
        (dy /
         (dx * dz))) / 2  # Divide by number of parallel edges per resistor

    # Material definitions
    material(absorberEr, absorbersig, 1, 0, 'absorber')
    material(3, 0, 1, 0, 'pcb')
    material(2.35, 0, 1, 0, 'hdpe')
    material(2.26, 0, 1, 0, 'polypropylene')
    material(3, txsiglower, 1, 0, 'txreslower')
    material(3, txsigupper, 1, 0, 'txresupper')
    material(3, rxsiglower, 1, 0, 'rxreslower')
    material(3, rxsigupper, 1, 0, 'rxresupper')

    # Antenna geometry
    # Shield - metallic enclosure
    box(x,
        y,
        z + skidthickness,
        x + casesize[0],
        y + casesize[1],
        z + skidthickness + casesize[2],
        'pec',
        rotate90origin=rotate90origin)
    box(x + 0.020,
        y + casethickness,
        z + skidthickness,
        x + 0.100,
        y + casesize[1] - casethickness,
        z + skidthickness + casethickness,
        'free_space',
        rotate90origin=rotate90origin)
    box(x + 0.100,
        y + casethickness,
        z + skidthickness,
        x + casesize[0] - casethickness,
        y + casesize[1] - casethickness,
        z + skidthickness + casethickness,
        'free_space',
        rotate90origin=rotate90origin)

    # Absorber material
    box(x + 0.020,
        y + casethickness,
        z + skidthickness,
        x + 0.100,
        y + casesize[1] - casethickness,
        z + skidthickness + casesize[2] - casethickness,
        'absorber',
        rotate90origin=rotate90origin)
    box(x + 0.100,
        y + casethickness,
        z + skidthickness,
        x + casesize[0] - casethickness,
        y + casesize[1] - casethickness,
        z + skidthickness + casesize[2] - casethickness,
        'absorber',
        rotate90origin=rotate90origin)

    # Shield - cylindrical sections
    cylinder(x + 0.055,
             y + casesize[1] - 0.008,
             z + skidthickness,
             x + 0.055,
             y + casesize[1] - 0.008,
             z + skidthickness + casesize[2] - casethickness,
             0.008,
             'pec',
             rotate90origin=rotate90origin)
    cylinder(x + 0.055,
             y + 0.008,
             z + skidthickness,
             x + 0.055,
             y + 0.008,
             z + skidthickness + casesize[2] - casethickness,
             0.008,
             'pec',
             rotate90origin=rotate90origin)
    cylinder(x + 0.147,
             y + casesize[1] - 0.008,
             z + skidthickness,
             x + 0.147,
             y + casesize[1] - 0.008,
             z + skidthickness + casesize[2] - casethickness,
             0.008,
             'pec',
             rotate90origin=rotate90origin)
    cylinder(x + 0.147,
             y + 0.008,
             z + skidthickness,
             x + 0.147,
             y + 0.008,
             z + skidthickness + casesize[2] - casethickness,
             0.008,
             'pec',
             rotate90origin=rotate90origin)
    cylinder(x + 0.055,
             y + casesize[1] - 0.008,
             z + skidthickness,
             x + 0.055,
             y + casesize[1] - 0.008,
             z + skidthickness + casesize[2] - casethickness,
             0.007,
             'free_space',
             rotate90origin=rotate90origin)
    cylinder(x + 0.055,
             y + 0.008,
             z + skidthickness,
             x + 0.055,
             y + 0.008,
             z + skidthickness + casesize[2] - casethickness,
             0.007,
             'free_space',
             rotate90origin=rotate90origin)
    cylinder(x + 0.147,
             y + casesize[1] - 0.008,
             z + skidthickness,
             x + 0.147,
             y + casesize[1] - 0.008,
             z + skidthickness + casesize[2] - casethickness,
             0.007,
             'free_space',
             rotate90origin=rotate90origin)
    cylinder(x + 0.147,
             y + 0.008,
             z + skidthickness,
             x + 0.147,
             y + 0.008,
             z + skidthickness + casesize[2] - casethickness,
             0.007,
             'free_space',
             rotate90origin=rotate90origin)
    box(x + 0.054,
        y + casesize[1] - 0.016,
        z + skidthickness,
        x + 0.056,
        y + casesize[1] - 0.014,
        z + skidthickness + casesize[2] - casethickness,
        'free_space',
        rotate90origin=rotate90origin)
    box(x + 0.054,
        y + 0.014,
        z + skidthickness,
        x + 0.056,
        y + 0.016,
        z + skidthickness + casesize[2] - casethickness,
        'free_space',
        rotate90origin=rotate90origin)
    box(x + 0.146,
        y + casesize[1] - 0.016,
        z + skidthickness,
        x + 0.148,
        y + casesize[1] - 0.014,
        z + skidthickness + casesize[2] - casethickness,
        'free_space',
        rotate90origin=rotate90origin)
    box(x + 0.146,
        y + 0.014,
        z + skidthickness,
        x + 0.148,
        y + 0.016,
        z + skidthickness + casesize[2] - casethickness,
        'free_space',
        rotate90origin=rotate90origin)

    # PCB
    box(x + 0.020,
        y + 0.018,
        z + skidthickness,
        x + casesize[0] - casethickness,
        y + casesize[1] - 0.018,
        z + skidthickness + pcbthickness,
        'pcb',
        rotate90origin=rotate90origin)

    # Shield - Tx & Rx cavities
    box(x + 0.032,
        y + 0.022,
        z + skidthickness,
        x + 0.032 + cavitysize[0],
        y + 0.022 + cavitysize[1],
        z + skidthickness + cavitysize[2],
        'pec',
        rotate90origin=rotate90origin)
    box(x + 0.032 + cavitythickness,
        y + 0.022 + cavitythickness,
        z + skidthickness,
        x + 0.032 + cavitysize[0] - cavitythickness,
        y + 0.022 + cavitysize[1] - cavitythickness,
        z + skidthickness + cavitysize[2],
        'absorber',
        rotate90origin=rotate90origin)
    box(x + 0.108,
        y + 0.022,
        z + skidthickness,
        x + 0.108 + cavitysize[0],
        y + 0.022 + cavitysize[1],
        z + skidthickness + cavitysize[2],
        'pec',
        rotate90origin=rotate90origin)
    box(x + 0.108 + cavitythickness,
        y + 0.022 + cavitythickness,
        z + skidthickness,
        x + 0.108 + cavitysize[0] - cavitythickness,
        y + 0.022 + cavitysize[1] - cavitythickness,
        z + skidthickness + cavitysize[2],
        'free_space',
        rotate90origin=rotate90origin)

    # Shield - Tx & Rx cavities - joining strips
    box(x + 0.032 + cavitysize[0],
        y + 0.022 + cavitysize[1] - 0.006,
        z + skidthickness + cavitysize[2] - casethickness,
        x + 0.108,
        y + 0.022 + cavitysize[1],
        z + skidthickness + cavitysize[2],
        'pec',
        rotate90origin=rotate90origin)
    box(x + 0.032 + cavitysize[0],
        y + 0.022,
        z + skidthickness + cavitysize[2] - casethickness,
        x + 0.108,
        y + 0.022 + 0.006,
        z + skidthickness + cavitysize[2],
        'pec',
        rotate90origin=rotate90origin)

    # PCB - replace bits chopped by TX & Rx cavities
    box(x + 0.032 + cavitythickness,
        y + 0.022 + cavitythickness,
        z + skidthickness,
        x + 0.032 + cavitysize[0] - cavitythickness,
        y + 0.022 + cavitysize[1] - cavitythickness,
        z + skidthickness + pcbthickness,
        'pcb',
        rotate90origin=rotate90origin)
    box(x + 0.108 + cavitythickness,
        y + 0.022 + cavitythickness,
        z + skidthickness,
        x + 0.108 + cavitysize[0] - cavitythickness,
        y + 0.022 + cavitysize[1] - cavitythickness,
        z + skidthickness + pcbthickness,
        'pcb',
        rotate90origin=rotate90origin)

    # PCB components
    # Tx bowtie
    if resolution == 0.001:
        triangle(tx[0],
                 tx[1] - 0.001,
                 tx[2],
                 tx[0] - 0.026,
                 tx[1] - bowtieheight - 0.001,
                 tx[2],
                 tx[0] + 0.026,
                 tx[1] - bowtieheight - 0.001,
                 tx[2],
                 0,
                 'pec',
                 rotate90origin=rotate90origin)
        edge(tx[0],
             tx[1] - 0.001,
             tx[2],
             tx[0],
             tx[1],
             tx[2],
             'pec',
             rotate90origin=rotate90origin)
        triangle(tx[0],
                 tx[1] + 0.002,
                 tx[2],
                 tx[0] - 0.026,
                 tx[1] + bowtieheight + 0.002,
                 tx[2],
                 tx[0] + 0.026,
                 tx[1] + bowtieheight + 0.002,
                 tx[2],
                 0,
                 'pec',
                 rotate90origin=rotate90origin)
        edge(tx[0],
             tx[1] + 0.001,
             tx[2],
             tx[0],
             tx[1] + 0.002,
             tx[2],
             'pec',
             rotate90origin=rotate90origin)
    elif resolution == 0.002:
        triangle(tx[0],
                 tx[1],
                 tx[2],
                 tx[0] - 0.026,
                 tx[1] - bowtieheight,
                 tx[2],
                 tx[0] + 0.026,
                 tx[1] - bowtieheight,
                 tx[2],
                 0,
                 'pec',
                 rotate90origin=rotate90origin)
        triangle(tx[0],
                 tx[1] + 0.002,
                 tx[2],
                 tx[0] - 0.026,
                 tx[1] + bowtieheight + 0.002,
                 tx[2],
                 tx[0] + 0.026,
                 tx[1] + bowtieheight + 0.002,
                 tx[2],
                 0,
                 'pec',
                 rotate90origin=rotate90origin)

    # Rx bowtie
    if resolution == 0.001:
        triangle(tx[0] + 0.076,
                 tx[1] - 0.001,
                 tx[2],
                 tx[0] + 0.076 - 0.026,
                 tx[1] - bowtieheight - 0.001,
                 tx[2],
                 tx[0] + 0.076 + 0.026,
                 tx[1] - bowtieheight - 0.001,
                 tx[2],
                 0,
                 'pec',
                 rotate90origin=rotate90origin)
        edge(tx[0] + 0.076,
             tx[1] - 0.001,
             tx[2],
             tx[0] + 0.076,
             tx[1],
             tx[2],
             'pec',
             rotate90origin=rotate90origin)
        triangle(tx[0] + 0.076,
                 tx[1] + 0.002,
                 tx[2],
                 tx[0] + 0.076 - 0.026,
                 tx[1] + bowtieheight + 0.002,
                 tx[2],
                 tx[0] + 0.076 + 0.026,
                 tx[1] + bowtieheight + 0.002,
                 tx[2],
                 0,
                 'pec',
                 rotate90origin=rotate90origin)
        edge(tx[0] + 0.076,
             tx[1] + 0.001,
             tx[2],
             tx[0] + 0.076,
             tx[1] + 0.002,
             tx[2],
             'pec',
             rotate90origin=rotate90origin)
    elif resolution == 0.002:
        triangle(tx[0] + 0.076,
                 tx[1],
                 tx[2],
                 tx[0] + 0.076 - 0.026,
                 tx[1] - bowtieheight,
                 tx[2],
                 tx[0] + 0.076 + 0.026,
                 tx[1] - bowtieheight,
                 tx[2],
                 0,
                 'pec',
                 rotate90origin=rotate90origin)
        triangle(tx[0] + 0.076,
                 tx[1] + 0.002,
                 tx[2],
                 tx[0] + 0.076 - 0.026,
                 tx[1] + bowtieheight + 0.002,
                 tx[2],
                 tx[0] + 0.076 + 0.026,
                 tx[1] + bowtieheight + 0.002,
                 tx[2],
                 0,
                 'pec',
                 rotate90origin=rotate90origin)

    # Tx surface mount resistors (lower y coordinate)
    if resolution == 0.001:
        edge(tx[0] - 0.023,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] - 0.023,
             tx[1] - bowtieheight - dy,
             tx[2],
             'txreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] - 0.023 + dx,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] - 0.023 + dx,
             tx[1] - bowtieheight - dy,
             tx[2],
             'txreslower',
             rotate90origin=rotate90origin)
        edge(tx[0],
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0],
             tx[1] - bowtieheight - dy,
             tx[2],
             'txreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] + dx,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] + dx,
             tx[1] - bowtieheight - dy,
             tx[2],
             'txreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.022,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] + 0.022,
             tx[1] - bowtieheight - dy,
             tx[2],
             'txreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.022 + dx,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] + 0.022 + dx,
             tx[1] - bowtieheight - dy,
             tx[2],
             'txreslower',
             rotate90origin=rotate90origin)
    elif resolution == 0.002:
        edge(tx[0] - 0.023,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] - 0.023,
             tx[1] - bowtieheight,
             tx[2],
             'txreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] - 0.023 + dx,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] - 0.023 + dx,
             tx[1] - bowtieheight,
             tx[2],
             'txreslower',
             rotate90origin=rotate90origin)
        edge(tx[0],
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0],
             tx[1] - bowtieheight,
             tx[2],
             'txreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] + dx,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] + dx,
             tx[1] - bowtieheight,
             tx[2],
             'txreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.020,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] + 0.020,
             tx[1] - bowtieheight,
             tx[2],
             'txreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.020 + dx,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] + 0.020 + dx,
             tx[1] - bowtieheight,
             tx[2],
             'txreslower',
             rotate90origin=rotate90origin)

    # Tx surface mount resistors (upper y coordinate)
    if resolution == 0.001:
        edge(tx[0] - 0.023,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] - 0.023,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'txresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] - 0.023 + dx,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] - 0.023 + dx,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'txresupper',
             rotate90origin=rotate90origin)
        edge(tx[0],
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0],
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'txresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] + dx,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] + dx,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'txresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.022,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] + 0.022,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'txresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.022 + dx,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] + 0.022 + dx,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'txresupper',
             rotate90origin=rotate90origin)
    elif resolution == 0.002:
        edge(tx[0] - 0.023,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] - 0.023,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'txresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] - 0.023 + dx,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] - 0.023 + dx,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'txresupper',
             rotate90origin=rotate90origin)
        edge(tx[0],
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0],
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'txresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] + dx,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] + dx,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'txresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.020,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] + 0.020,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'txresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.020 + dx,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] + 0.020 + dx,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'txresupper',
             rotate90origin=rotate90origin)

    # Rx surface mount resistors (lower y coordinate)
    if resolution == 0.001:
        edge(tx[0] - 0.023 + 0.076,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] - 0.023 + 0.076,
             tx[1] - bowtieheight - dy,
             tx[2],
             'rxreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] - 0.023 + dx + 0.076,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] - 0.023 + dx + 0.076,
             tx[1] - bowtieheight - dy,
             tx[2],
             'rxreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.076,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] + 0.076,
             tx[1] - bowtieheight - dy,
             tx[2],
             'rxreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] + dx + 0.076,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] + dx + 0.076,
             tx[1] - bowtieheight - dy,
             tx[2],
             'rxreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.022 + 0.076,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] + 0.022 + 0.076,
             tx[1] - bowtieheight - dy,
             tx[2],
             'rxreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.022 + dx + 0.076,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] + 0.022 + dx + 0.076,
             tx[1] - bowtieheight - dy,
             tx[2],
             'rxreslower',
             rotate90origin=rotate90origin)
    elif resolution == 0.002:
        edge(tx[0] - 0.023 + 0.076,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] - 0.023 + 0.076,
             tx[1] - bowtieheight,
             tx[2],
             'rxreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] - 0.023 + dx + 0.076,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] - 0.023 + dx + 0.076,
             tx[1] - bowtieheight,
             tx[2],
             'rxreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.076,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] + 0.076,
             tx[1] - bowtieheight,
             tx[2],
             'rxreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] + dx + 0.076,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] + dx + 0.076,
             tx[1] - bowtieheight,
             tx[2],
             'rxreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.020 + 0.076,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] + 0.020 + 0.076,
             tx[1] - bowtieheight,
             tx[2],
             'rxreslower',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.020 + dx + 0.076,
             tx[1] - bowtieheight - 0.004,
             tx[2],
             tx[0] + 0.020 + dx + 0.076,
             tx[1] - bowtieheight,
             tx[2],
             'rxreslower',
             rotate90origin=rotate90origin)

    # Rx surface mount resistors (upper y coordinate)
    if resolution == 0.001:
        edge(tx[0] - 0.023 + 0.076,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] - 0.023 + 0.076,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'rxresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] - 0.023 + dx + 0.076,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] - 0.023 + dx + 0.076,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'rxresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.076,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] + 0.076,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'rxresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] + dx + 0.076,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] + dx + 0.076,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'rxresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.022 + 0.076,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] + 0.022 + 0.076,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'rxresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.022 + dx + 0.076,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] + 0.022 + dx + 0.076,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'rxresupper',
             rotate90origin=rotate90origin)
    elif resolution == 0.002:
        edge(tx[0] - 0.023 + 0.076,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] - 0.023 + 0.076,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'rxresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] - 0.023 + dx + 0.076,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] - 0.023 + dx + 0.076,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'rxresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.076,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] + 0.076,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'rxresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] + dx + 0.076,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] + dx + 0.076,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'rxresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.020 + 0.076,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] + 0.020 + 0.076,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'rxresupper',
             rotate90origin=rotate90origin)
        edge(tx[0] + 0.020 + dx + 0.076,
             tx[1] + bowtieheight + 0.002,
             tx[2],
             tx[0] + 0.020 + dx + 0.076,
             tx[1] + bowtieheight + 0.006,
             tx[2],
             'rxresupper',
             rotate90origin=rotate90origin)

    # Skid
    box(x,
        y,
        z,
        x + casesize[0],
        y + casesize[1],
        z + polypropylenethickness,
        'polypropylene',
        rotate90origin=rotate90origin)
    box(x,
        y,
        z + polypropylenethickness,
        x + casesize[0],
        y + casesize[1],
        z + polypropylenethickness + hdpethickness,
        'hdpe',
        rotate90origin=rotate90origin)

    # Geometry views
    # geometry_view(x - dx, y - dy, z - dz, x + casesize[0] + dx, y + casesize[1] + dy, z + casesize[2] + skidthickness + dz, dx, dy, dz, 'antenna_like_MALA_1200')
    # geometry_view(x, y, z, x + casesize[0], y + casesize[1], z + 0.010, dx, dy, dz, 'antenna_like_MALA_1200_pcb', type='f')

    # Excitation
    print('#waveform: gaussian 1.0 {} myGaussian'.format(excitationfreq))
    voltage_source('y',
                   tx[0],
                   tx[1],
                   tx[2],
                   sourceresistance,
                   'myGaussian',
                   dxdy=(resolution, resolution),
                   rotate90origin=rotate90origin)

    # Output point - receiver bowtie
    rx(tx[0] + 0.076,
       tx[1],
       tx[2],
       identifier='rxbowtie',
       to_save=[output],
       polarisation='y',
       dxdy=(resolution, resolution),
       rotate90origin=rotate90origin)
Beispiel #26
0
# Model results
f = h5py.File(args.modelfile, 'r')
path = '/rxs/rx1/'
availablecomponents = list(f[path].keys())

# Check for polarity of output and if requested output is in file
if args.output[0][0] == 'm':
    polarity = -1
    args.outputs[0] = args.output[0][1:]
else:
    polarity = 1

if args.output[0] not in availablecomponents:
    raise CmdInputError(
        '{} output requested to plot, but the available output for receiver 1 is {}'
        .format(args.output[0], ', '.join(availablecomponents)))

floattype = f[path + args.output[0]].dtype
iterations = f.attrs['Iterations']
dt = f.attrs['dt']
model = np.zeros(iterations, dtype=floattype)
model = f[path + args.output[0]][:] * polarity
model /= np.amax(np.abs(model))
timemodel = np.linspace(0, 1, iterations)
timemodel *= (iterations * dt)
f.close()

# Find location of maximum value from model
modelmax = np.where(np.abs(model) == 1)[0][0]
def python_code_blocks(inputfile, usernamespace):
    """Looks for and processes any Python code found in the input file. It will ignore any lines that are comments, i.e. begin with a double hash (##), and any blank lines. It will also ignore any lines that do not begin with a hash (#) after it has processed Python commands.
        
    Args:
        inputfile (str): Name of the input file to open.
        usernamespace (dict): Namespace that can be accessed by user in any Python code blocks in input file.
        
    Returns:
        processedlines (list): Input commands after Python processing.
    """

    with open(inputfile, 'r') as f:
        # Strip out any newline characters and comments that must begin with double hashes
        inputlines = [
            line.rstrip() for line in f
            if (not line.startswith('##') and line.rstrip('\n'))
        ]

    # List to hold final processed commands
    processedlines = []

    x = 0
    while (x < len(inputlines)):
        if (inputlines[x].startswith('#python:')):
            # String to hold Python code to be executed
            pythoncode = ''
            x += 1
            while not inputlines[x].startswith('#end_python:'):
                # Add all code in current code block to string
                pythoncode += inputlines[x] + '\n'
                x += 1
                if x == len(inputlines):
                    raise CmdInputError(
                        'Cannot find the end of the Python code block, i.e. missing #end_python: command.'
                    )
            # Compile code for faster execution
            pythoncompiledcode = compile(pythoncode, '<string>', 'exec')
            # Redirect stdio to a ListStream
            sys.stdout = codeout = ListStream()
            # Execute code block & make available only usernamespace
            exec(pythoncompiledcode, usernamespace)

            # Now strip out any lines that don't begin with a hash command
            codeproc = [
                line + ('\n') for line in codeout.data
                if (line.startswith('#'))
            ]

            # Add processed Python code to list
            processedlines.extend(codeproc)

        elif (inputlines[x].startswith('#')):
            # Add gprMax command to list
            inputlines[x] += ('\n')
            processedlines.append(inputlines[x])

        x += 1

    sys.stdout = sys.__stdout__  # Reset stdio

    return processedlines