Skip to content

Commit 289c6fe

Browse files
Stokes I 3d frequency fix (#119)
* Add ref. freq. to fitIcube outputs. * Un-deprecated the Stokes I renormalize parameters function. Added renormalization of uncertainties. * Change default Stokes I freq to mean of lambda^2, not mean of freq. * Stokes I model parameters now being shifted to polarization reference frequency. Updated test values. * Added new tool to perform Stokes I ref.freq. rescaling for 3D Stokes I products. * Added warning for large shifts in reference freq.
1 parent 676bb48 commit 289c6fe

6 files changed

Lines changed: 523 additions & 46 deletions

File tree

RMtools_1D/do_RMsynth_1D.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -351,21 +351,27 @@ def run_rmsynth(
351351
if verbose:
352352
log("> RM-synthesis completed in %.2f seconds." % cputime)
353353

354-
# Determine the Stokes I value at lam0Sq_m2 from the Stokes I model
355-
# This will break if lam0Sq_m2==0. Using the mean frequency in this case.
354+
# Convert Stokes I model to polarization reference frequency. If lambda^2_0 is
355+
# non-zero, use that as polarization reference frequency and adapt Stokes I model.
356+
# If lambda^2_0 is zero, make polarization reference frequency equal to
357+
# Stokes I reference frequency.
358+
359+
if lam0Sq_m2 == 0: # Rudnick-Cotton adapatation
360+
freq0_Hz = fitDict["reference_frequency_Hz"]
361+
else: # standard RM-synthesis
362+
freq0_Hz = C / m.sqrt(lam0Sq_m2)
363+
fitDict = renormalize_StokesI_model(fitDict, freq0_Hz)
364+
365+
# Set Ifreq0 (Stokes I at reference frequency) from either supplied model
366+
# (interpolated as required) or fit model, as appropriate.
356367
# Multiply the dirty FDF by Ifreq0 to recover the PI
357-
freq0_Hz = C / m.sqrt(lam0Sq_m2) if lam0Sq_m2 > 0 else np.nanmean(freqArr_Hz)
358368
if modStokesI is None:
359369
Ifreq0 = calculate_StokesI_model(fitDict, freq0_Hz)
360370
elif modStokesI is not None:
361371
modStokesI_interp = interp1d(freqArr_Hz, modStokesI)
362372
Ifreq0 = modStokesI_interp(freq0_Hz)
363373
dirtyFDF *= Ifreq0 # FDF is in fracpol units initially, convert back to flux
364374

365-
# if modStokesI is None:
366-
# #Need to renormalize the Stokes I parameters here to the actual reference frequency.
367-
# fitDict=renormalize_StokesI_model(fitDict,freq0_Hz)
368-
369375
# Calculate the theoretical noise in the FDF !!Old formula only works for wariance weights!
370376
weightArr = np.where(np.isnan(weightArr), 0.0, weightArr)
371377
dFDFth = np.abs(Ifreq0) * np.sqrt(
@@ -387,7 +393,6 @@ def run_rmsynth(
387393
mDict["polyCoefferr"] = ",".join(
388394
[str(x.astype(np.float32)) for x in fitDict["perror"]]
389395
)
390-
mDict["poly_reffreq"] = fitDict["reference_frequency_Hz"]
391396
mDict["polyOrd"] = fitDict["polyOrd"]
392397
mDict["IfitStat"] = fitDict["fitStatus"]
393398
mDict["IfitChiSqRed"] = fitDict["chiSqRed"]

RMtools_3D/do_fitIcube.py

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,13 @@
3535
# =============================================================================#
3636

3737
import argparse
38-
import multiprocessing as mp
3938
import os
4039
import sys
4140
import time
4241
from functools import partial
4342

4443
import astropy.io.fits as pf
4544
import numpy as np
46-
from tqdm.auto import tqdm
4745
from tqdm.contrib.concurrent import process_map
4846

4947
from RMtools_3D.do_RMsynth_3D import readFitsCube
@@ -329,7 +327,8 @@ def savefits_mask(data, header, outDir, prefixOut):
329327
headMask = strip_fits_dims(header=header, minDim=2)
330328
headMask["DATAMAX"] = 1
331329
headMask["DATAMIN"] = 0
332-
del headMask["BUNIT"]
330+
if "BUNIT" in headMask:
331+
del headMask["BUNIT"]
333332

334333
mskArr = np.where(data > 0, 1.0, np.nan)
335334
MaskfitsFile = os.path.join(outDir, prefixOut + "mask.fits")
@@ -348,17 +347,16 @@ def savefits_Coeffs(data, dataerr, header, polyOrd, outDir, prefixOut):
348347
prefixOut: prefix to use on the output name
349348
"""
350349

351-
headcoeff = strip_fits_dims(header=header, minDim=2)
352-
headcoeff["BUNIT"] = ""
353-
if "BTYPE" in headcoeff:
354-
del headcoeff["BTYPE"]
350+
header["BUNIT"] = ""
351+
if "BTYPE" in header:
352+
del header["BTYPE"]
355353

356354
for i in range(np.abs(polyOrd) + 1):
357355
outname = os.path.join(outDir, prefixOut + "coeff" + str(i) + ".fits")
358-
pf.writeto(outname, data[i], headcoeff, overwrite=True)
356+
pf.writeto(outname, data[i], header, overwrite=True)
359357

360358
outname = os.path.join(outDir, prefixOut + "coeff" + str(i) + "err.fits")
361-
pf.writeto(outname, dataerr[i], headcoeff, overwrite=True)
359+
pf.writeto(outname, dataerr[i], header, overwrite=True)
362360

363361

364362
def savefits_model_I(data, header, outDir, prefixOut):
@@ -422,10 +420,12 @@ def fit_spectra_I(
422420
outs["I"] = pixImodel.astype("float32")
423421
outs["coeffs"] = pixFitDict["p"].astype("float32")
424422
outs["coeffs_err"] = pixFitDict["perror"].astype("float32")
425-
outs["chiSq"] = pixFitDict["chiSq"].astype("float32")
426-
outs["chiSqRed"] = pixFitDict["chiSqRed"].astype("float32")
423+
outs["chiSq"] = pixFitDict["chiSq"]
424+
outs["chiSqRed"] = pixFitDict["chiSqRed"]
427425
outs["nIter"] = pixFitDict["nIter"]
428-
outs["AIC"] = pixFitDict["AIC"].astype("float32")
426+
outs["AIC"] = pixFitDict["AIC"]
427+
outs["covar"] = pixFitDict["pcov"]
428+
outs["reference_frequency_Hz"] = pixFitDict["reference_frequency_Hz"]
429429

430430
return outs
431431

@@ -501,7 +501,11 @@ def make_model_I(
501501

502502
coeffs = np.array([mskArr] * 6)
503503
coeffs_error = np.array([mskArr] * 6)
504+
reffreq = np.squeeze(np.array([mskArr]))
505+
506+
covars = np.array([[mskArr] * 6] * 6)
504507
datacube = np.squeeze(datacube)
508+
505509
# Select only the spectra with emission
506510
srcData = np.rot90(datacube[:, mskSrc > 0])
507511

@@ -547,36 +551,72 @@ def make_model_I(
547551
for _, an in enumerate(xy):
548552
i, x, y = an
549553

550-
modelIcube[:, x, y] = results[_]["I"]
554+
modelIcube[:, x, y] = results[i]["I"]
555+
reffreq[x, y] = results[i]["reference_frequency_Hz"]
556+
covars[:, :, x, y] = results[i]["covar"]
551557

552558
for k, j, l in zip(
553-
range(len(coeffs)), results[_]["coeffs"], results[_]["coeffs_err"]
559+
range(len(coeffs)), results[i]["coeffs"], results[i]["coeffs_err"]
554560
):
555561
coeffs[5 - k, x, y] = j
556562
coeffs_error[5 - k, x, y] = l
557563

558-
header["HISTORY"] = "Stokes I model fitted by RM-Tools"
564+
headcoeff["HISTORY"] = "Stokes I model fitted by RM-Tools"
559565
if polyOrd < 0:
560-
header["HISTORY"] = (
566+
headcoeff["HISTORY"] = (
561567
f"Fit model is dynamic order {fit_function}-polynomial, max order {-polyOrd}"
562568
)
563569
else:
564-
header["HISTORY"] = f"Fit model is {polyOrd}-order {fit_function}-polynomial"
570+
headcoeff["HISTORY"] = f"Fit model is {polyOrd}-order {fit_function}-polynomial"
565571

566-
print("Saving mask image.")
567-
savefits_mask(data=mskSrc, header=header, outDir=outDir, prefixOut=prefixOut)
572+
if verbose:
573+
print("Saving mask image.")
574+
savefits_mask(data=mskSrc, header=headcoeff, outDir=outDir, prefixOut=prefixOut)
568575

569-
print("Saving model I coefficients.")
576+
if verbose:
577+
print("Saving model I coefficients.")
570578
savefits_Coeffs(
571579
data=coeffs,
572580
dataerr=coeffs_error,
573-
header=header,
581+
header=headcoeff,
574582
polyOrd=polyOrd,
575583
outDir=outDir,
576584
prefixOut=prefixOut,
577585
)
578586

579-
print("Saving model I cube image. ")
587+
# Save frequency map
588+
head_freq = headcoeff.copy()
589+
head_freq["BUNIT"] = "Hz"
590+
if "BTYPE" in headcoeff:
591+
del headcoeff["BTYPE"]
592+
593+
outname = os.path.join(outDir, prefixOut + "reffreq.fits")
594+
pf.writeto(outname, reffreq, head_freq, overwrite=True)
595+
596+
# Save covariance maps -- these are necessary if/when converting the model
597+
# reference frequency.
598+
# Structure will be a single file as a 4D cube, with the 3rd and 4th dimensions
599+
# iterating over the two axes of the covariance matrix.
600+
head_covar = headcoeff.copy()
601+
head_covar["NAXIS"] = 4
602+
head_covar["NAXIS3"] = 6
603+
head_covar["NAXIS4"] = 6
604+
head_covar["CTYPE3"] = "INDEX"
605+
head_covar["CTYPE4"] = "INDEX"
606+
head_covar["CRVAL3"] = 0
607+
head_covar["CRVAL4"] = 0
608+
head_covar["CDELT3"] = 1
609+
head_covar["CDELT4"] = 1
610+
head_covar["CRPIX3"] = 1
611+
head_covar["CRPIX4"] = 1
612+
head_covar["CUNIT3"] = ""
613+
head_covar["CUNIT4"] = ""
614+
615+
outname = os.path.join(outDir, prefixOut + "covariance.fits")
616+
pf.writeto(outname, covars, head_covar, overwrite=True)
617+
618+
if verbose:
619+
print("Saving model I cube image. ")
580620
savefits_model_I(data=modelIcube, header=header, outDir=outDir, prefixOut=prefixOut)
581621

582622
np.savetxt(os.path.join(outDir, prefixOut + "noise.dat"), rms_Arr)

0 commit comments

Comments
 (0)