Skip to content

Commit

Permalink
Refactor photon interaction handling to incorporate photon states and…
Browse files Browse the repository at this point in the history
… improve detection logic, see #63
  • Loading branch information
AgustinVallejo committed Dec 4, 2024
1 parent 6771ebf commit 772e5a2
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 140 deletions.
12 changes: 10 additions & 2 deletions js/photons/model/Laser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,16 @@ export default class Laser {

// Activate the photon and set its position, direction, and polarization angle.
photonToActivate.activeProperty.set( true );
photonToActivate.positionProperty.set( this.position.plusXY( xOffset, yOffset ) );
photonToActivate.directionProperty.set( this.emissionDirection );

// For each of the two possible states of the photon, set the same position and direction
photonToActivate.possibleStates.forEach( state => {
state.positionProperty.set( this.position.plusXY( xOffset, yOffset ) );
state.directionProperty.set( this.emissionDirection );
} );

// Initially, the first state is the one with 100% probability
// It makes no difference before they reach the splitter.
photonToActivate.possibleStates[ 0 ].probabilityProperty.set( 1 );
photonToActivate.polarizationAngleProperty.set( polarizationAngle );
}
}
Expand Down
6 changes: 3 additions & 3 deletions js/photons/model/Mirror.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { EmptySelfOptions } from '../../../../phet-core/js/optionize.js';
import PickRequired from '../../../../phet-core/js/types/PickRequired.js';
import { PhetioObjectOptions } from '../../../../tandem/js/PhetioObject.js';
import quantumMeasurement from '../../quantumMeasurement.js';
import Photon, { DOWN } from './Photon.js';
import Photon, { DOWN, PhotonState } from './Photon.js';
import { PhotonInteractionTestResult } from './PhotonsModel.js';
import { TPhotonInteraction } from './TPhotonInteraction.js';

Expand All @@ -40,12 +40,12 @@ export default class Mirror implements TPhotonInteraction {
this.mirrorSurfaceLine = new Line( endpoint1, endpoint2 );
}

