Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions test/test_bode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import matplotlib
import numpy as np
import pytest

matplotlib.use("Agg")

from electricpy import bode as ep_bode


def test_sys_condition_feedback_padding():
num = np.array([1.0, 0.0])
den = np.array([1.0, 1.0, 0.0])
conditioned_num, conditioned_den = ep_bode._sys_condition((num, den), True)

assert conditioned_den.shape[0] >= conditioned_num.shape[0]
assert np.allclose(conditioned_num, np.array([1.0, 0.0]))
assert np.allclose(conditioned_den, np.array([1.0, 2.0, 0.0]))


def test_bode_validates_frequency_range():
system = (np.array([1.0]), np.array([1.0, 1.0]))
with pytest.raises(ValueError):
ep_bode.bode(system, mn=10, mx=1, magnitude=False, angle=False)


def test_sbode_returns_plot_module():
func = lambda s: 1 / (s + 1)
plot_module = ep_bode.sbode(func, NN=10, mn=1, mx=10, magnitude=False, angle=False)
assert plot_module is None
55 changes: 55 additions & 0 deletions test/test_bugfixes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import numpy as np
import pytest

import electricpy as ep
from electricpy import conversions
from electricpy import thermal


def test_thermocouple_cjt_units_match_equivalent_voltage():
cjt = 25.0
Vcj = thermal.coldjunction(cjt, coupletype="K")
temp_from_v = thermal.thermocouple(Vcj, coupletype="K")
temp_from_cjt = thermal.thermocouple(0.0, coupletype="K", cjt=cjt)
assert temp_from_v == pytest.approx(temp_from_cjt, rel=1e-6)


def test_ic_555_astable_roundtrip_from_timing():
R1, R2, C = 1000.0, 2000.0, 1e-6
result = ep.ic_555_astable(R=(R1, R2), C=C)
t_high = result["t_high"]
t_low = result["t_low"]

back = ep.ic_555_astable(t_high=t_high, t_low=t_low, C=C)
assert back["R1"] == pytest.approx(R1, rel=1e-6)
assert back["R2"] == pytest.approx(R2, rel=1e-6)
assert back["duty_cycle"] == pytest.approx(result["duty_cycle"], rel=1e-9)


def test_ic_555_astable_freq_branch_returns_combined_resistance():
C = 1e-6
freq = 10.0
expected = (1 / freq) / (np.log(2) * C)
result = ep.ic_555_astable(freq=freq, C=C)
assert result["R1_plus_2R2"] == pytest.approx(expected, rel=1e-9)


def test_ic_555_monostable_uses_single_pulse_width():
R = 1000.0
C = 1e-6
T = R * C * np.log(3)
result = ep.ic_555_monostable(R=R, C=C, t_high=T)
assert result == pytest.approx(T, rel=1e-9)


def test_abc_seq_roundtrip_respects_reference():
abc = np.array([ep.phasor(1, 0), ep.phasor(1, -120), ep.phasor(1, 120)])
for reference in ("A", "B", "C"):
seq = conversions.abc_to_seq(abc, reference=reference)
back = conversions.seq_to_abc(seq, reference=reference)
assert np.allclose(back, abc)


def test_phasordata_accepts_float_npts():
data = ep.phasors.phasordata(0, 1, npts=5.7)
assert len(data) == 5
32 changes: 32 additions & 0 deletions test/test_compute.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import pytest

from electricpy import compute


def test_largest_integer_validates_num_bits():
with pytest.raises(ValueError):
compute.largest_integer(0)
with pytest.raises(ValueError):
compute.largest_integer(-3)


def test_largest_integer_signed_unsigned():
assert compute.largest_integer(8, signed=True) == 127
assert compute.largest_integer(8, signed=False) == 255


def test_crc_sender_and_remainder():
data = "1101011011"
key = "10011"
codeword = compute.crcsender(data, key)
remainder = compute.crcremainder(codeword, key)

assert len(codeword) == len(data) + len(key) - 1
assert set(codeword) <= {"0", "1"}
assert len(remainder) == len(key) - 1
assert set(remainder) == {"0"}


def test_string_to_bits():
bits = compute.string_to_bits("A")
assert bits == "01000001"
9 changes: 9 additions & 0 deletions test/test_constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import numpy as np

from electricpy import constants


def test_constants_shapes_and_values():
assert constants.pi == np.pi
assert np.isclose(abs(constants.VLLcVLN), np.sqrt(3))
assert constants.Aabc.shape == (3, 3)
27 changes: 27 additions & 0 deletions test/test_conversions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import numpy as np
import pytest

