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)
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 run_model(args, currentmodelrun, modelend, numbermodelruns, inputfile, usernamespace): """Runs a model - processes the input file; builds the Yee cells; calculates update coefficients; runs main FDTD loop. Args: args (dict): Namespace with command line arguments currentmodelrun (int): Current model run number. modelend (int): Number of last model to run. numbermodelruns (int): Total number of model runs. inputfile (object): File object for the input file. usernamespace (dict): Namespace that can be accessed by user in any Python code blocks in input file. Returns: tsolve (int): Length of time (seconds) of main FDTD calculations """ # Monitor memory usage p = psutil.Process() # Declare variable to hold FDTDGrid class global G # Used for naming geometry and output files appendmodelnumber = '' if numbermodelruns == 1 and not args.task and not args.restart else '_'+str(currentmodelrun) appendmodelnumberGeometry = '' if numbermodelruns == 1 and not args.task and not args.restart or args.geometry_fixed else '_'+str(currentmodelrun) # Normal model reading/building process; bypassed if geometry information to be reused if 'G' not in globals(): # Initialise an instance of the FDTDGrid class G = FDTDGrid() # Get information about host machine # (need to save this info to FDTDGrid instance after it has been created) G.hostinfo = get_host_info() # Single GPU object if args.gpu: G.gpu = args.gpu G.inputfilename = os.path.split(inputfile.name)[1] G.inputdirectory = os.path.dirname(os.path.abspath(inputfile.name)) inputfilestr = '\n--- Model {}/{}, input file: {}'.format(currentmodelrun, modelend, inputfile.name) if G.messages: print(Fore.GREEN + '{} {}\n'.format(inputfilestr, '-' * (get_terminal_width() - 1 - len(inputfilestr))) + Style.RESET_ALL) # Add the current model run to namespace that can be accessed by # user in any Python code blocks in input file usernamespace['current_model_run'] = currentmodelrun # Read input file and process any Python and include file commands processedlines = process_python_include_code(inputfile, usernamespace) # Print constants/variables in user-accessable namespace uservars = '' for key, value in sorted(usernamespace.items()): if key != '__builtins__': uservars += '{}: {}, '.format(key, value) if G.messages: print('Constants/variables used/available for Python scripting: {{{}}}\n'.format(uservars[:-2])) # Write a file containing the input commands after Python or include file commands have been processed if args.write_processed: write_processed_file(processedlines, appendmodelnumber, G) # Check validity of command names and that essential commands are present singlecmds, multicmds, geometry = check_cmd_names(processedlines) # Create built-in materials m = Material(0, 'pec') m.se = float('inf') m.type = 'builtin' m.averagable = False G.materials.append(m) m = Material(1, 'free_space') m.type = 'builtin' G.materials.append(m) # Process parameters for commands that can only occur once in the model process_singlecmds(singlecmds, G) # Process parameters for commands that can occur multiple times in the model if G.messages: print() process_multicmds(multicmds, G) # Estimate and check memory (RAM) usage G.memory_estimate_basic() #G.memory_check() #if G.messages: # if G.gpu is None: # print('\nMemory (RAM) required: ~{}\n'.format(human_size(G.memoryusage))) # else: # print('\nMemory (RAM) required: ~{} host + ~{} GPU\n'.format(human_size(G.memoryusage), human_size(G.memoryusage))) # Initialise an array for volumetric material IDs (solid), boolean # arrays for specifying materials not to be averaged (rigid), # an array for cell edge IDs (ID) G.initialise_geometry_arrays() # Initialise arrays for the field components if G.gpu is None: G.initialise_field_arrays() # Process geometry commands in the order they were given process_geometrycmds(geometry, G) # Build the PMLs and calculate initial coefficients if G.messages: print() if all(value == 0 for value in G.pmlthickness.values()): if G.messages: print('PML: switched off') pass # If all the PMLs are switched off don't need to build anything else: # Set default CFS parameters for PML if not given if not G.cfs: G.cfs = [CFS()] if G.messages: if all(value == G.pmlthickness['x0'] for value in G.pmlthickness.values()): pmlinfo = str(G.pmlthickness['x0']) else: pmlinfo = '' for key, value in G.pmlthickness.items(): pmlinfo += '{}: {}, '.format(key, value) pmlinfo = pmlinfo[:-2] + ' cells' print('PML: formulation: {}, order: {}, thickness: {}'.format(G.pmlformulation, len(G.cfs), pmlinfo)) pbar = tqdm(total=sum(1 for value in G.pmlthickness.values() if value > 0), desc='Building PML boundaries', ncols=get_terminal_width() - 1, file=sys.stdout, disable=not G.progressbars) build_pmls(G, pbar) pbar.close() # Build the model, i.e. set the material properties (ID) for every edge # of every Yee cell if G.messages: print() pbar = tqdm(total=2, desc='Building main grid', ncols=get_terminal_width() - 1, file=sys.stdout, disable=not G.progressbars) build_electric_components(G.solid, G.rigidE, G.ID, G) pbar.update() build_magnetic_components(G.solid, G.rigidH, G.ID, G) pbar.update() pbar.close() # Add PEC boundaries to invariant direction in 2D modes # N.B. 2D modes are a single cell slice of 3D grid if '2D TMx' in G.mode: # Ey & Ez components G.ID[1, 0, :, :] = 0 G.ID[1, 1, :, :] = 0 G.ID[2, 0, :, :] = 0 G.ID[2, 1, :, :] = 0 elif '2D TMy' in G.mode: # Ex & Ez components G.ID[0, :, 0, :] = 0 G.ID[0, :, 1, :] = 0 G.ID[2, :, 0, :] = 0 G.ID[2, :, 1, :] = 0 elif '2D TMz' in G.mode: # Ex & Ey components G.ID[0, :, :, 0] = 0 G.ID[0, :, :, 1] = 0 G.ID[1, :, :, 0] = 0 G.ID[1, :, :, 1] = 0 # Process any voltage sources (that have resistance) to create a new # material at the source location for voltagesource in G.voltagesources: voltagesource.create_material(G) # Initialise arrays of update coefficients to pass to update functions G.initialise_std_update_coeff_arrays() # Initialise arrays of update coefficients and temporary values if # there are any dispersive materials if Material.maxpoles != 0: # Update estimated memory (RAM) usage G.memoryusage += int(3 * Material.maxpoles * (G.nx + 1) * (G.ny + 1) * (G.nz + 1) * np.dtype(complextype).itemsize) G.memory_check() if G.messages: print('\nMemory (RAM) required - updated (dispersive): ~{}\n'.format(human_size(G.memoryusage))) G.initialise_dispersive_arrays() # Check there is sufficient memory to store any snapshots if G.snapshots: snapsmemsize = 0 for snap in G.snapshots: # 2 x required to account for electric and magnetic fields snapsmemsize += (2 * snap.datasizefield) G.memoryusage += int(snapsmemsize) G.memory_check(snapsmemsize=int(snapsmemsize)) if G.messages: print('\nMemory (RAM) required - updated (snapshots): ~{}\n'.format(human_size(G.memoryusage))) # Process complete list of materials - calculate update coefficients, # store in arrays, and build text list of materials/properties materialsdata = process_materials(G) if G.messages: print('\nMaterials:') materialstable = AsciiTable(materialsdata) materialstable.outer_border = False materialstable.justify_columns[0] = 'right' print(materialstable.table) # Check to see if numerical dispersion might be a problem results = dispersion_analysis(G) if results['error'] and G.messages: print(Fore.RED + "\nWARNING: Numerical dispersion analysis not carried out as {}".format(results['error']) + Style.RESET_ALL) elif results['N'] < G.mingridsampling: raise GeneralError("Non-physical wave propagation: Material '{}' has wavelength sampled by {} cells, less than required minimum for physical wave propagation. Maximum significant frequency estimated as {:g}Hz".format(results['material'].ID, results['N'], results['maxfreq'])) elif results['deltavp'] and np.abs(results['deltavp']) > G.maxnumericaldisp and G.messages: print(Fore.RED + "\nWARNING: Potentially significant numerical dispersion. Estimated largest physical phase-velocity error is {:.2f}% in material '{}' whose wavelength sampled by {} cells. Maximum significant frequency estimated as {:g}Hz".format(results['deltavp'], results['material'].ID, results['N'], results['maxfreq']) + Style.RESET_ALL) elif results['deltavp'] and G.messages: print("\nNumerical dispersion analysis: estimated largest physical phase-velocity error is {:.2f}% in material '{}' whose wavelength sampled by {} cells. Maximum significant frequency estimated as {:g}Hz".format(results['deltavp'], results['material'].ID, results['N'], results['maxfreq'])) # If geometry information to be reused between model runs else: inputfilestr = '\n--- Model {}/{}, input file (not re-processed, i.e. geometry fixed): {}'.format(currentmodelrun, modelend, inputfile.name) if G.messages: print(Fore.GREEN + '{} {}\n'.format(inputfilestr, '-' * (get_terminal_width() - 1 - len(inputfilestr))) + Style.RESET_ALL) if G.gpu is None: # Clear arrays for field components G.initialise_field_arrays() # Clear arrays for fields in PML for pml in G.pmls: pml.initialise_field_arrays() # Adjust position of simple sources and receivers if required if G.srcsteps[0] != 0 or G.srcsteps[1] != 0 or G.srcsteps[2] != 0: for source in itertools.chain(G.hertziandipoles, G.magneticdipoles): if currentmodelrun == 1: if source.xcoord + G.srcsteps[0] * modelend < 0 or source.xcoord + G.srcsteps[0] * modelend > G.nx or source.ycoord + G.srcsteps[1] * modelend < 0 or source.ycoord + G.srcsteps[1] * modelend > G.ny or source.zcoord + G.srcsteps[2] * modelend < 0 or source.zcoord + G.srcsteps[2] * modelend > G.nz: raise GeneralError('Source(s) will be stepped to a position outside the domain.') source.xcoord = source.xcoordorigin + (currentmodelrun - 1) * G.srcsteps[0] source.ycoord = source.ycoordorigin + (currentmodelrun - 1) * G.srcsteps[1] source.zcoord = source.zcoordorigin + (currentmodelrun - 1) * G.srcsteps[2] if G.rxsteps[0] != 0 or G.rxsteps[1] != 0 or G.rxsteps[2] != 0: for receiver in G.rxs: if currentmodelrun == 1: if receiver.xcoord + G.rxsteps[0] * modelend < 0 or receiver.xcoord + G.rxsteps[0] * modelend > G.nx or receiver.ycoord + G.rxsteps[1] * modelend < 0 or receiver.ycoord + G.rxsteps[1] * modelend > G.ny or receiver.zcoord + G.rxsteps[2] * modelend < 0 or receiver.zcoord + G.rxsteps[2] * modelend > G.nz: raise GeneralError('Receiver(s) will be stepped to a position outside the domain.') receiver.xcoord = receiver.xcoordorigin + (currentmodelrun - 1) * G.rxsteps[0] receiver.ycoord = receiver.ycoordorigin + (currentmodelrun - 1) * G.rxsteps[1] receiver.zcoord = receiver.zcoordorigin + (currentmodelrun - 1) * G.rxsteps[2] # Write files for any geometry views and geometry object outputs if not (G.geometryviews or G.geometryobjectswrite) and args.geometry_only and G.messages: print(Fore.RED + '\nWARNING: No geometry views or geometry objects to output found.' + Style.RESET_ALL) if G.geometryviews and (not args.geometry_fixed or currentmodelrun == 1): if G.messages: print() for i, geometryview in enumerate(G.geometryviews): geometryview.set_filename(appendmodelnumberGeometry, G) pbar = tqdm(total=geometryview.datawritesize, unit='byte', unit_scale=True, desc='Writing geometry view file {}/{}, {}'.format(i + 1, len(G.geometryviews), os.path.split(geometryview.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not G.progressbars) geometryview.write_vtk(G, pbar) pbar.close() if G.geometryobjectswrite: for i, geometryobject in enumerate(G.geometryobjectswrite): pbar = tqdm(total=geometryobject.datawritesize, unit='byte', unit_scale=True, desc='Writing geometry object file {}/{}, {}'.format(i + 1, len(G.geometryobjectswrite), os.path.split(geometryobject.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not G.progressbars) geometryobject.write_hdf5(G, pbar) pbar.close() # If only writing geometry information if args.geometry_only: tsolve = 0 # Run simulation else: # Output filename inputdirectory, inputfilename = os.path.split(os.path.join(G.inputdirectory, G.inputfilename)) if G.outputdirectory is None: outputdir = inputdirectory else: outputdir = G.outputdirectory # Save current directory curdir = os.getcwd() os.chdir(inputdirectory) outputdir = os.path.abspath(outputdir) if not os.path.isdir(outputdir): os.mkdir(outputdir) if G.messages: print('\nCreated output directory: {}'.format(outputdir)) # Restore current directory os.chdir(curdir) basename, ext = os.path.splitext(inputfilename) outputfile = os.path.join(outputdir, basename + appendmodelnumber + '.out') if G.messages: print('\nOutput file: {}\n'.format(outputfile)) # Main FDTD solving functions for either CPU or GPU if G.gpu is None: tsolve = solve_cpu(currentmodelrun, modelend, G) else: tsolve, memsolve = solve_gpu(currentmodelrun, modelend, G) # Write an output file in HDF5 format write_hdf5_outputfile(outputfile, G) # Write any snapshots to file if G.snapshots: # Create directory and construct filename from user-supplied name and model run number snapshotdir = os.path.join(G.inputdirectory, os.path.splitext(G.inputfilename)[0] + '_snaps' + appendmodelnumber) if not os.path.exists(snapshotdir): os.mkdir(snapshotdir) if G.messages: print() for i, snap in enumerate(G.snapshots): snap.filename = os.path.abspath(os.path.join(snapshotdir, snap.basefilename + '.vti')) pbar = tqdm(total=snap.vtkdatawritesize, leave=True, unit='byte', unit_scale=True, desc='Writing snapshot file {} of {}, {}'.format(i + 1, len(G.snapshots), os.path.split(snap.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not G.progressbars) snap.write_vtk_imagedata(pbar, G) pbar.close() if G.messages: print() if G.messages: if G.gpu is None: print('Memory (RAM) used: ~{}'.format(human_size(p.memory_info().rss))) else: print('Memory (RAM) used: ~{} host + ~{} GPU'.format(human_size(p.memory_info().rss), human_size(memsolve))) print('Solving time [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=tsolve))) # If geometry information to be reused between model runs then FDTDGrid # class instance must be global so that it persists if not args.geometry_fixed or currentmodelrun is modelend: del G return tsolve
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)