diff --git a/doc/implementation-notes.md b/doc/implementation-notes.md index c0526d7b..3cfcb87d 100644 --- a/doc/implementation-notes.md +++ b/doc/implementation-notes.md @@ -152,8 +152,8 @@ The electromagnet is also a "Hollywood" model, based on a coil magnet. See detai ### Coil -The same coil implementation (`Coil`) is used for both the pickup coil and the electromagnet coil. The concept of "current amplitude" -is fundamental to understand the sim model. It is described in [model.md](https://github.com/phetsims/faradays-electromagnetic-lab/blob/main/doc/model.md#bar-magnet) and in `Coil`. +The same coil implementation (`Coil`) is used for both the pickup coil and the electromagnet coil. The concept of "normalized current" +is fundamental to understanding the sim model. It is described in [model.md](https://github.com/phetsims/faradays-electromagnetic-lab/blob/main/doc/model.md#bar-magnet) and in `Coil`. The most complicated part of the sim may be `Coil.createCoilSegments`. It creates an ordered `CoilSegment[]` that describes the shape of the coil, and the path that electrons follow as they flow through the coil. So that objects (bar magnet, compass,...) may diff --git a/doc/model.md b/doc/model.md index b882fd2b..1e5b6868 100644 --- a/doc/model.md +++ b/doc/model.md @@ -13,7 +13,7 @@ are provided, e.g. `BarMagnet`. Code can be found [here](https://github.com/phe **B-field**: a synonym for magnetic field. -**Current amplitude**: A value with range [-1,1]. The magnitude describes the amount of current relative to the +**Normalized Current**: A value with range [-1,1]. The magnitude describes the amount of current relative to the maximum current that may be induced in the model. The sign indicates the direction of the current. View components use this value to determine how they should respond to induced current. For example, deflection of the voltmeter needle, brightness of the light bulb, and speed of @@ -49,17 +49,17 @@ points in these grids, scaled to match the strength of the bar magnet. ### Electromagnet The electromagnet (see `Electromagnet`) is based on a coil magnet model. Its voltage source can be either a DC or AC power supply. -The strength of the B-field produced by the electromagnet is proportional to the amplitude of the voltage in the -voltage source and the number of loops in the coil. (The diameter of the loops is fixed.) The current amplitude in the coil -is proportional to the amplitude of the voltage source. **Note that there is no model of resistance for the coil or voltage source.** +The strength of the B-field produced by the electromagnet is proportional to the voltage in the +power supply and the number of loops in the coil. (The diameter of the loops is fixed.) The normalized current in the coil +is proportional to the voltage of the power supply. **Note that there is no model of resistance for the coil or voltage source.** The DC power supply (aka battery) has a maximum voltage, and its voltage -amplitude and polarity is varied by the user via a slider control. See `DCPowerSuply`. +and polarity is varied by the user via a slider control. See `DCPowerSuply`. The AC Power Supply has a configurable maximum voltage. The user varies the maximum voltage amplitude and frequency using sliders. The voltage amplitude varies over time. See `ACPowerSupply`. -Electrons in the electromagnet's coil move at a speed and direction that is proportional to the current amplitude in +Electrons in the electromagnet's coil move at a speed and direction that is proportional to the normalized current in the coil. More current results in faster speed. A change in polarity of the magnet results in change in direction of the electrons. @@ -100,13 +100,14 @@ The pickup coil (see `PickupCoil`) is the most complicated part of the model, an The magnetic field is sampled and averaged along a vertical line through the center of the coil. The average is used to compute the flux in one loop of the coil, then multiplied by the number of loops. The flux is measured over time. A change in -flux induces an EMF, and the current amplitude is a function of the induced EMF. +flux induces an EMF, and the normalized current proportional to the induced EMF. Resistance of the coil +is constant; it does not vary with the loop area and number of loops. The pickup coil can have one of two indicators attached to it: a light bulb (`LightBulb`) or a voltmeter (`Voltmeter`). These indicators -react to the current amplitude in the coil The light bulb’s intensity is proportional to the absolute value -of the current amplitude. The voltmeter’s needle deflection is proportional to the current amplitude, and -uses an ah hoc algorithm that makes the needle wobble around the zero point. +react to the normalized current in the coil The light bulb’s intensity is proportional to the absolute value +of the normalized current. The voltmeter’s needle deflection is proportional to the normalized current, and +uses an ah hoc algorithm that makes adds kinematic behavior to the needle. -Similar to the electromagnet coil, electrons in the pickup coil react to the current amplitude in the coil. +Similar to the electromagnet coil, electrons in the pickup coil react to the normalized current in the coil. More current results in faster speed. A change in polarity of the magnet field results in change in direction of the electrons. diff --git a/js/common/FELConstants.ts b/js/common/FELConstants.ts index 35adbef4..87d51f35 100644 --- a/js/common/FELConstants.ts +++ b/js/common/FELConstants.ts @@ -190,13 +190,18 @@ const FELConstants = { // Model MAGNET_STRENGTH_RANGE: new Range( 0, 300 ), // G - // Range of currentAmplitudeProperty. The magnitude determines the relative amount of current, while the sign - // determines the current direction. See Coil currentAmplitudeProperty and - // https://github.com/phetsims/faradays-electromagnetic-lab/issues/63 - CURRENT_AMPLITUDE_RANGE: new Range( -1, 1 ), - - // Absolute current amplitude below this value is treated as zero. - CURRENT_AMPLITUDE_THRESHOLD: 0.001 + // Range of normalizedCurrentProperty. The magnitude determines the relative amount of current, while the sign + // determines the current direction. See Coil.normalizedCurrentProperty. + NORMALIZED_CURRENT_RANGE: new Range( -1, 1 ), + + // Absolute normalized current below this value is treated as zero. + NORMALIZED_CURRENT_THRESHOLD: 0.001, + + // phetioDocumentation for all instances of normalizedCurrentProperty. + NORMALIZED_CURRENT_PHET_IO_DOCUMENTATION: + 'For internal use only. Current in the coil is normalized to the range [-1,1]. ' + + 'The magnitude indicates the relative amount of current flowing in the coil, ' + + 'while the sign indicates the direction of flow.' }; faradaysElectromagneticLab.register( 'FELConstants', FELConstants ); diff --git a/js/common/model/Coil.ts b/js/common/model/Coil.ts index ca108834..809b301f 100644 --- a/js/common/model/Coil.ts +++ b/js/common/model/Coil.ts @@ -81,8 +81,12 @@ export default class Coil extends PhetioObject { // current relative to some maximum current in the model. The sign indicates the direction of that current. View // components can use this value to determine how they should behave -- eg, how far to move a voltmeter needle, // how bright to make a light bulb, and how fast to move electrons. - public readonly currentAmplitudeProperty: TReadOnlyProperty; - private readonly currentAmplitudeRange: Range; + // See https://github.com/phetsims/faradays-electromagnetic-lab/issues/63 + // + // In the Java version, this was named currentAmplitudeProperty. But there were objections to that name during code + // review because amplitude is not signed. See https://github.com/phetsims/faradays-electromagnetic-lab/issues/130 + public readonly normalizedCurrentProperty: TReadOnlyProperty; + private readonly normalizedCurrentRange: Range; // Width of the wire that makes up the coil. public readonly wireWidth: number; @@ -116,8 +120,8 @@ export default class Coil extends PhetioObject { // Fires after electrons have moved. public readonly electronsMovedEmitter: Emitter; - public constructor( currentAmplitudeProperty: TReadOnlyProperty, currentAmplitudeRange: Range, providedOptions: CoilOptions ) { - assert && assert( currentAmplitudeRange.equals( FELConstants.CURRENT_AMPLITUDE_RANGE ) ); + public constructor( normalizedCurrentProperty: TReadOnlyProperty, normalizedCurrentRange: Range, providedOptions: CoilOptions ) { + assert && assert( normalizedCurrentRange.equals( FELConstants.NORMALIZED_CURRENT_RANGE ) ); const options = optionize()( { @@ -136,8 +140,8 @@ export default class Coil extends PhetioObject { super( options ); - this.currentAmplitudeProperty = currentAmplitudeProperty; - this.currentAmplitudeRange = currentAmplitudeRange; + this.normalizedCurrentProperty = normalizedCurrentProperty; + this.normalizedCurrentRange = normalizedCurrentRange; this.wireWidth = options.wireWidth; this.loopSpacing = options.loopSpacing; @@ -219,7 +223,7 @@ export default class Coil extends PhetioObject { public step( dt: number ): void { // Step the electrons if there's current flow in the coil, and the electrons are visible. - if ( this.currentAmplitudeProperty.value !== 0 && this.electronsVisibleProperty.value ) { + if ( this.normalizedCurrentProperty.value !== 0 && this.electronsVisibleProperty.value ) { this.electronsProperty.value.forEach( electron => electron.step( dt ) ); this.electronsMovedEmitter.emit(); } @@ -408,7 +412,7 @@ export default class Coil extends PhetioObject { const coilSegmentPosition = i / numberOfElectrons; // Model - const electron = new Electron( this.currentAmplitudeProperty, this.currentAmplitudeRange, { + const electron = new Electron( this.normalizedCurrentProperty, this.normalizedCurrentRange, { coilSegments: coilSegments, coilSegmentIndex: coilSegmentIndex, coilSegmentPosition: coilSegmentPosition, diff --git a/js/common/model/CurrentSource.ts b/js/common/model/CurrentSource.ts index a46e0d31..2b87cce4 100644 --- a/js/common/model/CurrentSource.ts +++ b/js/common/model/CurrentSource.ts @@ -45,8 +45,8 @@ export default class CurrentSource extends PhetioObject { // Voltage that will cause current flow public readonly voltageProperty: NumberProperty; - // Amplitude of the current, relative to the voltage. See Coil currentAmplitudeProperty. - public readonly currentAmplitudeProperty: TReadOnlyProperty; + // Normalized current, relative to the voltage. See Coil normalizedCurrentProperty. + public readonly normalizedCurrentProperty: TReadOnlyProperty; protected constructor( providedOptions: CurrentSourceOptions ) { @@ -73,9 +73,10 @@ export default class CurrentSource extends PhetioObject { phetioFeatured: true }, options.voltagePropertyOptions ) ); - this.currentAmplitudeProperty = new DerivedProperty( [ this.voltageProperty ], - voltage => Utils.linear( voltageRange.min, voltageRange.max, FELConstants.CURRENT_AMPLITUDE_RANGE.min, FELConstants.CURRENT_AMPLITUDE_RANGE.max, voltage ), { - isValidValue: currentAmplitude => FELConstants.CURRENT_AMPLITUDE_RANGE.contains( currentAmplitude ) + // Normalized current is a linear mapping from voltage. We are considering resistance to be constant. + this.normalizedCurrentProperty = new DerivedProperty( [ this.voltageProperty ], + voltage => Utils.linear( voltageRange.min, voltageRange.max, FELConstants.NORMALIZED_CURRENT_RANGE.min, FELConstants.NORMALIZED_CURRENT_RANGE.max, voltage ), { + isValidValue: normalizedCurrent => FELConstants.NORMALIZED_CURRENT_RANGE.contains( normalizedCurrent ) } ); } diff --git a/js/common/model/Electromagnet.ts b/js/common/model/Electromagnet.ts index 8b281e02..cdf2289b 100644 --- a/js/common/model/Electromagnet.ts +++ b/js/common/model/Electromagnet.ts @@ -59,18 +59,19 @@ export default class Electromagnet extends CoilMagnet { phetioFeatured: true } ); - // Current amplitude in the coil is equivalent to the current amplitude of the selected power supply. - // See Coil currentAmplitudeProperty for additional documentation. - const currentAmplitudeProperty = new DerivedProperty( - [ currentSourceProperty, dcPowerSupply.currentAmplitudeProperty, acPowerSupply.currentAmplitudeProperty ], - ( currentSource, dcCurrentAmplitude, acCurrentAmplitude ) => - ( currentSource === dcPowerSupply ) ? dcCurrentAmplitude : acCurrentAmplitude, { - isValidValue: currentAmplitude => FELConstants.CURRENT_AMPLITUDE_RANGE.contains( currentAmplitude ), - tandem: coilTandem.createTandem( 'currentAmplitudeProperty' ), - phetioValueType: NumberIO + // Normalized current in the coil is equivalent to the normalized current produced by the selected power supply, + // See Coil normalizedCurrentProperty for additional documentation. + const normalizedCurrentProperty = new DerivedProperty( + [ currentSourceProperty, dcPowerSupply.normalizedCurrentProperty, acPowerSupply.normalizedCurrentProperty ], + ( currentSource, dcNormalizedCurrent, acNormalizedCurrent ) => + ( currentSource === dcPowerSupply ) ? dcNormalizedCurrent : acNormalizedCurrent, { + isValidValue: normalizedCurrent => FELConstants.NORMALIZED_CURRENT_RANGE.contains( normalizedCurrent ), + tandem: coilTandem.createTandem( 'normalizedCurrentProperty' ), + phetioValueType: NumberIO, + phetioDocumentation: FELConstants.NORMALIZED_CURRENT_PHET_IO_DOCUMENTATION } ); - const coil = new Coil( currentAmplitudeProperty, FELConstants.CURRENT_AMPLITUDE_RANGE, { + const coil = new Coil( normalizedCurrentProperty, FELConstants.NORMALIZED_CURRENT_RANGE, { maxLoopArea: 7854, // to match Java version loopAreaPercentRange: new RangeWithValue( 100, 100, 100 ), // fixed loop area numberOfLoopsRange: new RangeWithValue( 1, 4, 4 ), @@ -80,10 +81,10 @@ export default class Electromagnet extends CoilMagnet { // As we said in the Java version... This is a bit of a "fudge". Strength of the magnet is proportional to its EMF. const strengthProperty = new DerivedProperty( - [ coil.numberOfLoopsProperty, coil.numberOfLoopsProperty.rangeProperty, currentAmplitudeProperty ], - ( numberOfLoops, numberOfLoopsRange, currentAmplitude ) => { - const amplitude = ( numberOfLoops / numberOfLoopsRange.max ) * currentAmplitude; - return Math.abs( amplitude ) * FELConstants.MAGNET_STRENGTH_RANGE.max; + [ coil.numberOfLoopsProperty, coil.numberOfLoopsProperty.rangeProperty, normalizedCurrentProperty ], + ( numberOfLoops, numberOfLoopsRange, normalizedCurrent ) => { + const amplitude = Math.abs( ( numberOfLoops / numberOfLoopsRange.max ) * normalizedCurrent ); + return amplitude * FELConstants.MAGNET_STRENGTH_RANGE.max; }, { units: 'G', isValidValue: strength => FELConstants.MAGNET_STRENGTH_RANGE.contains( strength ), @@ -99,11 +100,11 @@ export default class Electromagnet extends CoilMagnet { this.acPowerSupply = acPowerSupply; this.currentSourceProperty = currentSourceProperty; - // Polarity is determined by the sign of the current amplitude. - assert && assert( FELConstants.CURRENT_AMPLITUDE_RANGE.min < 0 && FELConstants.CURRENT_AMPLITUDE_RANGE.max > 0, - 'currentAmplitudeProperty listener assumes that range is signed' ); - this.coil.currentAmplitudeProperty.link( currentAmplitude => { - this.rotationProperty.value = ( currentAmplitude >= 0 ) ? 0 : Math.PI; + // Polarity is determined by the sign of the normalized current. + assert && assert( FELConstants.NORMALIZED_CURRENT_RANGE.min < 0 && FELConstants.NORMALIZED_CURRENT_RANGE.max > 0, + 'normalizedCurrentProperty listener assumes that range is signed' ); + this.coil.normalizedCurrentProperty.link( normalizedCurrent => { + this.rotationProperty.value = ( normalizedCurrent >= 0 ) ? 0 : Math.PI; } ); this.shapeVisibleProperty = new BooleanProperty( false diff --git a/js/common/model/Electron.ts b/js/common/model/Electron.ts index 2eb1356b..11db33e5 100644 --- a/js/common/model/Electron.ts +++ b/js/common/model/Electron.ts @@ -48,9 +48,9 @@ type ElectronOptions = SelfOptions; export default class Electron { - // Amplitude of the current that this electron represents. - private readonly currentAmplitudeProperty: TReadOnlyProperty; - private readonly currentAmplitudeRange: Range; + // Current that this electron represents. + private readonly normalizedCurrentProperty: TReadOnlyProperty; + private readonly normalizedCurrentRange: Range; // Electron's position, relative to the coil's position. This Vector2 is mutated as position changes. private readonly position: Vector2; @@ -71,13 +71,13 @@ export default class Electron { // Scale for adjusting speed. private readonly speedScaleProperty: TReadOnlyProperty; - public constructor( currentAmplitudeProperty: TReadOnlyProperty, currentAmplitudeRange: Range, providedOptions: ElectronOptions ) { + public constructor( normalizedCurrentProperty: TReadOnlyProperty, normalizedCurrentRange: Range, providedOptions: ElectronOptions ) { const options = providedOptions; assert && assert( COIL_SEGMENT_POSITION_RANGE.contains( options.coilSegmentPosition ) ); - this.currentAmplitudeProperty = currentAmplitudeProperty; - this.currentAmplitudeRange = currentAmplitudeRange; + this.normalizedCurrentProperty = normalizedCurrentProperty; + this.normalizedCurrentRange = normalizedCurrentRange; const coilSegment = options.coilSegments[ options.coilSegmentIndex ]; @@ -130,7 +130,7 @@ export default class Electron { public step( dt: number ): void { assert && assert( dt === ConstantDtClock.DT, `invalid dt=${dt}` ); - this.speedAndDirection = this.currentAmplitudeToSpeedAndDirection( this.currentAmplitudeProperty.value ); + this.speedAndDirection = this.normalizedCurrentToSpeedAndDirection( this.normalizedCurrentProperty.value ); if ( this.speedAndDirection !== 0 ) { @@ -158,13 +158,13 @@ export default class Electron { } /** - * Maps current amplitude in the coil to the electron's speed and direction. + * Maps normalized current in the coil to the electron's speed and direction. */ - private currentAmplitudeToSpeedAndDirection( currentAmplitude: number ): number { + private normalizedCurrentToSpeedAndDirection( normalizedCurrent: number ): number { let speedAndDirection = 0; - if ( Math.abs( currentAmplitude ) > FELConstants.CURRENT_AMPLITUDE_THRESHOLD ) { - speedAndDirection = Utils.linear( this.currentAmplitudeRange.min, this.currentAmplitudeRange.max, - this.speedAndDirectionRange.min, this.speedAndDirectionRange.max, currentAmplitude ); + if ( Math.abs( normalizedCurrent ) > FELConstants.NORMALIZED_CURRENT_THRESHOLD ) { + speedAndDirection = Utils.linear( this.normalizedCurrentRange.min, this.normalizedCurrentRange.max, + this.speedAndDirectionRange.min, this.speedAndDirectionRange.max, normalizedCurrent ); } return speedAndDirection; } diff --git a/js/common/model/LightBulb.ts b/js/common/model/LightBulb.ts index adecfb73..296f48db 100644 --- a/js/common/model/LightBulb.ts +++ b/js/common/model/LightBulb.ts @@ -2,7 +2,7 @@ /** * LightBulb is the model of the light bulb, as an indicator of current in the pickup coil. Brightness of the light - * is proportional to the current amplitude in the pickup coil. + * is proportional to the current in the pickup coil. * * This is based on LightBulb.java in the Java version of this sim. * @@ -38,7 +38,7 @@ export default class LightBulb extends CurrentIndicator { public readonly brightnessProperty: TReadOnlyProperty; private readonly _brightnessProperty: NumberProperty; - public constructor( currentAmplitudeProperty: TReadOnlyProperty, currentAmplitudeRange: Range, providedOptions: LightBulbOptions ) { + public constructor( normalizedCurrentProperty: TReadOnlyProperty, normalizedCurrentRange: Range, providedOptions: LightBulbOptions ) { const options = optionize()( { @@ -49,7 +49,7 @@ export default class LightBulb extends CurrentIndicator { super( options ); // Unfortunately cannot be a DerivedProperty, because the derivation depends on both the new and old value - // of currentAmplitudeProperty. + // of normalizedCurrentProperty. this._brightnessProperty = new NumberProperty( 0, { range: BRIGHTNESS_RANGE, tandem: options.tandem.createTandem( 'brightnessProperty' ), @@ -58,24 +58,24 @@ export default class LightBulb extends CurrentIndicator { } ); this.brightnessProperty = this._brightnessProperty; - currentAmplitudeProperty.link( ( currentAmplitude, previousCurrentAmplitude ) => { + normalizedCurrentProperty.link( ( normalizedCurrent, previousNormalizedCurrent ) => { let brightness = 0; - if ( previousCurrentAmplitude !== null && - Math.sign( currentAmplitude ) !== Math.sign( previousCurrentAmplitude ) && + if ( previousNormalizedCurrent !== null && + Math.sign( normalizedCurrent ) !== Math.sign( previousNormalizedCurrent ) && !options.lightsWhenCurrentChangesDirection ) { // Current changed direction and should not light the bulb. brightness = 0; } - else if ( Math.abs( currentAmplitude ) < FELConstants.CURRENT_AMPLITUDE_THRESHOLD ) { + else if ( Math.abs( normalizedCurrent ) < FELConstants.NORMALIZED_CURRENT_THRESHOLD ) { // Current below the threshold does not light the bulb. brightness = 0; } else { - // Map current amplitude to brightness. - brightness = Utils.linear( 0, currentAmplitudeRange.max, BRIGHTNESS_RANGE.min, BRIGHTNESS_RANGE.max, Math.abs( currentAmplitude ) ); + // Map current to brightness. + brightness = Utils.linear( 0, normalizedCurrentRange.max, BRIGHTNESS_RANGE.min, BRIGHTNESS_RANGE.max, Math.abs( normalizedCurrent ) ); } this._brightnessProperty.value = brightness; diff --git a/js/common/model/PickupCoil.ts b/js/common/model/PickupCoil.ts index eb6eafe1..6f4f3d4f 100644 --- a/js/common/model/PickupCoil.ts +++ b/js/common/model/PickupCoil.ts @@ -67,8 +67,8 @@ export default class PickupCoil extends FELMovable { //TODO https://github.com/phetsims/faradays-electromagnetic-lab/issues/66 This can likely be deleted when calibration method is changed. private largestAbsoluteEMF; // in volts - // Amplitude and direction of current in the coil. See Coil currentAmplitudeProperty. - private readonly currentAmplitudeProperty: TReadOnlyProperty; + // Relative magnitude and direction of current in the coil. See Coil normalizedCurrentProperty. + private readonly normalizedCurrentProperty: TReadOnlyProperty; // Devices that respond to induced EMF, aka 'current indicators' public readonly lightBulb: LightBulb; @@ -80,11 +80,10 @@ export default class PickupCoil extends FELMovable { // B-field sample points along the vertical axis of the coil public readonly samplePointsProperty: TReadOnlyProperty; - // DEBUG: Writeable via developer controls only, when running with &dev query parameter. - // Dividing the coil's EMF by this number will give us the coil's current amplitude, a number between 0 and 1 that - // determines the responsiveness of view components. This number should be set as close as possible to the maximum - // EMF that can be induced given the range of all model parameters. See calibrateMaxEMF for guidance on how - // to set this. + // DEBUG: Writeable via developer controls only, when running with &dev query parameter. Dividing the coil's EMF by + // this number will give us the coil's normalized current (see Coil.normalizedCurrentProperty), which determines the + // responsiveness of view components. This number should be set as close as possible to the maximum EMF that can be + // induced given the range of all model parameters. See calibrateMaxEMF for guidance on how to set this. public readonly maxEMFProperty: NumberProperty; // DEBUG: Writeable via developer controls only, when running with &dev query parameter. @@ -161,18 +160,20 @@ export default class PickupCoil extends FELMovable { // Do not instrument. This is a PhET developer Property. } ); - this.currentAmplitudeProperty = new DerivedProperty( [ this.emfProperty, this.maxEMFProperty ], + // Normalized current is proportional to the induced EMF. We are considering resistance to be constant, + // unaffected by the coil's loop area and number of loops. + this.normalizedCurrentProperty = new DerivedProperty( [ this.emfProperty, this.maxEMFProperty ], ( emf, maxEMF ) => { - const currentAmplitude = emf / maxEMF; - return FELConstants.CURRENT_AMPLITUDE_RANGE.constrainValue( currentAmplitude ); + const normalizedCurrent = emf / maxEMF; + return FELConstants.NORMALIZED_CURRENT_RANGE.constrainValue( normalizedCurrent ); }, { - isValidValue: currentAmplitude => FELConstants.CURRENT_AMPLITUDE_RANGE.contains( currentAmplitude ), - tandem: coilTandem.createTandem( 'currentAmplitudeProperty' ), + isValidValue: normalizedCurrent => FELConstants.NORMALIZED_CURRENT_RANGE.contains( normalizedCurrent ), + tandem: coilTandem.createTandem( 'normalizedCurrentProperty' ), phetioValueType: NumberIO, - phetioDocumentation: 'For internal use only.' + phetioDocumentation: FELConstants.NORMALIZED_CURRENT_PHET_IO_DOCUMENTATION } ); - this.coil = new Coil( this.currentAmplitudeProperty, FELConstants.CURRENT_AMPLITUDE_RANGE, + this.coil = new Coil( this.normalizedCurrentProperty, FELConstants.NORMALIZED_CURRENT_RANGE, combineOptions( { maxLoopArea: 70685, // to match Java version loopAreaPercentRange: new RangeWithValue( 20, 100, 50 ), @@ -180,12 +181,12 @@ export default class PickupCoil extends FELMovable { tandem: coilTandem }, options.coilOptions ) ); - this.lightBulb = new LightBulb( this.currentAmplitudeProperty, FELConstants.CURRENT_AMPLITUDE_RANGE, + this.lightBulb = new LightBulb( this.normalizedCurrentProperty, FELConstants.NORMALIZED_CURRENT_RANGE, combineOptions( { tandem: options.tandem.createTandem( 'lightBulb' ) }, options.lightBulbOptions ) ); - this.voltmeter = new Voltmeter( this.currentAmplitudeProperty, FELConstants.CURRENT_AMPLITUDE_RANGE, + this.voltmeter = new Voltmeter( this.normalizedCurrentProperty, FELConstants.NORMALIZED_CURRENT_RANGE, combineOptions( { tandem: options.tandem.createTandem( 'voltmeter' ) }, options.voltmeterOptions ) ); @@ -298,7 +299,7 @@ export default class PickupCoil extends FELMovable { // for maxEMFProperty. if ( absEMF > this.largestAbsoluteEMF ) { this.largestAbsoluteEMF = absEMF; - console.log( `PickupCoil.calibrateMaxEMF, largestAbsoluteEMF=${this.largestAbsoluteEMF} for currentAmplitude=${this.currentAmplitudeProperty.value}` ); + console.log( `PickupCoil.calibrateMaxEMF, largestAbsoluteEMF=${this.largestAbsoluteEMF} for normalizedCurrent=${this.normalizedCurrentProperty.value}` ); // If this prints, you have maxEMFProperty set too low. This will cause view components to exhibit responses // that are less than their maximums. For example, the voltmeter won't fully deflect, and the lightbulb won't diff --git a/js/common/model/Voltmeter.ts b/js/common/model/Voltmeter.ts index ffb29773..7af7562c 100644 --- a/js/common/model/Voltmeter.ts +++ b/js/common/model/Voltmeter.ts @@ -39,9 +39,9 @@ export type VoltmeterOptions = SelfOptions & PickRequired; - private readonly currentAmplitudeRange: Range; + // Normalized current in the pickup coil. See Coil.normalizedCurrentProperty. + private readonly normalizedCurrentProperty: TReadOnlyProperty; + private readonly normalizedCurrentRange: Range; // The deflection angle of the voltmeter's needle, relative to zero volts. public readonly needleAngleProperty: TReadOnlyProperty; @@ -51,8 +51,8 @@ export default class Voltmeter extends CurrentIndicator { // Whether kinematics of the needle is enabled. private readonly kinematicsEnabledProperty: TReadOnlyProperty; - public constructor( currentAmplitudeProperty: TReadOnlyProperty, - currentAmplitudeRange: Range, + public constructor( normalizedCurrentProperty: TReadOnlyProperty, + normalizedCurrentRange: Range, providedOptions: VoltmeterOptions ) { const options = optionize()( { @@ -63,8 +63,8 @@ export default class Voltmeter extends CurrentIndicator { super( options ); - this.currentAmplitudeProperty = currentAmplitudeProperty; - this.currentAmplitudeRange = currentAmplitudeRange; + this.normalizedCurrentProperty = normalizedCurrentProperty; + this.normalizedCurrentRange = normalizedCurrentRange; this.needAngleRange = new Range( -Math.PI / 2, Math.PI / 2 ); this._needleAngleProperty = new NumberProperty( 0, { @@ -111,24 +111,24 @@ export default class Voltmeter extends CurrentIndicator { } /** - * Gets the desired needle deflection angle, in radians. - * This is the angle that corresponds exactly to the voltage read by the meter. + * Gets the desired needle deflection angle, in radians. This is the angle that corresponds exactly to the "voltage" + * read by the meter. Note that the meter actually uses current as the signal, a 'Hollywood' model. */ private getDesiredNeedleAngle(): number { - // Use amplitude of the voltage source as our signal. - let currentAmplitude = this.currentAmplitudeProperty.value; + // Use normalized current in the coil as our signal. + let normalizedCurrent = this.normalizedCurrentProperty.value; - // Absolute amplitude below the threshold is effectively zero. - if ( Math.abs( currentAmplitude ) < FELConstants.CURRENT_AMPLITUDE_THRESHOLD ) { - currentAmplitude = 0; + // Current below the threshold is effectively zero. + if ( Math.abs( normalizedCurrent ) < FELConstants.NORMALIZED_CURRENT_THRESHOLD ) { + normalizedCurrent = 0; } - // Map from currentAmplitude to needleAngle. + // Map from current to needleAngle. return Utils.linear( - this.currentAmplitudeRange.min, this.currentAmplitudeRange.max, + this.normalizedCurrentRange.min, this.normalizedCurrentRange.max, this.needAngleRange.min, this.needAngleRange.max, - currentAmplitude + normalizedCurrent ); } } diff --git a/js/common/view/DCPowerSupplyNode.ts b/js/common/view/DCPowerSupplyNode.ts index 36f150eb..926745b2 100644 --- a/js/common/view/DCPowerSupplyNode.ts +++ b/js/common/view/DCPowerSupplyNode.ts @@ -89,12 +89,12 @@ export default class DCPowerSupplyNode extends Node { this.visibleProperty.lazyLink( visible => !visible && this.interruptSubtreeInput() ); // Reflect the battery about the y-axis to change its polarity. - dcPowerSupply.currentAmplitudeProperty.link( ( amplitude, previousAmplitude ) => { - if ( amplitude >= 0 && ( previousAmplitude === null || previousAmplitude < 0 ) ) { + dcPowerSupply.normalizedCurrentProperty.link( ( normalizedCurrent, previousNormalizedCurrent ) => { + if ( normalizedCurrent >= 0 && ( previousNormalizedCurrent === null || previousNormalizedCurrent < 0 ) ) { batteryNode.matrix = Matrix3.IDENTITY; batteryNode.center = Vector2.ZERO; } - else if ( amplitude < 0 && ( previousAmplitude === null || previousAmplitude >= 0 ) ) { + else if ( normalizedCurrent < 0 && ( previousNormalizedCurrent === null || previousNormalizedCurrent >= 0 ) ) { batteryNode.matrix = Matrix3.X_REFLECTION; batteryNode.center = Vector2.ZERO; } @@ -102,10 +102,10 @@ export default class DCPowerSupplyNode extends Node { // Position the volts value at the positive pole of the battery. Multilink.multilink( - [ dcPowerSupply.currentAmplitudeProperty, voltsText.boundsProperty ], - ( amplitude, voltsTextBounds ) => { + [ dcPowerSupply.normalizedCurrentProperty, voltsText.boundsProperty ], + ( normalizedCurrent, voltsTextBounds ) => { const xMargin = 15; - if ( amplitude >= 0 ) { + if ( normalizedCurrent >= 0 ) { voltsText.right = batteryNode.right - xMargin; } else { diff --git a/js/common/view/FELScreenIconFactory.ts b/js/common/view/FELScreenIconFactory.ts index b02ab15e..6ad2427e 100644 --- a/js/common/view/FELScreenIconFactory.ts +++ b/js/common/view/FELScreenIconFactory.ts @@ -127,7 +127,7 @@ function createBarMagnetNode( size?: Dimension2 ): Node { */ function createCoilNode( numberOfLoops: number, loopSpacing: number, maxLoopArea: number ): Node { - const coil = new Coil( new NumberProperty( 0 ), FELConstants.CURRENT_AMPLITUDE_RANGE, { + const coil = new Coil( new NumberProperty( 0 ), FELConstants.NORMALIZED_CURRENT_RANGE, { numberOfLoopsRange: new RangeWithValue( numberOfLoops, numberOfLoops, numberOfLoops ), loopSpacing: loopSpacing, maxLoopArea: maxLoopArea,