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. """ # Check if coordinates are within the bounds of the grid def check_coordinates(x, y, z, name=''): try: G.within_bounds(x=x, y=y, z=z) except ValueError as err: s = "'{}: {} ' {} {}-coordinate is not within the model domain".format(cmdname, ' '.join(tmp), name, err.args[0]) raise CmdInputError(s) # 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 maximum amplitude scaling {: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 polarisation = tmp[0].lower() if polarisation not in ('x', 'y', 'z'): raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' polarisation must be x, y, or z') xcoord = G.calculate_coord('x', tmp[1]) ycoord = G.calculate_coord('y', tmp[2]) zcoord = G.calculate_coord('z', tmp[3]) resistance = float(tmp[4]) check_coordinates(xcoord, ycoord, zcoord) if xcoord < G.pmlthickness['xminus'] or xcoord > G.nx - G.pmlthickness['xplus'] or ycoord < G.pmlthickness['yminus'] or ycoord > G.ny - G.pmlthickness['yplus'] or zcoord < G.pmlthickness['zminus'] or zcoord > G.nz - G.pmlthickness['zplus']: print(Fore.RED + "WARNING: '" + cmdname + ': ' + ' '.join(tmp) + "'" + ' sources and receivers should not normally be positioned within the PML.' + Style.RESET_ALL) 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 = polarisation v.xcoord = xcoord v.ycoord = ycoord v.zcoord = zcoord v.ID = v.__class__.__name__ + '(' + str(v.xcoord) + ',' + str(v.ycoord) + ',' + str(v.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 else: v.stop = stop 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 polarisation = tmp[0].lower() if polarisation not in ('x', 'y', 'z'): raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' polarisation must be x, y, or z') xcoord = G.calculate_coord('x', tmp[1]) ycoord = G.calculate_coord('y', tmp[2]) zcoord = G.calculate_coord('z', tmp[3]) check_coordinates(xcoord, ycoord, zcoord) if xcoord < G.pmlthickness['xminus'] or xcoord > G.nx - G.pmlthickness['xplus'] or ycoord < G.pmlthickness['yminus'] or ycoord > G.ny - G.pmlthickness['yplus'] or zcoord < G.pmlthickness['zminus'] or zcoord > G.nz - G.pmlthickness['zplus']: print(Fore.RED + "WARNING: '" + cmdname + ': ' + ' '.join(tmp) + "'" + ' sources and receivers should not normally be positioned within the PML.' + Style.RESET_ALL) # 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 = polarisation # Set length of dipole to grid size in polaristion direction if h.polarisation == 'x': h.dl = G.dx elif h.polarisation == 'y': h.dl = G.dy elif h.polarisation == 'z': h.dl = G.dz h.xcoord = xcoord h.ycoord = ycoord h.zcoord = zcoord h.xcoordorigin = xcoord h.ycoordorigin = ycoord h.zcoordorigin = zcoord h.ID = h.__class__.__name__ + '(' + str(h.xcoord) + ',' + str(h.ycoord) + ',' + str(h.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 else: h.stop = stop 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: if G.dimension == '2D': print('Hertzian dipole is a line source in 2D 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)) else: 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 polarisation = tmp[0].lower() if polarisation not in ('x', 'y', 'z'): raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' polarisation must be x, y, or z') xcoord = G.calculate_coord('x', tmp[1]) ycoord = G.calculate_coord('y', tmp[2]) zcoord = G.calculate_coord('z', tmp[3]) check_coordinates(xcoord, ycoord, zcoord) if xcoord < G.pmlthickness['xminus'] or xcoord > G.nx - G.pmlthickness['xplus'] or ycoord < G.pmlthickness['yminus'] or ycoord > G.ny - G.pmlthickness['yplus'] or zcoord < G.pmlthickness['zminus'] or zcoord > G.nz - G.pmlthickness['zplus']: print(Fore.RED + "WARNING: '" + cmdname + ': ' + ' '.join(tmp) + "'" + ' sources and receivers should not normally be positioned within the PML.' + Style.RESET_ALL) # 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 = polarisation m.xcoord = xcoord m.ycoord = ycoord m.zcoord = zcoord m.xcoordorigin = xcoord m.ycoordorigin = ycoord m.zcoordorigin = zcoord m.ID = m.__class__.__name__ + '(' + str(m.xcoord) + ',' + str(m.ycoord) + ',' + str(m.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 else: m.stop = stop 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 polarisation = tmp[0].lower() if polarisation not in ('x', 'y', 'z'): raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' polarisation must be x, y, or z') xcoord = G.calculate_coord('x', tmp[1]) ycoord = G.calculate_coord('y', tmp[2]) zcoord = G.calculate_coord('z', tmp[3]) resistance = float(tmp[4]) check_coordinates(xcoord, ycoord, zcoord) if xcoord < G.pmlthickness['xminus'] or xcoord > G.nx - G.pmlthickness['xplus'] or ycoord < G.pmlthickness['yminus'] or ycoord > G.ny - G.pmlthickness['yplus'] or zcoord < G.pmlthickness['zminus'] or zcoord > G.nz - G.pmlthickness['zplus']: print(Fore.RED + "WARNING: '" + cmdname + ': ' + ' '.join(tmp) + "'" + ' sources and receivers should not normally be positioned within the PML.' + Style.RESET_ALL) 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 = polarisation t.xcoord = xcoord t.ycoord = ycoord t.zcoord = zcoord t.ID = t.__class__.__name__ + '(' + str(t.xcoord) + ',' + str(t.ycoord) + ',' + str(t.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 else: t.stop = stop 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) check_coordinates(xcoord, ycoord, zcoord) if xcoord < G.pmlthickness['xminus'] or xcoord > G.nx - G.pmlthickness['xplus'] or ycoord < G.pmlthickness['yminus'] or ycoord > G.ny - G.pmlthickness['yplus'] or zcoord < G.pmlthickness['zminus'] or zcoord > G.nz - G.pmlthickness['zplus']: print(Fore.RED + "WARNING: '" + cmdname + ': ' + ' '.join(tmp) + "'" + ' sources and receivers should not normally be positioned within the PML.' + Style.RESET_ALL) r = Rx() r.xcoord = xcoord r.ycoord = ycoord r.zcoord = zcoord r.xcoordorigin = xcoord r.ycoordorigin = ycoord r.zcoordorigin = 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.ID = r.__class__.__name__ + '(' + str(r.xcoord) + ',' + str(r.ycoord) + ',' + str(r.zcoord) + ')' for key in Rx.defaultoutputs: r.outputs[key] = np.zeros(G.iterations, dtype=floattype) else: r.ID = tmp[3] # Check and add field output names for field in tmp[4::]: if field in Rx.availableoutputs: r.outputs[field] = np.zeros(G.iterations, dtype=floattype) 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 component(s) {} created.'.format(r.xcoord * G.dx, r.ycoord * G.dy, r.zcoord * G.dz, ', '.join(r.outputs))) G.rxs.append(r) # Receiver array cmdname = '#rx_array' 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 = G.calculate_coord('x', tmp[0]) ys = G.calculate_coord('y', tmp[1]) zs = G.calculate_coord('z', tmp[2]) xf = G.calculate_coord('x', tmp[3]) yf = G.calculate_coord('y', tmp[4]) zf = G.calculate_coord('z', tmp[5]) dx = G.calculate_coord('x', tmp[6]) dy = G.calculate_coord('y', tmp[7]) dz = G.calculate_coord('z', tmp[8]) check_coordinates(xs, ys, zs, name='lower') check_coordinates(xf, yf, zf, name='upper') if xcoord < G.pmlthickness['xminus'] or xcoord > G.nx - G.pmlthickness['xplus'] or ycoord < G.pmlthickness['yminus'] or ycoord > G.ny - G.pmlthickness['yplus'] or zcoord < G.pmlthickness['zminus'] or zcoord > G.nz - G.pmlthickness['zplus']: print(Fore.RED + "WARNING: '" + cmdname + ': ' + ' '.join(tmp) + "'" + ' sources and receivers should not normally be positioned within the PML.' + Style.RESET_ALL) 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: if dx == 0: dx = 1 else: raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should not be less than the spatial discretisation') if dy < G.dy: if dy == 0: dy = 1 else: raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should not be less than the spatial discretisation') if dz < G.dz: if dz == 0: dz = 1 else: raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should not be less than the spatial discretisation') if G.messages: print('Receiver array {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m with steps {:g}m, {:g}m, {:g}m'.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)) for x in range(xs, xf + 1, dx): for y in range(ys, yf + 1, dy): for z in range(zs, zf + 1, dz): r = Rx() r.xcoord = x r.ycoord = y r.zcoord = z r.xcoordorigin = x r.ycoordorigin = y r.zcoordorigin = z r.ID = r.__class__.__name__ + '(' + str(x) + ',' + str(y) + ',' + str(z) + ')' for key in Rx.defaultoutputs: r.outputs[key] = np.zeros(G.iterations, dtype=floattype) if G.messages: print(' Receiver at {:g}m, {:g}m, {:g}m with output component(s) {} created.'.format(r.xcoord * G.dx, r.ycoord * G.dy, r.zcoord * G.dz, ', '.join(r.outputs))) G.rxs.append(r) # 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 = G.calculate_coord('x', tmp[0]) ys = G.calculate_coord('y', tmp[1]) zs = G.calculate_coord('z', tmp[2]) xf = G.calculate_coord('x', tmp[3]) yf = G.calculate_coord('y', tmp[4]) zf = G.calculate_coord('z', tmp[5]) dx = G.calculate_coord('x', tmp[6]) dy = G.calculate_coord('y', tmp[7]) dz = G.calculate_coord('z', tmp[8]) # If number of iterations given try: time = int(tmp[9]) # If real floating point value given except: time = float(tmp[9]) if time > 0: time = round_value((time / G.dt)) + 1 else: raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' time value must be greater than zero') check_coordinates(xs, ys, zs, name='lower') check_coordinates(xf, yf, zf, name='upper') 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.basefilename)) G.snapshots.append(s) # Materials 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 tmp[1] != 'inf': se = float(tmp[1]) if se < 0: raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for conductivity') else: se = float('inf') 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]) m.er = float(tmp[0]) m.se = se m.mr = float(tmp[2]) m.sm = float(tmp[3]) # Set material averaging to False if infinite conductivity, i.e. pec if m.se == float('inf'): m.averagable = False if G.messages: tqdm.write('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.averagable = 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: tqdm.write('Debye 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.averagable = 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: tqdm.write('Lorentz 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.averagable = 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: tqdm.write('Drude 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 = G.calculate_coord('x', tmp[0]) ys = G.calculate_coord('y', tmp[1]) zs = G.calculate_coord('z', tmp[2]) xf = G.calculate_coord('x', tmp[3]) yf = G.calculate_coord('y', tmp[4]) zf = G.calculate_coord('z', tmp[5]) dx = G.calculate_coord('x', tmp[6]) dy = G.calculate_coord('y', tmp[7]) dz = G.calculate_coord('z', tmp[8]) check_coordinates(xs, ys, zs, name='lower') check_coordinates(xf, yf, zf, name='upper') 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)') if tmp[10].lower() == 'f' and (dx * G.dx != G.dx or dy * G.dy != G.dy or dz * G.dz != G.dz): raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires the spatial discretisation for the geometry view to be the same as the model for geometry view of type f (fine)') # Set type of geometry file if tmp[10].lower() == 'n': fileext = '.vti' else: fileext = '.vtp' g = GeometryView(xs, ys, zs, xf, yf, zf, dx, dy, dz, tmp[9], fileext) 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, with filename base {} 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.basefilename)) # Append the new GeometryView object to the geometry views list G.geometryviews.append(g) # Geometry object(s) output cmdname = '#geometry_objects_write' if multicmds[cmdname] != 'None': for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 7: raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly seven parameters') xs = G.calculate_coord('x', tmp[0]) ys = G.calculate_coord('y', tmp[1]) zs = G.calculate_coord('z', tmp[2]) xf = G.calculate_coord('x', tmp[3]) yf = G.calculate_coord('y', tmp[4]) zf = G.calculate_coord('z', tmp[5]) check_coordinates(xs, ys, zs, name='lower') check_coordinates(xf, yf, zf, name='upper') if xs >= xf or ys >= yf or zs >= zf: raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower coordinates should be less than the upper coordinates') g = GeometryObjects(xs, ys, zs, xf, yf, zf, tmp[6]) if G.messages: print('Geometry objects in the volume from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m, will be written to {}, with materials written to {}'.format(xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, g.filename, g.materialsfilename)) # Append the new GeometryView object to the geometry objects to write list G.geometryobjectswrite.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 or equal to one') 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: {}) 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)
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()
"""Plot waveforms that can be used for sources.""" # Parse command line arguments parser = argparse.ArgumentParser(description='Plot waveforms that can be used for sources.', usage='cd gprMax; python -m tools.plot_waveform type amp freq timewindow dt') parser.add_argument('type', help='type of waveform, e.g. gaussian, ricker etc...') 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', type=float, help='time window to view waveform') parser.add_argument('dt', type=float, help='time step to view waveform') args = parser.parse_args() w = Waveform() w.type = args.type w.amp = args.amp w.freq = args.freq timewindow = args.timewindow dt = args.dt time = np.arange(0, timewindow, dt) waveform = np.zeros(len(time)) timeiter = np.nditer(time, flags=['c_index']) while not timeiter.finished: waveform[timeiter.index] = w.calculate_value(timeiter[0], dt) timeiter.iternext() # Calculate frequency spectra of waveform power = 20 * np.log10(np.abs(np.fft.fft(waveform))**2) f = np.fft.fftfreq(power.size, d=dt)
def hertzian_dipole_fs(iterations, dt, dxdydz, rx): """Analytical solution of a z-directed Hertzian dipole in free space with a Gaussian current waveform (http://dx.doi.org/10.1016/0021-9991(83)90103-1). Args: iterations (int): Number of time steps. dt (float): Time step (seconds). dxdydz (float): Tuple of spatial resolution (metres). rx (float): Tuple of coordinates of receiver position relative to transmitter position (metres). Returns: fields (float): Array contain electric and magnetic field components. """ # Waveform w = Waveform() w.type = 'gaussiandot' w.amp = 1 w.freq = 1e9 # Waveform integral wint = Waveform() wint.type = 'gaussian' wint.amp = w.amp wint.freq = w.freq # Waveform first derivative wdot = Waveform() wdot.type = 'gaussiandotdot' wdot.amp = w.amp wdot.freq = w.freq # Time time = np.linspace(0, 1, iterations) time *= (iterations * dt) # Spatial resolution dx = dxdydz[0] dy = dxdydz[1] dz = dxdydz[2] # Length of Hertzian dipole dl = dz # Coordinates of Rx relative to Tx x = rx[0] y = rx[1] z = rx[2] if z == 0: sign_z = 1 else: sign_z = np.sign(z) # Coordinates of Rx for Ex FDTD component Ex_x = x + 0.5 * dx Ex_y = y Ex_z = z - 0.5 * dz Er_x = np.sqrt((Ex_x**2 + Ex_y**2 + Ex_z**2)) tau_Ex = Er_x / c # Coordinates of Rx for Ey FDTD component Ey_x = x Ey_y = y + 0.5 * dy Ey_z = z - 0.5 * dz Er_y = np.sqrt((Ey_x**2 + Ey_y**2 + Ey_z**2)) tau_Ey = Er_y / c # Coordinates of Rx for Ez FDTD component Ez_x = x Ez_y = y Ez_z = z Er_z = np.sqrt((Ez_x**2 + Ez_y**2 + Ez_z**2)) tau_Ez = Er_z / c # Coordinates of Rx for Hx FDTD component Hx_x = x Hx_y = y + 0.5 * dy Hx_z = z Hr_x = np.sqrt((Hx_x**2 + Hx_y**2 + Hx_z**2)) tau_Hx = Hr_x / c # Coordinates of Rx for Hy FDTD component Hy_x = x + 0.5 * dx Hy_y = y Hy_z = z Hr_y = np.sqrt((Hy_x**2 + Hy_y**2 + Hy_z**2)) tau_Hy = Hr_y / c # Coordinates of Rx for Hz FDTD component Hz_x = x + 0.5 * dx Hz_y = y + 0.5 * dy Hz_z = z - 0.5 * dz Hr_z = np.sqrt((Hz_x**2 + Hz_y**2 + Hz_z**2)) tau_Hz = Hr_z / c # Initialise fields fields = np.zeros((iterations, 6)) # Calculate fields for timestep in range(iterations): # Calculate values for waveform, I * dl (current multiplied by dipole length) to match gprMax behaviour fint_Ex = wint.calculate_value((timestep * dt) - tau_Ex, dt) * dl f_Ex = w.calculate_value((timestep * dt) - tau_Ex, dt) * dl fdot_Ex = wdot.calculate_value((timestep * dt) - tau_Ex, dt) * dl fint_Ey = wint.calculate_value((timestep * dt) - tau_Ey, dt) * dl f_Ey= w.calculate_value((timestep * dt) - tau_Ey, dt) * dl fdot_Ey = wdot.calculate_value((timestep * dt) - tau_Ey, dt) * dl fint_Ez = wint.calculate_value((timestep * dt) - tau_Ez, dt) * dl f_Ez = w.calculate_value((timestep * dt) - tau_Ez, dt) * dl fdot_Ez = wdot.calculate_value((timestep * dt) - tau_Ez, dt) * dl fint_Hx = wint.calculate_value((timestep * dt) - tau_Hx, dt) * dl f_Hx = w.calculate_value((timestep * dt) - tau_Hx, dt) * dl fdot_Hx = wdot.calculate_value((timestep * dt) - tau_Hx, dt) * dl fint_Hy = wint.calculate_value((timestep * dt) - tau_Hy, dt) * dl f_Hy= w.calculate_value((timestep * dt) - tau_Hy, dt) * dl fdot_Hy = wdot.calculate_value((timestep * dt) - tau_Hy, dt) * dl fint_Hz = wint.calculate_value((timestep * dt) - tau_Hz, dt) * dl f_Hz = w.calculate_value((timestep * dt) - tau_Hz, dt) * dl fdot_Hz = wdot.calculate_value((timestep * dt) - tau_Hz, dt) * dl # Ex fields[timestep, 0] = ((Ex_x * Ex_z) / (4 * np.pi * e0 * Er_x**5)) * (3 * (fint_Ex + (tau_Ex * f_Ex)) + (tau_Ex**2 * fdot_Ex)) # Ey try: tmp = Ey_y / Ey_x except ZeroDivisionError: tmp = 0 fields[timestep, 1] = tmp * ((Ey_x * Ey_z) / (4 * np.pi * e0 * Er_y**5)) * (3 * (fint_Ey + (tau_Ey * f_Ey)) + (tau_Ey**2 * fdot_Ey)) # Ez fields[timestep, 2] = (1 / (4 * np.pi * e0 * Er_z**5)) * ((2 * Ez_z**2 - (Ez_x**2 + Ez_y**2)) * (fint_Ez + (tau_Ez * f_Ez)) - (Ez_x**2 + Ez_y**2) * tau_Ez**2 * fdot_Ez) # Hx fields[timestep, 3] = - (Hx_y / (4 * np.pi * Hr_x**3)) * (f_Hx + (tau_Hx * fdot_Hx)) # Hy try: tmp = Hy_x / Hy_y except ZeroDivisionError: tmp = 0 fields[timestep, 4] = - tmp * (- (Hy_y / (4 * np.pi * Hr_y**3)) * (f_Hy + (tau_Hy * fdot_Hy))) # Hz fields[timestep, 5] = 0 return fields
def hertzian_dipole_fs(iterations, dt, dxdydz, rx): """Analytical solution of a z-directed Hertzian dipole in free space with a Gaussian current waveform (http://dx.doi.org/10.1016/0021-9991(83)90103-1). Args: iterations (int): Number of time steps. dt (float): Time step (seconds). dxdydz (float): Tuple of spatial resolution (metres). rx (float): Tuple of coordinates of receiver position relative to transmitter position (metres). Returns: fields (float): Array contain electric and magnetic field components. """ # Waveform w = Waveform() w.type = 'gaussiandot' w.amp = 1 w.freq = 1e9 # Waveform integral wint = Waveform() wint.type = 'gaussian' wint.amp = w.amp wint.freq = w.freq # Waveform first derivative wdot = Waveform() wdot.type = 'gaussiandotdot' wdot.amp = w.amp wdot.freq = w.freq # Time time = np.linspace(0, 1, iterations) time *= (iterations * dt) # Spatial resolution dx = dxdydz[0] dy = dxdydz[1] dz = dxdydz[2] # Coordinates of Rx relative to Tx x = rx[0] y = rx[1] z = rx[2] if z == 0: sign_z = 1 else: sign_z = np.sign(z) # Coordinates of Rx for Ex FDTD component Ex_x = x + 0.5 * dx Ex_y = y Ex_z = z - 0.5 * dz Er_x = np.sqrt((Ex_x**2 + Ex_y**2 + Ex_z**2)) tau_Ex = Er_x / c # Coordinates of Rx for Ey FDTD component Ey_x = x Ey_y = y + 0.5 * dy Ey_z = z - 0.5 * dz Er_y = np.sqrt((Ey_x**2 + Ey_y**2 + Ey_z**2)) tau_Ey = Er_y / c # Coordinates of Rx for Ez FDTD component Ez_x = x Ez_y = y Ez_z = z Er_z = np.sqrt((Ez_x**2 + Ez_y**2 + Ez_z**2)) tau_Ez = Er_z / c # Coordinates of Rx for Hx FDTD component Hx_x = x Hx_y = y + 0.5 * dy Hx_z = z Hr_x = np.sqrt((Hx_x**2 + Hx_y**2 + Hx_z**2)) tau_Hx = Hr_x / c # Coordinates of Rx for Hy FDTD component Hy_x = x + 0.5 * dx Hy_y = y Hy_z = z Hr_y = np.sqrt((Hy_x**2 + Hy_y**2 + Hy_z**2)) tau_Hy = Hr_y / c # Coordinates of Rx for Hz FDTD component Hz_x = x + 0.5 * dx Hz_y = y + 0.5 * dy Hz_z = z - 0.5 * dz Hr_z = np.sqrt((Hz_x**2 + Hz_y**2 + Hz_z**2)) tau_Hz = Hr_z / c # Initialise fields fields = np.zeros((iterations, 6)) # Calculate fields for timestep in range(iterations): # Calculate values for waveform fint_Ex = wint.calculate_value((timestep * dt) - tau_Ex, dt) * dx f_Ex = w.calculate_value((timestep * dt) - tau_Ex, dt) * dx fdot_Ex = wdot.calculate_value((timestep * dt) - tau_Ex, dt) * dx fint_Ey = wint.calculate_value((timestep * dt) - tau_Ey, dt) * dy f_Ey = w.calculate_value((timestep * dt) - tau_Ey, dt) * dy fdot_Ey = wdot.calculate_value((timestep * dt) - tau_Ey, dt) * dy fint_Ez = wint.calculate_value((timestep * dt) - tau_Ez, dt) * dz f_Ez = w.calculate_value((timestep * dt) - tau_Ez, dt) * dz fdot_Ez = wdot.calculate_value((timestep * dt) - tau_Ez, dt) * dz fint_Hx = wint.calculate_value((timestep * dt) - tau_Hx, dt) * dx f_Hx = w.calculate_value((timestep * dt) - tau_Hx, dt) * dx fdot_Hx = wdot.calculate_value((timestep * dt) - tau_Hx, dt) * dx fint_Hy = wint.calculate_value((timestep * dt) - tau_Hy, dt) * dy f_Hy = w.calculate_value((timestep * dt) - tau_Hy, dt) * dy fdot_Hy = wdot.calculate_value((timestep * dt) - tau_Hy, dt) * dy fint_Hz = wint.calculate_value((timestep * dt) - tau_Hz, dt) * dz f_Hz = w.calculate_value((timestep * dt) - tau_Hz, dt) * dz fdot_Hz = wdot.calculate_value((timestep * dt) - tau_Hz, dt) * dz # Ex fields[timestep, 0] = ((Ex_x * Ex_z) / (4 * np.pi * e0 * Er_x**5)) * (3 * (fint_Ex + (tau_Ex * f_Ex)) + (tau_Ex**2 * fdot_Ex)) # Ey try: tmp = Ey_y / Ey_x except ZeroDivisionError: tmp = 0 fields[timestep, 1] = tmp * ( (Ey_x * Ey_z) / (4 * np.pi * e0 * Er_y**5)) * (3 * (fint_Ey + (tau_Ey * f_Ey)) + (tau_Ey**2 * fdot_Ey)) # Ez fields[timestep, 2] = (1 / (4 * np.pi * e0 * Er_z**5)) * ( (2 * Ez_z**2 - (Ez_x**2 + Ez_y**2)) * (fint_Ez + (tau_Ez * f_Ez)) - (Ez_x**2 + Ez_y**2) * tau_Ez**2 * fdot_Ez) # Hx fields[timestep, 3] = -(Hx_y / (4 * np.pi * Hr_x**3)) * (f_Hx + (tau_Hx * fdot_Hx)) # Hy try: tmp = Hy_x / Hy_y except ZeroDivisionError: tmp = 0 fields[timestep, 4] = -tmp * (-(Hy_y / (4 * np.pi * Hr_y**3)) * (f_Hy + (tau_Hy * fdot_Hy))) # Hz fields[timestep, 5] = 0 return fields
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') 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_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)