From e7c267c47ff8bf46b083bfc92ad8e4bf888c5794 Mon Sep 17 00:00:00 2001 From: jbphet Date: Wed, 18 Dec 2024 16:45:14 -0700 Subject: [PATCH] fix problem where split photons were not getting to detectors at the same time, see https://github.com/phetsims/quantum-measurement/issues/65 --- js/photons/model/Photon.ts | 6 ++- js/photons/model/PhotonDetector.ts | 11 ++--- .../model/PhotonsExperimentSceneModel.ts | 41 +++++++++++-------- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/js/photons/model/Photon.ts b/js/photons/model/Photon.ts index 36a7e5c..3e7cefd 100644 --- a/js/photons/model/Photon.ts +++ b/js/photons/model/Photon.ts @@ -26,7 +26,7 @@ export const RIGHT = new Vector2( 1, 0 ); // the resulting states will either be measured as vertical or horizontal. export type PossiblePolarizationResult = 'vertical' | 'horizontal'; -export default class Photon { +class Photon { // the angle of polarization for this photon, in degrees public polarizationAngle: number; @@ -139,4 +139,6 @@ export type PhotonStateObject = { possibleMotionStates: PhotonMotionState[]; }; -quantumMeasurement.register( 'Photon', Photon ); \ No newline at end of file +quantumMeasurement.register( 'Photon', Photon ); + +export default Photon; \ No newline at end of file diff --git a/js/photons/model/PhotonDetector.ts b/js/photons/model/PhotonDetector.ts index 33778dc..c4e3a6b 100644 --- a/js/photons/model/PhotonDetector.ts +++ b/js/photons/model/PhotonDetector.ts @@ -82,9 +82,10 @@ class PhotonDetector implements TPhotonInteraction { position.plus( new Vector2( -this.apertureDiameter / 2, 0 ) ), position.plus( new Vector2( this.apertureDiameter / 2, 0 ) ) ); + const apertureLineYOffset = this.detectionDirection === 'up' ? this.apertureHeight : -this.apertureHeight; this.absorptionLine = new Line( - position.plus( new Vector2( -this.apertureDiameter / 2, this.detectionDirection === 'up' ? this.apertureHeight : -this.apertureHeight ) ), - position.plus( new Vector2( this.apertureDiameter / 2, this.detectionDirection === 'up' ? this.apertureHeight : -this.apertureHeight ) ) + position.plus( new Vector2( -this.apertureDiameter / 2, apertureLineYOffset ) ), + position.plus( new Vector2( this.apertureDiameter / 2, apertureLineYOffset ) ) ); this.detectionRateProperty = new AveragingCounterNumberProperty( { @@ -130,15 +131,15 @@ class PhotonDetector implements TPhotonInteraction { // If, by any chance, the photon would cross BOTH the detection and absorption lines, we'll consider it to be // detected and absorbed. - if ( detectionIntersectionPoint !== null && absorptionIntersectionPoint !== null ) { + if ( detectionIntersectionPoint && absorptionIntersectionPoint ) { mapOfStatesToInteractions.set( photonState, { interactionType: 'detectedAndAbsorbed', detectionInfo: { detector: this } } ); } // If the photon would cross the detection line, but not the absorption line, we'll consider it to be detected. - else if ( detectionIntersectionPoint !== null ) { + else if ( detectionIntersectionPoint ) { mapOfStatesToInteractions.set( photonState, { interactionType: 'detected', detectionInfo: { detector: this } } ); } // If the photon would cross the absorption line, but not the detection line, we'll consider it to be absorbed. - else if ( absorptionIntersectionPoint !== null ) { + else if ( absorptionIntersectionPoint ) { mapOfStatesToInteractions.set( photonState, { interactionType: 'absorbed' } ); } } ); diff --git a/js/photons/model/PhotonsExperimentSceneModel.ts b/js/photons/model/PhotonsExperimentSceneModel.ts index d50eda6..9687072 100644 --- a/js/photons/model/PhotonsExperimentSceneModel.ts +++ b/js/photons/model/PhotonsExperimentSceneModel.ts @@ -28,6 +28,7 @@ import Mirror from './Mirror.js'; import Photon, { PHOTON_SPEED } from './Photon.js'; import { PhotonCollection } from './PhotonCollection.js'; import PhotonDetector, { COUNT_RANGE } from './PhotonDetector.js'; +import { PhotonMotionState } from './PhotonMotionState.js'; import PolarizingBeamSplitter from './PolarizingBeamSplitter.js'; import { TPhotonInteraction } from './TPhotonInteraction.js'; @@ -236,13 +237,17 @@ export default class PhotonsExperimentSceneModel { // beam splitter, mirror, and photon detectors. this.photonCollection.photons.forEach( photon => { - let interactionCount = 0; + const hadInteractionMap: Map = new Map(); for ( const potentiallyInteractingElement of potentialInteractors ) { // Test for interactions with the potential interactors. const interactions = potentiallyInteractingElement.testForPhotonInteraction( photon, dt ); + interactions.forEach( ( interaction, photonState ) => { + hadInteractionMap.set( photonState, true ); + } ); + // For each of the interactions, update the photon state. for ( const [ photonState, interaction ] of interactions ) { @@ -263,11 +268,8 @@ export default class PhotonsExperimentSceneModel { // Step the photon the remaining time. photonState.step( dt - dtToReflectionPoint ); - - interactionCount++; } - - if ( interaction.interactionType === 'split' ) { + else if ( interaction.interactionType === 'split' ) { assert && assert( interaction.splitInfo, 'split info missing' ); assert && assert( photon.possibleMotionStates.length === 1, 'there should be 1 motion state' ); @@ -287,9 +289,13 @@ export default class PhotonsExperimentSceneModel { interaction.splitInfo!.splitStates[ 1 ].direction, interaction.splitInfo!.splitStates[ 1 ].probability ); - } - if ( interaction.interactionType === 'detected' || interaction.interactionType === 'detectedAndAbsorbed' ) { + hadInteractionMap.set( photon.possibleMotionStates[ 1 ], true ); + + // Step the motion states the remaining time. + photon.possibleMotionStates.forEach( state => state.step( dt - dtToSplitPoint ) ); + } + else if ( interaction.interactionType === 'detected' || interaction.interactionType === 'detectedAndAbsorbed' ) { const detector = interaction.detectionInfo!.detector; @@ -300,31 +306,32 @@ export default class PhotonsExperimentSceneModel { photon.setMotionStateProbability( photonState, 1 ); detector.detectionCountProperty.value = Math.min( detector.detectionCountProperty.value + 1, COUNT_RANGE.max ); detector.detectionRateProperty.countEvent(); - } else { - // If this photon state does not trigger the detector the associated probability goes to 0%, which will make - // the other state's probability 100%. + // If this photon state does not trigger the detector the associated probability goes to 0, which will + // make the other state's probability 1. photon.setMotionStateProbability( photonState, 0 ); } - } - if ( interaction.interactionType === 'absorbed' || interaction.interactionType === 'detectedAndAbsorbed' ) { + photonState.step( dt ); + } + else if ( interaction.interactionType === 'absorbed' || interaction.interactionType === 'detectedAndAbsorbed' ) { // This interaction indicates that the photon was absorbed, so it should be removed from the photon // collection. photonsToRemove.push( photon ); - - interactionCount++; } } } } - if ( interactionCount === 0 ) { - photon.step( dt ); - } + // If there were no interactions, step the photon forward in time. + photon.possibleMotionStates.forEach( state => { + if ( !hadInteractionMap.get( state ) ) { + state.step( dt ); + } + } ); } ); // Remove any photons that were absorbed by something.