-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathModifiedNodalAnalysisAdapter.js
257 lines (216 loc) · 9.41 KB
/
ModifiedNodalAnalysisAdapter.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
// Copyright 2019-2020, University of Colorado Boulder
/**
* Takes a Circuit, creates a corresponding DynamicCircuit, solves the DynamicCircuit and applies the results back
* to the original Circuit.
*
* @author Sam Reid (PhET Interactive Simulations)
*/
import CCKCQueryParameters from '../CCKCQueryParameters.js';
import circuitConstructionKitCommon from '../circuitConstructionKitCommon.js';
import Capacitor from './Capacitor.js';
import DynamicCircuit from './DynamicCircuit.js';
import Fuse from './Fuse.js';
import Inductor from './Inductor.js';
import LightBulb from './LightBulb.js';
import ModifiedNodalAnalysisCircuitElement from './ModifiedNodalAnalysisCircuitElement.js';
import Resistor from './Resistor.js';
import SeriesAmmeter from './SeriesAmmeter.js';
import Switch from './Switch.js';
import TimestepSubdivisions from './TimestepSubdivisions.js';
import VoltageSource from './VoltageSource.js';
import Wire from './Wire.js';
// constants
const TIMESTEP_SUBDIVISIONS = new TimestepSubdivisions();
class ResistiveBatteryAdapter extends DynamicCircuit.ResistiveBattery {
constructor( c, battery ) {
super(
c.vertexGroup.indexOf( battery.startVertexProperty.value ),
c.vertexGroup.indexOf( battery.endVertexProperty.value ),
battery.voltageProperty.value,
battery.internalResistanceProperty.value
);
// @public (read-only)
this.battery = battery;
}
/**
* @param circuitResult
* @public
*/
applySolution( circuitResult ) {
this.battery.currentProperty.value = circuitResult.getTimeAverageCurrent( this );
}
}
class ResistorAdapter extends ModifiedNodalAnalysisCircuitElement {
/**
* @param {Circuit} circuit
* @param {Resistor} resistor
*/
constructor( circuit, resistor ) {
super(
circuit.vertexGroup.indexOf( resistor.startVertexProperty.value ),
circuit.vertexGroup.indexOf( resistor.endVertexProperty.value ),
resistor,
resistor.resistanceProperty.value
);
this.resistor = resistor;
}
/**
* @param circuitResult
* @public
*/
applySolution( circuitResult ) {
this.resistor.currentProperty.value = circuitResult.getTimeAverageCurrent( this );
}
}
class CapacitorAdapter extends DynamicCircuit.DynamicCapacitor {
/**
* @param {Circuit} circuit
* @param {Capacitor} capacitor
*/
constructor( circuit, capacitor ) {
const dynamicCircuitCapacitor = new DynamicCircuit.Capacitor(
circuit.vertexGroup.indexOf( capacitor.startVertexProperty.value ),
circuit.vertexGroup.indexOf( capacitor.endVertexProperty.value ),
capacitor.capacitanceProperty.value
);
super( dynamicCircuitCapacitor, new DynamicCircuit.DynamicElementState( capacitor.mnaVoltageDrop, capacitor.mnaCurrent ) );
// @private - alongside this.dynamicCircuitCapacitor assigned in the supertype
this.capacitor = capacitor;
}
/**
* @param {CircuitResult} circuitResult
* @public
*/
applySolution( circuitResult ) {
this.capacitor.currentProperty.value = circuitResult.getTimeAverageCurrent( this.dynamicCircuitCapacitor );
this.capacitor.mnaCurrent = circuitResult.getInstantaneousCurrent( this.dynamicCircuitCapacitor );
this.capacitor.mnaVoltageDrop = circuitResult.getInstantaneousVoltage( this.dynamicCircuitCapacitor );
}
}
class InductorAdapter extends DynamicCircuit.DynamicInductor {
/**
* @param {Circuit} circuit
* @param {Inductor} inductor
*/
constructor( circuit, inductor ) {
const dynamicCircuitInductor = new DynamicCircuit.Inductor(
circuit.vertexGroup.indexOf( inductor.startVertexProperty.value ),
circuit.vertexGroup.indexOf( inductor.endVertexProperty.value ),
inductor.inductanceProperty.value
);
super( dynamicCircuitInductor, new DynamicCircuit.DynamicElementState( inductor.mnaVoltageDrop, inductor.mnaCurrent ) );
// @private - alongside this.dynamicCircuitInductor assigned in the supertype
this.inductor = inductor;
}
/**
* @param {CircuitResult} circuitResult
* @public
*/
applySolution( circuitResult ) {
// TODO: (sign-error):
this.inductor.currentProperty.value = -circuitResult.getTimeAverageCurrent( this.dynamicCircuitInductor );
this.inductor.mnaCurrent = circuitResult.getInstantaneousCurrent( this.dynamicCircuitInductor );
this.inductor.mnaVoltageDrop = circuitResult.getInstantaneousVoltage( this.dynamicCircuitInductor );
}
}
class ModifiedNodalAnalysisAdapter {
/**
* Solves the system with Modified Nodal Analysis, and apply the results back to the Circuit.
* @param {Circuit} circuit
* @param {number} dt
* @public
*/
static solveModifiedNodalAnalysis( circuit, dt ) {
const resistiveBatteryAdapters = [];
const resistorAdapters = [];
const capacitorAdapters = [];
const inductorAdapters = [];
for ( let i = 0; i < circuit.circuitElements.length; i++ ) {
const branch = circuit.circuitElements.get( i );
if ( branch instanceof VoltageSource ) {
branch.passProperty.reset(); // also resets the internalResistance for the first pass computation
resistiveBatteryAdapters.push( new ResistiveBatteryAdapter( circuit, branch ) );
}
else if ( branch instanceof Resistor ||
branch instanceof Fuse ||
branch instanceof Wire ||
branch instanceof LightBulb ||
branch instanceof SeriesAmmeter ||
// Since no closed circuit there; see below where current is zeroed out
( branch instanceof Switch && branch.closedProperty.value ) ) {
resistorAdapters.push( new ResistorAdapter( circuit, branch ) );
}
else if ( branch instanceof Switch && !branch.closedProperty.value ) {
// no element for an open switch
}
else if ( branch instanceof Capacitor ) {
capacitorAdapters.push( new CapacitorAdapter( circuit, branch ) );
}
else if ( branch instanceof Inductor ) {
inductorAdapters.push( new InductorAdapter( circuit, branch ) );
}
else {
assert && assert( false, 'Type not found: ' + branch.constructor.name );
}
}
const dynamicCircuit = new DynamicCircuit( resistorAdapters, resistiveBatteryAdapters, capacitorAdapters, inductorAdapters );
let circuitResult = dynamicCircuit.solveWithSubdivisions( TIMESTEP_SUBDIVISIONS, dt );
// if any battery exceeds its current threshold, increase its resistance and run the solution again.
// see https://github.com/phetsims/circuit-construction-kit-common/issues/245
let needsHelp = false;
resistorAdapters.forEach( resistorAdapter => {
if ( resistorAdapter.circuitElement instanceof LightBulb && !resistorAdapter.circuitElement.ohmic ) {
resistorAdapter.resistance = 1.0;
needsHelp = true;
}
} );
resistiveBatteryAdapters.forEach( batteryAdapter => {
if ( Math.abs( circuitResult.getTimeAverageCurrent( batteryAdapter ) ) > CCKCQueryParameters.batteryCurrentThreshold ) {
batteryAdapter.battery.passProperty.value = 2;
batteryAdapter.resistance = batteryAdapter.battery.internalResistanceProperty.value;
needsHelp = true;
}
} );
resistorAdapters.forEach( resistorAdapter => {
if ( resistorAdapter.circuitElement instanceof LightBulb && !resistorAdapter.circuitElement.ohmic ) {
const logWithBase = ( value, base ) => Math.log( value ) / Math.log( base );
const v0 = circuitResult.resultSet.getFinalState().dynamicCircuitSolution.getNodeVoltage( resistorAdapter.nodeId0 );
const v1 = circuitResult.resultSet.getFinalState().dynamicCircuitSolution.getNodeVoltage( resistorAdapter.nodeId1 );
const V = Math.abs( v1 - v0 );
const base = 2;
// I = ln(V)
// V=IR
// V=ln(V)R
// R = V/ln(V)
// Adjust so it looks good in comparison to a standard bulb
const coefficient = 3;
// shift by base so at V=0 the log is 1
resistorAdapter.value = 10 + coefficient * V / logWithBase( V + base, base );
resistorAdapter.circuitElement.resistanceProperty.value = resistorAdapter.value;
}
} );
if ( needsHelp ) {
circuitResult = dynamicCircuit.solveWithSubdivisions( TIMESTEP_SUBDIVISIONS, dt );
}
resistiveBatteryAdapters.forEach( batteryAdapter => batteryAdapter.applySolution( circuitResult ) );
resistorAdapters.forEach( resistorAdapter => resistorAdapter.applySolution( circuitResult ) );
capacitorAdapters.forEach( capacitorAdapter => capacitorAdapter.applySolution( circuitResult ) );
inductorAdapters.forEach( inductorAdapter => inductorAdapter.applySolution( circuitResult ) );
// zero out currents on open branches
for ( let i = 0; i < circuit.circuitElements.length; i++ ) {
const branch = circuit.circuitElements.get( i );
if ( branch instanceof Switch && !branch.closedProperty.value ) {
branch.currentProperty.value = 0.0;
// sw.setVoltageDrop( 0.0 );
}
}
// Apply the node voltages to the vertices
circuit.vertexGroup.forEach( ( vertex, i ) => {
const v = circuitResult.resultSet.getFinalState().dynamicCircuitSolution.getNodeVoltage( i );
// Unconnected vertices like those in the black box may not have an entry in the matrix, so mark them as zero.
vertex.voltageProperty.set( v || 0 );
} );
}
}
circuitConstructionKitCommon.register( 'ModifiedNodalAnalysisAdapter', ModifiedNodalAnalysisAdapter );
export default ModifiedNodalAnalysisAdapter;