Skip to content

Commit 731df6a

Browse files
authored
Merge pull request #199 from spice-herald/harmonics
Ignore harmonics
2 parents c286f8c + c602ced commit 731df6a

File tree

2 files changed

+72
-21
lines changed

2 files changed

+72
-21
lines changed

qetpy/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# special file for defining the current version of the package
2-
__version__ = "1.8.2"
2+
__version__ = "1.8.3"

qetpy/core/_of_base.py

Lines changed: 71 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,7 @@ def set_time_constraints(self, channels,
11041104
def set_csd(self, channels, csd,
11051105
coupling='AC',
11061106
ignored_frequency_peaks=None,
1107+
ignore_harmonics=False,
11071108
calc_icov=True):
11081109
"""
11091110
Add csd, calculate inverse (optional)
@@ -1131,6 +1132,8 @@ def set_csd(self, channels, csd,
11311132
calculation. The nearest bin (both positive/negative frequencies) from
11321133
given frequency is selected and CSD frequency set to infinity.
11331134
1135+
ignore_harmonics : boolean
1136+
if True, harmonics of the ignored_frequency_peaks are also ignored up to max frequency
11341137
11351138
Returns
11361139
-------
@@ -1173,11 +1176,13 @@ def set_csd(self, channels, csd,
11731176
if calc_icov:
11741177
self.calc_icovf(channel_name,
11751178
coupling=coupling,
1176-
ignored_frequency_peaks=ignored_frequency_peaks)
1179+
ignored_frequency_peaks=ignored_frequency_peaks,
1180+
ignore_harmonics=ignore_harmonics)
11771181

11781182

11791183
def set_psd(self, channel, psd, coupling='AC',
1180-
ignored_frequency_peaks=None):
1184+
ignored_frequency_peaks=None,
1185+
ignore_harmonics=False):
11811186
"""
11821187
Add psd for specified channel
11831188
If psd already exist, it is overwritten
@@ -1202,7 +1207,9 @@ def set_psd(self, channel, psd, coupling='AC',
12021207
frequencies that are ignored in optimal filter
12031208
calculation. The nearest bin (both positive/negative frequencies) from
12041209
given frequency is selected and CSD frequency set to infinity.
1205-
1210+
1211+
ignore_harmonics : boolean
1212+
if True, harmonics of the ignored_frequency_peaks are also ignored up to max frequency
12061213
12071214
Returns
12081215
-------
@@ -1236,13 +1243,15 @@ def set_psd(self, channel, psd, coupling='AC',
12361243
csd = psd[np.newaxis, np.newaxis, :] # now shape is (1, 1, nfreq)
12371244

12381245
self.set_csd(channel, csd, coupling=coupling,
1239-
ignored_frequency_peaks=ignored_frequency_peaks)
1246+
ignored_frequency_peaks=ignored_frequency_peaks,
1247+
ignore_harmonics=ignore_harmonics)
12401248

12411249

12421250

12431251
def calc_icovf(self, channels,
12441252
coupling='AC',
1245-
ignored_frequency_peaks=None):
1253+
ignored_frequency_peaks=None,
1254+
ignore_harmonics=False):
12461255
"""
12471256
A function that inverts the csd or covariance noise matrix between channels.
12481257
@@ -1265,7 +1274,8 @@ def calc_icovf(self, channels,
12651274
calculation. The nearest bin (both positive/negative frequencies) from
12661275
given frequency is selected and CSD frequency set to infinity.
12671276
1268-
1277+
ignore_harmonics : boolean
1278+
if True, harmonics of the ignored_frequency_peaks are also ignored up to max frequency
12691279
12701280
Returns
12711281
-------
@@ -1287,29 +1297,70 @@ def calc_icovf(self, channels,
12871297
if coupling == 'AC':
12881298
temp_icovf[:,:,0] = 0.0
12891299

1300+
12901301
if ignored_frequency_peaks is not None:
12911302

1303+
# normalize input to a list
12921304
if not isinstance(ignored_frequency_peaks, list):
12931305
ignored_frequency_peaks = [ignored_frequency_peaks]
1306+
print(ignored_frequency_peaks)
1307+
freqs = np.asarray(self._fft_freqs)
1308+
fmax = float(np.max(np.abs(freqs)))
1309+
1310+
# tolerance ?
1311+
"""
1312+
# estimate frequency-bin spacing (robust to sign order)
1313+
# sort by absolute frequency to avoid the 0 jump
1314+
sort_idx = np.argsort(np.abs(freqs))
1315+
df_est = np.median(np.diff(np.sort(np.abs(freqs))))
1316+
if not np.isfinite(df_est) or df_est <= 0:
1317+
# fallback if something weird with freqs
1318+
df_est = np.min(np.abs(np.diff(np.sort(freqs)))) if freqs.size > 1 else 0.0
1319+
1320+
# tolerance: half a bin by default
1321+
tol = 0.5 * df_est if df_est > 0 else 0.0
1322+
"""
1323+
1324+
# accumulate all bins to zero in one mask
1325+
mask_all = np.zeros_like(freqs, dtype=bool)
1326+
1327+
for f in ignored_frequency_peaks:
1328+
if f is None or not np.isfinite(f):
1329+
continue
12941330

1295-
for freq in ignored_frequency_peaks:
1331+
f = float(np.abs(f))
1332+
if f == 0.0:
1333+
# Special case: DC (closest-to-zero bin)
1334+
idx0 = int(np.argmin(np.abs(freqs)))
1335+
mask_all[idx0] = True
1336+
continue
12961337

1297-
# make sure frequency positive
1298-
freq = np.abs(freq)
1338+
# Build the target frequencies to zero
1339+
if ignore_harmonics:
1340+
kmax = int(np.floor(fmax / f))
1341+
if kmax <= 0:
1342+
continue
1343+
targets = f * np.arange(1, kmax + 1, dtype=float)
1344+
else:
1345+
targets = np.array([f], dtype=float)
12991346

1300-
# positive frequency
1301-
idx_pos = int(np.argmin(np.abs(self._fft_freqs - freq)))
1302-
mask = np.zeros_like(self._fft_freqs, dtype=bool)
1303-
mask[idx_pos] = True
1347+
# For each target harmonic, hit both + and - sides
1348+
for t in targets:
1349+
1350+
# positive side
1351+
idx_pos = int(np.argmin(np.abs(freqs - t)))
1352+
#if np.abs(freqs[idx_pos] - t) <= tol:
1353+
mask_all[idx_pos] = True
1354+
1355+
# negative side
1356+
idx_neg = int(np.argmin(np.abs(freqs + t)))
1357+
#if np.abs(freqs[idx_neg] + t) <= tol:
1358+
mask_all[idx_neg] = True
1359+
1360+
# apply once
1361+
temp_icovf[..., mask_all] = 0.0
13041362

1305-
# negative frequency
1306-
idx_neg = int(np.argmin(np.abs(self._fft_freqs + freq)))
1307-
mask[idx_neg] = True
13081363

1309-
# modify
1310-
temp_icovf[..., mask] = 0.0
1311-
1312-
13131364
self._icovf[channel_name] = temp_icovf
13141365

13151366

0 commit comments

Comments
 (0)