Skip to content

Commit

Permalink
Corrected the particle paths, see #53
Browse files Browse the repository at this point in the history
  • Loading branch information
AgustinVallejo committed Oct 22, 2024
1 parent 3292192 commit 0892142
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 66 deletions.
29 changes: 17 additions & 12 deletions js/spin/model/ParticleRays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
/**
* ParticleRays contains the data for the particle ray paths in the UI.
*
* All possible paths are indexed as such:
* 0 - Source to SG1
* 1 - SG1 to infinity (top)
* 2 - SG1 to infinity (bottom)
* 3 - SG1 to SG2
* 4 - SG2 to infinity (top)
* 5 - SG2 to infinity (bottom)
* 6 - SG2 to SG3
* 7 - SG3 to infinity (top)
* 8 - SG3 to infinity (bottom)
*
* @author Agustín Vallejo
*/

Expand Down Expand Up @@ -63,28 +74,22 @@ export default class ParticleRays {
this.updatedEmitter.emit();
}

public updateFirstRay( rayPoints: Vector2[] ): void {
this.allPossiblePaths[ 0 ] = rayPoints;
}

public assignRayToParticle( particle: ParticleWithSpinModel ): void {
if ( this.isShortExperiment ) {
particle.path = [
this.activePaths[ 0 ],
this.activePaths[ particle.secondSpinUp ? 1 : 2 ]
...this.allPossiblePaths[ 0 ], // Source to SG1
...this.allPossiblePaths[ particle.secondSpinUp ? 1 : 2 ] // SG1 to infinity
];
}
else {
particle.path = [
this.activePaths[ 0 ],
this.activePaths[ particle.secondSpinUp ? 1 : 2 ],
this.activePaths[ 3 + ( particle.secondSpinUp ? 2 : 0 ) + ( particle.thirdSpinUp ? 1 : 0 ) ]
...this.allPossiblePaths[ 0 ], // Source to SG1
...this.allPossiblePaths[ particle.secondSpinUp ? 3 : 6 ], // SG1 to SG2
...this.allPossiblePaths[ 4 + ( particle.secondSpinUp ? 0 : 3 ) + ( particle.thirdSpinUp ? 0 : 1 ) ]
];
}

// Set the speed so it takes 1s to travel the first ray
particle.speed = this.activePaths[ 0 ][ 0 ].distance( this.activePaths[ 0 ][ 1 ] );

particle.updateSpeed();
}

public updateProbabilities( probabilities: number[] ): void {
Expand Down
7 changes: 7 additions & 0 deletions js/spin/model/ParticleSourceModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ export default class ParticleSourceModel {
} );

}

public reset(): void {
this.sourceModeProperty.reset();
this.positionProperty.reset();
this.currentlyShootingParticlesProperty.reset();
this.particleAmmountProperty.reset();
}
}

quantumMeasurement.register( 'ParticleSourceModel', ParticleSourceModel );
78 changes: 40 additions & 38 deletions js/spin/model/ParticleWithSpinModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@ import Vector2 from '../../../../dot/js/Vector2.js';
import Vector2Property from '../../../../dot/js/Vector2Property.js';
import quantumMeasurement from '../../quantumMeasurement.js';

// constants
const SPEED_MULTIPLIER = 1.5;

