Python NET library validation
Denis Barkats (Aug 20th 2017)
I wrote a python library version (NETlib.py) of Jamie Bock's famous NET calculation spreadsheet. The goal was simplify the usability, and version control.
An earlier version of this code (still coded in matlab at the time) was used for comparing the NETs within the CMB-S4 defined bands (2017-02-21 CMB-S4 logbook posting).
With the python library in hand, a web GUI to interface to it seemed like a natural fit. Take a look at the Beta version and send me feedback (email@example.com).
This posting describes the validation against Jamie's spreadsheet calculation. Based on the simple tests below, the results here are consistent with Jamie's spreadsheet.
Thanks to Justin Willmert for helping with the web interface look and feel.
- Add in realistic bandpass: Either bandpasses with a Chebychev ripple on top to mimic realistic bandpasses, or read in our real BK published bandpasses, or allow the user to input a real bandpass file.
- Check the results as a function altitude to verify that the NETs make sense for LDB or space loading.
- Place the code on github under git control.
The NETlib.py detailed documentation is linked from the NET webGUI itself. I'm happy to clarify if anything is unclear or wrong.
In order to validate this new NET python code, I compare it directly to Jamie's latest spreadsheet online version.
- Site: The spreadsheet is hard-coded for South Pole atmosphere, so I use that site. I checked and the spreadsheet uses the same atmospheric profile aswhat I'm using (the 10yr merra2-avg profile from Scott's tutorial, yields a PWV = 0.425mm)
- Bandpass: This initial comparison only checks the flat top hat bandpass model. More complicated bandpass will come in upcoming features.
- All NET are calculated for elevation = 45deg.
- Bolometers parameter are matched. The spreadsheet allows you to input: beta, the safety factor (sf) and the bolometer critical temperature (Tc). Additionally, NETlib.py allows you to specify the bath temperature (T0), R_tes and T_shunt (used to calculate NEP_tes and NEP_shunt)
Given the above differences, the hardest part of a fair comparison was dealing with the optical efficiency which is specified as an input in the spreadsheet but comes out of the instrument layer definition from the python code. What I did to make them match was to add a fake layer in the instrument with 0K physical temperature and whose emissvity I tweaked until the optical efficiency matched the 30% assumed in the spreadsheet.
- The main difference is in the way the optical loading is calculated. As described above, the python code sets up a 1D radiative transfer through a stack of cmb + atmosphere + instrument + detector layers to derive the total loading on the detector. The optical efficiency is an output of the code and depends on the input emissivities of the various instrument layers. The spreadsheet treats the layers more independantly as follows:
The instrument is a set of layers each of the physical temperature (Ti) and emissivity (epsi)
The instrument loading is simply:
# Trj_instr = Sum (Ti*epsi)
Then, the instrument loading is simply
# dQinstr(v) = bandpass * oe * k * Trj_instr * dv
# Qopt = Integral(dQinst dv) in pW
Note that dQinstr above is a constant
Given an atmospheric brightness temperature spectrm Trj_atm(v), a tophat bandpass (bandpass = 0 outside the band 1 inside the band), an end-to-end optical efficiency (oe), and an elevation (el), the incident power from the atmosphere on the detector is
# dQatm = bandpass * oe * k * Trj_atm(v) * dv /(sin el)
# Qatm = Integral(dQatm(v) dv) in pW
# dQcmb = bandpass * (1-(1-Tx_atm(v))/sin el) * oe * h * v * dv / exp(hv/kTcmb)
# Qcmb = Integral(dQcmb dv) in pW
The total loading is then simply
# Qtot = Qatm+Qinstr+Qcmb
So from the description above, we expect some differences in the resulting Trj_instr, Qinstr, and Qatm and that the 1D radiative transfer is likely more correct as it treats the cumulative effects.
- Equation for Gc matches except that in the spreadsheet has T0 hard-coded to T0 = 0.250K. Only difference in Gc comes from differences in Q_tot.
- Same equation for NEP_shot noise. Only difference comes from difference in Q_tot.
- Same equation for NEP_bose noise contribution. Only difference comes from Q_tot.
- Same equation for NEP_phonon noise, except that spreadsheet has the value of F (which depends on Beta, T0 and Tc) hard-coded to 0.823, while it's explicitely re-calculated in the NETlib.py
- Detector noise in Jamie's spreadsheet only accounts for phonon noise and ignores shunt and tes noise contributions (which are indeed sub-dominant)
Below are the compared results for 4 specific cases. The bold results are from NETlib.py (as used by the online tool), the other from the spreadsheet.
We can see that the dPdT , Qcmb and Qatm match well, though for Qatm not as well as I would have expecte given the match in the atmospheric spectra. I will check why. I'm also not sure why dPdTcmb at v=40GHz differs so much given that it matches perfectly at other frequencies. I will also check. As expected, the largest differences come from the calculated instrument loading (Qinst). This difference obviously gets passed on to the NEP_shot and NEP_bose. The small difference in NEP_det comes from ignoring the tes and shunt noise in the spreadsheet.
|Case: v_cen, frac_bw (v_lo/v_hi)
||Qinst [pW] / [Krj]
|40 GHz, 0.22 (35.6/44.4)
||0.204, 0.17 / 5.87,4.7
|94GHz, 0.22 (83.7/104.3)
||0.651, 0.62 / 8.02, 7.2
|148.5GHz, 0.22 (132.2/164.8)
||1.316, 1.27 / 10.10, 9.4
|230GHz, 0.22 (204.7/255.3)
||2.96, 2.89 / 15.13, 13.8
|270GHz, 0.22 (240.2/299.7)
||3.44, 3.39 / 15.32, 13.8