Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
4f54c4b2
Commit
4f54c4b2
authored
Jan 19, 2012
by
cjt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
new .js files
parent
74e9fc7a
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
365 additions
and
147 deletions
+365
-147
js/cktsim.js
+330
-131
js/schematic.js
+35
-16
No files found.
js/cktsim.js
View file @
4f54c4b2
...
...
@@ -6,6 +6,7 @@
// Copyright (C) 2011 Massachusetts Institute of Technology
// create a circuit for simulation using "new cktsim.Circuit()"
// for modified nodal analysis (MNA) stamps see
...
...
@@ -30,9 +31,11 @@ cktsim = (function() {
max_dc_iters
=
200
;
// max iterations before giving pu
max_tran_iters
=
10
;
// max iterations before giving up
increase_limit
=
4
;
// if we converge in this many iterations, increase time step
time_step_increase_factor
=
2.0
;
time_step_decrease_factor
=
0.3
;
reltol
=
0.001
;
// convergence criterion relative to max observed value
time_step_increase_factor
=
2.0
;
// How much can lte let timestep grow.
lte_step_decrease_factor
=
8
;
// How much will lte shrink timestep in one iter.
nr_step_decrease_factor
=
4
;
// How much Newton will shink timeste in one iter.
reltol
=
0.0001
;
// convergence criterion relative to max observed value
lterel
=
4
;
// The ratio between lte error and Newton error.
function
Circuit
()
{
this
.
node_map
=
new
Array
();
...
...
@@ -141,16 +144,15 @@ cktsim = (function() {
// if converges: updates this.solution, this.soln_max, returns iter count
// otherwise: return undefined and set this.problem_node
//
The argument should be a function that sets up the linear system.
//
Load should compute -f and df/dx (note the sign pattern!)
Circuit
.
prototype
.
find_solution
=
function
(
load
,
maxiters
)
{
var
soln
=
this
.
solution
;
var
rhs
=
this
.
rhs
;
var
d_sol
,
temp
,
converged
;
var
d_sol
,
converged
;
// iteratively solve until values convere or iteration limit exceeded
for
(
var
iter
=
0
;
iter
<
maxiters
;
iter
++
)
{
// set up equations
// no longer needed this.initialize_linear_system();
load
(
this
,
soln
,
rhs
);
// Compute the Newton delta
...
...
@@ -173,7 +175,7 @@ cktsim = (function() {
this
.
problem_node
=
i
;
}
}
//
alert(numeric.prettyPrint(this.solution));
//
alert(numeric.prettyPrint(this.solution);)
if
(
converged
==
true
)
return
iter
+
1
;
}
// too many iterations
...
...
@@ -189,7 +191,7 @@ cktsim = (function() {
this
.
devices
[
i
].
load_linear
(
this
)
}
// Define f and df/dx for Newton solver
// Define
-
f and df/dx for Newton solver
function
load_dc
(
ckt
,
soln
,
rhs
)
{
// rhs is initialized to -Gl * soln
ckt
.
matv_mult
(
ckt
.
Gl
,
soln
,
rhs
,
-
1.0
);
...
...
@@ -198,7 +200,7 @@ cktsim = (function() {
// Now load up the nonlinear parts of rhs and G
for
(
var
i
=
ckt
.
devices
.
length
-
1
;
i
>=
0
;
--
i
)
ckt
.
devices
[
i
].
load_dc
(
ckt
,
soln
,
rhs
);
// G matrix is
initialized with linear Gl
// G matrix is
copied in to the system matrix
ckt
.
copy_mat
(
ckt
.
G
,
ckt
.
matrix
);
}
...
...
@@ -221,9 +223,80 @@ cktsim = (function() {
}
// Transient analysis (needs work!)
Circuit
.
prototype
.
tran
=
function
(
ntpts
,
tstart
,
tstop
,
no_dc
)
{
Circuit
.
prototype
.
tran
=
function
(
ntpts
,
tstart
,
tstop
,
probenames
,
no_dc
)
{
// Define -f and df/dx for Newton solver
function
load_tran
(
ckt
,
soln
,
rhs
)
{
// Crnt is initialized to -Gl * soln
ckt
.
matv_mult
(
ckt
.
Gl
,
soln
,
ckt
.
c
,
-
1.0
);
// G matrix is initialized with linear Gl
ckt
.
copy_mat
(
ckt
.
Gl
,
ckt
.
G
);
// Now load up the nonlinear parts of crnt and G
for
(
var
i
=
ckt
.
devices
.
length
-
1
;
i
>=
0
;
--
i
)
ckt
.
devices
[
i
].
load_tran
(
ckt
,
soln
,
ckt
.
c
,
ckt
.
time
);
// Exploit the fact that storage elements are linear
ckt
.
matv_mult
(
ckt
.
C
,
soln
,
ckt
.
q
,
1.0
);
// -rhs = c - dqdt
for
(
var
i
=
ckt
.
N
-
1
;
i
>=
0
;
--
i
)
{
var
dqdt
=
ckt
.
alpha0
*
ckt
.
q
[
i
]
+
ckt
.
alpha1
*
ckt
.
oldq
[
i
]
+
ckt
.
alpha2
*
ckt
.
old2q
[
i
];
//alert(numeric.prettyPrint(dqdt));
rhs
[
i
]
=
ckt
.
beta0
[
i
]
*
ckt
.
c
[
i
]
+
ckt
.
beta1
[
i
]
*
ckt
.
oldc
[
i
]
-
dqdt
;
}
// matrix = beta0*G + alpha0*C.
ckt
.
mat_scale_add
(
ckt
.
G
,
ckt
.
C
,
ckt
.
beta0
,
ckt
.
alpha0
,
ckt
.
matrix
);
}
var
p
=
new
Array
(
3
);
function
interp_coeffs
(
t
,
t0
,
t1
,
t2
)
{
// Poly coefficients
var
dtt0
=
(
t
-
t0
);
var
dtt1
=
(
t
-
t1
);
var
dtt2
=
(
t
-
t2
);
var
dt0dt1
=
(
t0
-
t1
);
var
dt0dt2
=
(
t0
-
t2
);
var
dt1dt2
=
(
t1
-
t2
);
p
[
0
]
=
(
dtt1
*
dtt2
)
/
(
dt0dt1
*
dt0dt2
);
p
[
1
]
=
(
dtt0
*
dtt2
)
/
(
-
dt0dt1
*
dt1dt2
);
p
[
2
]
=
(
dtt0
*
dtt1
)
/
(
dt0dt2
*
dt1dt2
);
return
p
;
}
function
pick_step
(
ckt
,
step_index
)
{
var
min_shrink_factor
=
1.0
/
lte_step_decrease_factor
;
var
max_growth_factor
=
time_step_increase_factor
;
var
N
=
ckt
.
N
;
var
p
=
interp_coeffs
(
ckt
.
time
,
ckt
.
oldt
,
ckt
.
old2t
,
ckt
.
old3t
);
var
trapcoeff
=
0.5
*
(
ckt
.
time
-
ckt
.
oldt
)
/
(
ckt
.
time
-
ckt
.
old3t
);
var
maxlteratio
=
0.0
;
for
(
var
i
=
ckt
.
N
-
1
;
i
>=
0
;
--
i
)
{
if
(
ckt
.
ltecheck
[
i
])
{
// Check lte on variable
var
pred
=
p
[
0
]
*
ckt
.
oldsol
[
i
]
+
p
[
1
]
*
ckt
.
old2sol
[
i
]
+
p
[
2
]
*
ckt
.
old3sol
[
i
];
var
lte
=
Math
.
abs
((
ckt
.
solution
[
i
]
-
pred
))
*
trapcoeff
;
var
lteratio
=
lte
/
(
lterel
*
(
ckt
.
abstol
[
i
]
+
reltol
*
ckt
.
soln_max
[
i
]));
maxlteratio
=
Math
.
max
(
maxlteratio
,
lteratio
);
}
}
var
new_step
;
var
lte_step_ratio
=
1.0
/
Math
.
pow
(
maxlteratio
,
1
/
3
);
// Cube root because trap
if
(
lte_step_ratio
<
1.0
)
{
// Shrink the timestep to make lte
lte_step_ratio
=
Math
.
max
(
lte_step_ratio
,
min_shrink_factor
);
new_step
=
(
ckt
.
time
-
ckt
.
oldt
)
*
0.75
*
lte_step_ratio
;
new_step
=
Math
.
max
(
new_step
,
ckt
.
min_step
);
}
else
{
lte_step_ratio
=
Math
.
min
(
lte_step_ratio
,
max_growth_factor
);
if
(
lte_step_ratio
>
1.2
)
/* Increase timestep due to lte. */
new_step
=
(
ckt
.
time
-
ckt
.
oldt
)
*
lte_step_ratio
/
1.2
;
else
new_step
=
(
ckt
.
time
-
ckt
.
oldt
);
new_step
=
Math
.
min
(
new_step
,
ckt
.
max_step
);
}
return
new_step
;
}
// Standard to do a dc analysis before transient
// Otherwise, do the setup also done in dc.
//no_dc = true;
if
((
this
.
diddc
==
false
)
&&
(
no_dc
==
false
))
this
.
dc
();
else
{
// Allocate matrices and vectors.
...
...
@@ -242,70 +315,140 @@ cktsim = (function() {
var
response
=
new
Array
(
N
+
1
);
for
(
var
i
=
N
;
i
>=
0
;
--
i
)
response
[
i
]
=
new
Array
();
// Allocate space to put previous charge and current
this
.
oldc
=
new
Array
(
this
.
N
);
// Allocate back vectors for up to a second order method
this
.
old3sol
=
new
Array
(
this
.
N
);
this
.
old3q
=
new
Array
(
this
.
N
);
this
.
old2sol
=
new
Array
(
this
.
N
);
this
.
old2q
=
new
Array
(
this
.
N
);
this
.
oldsol
=
new
Array
(
this
.
N
);
this
.
oldq
=
new
Array
(
this
.
N
);
this
.
c
=
new
Array
(
this
.
N
);
this
.
q
=
new
Array
(
this
.
N
);
this
.
oldc
=
new
Array
(
this
.
N
);
this
.
c
=
new
Array
(
this
.
N
);
this
.
alpha0
=
1.0
;
this
.
alpha1
=
0.0
;
this
.
alpha2
=
0.0
;
this
.
beta0
=
new
Array
(
this
.
N
);
this
.
beta1
=
new
Array
(
this
.
N
);
// Define f and df/dx for Newton solver
function
load_tran
(
ckt
,
soln
,
rhs
)
{
// rhs is initialized to -Gl * soln
ckt
.
matv_mult
(
ckt
.
Gl
,
soln
,
ckt
.
c
,
-
1.0
);
// G matrix is initialized with linear Gl
ckt
.
copy_mat
(
ckt
.
Gl
,
ckt
.
G
);
// Now load up the nonlinear parts of rhs and G
for
(
var
i
=
ckt
.
devices
.
length
-
1
;
i
>=
0
;
--
i
)
ckt
.
devices
[
i
].
load_tran
(
ckt
,
soln
,
ckt
.
c
,
ckt
.
time
);
// Exploit the fact that storage elements are linear
ckt
.
matv_mult
(
ckt
.
C
,
soln
,
ckt
.
q
,
1.0
);
for
(
var
i
=
ckt
.
N
-
1
;
i
>=
0
;
--
i
)
rhs
[
i
]
=
ckt
.
alpha0
*
(
ckt
.
oldq
[
i
]
-
ckt
.
q
[
i
])
+
ckt
.
c
[
i
]
// rhs is initialized to -Gl * soln
ckt
.
matv_mult
(
ckt
.
Gl
,
soln
,
ckt
.
c
,
-
1.0
);
// Mark the algebraic rows (useful for trap)
this
.
ar
=
this
.
zero_row
(
this
.
C
);
// system matrix is G - alpha0 C.
ckt
.
mat_scale_add
(
ckt
.
G
,
ckt
.
C
,
ckt
.
alpha0
,
ckt
.
matrix
);
// Non-algebraic variables and probe variables get lte
this
.
ltecheck
=
new
Array
(
this
.
N
);
for
(
var
i
=
N
;
i
>=
0
;
--
i
)
this
.
ltecheck
[
i
]
=
(
this
.
ar
[
i
]
==
0
);
for
(
var
name
in
this
.
node_map
)
{
var
index
=
this
.
node_map
[
name
];
for
(
var
i
=
probenames
.
length
;
i
>=
0
;
--
i
)
{
if
(
name
==
probenames
[
i
])
{
this
.
ltecheck
[
index
]
=
true
;
break
;
}
}
}
this
.
time
=
tstart
;
var
dt
=
(
tstop
-
tstart
)
/
ntpts
;
this
.
max_step
=
(
tstop
-
tstart
)
/
ntpts
;
this
.
min_step
=
this
.
max_step
/
1
e8
;
var
new_step
=
this
.
max_step
/
1
e6
;
this
.
oldt
=
this
.
time
-
new_step
;
// Initialize
this.c and this.q
// Initialize
old crnts, charges, and solutions.
load_tran
(
this
,
this
.
solution
,
this
.
rhs
)
for
(
var
i
=
N
-
1
;
i
>=
0
;
--
i
)
{
this
.
old3sol
[
i
]
=
this
.
solution
[
i
];
this
.
old2sol
[
i
]
=
this
.
solution
[
i
];
this
.
oldsol
[
i
]
=
this
.
solution
[
i
];
this
.
old3q
[
i
]
=
this
.
q
[
i
];
this
.
old2q
[
i
]
=
this
.
q
[
i
];
this
.
oldq
[
i
]
=
this
.
q
[
i
];
this
.
oldc
[
i
]
=
this
.
c
[
i
];
}
for
(
var
tindex
=
0
;
tindex
<
ntpts
;
tindex
++
)
{
var
step_index
=
-
2
;
// Start with two pseudo-Euler steps
var
beta0
,
beta1
;
while
(
this
.
time
<=
tstop
)
{
// Save the just computed solution, and move back q and c.
for
(
var
i
=
this
.
N
-
1
;
i
>=
0
;
--
i
)
{
response
[
i
].
push
(
this
.
solution
[
i
]);
if
(
step_index
>=
0
)
response
[
i
].
push
(
this
.
solution
[
i
]);
this
.
oldc
[
i
]
=
this
.
c
[
i
];
this
.
old3sol
[
i
]
=
this
.
old2sol
[
i
];
this
.
old2sol
[
i
]
=
this
.
oldsol
[
i
];
this
.
oldsol
[
i
]
=
this
.
solution
[
i
];
this
.
old3q
[
i
]
=
this
.
oldq
[
i
];
this
.
old2q
[
i
]
=
this
.
oldq
[
i
];
this
.
oldq
[
i
]
=
this
.
q
[
i
];
}
response
[
this
.
N
].
push
(
this
.
time
);
this
.
oldtime
=
this
.
time
;
if
(
this
.
time
>=
tstop
)
break
;
// Pick a timestep and an integration method
if
(
this
.
time
+
1.1
*
dt
>
tstop
)
this
.
time
=
tstop
;
else
this
.
time
+=
dt
;
// Set the timestep
this
.
alpha0
=
1.0
/
(
this
.
time
-
this
.
oldtime
);
// Predict the solution, nah maybe later.
if
(
step_index
<
0
)
{
// Take a prestep using BE
this
.
old3t
=
this
.
old2t
-
(
this
.
oldt
-
this
.
old2t
)
this
.
old2t
=
this
.
oldt
-
(
tstart
-
this
.
oldt
)
this
.
oldt
=
tstart
-
(
this
.
time
-
this
.
oldt
);
this
.
time
=
tstart
;
beta0
=
1.0
;
beta1
=
0.0
;
}
else
{
// Take a regular step
// Save the time, and rotate time wheel
response
[
this
.
N
].
push
(
this
.
time
);
this
.
old3t
=
this
.
old2t
;
this
.
old2t
=
this
.
oldt
;
this
.
oldt
=
this
.
time
;
// Make sure we come smoothly in to the interval end.
if
(
this
.
time
>=
tstop
)
break
;
// We're done.
else
if
(
this
.
time
+
new_step
>
tstop
)
this
.
time
=
tstop
;
else
if
(
this
.
time
+
1.5
*
new_step
>
tstop
)
this
.
time
+=
(
2
/
3
)
*
(
tstop
-
this
.
time
);
else
this
.
time
+=
new_step
;
// Trapezoidal rule betas
beta0
=
0.5
;
beta1
=
0.5
;
}
//
Use Newton to compute the solution
.
var
iterations
=
this
.
find_solution
(
load_tran
,
max_tran_iters
)
;
//
Keep track of step index
.
step_index
+=
1
;
if
(
iterations
==
undefined
)
alert
(
'timestep nonconvergence, try more time points'
);
// For trap rule, turn off current avging for algebraic eqns
for
(
var
i
=
this
.
N
-
1
;
i
>=
0
;
--
i
)
{
this
.
beta0
[
i
]
=
beta0
+
this
.
ar
[
i
]
*
beta1
;
this
.
beta1
[
i
]
=
(
1.0
-
this
.
ar
[
i
])
*
beta1
;
}
// Loop to find NR converging timestep with okay LTE
while
(
true
)
{
// Set the timestep coefficients (alpha2 is for bdf2).
this
.
alpha0
=
1.0
/
(
this
.
time
-
this
.
oldt
);
this
.
alpha1
=
-
this
.
alpha0
;
this
.
alpha2
=
0
;
// Use Newton to compute the solution.
var
iterations
=
this
.
find_solution
(
load_tran
,
max_tran_iters
);
// If NR succeeds and stepsize is at min, accept and newstep=maxgrowth*minstep.
// Else if Newton Fails, shrink step by a factor and try again
// Else LTE picks new step, if bigger accept current step and go on.
if
((
iterations
!=
undefined
)
&&
(
step_index
<=
0
||
(
this
.
time
-
this
.
oldt
)
<
(
1
+
reltol
)
*
this
.
min_step
))
{
if
(
step_index
>
0
)
new_step
=
time_step_increase_factor
*
this
.
min_step
;
break
;
}
else
if
(
iterations
==
undefined
)
{
// NR nonconvergence, shrink by factor
//alert('timestep nonconvergence');
this
.
time
=
this
.
oldt
+
(
this
.
time
-
this
.
oldt
)
/
nr_step_decrease_factor
;
}
else
{
// Check the LTE and shrink step if needed.
new_step
=
pick_step
(
this
,
step_index
);
if
(
new_step
<
(
1.0
-
reltol
)
*
(
this
.
time
-
this
.
oldt
))
{
this
.
time
=
this
.
oldt
+
new_step
;
// Try again
}
else
break
;
// LTE okay, new_step for next step
}
}
}
// create solution dictionary
...
...
@@ -355,14 +498,12 @@ cktsim = (function() {
// Find complex x+jy that sats Gx-omega*Cy=rhs; omega*Cx+Gy=0
// Note: solac[0:N-1]=x, solac[N:2N-1]=y
for
(
var
i
=
N
-
1
;
i
>=
0
;
--
i
)
{
for
(
var
i
=
N
-
1
;
i
>=
0
;
--
i
)
{
// First the rhs, replicated for real and imaginary
matrixac
[
i
][
2
*
N
]
=
this
.
rhs
[
i
];
matrixac
[
i
+
N
][
2
*
N
]
=
0
;
for
(
var
j
=
N
-
1
;
j
>=
0
;
--
j
)
{
for
(
var
j
=
N
-
1
;
j
>=
0
;
--
j
)
{
matrixac
[
i
][
j
]
=
G
[
i
][
j
];
matrixac
[
i
+
N
][
j
+
N
]
=
G
[
i
][
j
];
matrixac
[
i
][
j
+
N
]
=
-
omega
*
C
[
i
][
j
];
...
...
@@ -486,7 +627,6 @@ cktsim = (function() {
return
this
.
add_device
(
d
,
name
);
}
///////////////////////////////////////////////////////////////////////////////
//
// Support for creating and solving a system of linear equations
...
...
@@ -501,15 +641,6 @@ cktsim = (function() {
// Knowns (A and b) are stored in an augmented matrix M = [A | b]
// Matrix is stored as an array of arrays: M[row][col].
// set augmented matrix to zero
Circuit
.
prototype
.
initialize_linear_system
=
function
()
{
for
(
var
i
=
this
.
N
-
1
;
i
>=
0
;
--
i
)
{
var
row
=
this
.
matrix
[
i
];
for
(
var
j
=
this
.
N
;
j
>=
0
;
--
j
)
// N+1 entries
row
[
j
]
=
0
;
}
}
// Allocate an NxM matrix
Circuit
.
prototype
.
make_mat
=
function
(
N
,
M
)
{
var
mat
=
new
Array
(
N
);
...
...
@@ -528,55 +659,62 @@ cktsim = (function() {
var
m
=
M
[
0
].
length
;
if
(
n
!=
b
.
length
||
m
!=
x
.
length
)
{
throw
'Rows of M mismatched to b or cols mismatch to x.'
;
}
for
(
var
i
=
0
;
i
<
n
;
i
++
)
{
throw
'Rows of M mismatched to b or cols mismatch to x.'
;
for
(
var
i
=
0
;
i
<
n
;
i
++
)
{
var
temp
=
0
;
for
(
var
j
=
0
;
j
<
m
;
j
++
)
{
temp
+=
M
[
i
][
j
]
*
x
[
j
];
}
for
(
var
j
=
0
;
j
<
m
;
j
++
)
temp
+=
M
[
i
][
j
]
*
x
[
j
];
b
[
i
]
=
scale
*
temp
;
// Recall the neg in the name
}
}
//
Form C = A + scale*B
Circuit
.
prototype
.
mat_scale_add
=
function
(
A
,
B
,
scale
,
C
)
{
//
C = scalea*A + scaleb*B, scalea, scaleb eithers numbers or arrays (row scaling)
Circuit
.
prototype
.
mat_scale_add
=
function
(
A
,
B
,
scale
a
,
scaleb
,
C
)
{
var
n
=
A
.
length
;
var
m
=
A
[
0
].
length
;
if
(
n
>
B
.
length
||
m
>
B
[
0
].
length
)
{
throw
'Row or columns of A to large for B'
;
}
throw
'Row or columns of A to large for B'
;
if
(
n
>
C
.
length
||
m
>
C
[
0
].
length
)
{
throw
'Row or columns of A to large for C'
;
}
for
(
var
i
=
0
;
i
<
n
;
i
++
)
{
for
(
var
j
=
0
;
j
<
m
;
j
++
)
{
C
[
i
][
j
]
=
A
[
i
][
j
]
+
scale
*
B
[
i
][
j
];
}
}
throw
'Row or columns of A to large for C'
;
if
((
typeof
scalea
==
'number'
)
&&
(
typeof
scaleb
==
'number'
))
for
(
var
i
=
0
;
i
<
n
;
i
++
)
for
(
var
j
=
0
;
j
<
m
;
j
++
)
C
[
i
][
j
]
=
scalea
*
A
[
i
][
j
]
+
scaleb
*
B
[
i
][
j
];
else
if
((
typeof
scaleb
==
'number'
)
&&
(
scalea
instanceof
Array
))
for
(
var
i
=
0
;
i
<
n
;
i
++
)
for
(
var
j
=
0
;
j
<
m
;
j
++
)
C
[
i
][
j
]
=
scalea
[
i
]
*
A
[
i
][
j
]
+
scaleb
*
B
[
i
][
j
];
else
if
((
typeof
scaleb
instanceof
Array
)
&&
(
scalea
instanceof
Array
))
for
(
var
i
=
0
;
i
<
n
;
i
++
)
for
(
var
j
=
0
;
j
<
m
;
j
++
)
C
[
i
][
j
]
=
scalea
[
i
]
*
A
[
i
][
j
]
+
scaleb
[
i
]
*
B
[
i
][
j
];
else
throw
'scalea and scaleb must be scalars or Arrays'
;
}
// Returns a vector of ones and zeros, ones denote zero rows in M
Circuit
.
prototype
.
zero_row
=
function
(
M
)
{
var
N
=
M
.
length
var
one_if_zero
=
new
Array
(
N
);
for
(
var
i
=
N
-
1
;
i
>=
0
;
i
--
)
if
((
Math
.
max
.
apply
(
Math
,
M
[
i
])
==
0
)
&&
(
Math
.
min
.
apply
(
Math
,
M
[
i
])
==
0
))
one_if_zero
[
i
]
=
1.0
;
else
one_if_zero
[
i
]
=
0.0
;
return
one_if_zero
;
}
// Copy A -> using the bounds of A
Circuit
.
prototype
.
copy_mat
=
function
(
src
,
dest
)
{
var
n
=
src
.
length
;
var
m
=
src
[
0
].
length
;
if
(
n
>
dest
.
length
||
m
>
dest
[
0
].
length
)
{
throw
'Rows or cols > rows or cols of dest'
;
}
throw
'Rows or cols > rows or cols of dest'
;
for
(
var
i
=
0
;
i
<
n
;
i
++
)
{
for
(
var
j
=
0
;
j
<
m
;
j
++
)
{
dest
[
i
][
j
]
=
src
[
i
][
j
];
}
}
}
// add val component between two nodes to matrix M
...
...
@@ -645,9 +783,8 @@ cktsim = (function() {
// Copy the rhs in to the last column of M if one is given.
if
(
rhs
!=
null
)
{
for
(
var
row
=
0
;
row
<
N
;
row
++
)
{
for
(
var
row
=
0
;
row
<
N
;
row
++
)
M
[
row
][
N
]
=
rhs
[
row
];
}
}
// gaussian elimination
...
...
@@ -891,11 +1028,8 @@ cktsim = (function() {
//
///////////////////////////////////////////////////////////////////////////////
// argument is a string describing the source's value:
// <value> or dc(<value>) -- constant value
// pulse(<vinit>,<vpulse>,<tdelay>,<trise>,<tfall>,<t_width>,<t_period>)
// sin(<voffset>,<vamplitude>,<hz>,<tdelay>,<phase_offset_degrees>)
// pwl(<time>,<value>,...) -- piecewise linear: time,value pairs
// argument is a string describing the source's value (see comments for details)
// source types: dc,step,square,triangle,sin,pulse,pwl,pwlr
// returns an object with the following attributes:
// value(t) -- compute source value at time t
...
...
@@ -935,17 +1069,59 @@ cktsim = (function() {
}
// post-processing for constant sources
// dc(v)
if
(
src
.
fun
==
'dc'
)
{
var
value
=
src
.
args
[
0
];
if
(
value
===
undefined
)
value
=
0
;
src
.
value
=
function
(
t
)
{
return
value
;
}
// closure
}
// post-processing for step sources
// step(v_init,v_plateau,t_delay,t_rise,t_fall)
else
if
(
src
.
fun
==
'step'
)
{
var
v1
=
arg_value
(
src
.
args
,
0
,
0
);
// default init value: 0V
var
v2
=
arg_value
(
src
.
args
,
1
,
1
);
// default plateau value: 1V
var
td
=
Math
.
max
(
0
,
arg_value
(
src
.
args
,
2
,
0
));
// time step starts
var
tr
=
Math
.
abs
(
arg_value
(
src
.
args
,
3
,
1
e
-
9
));
// default rise time: 1ns
pwl_source
(
src
,[
td
,
v1
,
td
+
tr
,
v2
],
false
);
}
// post-processing for square wave
// square(v_init,v_plateau,t_period)
else
if
(
src
.
fun
==
'square'
)
{
var
v1
=
arg_value
(
src
.
args
,
0
,
0
);
// default init value: 0V
var
v2
=
arg_value
(
src
.
args
,
1
,
1
);
// default plateau value: 1V
var
freq
=
Math
.
abs
(
arg_value
(
src
.
args
,
2
,
1
));
// default frequency: 1s
var
per
=
freq
==
0
?
Infinity
:
1
/
freq
;
var
t_change
=
0.01
*
per
;
// rise and fall time
var
t_pw
=
0.49
*
per
;
// half the cycle minus rise and fall time
pwl_source
(
src
,[
0
,
v1
,
t_change
,
v2
,
t_change
+
t_pw
,
v2
,
t_change
+
t_pw
+
t_change
,
v1
,
per
,
v1
],
true
);
}
// post-processing for triangle
// triangle(v_init,v_plateua,t_period)
else
if
(
src
.
fun
==
'triangle'
)
{
var
v1
=
arg_value
(
src
.
args
,
0
,
0
);
// default init value: 0V
var
v2
=
arg_value
(
src
.
args
,
1
,
1
);
// default plateau value: 1V
var
freq
=
Math
.
abs
(
arg_value
(
src
.
args
,
2
,
1
));
// default frequency: 1s
var
per
=
freq
==
0
?
Infinity
:
1
/
freq
;
pwl_source
(
src
,[
0
,
v1
,
per
/
2
,
v2
,
per
,
v1
],
true
);
}
// post-processing for pwl and pwlr sources
// pwl[r](t1,v1,t2,v2,...)
else
if
(
src
.
fun
==
'pwl'
||
src
.
fun
==
'pwlr'
)
{
pwl_source
(
src
,
src
.
args
,
src
.
fun
==
'pwlr'
);
}
// post-processing for pulsed sources
// pulse(v_init,v_plateau,t_delay,t_rise,t_fall,t_width,t_period)
else
if
(
src
.
fun
==
'pulse'
)
{
var
v1
=
arg_value
(
src
.
args
,
0
,
0
);
// default init value: 0V
var
v2
=
arg_value
(
src
.
args
,
1
,
1
);
// default plateau value: 1V
var
td
=
Math
.
m
in
(
0
,
arg_value
(
src
.
args
,
2
,
0
));
// time pulse starts
var
td
=
Math
.
m
ax
(
0
,
arg_value
(
src
.
args
,
2
,
0
));
// time pulse starts
var
tr
=
Math
.
abs
(
arg_value
(
src
.
args
,
3
,
1
e
-
9
));
// default rise time: 1ns
var
tf
=
Math
.
abs
(
arg_value
(
src
.
args
,
4
,
1
e
-
9
));
// default rise time: 1ns
...
...
@@ -957,35 +1133,16 @@ cktsim = (function() {
var
t3
=
t2
+
pw
;
// time when v2 -> v1 transition starts
var
t4
=
t3
+
tf
;
// time when v2 -> v1 transition ends
// return value of source at time t
src
.
value
=
function
(
t
)
{
// closure
var
tmod
=
Math
.
fmod
(
t
,
per
);
if
(
tmod
<
t1
)
return
v1
;
else
if
(
tmod
<
t2
)
return
v1
+
(
v2
-
v1
)
*
(
tmod
-
t1
)
/
(
t2
-
t1
);
else
if
(
tmod
<
t3
)
return
v2
;
else
if
(
tmod
<
t4
)
return
v2
+
(
v1
-
v2
)
*
(
tmod
-
t3
)
/
(
t4
-
t3
);
else
return
v1
;
}
// return time of next inflection point after time t
src
.
inflection_point
=
function
(
t
)
{
// closure
var
tstart
=
per
*
Math
.
floor
(
t
/
per
);
var
tmod
=
t
-
tstart
;
if
(
tmod
<
t1
)
return
tstart
+
t1
;
else
if
(
t
<
t2
)
return
tstart
+
t2
;
else
if
(
t
<
t3
)
return
tstart
+
t3
;
else
if
(
t
<
t4
)
return
tstart
+
t4
;
else
return
tstart
+
per
+
t1
;
}
pwl_source
(
src
,[
t1
,
v1
,
t2
,
v2
,
t3
,
v2
,
t4
,
v1
,
per
,
v1
],
true
);
}
// post-processing for sinusoidal sources
// sin(v_offset,v_amplitude,freq_hz,t_delay,phase_offset_degrees)
else
if
(
src
.
fun
==
'sin'
)
{
var
degrees_to_radians
=
2
*
Math
.
PI
/
360.0
;
var
voffset
=
arg_value
(
src
.
args
,
0
,
0
);
// default offset voltage: 0V
var
va
=
arg_value
(
src
.
args
,
1
,
1
);
// default amplitude: -1V to 1V
var
freq
=
arg_value
(
src
.
args
,
2
,
1
);
// default frequency: 1Hz
var
td
=
Math
.
m
in
(
0
,
arg_value
(
src
.
args
,
3
,
0
));
// default time delay: 0sec
var
freq
=
Math
.
abs
(
arg_value
(
src
.
args
,
2
,
1
)
);
// default frequency: 1Hz
var
td
=
Math
.
m
ax
(
0
,
arg_value
(
src
.
args
,
3
,
0
));
// default time delay: 0sec
var
phase
=
arg_value
(
src
.
args
,
4
,
0
);
// default phase offset: 0 degrees
phase
/=
360.0
;
...
...
@@ -994,8 +1151,8 @@ cktsim = (function() {
src
.
value
=
function
(
t
)
{
// closure
if
(
t
<
td
)
return
voffset
+
va
*
Math
.
sin
(
2
*
Math
.
PI
*
phase
);
else
{
t
-=
td
;
return
v
offset
+
va
*
Math
.
sin
(
2
*
Math
.
PI
*
(
freq
*
(
t
-
td
)
+
phase
))
;
var
val
=
voffset
+
va
*
Math
.
sin
(
2
*
Math
.
PI
*
(
freq
*
(
t
-
td
)
+
phase
))
;
return
v
al
;
}
}
...
...
@@ -1014,6 +1171,48 @@ cktsim = (function() {
return
src
;
}
function
pwl_source
(
src
,
tv_pairs
,
repeat
)
{
var
nvals
=
tv_pairs
.
length
;
if
(
nvals
%
2
==
1
)
npts
-=
1
;
// make sure it's even!
if
(
nvals
<=
2
)
{
// handle degenerate case
src
.
value
=
function
(
t
)
{
return
nvals
==
2
?
tv_pairs
[
1
]
:
0
;
}
src
.
inflection_point
=
function
(
t
)
{
return
undefined
;
}
}
else
{
src
.
value
=
function
(
t
)
{
// closure
if
(
repeat
)
// make time periodic if values are to be repeated
t
=
Math
.
fmod
(
t
,
tv_pairs
[
nvals
-
2
]);
var
last_t
=
tv_pairs
[
0
];
var
last_v
=
tv_pairs
[
1
];
if
(
t
>
last_t
)
{
var
next_t
,
next_v
;
for
(
var
i
=
2
;
i
<
nvals
;
i
+=
2
)
{
next_t
=
tv_pairs
[
i
];
next_v
=
tv_pairs
[
i
+
1
];
if
(
next_t
>
last_t
)
// defend against bogus tv pairs
if
(
t
<
next_t
)
return
last_v
+
(
next_v
-
last_v
)
*
(
t
-
last_t
)
/
(
next_t
-
last_t
);
last_t
=
next_t
;
last_v
=
next_v
;
}
}
return
last_v
;
}
src
.
inflection_point
=
function
(
t
)
{
// closure
if
(
repeat
)
// make time periodic if values are to be repeated
t
=
Math
.
fmod
(
t
,
tv_pairs
[
nvals
-
2
]);
for
(
var
i
=
0
;
i
<
nvals
;
i
+=
2
)
{
var
next_t
=
tv_pairs
[
i
];
if
(
t
<
next_t
)
return
next_t
;
}
return
undefined
;
}
}
}
// helper function: return args[index] if present, else default_v
function
arg_value
(
args
,
index
,
default_v
)
{
if
(
index
<
args
.
length
)
{
...
...
js/schematic.js
View file @
4f54c4b2
...
...
@@ -89,6 +89,10 @@ schematic = (function() {
's'
:
[
Probe
,
'Scope Probe'
],
};
// global clipboard
if
(
typeof
sch_clipboard
==
'undefined'
)
sch_clipboard
=
[];
///////////////////////////////////////////////////////////////////////////////
//
// Schematic = diagram + parts bin + status area
...
...
@@ -104,7 +108,6 @@ schematic = (function() {
if
(
this
.
origin_x
==
undefined
)
this
.
origin_x
=
0
;
this
.
origin_y
=
input
.
getAttribute
(
"origin_y"
);
if
(
this
.
origin_y
==
undefined
)
this
.
origin_y
=
0
;
this
.
clipboard
=
undefined
;
// use user-supplied list of parts if supplied
// else just populate parts bin with all the parts
...
...
@@ -257,6 +260,7 @@ schematic = (function() {
tr
=
document
.
createElement
(
'tr'
);
table
.
appendChild
(
tr
);
td
=
document
.
createElement
(
'td'
);
td
.
style
.
verticalAlign
=
'top'
;
td
.
colSpan
=
2
;
tr
.
appendChild
(
td
);
for
(
var
i
=
0
;
i
<
this
.
toolbar
.
length
;
++
i
)
{
...
...
@@ -267,12 +271,12 @@ schematic = (function() {
// add canvas and parts bin to DOM
tr
=
document
.
createElement
(
'tr'
);
tr
.
vAlign
=
'top'
;
table
.
appendChild
(
tr
);
td
=
document
.
createElement
(
'td'
);
tr
.
appendChild
(
td
);
td
.
appendChild
(
this
.
canvas
);
td
=
document
.
createElement
(
'td'
);
td
.
style
.
verticalAlign
=
'top'
;
tr
.
appendChild
(
td
);
var
parts_table
=
document
.
createElement
(
'table'
);
td
.
appendChild
(
parts_table
);
...
...
@@ -306,7 +310,8 @@ schematic = (function() {
this
.
input
.
parentNode
.
insertBefore
(
table
,
this
.
input
.
nextSibling
);
// process initial contents of diagram
this
.
load_schematic
(
this
.
input
.
value
);
this
.
load_schematic
(
this
.
input
.
getAttribute
(
'value'
),
this
.
input
.
getAttribute
(
'initial_value'
));
}
part_w
=
42
;
// size of a parts bin compartment
...
...
@@ -421,14 +426,14 @@ schematic = (function() {
Schematic
.
prototype
.
cut
=
function
()
{
// clear previous contents
this
.
clipboard
=
[];
sch_
clipboard
=
[];
// look for selected components, move them to clipboard.
for
(
var
i
=
this
.
components
.
length
-
1
;
i
>=
0
;
--
i
)
{
var
c
=
this
.
components
[
i
];
if
(
c
.
selected
)
{
c
.
delete
();
this
.
clipboard
.
push
(
c
);
sch_
clipboard
.
push
(
c
);
}
}
...
...
@@ -438,13 +443,13 @@ schematic = (function() {
Schematic
.
prototype
.
copy
=
function
()
{
// clear previous contents
this
.
clipboard
=
[];
sch_
clipboard
=
[];
// look for selected components, copy them to clipboard.
for
(
var
i
=
this
.
components
.
length
-
1
;
i
>=
0
;
--
i
)
{
var
c
=
this
.
components
[
i
];
if
(
c
.
selected
)
this
.
clipboard
.
push
(
c
.
clone
(
c
.
x
,
c
.
y
));
sch_
clipboard
.
push
(
c
.
clone
(
c
.
x
,
c
.
y
));
}
}
...
...
@@ -453,8 +458,8 @@ schematic = (function() {
// components in the clipboard
var
left
=
undefined
;
var
top
=
undefined
;
for
(
var
i
=
this
.
clipboard
.
length
-
1
;
i
>=
0
;
--
i
)
{
var
c
=
this
.
clipboard
[
i
];
for
(
var
i
=
sch_
clipboard
.
length
-
1
;
i
>=
0
;
--
i
)
{
var
c
=
sch_
clipboard
[
i
];
left
=
left
?
Math
.
min
(
left
,
c
.
x
)
:
c
.
x
;
top
=
top
?
Math
.
min
(
top
,
c
.
y
)
:
c
.
y
;
}
...
...
@@ -467,8 +472,8 @@ schematic = (function() {
// make clones of components on the clipboard, positioning
// them relative to the cursor
for
(
var
i
=
this
.
clipboard
.
length
-
1
;
i
>=
0
;
--
i
)
{
var
c
=
this
.
clipboard
[
i
];
for
(
var
i
=
sch_
clipboard
.
length
-
1
;
i
>=
0
;
--
i
)
{
var
c
=
sch_
clipboard
[
i
];
var
new_c
=
c
.
clone
(
this
.
cursor_x
+
(
c
.
x
-
left
),
this
.
cursor_y
+
(
c
.
y
-
top
));
new_c
.
set_select
(
true
);
new_c
.
add
(
this
);
...
...
@@ -485,8 +490,12 @@ schematic = (function() {
////////////////////////////////////////////////////////////////////////////////
// load diagram from JSON representation
Schematic
.
prototype
.
load_schematic
=
function
(
value
)
{
if
(
value
)
{
Schematic
.
prototype
.
load_schematic
=
function
(
value
,
initial_value
)
{
// use default value if no schematic info in value
if
(
value
==
undefined
||
value
.
indexOf
(
'['
)
==
-
1
)
value
=
initial_value
;
if
(
value
&&
value
.
indexOf
(
'['
)
!=
-
1
)
{
// convert string value into data structure
var
json
=
JSON
.
parse
(
value
);
...
...
@@ -507,7 +516,9 @@ schematic = (function() {
}
else
if
(
c
[
0
]
==
'w'
)
{
// wire
this
.
add_wire
(
c
[
1
][
0
],
c
[
1
][
1
],
c
[
1
][
2
],
c
[
1
][
3
]);
}
else
if
(
c
[
0
]
==
'dc'
||
c
[
0
]
==
'ac'
||
c
[
0
]
==
'transient'
)
{
}
else
if
(
c
[
0
]
==
'dc'
)
{
this
.
dc_results
=
c
[
1
];
}
else
if
(
c
[
0
]
==
'ac'
||
c
[
0
]
==
'transient'
)
{
// ignore analysis results
}
else
{
// ordinary component
...
...
@@ -767,9 +778,17 @@ schematic = (function() {
sch
.
tran_npts
=
content
.
fields
[
npts_lbl
].
value
;
sch
.
tran_tstop
=
content
.
fields
[
tstop_lbl
].
value
;
// gather a list of nodes that are being probed. These
// will be added to the list of nodes checked during the
// LTE calculations in transient analysis
var
probe_list
=
sch
.
find_probes
();
var
probe_names
=
new
Array
(
probe_list
.
length
);
for
(
var
i
=
probe_list
.
length
-
1
;
i
>=
0
;
--
i
)
probe_names
[
i
]
=
probe_list
[
i
][
1
];
// run the analysis
var
results
=
ckt
.
tran
(
ckt
.
parse_number
(
sch
.
tran_npts
),
0
,
ckt
.
parse_number
(
sch
.
tran_tstop
),
false
);
ckt
.
parse_number
(
sch
.
tran_tstop
),
probe_names
,
false
);
// save a copy of the results for submission
this
.
transient_results
=
{};
...
...
@@ -871,7 +890,7 @@ schematic = (function() {
}
this
.
enable_tool
(
'cut'
,
selections
);
this
.
enable_tool
(
'copy'
,
selections
);
this
.
enable_tool
(
'paste'
,
this
.
clipboard
);
this
.
enable_tool
(
'paste'
,
sch_clipboard
.
length
>
0
);
// connection points: draw one at each location
for
(
var
location
in
this
.
connection_points
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment