import numpy as np
from SciDataTool import Data1D, DataFreq, DataTime, DataDual, DataND
from ...Functions.Winding.gen_phase_list import gen_name
[docs]def n2dqh_DataTime(data_n, is_dqh_rms=True, phase_dir=None):
"""n phases to dqh equivalent coordinate transformation of DataTime object
Parameters
----------
data_n : DataTime
data object containing values over time and phase axes
is_dqh_rms : boolean
True to return dq currents in rms value (Pyleecan convention), False to return peak values
phase_dir: int
direction of phase distribution: +/-1 (-1 clockwise) to enforce
Returns
-------
data_dqh : DataTime
data object transformed in dqh frame
"""
if phase_dir is None:
# Check if input data object is compliant with dqh transformation
# and get phase_dir from data object
phase_dir = get_phase_dir_DataTime(data_n)
else:
# Only check if input data object is compliant with dqh transformation
_check_data(data_n)
if "angle_elec" not in data_n.axes[0].normalizations:
raise Exception("Time axis should contain angle_elec normalization")
# Get values for one time period converted in electrical angle and for all phases
angle_elec = data_n.axes[0].get_values(
normalization="angle_elec", is_oneperiod=True
)
data_n_val = data_n.get_along("time[oneperiod]", "phase", is_squeeze=False)[
data_n.symbol
]
# Convert values to dqh frame
data_dqh_val = n2dqh(data_n_val, angle_elec, is_dqh_rms, phase_dir)
# Get time axis on one period
per_t, is_aper_t = data_n.axes[0].get_periodicity()
per_t = int(per_t / 2) if is_aper_t else per_t
Time = data_n.axes[0].get_axis_periodic(per_t, is_aper=False)
# Create DQH axis
axis_dq = Data1D(
name="phase",
unit="",
values=["direct", "quadrature", "homopolar"],
is_components=True,
)
# Get normalizations
normalizations = dict()
if data_n.normalizations is not None and len(data_n.normalizations) > 0:
for key, val in data_n.normalizations.items():
normalizations[key] = val.copy()
# Create DataTime object in dqh frame
data_dqh = DataTime(
name=data_n.name + " in DQH frame",
unit=data_n.unit,
symbol=data_n.symbol,
values=data_dqh_val,
axes=[Time, axis_dq],
normalizations=normalizations,
is_real=data_n.is_real,
)
return data_dqh
[docs]def n2dqh(Z_n, angle_elec, is_dqh_rms=True, phase_dir=None):
"""n phases to dqh equivalent coordinate transformation
Parameters
----------
Z_n : ndarray
matrix (N x n) of n phases values
angle_elec : ndarray
angle of the rotor coordinate system
is_dqh_rms : boolean
True to return dq currents in rms value (Pyleecan convention), False to return peak values
phase_dir: int
direction of phase distribution: +/-1 (-1 clockwise) to enforce
Returns
-------
Z_dqh : ndarray
transformed matrix (N x 3) of dqh equivalent values
"""
if phase_dir is None:
# Get phase_dir from Z_n
phase_dir = get_phase_dir(Z_n)
Z_dqh = abc2dqh(n2abc(Z_n, phase_dir), angle_elec)
if is_dqh_rms:
# Divide by sqrt(2) to go from (Id_peak, Iq_peak) to (Id_rms, Iq_rms)
Z_dqh /= np.sqrt(2)
return Z_dqh
[docs]def n2abc(Z_n, phase_dir=None):
"""n phase to 3 phases equivalent coordinate transformation, i.e. Clarke transformation
Parameters
----------
Z_n : ndarray
matrix (N x n) of n phase values
phase_dir: int
direction of phase distribution: +/-1 (-1 clockwise) to enforce
Returns
-------
Z_abc : ndarray
transformed matrix (N x 3) of 3 phases equivalent values (alpha-beta-gamma)
"""
n = Z_n.shape[1]
n_2_abc = comp_Clarke_transform(n, is_inv=False, phase_dir=phase_dir)
Z_abc = np.matmul(Z_n, n_2_abc)
return Z_abc
[docs]def abc2dqh(Z_abc, angle_elec):
"""alpha-beta-gamma to dqh coordinate transformation
Parameters
----------
Z_abc : ndarray
matrix (N x 3) of alpha-beta-gamma - reference frame values
angle_elec : ndarray
angle of the rotor coordinate system
Returns
-------
Z_dqh : ndarray
transformed (dqh) values
"""
if Z_abc.ndim == 1:
Z_abc = Z_abc[None, :]
sin_angle_elec = np.sin(angle_elec)
cos_angle_elec = np.cos(angle_elec)
Z_dqh = np.zeros((angle_elec.size, 3))
# d-axis
Z_dqh[:, 0] = Z_abc[:, 0] * cos_angle_elec + Z_abc[:, 1] * sin_angle_elec
# q-axis
Z_dqh[:, 1] = -Z_abc[:, 0] * sin_angle_elec + Z_abc[:, 1] * cos_angle_elec
# Homopolar axis
Z_dqh[:, 2] = Z_abc[:, 2]
return Z_dqh
[docs]def dqh2n_DataTime(data_dqh, n, is_n_rms=False, phase_dir=None):
"""dqh to n phase coordinate transformation of DataTime object
Parameters
----------
data_dqh : DataTime
data object containing values over time in dqh frame
n: int
number of phases
is_n_rms : boolean
True to return n currents in rms value, False to return peak values (Pyleecan convention)
phase_dir: int
direction of phase distribution: +/-1 (-1 clockwise) to enforce
Returns
-------
data_n : DataTime
data object containing values over time and phase axes
"""
# Check if input data object is compliant with dqh transformation
_check_data(data_dqh)
if "angle_elec" not in data_dqh.axes[0].normalizations:
raise Exception("Time axis should contain angle_elec normalization")
# Get values for one time period converted in electrical angle and for all phases
angle_elec = data_dqh.axes[0].get_values(
normalization="angle_elec", is_oneperiod=True
)
data_dqh_val = data_dqh.get_along("time[oneperiod]", "phase", is_squeeze=False)[
data_dqh.symbol
]
# Convert values to dqh frame
data_n_val = dqh2n(data_dqh_val, angle_elec, n, is_n_rms, phase_dir)
# Get time axis on one period
per_t, is_aper_t = data_dqh.axes[0].get_periodicity()
per_t = int(per_t / 2) if is_aper_t else per_t
Time = data_dqh.axes[0].get_axis_periodic(per_t, is_aper=False)
# Create DQH axis
Phase = Data1D(
name="phase",
unit="",
values=gen_name(n),
is_components=True,
)
# Get normalizations
normalizations = dict()
if data_dqh.normalizations is not None and len(data_dqh.normalizations) > 0:
for key, val in data_dqh.normalizations.items():
normalizations[key] = val.copy()
# Create DataTime object in dqh frame
data_n = DataTime(
name=data_dqh.name.replace(" in DQH frame", ""),
unit=data_dqh.unit,
symbol=data_dqh.symbol,
values=data_n_val,
axes=[Time, Phase],
normalizations=normalizations,
is_real=data_dqh.is_real,
)
return data_n
[docs]def dqh2n(Z_dqh, angle_elec, n, is_n_rms=False, phase_dir=None):
"""dqh to n phase coordinate transformation
Parameters
----------
Z_dqh : ndarray
matrix (N x 3) of dqh phase values
angle_elec : ndarray
angle of the rotor coordinate system
n: int
number of phases
is_n_rms : boolean
True to return n currents in rms value, False to return peak values (Pyleecan convention)
phase_dir: int
direction of phase distribution: +/-1 (-1 clockwise) to enforce
Returns
-------
Z_n : ndarray
transformed matrix (N x n) of n phase values
"""
Z_n = abc2n(dqh2abc(Z_dqh, angle_elec), n, phase_dir)
if not is_n_rms:
# Multiply by sqrt(2) to from (I_n_rms) to (I_n_peak)
Z_n *= np.sqrt(2)
return Z_n
[docs]def abc2n(Z_abc, n=3, phase_dir=None):
"""3 phase equivalent to n phase coordinate transformation, i.e. Clarke transformation
Parameters
----------
Z_abc : ndarray
matrix (N x 3) of 3 phase equivalent values in alpha-beta-gamma frame
n: int
number of phases
phase_dir: int
direction of phase distribution: +/-1 (-1 clockwise) to enforce
Returns
-------
Z_n : ndarray
transformed matrix (N x n) of n phase values
"""
# Inverse of Clarke transformation matrix
ab_2_n = comp_Clarke_transform(n, is_inv=True, phase_dir=phase_dir)
Z_n = np.matmul(Z_abc, ab_2_n)
return Z_n
[docs]def dqh2abc(Z_dqh, angle_elec):
"""dqh to alpha-beta-gamma coordinate transformation
Parameters
----------
Z_dqh : ndarray
matrix (N x 3) of dqh - reference frame values
angle_elec : ndarray
angle of the rotor coordinate system
Returns
-------
Z_abc : ndarray
transformed array
"""
if Z_dqh.ndim == 1:
Z_dqh = Z_dqh[None, :]
sin_angle_elec = np.sin(angle_elec)
cos_angle_elec = np.cos(angle_elec)
Z_abc = np.zeros((angle_elec.size, 3))
Z_abc[:, 0] = Z_dqh[:, 0] * cos_angle_elec - Z_dqh[:, 1] * sin_angle_elec
Z_abc[:, 1] = Z_dqh[:, 0] * sin_angle_elec + Z_dqh[:, 1] * cos_angle_elec
Z_abc[:, 2] = Z_dqh[:, 2]
return Z_abc
[docs]def get_phase_dir_DataTime(data_n):
"""Get the phase rotating direction of input n-phase DataTime object
Parameters
----------
data_n : DataTime
data object containing values over time and phase axes
Returns
-------
phase_dir : int
rotating direction of phases +/-1
"""
# Check if input data object is compliant with dqh transformation
_check_data(data_n)
# Extract values from DataTime
Z_n = data_n.get_along("time[oneperiod]", "phase")[data_n.symbol]
# Get current_dir
current_dir = int(np.sign(data_n.axes[0].normalizations["angle_elec"].ref))
# Get phase direction
phase_dir = get_phase_dir(Z_n, current_dir)
return phase_dir
[docs]def get_phase_dir(Z_n, current_dir):
"""Get the phase rotating direction of input n-phase quantity by looking at phase of maximum component
Parameters
----------
Z_n : ndarray
matrix (N x n) of n phase values
current_dir: int
direction of current waveform: +/-1 (-1 clockwise) to enforce
Returns
----------
phase_dir : int
rotating direction of phases +/-1
"""
from ...Functions.Electrical.dqh_transformation_freq import (
get_phase_dir as get_phase_dir_freq,
)
# Get number of time steps
N = Z_n.shape[0]
# Get Fourier transform for all phases
TF_Zn = np.fft.fft(Z_n, axis=0)[: int(N / 2), :]
# Calculate phase_dir from FFT components
phase_dir = get_phase_dir_freq(TF_Zn, current_dir)
return phase_dir
def _check_data(data, is_freq=False):
"""Check if input Data object is compliant with dqh transformation
Parameters
----------
data : Data
data object to check
"""
if not isinstance(data, (DataTime, DataFreq, DataDual, DataND)):
raise Exception(
"Input object should be a DataTime, DataFreq or DataDual object"
)
elif isinstance(data, DataTime):
_check_DataTime(data)
elif isinstance(data, DataFreq):
_check_DataFreq(data)
elif isinstance(data, DataDual):
if is_freq:
data.axes = data.axes_df
data.values = data.values_df
_check_DataFreq(data)
else:
data.axes = data.axes_dt
data.values = data.values_dt
_check_DataTime(data)
elif isinstance(data, DataND):
try:
_check_DataTime(data)
except Exception:
_check_DataFreq(data)
def _check_DataTime(data):
"""Check if input DataTime object is compliant with dqh transformation
Parameters
----------
data : DataTime
DataTimes object to check
"""
if len(data.axes) != 2:
raise Exception("DataTime object should contain two axes: time and phase")
if data.axes[0].name != "time":
raise Exception("DataTime object should contain time as first axis")
if data.axes[1].name != "phase":
raise Exception("DataTime object should contain phase as second axis")
def _check_DataFreq(data):
"""Check if input DataFreq object is compliant with dqh transformation
Parameters
----------
data : DataFreq
DataFreq object to check
"""
if len(data.axes) != 2:
raise Exception("DataFreq object should contain two axes: freqs and phase")
if data.axes[0].name != "freqs":
raise Exception("DataFreq object should contain freqs as first axis")
if data.axes[1].name != "phase":
raise Exception("DataFreq object should contain phase as second axis")