Skip to content

Commit 14eaefa

Browse files
committed
Add estimator logic to keep a pixel's probes if at least 2 probe pairs were usable.
1 parent 1070614 commit 14eaefa

2 files changed

Lines changed: 49 additions & 22 deletions

File tree

falco/est.py

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ def pairwise_probing(mp, ev, jacStruct=np.array([])):
240240
Itr = ev.Itr
241241
whichDM = mp.est.probe.whichDM
242242

243-
# # Reset "ev" if not using a Kalman filter.
243+
# # Reset "ev" if not using a Kalman filter.
244244
# if mp.estimator != 'pwp-kf':
245245
# # ev = falco.config.Object()
246246
# ev.dm1 = falco.config.Object()
@@ -293,12 +293,12 @@ def pairwise_probing(mp, ev, jacStruct=np.array([])):
293293

294294
# Store the initial DM commands
295295
if np.any(mp.dm_ind == 1):
296-
DM1Vnom = mp.dm1.V
296+
DM1Vnom = mp.dm1.V.copy()
297297
else:
298298
DM1Vnom = np.zeros((mp.dm1.Nact, mp.dm1.Nact))
299299

300300
if np.any(mp.dm_ind == 2):
301-
DM2Vnom = mp.dm2.V
301+
DM2Vnom = mp.dm2.V.copy()
302302
else:
303303
DM2Vnom = np.zeros((mp.dm2.Nact, mp.dm2.Nact))
304304

@@ -323,7 +323,9 @@ def pairwise_probing(mp, ev, jacStruct=np.array([])):
323323
for k in range(Npairs-1):
324324
probePhaseVec = np.append(probePhaseVec, probePhaseVec[-1]-(Npairs-1))
325325
probePhaseVec = np.append(probePhaseVec, probePhaseVec[-1]+Npairs)
326+
# print(f'probePhaseVec = {probePhaseVec}')
326327
probePhaseVec = probePhaseVec*np.pi/(Npairs)
328+
print(f'probePhaseVec = {probePhaseVec}')
327329

328330
# Only for square probe region centered on the star
329331
if mp.estimator in ('pairwise', 'pairwise-square', 'pwp-bp-square'):
@@ -378,8 +380,8 @@ def pairwise_probing(mp, ev, jacStruct=np.array([])):
378380

379381
# Measure current contrast level average
380382
# Reset DM commands to the unprobed state:
381-
mp.dm1.V = DM1Vnom
382-
mp.dm2.V = DM2Vnom
383+
mp.dm1.V = DM1Vnom.copy()
384+
mp.dm2.V = DM2Vnom.copy()
383385
# Separate out image values at DH pixels and delta DM voltage settings
384386
Iplus = np.zeros((mp.Fend.corr.Npix, Npairs))
385387
Iminus = np.zeros((mp.Fend.corr.Npix, Npairs))
@@ -391,7 +393,7 @@ def pairwise_probing(mp, ev, jacStruct=np.array([])):
391393
# Compute probe shapes and take probed images:
392394

393395
# Take initial, unprobed image (for unprobed DM settings).
394-
whichImage = 1
396+
whichImage = 0
395397
I0 = falco.imaging.get_sbp_image(mp, iSubband)
396398
I0vec = I0[mp.Fend.corr.maskBool] # Vectorize the dark hole
397399

@@ -402,9 +404,9 @@ def pairwise_probing(mp, ev, jacStruct=np.array([])):
402404
# Store values for first image and its DM commands
403405
ev.imageArray[:, :, whichImage, iSubband] = I0
404406
if np.any(mp.dm_ind == 1):
405-
ev.dm1.Vall[:, :, whichImage, iSubband] = mp.dm1.V
407+
ev.dm1.Vall[:, :, whichImage, iSubband] = mp.dm1.V.copy()
406408
if np.any(mp.dm_ind == 2):
407-
ev.dm2.Vall[:, :, whichImage, iSubband] = mp.dm2.V
409+
ev.dm2.Vall[:, :, whichImage, iSubband] = mp.dm2.V.copy()
408410

409411
# Compute the average Inorm in the scoring and correction regions
410412
ev.corr = falco.config.Object()
@@ -502,7 +504,7 @@ def pairwise_probing(mp, ev, jacStruct=np.array([])):
502504
ampSq[ampSq < 0] = 0 # If probe amplitude is zero, set amp = 0
503505
amp = np.sqrt(ampSq) # E-field amplitudes, dimensions: [mp.Fend.corr.Npix, Npairs]
504506
isnonzero = np.all(amp, 1)
505-
zAll = ((Iplus-Iminus)/4).T # Measurement vector, dimensions: [Npairs,mp.Fend.corr.Npix]
507+
zAll = ((Iplus-Iminus)/4).T # Measurement vector, dimensions: [Npairs, mp.Fend.corr.Npix]
506508
ampSq2Dcube = np.zeros((mp.Fend.Neta, mp.Fend.Nxi, mp.est.probe.Npairs))
507509
for iProbe in range(Npairs): # Display the actual probe intensity
508510
ampSq2D = np.zeros((mp.Fend.Neta, mp.Fend.Nxi))
@@ -544,9 +546,9 @@ def pairwise_probing(mp, ev, jacStruct=np.array([])):
544546