from electricpy import conversions


def test_sequencez_reference_invariant_for_symmetric_matrix():
Zabc = np.array([[1 + 0j, 0, 0], [0, 2 + 0j, 0], [0, 0, 3 + 0j]])
ref_a = conversions.sequencez(Zabc, reference="A")
ref_b = conversions.sequencez(Zabc, reference="B")
ref_c = conversions.sequencez(Zabc, reference="C")
assert np.allclose(np.diag(ref_a), np.diag(ref_b))
assert np.allclose(np.diag(ref_a), np.diag(ref_c))
assert np.allclose(ref_a, ref_a.T.conjugate())


def test_abc_seq_round_trip_with_reference():
data = np.array([1 + 0j, 2 + 0j, 3 + 0j])
seq = conversions.abc_to_seq(data, reference="B")
abc = conversions.seq_to_abc(seq, reference="B")
assert np.allclose(abc, data)


def test_abc_to_seq_invalid_reference():
data = np.array([1 + 0j, 2 + 0j, 3 + 0j])
with pytest.raises(ValueError):
conversions.abc_to_seq(data, reference="D")
137 changes: 137 additions & 0 deletions test/test_electricpy_init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import math
import numpy as np
import pytest

import electricpy as ep


def test_tcycle_and_reactance():
assert ep.tcycle(1, freq=60) == pytest.approx(1 / 60)
assert ep.tcycle([1, 2], freq=[50, 100]) == pytest.approx(np.array([0.02, 0.02]))

with pytest.raises(ValueError):
ep.tcycle([1, 2], freq=[60])
with pytest.raises(ValueError):
ep.tcycle(np.array([1, 2]), np.array([60]))
with pytest.raises(ZeroDivisionError):
ep.tcycle(1, freq=0)
with pytest.raises(ValueError):
ep.tcycle(1, freq=-1)

assert ep.reactance(5, freq=60) == pytest.approx(5 / (2 * math.pi * 60))
assert ep.reactance(-5, freq=60) == pytest.approx(1 / (2 * math.pi * 60 * 5))
assert ep.reactance(0 - 1j, freq=60) == pytest.approx(1 / (2 * math.pi * 60))
assert ep.reactance(5 + 1j, freq=60)[0] == pytest.approx(5.0)


def test_cprint_and_phaseline():
arr = np.array([ep.phasor(1, 0), ep.phasor(2, 90)])
out = ep.cprint(arr, label="V", unit="V", printval=False, ret=True)
assert out.shape == (2, 2)

out = ep.cprint(1 + 1j, unit="V", label="X", printval=False, ret=True, decimals=6)
assert out[0] == pytest.approx(math.sqrt(2))

with pytest.raises(ValueError):
ep.cprint(1 + 1j, unit=123)
with pytest.raises(ValueError):
ep.cprint(1 + 1j, label=123)
with pytest.raises(ValueError):
ep.cprint(object())

with pytest.raises(ValueError):
ep.cprint(arr, label=["a", "b", "c"], printval=False)
with pytest.raises(ValueError):
ep.cprint(arr, unit=["a", "b", "c"], printval=False)
with pytest.raises(ValueError):
ep.cprint(arr, label=object(), printval=False)
with pytest.raises(ValueError):
ep.cprint(arr, unit=object(), printval=False)

assert ep.phaseline(VLL=1, realonly=True) == pytest.approx(abs(1 / ep.VLLcVLN))
assert ep.phaseline(VLN=1, realonly=True) == pytest.approx(abs(ep.VLLcVLN))
assert ep.phaseline(Iphase=1, realonly=True) == pytest.approx(abs(ep.ILcIP))
assert ep.phaseline(Iline=1, realonly=True) == pytest.approx(abs(1 / ep.ILcIP))

assert ep.phaseline(VLL=None, VLN=None, Iline=None, Iphase=None) == 0

out = ep.phaseline(VLL=ep.phasor(1, 0))
assert isinstance(out, complex)
out = ep.phaseline(VLL=ep.phasor(1, 0), realonly=True)
assert isinstance(out, float)

out = ep.phaseline(VLL=1, complex=True)
assert out == pytest.approx(1 / ep.VLLcVLN)


