Skip to content

Commit

Permalink
added ability to lock out bicycle pump when it is injecting too quick…
Browse files Browse the repository at this point in the history
…ly, see #276 and #292
  • Loading branch information
jbphet committed Jun 12, 2020
1 parent 592a800 commit b0459fb
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 41 deletions.
84 changes: 45 additions & 39 deletions js/common/model/MultipleParticleModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import Emitter from '../../../../axon/js/Emitter.js';
import EnumerationProperty from '../../../../axon/js/EnumerationProperty.js';
import NumberProperty from '../../../../axon/js/NumberProperty.js';
import ObservableArray from '../../../../axon/js/ObservableArray.js';
import Property from '../../../../axon/js/Property.js';
import Range from '../../../../dot/js/Range.js';
import Utils from '../../../../dot/js/Utils.js';
import Vector2 from '../../../../dot/js/Vector2.js';
Expand Down Expand Up @@ -216,21 +215,27 @@ class MultipleParticleModel extends PhetioObject {
} );

// @public (read-only)
this.numberOfMoleculesRangeProperty = new Property( new Range( 0, SOMConstants.MAX_NUM_ATOMS ) );
this.maxNumberOfMoleculesProperty = new NumberProperty( SOMConstants.MAX_NUM_ATOMS );

// @private {NumberProperty}
this.numMoleculesQueuedForInjectionProperty = new NumberProperty( 0 );

