-
Notifications
You must be signed in to change notification settings - Fork 1
/
CircuitS.jl
402 lines (337 loc) · 17.4 KB
/
CircuitS.jl
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
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
using Symbolics
@enum ELEM_TYPE Resistor=1 Inductor Capacitor Voltage Current Impedance Admitance OpAmp ABCDElem VCVS VCCS CCVS CCCS IdealT InductiveT TransmissionLine
mutable struct Circuit
elements::Array{Any}
num_nodes::Int
eq_current::Array{Any}
eq_current_mult::Array{Any}
eq_potential::Array{Any}
var_voltage::Array{Any}
var_current::Array{Any}
var_element::Dict
replacements::Dict
sym_s::Any
time::Bool
omega::String
end
@doc """
circuit = create_circuit()
Creates and returns an empty circuit object.
""" ->
function create_circuit()
"""Creates and returns a default circuit object"""
circuit::Circuit = Circuit([], 0, [], [], [], [], [], Dict(), Dict(), 0, false, "")
return circuit
end
@doc """
add_element(elem::Vector, circuit::Circuit)
Adds an element to the circuit.
# Arguments
- `elem::Vector`: Element to be added. Elements are given in specific formats:
- [type, id, a, b]
- [type, id, a, b, IC]
- [type, id, [a1,a2], b]
- [type, id, [a1,a2], [b1,b2]]
- [type, id, [a1,a2], [b1,b2], IC]
Details of each element are found in the docs.
- `circuit::Circuit`: the circuit object to which the element will be added to
# Examples
```julia-repl
add_element([Resistor, "R1", 1, 0], circuit)
add_element([Voltage, "V1", 1, 0], circuit)
```
""" ->
function add_element(elem, circuit::Circuit)
"""Adds 'elem' defined in format [TYPE, parms ...] to the circuit"""
push!(circuit.elements, elem)
end
@doc """
remove_element(elem::String, circuit::Circuit)
Removes an element with given id `elem` from the circuit.
""" ->
function remove_element(elem::String, circuit::Circuit)
circuit.elements = [ circuit.elements[i] for i in 1:length(circuit.elements) if circuit.elements[i][2] != elem ]
end
@doc """
init_circuit(circuit::Circuit, replacements::Dict, omega::String="")
Prepares the circuit for simulation. Circuits have to be initialized before the simulation every time a new element is added.
# Arguments
- `circuit::Circuit`: A circuit to be initialized
- `replacements::Dict`: Dictionary of replacements in the circuit, given in format: `Dict([ R1 => R, R2 => R, ...])`
- `R1`, `R2` and `R` are symbols
- `omega::String`: A replacement for `s=j*omega`
!!! note "Note"
If given, `omega` has to be a single symbol, given as a string. It can be replaced by a complex term in `replacements::Dict`.
# Examples
```julia-repl
@variables R1, R2, R
init_circuit(circuit, Dict([ R1 => R, R2 => R]);
```
""" ->
function init_circuit(circuit::Circuit, replacements::Dict = Dict(), omega::String="")
"""Prepares the circuit for simulation"""
# set replacements for simulate function
circuit.replacements = replacements
# calculate number of nodes
circuit.num_nodes = 0
for elem in circuit.elements
if (!(elem[4] isa Number))
circuit.num_nodes = max(circuit.num_nodes, elem[3][1], elem[3][2], elem[4][1], elem[4][2])
elseif (!(elem[3] isa Number))
circuit.num_nodes = max(circuit.num_nodes, elem[3][1], elem[3][2], elem[4])
else
circuit.num_nodes = max(circuit.num_nodes, elem[3], elem[4])
end
end
circuit.num_nodes += 1
# Set base equation for KZS and node potential
circuit.eq_current = [0 for item in 1:circuit.num_nodes]
circuit.eq_current_mult = [1 for item in 1:circuit.num_nodes]
circuit.eq_potential = [Symbolics.variables("V"*string(item) )[1] for item in 0:circuit.num_nodes-1]
circuit.eq_potential[1] = 0
circuit.var_voltage = []
circuit.var_current = []
# Create element symbols
for elem in circuit.elements
circuit.var_element[elem[2]] = Symbolics.variables(elem[2])[1]
end
# check for time domain
if (omega != "")
circuit.time = true
end
circuit.omega = omega
circuit.sym_s = Symbolics.variables("s")[1]
# calculate equations from elements
for elem in circuit.elements
elemType::ELEM_TYPE = elem[1]
if (elemType == Resistor || elemType == Impedance)
circuit.eq_current_mult[ elem[3] + 1 ] *= circuit.var_element[elem[2]]
circuit.eq_current_mult[ elem[4] + 1 ] *= circuit.var_element[elem[2]]
circuit.eq_current[ elem[3] + 1 ] += (circuit.eq_potential[elem[3]+1] - circuit.eq_potential[elem[4]+1]) / circuit.var_element[elem[2]]
circuit.eq_current[ elem[4] + 1 ] += (circuit.eq_potential[elem[4]+1] - circuit.eq_potential[elem[3]+1]) / circuit.var_element[elem[2]]
elseif (elemType == Admitance)
circuit.eq_current[ elem[3] + 1 ] += (circuit.eq_potential[elem[3]+1] - circuit.eq_potential[elem[4]+1]) * circuit.var_element[elem[2]]
circuit.eq_current[ elem[4] + 1 ] += (circuit.eq_potential[elem[4]+1] - circuit.eq_potential[elem[3]+1]) * circuit.var_element[elem[2]]
elseif (elemType == Inductor)
I0 = if (length(elem) == 5 && !circuit.time) Symbolics.variables(elem[5])[1] else 0 end
circuit.eq_current_mult[ elem[3] + 1 ] *= (circuit.sym_s * circuit.var_element[elem[2]])
circuit.eq_current_mult[ elem[4] + 1 ] *= (circuit.sym_s * circuit.var_element[elem[2]])
circuit.eq_current[ elem[3] + 1 ] += (circuit.eq_potential[elem[3]+1] - circuit.eq_potential[elem[4]+1]) / (circuit.sym_s * circuit.var_element[elem[2]]) + (I0/circuit.sym_s)
circuit.eq_current[ elem[4] + 1 ] += (circuit.eq_potential[elem[4]+1] - circuit.eq_potential[elem[3]+1]) / (circuit.sym_s * circuit.var_element[elem[2]]) - (I0/circuit.sym_s)
elseif (elemType == Capacitor)
U0 = if (length(elem) == 5 && !circuit.time) Symbolics.variables(elem[5])[1] else 0 end
circuit.eq_current[ elem[3] + 1 ] += (circuit.eq_potential[elem[3]+1] - circuit.eq_potential[elem[4]+1]) * circuit.sym_s * circuit.var_element[elem[2]] - U0*circuit.var_element[elem[2]]
circuit.eq_current[ elem[4] + 1 ] += (circuit.eq_potential[elem[4]+1] - circuit.eq_potential[elem[3]+1]) * circuit.sym_s * circuit.var_element[elem[2]] + U0*circuit.var_element[elem[2]]
elseif (elemType == Voltage)
branch_current = Symbolics.variables("I_"*elem[2])[1]
push!(circuit.var_current, branch_current)
push!(circuit.var_voltage, circuit.eq_potential[elem[3]+1] - circuit.eq_potential[elem[4]+1] - circuit.var_element[elem[2]])
circuit.eq_current[elem[3]+1] += branch_current
circuit.eq_current[elem[4]+1] -= branch_current
elseif (elemType == Current)
circuit.eq_current[elem[3]+1] += circuit.var_element[elem[2]]
circuit.eq_current[elem[4]+1] -= circuit.var_element[elem[2]]
elseif (elemType == OpAmp)
branch_current = Symbolics.variables("I_"*elem[2])[1]
push!(circuit.var_current, branch_current)
push!(circuit.var_voltage, (circuit.eq_potential[elem[3][1]+1] - circuit.eq_potential[elem[3][2]+1]) )
circuit.eq_current[elem[4]+1] += branch_current
elseif (elemType == ABCDElem)
a11, a12, a21, a22 = Symbolics.variables(elem[5][1])[1], Symbolics.variables(elem[5][2])[1], Symbolics.variables(elem[5][3])[1], Symbolics.variables(elem[5][4])[1]
I_A, I_B = Symbolics.variables("I_"*elem[2]*"_"*string(elem[3][1]))[1], Symbolics.variables("I_"*elem[2]*"_"*string(elem[4][1]))[1]
circuit.eq_current[elem[3][1]+1] += I_A
circuit.eq_current[elem[3][2]+1] -= I_A
circuit.eq_current[elem[4][1]+1] -= I_B
circuit.eq_current[elem[4][2]+1] += I_B
push!(circuit.var_voltage, circuit.eq_potential[elem[3][1]+1] - circuit.eq_potential[elem[4][1]+1] - (a11 * circuit.eq_potential[elem[4][1]+1] - circuit.eq_potential[elem[4][2]+1] + a12*I_B ) )
push!(circuit.var_voltage, I_A - (a21*circuit.eq_potential[elem[4][1]+1] - circuit.eq_potential[elem[4][2]+1] + a22*I_B ) )
push!(circuit.var_current, I_A)
push!(circuit.var_current, I_B)
elseif (elemType == VCVS)
amp = Symbolics.variables(elem[5])[1]
I = Symbolics.variables("I_"*elem[2])[1]
circuit.eq_current[elem[4][1]+1] += I
circuit.eq_current[elem[4][2]+1] -= I
push!(circuit.var_voltage, circuit.eq_potential[elem[4][1]+1] - circuit.eq_potential[elem[4][2]+1] - amp* (circuit.eq_potential[elem[3][1]+1] - elem[3][2]+1) )
push!(circuit.var_current, I)
elseif (elemType == VCCS)
trans = Symbolics.variables(elem[5])[1]
circuit.eq_current[elem[4][1]+1] += trans*(circuit.eq_potential[elem[3][1]+1] - circuit.eq_potential[elem[3][2]+1])
circuit.eq_current[elem[4][2]+1] -= trans*(circuit.eq_potential[elem[3][1]+1] - circuit.eq_potential[elem[3][2]+1])
elseif (elemType == CCCS)
amp = Symbolics.variables(elem[5])[1]
I = Symbolics.variables("I_"*elem[2])[1]
circuit.eq_current[elem[3][1]+1] += I
circuit.eq_current[elem[3][2]+1] -= I
circuit.eq_current[elem[4][1]+1] += amp*I
circuit.eq_current[elem[4][2]+1] -= amp*I
push!(circuit.var_voltage, circuit.eq_potential[elem[3][1]+1] - circuit.eq_potential[elem[3][2]+1])
push!(circuit.var_current, I)
elseif (elemType == CCVS)
trans = Symbolics.variables(elem[5])[1]
I = Symbolics.variables("I_"*elem[2])[1]
circuit.eq_current_mult[elem[3][1]+1] *= trans
circuit.eq_current_mult[elem[3][2]+1] *= trans
circuit.eq_current[elem[3][1]+1] += (circuit.eq_potential[elem[4][1]+1] - circuit.eq_potential[elem[4][2]+1])/trans
circuit.eq_current[elem[3][2]+1] -= (circuit.eq_potential[elem[4][1]+1] - circuit.eq_potential[elem[4][2]+1])/trans
circuit.eq_current[elem[4][1]+1] += I
circuit.eq_current[elem[4][2]+1] -= I
push!(circuit.var_voltage, circuit.eq_potential[elem[3][1]+1] - circuit.eq_potential[elem[3][2]+1])
push!(circuit.var_current, I)
elseif (elemType == IdealT)
I = Symbolics.variables("I_"*elem[2])[1]
m = Symbolics.variables(elem[5])[1]
#TODO: struja transformatora ide u pogresnim smerovima? (2 linije ispod)
circuit.eq_current[elem[3][1]+1] += I
circuit.eq_current[elem[3][2]+1] -= I
circuit.eq_current[elem[4][1]+1] += -m * I
circuit.eq_current[elem[4][2]+1] -= -m * I
eq = circuit.eq_potential[elem[3][1]+1] - circuit.eq_potential[elem[3][2]+1] - m * (circuit.eq_potential[elem[4][1]+1] - circuit.eq_potential[elem[4][2]+1])
push!(circuit.var_voltage, eq)
push!(circuit.var_current, I)
elseif (elemType == InductiveT)
L1 = Symbolics.variables(elem[5][1])[1]
L2 = Symbolics.variables(elem[5][2])[1]
L12 = Symbolics.variables(elem[5][3])[1]
I01 = 0
I02 = 0
if length(elem) == 6 && !circuit.time
if elem[6][1] != 0
I01 = Symbolics.variables(elem[6][1])[1]
end
if elem[6][2] != 0
I02 = Symbolics.variables(elem[6][2])[1]
end
end
IK_A = Symbolics.variables("I_" * elem[2] * "_" * string(elem[3][1]))[1]
IK_B = Symbolics.variables("I_" * elem[2] * "_" * string(elem[4][1]))[1]
circuit.eq_current[elem[3][1]+1] += IK_A
circuit.eq_current[elem[3][2]+1] -= IK_A
circuit.eq_current[elem[4][1]+1] += IK_B
circuit.eq_current[elem[4][2]+1] -= IK_B
push!(circuit.var_voltage, circuit.eq_potential[elem[3][1]+1] - circuit.eq_potential[elem[3][2]+1] - ( L1 * circuit.sym_s * IK_A - L1 * I01 +
L12 * circuit.sym_s * IK_B - L12 * I02 ))
push!(circuit.var_voltage, circuit.eq_potential[elem[4][1]+1] - circuit.eq_potential[elem[4][2]+1] - ( L12 * circuit.sym_s * IK_A - L12 * I01 +
L2 * circuit.sym_s * IK_B - L2 * I02 ) )
push!(circuit.var_current, IK_A)
push!(circuit.var_current, IK_B)
elseif (elemType == TransmissionLine)
#TODO: Zasto Zc i tau NE treba da budu simboli?
Zc = Symbolics.variables(elem[5][1])[1] #elem[5][1]
tau = Symbolics.variables(elem[5][2])[1] #elem[5][2]
IA_A = Symbolics.variables("I_" * elem[2] * "_" * string(elem[3][1]))[1]
IA_B = Symbolics.variables("I_" * elem[2] * "_" * string(elem[4][1]))[1]
if (!circuit.time)
circuit.eq_current[elem[3][1]+1] += IA_A
circuit.eq_current[elem[3][2]+1] -= IA_A
circuit.eq_current[elem[4][1]+1] += IA_B
circuit.eq_current[elem[4][2]+1] -= IA_B
push!(circuit.var_voltage, circuit.eq_potential[elem[3][1]+1] - circuit.eq_potential[elem[3][2]+1] - (Zc*IA_A + Zc*IA_B*exp(-tau*circuit.sym_s) + (circuit.eq_potential[elem[4][1]+1] - circuit.eq_potential[elem[3][1]+1])*exp(-tau*circuit.sym_s)))
push!(circuit.var_voltage, circuit.eq_potential[elem[4][1]+1] - circuit.eq_potential[elem[4][2]+1] - (Zc*IA_B + Zc*IA_A*exp(-tau*circuit.sym_s) + (circuit.eq_potential[elem[3][1]+1] - circuit.eq_potential[elem[3][2]+1])*exp(-tau*circuit.sym_s)))
else
theta = tau
circuit.eq_current[elem[3][1]+1] += IA_A
circuit.eq_current[elem[3][2]+1] -= IA_A
circuit.eq_current[elem[4][1]+1] -= IA_B
circuit.eq_current[elem[4][2]+1] += IA_B
push!(circuit.var_voltage, circuit.eq_potential[elem[3][1]+1] - circuit.eq_potential[elem[3][2]+1] - (cos(theta)*(circuit.eq_potential[elem[4][1]+1] - circuit.eq_potential[elem[4][2]+1]) + im*Zc*sin(theta)*IA_B))
push!(circuit.var_voltage, IA_A - (im*(1/Zc)*sin(theta)*(circuit.eq_potential[elem[4][1]+1] - circuit.eq_potential[elem[4][2]+1]) + cos(theta)*IA_B))
end
push!(circuit.var_current, IA_A)
push!(circuit.var_current, IA_B)
end
end
end
@doc """
get_equations(circuit::Circuit)
Returns a list of MNA equations with applied replacements.
# Arguments
- `circuit::Circuit`: Initialized circuit
# Returns
`result::Vector`: A list of all equations
# Examples
```julia-repl
result = get_equations(circuit);
```
""" ->
function get_equations(circuit::Circuit)
equations::Array{Num} = circuit.eq_current[2:length(circuit.eq_current)]
equations = vcat(equations, circuit.var_voltage)
for i in 1:length(equations)
if (circuit.omega != "")
omega = Symbolics.variables(circuit.omega)[1]
equations[i] = substitute(equations[i], Dict([circuit.sym_s=>im*omega]))
end
equations[i] = substitute(equations[i], circuit.replacements)
end
return equations
end
@doc """
get_variables(circuit::Circuit)
Returns a list of MNA variables - symbols for node potentials and some currents.
# Arguments
- `circuit::Circuit`: Initialized circuit
# Returns
`result::Vector`: A list of all variables
# Examples
```julia-repl
result = get_variables(circuit);
```
""" ->
function get_variables(circuit::Circuit)
variables::Array{Num} = circuit.eq_potential[2:length(circuit.eq_potential)]
variables = vcat(variables, circuit.var_current)
return variables
end
@doc """
simulate(circuit::Circuit, simpl::Bool = true)
Simulates the circuit by calculating node potentials and some currents (MNA).
# Arguments
- `circuit::Circuit`: A circuit to be simulated
- `simpl::Bool = true`: Flag indicating whether to simplify the resulting equations
# Returns
`result::Dict`: A dictionary of all calculated potentials and currents
# Examples
```julia-repl
result = simulate(circuit);
```
""" ->
function simulate(circuit::Circuit, simpl::Bool = true)
"""Simulates the circuit by calculating all potentials and currents"""
for i in 1:length(circuit.eq_current)
circuit.eq_current[i] *= circuit.eq_current_mult[i]
circuit.eq_current[i] = simplify(circuit.eq_current[i], expand=true)
circuit.eq_current[i] = substitute(circuit.eq_current[i], circuit.replacements)
end
for i in 1:length(circuit.var_voltage)
circuit.var_voltage[i] = substitute(circuit.var_voltage[i], circuit.replacements)
#circuit.var_voltage[i] = simplify(circuit.var_voltage[i], expand=true)
end
# equations to solve
equations::Array{Num} = circuit.eq_current[2:length(circuit.eq_current)]
equations = vcat(equations, circuit.var_voltage)
tmp = [];
for i in 1:length(equations)
if (string(equations[i]) != "0")
push!(tmp, equations[i])
end
end
equations = tmp
# variables to solve for
variables::Array{Num} = circuit.eq_potential[2:length(circuit.eq_potential)]
variables = vcat(variables, circuit.var_current)
variables = [ variables[i] for i in 1:length(variables) if ( occursin(string(variables[i]), string(equations))) ]
#println(equations)
#println(variables)
result = Symbolics.solve_for(equations, variables, simplify=simpl)
result_map = Dict();
for i in 1:length(variables)
if (circuit.omega != "")
omega = Symbolics.variables(circuit.omega)[1]
result[i] = substitute(result[i], Dict([circuit.sym_s=>im*omega]))
result[i] = substitute(result[i], circuit.replacements)
end
result_map[string(variables[i])] = result[i]
end
return result_map
end