export class ParticleWithSpinModel {

public lifetime = 0;
public activeProperty: BooleanProperty;

public path: Vector2[][] = [];
public path: Vector2[] = [];

// Spin values of the particle in the XZ plane along its lifetime
public firstSpinVector = new Vector2( 0, 0 );
Expand All @@ -54,6 +57,14 @@ export class ParticleWithSpinModel {
this.positionProperty = new Vector2Property( new Vector2( 0, 0 ) );
}

/**
* Updates the speed of the particle based on the distance between the first two points of the path.
* When the multiplier is 1, it takes 1s to transverse from the emitting source to the first SG apparatus.
*/
public updateSpeed(): void {
this.speed = SPEED_MULTIPLIER * this.path[ 0 ].distance( this.path[ 1 ] );
}

public step( dt: number ): void {
if ( this.activeProperty.value ) {
this.lifetime += dt;
Expand All @@ -75,50 +86,41 @@ export class ParticleWithSpinModel {
* t = 4 to t = 5: Traveling onwards from SG2 or SG3 -> Paths 3 - 6
*/
public calculatePosition(): void {
// Travel from vector a to vector b based on speed and t
const travel = ( a: Vector2, b: Vector2, t: number ): Vector2 => {

// Travel until the final point, then stop
const clampTravel = ( a: Vector2, b: Vector2, t: number ): Vector2 => {
const direction = b.minus( a ).normalized();
return a.plus( direction.times( this.speed * t ) );
const distance = this.speed * t;
return distance < a.distance( b ) ? a.plus( direction.times( distance ) ) : b;
};

const fractionalLifetime = this.lifetime % 1;

switch( Math.floor( this.lifetime ) ) {
case 0:
// Between the first two points of the first path
this.positionProperty.value = travel( this.path[ 0 ][ 0 ], this.path[ 0 ][ 1 ], fractionalLifetime );
break;
case 1:
// In the middle of the SG1 apparatus
this.positionProperty.value = travel( this.path[ 0 ][ 1 ], this.path[ 1 ][ 0 ], 0.5 );
this.readyToBeMeasuredEmitter.emit();
break;
case 2:
// Along the second or third paths
this.positionProperty.value = travel( this.path[ 1 ][ 0 ], this.path[ 1 ][ 1 ], fractionalLifetime );
break;
case 3:
// Inside a SG2 or SG3 apparatus
if ( this.path.length > 3 ) {
this.positionProperty.value = travel( this.path[ 0 ][ 1 ], this.path[ 1 ][ 0 ], 0.5 );
this.readyToBeMeasuredEmitter.emit();
}
else {
this.positionProperty.value = travel( this.path[ 1 ][ 0 ], this.path[ 1 ][ 1 ], this.lifetime - 2 );
}
break;
default:
// Along the last paths
if ( this.path.length > 3 ) {
this.positionProperty.value = travel( this.path[ 2 ][ 0 ], this.path[ 2 ][ 1 ], this.lifetime - 4 );
// Similar to clampTravel but if it reached the end, move to the next pair of vectors until the end
const pathTravel = ( path: Vector2[], t: number ): Vector2 => {
let traveledTime = t;
let traveledDistance = this.speed * t;
let currentPosition = path[ 0 ];

for ( let i = 0; i < path.length - 1; i++ ) {
const start = path[ i ];
const end = path[ i + 1 ];
const segmentDistance = start.distance( end );

if ( traveledDistance < segmentDistance ) {
return clampTravel( start, end, traveledTime );
}
else {
this.positionProperty.value = travel( this.path[ 1 ][ 0 ], this.path[ 1 ][ 1 ], this.lifetime - 2 );
traveledTime -= segmentDistance / this.speed;
traveledDistance -= segmentDistance;
currentPosition = end;
}
break;
}
}

return currentPosition;
};

// Travel along the path
this.positionProperty.value = pathTravel( this.path, this.lifetime );

console.log( this.positionProperty.value );
}

public reset(): void {
Expand Down
29 changes: 20 additions & 9 deletions js/spin/model/SpinModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export default class SpinModel implements TModel {
tandem: providedOptions.tandem.createTandem( 'blochSphere' )
} );

const MAX_NUMBER_OF_SINGLE_PARTICLES = 10;
const MAX_NUMBER_OF_SINGLE_PARTICLES = 50;

this.currentExperimentProperty = new Property<SpinExperiment>( SpinExperiment.EXPERIMENT_1 );

Expand Down Expand Up @@ -150,6 +150,8 @@ export default class SpinModel implements TModel {
const SternGerlachs = [ this.firstSternGerlach, this.secondSternGerlach, this.thirdSternGerlach ];

this.currentExperimentProperty.link( experiment => {
this.singleParticles.forEach( particle => particle.reset() );

SternGerlachs.forEach( ( SternGerlach, index ) => {
if ( experiment.experimentSetting.length > index ) {
// TODO: Should visibility be only handled via the View? https://github.com/phetsims/quantum-preparement/issues/53
Expand Down Expand Up @@ -185,13 +187,16 @@ export default class SpinModel implements TModel {
let upProbability = 1;
upProbability = this.firstSternGerlach.prepare( this.spinStateProperty.value );
particleToActivate.secondSpinUp = dotRandom.nextDouble() < upProbability;
if ( particleToActivate.secondSpinUp ) {
upProbability = this.secondSternGerlach.prepare( this.firstSternGerlach.isZOrientedProperty ? SpinDirection.Z_PLUS : SpinDirection.X_PLUS );
particleToActivate.thirdSpinUp = dotRandom.nextDouble() < upProbability;
}
else {
const upProbability = this.thirdSternGerlach.prepare( this.firstSternGerlach.isZOrientedProperty ? SpinDirection.Z_MINUS : null );
particleToActivate.thirdSpinUp = dotRandom.nextDouble() < upProbability;

if ( this.currentExperimentProperty.value.experimentSetting.length > 1 ) {
if ( particleToActivate.secondSpinUp ) {
upProbability = this.secondSternGerlach.prepare( this.firstSternGerlach.isZOrientedProperty.value ? SpinDirection.Z_PLUS : SpinDirection.X_PLUS );
particleToActivate.thirdSpinUp = dotRandom.nextDouble() < upProbability;
}
else {
const upProbability = this.thirdSternGerlach.prepare( this.firstSternGerlach.isZOrientedProperty.value ? SpinDirection.Z_MINUS : null );
particleToActivate.thirdSpinUp = dotRandom.nextDouble() < upProbability;
}
}

this.particleRays.assignRayToParticle( particleToActivate );
Expand Down Expand Up @@ -231,7 +236,13 @@ export default class SpinModel implements TModel {
* Resets the model.
*/
public reset(): void {
// TODO, see https://github.com/phetsims/quantum-preparement/issues/1
this.singleParticles.forEach( particle => particle.reset() );
this.currentExperimentProperty.reset();
this.spinStateProperty.reset();
this.firstSternGerlach.reset();
this.secondSternGerlach.reset();
this.thirdSternGerlach.reset();
this.particleSourceModel.reset();
}

/**
Expand Down
10 changes: 5 additions & 5 deletions js/spin/view/SpinMeasurementArea.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import Bounds2 from '../../../../dot/js/Bounds2.js';
import Vector2 from '../../../../dot/js/Vector2.js';
import { Shape } from '../../../../kite/js/imports.js';
import ModelViewTransform2 from '../../../../phetcommon/js/view/ModelViewTransform2.js';
import PhetFont from '../../../../scenery-phet/js/PhetFont.js';
import { Node, Text, VBox } from '../../../../scenery/js/imports.js';
Expand Down Expand Up @@ -61,14 +62,13 @@ export default class SpinMeasurementArea extends VBox {
particleSourceNode,
firstSternGerlachNode,
secondSternGerlachNode,
thirdSternGerlachNode,
particleRayPath
thirdSternGerlachNode
]
} );

// experimentAreaNode.clipArea = Shape.bounds( experimentAreaNode.localBounds );
experimentAreaNode.clipArea = Shape.bounds( experimentAreaNode.localBounds );

// experimentAreaNode.insertChild( 0, particleRayPath );
experimentAreaNode.insertChild( 0, particleRayPath );

super( {
children: [
Expand All @@ -81,7 +81,7 @@ export default class SpinMeasurementArea extends VBox {

],
spacing: 20,
margin: 20
margin: 30
} );

}
Expand Down
5 changes: 3 additions & 2 deletions js/spin/view/SpinScreenView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import SpinStatePreparationArea from './SpinStatePreparationArea.js';

export default class SpinScreenView extends QuantumMeasurementScreenView {

public constructor( model: SpinModel, tandem: Tandem ) {
public constructor( public readonly model: SpinModel, tandem: Tandem ) {

super( {
initialMockupOpacity: 0,
Expand All @@ -41,7 +41,7 @@ export default class SpinScreenView extends QuantumMeasurementScreenView {
this.addChild( dividingLine );

const spinMeasurementArea = new SpinMeasurementArea( model, this, this.layoutBounds, tandem.createTandem( 'spinMeasurementArea' ) );
spinMeasurementArea.centerX = 2 * dividingLineX;
spinMeasurementArea.left = dividingLineX;
this.addChild( spinMeasurementArea );

// TODO: This is a temporary workaround to make the mockup opacity work. We need to refactor this to use the mockup https://github.com/phetsims/quantum-measurement/issues/53
Expand All @@ -54,6 +54,7 @@ export default class SpinScreenView extends QuantumMeasurementScreenView {
}

public override reset(): void {
this.model.reset();
super.reset();
}
}
Expand Down

0 comments on commit 0892142

Please sign in to comment.