From eb6522e5b36bc1f42dab995c3a8caabe2a7674ff Mon Sep 17 00:00:00 2001 From: jbphet Date: Fri, 19 Jun 2020 13:53:25 -0600 Subject: [PATCH] added pressure to state, see https://github.com/phetsims/states-of-matter/issues/296 --- js/common/model/MultipleParticleModel.js | 21 ++++++------- js/common/model/TimeSpanDataQueue.js | 11 +++++-- .../model/engine/AbstractVerletAlgorithm.js | 30 +++++++++++++++++-- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/js/common/model/MultipleParticleModel.js b/js/common/model/MultipleParticleModel.js index 88db8a5e..2c88ebab 100644 --- a/js/common/model/MultipleParticleModel.js +++ b/js/common/model/MultipleParticleModel.js @@ -503,10 +503,8 @@ class MultipleParticleModel extends PhetioObject { // remove all atoms this.removeAllAtoms(); - // Reinitialize the model parameters, but not if this is a phet-io setState operation. - if ( !phet.joist.sim.isSettingPhetioStateProperty.value ) { - this.initializeModelParameters(); - } + // Reinitialize the model parameters. + this.initializeModelParameters(); // Set the model parameters that are dependent upon the substance being simulated. switch( substance ) { @@ -549,11 +547,8 @@ class MultipleParticleModel extends PhetioObject { throw( new Error( 'unsupported substance' ) ); // should never happen, debug if it does } - // In most cases, the container size is reset at this point, but when the substance is being updated using phet-io - // the container size is *not* reset, since doing so may overwrite a size that was specifically set by the user. - if ( !phet.joist.sim.isSettingPhetioStateProperty.value ) { - this.containerHeightProperty.reset(); - } + // Reset the container height. + this.containerHeightProperty.reset(); // Make sure the normalized container dimensions are correct for the substance and the current non-normalize size. this.updateNormalizedContainerDimensions(); @@ -1457,7 +1452,8 @@ class MultipleParticleModel extends PhetioObject { gravitationalAcceleration: this.gravitationalAcceleration, normalizedLidVelocityY: this.normalizedLidVelocityY, heatingCoolingAmount: this.heatingCoolingAmountProperty.value, - moleculeDataSet: MoleculeForceAndMotionDataSetIO.toStateObject( this.moleculeDataSet ) + moleculeDataSet: MoleculeForceAndMotionDataSetIO.toStateObject( this.moleculeDataSet ), + moleculeForcesAndMotionCalculatorPressure: this.moleculeForceAndMotionCalculator.pressureProperty.value } }; } @@ -1476,7 +1472,8 @@ class MultipleParticleModel extends PhetioObject { containerHeight: stateObject.private.containerHeight, normalizedLidVelocityY: stateObject.private.normalizedLidVelocityY, heatingCoolingAmount: stateObject.private.heatingCoolingAmount, - moleculeDataSet: MoleculeForceAndMotionDataSetIO.fromStateObject( stateObject.private.moleculeDataSet ) + moleculeDataSet: MoleculeForceAndMotionDataSetIO.fromStateObject( stateObject.private.moleculeDataSet ), + moleculeForcesAndMotionCalculatorPressure: stateObject.private.moleculeForcesAndMotionCalculatorPressure }; } @@ -1494,10 +1491,10 @@ class MultipleParticleModel extends PhetioObject { this.gravitationalAcceleration = state.gravitationalAcceleration; this.normalizedLidVelocityY = state.normalizedLidVelocityY; this.moleculeDataSet = state.moleculeDataSet; + this.moleculeForceAndMotionCalculator.presetPressure( state.moleculeForcesAndMotionCalculatorPressure ); } } - // static constants MultipleParticleModel.PARTICLE_CONTAINER_WIDTH = CONTAINER_WIDTH; MultipleParticleModel.PARTICLE_CONTAINER_INITIAL_HEIGHT = CONTAINER_INITIAL_HEIGHT; diff --git a/js/common/model/TimeSpanDataQueue.js b/js/common/model/TimeSpanDataQueue.js index 5ee27c78..242b85f6 100644 --- a/js/common/model/TimeSpanDataQueue.js +++ b/js/common/model/TimeSpanDataQueue.js @@ -14,7 +14,7 @@ function TimeSpanDataQueue( length, maxTimeSpan ) { this.length = length; this.maxTimeSpan = maxTimeSpan; - // allocate a fixed-size array so that subsequent allocations are not needed + // @private - array where entries are kept this.dataQueue = new Array( length ); // initialize the data array with a set of reusable objects so that subsequent allocations are not needed @@ -22,10 +22,12 @@ function TimeSpanDataQueue( length, maxTimeSpan ) { this.dataQueue[ i ] = { deltaTime: 0, value: null }; } - // initialize the variables used to make this thing work + // @public (read-only) {number} - the total of all values currently in the queue + this.total = 0; + + // @private - variables used to make this thing work this.head = 0; this.tail = 0; - this.total = 0; this.timeSpan = 0; } @@ -38,6 +40,7 @@ inherit( Object, TimeSpanDataQueue, { * span, and also updates the total value and the current time span * @param value * @param dt + * @public */ add: function( value, dt ) { @@ -61,6 +64,7 @@ inherit( Object, TimeSpanDataQueue, { while ( this.timeSpan > this.maxTimeSpan ) { const nextTail = ( this.tail + 1 ) % this.length; if ( nextTail === nextHead ) { + // nothing more can be removed, so bail assert && assert( false, 'time span exceeded, but nothing appears to be in the queue - probably a bug' ); break; @@ -73,6 +77,7 @@ inherit( Object, TimeSpanDataQueue, { /** * clear all data from the queue + * @public */ clear: function() { this.head = 0; diff --git a/js/common/model/engine/AbstractVerletAlgorithm.js b/js/common/model/engine/AbstractVerletAlgorithm.js index 4122c7eb..e2a7563f 100644 --- a/js/common/model/engine/AbstractVerletAlgorithm.js +++ b/js/common/model/engine/AbstractVerletAlgorithm.js @@ -16,7 +16,8 @@ import TimeSpanDataQueue from '../TimeSpanDataQueue.js'; // Constants that control the pressure calculation. The size of the pressure accumulator assumes a max sim rate of // 1 / 60, which derives from the standard 60 FPS rate at which browsers currently run. May need to go up someday. const PRESSURE_CALC_TIME_WINDOW = 12; // in seconds, empirically determined to be responsive but not jumpy -const PRESSURE_ACCUMULATOR_LENGTH = Math.ceil( PRESSURE_CALC_TIME_WINDOW / ( 1 / 60 ) * 1.1 ); +const NOMINAL_DT = 1 / 60; // in seconds, assuming 60 fps +const PRESSURE_ACCUMULATOR_LENGTH = Math.ceil( PRESSURE_CALC_TIME_WINDOW / NOMINAL_DT * 1.1 ); // constants that control when the container explodes const EXPLOSION_PRESSURE = 41; // in model units, empirically determined @@ -45,7 +46,7 @@ function AbstractVerletAlgorithm( multipleParticleModel ) { // during execution of the Verlet algorithm, must be cleared by the client. this.lidChangedParticleVelocity = false; - // @private, moving time window queue for tracking the pressure data + // @private {TimeSpanDataQueue}, moving time window queue for tracking the pressure data this.pressureAccumulatorQueue = new TimeSpanDataQueue( PRESSURE_ACCUMULATOR_LENGTH, PRESSURE_CALC_TIME_WINDOW ); // @private, tracks time above the explosion threshold @@ -292,6 +293,31 @@ inherit( Object, AbstractVerletAlgorithm, { } }, + /** + * This method allows the caller to set an initial value for the pressure. It was added in support of phet-io state + * setting so that the pressure calculation could be set to an initial value above zero that reflected the state + * extracted from another instance of the simulation. + * @param {number} pressure + */ + presetPressure: function( pressure ) { + + assert && assert( + phet.joist.sim.isSettingPhetioStateProperty.value, + 'this method is intended for use during state setting only' + ); + + // get rid of any accumulated values + this.pressureAccumulatorQueue.clear(); + + // calculate the instantaneous sample value needed to make the overall pressure be the needed value + const pressureSampleInstantaneousValue = pressure * PRESSURE_CALC_TIME_WINDOW / PRESSURE_ACCUMULATOR_LENGTH; + + // add the entries to the accumulator + _.times( PRESSURE_ACCUMULATOR_LENGTH, () => { + this.pressureAccumulatorQueue.add( pressureSampleInstantaneousValue, NOMINAL_DT ); + } ); + }, + // static final PARTICLE_INTERACTION_DISTANCE_THRESH_SQRD: 6.25,