// @public (read-only) - indicates whether injection of additional molecules is allowed
this.isInjectionAllowedProperty = new DerivedProperty(
[
this.isPlayingProperty,
this.numMoleculesQueuedForInjectionProperty,
this.isExplodedProperty,
this.numberOfMoleculesRangeProperty,
this.containerHeightProperty
this.maxNumberOfMoleculesProperty,
this.containerHeightProperty,
this.targetNumberOfMoleculesProperty
],
( isPlaying, isExploded, numberOfMoleculesRange, containerHeight ) => {
( isPlaying, numberOfMoleculesQueuedForInjection, isExploded, maxNumberOfMoleculesProperty, containerHeight, targetNumberOfMolecules ) => {
return isPlaying &&
numberOfMoleculesQueuedForInjection < MAX_MOLECULES_QUEUED_FOR_INJECTION &&
!isExploded &&
( containerHeight / this.particleDiameter ) > this.injectionPointY &&
this.targetNumberOfMoleculesProperty.value < numberOfMoleculesRange.max;
targetNumberOfMolecules < maxNumberOfMoleculesProperty;
}
);

Expand Down Expand Up @@ -264,7 +269,6 @@ class MultipleParticleModel extends PhetioObject {
this.particleDiameter = 1;
this.minModelTemperature = null;
this.residualTime = 0;
this.numMoleculesAwaitingInjection = 0;
this.moleculeInjectionHoldoffTimer = 0;
this.injectionPointX = 0;
this.injectionPointY = 0;
Expand Down Expand Up @@ -296,17 +300,22 @@ class MultipleParticleModel extends PhetioObject {
this.containerHeightProperty.link( this.updateNormalizedContainerDimensions.bind( this ) );

// listen for new molecules being added (generally from the pump)
this.targetNumberOfMoleculesProperty.lazyLink( newValue => {
this.targetNumberOfMoleculesProperty.lazyLink( targetNumberOfMolecules => {

assert && assert(
targetNumberOfMolecules <= this.maxNumberOfMoleculesProperty.value,
'target number of molecules set above max allowed'
);

const currentNumberOfMolecules = Math.floor(
this.moleculeDataSet.numberOfAtoms / this.moleculeDataSet.atomsPerMolecule
);

if ( newValue > currentNumberOfMolecules ) {
const delta = newValue - currentNumberOfMolecules;
const numberOfMoleculesToAdd = targetNumberOfMolecules -
( currentNumberOfMolecules + this.numMoleculesQueuedForInjectionProperty.value );

for ( let i = 0; i < delta; i++ ) {
this.injectMolecule();
}
for ( let i = 0; i < numberOfMoleculesToAdd; i++ ) {
this.queueMoleculeForInjection();
}
} );

Expand Down Expand Up @@ -337,7 +346,7 @@ class MultipleParticleModel extends PhetioObject {
}

// clear the injection counter - all atoms and molecules should be accounted for at this point
this.numMoleculesAwaitingInjection = 0;
this.numMoleculesQueuedForInjectionProperty.reset();

// synchronize the positions of the scaled atoms to the normalized data set
this.syncAtomPositions();
Expand Down Expand Up @@ -559,12 +568,10 @@ class MultipleParticleModel extends PhetioObject {
// Reset the moving average of temperature differences.
this.averageTemperatureDifference.reset();

// Set the number of molecules and range for the current substance
// Set the number of molecules and range for the current substance.
const atomsPerMolecule = this.moleculeDataSet.atomsPerMolecule;
this.targetNumberOfMoleculesProperty.set( Math.floor( this.moleculeDataSet.numberOfAtoms / atomsPerMolecule ) );
this.numberOfMoleculesRangeProperty.set(
new Range( 0, Utils.toFixedNumber( SOMConstants.MAX_NUM_ATOMS / atomsPerMolecule, 0 ) )
);
this.maxNumberOfMoleculesProperty.set( Math.floor( SOMConstants.MAX_NUM_ATOMS / atomsPerMolecule ) );
}

/**
Expand Down Expand Up @@ -661,33 +668,28 @@ class MultipleParticleModel extends PhetioObject {
* Inject a new molecule of the current type. This method actually queues it for injection, actual injection
* occurs during model steps. Be aware that this silently ignores the injection request if the model is not in a
* state to support injection.
* @public
* @private
*/
injectMolecule() {

// only allow particle injection if the model is in a state that supports it
if ( this.isInjectionAllowedProperty.value ) {
this.numMoleculesAwaitingInjection = Math.min(
this.numMoleculesAwaitingInjection + 1,
MAX_MOLECULES_QUEUED_FOR_INJECTION
);
}
queueMoleculeForInjection() {
this.numMoleculesQueuedForInjectionProperty.set( this.numMoleculesQueuedForInjectionProperty.value + 1 );
}

/**
* Inject a new molecule of the current type into the model. This uses the current temperature to assign an initial
* velocity.
* @private
*/
injectMoleculeInternal() {
injectMolecule() {

// Check if conditions are right for injection of molecules and, if not, don't do it.
if ( !this.isInjectionAllowedProperty.value ||
this.moleculeDataSet.getNumberOfRemainingSlots() <= 0 ) {
assert && assert(
this.numMoleculesQueuedForInjectionProperty.value > 0,
'this method should not be called when nothing is queued for injection'
);

this.numMoleculesAwaitingInjection = 0;
return;
}
assert && assert(
this.moleculeDataSet.getNumberOfRemainingSlots() > 0,
'injection attempted when there is no room in the data set'
);

// Choose an injection angle with some amount of randomness.
const injectionAngle = ( phet.joist.random.nextDouble() - 0.5 ) * INJECTED_MOLECULE_ANGLE_SPREAD;
Expand Down Expand Up @@ -733,6 +735,8 @@ class MultipleParticleModel extends PhetioObject {
this.syncAtomPositions();

this.moleculeInjectedThisStep = true;

this.numMoleculesQueuedForInjectionProperty.set( this.numMoleculesQueuedForInjectionProperty.value - 1 );
}

/**
Expand Down Expand Up @@ -899,9 +903,8 @@ class MultipleParticleModel extends PhetioObject {
// Inject a new molecule if there is one ready and it isn't too soon after a previous injection. This is done
// before execution of the Verlet algorithm so that its velocity will be taken into account when the temperature
// is calculated.
if ( this.numMoleculesAwaitingInjection > 0 && this.moleculeInjectionHoldoffTimer === 0 ) {
this.injectMoleculeInternal();
this.numMoleculesAwaitingInjection--;
if ( this.numMoleculesQueuedForInjectionProperty.value > 0 && this.moleculeInjectionHoldoffTimer === 0 ) {
this.injectMolecule();
this.moleculeInjectionHoldoffTimer = MOLECULE_INJECTION_HOLDOFF_TIME;
}
else if ( this.moleculeInjectionHoldoffTimer > 0 ) {
Expand Down Expand Up @@ -952,6 +955,7 @@ class MultipleParticleModel extends PhetioObject {

// Prevent the substance from floating up too rapidly when heated.
if ( currentTemperature < LIQUID_TEMPERATURE && this.heatingCoolingAmountProperty.get() > 0 ) {

// This is necessary to prevent the substance from floating up when heated from absolute zero.
this.dampUpwardMotion( dt );
}
Expand Down Expand Up @@ -1244,6 +1248,7 @@ class MultipleParticleModel extends PhetioObject {
particleDiameter = SOMConstants.ADJUSTABLE_ATTRACTION_DEFAULT_RADIUS * 2;
}
else {

// Force it to neon.
substance = SubstanceType.NEON;
particleDiameter = SOMConstants.NEON_RADIUS * 2;
Expand Down Expand Up @@ -1385,6 +1390,7 @@ class MultipleParticleModel extends PhetioObject {
// state checking
assert && assert( this.isExplodedProperty.get(), 'attempt to return lid when container hadn\'t exploded' );
if ( !this.isExplodedProperty.get() ) {

// ignore request if container hasn't exploded
return;
}
Expand Down Expand Up @@ -1426,7 +1432,7 @@ class MultipleParticleModel extends PhetioObject {
// Set the phase to be gas, since otherwise the extremely high kinetic energy of the molecules causes an
// unreasonably high temperature for the molecules that remain in the container. Doing this generally cools them
// down into a more manageable state.
if ( numMoleculesOutsideContainer > 0 ) {
if ( numMoleculesOutsideContainer > 0 && this.moleculeForceAndMotionCalculator.calculatedTemperature > GAS_TEMPERATURE ) {
this.phaseStateChanger.setPhase( PhaseStateEnum.GAS );
}

Expand Down
28 changes: 26 additions & 2 deletions js/phase-changes/view/PhaseChangesScreenView.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
* @author Aaron Davis
*/

import DerivedProperty from '../../../../axon/js/DerivedProperty.js';
import ObservableArray from '../../../../axon/js/ObservableArray.js';
import Property from '../../../../axon/js/Property.js';
import Bounds2 from '../../../../dot/js/Bounds2.js';
import Range from '../../../../dot/js/Range.js';
import Vector2 from '../../../../dot/js/Vector2.js';
import ScreenView from '../../../../joist/js/ScreenView.js';
import inherit from '../../../../phet-core/js/inherit.js';
Expand Down Expand Up @@ -164,12 +166,34 @@ function PhaseChangesScreenView( model, isPotentialGraphEnabled, tandem ) {
// Hose attaches to the bottom left side of the container.
const hoseAttachmentPoint = new Vector2( nominalParticleAreaViewBounds.left, nominalParticleAreaViewBounds.bottom - 70 );

// Create a derived property that will be used to control the enabled state of the bicycle pump node.
const bicyclePumpEnabledProperty = new DerivedProperty(
[
model.isPlayingProperty,
model.isExplodedProperty,
model.maxNumberOfMoleculesProperty,
model.targetNumberOfMoleculesProperty
],
( isPlaying, isExploded, maxNumberOfMoleculesProperty, targetNumberOfMolecules ) => {
return isPlaying &&
!isExploded &&
targetNumberOfMolecules < maxNumberOfMoleculesProperty;
}
);

// Create a range property that can be provided to the bicycle pump.
const numberOfMoleculesRangeProperty = new Property( new Range( 0, model.maxNumberOfMoleculesProperty.value ) );
model.maxNumberOfMoleculesProperty.lazyLink( maxNumberOfMolecules => {
numberOfMoleculesRangeProperty.set( new Range( 0, maxNumberOfMolecules ) );
} );

// add bicycle pump node
this.pumpNode = new BicyclePumpNode(
model.targetNumberOfMoleculesProperty,
model.numberOfMoleculesRangeProperty,
numberOfMoleculesRangeProperty,
{
enabledProperty: model.isInjectionAllowedProperty,
nodeEnabledProperty: bicyclePumpEnabledProperty,
injectionEnabledProperty: model.isInjectionAllowedProperty,
translation: pumpPosition,
hoseAttachmentOffset: hoseAttachmentPoint.minus( pumpPosition ),
hoseCurviness: 1.5,
Expand Down

0 comments on commit b0459fb

Please sign in to comment.