-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wave amplitude changes when frequency is changed #435
Comments
I tested this and observed the same phenomenon in master. At min frequency and max amplitude for water, I recorded a level of about 81px in the graph. At max frequency and max amplitude, I recorded a level of about 55px in the graph. That is about 68% of the original value. I am not aware of any code that would be explicitly responsible for this deviation. I hypothesized that it's a numerical sampling issue: that the long wavelength waves have a better opportunity to reach the full amplitude and that short wavelength waves may skip over it. I output the values for each case and observed behavior like this: High Frequency wave:
Low frequency wave:
Note how the high frequency wave maxes out at Note also how the high frequency wave takes a value of >=11 for 5 sample steps, whereas the low frequency wave takes a value of >=11 for 19 sample steps. I wonder if the high frequency peak is getting "washed out" during the propagation whereas the low frequency peak is more resilient. If we were using an analytical model we wouldn't see this problem at all, and I presume this problem would disappear even for the emergent model as the lattice density tended toward infinity. @arouinfar or @ariel-phet can you please recommend how to proceed? |
@samreid @arouinfar as a note, I just tested and this bug appears in the Java version, so it is assuredly part of the original model. @samreid my instinct would be that this discrepancy is related to the damping in the model. Basically the higher the frequency, the more cycles to damp. Would it be possible to try a frequency dependent damping constant in the model and see if that alleviates the issue? |
We currently do not apply damping in the main part of the wave area lattice. We experimented with it briefly a few times and ultimately decided it caused more problems than it solved. Here's a brief history: October 8, 2017: Model is introduced with no damping March 13, 2018: Added damping, with a scale factor of Jan 6, 2019: Added damping scale factor of Brainstorming some ideas:
|
@samreid what then causes the wave to decrease in amplitude as it gets farther from the source, what factor is doing that in the model? It seems that would be the first place to investigate |
We are using the discretized wave equation approximation described in http://www.mtnmath.com/whatth/node47.html, in combination with a numerical approximation of absorbing boundary conditions along the edges described in https://www.phy.ornl.gov/csep/sw/node22.html. Here is the main part of the physics: for ( let i = 1; i < width - 1; i++ ) {
for ( let j = 1; j < height - 1; j++ ) {
const neighborSum = matrix1.get( i + 1, j ) +
matrix1.get( i - 1, j ) +
matrix1.get( i, j + 1 ) +
matrix1.get( i, j - 1 );
const m1ij = matrix1.get( i, j );
const value = m1ij * 2 - matrix2.get( i, j ) + WAVE_SPEED_SQUARED * ( neighborSum + m1ij * -4 );
matrix0.set( i, j, value );
if ( Math.abs( value ) > LIGHT_VISIT_THRESHOLD ) {
this.visitedMatrix.set( i, j, 1 );
}
}
} The decrease in amplitude as a function of position is an implicit emergent behavior from this computation rather than an explicit factor. I ran two experiments to test different hypotheses for what is causing this problem:
One thing noticed while investigating (1) was that simply increasing the lattice size yielded a decrease in the amplitude of the wave further from the source. Is that as expected since the source is still a point source (and hence proportionately smaller)? That led to a following idea to try: instead of a single cell on the lattice oscillating, I tried a 5-point "plus sign" shape like so: lattice.setCurrentValue( i, j, waveValue );
lattice.setCurrentValue( i + 1, j, waveValue * 0.8 );
lattice.setCurrentValue( i - 1, j, waveValue * 0.8 );
lattice.setCurrentValue( i, j + 1, waveValue * 0.8 );
lattice.setCurrentValue( i, j - 1, waveValue * 0.8 ); and the amplitude boosted from here to here (animated gif to help see the difference) I thought we could use that to artificially inflate the source for low frequency waves, however it is unclear how we could change this into a continuous rather than discrete change. I tried using something like: lattice.setCurrentValue( i, j, waveValue );
lattice.setCurrentValue( i + 1, j, waveValue * alpha );
lattice.setCurrentValue( i - 1, j, waveValue * alpha );
lattice.setCurrentValue( i, j + 1, waveValue * alpha );
lattice.setCurrentValue( i, j - 1, waveValue * alpha ); but it's not obvious how to compute alpha as a function of the frequency. Having alpha go to 0 or averaging it with the previous lattice value seems problematic, and I'm not sure what other ramifications of this pattern might be. What do you think might be happening? |
Also, I noticed something else when looking at the examples at the bottom of http://www.mtnmath.com/whatth/node47.html At Time=-1, the first wave values starts as 100, so the total value over the lattice sums up to 100. And it seems it is basically preserving the total "amount" on the lattice, just spreading it out. I tested to see if our wave equation has the same preservation property by running a similar experiment (a single cell set to v=100), and it seems it preserved the value nicely until it hit the boundaries: Index: js/common/model/Lattice.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- js/common/model/Lattice.js (revision a0a15cc4500ca34f6fec065cebb21ce192894883)
+++ js/common/model/Lattice.js (date 1566442285723)
@@ -270,15 +270,37 @@
*/
step() {
+ window.count = window.count || 0;
+ window.count++;
+
// Move to the next matrix
this.currentMatrixIndex = ( this.currentMatrixIndex - 1 + this.matrices.length ) % this.matrices.length;
- const matrix0 = this.matrices[ ( this.currentMatrixIndex + 0 ) % this.matrices.length ];
- const matrix1 = this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ];
- const matrix2 = this.matrices[ ( this.currentMatrixIndex + 2 ) % this.matrices.length ];
+ const matrix0 = this.matrices[ ( this.currentMatrixIndex + 0 ) % this.matrices.length ]; // current
+ const matrix1 = this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ]; // previous
+ const matrix2 = this.matrices[ ( this.currentMatrixIndex + 2 ) % this.matrices.length ]; // 2 steps ago
+
const width = matrix0.getRowDimension();
const height = matrix0.getColumnDimension();
+ if ( window.count === 100 ) {
+ matrix1.set( Math.round( width / 2 ), Math.round( height / 2 ), 100 );
+ matrix2.set( Math.round( width / 2 ), Math.round( height / 2 ), 100 );
+ //
+ // matrix1.set( Math.round( width / 2 )+1, Math.round( height / 2 ),1000 );
+ // matrix2.set( Math.round( width / 2 )+1, Math.round( height / 2 ),1000 );
+ //
+ // matrix1.set( Math.round( width / 2 )-1, Math.round( height / 2 ),1000 );
+ // matrix2.set( Math.round( width / 2 )-1, Math.round( height / 2 ),1000 );
+ //
+ // matrix1.set( Math.round( width / 2 ), Math.round( height / 2 )+1,1000 );
+ // matrix2.set( Math.round( width / 2 ), Math.round( height / 2 )+1,1000 );
+ //
+ // matrix1.set( Math.round( width / 2 ), Math.round( height / 2 )-1,1000 );
+ // matrix2.set( Math.round( width / 2 ), Math.round( height / 2 )-1,1000 );
+ }
+
+ let sum = 0;
// Main loop, doesn't update cells on the edges
for ( let i = 1; i < width - 1; i++ ) {
for ( let j = 1; j < height - 1; j++ ) {
@@ -289,12 +311,14 @@
const m1ij = matrix1.get( i, j );
const value = m1ij * 2 - matrix2.get( i, j ) + WAVE_SPEED_SQUARED * ( neighborSum + m1ij * -4 );
matrix0.set( i, j, value );
+ sum += value;
- if ( Math.abs( value ) > LIGHT_VISIT_THRESHOLD ) {
+ if ( Math.abs( value ) > LIGHT_VISIT_THRESHOLD || true ) {
this.visitedMatrix.set( i, j, 1 );
}
}
}
+ console.log( sum );
// Numerical computation of absorbing boundary conditions, under the assumption that the wave is perpendicular
// to the edge, see https://www.phy.ornl.gov/csep/sw/node22.html. This assumption does not hold everywhere, but
@@ -305,36 +329,36 @@
// cb => WAVE_SPEED
// Left edge
- let i = 0;
- for ( let j = 0; j < height; j++ ) {
- const sum = matrix1.get( i, j ) + matrix1.get( i + 1, j ) - matrix2.get( i + 1, j ) + WAVE_SPEED *
- ( matrix1.get( i + 1, j ) - matrix1.get( i, j ) + matrix2.get( i + 1, j ) - matrix2.get( i + 2, j ) );
- matrix0.set( i, j, sum );
- }
-
- // Right edge
- i = width - 1;
- for ( let j = 0; j < height; j++ ) {
- const sum = matrix1.get( i, j ) + matrix1.get( i - 1, j ) - matrix2.get( i - 1, j ) + WAVE_SPEED *
- ( matrix1.get( i - 1, j ) - matrix1.get( i, j ) + matrix2.get( i - 1, j ) - matrix2.get( i - 2, j ) );
- matrix0.set( i, j, sum );
- }
-
- // Top edge
- let j = 0;
- for ( let i = 0; i < width; i++ ) {
- const sum = matrix1.get( i, j ) + matrix1.get( i, j + 1 ) - matrix2.get( i, j + 1 ) + WAVE_SPEED *
- ( matrix1.get( i, j + 1 ) - matrix1.get( i, j ) + matrix2.get( i, j + 1 ) - matrix2.get( i, j + 2 ) );
- matrix0.set( i, j, sum );
- }
-
- // Bottom edge
- j = height - 1;
- for ( let i = 0; i < width; i++ ) {
- const sum = matrix1.get( i, j ) + matrix1.get( i, j - 1 ) - matrix2.get( i, j - 1 ) + WAVE_SPEED *
- ( matrix1.get( i, j - 1 ) - matrix1.get( i, j ) + matrix2.get( i, j - 1 ) - matrix2.get( i, j - 2 ) );
- matrix0.set( i, j, sum );
- }
+ // let i = 0;
+ // for ( let j = 0; j < height; j++ ) {
+ // const sum = matrix1.get( i, j ) + matrix1.get( i + 1, j ) - matrix2.get( i + 1, j ) + WAVE_SPEED *
+ // ( matrix1.get( i + 1, j ) - matrix1.get( i, j ) + matrix2.get( i + 1, j ) - matrix2.get( i + 2, j ) );
+ // matrix0.set( i, j, sum );
+ // }
+ //
+ // // Right edge
+ // i = width - 1;
+ // for ( let j = 0; j < height; j++ ) {
+ // const sum = matrix1.get( i, j ) + matrix1.get( i - 1, j ) - matrix2.get( i - 1, j ) + WAVE_SPEED *
+ // ( matrix1.get( i - 1, j ) - matrix1.get( i, j ) + matrix2.get( i - 1, j ) - matrix2.get( i - 2, j ) );
+ // matrix0.set( i, j, sum );
+ // }
+ //
+ // // Top edge
+ // let j = 0;
+ // for ( let i = 0; i < width; i++ ) {
+ // const sum = matrix1.get( i, j ) + matrix1.get( i, j + 1 ) - matrix2.get( i, j + 1 ) + WAVE_SPEED *
+ // ( matrix1.get( i, j + 1 ) - matrix1.get( i, j ) + matrix2.get( i, j + 1 ) - matrix2.get( i, j + 2 ) );
+ // matrix0.set( i, j, sum );
+ // }
+ //
+ // // Bottom edge
+ // j = height - 1;
+ // for ( let i = 0; i < width; i++ ) {
+ // const sum = matrix1.get( i, j ) + matrix1.get( i, j - 1 ) - matrix2.get( i, j - 1 ) + WAVE_SPEED *
+ // ( matrix1.get( i, j - 1 ) - matrix1.get( i, j ) + matrix2.get( i, j - 1 ) - matrix2.get( i, j - 2 ) );
+ // matrix0.set( i, j, sum );
+ // }
}
}
Index: js/common/WaveInterferenceConstants.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- js/common/WaveInterferenceConstants.js (revision a0a15cc4500ca34f6fec065cebb21ce192894883)
+++ js/common/WaveInterferenceConstants.js (date 1566441304471)
@@ -85,7 +85,7 @@
FEMTO: 1E-15,
// Cell that oscillates, specified as an offset from the origin of the lattice (includes damping region).
- POINT_SOURCE_HORIZONTAL_COORDINATE: Util.roundSymmetric( 3 * CALIBRATION_SCALE ) + LATTICE_PADDING,
+ POINT_SOURCE_HORIZONTAL_COORDINATE: Math.round( WaveInterferenceQueryParameters.latticeSize / 2 ),
// The lattice must have an odd dimension, so that there can be a cell exactly in the middle (for a single-cell
// oscillator), symmetry for the two oscillator screen, and so the 1-cell wide barrier can appear directly in the
Next, I moved the point source to the center of the lattice, turned off damping and created this patch to test the sum vs time for our oscillator. I found the sum is not preserved, but increased linearly (note the wave starts on the negative amplitude side). I wonder if the time-dependent slope at the oscillator creates this? Or maybe something is wrong? I'm confused and uncertain. Index: js/common/model/Lattice.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- js/common/model/Lattice.js (revision a0a15cc4500ca34f6fec065cebb21ce192894883)
+++ js/common/model/Lattice.js (date 1566440838367)
@@ -270,15 +270,20 @@
*/
step() {
+ window.count = window.count || 0;
+ window.count++;
+
// Move to the next matrix
this.currentMatrixIndex = ( this.currentMatrixIndex - 1 + this.matrices.length ) % this.matrices.length;
- const matrix0 = this.matrices[ ( this.currentMatrixIndex + 0 ) % this.matrices.length ];
- const matrix1 = this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ];
- const matrix2 = this.matrices[ ( this.currentMatrixIndex + 2 ) % this.matrices.length ];
+ const matrix0 = this.matrices[ ( this.currentMatrixIndex + 0 ) % this.matrices.length ]; // current
+ const matrix1 = this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ]; // previous
+ const matrix2 = this.matrices[ ( this.currentMatrixIndex + 2 ) % this.matrices.length ]; // 2 steps ago
+
const width = matrix0.getRowDimension();
const height = matrix0.getColumnDimension();
+ let sum = 0;
// Main loop, doesn't update cells on the edges
for ( let i = 1; i < width - 1; i++ ) {
for ( let j = 1; j < height - 1; j++ ) {
@@ -289,12 +294,14 @@
const m1ij = matrix1.get( i, j );
const value = m1ij * 2 - matrix2.get( i, j ) + WAVE_SPEED_SQUARED * ( neighborSum + m1ij * -4 );
matrix0.set( i, j, value );
+ sum += value;
- if ( Math.abs( value ) > LIGHT_VISIT_THRESHOLD ) {
+ if ( Math.abs( value ) > LIGHT_VISIT_THRESHOLD || true ) {
this.visitedMatrix.set( i, j, 1 );
}
}
}
+ console.log( sum );
// Numerical computation of absorbing boundary conditions, under the assumption that the wave is perpendicular
// to the edge, see https://www.phy.ornl.gov/csep/sw/node22.html. This assumption does not hold everywhere, but
@@ -305,36 +312,36 @@
// cb => WAVE_SPEED
// Left edge
- let i = 0;
- for ( let j = 0; j < height; j++ ) {
- const sum = matrix1.get( i, j ) + matrix1.get( i + 1, j ) - matrix2.get( i + 1, j ) + WAVE_SPEED *
- ( matrix1.get( i + 1, j ) - matrix1.get( i, j ) + matrix2.get( i + 1, j ) - matrix2.get( i + 2, j ) );
- matrix0.set( i, j, sum );
- }
-
- // Right edge
- i = width - 1;
- for ( let j = 0; j < height; j++ ) {
- const sum = matrix1.get( i, j ) + matrix1.get( i - 1, j ) - matrix2.get( i - 1, j ) + WAVE_SPEED *
- ( matrix1.get( i - 1, j ) - matrix1.get( i, j ) + matrix2.get( i - 1, j ) - matrix2.get( i - 2, j ) );
- matrix0.set( i, j, sum );
- }
-
- // Top edge
- let j = 0;
- for ( let i = 0; i < width; i++ ) {
- const sum = matrix1.get( i, j ) + matrix1.get( i, j + 1 ) - matrix2.get( i, j + 1 ) + WAVE_SPEED *
- ( matrix1.get( i, j + 1 ) - matrix1.get( i, j ) + matrix2.get( i, j + 1 ) - matrix2.get( i, j + 2 ) );
- matrix0.set( i, j, sum );
- }
-
- // Bottom edge
- j = height - 1;
- for ( let i = 0; i < width; i++ ) {
- const sum = matrix1.get( i, j ) + matrix1.get( i, j - 1 ) - matrix2.get( i, j - 1 ) + WAVE_SPEED *
- ( matrix1.get( i, j - 1 ) - matrix1.get( i, j ) + matrix2.get( i, j - 1 ) - matrix2.get( i, j - 2 ) );
- matrix0.set( i, j, sum );
- }
+ // let i = 0;
+ // for ( let j = 0; j < height; j++ ) {
+ // const sum = matrix1.get( i, j ) + matrix1.get( i + 1, j ) - matrix2.get( i + 1, j ) + WAVE_SPEED *
+ // ( matrix1.get( i + 1, j ) - matrix1.get( i, j ) + matrix2.get( i + 1, j ) - matrix2.get( i + 2, j ) );
+ // matrix0.set( i, j, sum );
+ // }
+ //
+ // // Right edge
+ // i = width - 1;
+ // for ( let j = 0; j < height; j++ ) {
+ // const sum = matrix1.get( i, j ) + matrix1.get( i - 1, j ) - matrix2.get( i - 1, j ) + WAVE_SPEED *
+ // ( matrix1.get( i - 1, j ) - matrix1.get( i, j ) + matrix2.get( i - 1, j ) - matrix2.get( i - 2, j ) );
+ // matrix0.set( i, j, sum );
+ // }
+ //
+ // // Top edge
+ // let j = 0;
+ // for ( let i = 0; i < width; i++ ) {
+ // const sum = matrix1.get( i, j ) + matrix1.get( i, j + 1 ) - matrix2.get( i, j + 1 ) + WAVE_SPEED *
+ // ( matrix1.get( i, j + 1 ) - matrix1.get( i, j ) + matrix2.get( i, j + 1 ) - matrix2.get( i, j + 2 ) );
+ // matrix0.set( i, j, sum );
+ // }
+ //
+ // // Bottom edge
+ // j = height - 1;
+ // for ( let i = 0; i < width; i++ ) {
+ // const sum = matrix1.get( i, j ) + matrix1.get( i, j - 1 ) - matrix2.get( i, j - 1 ) + WAVE_SPEED *
+ // ( matrix1.get( i, j - 1 ) - matrix1.get( i, j ) + matrix2.get( i, j - 1 ) - matrix2.get( i, j - 2 ) );
+ // matrix0.set( i, j, sum );
+ // }
}
}
Index: js/common/WaveInterferenceConstants.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- js/common/WaveInterferenceConstants.js (revision a0a15cc4500ca34f6fec065cebb21ce192894883)
+++ js/common/WaveInterferenceConstants.js (date 1566441304471)
@@ -85,7 +85,7 @@
FEMTO: 1E-15,
// Cell that oscillates, specified as an offset from the origin of the lattice (includes damping region).
- POINT_SOURCE_HORIZONTAL_COORDINATE: Util.roundSymmetric( 3 * CALIBRATION_SCALE ) + LATTICE_PADDING,
+ POINT_SOURCE_HORIZONTAL_COORDINATE: Math.round( WaveInterferenceQueryParameters.latticeSize / 2 ),
// The lattice must have an odd dimension, so that there can be a cell exactly in the middle (for a single-cell
// oscillator), symmetry for the two oscillator screen, and so the 1-cell wide barrier can appear directly in the
|
@samreid not sure if this data point helps, but in playing around, I noticed that this amplitude discrepancy does not appear to occur if in Pulse mode (if looking at the positive peak of the pulse, it is the same height above zero for the lowest and highest frequency light wave). |
I tested and confirmed this problem is not present in pulse mode for light waves. However, I also noticed this problem is not present for continuous light waves. This is likely because the range between frequencies on the lattice is much smaller. I am still seeing the problem for max and min amplitude for water waves. But this may lead us to a potential solution: we could narrow the range of frequencies allowed for water and sound. |
Aug 29 notes AP: Tweak the initial amplitude base, fudge factor in the model but not in the view. |
@arouinfar suggests deferring this for RC.2. |
I would push against this a bit. I think it's important to have a range that would allow for doubling or quadrupling the frequency. For light, that's not physically possible. Currently the sound scene allows for doubling the frequency, which corresponds to one octave. Water allows for quadrupling the frequency, which is nice because you can see really long wavelengths. |
The proposal at the bottom of #435 (comment) (expanding the radius of the source) may be preferable to running one model for the model (with the fudge factor) and another model for the view (without the fudge factor). |
Leaving deferred until we publish the diffraction screen, but still keeping assigned to @samreid as this issue could be investigated independent of the RC cycle presumably if he has some time. |
It seems like making the next release contingent on a robust solution here may push out the publication date too far. Should this be deferred until a future release? |
Yes, it seems reasonable to continue deferring. |
Considering this sim has been out in the wild for sometime without complaints, I am fine with continuing to defer. Unassigning myself. |
@arouinfar forwarded this message from a client. Keeping the amplitude slider at the same value and changing the frequency ends up changing the amplitude of the wave:
The text was updated successfully, but these errors were encountered: