https://github.com/chill90/BoloCalc
Tip revision: 5b5fe53b155ebb7d1750975b0baa64bc7c3a04fb authored by Charles Hill on 17 September 2020, 07:23:00 UTC
Handling for updated atmosphere files
Handling for updated atmosphere files
Tip revision: 5b5fe53
sky.py
# Built-in modules
import numpy as np
import sys as sy
try:
import h5py as hp
except ImportError:
sy.stderr(
"BoloCalc Import Error: h5py not installed.\n"
"As of BoloCalc v0.10.0, h5py is used to load ATM profiles\n"
"Use pip to install via 'pip install h5py'\n"
"Or, if using an Anaconda environment, 'conda install h5py'\n")
import os
# BoloCalc modules
import src.foregrounds as fg
class Sky:
"""
Sky object contains the foregrounds and atmosphere
added line
Args:
tel (src.Telescope): parent Telescope object
Parents
tel (src.Telescope): Telescope object
"""
def __init__(self, tel):
# Store passed parameters
self.tel = tel
self._log = self.tel.exp.sim.log
self._phys = self.tel.exp.sim.phys
self._load = self.tel.exp.sim.load
self._infg = self.tel.exp.sim.param("infg")
self._atm_file = self.tel.exp.sim.atm_file
# Initialize foregrounds
if self._infg:
self._log.log("Initializing foregrounds in Sky object")
self._fg = fg.Foregrounds(self)
else:
self._fg = None
# Maximum and minimum allowed PWV
self._max_pwv = 8.0
self._min_pwv = 0.0
# Allowed site names
self._allowed_sites = [
"ATACAMA", "POLE", "MCMURDO", "SPACE", "CUST"]
# ***** Public Methods ******
def evaluate(self, sky_temp, pwv, elev, freqs):
"""
Generate the sky elements, absorbtivities, transmissions,
and temperatures
Args:
pwv (float): PWV
elev (float): elevation
freqs (float): frequencies [Hz] at which to evlauate the sky
"""
site = self.tel.param("site").upper()
# Custom sky effective brightness temperature
if sky_temp != "NA":
Nsky = ['Sky' for f in freqs]
Tsky = [sky_temp for f in freqs]
Esky = [1. for f in freqs]
Asky = [1. for f in freqs]
return [[Nsky],
[Asky],
[Esky],
[Tsky]]
elif site in self._allowed_sites:
# Check that an atmosphere exists
if site != 'SPACE':
Natm = ['ATM' for f in freqs]
Tatm, Eatm = self._atm_spectrum(pwv, elev, freqs)[1:]
Aatm = [1. for f in freqs]
# Won't look at the atmosphere from space, probably
else: # site = 'SPACE'
pass # no atmosphere
else:
self._log.err(
"Could not understand site '%s' defined for telescope '%s'\n"
"Allowed options: %s, or a float." % (
site.lower().capitalize(), self.tel.name,
', '.join(self._allowed_sites)))
Ncmb = ['CMB' for f in freqs]
Tcmb = [self._phys.Tcmb for f in freqs]
Ecmb = [1. for f in freqs]
Acmb = [1. for f in freqs]
# Include foregrounds
if self._infg:
Nsyn = ['SYNC' for f in freqs]
Tsyn = self._syn_temp(freqs)
Esyn = [1. for f in freqs]
Asyn = [1. for f in freqs]
Ndst = ['DUST' for f in freqs]
Tdst = self._dst_temp(freqs)
Edst = [1. for f in freqs]
Adst = [1. for f in freqs]
if site != 'SPACE':
return [[Ncmb, Nsyn, Ndst, Natm],
[Acmb, Asyn, Adst, Aatm],
[Ecmb, Esyn, Edst, Eatm],
[Tcmb, Tsyn, Tdst, Tatm]]
else:
return [[Ncmb, Nsyn, Ndst],
[Acmb, Asyn, Adst],
[Ecmb, Esyn, Edst],
[Tcmb, Tsyn, Tdst]]
# Do not include foregrounds
else:
if site != 'SPACE':
return [[Ncmb, Natm],
[Acmb, Aatm],
[Ecmb, Eatm],
[Tcmb, Tatm]]
else:
return [[Ncmb],
[Acmb],
[Ecmb],
[Tcmb]]
def pwv_sample(self):
""" Sample the PWV distribution """
samp = self.tel.pwv_sample()
# Minimum allowed PWV is 0 mm
if samp < self._min_pwv:
self._log.log('Cannot have PWV %.1f < %.1f. Using %.1f instead'
% (samp, self._min_pwv, self._min_pwv))
return self._min_pwv
# Maximum allowed PWV is 8 mm
elif samp > self._max_pwv:
self._log.log('Cannot have PWV %.1f > %.1f. Using %.1f instead'
% (samp, self._max_pwv, self._max_pwv))
return self._max_pwv
else:
return samp
# ***** Helper Methods *****
def _hdf5_select(self, pwv, elev):
""" Retrieve ATM spectrum from HDF5 file """
# Two-level dictionary structure in the HDF5 file
site = self.tel.param("site").lower().capitalize()
# McMurdo need camel casing
if site == "Mcmurdo":
site = "McMurdo"
key = "%d,%d" % (pwv, elev)
with hp.File("%s" % (self._atm_file), "r") as hf:
data = hf[site][key]
freq = data[0]
temp = data[2]
tran = data[3]
return (freq, tran, temp)
def _atm_spectrum(self, pwv, elev, freqs):
""" Atmosphere spectrum given a PWV and elevation """
GHz_to_Hz = 1.e+09
m_to_mm = 1.e+03
mm_to_um = 1.e+03
# Load custom ATM file if present
if self.tel.param("atm_file") is not None:
freq, tran, temp = self._load.atm(self.tel.param("atm_file"))
# Otherwise, select the atmosphere from the HDF5 file
else:
freq, tran, temp = self._hdf5_select(
int(round(pwv * m_to_mm, 1) * mm_to_um),
int(round(elev, 0)))
# Massage arrays
freq = (freq * GHz_to_Hz).flatten().tolist()
temp = np.interp(freqs, freq, temp).flatten().tolist()
tran = np.interp(freqs, freq, tran).flatten().tolist()
return freq, temp, tran
def _syn_temp(self, freqs):
""" Synchrotron physical temperature spectrum """
return self._fg.sync_temp(freqs)
def _dst_temp(self, freqs):
""" Dust spectrum """
return self._fg.dust_temp(freqs)