From a3c09d430957d56173a3158512a57638daae5272 Mon Sep 17 00:00:00 2001 From: AgustinVallejo Date: Wed, 8 Jan 2025 16:58:21 -0500 Subject: [PATCH] Improvements to precession and phetio fixes --- js/bloch-sphere/model/BlochSphereModel.ts | 18 +++++++- js/bloch-sphere/model/ComplexBlochSphere.ts | 6 ++- .../view/BlochSphereMeasurementArea.ts | 36 +++++++++------- .../view/MeasurementTimerControl.ts | 42 ++++--------------- js/common/QuantumMeasurementConstants.ts | 2 + 5 files changed, 52 insertions(+), 52 deletions(-) diff --git a/js/bloch-sphere/model/BlochSphereModel.ts b/js/bloch-sphere/model/BlochSphereModel.ts index 4757145..dfabc95 100644 --- a/js/bloch-sphere/model/BlochSphereModel.ts +++ b/js/bloch-sphere/model/BlochSphereModel.ts @@ -51,6 +51,9 @@ export default class BlochSphereModel implements TModel { // Time to measurement public timeToMeasurementProperty: NumberProperty; + // Current time + public measurementTimeProperty: NumberProperty; + // Measurement basis public measurementBasisProperty: Property; @@ -120,14 +123,20 @@ export default class BlochSphereModel implements TModel { range: new Range( 0, 1 ) } ); + this.measurementTimeProperty = new NumberProperty( 0, { + tandem: providedOptions.tandem.createTandem( 'measurementTimeProperty' ) + } ); + this.measurementBasisProperty = new Property( MeasurementBasis.S_SUB_Z, { tandem: providedOptions.tandem.createTandem( 'measurementBasisProperty' ), - phetioValueType: EnumerationIO( MeasurementBasis ) + phetioValueType: EnumerationIO( MeasurementBasis ), + phetioFeatured: true } ); this.isSingleMeasurementModeProperty = new BooleanProperty( true, { tandem: providedOptions.tandem.createTandem( 'isSingleMeasurementModeProperty' ), - phetioReadOnly: true + phetioReadOnly: true, + phetioFeatured: true } ); this.upMeasurementCountProperty = new NumberProperty( 0, { @@ -264,6 +273,7 @@ export default class BlochSphereModel implements TModel { this.magneticFieldStrengthProperty.reset(); this.measurementBasisProperty.reset(); this.isSingleMeasurementModeProperty.reset(); + this.measurementTimeProperty.reset(); } /** @@ -275,6 +285,10 @@ export default class BlochSphereModel implements TModel { this.multiMeasurementBlochSpheres.forEach( blochSphere => { blochSphere.step( dt ); } ); + if ( this.selectedSceneProperty.value === BlochSphereScene.PRECESSION ) { + this.measurementTimeProperty.value += dt; + this.measurementTimeProperty.value %= 2; + } } } diff --git a/js/bloch-sphere/model/ComplexBlochSphere.ts b/js/bloch-sphere/model/ComplexBlochSphere.ts index f96c022..62a59c0 100644 --- a/js/bloch-sphere/model/ComplexBlochSphere.ts +++ b/js/bloch-sphere/model/ComplexBlochSphere.ts @@ -14,6 +14,7 @@ import Utils from '../../../../dot/js/Utils.js'; import optionize from '../../../../phet-core/js/optionize.js'; import { PhetioObjectOptions } from '../../../../tandem/js/PhetioObject.js'; import AbstractBlochSphere, { AbstractBlochSphereOptions } from '../../common/model/AbstractBlochSphere.js'; +import QuantumMeasurementConstants from '../../common/QuantumMeasurementConstants.js'; import quantumMeasurement from '../../quantumMeasurement.js'; import { MeasurementBasis } from './MeasurementBasis.js'; import { StateDirection } from './StateDirection.js'; @@ -46,11 +47,12 @@ export default class ComplexBlochSphere extends AbstractBlochSphere { /** - * Abstract method that should run calculations to update the Bloch Sphere representation. + * Calculate precession and add it to azimuthal angle, constrained from 0 to 2 PI. */ public override step( dt: number ): void { + const precession = this.rotatingSpeedProperty.value * QuantumMeasurementConstants.MAX_PRECESSION_RATE * dt; this.azimuthalAngleProperty.value = Utils.moduloBetweenDown( - this.azimuthalAngleProperty.value + this.rotatingSpeedProperty.value * dt, 0, 2 * Math.PI + this.azimuthalAngleProperty.value + precession, 0, 2 * Math.PI ); } diff --git a/js/bloch-sphere/view/BlochSphereMeasurementArea.ts b/js/bloch-sphere/view/BlochSphereMeasurementArea.ts index 28e445e..7eafd8f 100644 --- a/js/bloch-sphere/view/BlochSphereMeasurementArea.ts +++ b/js/bloch-sphere/view/BlochSphereMeasurementArea.ts @@ -10,7 +10,7 @@ import DerivedProperty from '../../../../axon/js/DerivedProperty.js'; import DerivedStringProperty from '../../../../axon/js/DerivedStringProperty.js'; -import { EmptySelfOptions } from '../../../../phet-core/js/optionize.js'; +import optionize, { EmptySelfOptions } from '../../../../phet-core/js/optionize.js'; import WithRequired from '../../../../phet-core/js/types/WithRequired.js'; import PhetFont from '../../../../scenery-phet/js/PhetFont.js'; import { Node, NodeOptions, RichText, RichTextOptions, Text, VBox } from '../../../../scenery/js/imports.js'; @@ -40,18 +40,14 @@ export default class BlochSphereMeasurementArea extends Node { public constructor( model: BlochSphereModel, providedOptions: BlochSphereMeasurementAreaOptions ) { - super( providedOptions ); - const equationNode = new BlochSphereNumericalEquationNode( model, { tandem: providedOptions.tandem.createTandem( 'equationNode' ), centerY: -50 } ); - this.addChild( equationNode ); const magneticFieldNode = new MagneticFieldNode( model.magneticFieldStrengthProperty, { visibleProperty: DerivedProperty.valueEqualsConstant( model.selectedSceneProperty, BlochSphereScene.PRECESSION ) } ); - this.addChild( magneticFieldNode ); const singleMeasurementBlochSphereNode = new BlochSphereNode( model.singleMeasurementBlochSphere, { tandem: providedOptions.tandem.createTandem( 'singleMeasurementBlochSphereNode' ), @@ -61,7 +57,6 @@ export default class BlochSphereMeasurementArea extends Node { center: magneticFieldNode.center, visibleProperty: model.isSingleMeasurementModeProperty } ); - this.addChild( singleMeasurementBlochSphereNode ); const multipleMeasurementBlochSpheresTandem = providedOptions.tandem.createTandem( 'multipleMeasurementBlochSpheres' ); const multipleMeasurementBlochSpheresNode = new Node( { @@ -81,7 +76,6 @@ export default class BlochSphereMeasurementArea extends Node { multipleMeasurementBlochSpheresNode.addChild( blochSphereNode ); } ); multipleMeasurementBlochSpheresNode.center = magneticFieldNode.center.plusXY( 0, 30 ); - this.addChild( multipleMeasurementBlochSpheresNode ); const spinUpLabelStringProperty = new DerivedStringProperty( [ model.measurementBasisProperty ], @@ -107,10 +101,14 @@ export default class BlochSphereMeasurementArea extends Node { isSingleMeasurement => { return { createNode: () => new Text( isSingleMeasurement ? 'Single' : 'Multiple', { font: new PhetFont( 16 ) } ), - value: isSingleMeasurement + value: isSingleMeasurement, + tandemName: isSingleMeasurement ? 'singleMeasurementRadioButton' : 'multipleMeasurementRadioButton' }; } ); - const singleOrMultipleRadioButtonGroup = new AquaRadioButtonGroup( model.isSingleMeasurementModeProperty, radioButtonItems, { orientation: 'vertical' } ); + const singleOrMultipleRadioButtonGroup = new AquaRadioButtonGroup( model.isSingleMeasurementModeProperty, radioButtonItems, { + orientation: 'vertical', + tandem: providedOptions.tandem.createTandem( 'singleOrMultipleRadioButtonGroup' ) + } ); const basisRadioButtonTextOptions: RichTextOptions = { font: new PhetFont( 18 ), @@ -143,7 +141,7 @@ export default class BlochSphereMeasurementArea extends Node { } ); - const measurementTimerControl = new MeasurementTimerControl( model.timeToMeasurementProperty, model.singleMeasurementBlochSphere.azimuthalAngleProperty, { + const measurementTimerControl = new MeasurementTimerControl( model.timeToMeasurementProperty, model.measurementTimeProperty, { tandem: providedOptions.tandem.createTandem( 'measurementTimerControl' ), visibleProperty: DerivedProperty.valueEqualsConstant( model.selectedSceneProperty, BlochSphereScene.PRECESSION ) } ); @@ -199,16 +197,26 @@ export default class BlochSphereMeasurementArea extends Node { prepareObserveButton ] } ); - this.addChild( measurementControls ); - this.addChild( new MagneticFieldControl( model.magneticFieldStrengthProperty, { + const magneticFieldControl = new MagneticFieldControl( model.magneticFieldStrengthProperty, { centerX: singleMeasurementBlochSphereNode.centerX, top: magneticFieldNode.bottom + 10, visibleProperty: DerivedProperty.valueEqualsConstant( model.selectedSceneProperty, BlochSphereScene.PRECESSION ), tandem: providedOptions.tandem.createTandem( 'magneticFieldControl' ) - } ) ); + } ); + + const options = optionize()( { + children: [ + equationNode, + magneticFieldNode, + singleMeasurementBlochSphereNode, + multipleMeasurementBlochSpheresNode, + measurementControls, + magneticFieldControl + ] + }, providedOptions ); - this.mutate( providedOptions ); + super( options ); this.pdomOrder = [ measurementControlPanel, diff --git a/js/bloch-sphere/view/MeasurementTimerControl.ts b/js/bloch-sphere/view/MeasurementTimerControl.ts index 5ee1646..0c77faa 100644 --- a/js/bloch-sphere/view/MeasurementTimerControl.ts +++ b/js/bloch-sphere/view/MeasurementTimerControl.ts @@ -31,35 +31,9 @@ export default class MeasurementTimerControl extends Node { public constructor( timeToMeasurementProperty: NumberProperty, - azimuthProperty: NumberProperty, + measurementTimeProperty: NumberProperty, providedOptions: MeasurementTimerControlOptions ) { - // - // new Path( new Shape().moveTo( -SLIDER_TRACK_SIZE.width / 2, 0 ).lineTo( SLIDER_TRACK_SIZE.width / 2, 0 ), { - // stroke: 'grey', - // lineWidth: 1, - // lineDash: [ 2, 2 ] - // } ); - // - // // Add horizontal lines to the magnetic field indicator. - // const numberOfIndicatorLines = 5; - // _.times( numberOfIndicatorLines, num => { - // const indicatorLine = new Line( -5, 0, 5, 0, { - // stroke: 'gray', - // centerY: -SLIDER_TRACK_SIZE.height / 2 + num * SLIDER_TRACK_SIZE.height / ( numberOfIndicatorLines - 1 ) - // } ); - // magneticFieldIndicator.addChild( indicatorLine ); - // indicatorLine.moveToBack(); - // } ); - // - // const labeledMagneticFieldIndicator = new HBox( { - // children: [ - // new Text( '0', { font: new PhetFont( 12 ) } ), - // magneticFieldIndicator - // ], - // spacing: 5, - // resize: false - // } ); const thumbOffset = 30; const thumbDimensions = new Dimension2( 30, 30 ); @@ -84,17 +58,17 @@ export default class MeasurementTimerControl extends Node { timeToMeasurementSlider.addMinorTick( 0.6 ); timeToMeasurementSlider.addMinorTick( 0.8 ); - const azimuthIndicatorScale = 15; - const azimuthIndicator = new ArrowNode( 0, azimuthIndicatorScale, 0, 0, { + const timeIndicatorScale = 15; + const timeIndicator = new ArrowNode( 0, timeIndicatorScale, 0, 0, { fill: 'magenta', stroke: null, - headHeight: azimuthIndicatorScale, - headWidth: azimuthIndicatorScale, + headHeight: timeIndicatorScale, + headWidth: timeIndicatorScale, tailWidth: 0 } ); - azimuthProperty.link( azimuth => { - azimuthIndicator.centerX = azimuth / ( 2 * Math.PI ) * SLIDER_TRACK_SIZE.width; + measurementTimeProperty.link( measurementTime => { + timeIndicator.centerX = measurementTime / 2 * SLIDER_TRACK_SIZE.width; } ); const thumbLine = new Path( new Shape().circle( 0, 0, 2 ).moveTo( 0, 0 ).lineTo( 0, thumbOffset - thumbDimensions.height / 2 ), { @@ -136,7 +110,7 @@ export default class MeasurementTimerControl extends Node { const options = optionize()( { children: [ - azimuthIndicator, + timeIndicator, timeToMeasurementSlider, thumbLine, measurementSymbol diff --git a/js/common/QuantumMeasurementConstants.ts b/js/common/QuantumMeasurementConstants.ts index fad4ec9..9ad4283 100644 --- a/js/common/QuantumMeasurementConstants.ts +++ b/js/common/QuantumMeasurementConstants.ts @@ -29,6 +29,8 @@ const QuantumMeasurementConstants = { KET: '\u27e9', HBAR: '\u210F', + MAX_PRECESSION_RATE: Math.PI, // radians per second + expectedPercentagePathOptions: { stroke: '#0a0', lineWidth: 5