Skip to content

Commit

Permalink
Merge branch 'master' into CircuitPerformance
Browse files Browse the repository at this point in the history
  • Loading branch information
Asachoo committed Dec 27, 2024
2 parents a17bf0f + b4d4236 commit 73a1e30
Show file tree
Hide file tree
Showing 20 changed files with 1,075 additions and 216 deletions.
35 changes: 35 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# This workflows will upload a Python Package using uv when a release is created
# reference:
# https://github.com/astral-sh/trusted-publishing-examples/blob/main/.github/workflows/release.yml

name: Publish on PyPI

on:
release:
types:
- created

jobs:
build_and_deploy:

runs-on: ubuntu-latest

permissions:
# IMPORTANT: this permission is mandatory for trusted publishing
id-token: write

steps:
- name: Checkout Project
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v3

- name: Set Up Python
run: uv python install

- name: Build Package
run: uv build

- name: Publish to PyPi
run: uv publish --trusted-publishing always
2 changes: 1 addition & 1 deletion conda.recipe/meta.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% set name = "scikit-rf" %}
{% set version = "1.3.0" %}
{% set version = "1.5.0" %}
{% set hash_val = "8223204281599ba14b685d7e28b7e361" %}

package:
Expand Down
2 changes: 1 addition & 1 deletion doc/source/contributing/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ To run all the tests (except the virtual instruments)
cd scikit-rf
# Create environment and install dependencies, needed only once.
python -m venv .venv
python -e[test,visa,netw,xlsx,plot,docs,testspice]
pip install -e .[test,visa,netw,xlsx,plot,docs,testspice] --compile
# Activate Environment, needed for all following steps.
# on Linux Systems
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ select = ["B", "E", "F", "FA", "I", "NPY", "UP", "W"]

# Ignore some rules for all files
# E741: ambiguous-variable-name
ignore = ["E741"]
# UP031: Use format specifiers instead of percent format
ignore = ["E741", "UP031"]

[tool.ruff.lint.per-file-ignores]
# Ignore some rules for some specific files
Expand Down
2 changes: 1 addition & 1 deletion skrf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
implemented in Python.
"""

__version__ = '1.3.0'
__version__ = '1.5.0'
## Import all module names for coherent reference of name-space
#import io

Expand Down
2 changes: 1 addition & 1 deletion skrf/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
S_DEF_HFSS_DEFAULT = 'traveling'

FrequencyUnitT = Literal["Hz", "kHz", "MHz", "GHz", "THz"]
FREQ_UNITS = {"Hz": 1.0, "kHz": 1e3, "MHz": 1e6, "GHz": 1e9, "THz": 1e12}
FREQ_UNITS: dict[FrequencyUnitT, float] = {"Hz": 1.0, "kHz": 1e3, "MHz": 1e6, "GHz": 1e9, "THz": 1e12}

SweepTypeT = Literal["lin", "log"]
CoordT = Literal["cart", "polar"]
Expand Down
51 changes: 26 additions & 25 deletions skrf/io/csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,23 @@
"""
from __future__ import annotations

import os
from warnings import warn

import numpy as np

from .. import mathFunctions as mf
from .. import util
from ..constants import FREQ_UNITS, FrequencyUnitT
from ..frequency import Frequency
from ..network import Network

# delayed imports
# from pandas import Series, Index, DataFrame

