<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Voltages and Currents in Circuits
The scikit-rf Circuit object allows one to deduce the voltages and currents at all the intersections of the circuit for a given power (and phase) excitation at circuit's ports. Here, few examples are examined, in order to clarify the conventions used for current directions.
Step1:  A simple transmission line
To start, let's assume a simple transmission line excited by a generator. Let's assume the generator is matched to the line ($Z_s=Z_0$) and the line connected to a matched load ($Z_0=Z_L$), all 50 Ohm. 
<img src="circuit_vi_simple_line.svg">
What is the RF currents and voltages at the input and output of this line for a given input power (and phase)?
Step2:  Assuming the source generates an input power of $P_f$ with a phase $\phi$, with such a line the voltage and current at the entrance of the line are
Step3:  The voltage and current evolve along the transmission line. The voltage and current at the output of the line can be calculated using the transmission line tools provided with scikit-rf
Step4:  Let's perform the same calculation using Circuit. First, one needs to define the circuit, that is to create input/output ports and to connect these ports to the transmission line Network we've already created. Then, we can build the circuit
Step5:  It's always a good practice to check if the circuit's graph is as expected. In this case, the graph is pretty simple
Step6:  Circuit provides two methods to determine voltages and currents at the circuit input/output ports (also known as "external ports"). These methods take as input the power and phase inputs at each ports
Step7:  The results are similar to the previous, except the sign of the current at port2 which is reversed. 
This is normal, as the currents_external() method defines the positive direction of a current as the direction which "enters" into the circuit. More details about this convention at the bottom of this example.
A coaxial T with different characteristic impedances
As a more advanced example, we've built in a full-wave software (here ANSYS HFSS, but other are fine too) the following structure
Step8:  In a Circuit simulator
The voltages and currents can also be derived using a Circuit simulator, like for example
Step9:  With Circuit
Now let's build the same circuit with scikit-rf Circuit
Step10:  The voltages and currents at the ports for the given excitation is
Step11:  These results matches well the one given by the full-wave calculations, hourra. 
external vs internal ports?
You have maybe noticed in the Circuit documentation that we often talk about "internal" or "inner" ports, and of "external" or "outer" ports. The external ports corresponds to the Circuit.Port() Networks defined when building a Circuit. The internal ports are all the other connexions inside the Circuit
The Circuit algorithm allows one to have access to the voltages and current at the internal connexions inside the circuit. In the previous examples, there is not too much internal ports as we've connected a N-port directly to external ports. However, in more complex usages we can have a lot (look to the other Circuit examples).
In Circuit, voltages and currents are peak values. While voltages are defined in a non-ambiguous manner, positive currents can be defined in a way or another, leading to a 180 degree ambiguity. To solve this, we have chosen the following definition
<ASSISTANT_TASK:>
Python Code:
%matplotlib inline
# standard imports
import numpy as np
import matplotlib.pyplot as plt
import skrf as rf
rf.stylely()
Explanation: Voltages and Currents in Circuits
The scikit-rf Circuit object allows one to deduce the voltages and currents at all the intersections of the circuit for a given power (and phase) excitation at circuit's ports. Here, few examples are examined, in order to clarify the conventions used for current directions.
End of explanation
P_f = 1  # forward power in Watt
Z = 50  # source internal impedance, line characteristic impedance and load impedance
L = 10  # line length in [m]
freq = rf.Frequency(2, 2, 1, unit='GHz')
line_media = rf.media.DefinedGammaZ0(freq, z0=Z)  # lossless line medium
line = line_media.line(d=L, unit='m', name='line')  # transmission line Network
Explanation: A simple transmission line
To start, let's assume a simple transmission line excited by a generator. Let's assume the generator is matched to the line ($Z_s=Z_0$) and the line connected to a matched load ($Z_0=Z_L$), all 50 Ohm. 
<img src="circuit_vi_simple_line.svg">
What is the RF currents and voltages at the input and output of this line for a given input power (and phase)?
End of explanation
V_in = np.sqrt(2*Z*P_f)
I_in = np.sqrt(2*P_f/Z)
print(f'Input voltage and current: {V_in} V and {I_in} A')
Explanation: Assuming the source generates an input power of $P_f$ with a phase $\phi$, with such a line the voltage and current at the entrance of the line are:
$$
V_{in} = \sqrt{2 Z_0 P_f} e^{j \phi}
$$
$$
I_{in} = \sqrt{2 \frac{P_f}{Z_0}} e^{j \phi}
$$
End of explanation
theta = rf.theta(line_media.gamma, freq.f, L)  # electrical length 
V_out, I_out = rf.tlineFunctions.voltage_current_propagation(V_in, I_in, Z, theta)
print(f'Output voltage and current: {V_out} V and {I_out} A')
Explanation: The voltage and current evolve along the transmission line. The voltage and current at the output of the line can be calculated using the transmission line tools provided with scikit-rf:
End of explanation
port1 = rf.Circuit.Port(frequency=freq, name='port1', z0=50)
port2 = rf.Circuit.Port(frequency=freq, name='port2', z0=50)
cnx = [
    [(port1, 0), (line, 0)],
    [(port2, 0), (line, 1)]
]
crt = rf.Circuit(cnx)
Explanation: Let's perform the same calculation using Circuit. First, one needs to define the circuit, that is to create input/output ports and to connect these ports to the transmission line Network we've already created. Then, we can build the circuit:
End of explanation
crt.plot_graph(network_labels=True, edge_labels=True, inter_labels=True)
Explanation: It's always a good practice to check if the circuit's graph is as expected. In this case, the graph is pretty simple: 2 ports connected to a 2-ports Network.
End of explanation
power = [1, 0]  # 1 Watt at port1 and 0 at port2
phase = [0, 0]  # 0 radians
V_at_ports = crt.voltages_external(power, phase)
print(V_at_ports)
I_at_ports = crt.currents_external(power, phase)
print(I_at_ports)
Explanation: Circuit provides two methods to determine voltages and currents at the circuit input/output ports (also known as "external ports"). These methods take as input the power and phase inputs at each ports:
End of explanation
import pandas as pd  # convenient to read .csv files
pd.read_csv('circuit_vi_HFSS_Voltages.csv')
pd.read_csv('circuit_vi_HFSS_Currents.csv')
Explanation: The results are similar to the previous, except the sign of the current at port2 which is reversed. 
This is normal, as the currents_external() method defines the positive direction of a current as the direction which "enters" into the circuit. More details about this convention at the bottom of this example.
A coaxial T with different characteristic impedances
As a more advanced example, we've built in a full-wave software (here ANSYS HFSS, but other are fine too) the following structure: a coaxial T, defined with different coaxial cross-sections (and such different characteristic impedances). 
<img src="circuit_vi_HFSS_Coaxial_T.png" with="800">
All the three ports are excited with different power and phase to complicate a bit, as the following:
| port |  power [W] | phase [deg] |
| --- | --- | --- |
| #1 | 1 | -10 |
| #2 | 2 | -20 |
| #3 | 3 | +60 |
Full-wave reference solution
The voltages and currents are evaluated in the full-wave software directly. Voltage is obtained by integrating the E fields along a straight line going from the inner to the outer conductors in the port's cross-sections. Current is obtained by integrating the H fields along a circle enclosing the inner conductor in port's cross-sections. Note the circles are directed in order to define the positive current direction as the direction inward the ports (right-hand rule). 
The full-wave solutions are given here for reference, at 3 frequencies:
End of explanation
pd.read_csv('circuit_vi_Designer_Voltages.csv')
pd.read_csv('circuit_vi_Designer_Currents.csv')
Explanation: In a Circuit simulator
The voltages and currents can also be derived using a Circuit simulator, like for example:
<img src="circuit_vi_Designer_circuit.png" width="800">
(where, again, the current probes are oriented in such a way that the current is positive when flowing in the Network). This gives essentially the same results:
End of explanation
# Importing the 3-port .s3p file exported from full-wave simulation
coaxial_T = rf.Network('circuit_vi_Coaxial_T.s3p')
# pay attention to the port's characteristic impedance
# it should match the Network characteristic impedances otherwise this will generate mismatches 
port1 = rf.Circuit.Port(coaxial_T.frequency, 'port1', coaxial_T.z0[:,0])
port2 = rf.Circuit.Port(coaxial_T.frequency, 'port2', coaxial_T.z0[:,1])
port3 = rf.Circuit.Port(coaxial_T.frequency, 'port3', coaxial_T.z0[:,2])
# connexion list
cnx = [
    [(port1, 0), (coaxial_T, 0)],
    [(port2, 0), (coaxial_T, 1)],
    [(port3, 0), (coaxial_T, 2)]
]
# building the circuit
crt = rf.Circuit(cnx)
# let's check if our connexion list is correctly defined
crt.plot_graph(network_labels=True, edge_labels=True, inter_labels=True)
Explanation: With Circuit
Now let's build the same circuit with scikit-rf Circuit:
End of explanation
power = [1, 2, 3]  # input power in watts at ports 1, 2 and 3
phase = np.deg2rad([-10, -20, +60])  # input phase in rad at ports 1, 2 and 3
voltages = crt.voltages_external(power, phase)
currents = crt.currents_external(power, phase)
# just for a better rendering in the notebook 
pd.concat([
    pd.DataFrame(np.abs(voltages), columns=['mag V1', 'mag V2', 'mag V3'], index=crt.frequency.f/1e6),
    pd.DataFrame(np.angle(voltages, deg=True), columns=['Phase V1', 'Phase V2', 'Phase V3'], index=crt.frequency.f/1e6)
], axis=1)
# just for a better rendering in the notebook 
pd.concat([
    pd.DataFrame(np.abs(currents), columns=['mag I1', 'mag I2', 'mag I3'], index=crt.frequency.f/1e6),
    pd.DataFrame(np.angle(currents, deg=True), columns=['Phase I1', 'Phase I2', 'Phase I3'], index=crt.frequency.f/1e6)
], axis=1)
Explanation: The voltages and currents at the ports for the given excitation is:
End of explanation
# internals currents (currents at all connexions)
# in this example, there are 6 internal connexions (3 pairs of connexions)
crt.currents(power, phase)
# This gives the indices of the "external" ports
crt.port_indexes
# So we can keep only "external" ports
crt.currents(power, phase)[:, crt.port_indexes]
# note the sign difference due to the convention chosen for internal ports
crt.currents_external(power, phase)
Explanation: These results matches well the one given by the full-wave calculations, hourra. 
external vs internal ports?
You have maybe noticed in the Circuit documentation that we often talk about "internal" or "inner" ports, and of "external" or "outer" ports. The external ports corresponds to the Circuit.Port() Networks defined when building a Circuit. The internal ports are all the other connexions inside the Circuit
The Circuit algorithm allows one to have access to the voltages and current at the internal connexions inside the circuit. In the previous examples, there is not too much internal ports as we've connected a N-port directly to external ports. However, in more complex usages we can have a lot (look to the other Circuit examples).
In Circuit, voltages and currents are peak values. While voltages are defined in a non-ambiguous manner, positive currents can be defined in a way or another, leading to a 180 degree ambiguity. To solve this, we have chosen the following definition: internal currents are defined such as they are measured positive when flowing into Networks. 
Hence, you find that "external" current are sign opposite of "internal" ones at the corresponding ports, because the internal currents are actually flowing into the ports Networks.
End of explanation
<END_TASK>