Ashby charts for fluids and strings¶
Matthew C M Wright mcmw@isvr.soton.ac.uk
Updated 2020-09-20
Introduction¶
Material selection charts, often called Ashby charts, are scatter plots whose axes represent two different properties of a material. They often show categories of materials e.g. woods, metals, rather than individual material.
By overlaying contours of some desirable property function of the axis variables one can identify the material with the highest value of that property. For example, Schelleng [Sch63] showed that sound radiation from violins should be proportional to \(\sqrt{E/\rho^3}\) where \(E\) is Young’s modulus and \(\rho\) is density. Superimposing contours of this function over an Ashby chart with these axes indicates that woods have better sound radiation properties than other available materials.
Logarithmic axes allow a wide range of values to be displayed mean that contours of power-law functions appear as straight lines.
As well as their utility for practising engineers they have educational value, allowing students to visualise the differences between classes of materials. If students are provided with the means to construct such charts themselves they can explore and investigate the data for themseves. In this notebook I show how an Ashby chart for fluids, showing properties of interest to acousticians, can be constructed, and discuss how they can be used in an educational setting. The concept is then extended to musical strings.
The code is written in Python 3 using the following libraries:
NumPy for array manipulation
Pandas for data handling
Plotly Express for visualisation
Some of the less obvious aspects of the coding features are pointed out during the notebook, which provides a number of examples of Plotly Express usage, which may be of use to those using it for other tasks as some of the usage is not entirely straightforward. If, on the other hand, you know a better way to achieve the results here please let me know, either via email or github pull request.
This is a work in progress; in particular I hope to add string data forr more instruments and would be glad to hear of sources of that information.
import numpy as np
import pandas as pd
import plotly.express as px
Fluids¶
Data¶
A CSV data file Fluid properties.csv
, available in the GitHub repository, contains properties of a number of fluids at NTP (20°C, 1 atm = 101.325 kPa), obtained from the NIST Thermophysical Properties of Fluid Systems tool, which is based on Lemmon, McLinden and Friend’s chapter in the NIST Chemistry WebBook [EWLFed].
It can be read into a pandas dataframe as follows.
fluids = pd.read_csv('Fluid properties.csv', index_col = 0)
This creates a dataframe object called fluids
. The last argument assigns the leftmost column (containing the fluid names) to be the index of the dataframe (Python indexes start at zero). We can look at the first few substances:
fluids.head()
Density | Sound speed | Phase | |
---|---|---|---|
Air | 1.204300 | 343.390 | gas |
Ammonia | 0.716051 | 429.403 | gas |
Argon | 1.661820 | 318.959 | gas |
Benzene | 878.905000 | 1324.010 | liquid |
Butane | 2.495750 | 208.534 | gas |
The dataframe can be interrogated using indexes, booleans and dataframe methods. For example, the five densest gases can be found as follows.
fluids[fluids['Phase'] == 'gas'].sort_values(by = 'Density')[-5:]
Density | Sound speed | Phase | |
---|---|---|---|
R227ea | 7.28147 | 120.519 | gas |
R114 | 7.34217 | 120.389 | gas |
R218 | 7.99768 | 114.917 | gas |
RC318 | 8.58872 | 110.250 | gas |
Decafluorobutane | 10.30700 | 99.723 | gas |
Here
fluids[fluids['Phase'] == 'gas']
is a dataframe containing only gases due to its boolean index,sort_values()
is a method available to dataframes whose keyword argumentby
determines the sort index,[-5:]
is an index that specifies the last five elements of the list-like object it is appended to.
It is possible to write e.g. fluids.Density
instead of fluids['Density]
, but this syntax cannot easily be extended to columns with spaces in their headings, so will not be used here.
The properties of a particular fluid can be extracted like this:
fluids.loc['Air']
Density 1.2043
Sound speed 343.39
Phase gas
Name: Air, dtype: object
A particular property of a particular fluid can be extracted like this:
fluids.loc['Air']['Density']
1.2043
Writing fluids.loc['Air', 'Density']
would have the same effect.
An additional fluid can be added like this:
fluids.loc['Dodecafluoropentane'] = [10.31, 99.72, 'gas']
Visualization¶
Interrogating and visualizing this data set can form the basis for a lesson, an activity or a project. Some suggestions are given below that illustrate the possibilities.
An obvious question to ask is how sound speed varies with density. We can plot it like this:
fig = px.scatter(fluids,
x = 'Density',
y = 'Sound speed',
labels = {'Density': 'Density [kg/m^3]',
'Sound speed': 'Sound speed [m/s]'},
color = 'Phase',
hover_name = fluids.index,
template = 'plotly_white')
fig.show()
To avoid having to set the template in future plots we can make it the default from now on. We will also set the default figure width.
px.defaults.template = 'plotly_white'
px.defaults.width = 700
Students can explore the chart by hovering on data points to reveal substances’ names. Changing hover_name = fluids.index
to text = fluids.index
will display the names of each substance on the chart permanently. This will be crowded, but the zoom and pan tools can then be used to explore particular regions of the chart. Using the mouse to drag a box around a region zooms to that region; double-clicking restores the original axes.
Suggested activities:¶
The following examples of student activities cover a range of levels from in-class test to longer term project, and include physics, data visualization and coding.
Find the gas with the highest sound speed and the liquid with the lowest sound speed.
Find the liquid and gas with the most similar sound speed, and give your definition of ‘most similar’.
Mercury is not included in the spreradsheet. Where on the chart would you expect it to lie (this could be a class discussion)? Once you have recorded your prediction(s), look up its properties and add it.
Do the same for seawater of a given salinity and/or air of a given humidity.
Refrigerants are denoted by an R followed by their ASHRAE number[ASH19]. Modify the code to show refrigerants with a different colour, without editing the CSV file.
Click on ‘gas’ in the legend; this will stop displaying the gases and rescale the axes appropriately. Identify any commonalities among liquids that are similarly arranged or clustered. Investigate possible reasons for this.
Turn the gases back on and the liquids off, and notice the curved shape of the envelope of points. Add the options
log_x = True
andlog_y = True
to thepx.scatter()
to show whether it follows a power law. Superimpose the ideal gas law prediction for sound speed \(c = \sqrt{\gamma P/\rho}\) for monatomic (\(\gamma = 5/3\)) and diatomic (\(\gamma = 7/5\)) gases.
The code required for the last of these is given below by way of a worked example. The points corresponding to air and water are picked out in different colours with larger markers, because of their importance in acoustics applications. Hover information is turned off for the air and water highlights and the ideal gas law predictions, to avoid interfering with the fluid data-points.
P = 101.325e3
gamma_monatomic = 5/3
gamma_diatomic = 7/5
rho = np.logspace(-1.1, 1, 100)
fig = px.scatter(fluids,
x = 'Density',
y = 'Sound speed',
hover_name = fluids.index,
color = 'Phase',
log_x = True,
log_y = True,
labels =
{'Density': 'Density [kg/m^3]',
'Sound speed': 'Sound speed [m/s]'})
fig.add_scatter(x = [fluids.loc['Water', 'Density']],
y = [fluids.loc['Water', 'Sound speed']],
mode = 'markers',
marker = {'size': 8},
name = 'Water',
hoverinfo = 'skip')
fig.add_scatter(x = [fluids.loc['Air', 'Density']],
y = [fluids.loc['Air', 'Sound speed']],
mode = 'markers',
marker = {'size': 8},
name = 'Air',
hoverinfo = 'skip')
fig.add_scatter(x = rho,
y = np.sqrt(gamma_monatomic*P/rho),
name = 'Ideal monatomic gas',
hoverinfo = 'skip')
fig.add_scatter(x = rho,
y = np.sqrt(gamma_diatomic*P/rho),
name = 'Ideal diatomic gas',
hoverinfo = 'skip')
fig.show()
Static plots, for insertion into PowerPoint slides, for example, can be generated using the ‘camera’ icon above the chart. The interactive html version can be saved with e.g. fig.write_html('Acoustic_Ashby_chart.html')
. Specifying width
and height
will prevent the resulting figure from resizing and changing aspect ratio with the browser window.
Bulk modulus¶
A change of axes provides a different view of the data. We can add a new column in the dataframe containing the fluid’s isentropic bulk modulus \(B = \rho c^2\) (the inverse of its compressibility) like this:
fluids['Bulk modulus'] = fluids['Density']*fluids['Sound speed']**2
We can use this to plot bulk modulus vs density.
#```{margin}
#<p style="margin-bottom:6cm;"> </p>
#```
fig = px.scatter(fluids,
x = 'Density',
y = 'Bulk modulus',
color = 'Phase',
hover_name = fluids.index,
log_x = True,
log_y = True,
labels =
{'Density': 'Density [kg/m^3]',
'Bulk modulus': 'Bulk modulus [Pa]'})
fig.add_scatter(x = [fluids.loc['Water', 'Density']],
y = [fluids.loc['Water', 'Bulk modulus']],
mode = 'markers',
marker = {'size': 8},
name = 'Water',
hoverinfo = 'skip')
fig.add_scatter(x = [fluids.loc['Air', 'Density']],
y = [fluids.loc['Air', 'Bulk modulus']],
mode = 'markers',
marker = {'size': 8},
name = 'Air',
hoverinfo = 'skip')
fig.show()
Suggested activities¶
Deduce from the ideal gas law where you expect monatomic gases to be located on this chart, and then check your prediction (turn the liquids off for easier viewing).
In which direction(s) would the points move if the temperature increased and the pressure remained the same?
Plot contours of sound speed \(c = \sqrt{B/\rho}\) and label them at appropriate intervals.
Do the same for characteristic specific acoustic impedance \(z = \rho c = \sqrt{B\rho}\).
The following code provides a worked example for the second and third of these. In fact contours of \(\log_{10} c\) and \(\log_{10} z\) are plotted, allowing them to be evenly spaced on the logarithmic axes. Additional contours (shown with dashed lines) pass through the data points for air and water.
rho, B = np.logspace(-1.3, 4, 100), np.logspace(4.5, 10, 100)
rho_grid, B_grid = np.meshgrid(rho, B)
z = np.sqrt(B_grid*rho_grid)
c = np.sqrt(B_grid/rho_grid)
rho_air, c_air = fluids.loc['Air', ['Density', 'Sound speed']]
rho_water, c_water = fluids.loc['Water', ['Density', 'Sound speed']]
z_air, z_water = rho_air*c_air, rho_water*c_water
fig = px.scatter(fluids,
x = 'Density',
y = 'Bulk modulus',
color = 'Phase',
hover_name = fluids.index,
log_x = True,
log_y = True,
labels =
{'Density': 'Density [kg/m^3]',
'Bulk modulus': 'Bulk modulus [Pa]'})
fig.add_scatter(x = [fluids.loc['Water', 'Density']],
y = [fluids.loc['Water', 'Bulk modulus']],
mode = 'markers',
marker = {'size': 8},
name = 'Water',
hoverinfo = 'skip')
fig.add_scatter(x = [fluids.loc['Air', 'Density']],
y = [fluids.loc['Air', 'Bulk modulus']],
mode = 'markers',
marker = {'size': 8},
name = 'Air',
hoverinfo = 'skip')
fig.add_contour(x = rho,
y = B,
z = np.log10(z),
name = 'log10(z [rayl])',
contours = {'coloring': 'none',
'start': 2,
'end': 6,
'size': 1,
'showlabels': True},
hoverinfo = 'skip')
fig.add_contour(x = rho,
y = B,
z = np.log10(c),
name = 'log10(c [m/s])',
contours = {'coloring': 'none',
'start': 0,
'end': 6,
'size': 1,
'showlabels': True},
line = {'color': 'Blue'},
hoverinfo = 'skip')
fig.add_contour(x = rho,
y = B,
z = np.log10(z),
showlegend = False,
contours = {'coloring': 'none',
'start': np.log10(z_air),
'end': np.log10(z_water),
'size': np.log10(z_water/z_air),
'showlabels': True},
line = {'dash': 'dash'},
hoverinfo = 'skip')
fig.add_contour(x = rho,
y = B,
z = np.log10(c),
showlegend = False,
contours = {'coloring': 'none',
'start': np.log10(c_air),
'end': np.log10(c_water),
'size': np.log10(c_water/c_air),
'showlabels': True},
line = {'color': 'Blue',
'dash': 'dash'},
hoverinfo = 'skip')
fig.show()
This diagram can be used to explore the normal incidence reflection and transmission between fluids with different impedances, for example, what reflection coefficient applies when the fluids are one, two or three impedance contours apart? It could also inform a discussion about the effect of gas bubbles on underwater sound. A more advanced programming exercise would be to animate the motion of data points with changing temperature.
Students could also gather data for solids to add to the chart, which could prompt a discussion on whether to use the solids’ actual bulk modulus or its Young’s modulus.
Strings¶
For longitudinal sound waves in fluids the bulk modulus provides the restoring force that makes the fluid return to its equiliibrium state when disturbed; density provides the inertia that makes them take time to do so. Liquids are stiff and heavy, and lie at the top right of the chart; gases are floppy and light, and lie at the bottom left of the chart.
For transverse waves on strings, such as those on musical instruments, the restoring force is provided by the tension \(\tau\) (newtons) provides the restoring force, and mass per unit length \(\mu\) (kilograms per metre) provides the restoring force. Furthermore, wave speed and impedance (mechanical this time) bear the same relationship to \(\tau\) and \(\mu\) that their acoustic counterparts do to \(B\) and \(\rho\), i.e.
(the symbols \(c\) and \(z\) will refer to strings for the remainder of this notebook). Therefore if we make an Ashby chart for strings, using \(\mu\) and \(\tau\) as the axes, the speed and impedance contours will be the same as they were for the fluids chart.
Guitars¶
Another file has data for several sets of guitar strings. The data was obtained from the website of the D’Addario company, who helpfully provide tension data (in kg) for all the strings they sell (D’Addario undated).
strings_g = pd.read_csv('String data guitar.csv')
strings_g.head()
Type | Instrument | Brand | String material | String code | String type | Note | Gauge (thou) | Tension (kg) | Scale (cm) | |
---|---|---|---|---|---|---|---|---|---|---|
0 | Electric | Guitar | D'addarrio | XL Prosteels | EPS530 | Extra Super Light | E4 | 8 | 4.71 | 64.5 |
1 | Electric | Guitar | D'addarrio | XL Prosteels | EPS530 | Extra Super Light | B3 | 10 | 4.13 | 64.5 |
2 | Electric | Guitar | D'addarrio | XL Prosteels | EPS530 | Extra Super Light | G3 | 15 | 5.85 | 64.5 |
3 | Electric | Guitar | D'addarrio | XL Prosteels | EPS530 | Extra Super Light | D3 | 21 | 4.98 | 64.5 |
4 | Electric | Guitar | D'addarrio | XL Prosteels | EPS530 | Extra Super Light | A2 | 30 | 5.95 | 64.5 |
No index_col
was specified when importing because there isn’t a suitable column to use as an index. Pandas has therefore generated a numerical index. Each row corresponds to a different string, string sets are grouped together and strings are given in descending pitch order within sets.
The tension and scale can then be converted to SI units; we’ll give these units in brackets rather than parentheses so that column names can be directly used as axis labels.
strings_g['Tension [N]'] = strings_g['Tension (kg)'] * 9.81
strings_g['Scale [m]'] = strings_g['Scale (cm)']/100
The mass per unit length is not provided, but can be inferred from the tension and the fundamental frequency. The note names, in Scientific Pitch Notation (SPN) are given according to standard tuning, and from these the fundamental frequencies can be calculated as follows, assumingg equal temperament.
note_names = ['C', 'Cs', 'D', 'Ds', 'E', 'F', 'Fs', 'G',
'Gs', 'A', 'As', 'B']
for index, row in strings_g.iterrows():
semitones = note_names.index(row['Note'][:-1])
strings_g.loc[index, 'Frequency [Hz]'] = 440 * 2** \
(int(row['Note'][-1]) - 4 + (semitones - 9)/12)
In note_names
a sharp is designated by s
rather than the usual sign. The conversion has to be done a row at a time because the string index that extracts the letter and number components of the SPN note-name cannot be broadcasted over dataframe columns.
The formula in the last line calculates the number of semitones the note in question is from A4, assumed to be at 440 Hz (A
being the ninth element of note_names
, counting from zero).
Having found the frequency \(f\) the wave speed \(c\) from the knowledge that string waves travels twice the length of the string \(L\) in a period, so that \(c = 2fL\). The mass per unit length can then be inferred by rearranging \(c = \sqrt{\tau/\mu}\) to give \(\mu = \tau/c^2 = \tau/4L^2f^2\). So-called ‘scale length’ varies slightly between guitars, but has been assumed, in the data, to be 64.5 cm for all guitars.
strings_g['Mass/length [kg/m]'] = strings_g['Tension [N]']/ \
(4*(strings_g['Scale [m]'])**2 * strings_g['Frequency [Hz]']**2)
Strings from the same set can be shown as points joined by a line. In order to group string sets together we must define a name that is unique to each set. The string code also be used for this purpose, but would be less informative in the chart’s legend. Instead we combine multiple information fields.
#strings_g['Name'] = strings_g['Type'] + ' ' + \
# strings_g['Instrument'] + ' ' + \
# strings_g['String type']
strings_g['Name'] = strings_g['Type'] + ' ' + \
strings_g['String type']
Since the longer name occupies more space in the legend the overall default width should be increased.
#px.defaults.width = 900
If we assign the color
of the data points to this Name
column, and use lines as well as markers the points for individual strings will be joined up. Guitars, like most plucked string instruments, have all their strings the same length, but with differing weights so as to keep the tensions similar. We would therefore expect a set of guitar strings to make an approximately horizontal line when plotted with tension vs mass per unit length. We can test this conjecture.
fig = px.scatter(strings_g,
x = 'Mass/length [kg/m]',
y = 'Tension [N]',
color = 'Name',
log_x = True,
log_y = True)
fig.update_traces(mode = 'lines+markers')
fig.show()
The chart reveals that (for these string sets, at least):
For all but one set the top E-string is slightly tighter than its neighbour
For all sets the bottom E-string is slightly slacker than its neighbour
The variation in tension among the classical guitar string sets is much smaller than between the electric guitar sets, although the subjective difference is presumably significant
Steel-strung guitars, as is well known, either have additional bracing to withstand higher tension, or have solid bodies. Electric guitars can also tolerate lower tension than acoustic guitars because pickups respond to string velocity, which can be maintained independently of tension, rather than force on the bridge, which varies in proportion to tension.
Because all the strings are the same length all the notes tuned to a particular pitch must have the same wave-speed, and therefore lie on the same diagonal line. We can confirm this by joining up matching notes like this:
fig = px.scatter(strings_g,
x = 'Mass/length [kg/m]',
y = 'Tension [N]',
color = 'Name',
log_x = True,
log_y = True)
for i, note in enumerate(strings_g['Note'].unique()):
note_set = strings_g[strings_g['Note'] == note]
fig.add_scatter(x = note_set['Mass/length [kg/m]'],
y = note_set['Tension [N]'],
name = note,
marker = {'color': 'Grey',
'size': 1},
hoverinfo = 'skip')
fig.update_traces(mode = 'lines+markers')
fig.show()
Other instruments¶
A further file contains similar data for a range of instrument strings, including a subset of the guitar sets shown above. For instruments with re-entrant tunings, such as the higher ukuleles, strings are given in pitch order rather than strringing order.
The data can be input and processed in a similar way to the guitar data.
strings = pd.read_csv('String data various.csv')
strings['Tension [N]'] = strings['Tension (kg)'] * 9.81
strings['Scale [m]'] = strings['Scale (cm)']/100
for i, row in strings.iterrows():
semitones = note_names.index(row['Note'][:-1])
strings.loc[i, 'Frequency [Hz]'] = 440 * 2** \
(int(row['Note'][-1]) - 4 + (semitones - 9)/12)
strings['Mass/length [kg/m]'] = strings['Tension [N]']/ \
(4*(strings['Scale [m]'])**2 * strings['Frequency [Hz]']**2)
strings['Name'] = strings['Type'] + ' ' + \
strings['Instrument']
notype = pd.isnull(strings['Name'])
strings.loc[notype, 'Name'] = strings.loc[notype, 'Instrument']
For this set the string type is not included in the name to save space; most are medium apart from the electric guitar, which is light as these are widely used.
The resulting chart can be produce as before, and contours of wavespeed and impedance can be added in the same way as for the fluids.
mu = np.logspace(-3.7, -0.8, 100)
tau = np.logspace(1, 3, 100)
mu_grid, tau_grid = np.meshgrid(mu, tau)
z = np.sqrt(tau_grid*mu_grid)
c = np.sqrt(tau_grid/mu_grid)
fig = px.scatter(strings,
x = 'Mass/length [kg/m]',
y = 'Tension [N]',
color = 'Name',
log_x = True,
log_y = True)
fig.update_traces(mode = 'lines+markers')
fig.add_contour(x = mu,
y = tau,
z = np.log10(z),
name = 'log10(z [Ns/m])',
contours = {'coloring': 'none',
'start': -2,
'end': 1,
'size': 0.5,
'showlabels': True},
hoverinfo = 'skip')
fig.add_contour(x = mu,
y = tau,
z = np.log10(c),
name = 'log10(c [m/s])',
contours = {'coloring': 'none',
'start': 1,
'end': 3,
'size': 0.5,
'showlabels': True},
line = {'color': 'Blue'},
hoverinfo = 'skip')
fig.write_html('Strings_chart.html')
fig.show()
The slowest waves are on the bottom string of the electric bass, while the fastest are in the top string of the tenor guitar, slosely followed by the mandolin.
We can also use other combinations of properties to make charts, such as mass per unit length vs frequency.
fig = px.scatter(strings,
x = 'Frequency [Hz]',
y = 'Mass/length [kg/m]',
color = 'Name',
log_x = True,
log_y = True,
width = 600)
fig.update_traces(mode = 'lines+markers')
fig.show()
The resulting points cluster in two regions. Plotting the same data but colouring it according to the excitation method…
fig = px.scatter(strings,
x = 'Frequency [Hz]',
y = 'Mass/length [kg/m]',
hover_name = 'Name',
color = 'Method',
log_x = True,
log_y = True,
width = 600)
fig.show()
…reveals that these clusters largely correspond to plucked and bowed strings with two exceptions: the mandolin sits with the bowed strings and the erhu with the plucked strings. The mandolin is strung in pairs, but so is the oud (apart from its bottom string) and that sits at the edge of the plucked cluster tht is furthest from the bowed cluster.
Although tension is not explicitly plotted on this chart it implicitly enters via the identity \(4f^2\mu = \tau/L^2\), and the restoring force at the plucking/bowing point will scale with \(\tau/L\).
Mass, tension and frequency can be shown on a 3d plot as follows.
fig = px.line_3d(strings,
x = 'Mass/length [kg/m]',
y = 'Tension [N]',
z = 'Frequency [Hz]',
color = 'Name',
text = 'Instrument',
log_x = True,
log_y = True,
log_z = True)
fig.update_traces(mode = 'lines+markers',
marker = {'size': 3})
fig.show()
Viewed on these axes the bottom two strings of the mandolin and the violin are close, but the top two are further apart.
Scale can also be used as the third axis, in which case each instrument’s string-set will lie in a horizontal plane, with the exception of the banjo.
fig = px.line_3d(strings,
x = 'Mass/length [kg/m]',
y = 'Tension [N]',
z = 'Scale [m]',
color = 'Name',
text = 'Instrument',
log_x = True,
log_y = True,
log_z = True)
fig.update_traces(mode = 'lines+markers',
marker = {'size': 3})
fig.show()
The two instruments that lie closest on both sets of axes are the ruan and the tenor guitar.
Discussion and further work¶
All these charts, whether for fluids or strings, are subjective in that their appearance depends on which candidates are included and excuded from the datasets.
There are several instruments whose strings would be interesting to add to the string charts:
Theorbo: as well as having two scale lengths these have lower notes and longer strings than any instrument included so far.
Harp:
It would also be interesting to add keyboard strings, i.e. clavichord, harpsichord, and piano. Furthermore it would be interesting to compare early wood-framed piano strings to modern iron-framed strings, and the ‘short-scale’ upright piano with the concert grand. It seems likely that the strings of a clavichord would be similar to those of a hammered dulcimer but it would be nice to confirm this.
Conclusion¶
Taking an ‘Ashby chart’ approach to properties of interest to acousticians can provide useful insights that could be useful in acoustics education.
- ASH19
ASHRAE. Designation and safety classification of refrigerants. Standard, American Society of Heating, Refrigerating and Air-Conditioning Engineers, 2019. URL: https://www.ashrae.org/technical-resources/standards-and-guidelines/read-only-versions-of-ashrae-standards.
- EWLFed
Mark O. McLinden Eric W. Lemmon and Daniel G. Friend. NIST Chemistry WebBook, NIST Standard Reference Database Number 69, chapter Thermophysical Properties of Fluid Systems. National Institute of Standards and Technology, Gaithersburg MD, 20899, undated. doi:10.18434/T4D303.
- Sch63
John Schelleng. The violin as a circuit. J. Acoust. Soc. Am., 35(3):326–338, 1963. doi:10.1121/1.1918462.
- Wal09
Chris Waltham. A balsa violin. Am. J. Phys, 77(1):30–35, 2009. doi:10.1121/1.1913322.