545547
# For unprobed field based on model:
546548
if np.any(mp.dm_ind == 1):
547-
mp.dm1.V = DM1Vnom
549+
mp.dm1.V = DM1Vnom.copy()
548550
if np.any(mp.dm_ind == 2):
549-
mp.dm2.V = DM2Vnom
551+
mp.dm2.V = DM2Vnom.copy()
550552

551553
E0 = falco.model.compact(mp, modvar)
552554
E0vec = E0[mp.Fend.corr.maskBool]
@@ -578,8 +580,8 @@ def pairwise_probing(mp, ev, jacStruct=np.array([])):
578580
dphdm = np.zeros((mp.Fend.corr.Npix, Npairs)) # phases
579581
for iProbe in range(Npairs):
580582
dphdm[:, iProbe] = np.arctan2(
581-
np.imag(dEplus[:, iProbe]) - np.imag(dEminus[:, iProbe]),
582-
np.real(dEplus[:, iProbe]) - np.real(dEminus[:, iProbe]))
583+
np.imag(dEplus[:, iProbe] - dEminus[:, iProbe]),
584+
np.real(dEplus[:, iProbe] - dEminus[:, iProbe]))
583585

584586
# Batch process the measurements to estimate the electric field in the
585587
# dark hole. Done pixel by pixel.
@@ -592,30 +594,55 @@ def pairwise_probing(mp, ev, jacStruct=np.array([])):
592594

593595
Eest = np.zeros((mp.Fend.corr.Npix,), dtype=complex)
594596
zerosCounter = 0 # number of zeroed-out dark hole pixels
597+
partialCounter = 0 # number of pixels with >=2 but <Npairs usable probe pairs.
595598
for ipix in range(mp.Fend.corr.Npix):
596599

597600
if mp.est.flagUseJac:
598601
dE = dEplus[ipix, :].T
599602
H = np.array([np.real(dE), np.imag(dE)])
603+
Epix = np.linalg.pinv(H) @ zAll[:, ipix] # Batch processing
600604
else:
605+
allProbeInds = np.arange(Npairs, dtype=int)
606+
goodProbeInds = allProbeInds[amp[ipix, :] > 0]
607+
NpairsGood = len(goodProbeInds)
608+
601609
H = np.zeros([Npairs, 2]) # Observation matrix
602-
# Leave Eest for a pixel as zero if any probe amp is 0
603-
if isnonzero[ipix] == 1:
604-
for iProbe in range(Npairs):
605-
H[iProbe, :] = amp[ipix, iProbe] * \
606-
np.array([np.cos(dphdm[ipix, iProbe]),
607-
np.sin(dphdm[ipix, iProbe])])
608-
else:
610+
611+
# If <2 probe pairs had good measurements, can't do pinv. Leave Eest as zero.
612+
if NpairsGood < 2:
613+
zerosCounter = zerosCounter + 1
614+
Epix = np.zeros((2, 1))
609615
zerosCounter += 1
610616

611-
Epix = np.linalg.pinv(H) @ zAll[:, ipix] # Batch processing
617+
# Otherwise, use the 2+ good probe pair measurements for that pixel
618+
else:
619+
for iProbe in range(Npairs):
620+
H[iProbe, :] = amp[ipix, iProbe] * np.array([
621+
np.cos(dphdm[ipix, iProbe]), np.sin(dphdm[ipix, iProbe])])
622+
623+
Epix = np.linalg.pinv(H[goodProbeInds, :]) @ zAll[goodProbeInds, ipix] # Batch processing
624+
625+
# Record how many pixels didn't use all the probe pairs
626+
if NpairsGood < Npairs:
627+
partialCounter = partialCounter + 1
628+
629+
# # Leave Eest for a pixel as zero if any probe amp is 0
630+
# if isnonzero[ipix] == 1:
631+
# for iProbe in range(Npairs):
632+
# H[iProbe, :] = amp[ipix, iProbe] * \
633+
# np.array([np.cos(dphdm[ipix, iProbe]),
634+
# np.sin(dphdm[ipix, iProbe])])
635+
612636
Eest[ipix] = Epix[0] + 1j*Epix[1]
613637

614638
# If estimate is too bright, the estimate was probably bad.
615639
Eest[np.abs(Eest)**2 > mp.est.Ithreshold] = 0.0
616640

617641
print('%d of %d pixels were given zero probe amplitude.' %
618642
(zerosCounter, mp.Fend.corr.Npix))
643+
if Npairs > 2: # Only possible for Npairs > 2
644+
print('%d of %d pixels threw out at least one probe pair.' %
645+
(partialCounter, mp.Fend.corr.Npix))
619646

620647
# Initialize the state and state covariance estimates for the
621648
# Kalman filter. The state is the real and imag parts of the

falco/plot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ def pairwise_probes(mp, ev, dDMVplus, ampSq2Dcube, iSubband):
317317
for row in range(Npairs):
318318
ax = axs[row, col]
319319
ax.set_title(titles[col])
320-
ax.invert_yaxis()
320+
# ax.invert_yaxis()
321321
ax.set_box_aspect(1)
322322
if row != Npairs-1:
323323
ax.tick_params(labelbottom=False, bottom=False)

0 commit comments

Comments
 (0)