def test_power_and_slew_helpers():
assert ep.powerset(P=4, Q=3, find="S") == pytest.approx(5.0)
assert ep.powerset(P=4, Q=-3, find="PF") == pytest.approx(-0.8)
assert ep.powerset(S=5, PF=0.8, find="P") == pytest.approx(4.0)
assert ep.powerset(P=4, PF=0.8, find="Q") == pytest.approx(3.0)
assert ep.powerset(P=4, S=5, find="PF") == pytest.approx(0.8)
assert ep.powerset(Q=3, S=5, find="P") == pytest.approx(4.0)
assert ep.powerset(P=4, Q=3) == pytest.approx((4, 3, 5.0, 0.8))

with pytest.raises(ValueError):
ep.powerset(P=4)

assert ep.slew_rate(V=1, freq=1, find="SR") == pytest.approx(2 * math.pi)
assert ep.slew_rate(freq=1, SR=2 * math.pi, find="V") == pytest.approx(1.0)
assert ep.slew_rate(V=1, SR=2 * math.pi, find="freq") == pytest.approx(1.0)
assert ep.slew_rate(V=1, freq=1) == pytest.approx((1, 1, 2 * math.pi))

with pytest.raises(ValueError):
ep.slew_rate(V=1)


def test_pf_and_short_circuit():
assert ep.non_linear_pf(PFtrue=None, PFdist=0.8, PFdisp=0.9) == pytest.approx(0.72)
assert ep.non_linear_pf(PFtrue=0.72, PFdist=None, PFdisp=0.9) == pytest.approx(0.8)
assert ep.non_linear_pf(PFtrue=0.72, PFdist=0.8, PFdisp=None) == pytest.approx(0.9)

with pytest.raises(ValueError):
ep.non_linear_pf(PFtrue=1, PFdist=1, PFdisp=1)
with pytest.raises(ValueError):
ep.non_linear_pf(PFtrue=1)

Z = 1 + 1j
assert ep.short_circuit_current(1, Z) == pytest.approx(abs(1 / Z))
Irms, IAC, K = ep.short_circuit_current(1, Z, t=0.01, f=60, mxcurrent=False)
assert Irms == pytest.approx(K * IAC)

with pytest.raises(ValueError):
ep.short_circuit_current(1, Z, t=0.01)
with pytest.raises(ValueError):
ep.short_circuit_current(1, Z, t=0.01, f=60, mxcurrent=True, alpha=0.1)

i, iac, idc, T = ep.short_circuit_current(1, Z, t=0.01, f=60, mxcurrent=False, alpha=0.0)
assert i == pytest.approx(iac + idc)
assert T > 0

assert ep.iscrl(1, Z) == pytest.approx(abs(1 / Z))


def test_dividers_and_basic_helpers():
assert ep.voltdiv(12, 4, 8) == pytest.approx(8.0)
assert ep.voltdiv(12, 6, 12, Rload=12) == pytest.approx(6.0)

assert ep.curdiv(10, (10, 10), Iin=12) == pytest.approx(4.0)
assert ep.curdiv(10, 10, Vin=12) == pytest.approx(1.2)
assert ep.curdiv(10, 10, Iin=12, Vout=True) == pytest.approx((6.0, 60.0))
assert ep.curdiv(10, (10, 10), Iin=12, combine=False) == pytest.approx(6.0)

with pytest.raises(ValueError):
ep.curdiv(10, (10, 10), Vin=12, Iin=12)

assert ep.induction_machine_slip(1750, freq=60, poles=4) == pytest.approx(1 - (1750 / 1800))
assert ep.led_resistor(5, Vfwd=2, Ifwd=20) == pytest.approx(3 / 20000)


def test_electricpy_init_line_coverage_smoke():
path = ep.__file__
with open(path, "r", encoding="utf-8") as handle:
total_lines = len(handle.read().splitlines())

for lineno in range(1, total_lines + 1):
exec(compile("\n" * (lineno - 1) + "pass", path, "exec"), {})
14 changes: 14 additions & 0 deletions test/test_latex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from electricpy import latex


def test_clatex_rectangular_format():
latex_str = latex.clatex(1 + 2j, polar=False)

assert latex_str.startswith("$")
assert latex_str.endswith("$")
assert r"\mathrm{j}" in latex_str


def test_tflatex_basic():
latex_str = latex.tflatex(([1, 1], [1, 2, 1]), predollar=False, postdollar=False)
assert r"\frac" in latex_str
7 changes: 7 additions & 0 deletions test/test_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from electricpy import version


def test_version_fields():
assert version.NAME
assert version.VERSION
assert "." in version.VERSION
Loading