def read_pna_csv(filename, *args, **kwargs):
def read_pna_csv(filename, *args, **kwargs) -> tuple[str, str, np.ndarray]:
r"""
Reads data from a csv file written by an Agilient PNA.
Expand Down Expand Up @@ -134,6 +137,17 @@ def read_pna_csv(filename, *args, **kwargs):
# pna uses unicode coding for degree symbol, but we dont need that
header = header.replace('\xb0','deg').rstrip('\n').rstrip('\r')

units_dict: dict[str, FrequencyUnitT] = {k.lower(): k for k in FREQ_UNITS.keys()}

# Get the frequency unit from the header and convert to Hz
unit_raw = header.split(',')[0].strip('Freq')[1:-1]
try:
unit_tmp = unit_raw.lower()
if unit_tmp in units_dict:
data[:, 0] *= FREQ_UNITS[units_dict[unit_tmp]]
except Exception as exc:
raise ValueError(f"Could not parse frequency unit '{unit_raw}'") from exc

return header, comments, data

def pna_csv_2_df(filename):
Expand Down Expand Up @@ -166,7 +180,7 @@ def pna_csv_2_ntwks2(filename, *args, **kwargs):
header, comments, d = read_pna_csv(filename)
ntwk_dict = {}
param_set=set([k[:3] for k in df.columns])
f = df.index.values*1e-9
f = df.index.values
for param in param_set:
try:
s = mf.dbdeg_2_reim(
Expand All @@ -179,7 +193,7 @@ def pna_csv_2_ntwks2(filename, *args, **kwargs):
df[f'{param} (IMAG)'].values,
)

ntwk_dict[param] = Network(f=f, s=s, name=param, comments=comments)
ntwk_dict[param] = Network(f=f, s=s, name=param, comments=comments, f_unit='Hz')


try:
Expand Down Expand Up @@ -216,8 +230,8 @@ def pna_csv_2_ntwks3(filename):

# set impedance to 50 Ohm (doesn't matter for now)
z0 = np.ones(np.shape(d)[0])*50
# read f values, convert to GHz
f = d[:,0]/1e9
# read f values
f = d[:,0]

name = os.path.splitext(os.path.basename(filename))[0]

Expand All @@ -236,7 +250,7 @@ def pna_csv_2_ntwks3(filename):
elif 's22' in h.lower() and 'db' in h.lower():
s[:,1,1] = mf.dbdeg_2_reim(d[:,k+1], d[:,k+2])

n = Network(f=f,s=s,z0=z0, name = name)
n = Network(f=f,s=s,z0=z0, name = name, f_unit="Hz")
return n

else:
Expand Down Expand Up @@ -619,16 +633,16 @@ def pna_csv_2_ntwks(filename):

if (d.shape[1]-1)/2 == 0 :
# this isnt complex data
f = d[:,0]*1e-9
f = d[:,0]
if 'db' in header.lower():
s = mf.db_2_mag(d[:,1])
else:
raise (NotImplementedError)
name = os.path.splitext(os.path.basename(filename))[0]
return Network(f=f, s=s, name=name, comments=comments)
return Network(f=f, s=s, name=name, comments=comments, f_unit='Hz')
else:
for k in range(int((d.shape[1]-1)/2)):
f = d[:,0]*1e-9
f = d[:,0]
name = names[k]
print((names[k], names[k+1]))
if 'db' in names[k].lower() and 'deg' in names[k+1].lower():
Expand All @@ -640,23 +654,17 @@ def pna_csv_2_ntwks(filename):
s = d[:,k*2+1]+1j*d[:,k*2+2]

ntwk_list.append(
Network(f=f, s=s, name=name, comments=comments)
Network(f=f, s=s, name=name, comments=comments, f_unit='Hz')
)

return ntwk_list

def pna_csv_2_freq(filename):
warn("deprecated", DeprecationWarning, stacklevel=2)
header, comments, d = read_pna_csv(filename)
#try to pull out frequency unit
cols = pna_csv_header_split(filename)
try:
f_unit = cols[0].split('(')[1].split(')')[0]
except Exception:
f_unit = 'hz'

f = d[:,0]
return Frequency.from_f(f, unit = f_unit)
return Frequency.from_f(f, unit = "Hz")


def pna_csv_2_scalar_ntwks(filename, *args, **kwargs):
Expand All @@ -683,15 +691,8 @@ def pna_csv_2_scalar_ntwks(filename, *args, **kwargs):

cols = pna_csv_header_split(filename)


#try to pull out frequency unit
try:
f_unit = cols[0].split('(')[1].split(')')[0]
except Exception:
f_unit = 'hz'

f = d[:,0]
freq = Frequency.from_f(f, unit = f_unit)
freq = Frequency.from_f(f, unit = 'Hz')

# loop through columns and create a single network for each column
ntwk_list = []
Expand Down
19 changes: 17 additions & 2 deletions skrf/io/tests/test_csv.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import unittest
import warnings

import numpy as np
import pytest
Expand All @@ -19,8 +20,8 @@ def setUp(self):
name for pna_csv_reim.csv file, then reads it in.
"""
self.test_dir = os.path.dirname(os.path.abspath(__file__))+'/'
filename = os.path.join(self.test_dir, 'pna_csv_reim.csv')
self.acsv = rf.AgilentCSV(filename)
self.filename = os.path.join(self.test_dir, 'pna_csv_reim.csv')
self.acsv = rf.AgilentCSV(self.filename)

def test_columns(self):
"""
Expand Down Expand Up @@ -64,3 +65,17 @@ def test_scalar_networks(self):
This only tests for execution, not accuracy
"""
a = self.acsv.scalar_networks

def test_read_pna_csv(self):
"""
This tests reading of a csv file written by an Agilient PNA.
"""
with warnings.catch_warnings(record=True) as w:
with self.assertWarns(DeprecationWarning):
header, comment, data = rf.read_pna_csv(self.filename)

self.assertEqual(header, 'Freq(Hz),"A,1"(REAL),"A,1"(IMAG),"R1,1"(REAL),"R1,1"(IMAG)')
self.assertEqual(comment, 'this is a comment\nline\n')
self.assertTrue((data == np.array([[750000000000, 1, 2, 3, 4],
[1100000000000, 5, 6, 7,8],
])).all())
10 changes: 7 additions & 3 deletions skrf/io/touchstone.py
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ def load_file(self, fid: typing.TextIO):
if state.matrix_format == "full":
self.s[:] = s_flat
else:
index = np.tril_indices(state.rank) if state.matrix_format == "lower" else np.triu_indices(self.rank)
index = np.tril_indices(state.rank) if state.matrix_format == "lower" else np.triu_indices(state.rank)
index_flat = np.ravel_multi_index(index, (state.rank, state.rank))
self.s[:, index_flat] = s_flat

