Package: waver
waverRun simulations of thewave equationin nD on grids of variable speed in Python. This library owes a lot of its design and approach to thefdtdlibrary, a Python 3D electromagnetic FDTD simulator.This package allows for a fair amount of customization over your wave simulation. You canspecify the size and spacing of the gridspecify the time step for the simulation, which will be checked to ensure stability of the simulationspecify the duration of the simulationsetting a variable speed array (one value per grid point) to allow for "objects" in your environmentset the source of the wave, which can be a point, line, or any (n-1)D subarrayrecord the wave with a detector, which can be the full grid, the full boundary, or a particular boundaryuse convenience methods to run many simulations with different sources on the same grid and detector combinationYou can usenapari, a multi-dimensional image viewer for Python, to allow for easy visualization of the detected wave. Some functionality is also available as a napari plugin to allow for running simulations from a graphical user interface.Results can look likehttps://user-images.githubusercontent.com/6531703/128283012-a784ec06-4df9-4ddf-bf4f-e21b927fe4a3.movInstallationYou can installwaverviapip:pip install waverUsageConvenience MethodsThe most convenient way to use waver is to use one of two convenience methods that will create and run a simulation
for you and return the results.The first methodrun_single_sourceallows you to run a single simulation with a single source on one grid and
record the results using a detector. For examplefromwaver.simulationimportrun_single_sourcesingle_sim_params={'size':(12.8e-3,12.8e-3),'spacing':100e-6,'duration':80e-6,'min_speed':343,'max_speed':686,'speed':686,'time_step':50e-9,'temporal_downsample':2,'location':(6.4e-3,6.4e-3),'period':5e-6,'ncycles':1,}detected_wave,speed_grid=run_single_source(**single_sim_params)The second methodrun_multiple_sourcesallows you to run multiple simulations with multiple sources on the same
grid and with the same detector and return the results. For examplefromwaver.simulationimportrun_multiple_sourcesmulti_sim_params={'size':(12.8e-3,12.8e-3),'spacing':100e-6,'duration':80e-6,'min_speed':343,'max_speed':686,'speed':686,'time_step':50e-9,'temporal_downsample':2,'sources':[{'location':(6.4e-3,6.4e-3),'period':5e-6,'ncycles':1,}]}detected_wave,speed_grid=run_multiple_sources(**multi_sim_params)The main difference between these two methods is thatrun_multiple_sourcestakes asourcesparameter which takes a list
of dictionaries with keys corresponding to source related keyword arguments found inrun_single_source.VisualizationIf you want to quickly visualize the results ofrun_multiple_sources, you can use therun_and_visualizecommand which will
run the simulation and then launch napari with the results, as seen inexamples/2D/point_source.pyfromwaver.datasetsimportrun_and_visualizerun_and_visualize(**multi_sim_params)DatasetsIf you want to run simulations with on many different speed grids you can use thegenerate_simulation_datasetmethod as a convenience. The results will be saved to azarrfile of your chosing. You can then use theload_simulation_datasetto load the dataset.fromwaver.datasetsimportgenerate_simulation_dataset# Define root path for simulationpath='./simulation_dataset.zarr'runs=5# Define a simulation, 12.8mm, 100um spacingdataset_sim_params={'size':(12.8e-3,12.8e-3),'spacing':100e-6,'duration':80e-6,'min_speed':343,'max_speed':686,'speed':'mixed_random_ifft','time_step':50e-9,'sources':[{'location':(None,0),'period':5e-6,'ncycles':1,}],'temporal_downsample':2,'boundary':1,'edge':1,}# Run and save simulationgenerate_simulation_dataset(path,runs,**dataset_sim_params)Thegenerate_simulation_datasetallows thespeedto be a string that will specify a particular method of randomly generating speed values for the simulation grid.The Simulation ObjectIf you'd like to understand in a little bit more detail how a simulation is defined then you might want to use the unerlying simulation objectSimulationand manually set key objects like theSourceandDetector. A full example of this is as follows# Create a simulationsim=Simulation(size=size,spacing=spacing,max_speed=max_speed,time_step=time_step)# Set speed arraysim.set_speed(speed=speed,min_speed=min_speed,max_speed=max_speed)# Add sourcesim.add_source(location=location,period=period,ncycles=ncycles,phase=phase)# Add detector gridsim.add_detector(spatial_downsample=spatial_downsample,boundary=boundary,edge=edge)# Run simulationsim.run(duration=duration,temporal_downsample=temporal_downsample,progress=progress,leave=leave)# Print simulation wave and speed dataprint('wave: ',sim.detected_wave)print('speed: ',sim.grid_speed)Note these steps are done inside therun_single_sourcemethod for you as a convenience.Known LimitationsAperfectly matched layerboundary has recently been added, but might not perform well under all conditions. Additional contributions would be welcome here.Right now the simulations are quite slow. I'd like to add aJAXbackend, but
havn't done so yet. Contributions would be welcome.ContributingContributions are very welcome. Tests can be run withtox, please ensure
the coverage at least stays the same before you submit a pull request.LicenseDistributed under the terms of theBSD-3license,
"waver" is free and open source softwareIssuesIf you encounter any problems, pleasefile an issuealong with a detailed description.