public testForPhotonInteraction( photon: Photon, dt: number ): PhotonInteractionTestResult {
public testForPhotonInteraction( photonState: PhotonState, photon: Photon, dt: number ): PhotonInteractionTestResult {

assert && assert( photon.activeProperty.value, 'save CPU cycles - don\'t use this method with inactive photons' );

// Test for whether this photon crosses the surface of the beam splitter.
const photonIntersectionPoint = photon.getTravelPathIntersectionPoint(
const photonIntersectionPoint = photonState.getTravelPathIntersectionPoint(
this.mirrorSurfaceLine.start,
this.mirrorSurfaceLine.end,
dt
Expand Down
116 changes: 76 additions & 40 deletions js/photons/model/Photon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,63 @@ export const DOWN = new Vector2( 0, -1 );
export const LEFT = new Vector2( -1, 0 );
export const RIGHT = new Vector2( 1, 0 );

export default class Photon extends PhetioObject {
// Due to the experiment's nature, when photons are split,
// the resulting states will either be measured as vertical or horizontal.
type possiblePolarizationResult = 'vertical' | 'horizontal';

// position in 2D space
// TODO: This class could live in its own file, once the feature is fully green lit, will move https://github.com/phetsims/quantum-measurement/issues/63
/**
* PhotonState is a class that represents a possible state of a photon at a given point in time.
* It contains properties for position, direction and the probability of the photon being in that state.
*/
export class PhotonState {
public readonly positionProperty: Vector2Property;

// a unit vector that represents the direction of travel for this photon
public readonly directionProperty: Vector2Property;
public readonly probabilityProperty: NumberProperty;
public readonly polarization: possiblePolarizationResult;

public constructor( polarization: possiblePolarizationResult ) {
this.positionProperty = new Vector2Property( Vector2.ZERO );
this.directionProperty = new Vector2Property( RIGHT );
this.probabilityProperty = new NumberProperty( polarization === 'vertical' ? 1 : 0 );
this.polarization = polarization;
}

/**
* Get the point where this photon would intersect the provided line segment if it were to move for the specified
* amount of time. Returns null if the photon would not intersect the line segment.
* @param lineStart
* @param lineEnd
* @param dt - time step, in seconds
*/
public getTravelPathIntersectionPoint( lineStart: Vector2, lineEnd: Vector2, dt: number ): Vector2 | null {

// Create a line that represents the path of the photon.
const photonPathEndPoint = this.positionProperty.value.plus( this.directionProperty.value.timesScalar( PHOTON_SPEED * dt ) );

// Return the intersection point if there is one, null if not.
return Utils.lineSegmentIntersection(
this.positionProperty.value.x,
this.positionProperty.value.y,
photonPathEndPoint.x,
photonPathEndPoint.y,
lineStart.x,
lineStart.y,
lineEnd.x,
lineEnd.y
);
}

public step( dt: number ): void {
this.positionProperty.set( this.positionProperty.value.plus( this.directionProperty.value.timesScalar( PHOTON_SPEED * dt ) ) );
}
}

export default class Photon extends PhetioObject {

// Contains all the possible states of the photon, which include position, direction, and probability.
// Since they contain properties, and based on the design of this simulation, it will always have two states.
public possibleStates: [ PhotonState, PhotonState ];

// whether this photon is active, and should thus be moved by the model and shown in the view
public readonly activeProperty: BooleanProperty;
Expand All @@ -45,58 +95,44 @@ export default class Photon extends PhetioObject {
phetioState: false
} );

this.positionProperty = new Vector2Property( Vector2.ZERO, {
tandem: tandem.createTandem( 'positionProperty' )
} );

this.polarizationAngleProperty = new NumberProperty( 0, {
tandem: tandem.createTandem( 'polarizationAngleProperty' )
} );

this.directionProperty = new Vector2Property( RIGHT, {
tandem: tandem.createTandem( 'directionProperty' )
} );

this.activeProperty = new BooleanProperty( false, {
tandem: tandem.createTandem( 'activeProperty' )
} );

this.possibleStates = [
new PhotonState( 'vertical' ),
new PhotonState( 'horizontal' )
];

// Entangle the possible states
this.possibleStates[ 0 ].probabilityProperty.lazyLink( probability => {
this.possibleStates[ 1 ].probabilityProperty.set( 1 - probability );
} );
this.possibleStates[ 1 ].probabilityProperty.lazyLink( probability => {
this.possibleStates[ 0 ].probabilityProperty.set( 1 - probability );
} );

}

public step( dt: number ): void {
if ( this.activeProperty.value ) {
this.positionProperty.set( this.positionProperty.value.plus( this.directionProperty.value.timesScalar( PHOTON_SPEED * dt ) ) );
this.possibleStates.forEach( state => {
state.step( dt );
} );
}
}

public reset(): void {
this.positionProperty.reset();
this.directionProperty.reset();
this.activeProperty.reset();
}

/**
* Get the point where this photon would intersect the provided line segment if it were to move for the specified
* amount of time. Returns null if the photon would not intersect the line segment.
* @param lineStart
* @param lineEnd
* @param dt - time step, in seconds
*/
public getTravelPathIntersectionPoint( lineStart: Vector2, lineEnd: Vector2, dt: number ): Vector2 | null {

// Create a line that represents the path of the photon.
const photonPathEndPoint = this.positionProperty.value.plus( this.directionProperty.value.timesScalar( PHOTON_SPEED * dt ) );

// Return the intersection point if there is one, null if not.
return Utils.lineSegmentIntersection(
this.positionProperty.value.x,
this.positionProperty.value.y,
photonPathEndPoint.x,
photonPathEndPoint.y,
lineStart.x,
lineStart.y,
lineEnd.x,
lineEnd.y
);
this.possibleStates.forEach( state => {
state.positionProperty.reset();
state.directionProperty.reset();
state.probabilityProperty.reset();
} );
}
}

Expand Down
28 changes: 20 additions & 8 deletions js/photons/model/PhotonDetector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

import NumberProperty from '../../../../axon/js/NumberProperty.js';
import dotRandom from '../../../../dot/js/dotRandom.js';
import Range from '../../../../dot/js/Range.js';
import Vector2 from '../../../../dot/js/Vector2.js';
import { Line } from '../../../../kite/js/imports.js';
Expand All @@ -17,7 +18,7 @@ import { PhetioObjectOptions } from '../../../../tandem/js/PhetioObject.js';
import AveragingCounterNumberProperty from '../../common/model/AveragingCounterNumberProperty.js';
import quantumMeasurement from '../../quantumMeasurement.js';
import { PHOTON_BEAM_WIDTH } from './Laser.js';
import Photon from './Photon.js';
import Photon, { PhotonState } from './Photon.js';
import { PhotonInteractionTestResult } from './PhotonsModel.js';
import { TPhotonInteraction } from './TPhotonInteraction.js';

Expand Down Expand Up @@ -87,27 +88,38 @@ export default class PhotonDetector implements TPhotonInteraction {
} );
}

public testForPhotonInteraction( photon: Photon, dt: number ): PhotonInteractionTestResult {
public testForPhotonInteraction( photonState: PhotonState, photon: Photon, dt: number ): PhotonInteractionTestResult {

assert && assert( photon.activeProperty.value, 'save CPU cycles - don\'t use this method with inactive photons' );

// Test for whether this photon would cross the detection aperture.
const photonIntersectionPoint = photon.getTravelPathIntersectionPoint(
const photonIntersectionPoint = photonState.getTravelPathIntersectionPoint(
this.detectionLine.start,
this.detectionLine.end,
dt
);

const detectionResult: PhotonInteractionTestResult = photonIntersectionPoint !== null ?
{ interactionType: 'absorbed' } :
{ interactionType: 'none' };
// Assume no interaction until proven otherwise.
let interaction: PhotonInteractionTestResult = { interactionType: 'none' };

if ( photonIntersectionPoint !== null ) {
// Evaluate the detection result based on the probability of the photon actually being here!
if ( dotRandom.nextDouble() < photonState.probabilityProperty.value ) {
photonState.probabilityProperty.value = 1; // the photon is detected!
interaction = { interactionType: 'absorbed' };
}
else {
// If the photon is not detected. This state probability goes to 0%, which will make the other state 100%.
photonState.probabilityProperty.value = 0;
}
}

if ( detectionResult.interactionType === 'absorbed' ) {
if ( interaction.interactionType === 'absorbed' ) {
this.detectionCountProperty.value = Math.min( this.detectionCountProperty.value + 1, COUNT_RANGE.max );
this.detectionRateProperty.countEvent();
}

return detectionResult;
return interaction;
}

public step( dt: number ): void {
Expand Down
Loading

1 comment on commit 772e5a2

@AgustinVallejo
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually #65

Please sign in to comment.