"""Shared paths, constants and small helpers for the cold_occulters study.""" import os import ssl import time import urllib.parse import urllib.request import numpy as np STUDY = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) DATA = os.path.join(STUDY, "data") FIGS = os.path.join(STUDY, "figures") RAW = os.environ.get("COLDOCC_RAW", "fermi_raw/cold_occulters") for _d in (DATA, FIGS, RAW): os.makedirs(_d, exist_ok=True) OGLE_FTP = "https://www.astrouw.edu.pl/ogle" GAIA_TAP = "https://gea.esac.esa.int/tap-server/tap" VIZIER_TAP = "https://tapvizier.cds.unistra.fr/TAPVizieR/tap" # physical constants (SI) H_PLANCK = 6.62607015e-34 C_LIGHT = 2.99792458e8 K_B = 1.380649e-23 SIGMA_SB = 5.670374419e-8 L_SUN = 3.828e26 R_SUN = 6.957e8 AU = 1.495978707e11 PC = 3.0856775814913673e16 G_NEWT = 6.67430e-11 M_SUN = 1.98847e30 YEAR = 365.25 * 86400.0 KMS_TO_AU_PER_YR = 1e3 * YEAR / AU # 0.21095 au/yr per km/s KMS_TO_PC_PER_YR = 1e3 * YEAR / PC # 1.0227e-6 pc/yr per km/s AU_PC = AU / PC # 4.848e-6 pc # Einstein radius: r_E^2 = (4 G M / c^2) D_l D_ls / D_s ; in au^2 per (Msun kpc) RE2_AU2_PER_MSUN_KPC = 4 * G_NEWT * M_SUN / C_LIGHT ** 2 * (1e3 * PC) / AU ** 2 # 8.144 T1AU = (L_SUN / (4 * np.pi * SIGMA_SB * AU ** 2)) ** 0.25 # 393.6 K: blackbody sphere of 1 au radiating 1 Lsun def radius_au(L_lsun, T): """Radius of an opaque blackbody sphere re-radiating L at temperature T.""" return np.sqrt(L_lsun) * (T1AU / np.asarray(T, float)) ** 2 def _ctx(): return ssl.create_default_context() def http_get(url, timeout=300, data=None, retries=3): last = None for k in range(retries): try: req = urllib.request.Request(url, data=data, headers={"User-Agent": "fermi-cold-occulters/1.0"}) with urllib.request.urlopen(req, timeout=timeout, context=_ctx()) as r: return r.read() except Exception as e: # noqa: BLE001 (retry on any network error) last = e time.sleep(3 * (k + 1)) raise last def fetch(url, path, timeout=900, min_bytes=1): """Download url to path unless a non-empty copy is cached.""" if os.path.exists(path) and os.path.getsize(path) >= min_bytes: return path os.makedirs(os.path.dirname(path), exist_ok=True) blob = http_get(url, timeout=timeout) with open(path + ".part", "wb") as f: f.write(blob) os.replace(path + ".part", path) return path def tap_sync(base, adql, fmt="csv", timeout=600): data = urllib.parse.urlencode({"REQUEST": "doQuery", "LANG": "ADQL", "FORMAT": fmt, "QUERY": adql}).encode() return http_get(base + "/sync", timeout=timeout, data=data) def poisson_ul(n_obs, cl=0.95): """Classical one-sided Poisson upper limit on the mean for n_obs observed events, no background.""" from scipy.stats import chi2 return 0.5 * chi2.ppf(cl, 2 * (n_obs + 1))