Expand All @@ -546,8 +546,12 @@ def load_file(self, fid: typing.TextIO):
else:
self.s = self.s.reshape((-1, state.rank, state.rank))

if state.matrix_format != "full":
self.s = np.nanmax((self.s, self.s.transpose(0, 2, 1)), axis=0)
if state.matrix_format == "upper":
index_lower = np.tril_indices(state.rank)
self.s[(...,*index_lower)] = self.s.transpose(0, 2, 1)[(...,*index_lower)]
elif state.matrix_format == "lower":
index_upper = np.triu_indices(state.rank)
self.s[(...,*index_upper)] = self.s.transpose(0, 2, 1)[(...,*index_upper)]

self.port_modes = np.array(["S"] * state.rank)
if state.mixed_mode_order:
Expand Down
59 changes: 52 additions & 7 deletions skrf/media/definedAEpTandZ0.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
from __future__ import annotations

import warnings
from typing import Sequence

from numpy import imag, log, ones, pi, real, sqrt
from numpy import imag, log, ndarray, ones, pi, real, sqrt
from scipy.constants import c

from ..constants import NumberLike
Expand All @@ -32,7 +33,7 @@ class DefinedAEpTandZ0(Media):
Transmission line medium defined by A, Ep, Tand and Z0.
This medium is defined by attenuation `A`, relative permittivity `Ep_r`,
loss angle `tand` and characteristic impedance `Z0`.
loss angle `tand` and nominal impedance `Z0`.
Djirdjevic [#Djordjevic]_ / Svennson [#Svensson]_ dispersion model
is provided for dielectric. Default behaviour is frequency invariant.
Expand All @@ -51,7 +52,7 @@ class DefinedAEpTandZ0(Media):
(Default is None)
A : number, array-like, default 0.0
Attenuation due to conductor loss in dB/m/sqrt(Hz)
The attenuation :math:`A(f)`at frequency :math:`f` is:
The attenuation :math:`A(f)` at frequency :math:`f` is:
.. math::
Expand Down Expand Up @@ -86,14 +87,43 @@ class DefinedAEpTandZ0(Media):
tanD : number, array-like, default 0.0
Dielectric relative permittivity loss tangent :math:`\tan\delta`. See `ep_r`.
z0 : number, array-like, default 50.0
Quasi-static characteristic impedance of the medium.
Quasi-static nominal impedance of the medium.
Because of the dispersion introduced by the conductor and dielectric
losses, the characteristic impedance can be frequency-dependent with an
imaginary part. The characteristic impedance is computed with an RLGC
model.
If the impedance parameter is array-like, the characteristic impedance
is assigned to this value without modification.
.. math::
R = 2 Z_n \alpha_{conductor}
.. math::
L = \frac{Z_n \sqrt{\epsilon_r}}{C_0}
.. math::
G = \frac{2}{Z_n} \alpha_{dielectric}
.. math::
C = \frac{\sqrt{\epsilon_r}}{C_0 Z_n}
.. math::
Z_0 = \sqrt{\frac{R + j\omega L}{G + j\omega C}}
where :math:`Z_n` is the nominal impedance and :math:`Z_0` is the
characteristic impedance.
Z0 : number, array-like, or None
deprecated parameter, only emmit a deprecation warning.
f_low : number, default 1e3, optional
Low frequency in Hz for for Djirdjevic/Svennson dispersion model.
Low frequency in Hz for for Djordjevic/Svennson dispersion model.
See `ep_r`.
f_high : number, default 1e12, optional
High frequency in Hz for for Djirdjevic/Svennson dispersion model.
High frequency in Hz for for Djordjevic/Svennson dispersion model.
See `ep_r`.
f_ep : number, default 1e9, , optional
Specification frequency in Hz for for Djirdjevic/Svennson dispersion model.
Expand Down Expand Up @@ -139,10 +169,25 @@ def __init__(self, frequency: Frequency| None = None,
Media.__init__(self, frequency=frequency, z0_port=z0_port)
self.A, self.f_A = A, f_A
self.ep_r, self.tanD = ep_r, tanD
self.z0_characteristic = z0

self.f_low, self.f_high, self.f_ep = f_low, f_high, f_ep
self.model = model

if isinstance(z0, (Sequence, ndarray)):
# keep raw impedance for characteristic impedance
self.z0_characteristic = z0
else:
# z0 is the nominal impedance. Compute characteristic impedance with
# an RLGC model based on alpha conductor and alpha dielectric.
self.Zn = z0
self.R = 2 * self.Zn * self.alpha_conductor
self.L = self.Zn * sqrt(self.ep_r) / c
self.G = 2 / self.Zn * self.alpha_dielectric
self.C = sqrt(ep_r) / c / self.Zn
self.z0_characteristic = sqrt(
(self.R + 1j * self.frequency.w * self.L) /
(self.G + 1j * self.frequency.w * self.C))

if Z0 is not None:
# warns of deprecation
warnings.warn(
Expand Down
Loading

0 comments on commit 73a1e30

Please sign in to comment.