Source code for microcalorimetry.math.vna
"""A collection of analysis functions commonly used with VNA measurements."""
import numpy as np
import xarray as xr
import microcalorimetry._gwex as dfm
import warnings
DataArray = xr.DataArray
[docs]
def calc_s1p_basis(*s1p_c):
"""
Generate basis frequency dependent basis vectors for the RI plane.
Returns a 2x2 matrix that when given to rotate_s1p basis will transform
them onto the new coordinate space in the imaginary plane.
The new basis vectors are given from SVD, and so the first one represents
the directo of change for the given sensors.
Parameters
----------
*s1p_c : TYPE
Sensors.
Returns
-------
new_basis : TYPE
new basis vectors.
"""
vals = [dfm.convert(dfm.s1p_ri, s).values for s in s1p_c]
# insert a new dimension to stack along so you have
# ...., frequency, sensor, (sreal,simag)
axis = vals[0].ndim - 1
vals = [np.expand_dims(v, axis) for v in vals]
M = np.concat(vals, axis=axis)
# perform a stacked SVD
U, S, Vh = np.linalg.svd(M)
# construct nwe basis matrix
# transpose along last 2 dimensions
# we can right multiply this
axis = [i for i in range(Vh.ndim)]
axis[-1], axis[-2] = axis[-2], axis[-1]
new_basis_vals = Vh.transpose(axis)
# put into xarray object
# use s2p as a template
with warnings.catch_warnings(action='ignore'):
new_basis = dfm.empty_like(s1p_c[0], dfm.s2p_c).astype(float)
new_basis.values = new_basis_vals
new_basis.attrs.pop('dataformat')
return new_basis
[docs]
def rotate_s1p_basis(
s1p_c: xr.DataArray, basis: xr.DataArray, return_as: dfm.DataFormat = dfm.s1p_c
):
"""
Rotate an s1p_c file to a new basis in the RlIm plane.
Parameters
----------
s1p_c : xr.DataArray
Complex s1p file.
basis : xr.DataArray
s2p_c like with real values.
return_as: optional
What format to return as. Defaults to s1p_c.
Returns
-------
s1p_c
s1p_c data that has been rotated.
"""
ri = dfm.convert(dfm.s1p_ri, s1p_c)
ri_2 = np.expand_dims(ri.values, axis=ri.ndim - 1)
rotated = (ri_2 @ basis.values)[..., 0, :]
# out
out = dfm.empty_like(ri, dfm.s1p_ri)
out.values = rotated
return dfm.convert(return_as, out)
[docs]
def dbmag(s_params: DataArray, const: float = 20) -> DataArray:
"""
Convert complex s parameters to magnitude in dB.
Calculates by const*log10(linmag(s_params)). By default const = 20.
Parameters
----------
s_params : array like
any complex format s parameters. array like.
const : float, optional
Multiplication constant for converting to magnitude (i.e.
const*log10(magnitude). Default is 20
Returns
-------
DataArray
Magnitude of s parameters in dB.
"""
return const * np.log10(np.abs(s_params))
[docs]
def phase(s_params: DataArray):
"""
Calculate the phase of complex s parameters.
Parameters
----------
s_params : DataArray
any complex format s parameters. array like.
Returns
-------
DataArray
Phase of s_params.
"""
new = s_params.copy()
new.values = np.angle(new.values)
return new
[docs]
def linmag(s_params: DataArray):
"""
Calculate linear magnitude of complex s-parameters.
Parameters
----------
s_params : DataArray
any complex format s parameters.
Returns
-------
DataArray
Converted sparams.
"""
return np.abs(s_params)
[docs]
def rotate_device(device: DataArray, axes: list):
"""
Rotate the parameters of a device
Parameters
----------
device: DataArray
The device to rotate. Supported formats are s2p.
axes: list
The axes to change to. The original ones are the index (1, 2, ...) and the list
are the ones to change to, so for example (2, 1) on an s2p will flip sides 1 and 2.
Returns
-------
DataArray
Rotated device.
"""
# I can generalize later sort of; each format seems to have its own naming convention though
# I am assuming s3 and 4p's will be the same so I can easily generalize that part if so, they just dont exist currently
device_copy = device.copy(deep=True)
if device.dataformat == 's2p_c':
S11 = device_copy.loc[..., 1, 1].values
S22 = device_copy.loc[..., 2, 2].values
S12 = device_copy.loc[..., 1, 2].values
S21 = device_copy.loc[..., 2, 1].values
device_copy.loc[..., 1, 1] = S22
device_copy.loc[..., 2, 2] = S11
device_copy.loc[..., 1, 2] = S21
device_copy.loc[..., 2, 1] = S12
return device_copy