diff --git a/frb/builds/build_hosts.py b/frb/builds/build_hosts.py index 4e27e1a2..fde175f6 100644 --- a/frb/builds/build_hosts.py +++ b/frb/builds/build_hosts.py @@ -447,6 +447,7 @@ def run(host_input:pandas.core.series.Series, project_list, ref_list, '_galight.json', prefix=file_root+'_'+host_input.Galfit_filter) if not found_galfit: + embed(header='Check 450') raise IOError(f"Galfit file with filter {host_input.Galfit_filter} not found!") print(f"Galfit analysis slurped in via: {galfit_file}") Host.parse_galfit(galfit_file) @@ -487,6 +488,18 @@ def run(host_input:pandas.core.series.Series, hikey = key.replace('lo', 'up') Host.derived[hikey] = float(lit_tbl[hikey].data[0]) + # Add PATH + hosts_file = importlib_resources.files('frb.data.Galaxies')/'public_hosts.csv' + hosts_df = pandas.read_csv(hosts_file, index_col=False) + + indx = file_root == 'HG'+hosts_df.FRB.values + if np.any(indx): + # Get the row + host_row = hosts_df.loc[indx].iloc[0] + # Set P_Ox + Host.path['P_Ox'] = host_row.P_Ox + # Reference would go here + # Vet all assert Host.vet_all() diff --git a/frb/builds/build_hosts_web.py b/frb/builds/build_hosts_web.py new file mode 100644 index 00000000..025acaa9 --- /dev/null +++ b/frb/builds/build_hosts_web.py @@ -0,0 +1,496 @@ +#!/usr/bin/env python + +""" +Build FRB host JSON files for the web frontend. + +This script reads the generic host template: + + frb/data/Galaxies/frb_host_template.json + +and, for each FRB with an existing host file + + FRBxxxxxx_host.json + +plus the corresponding FRB file + + frb/data/FRBs/FRBxxxxxx.json + +produces a web-ready version + + FRBxxxxxx_host_web.json + +in the same FRB subdirectory. + +Usage: + + python frb/builds/build_hosts_web.py FRBs --frb FRB20180916B + python frb/builds/build_hosts_web.py FRBs # build all +""" + +import argparse +import copy +import json +from pathlib import Path + +from pkg_resources import resource_filename + + +# -------------------------------------------------------------------- +# Locate BASE_DIRs and template robustly +# -------------------------------------------------------------------- +def find_galaxies_base_dir() -> Path: + """ + Locate frb/data/Galaxies/frb_host_template.json. + """ + here = Path(__file__).resolve() + + # 1) Look for a repo-style layout relative to this file + for parent in [here] + list(here.parents): + candidate_base = parent / "frb" / "data" / "Galaxies" + candidate_template = candidate_base / "frb_host_template.json" + if candidate_template.exists(): + return candidate_base + + # 2) Fall back to package data + try: + pkg_dir = Path(resource_filename("frb", "data/Galaxies")).resolve() + if (pkg_dir / "frb_host_template.json").exists(): + return pkg_dir + except Exception: + pass + + raise FileNotFoundError( + "Could not find frb_host_template.json either in a repo-style " + "checkout or in the installed 'frb' package." + ) + + +def find_frbs_base_dir() -> Path: + """ + Locate frb/data/FRBs. + """ + here = Path(__file__).resolve() + + # 1) Repo-style layout + for parent in [here] + list(here.parents): + candidate_base = parent / "frb" / "data" / "FRBs" + if candidate_base.exists(): + return candidate_base + + # 2) Installed package data + try: + pkg_dir = Path(resource_filename("frb", "data/FRBs")).resolve() + if pkg_dir.exists(): + return pkg_dir + except Exception: + pass + + raise FileNotFoundError( + "Could not find frb/data/FRBs either in a repo-style " + "checkout or in the installed 'frb' package." + ) + + +# -------------------------------------------------------------------- +# JSON helpers +# -------------------------------------------------------------------- +def load_json(path: Path): + with path.open("r", encoding="utf-8") as f: + return json.load(f) + + +def save_json(path: Path, data): + path.parent.mkdir(parents=True, exist_ok=True) + # Don't sort keys so metadata/data grouping stays as we construct it. + with path.open("w", encoding="utf-8") as f: + json.dump(data, f, indent=2) + + +# -------------------------------------------------------------------- +# Utility: recursive key search +# -------------------------------------------------------------------- +def find_key_recursive(data, key): + """ + Recursively search for `key` anywhere inside `data` (dicts/lists). + + Returns the first value found, or None if not present. + """ + if isinstance(data, dict): + if key in data: + return data[key] + for v in data.values(): + found = find_key_recursive(v, key) + if found is not None: + return found + elif isinstance(data, list): + for item in data: + found = find_key_recursive(item, key) + if found is not None: + return found + return None + + +def get_scalar_from_source(source_data, key): + """ + Find `key` in source_data (recursively) and return a scalar: + + - If the value is a dict with a 'value' field, return that. + - Otherwise return the raw value. + - If not found, return None. + """ + if not source_data: + return None + raw_val = find_key_recursive(source_data, key) + if raw_val is None: + return None + if isinstance(raw_val, dict) and "value" in raw_val: + return raw_val["value"] + return raw_val + +def override_in_tree(tree, key, new_val): + """ + Recursively walk `tree` (dicts/lists) and replace any occurrence + of `key` with `new_val`. Does not create new keys, only overrides + existing ones. + """ + if isinstance(tree, dict): + if key in tree: + tree[key] = new_val + for v in tree.values(): + override_in_tree(v, key, new_val) + elif isinstance(tree, list): + for item in tree: + override_in_tree(item, key, new_val) + + +# -------------------------------------------------------------------- +# Data extraction based on template (metadata) +# -------------------------------------------------------------------- +def extract_data_by_template(template, source_data): + """ + Build a 'data' dict from a metadata template and a source_data dict + (merged host + FRB JSON). + + Rules: + * Only include fields that have corresponding metadata entries + (i.e., keys present in the template tree). + * 'metadata' controls the shape (grouping) of the output, but we + do NOT try to mirror its internal keys (description, unit, etc.). + * For a leaf metadata block (e.g., template['DM'] is a dict whose + values are not all dicts), we treat the key ('DM') as the data + key and grab the value of that key from source_data, searching + recursively if needed. + * If the found value is a dict with a 'value' key (e.g. {"unit":..., "value":...}), + we store only the numeric part (val['value']) in the data block. + """ + if not isinstance(template, dict): + return {} + + result = {} + + for key, t_val in template.items(): + # Case 1: metadata value is a dict + if isinstance(t_val, dict): + # Heuristic: if ANY of the values in t_val are non-dicts, + # then this is a *leaf metadata block* describing a single + # quantity (e.g., DM, ra_frb, etc.), not a pure grouping node. + any_non_dict = any(not isinstance(v, dict) for v in t_val.values()) + + if any_non_dict: + # Leaf metadata: look for this key anywhere in source_data + raw_val = find_key_recursive(source_data, key) + if raw_val is not None: + # If the value is a dict with a 'value' field, keep only that + if isinstance(raw_val, dict) and "value" in raw_val: + result[key] = raw_val["value"] + else: + result[key] = raw_val + else: + # Grouping node: recurse into children, but still search + # over the full source_data, not restricted by key path. + sub_data = extract_data_by_template(t_val, source_data) + if sub_data: + result[key] = sub_data + + # Case 2: metadata value is NOT a dict (string, number, etc.) + else: + # Treat this as metadata for a single quantity with name=key. + raw_val = find_key_recursive(source_data, key) + if raw_val is not None: + if isinstance(raw_val, dict) and "value" in raw_val: + result[key] = raw_val["value"] + else: + result[key] = raw_val + + return result + + + +# -------------------------------------------------------------------- +# Core build logic +# -------------------------------------------------------------------- +def build_single( + frb_name: str, + galaxies_base: Path, + frbs_base: Path, + template: dict, + overwrite: bool = False, +): + """ + Build FRB<...>_host_web.json for a single FRB. + + Parameters + ---------- + frb_name : str + Name like 'FRB20180916B'. + galaxies_base : Path + Path to frb/data/Galaxies. + frbs_base : Path + Path to frb/data/FRBs. + template : dict + Parsed template JSON (treated entirely as metadata). + overwrite : bool + If True, overwrite existing *_host_web.json. + """ + # Galaxy/host JSON: e.g. FRB20180916B_host.json + host_matches = list(galaxies_base.rglob(f"{frb_name}_host.json")) + if len(host_matches) == 0: + print(f"[WARN] No *_host.json found for {frb_name}") + return + + host_path = host_matches[0] + + # FRB JSON: e.g. frb/data/FRBs/FRB20180916B.json + frb_json_path = frbs_base / f"{frb_name}.json" + + print(f"[INFO] Building web host JSON for {frb_name}") + print(f"[INFO] Host JSON: {host_path}") + if frb_json_path.exists(): + print(f"[INFO] FRB JSON: {frb_json_path}") + else: + print(f"[WARN] No FRB JSON found at {frb_json_path}; using host JSON only") + + # Load host data + host_data = load_json(host_path) + + # Load FRB data if available + frb_data = {} + if frb_json_path.exists(): + frb_data = load_json(frb_json_path) + + # Merge data sources: + # start with host_data, then overlay frb_data so FRB entries + # (e.g., DM, ra_frb) are available and can override if needed. + merged_data = {} + merged_data.update(host_data) + merged_data.update(frb_data) + + # Metadata: just a copy of the template (we do NOT overwrite it with values) + metadata = copy.deepcopy(template) + + # Data: only fields with corresponding metadata, using merged_data. + # If something like DM is nested, find_key_recursive() will locate it. + data = extract_data_by_template(template, merged_data) + + # ------------------------------------------------------------------ + # Enforce specific sources for coordinates: + # - 'ra' and 'dec' from host_data (galaxy/host file) + # - 'ra_frb' and 'dec_frb' from frb_data (FRB file) + # ------------------------------------------------------------------ + + # Host (galaxy) coordinates + ra_host = get_scalar_from_source(host_data, "ra") + dec_host = get_scalar_from_source(host_data, "dec") + if ra_host is not None: + data["ra"] = ra_host + if dec_host is not None: + data["dec"] = dec_host + + # FRB coordinates: primary keys 'ra_frb' / 'dec_frb'; + # if for some reason the FRB JSON only has 'ra'/'dec', fall back to those. + ra_frb = get_scalar_from_source(frb_data, "ra_frb") + if ra_frb is None: + ra_frb = get_scalar_from_source(frb_data, "ra") + dec_frb = get_scalar_from_source(frb_data, "dec_frb") + if dec_frb is None: + dec_frb = get_scalar_from_source(frb_data, "dec") + + if ra_frb is not None: + data["ra_frb"] = ra_frb + if dec_frb is not None: + data["dec_frb"] = dec_frb + + # ------------------------------------------------------------------ + # eellipse: 'a' and 'b' from FRB JSON, convert deg -> arcsec + # ------------------------------------------------------------------ + eellipse_src = frb_data.get("eellipse", {}) + if isinstance(eellipse_src, dict): + # Values in FRB JSON are assumed to be in degrees + a_deg = get_scalar_from_source(eellipse_src, "a") + b_deg = get_scalar_from_source(eellipse_src, "b") + + # Convert to arcseconds + a_arcsec = a_deg * 3600.0 if a_deg is not None else None + b_arcsec = b_deg * 3600.0 if b_deg is not None else None + + if a_arcsec is not None or b_arcsec is not None: + # Ensure we have an 'eellipse' dict in the data block + if "eellipse" not in data or not isinstance(data["eellipse"], dict): + data["eellipse"] = {} + + if a_arcsec is not None: + data["eellipse"]["a"] = a_arcsec + if b_arcsec is not None: + data["eellipse"]["b"] = b_arcsec + + # ------------------------------------------------------------------ + # r-band magnitude: + # 1) Prefer DECaL_r / DECaL_r_err + # 2) Otherwise Pan-STARRS_r / Pan-STARRS_r_err + # r_mag_ref = "DECaL" or "Pan-STARRS" accordingly. + # ------------------------------------------------------------------ + r_mag = None + r_mag_err = None + r_mag_ref = None + + # First try DECaL + decal_r = get_scalar_from_source(host_data, "DECaL_r") + decal_r_err = get_scalar_from_source(host_data, "DECaL_r_err") + + if decal_r is not None: + r_mag = decal_r + r_mag_err = decal_r_err # may be None + r_mag_ref = "DECaL" + else: + # Fallback to Pan-STARRS + ps_r = get_scalar_from_source(host_data, "Pan-STARRS_r") + ps_r_err = get_scalar_from_source(host_data, "Pan-STARRS_r_err") + + if ps_r is not None: + r_mag = ps_r + r_mag_err = ps_r_err + r_mag_ref = "Pan-STARRS" + + # Write into data block if we found something + if r_mag is not None: + data["r_mag"] = r_mag + if r_mag_err is not None: + data["r_mag_err"] = r_mag_err + if r_mag_ref is not None: + data["r_mag_ref"] = r_mag_ref + + # ------------------------------------------------------------------ + # DM_ref, RM_ref, z_ref: + # All three share the SAME reference list from FRB JSON: frb_data["refs"] + # ------------------------------------------------------------------ + frb_refs = frb_data.get("refs", None) + + if isinstance(frb_refs, list): + # Use the same list for all three + data["DM_ref"] = frb_refs + data["RM_ref"] = frb_refs + data["z_ref"] = frb_refs + + + + out_path = host_path.parent / f"{frb_name}_host_web.json" + + if out_path.exists() and not overwrite: + print(f"[INFO] -> Skipping existing file {out_path}") + return + + combined = { + "metadata": metadata, + "data": data, + } + + save_json(out_path, combined) + + print(f"[INFO] -> Wrote {out_path}") + + +def build_all(galaxies_base: Path, frbs_base: Path, template: dict, overwrite: bool = False): + """ + Build *_host_web.json for all FRBs with *_host.json under galaxies_base. + """ + host_files = sorted(galaxies_base.rglob("*_host.json")) + + if not host_files: + print(f"[WARN] No *_host.json files found under {galaxies_base}") + return + + for host_path in host_files: + if host_path.name == "frb_host_template.json": + continue + + frb_name = host_path.stem.replace("_host", "") + build_single(frb_name, galaxies_base, frbs_base, template, overwrite=overwrite) + + +# -------------------------------------------------------------------- +# CLI +# -------------------------------------------------------------------- +def parse_args(options=None): + """ + Parse command-line arguments. + + We mirror the style of frb_build_web by requiring a first positional + argument specifying what to build (here: 'FRBs'). + """ + parser = argparse.ArgumentParser( + description=( + "Build FRB *_host_web.json files with separate 'metadata' and 'data' " + "blocks, where 'data' comes from the merged host+FRB JSON and only " + "contains fields that have corresponding metadata." + ) + ) + + parser.add_argument( + "what", + help="What to build (must be 'FRBs' to mirror frb_build_web).", + choices=["FRBs"], + ) + + parser.add_argument( + "--frb", + type=str, + default=None, + help="Specific FRB name (e.g. FRB20180916B). If omitted, build all.", + ) + + parser.add_argument( + "--overwrite", + action="store_true", + help="Overwrite existing *_host_web.json files.", + ) + + return parser.parse_args(options) + + +def main(options=None): + """ + Entry point for script / console usage. + """ + args = parse_args(options) + + galaxies_base = find_galaxies_base_dir() + frbs_base = find_frbs_base_dir() + template_path = galaxies_base / "frb_host_template.json" + + print(f"[INFO] Using Galaxies directory: {galaxies_base}") + print(f"[INFO] Using FRBs directory: {frbs_base}") + print(f"[INFO] Using template: {template_path}") + + if not template_path.exists(): + raise FileNotFoundError(f"Template JSON not found at {template_path}") + + template = load_json(template_path) + + if args.frb: + build_single(args.frb, galaxies_base, frbs_base, template, overwrite=args.overwrite) + else: + build_all(galaxies_base, frbs_base, template, overwrite=args.overwrite) + + +if __name__ == "__main__": + main() diff --git a/frb/builds/build_path.py b/frb/builds/build_path.py index 897fb601..f18e3550 100644 --- a/frb/builds/build_path.py +++ b/frb/builds/build_path.py @@ -1,4 +1,4 @@ -""" Top-level module to build or re-build the JSON files for +""" Top-level module to run PATH analysis on a list of FRB host galaxies""" from importlib.resources import files @@ -145,6 +145,7 @@ def main(options:str=None, frb:str=None): Args: options (str, optional): [description]. Defaults to None. frb (str, optional): FRB name + If None, will read the public host table and run on those """ # Read public host table if frb is None: @@ -156,10 +157,6 @@ def main(options:str=None, frb:str=None): else: # Generate the list frb_list = frb.split(',') - #ifrb = [FRB.by_name(ifrb) for ifrb in frbs] - #host = ifrb.grab_host() - #frb_list = [ifrb.frb_name] - #host_coords = [host.coord] # Load prior priors = load_std_priors() diff --git a/frb/data/FRBs/FRB20171020A.json b/frb/data/FRBs/FRB20171020A.json new file mode 100644 index 00000000..5058536b --- /dev/null +++ b/frb/data/FRBs/FRB20171020A.json @@ -0,0 +1,30 @@ +{ + "DM": { + "unit": "pc / cm3", + "value": 114.1 + }, + "DMISM": { + "unit": "pc / cm3", + "value": 37.46616994198802 + }, + "DM_err": { + "unit": "pc / cm3", + "value": 0.2 + }, + "FRB": "FRB20171020A", + "cosmo": "Planck18", + "dec": -19.66978611, + "eellipse": { + "a": 600.0, + "b": 600.0, + "cl": 68.0, + "theta": 0.0 + }, + "ra": 333.8272917, + "refs": [ + "Shannon2018", + "Mahony2018" + ], + "repeater": false, + "z": 0.00867 +} \ No newline at end of file diff --git a/frb/data/Galaxies/20121102A/FRB20121102A_host.json b/frb/data/Galaxies/20121102A/FRB20121102A_host.json index 713b8a3a..d4c47525 100644 --- a/frb/data/Galaxies/20121102A/FRB20121102A_host.json +++ b/frb/data/Galaxies/20121102A/FRB20121102A_host.json @@ -4,7 +4,7 @@ "dec": 33.14789805555556, "dec_FRB": 33.14793156, "derived": { - "AV_nebular": -0.14987551477721286, + "AV_nebular": -0.16167399704951668, "AV_old": 0.11, "AV_old_loerr": 0.06, "AV_old_ref": "Gordon2023", @@ -43,7 +43,7 @@ "SFR_SED_loerr": 0.01, "SFR_SED_ref": "Gordon2023", "SFR_SED_uperr": 0.02, - "SFR_nebular": 0.13176802257889894, + "SFR_nebular": 0.13030556188049094, "SFR_photom": 0.002035787096368971, "SFR_photom_err": 0.005813702135185201, "SMStar": 13035903.263379255, @@ -128,64 +128,67 @@ "[OIII] 5007_ref": "Tendulkar2017" }, "offsets": { - "ang_avg": 0.22739060490950594, - "ang_avg_err": 0.015495695605799073, - "ang_best": 0.22687098663780167, - "ang_best_err": 0.015504405356409817, - "physical": 0.7510479525383023, - "physical_err": 0.05132675654488136 + "ang_avg": 0.22739060492236665, + "ang_avg_err": 0.015495695605786343, + "ang_best": 0.2268709866506913, + "ang_best_err": 0.015504405356396151, + "physical": 0.7510479525809729, + "physical_err": 0.05132675654483613 + }, + "path": { + "P_Ox": 1.0 }, "photom": { "EBV": 0.6798, - "GMOS_N_g": 23.329214463965435, + "GMOS_N_g": 23.37173941487555, "GMOS_N_g_err": 0.12, - "GMOS_N_g_flux": 0.0016916643951493313, - "GMOS_N_g_flux_err": 0.00019769339537188934, + "GMOS_N_g_flux": 0.0016266878705262977, + "GMOS_N_g_flux_err": 0.0001797881875981479, "GMOS_N_g_ref": "Bassa2017", - "GMOS_N_i": 23.538006459003054, + "GMOS_N_i": 23.470412432617216, "GMOS_N_i_err": 0.09, - "GMOS_N_i_flux": 0.0013957171362773684, - "GMOS_N_i_flux_err": 0.00012062572389579638, + "GMOS_N_i_flux": 0.0014853712757988892, + "GMOS_N_i_flux_err": 0.0001231269752597785, "GMOS_N_i_ref": "Bassa2017", - "GMOS_N_r": 23.730606922036774, + "GMOS_N_r": 23.709083581668537, "GMOS_N_r_err": 0.14, - "GMOS_N_r_flux": 0.0011688458130394571, - "GMOS_N_r_flux_err": 0.0001608650767947018, + "GMOS_N_r_flux": 0.0011922478875840414, + "GMOS_N_r_flux_err": 0.00015373412393386067, "GMOS_N_r_ref": "Bassa2017", - "GMOS_N_z": 23.4892120964504, + "GMOS_N_z": 23.4253450166459, "GMOS_N_z_err": 0.13, - "GMOS_N_z_flux": 0.0014598732663756548, - "GMOS_N_z_flux_err": 0.0001856921658216167, + "GMOS_N_z_flux": 0.0015483245060917746, + "GMOS_N_z_flux_err": 0.00018538774419590303, "GMOS_N_z_ref": "Bassa2017", - "MMIRS_J": 23.508388643967457, + "MMIRS_J": 23.457063524590936, "MMIRS_J_err": 0.5085084168, - "MMIRS_J_flux": 0.0014343149882309072, - "MMIRS_J_flux_err": 0.000856805365110051, + "MMIRS_J_flux": 0.001503746343972243, + "MMIRS_J_flux_err": 0.0007042849536480726, "MMIRS_J_ref": "Gordon2023", - "MMIRS_K": 23.733094608431887, + "MMIRS_K": 23.696543121968844, "MMIRS_K_err": 0.5892336803, - "MMIRS_K_flux": 0.0011661707680265037, - "MMIRS_K_flux_err": 0.0008404052761181673, + "MMIRS_K_flux": 0.0012060984115958677, + "MMIRS_K_flux_err": 0.000654554764549769, "MMIRS_K_ref": "Gordon2023", - "Spitzer_3.6": 23.79419590236575, + "Spitzer_3.6": 23.775211690225486, "Spitzer_3.6_err": 0.20028143582916946, - "Spitzer_3.6_flux": 0.0011023553986591553, - "Spitzer_3.6_flux_err": 0.00022331087550180273, + "Spitzer_3.6_flux": 0.0011217996965718052, + "Spitzer_3.6_flux_err": 0.00020693392461033295, "Spitzer_3.6_ref": "Bassa2017", - "Spitzer_4.5": 24.717447113669405, + "Spitzer_4.5": 24.70006282754616, "Spitzer_4.5_err": 999.0, - "Spitzer_4.5_flux": 0.00047100026212537106, + "Spitzer_4.5_flux": 0.0004786023902795443, "Spitzer_4.5_flux_err": -99.0, "Spitzer_4.5_ref": "Bassa2017", - "WFC3_F110W": 23.081347094514243, + "WFC3_F110W": 23.02755754490239, "WFC3_F110W_err": 0.012, - "WFC3_F110W_flux": 0.002125500230932293, - "WFC3_F110W_flux_err": 2.3622197326669726e-05, + "WFC3_F110W_flux": 0.002233453815479198, + "WFC3_F110W_flux_err": 2.4685043814782763e-05, "WFC3_F110W_ref": "Bassa2017", - "WFC3_F160W": 22.962270841633313, + "WFC3_F160W": 22.91542642756748, "WFC3_F160W_err": 0.03, - "WFC3_F160W_flux": 0.0023718742357549238, - "WFC3_F160W_flux_err": 6.645113570741157e-05, + "WFC3_F160W_flux": 0.0024764491979752637, + "WFC3_F160W_flux_err": 6.842682008177884e-05, "WFC3_F160W_ref": "Bassa2017" }, "positional_error": { diff --git a/frb/data/Galaxies/20171020/FRB20171020_host.json b/frb/data/Galaxies/20171020A/FRB20171020A_host.json similarity index 57% rename from frb/data/Galaxies/20171020/FRB20171020_host.json rename to frb/data/Galaxies/20171020A/FRB20171020A_host.json index 5a26cd6c..d3527b20 100644 --- a/frb/data/Galaxies/20171020/FRB20171020_host.json +++ b/frb/data/Galaxies/20171020A/FRB20171020A_host.json @@ -1,8 +1,8 @@ { - "FRB": "FRB20171020", + "FRB": "FRB20171020A", "cosmo": "Planck18", "dec": -19.586, - "dec_FRB": -19.66978611111111, + "dec_FRB": -19.66978611, "derived": { "M_r": -17.9, "M_r_err": 0.1, @@ -15,15 +15,18 @@ "SFR_nebular_ref": "Mahony2018" }, "offsets": { - "ang_avg": 802.3114040934199, - "ang_avg_err": 417.47553833878595, - "ang_best": 313.04950185295974, - "ang_best_err": 643.1664124433216, - "physical": 57.68533434653865, - "physical_err": 118.5156638891039 + "ang_avg": 802.3113933098031, + "ang_avg_err": 417.4755334899458, + "ang_best": 313.04946774943465, + "ang_best_err": 643.1664270356115, + "physical": 57.685328062314476, + "physical_err": 118.51566657801129 + }, + "path": { + "P_Ox": 0.0 }, "ra": 333.8519999999999, - "ra_FRB": 333.8272916666666, + "ra_FRB": 333.8272917, "redshift": { "z": 0.00867, "z_FRB": 0.00867, diff --git a/frb/data/Galaxies/Literature/muller2025_photom.csv b/frb/data/Galaxies/Literature/muller2025_photom.csv new file mode 100644 index 00000000..03dd7c6e --- /dev/null +++ b/frb/data/Galaxies/Literature/muller2025_photom.csv @@ -0,0 +1,2 @@ +Name,ra,dec,WFC3_F110W,WFC3_F110W_err +HG20180916B,29.50121667,65.71474667,16.178,0.005 \ No newline at end of file diff --git a/frb/data/Galaxies/frb_host_template.json b/frb/data/Galaxies/frb_host_template.json new file mode 100644 index 00000000..d4bcc2b7 --- /dev/null +++ b/frb/data/Galaxies/frb_host_template.json @@ -0,0 +1,60 @@ +{ + "FRB": {"label": "FRB", "desc": "FRB TNS Name", "datatype": "string", "unit": "", "ucd": "meta.id;meta.main", "nullvalue": ""}, + "galID": {"label": "galID", "desc": "Host ID", "datatype": "string", "unit": "", "ucd": "meta.id", "nullvalue": ""}, + "ra": {"label": "RA", "desc": "Host right ascension", "datatype": "float", "unit": "deg", "ucd": "pos.eq.ra;meta.main", "nullvalue": null}, + "dec": {"label": "Dec", "desc": "Host declination", "datatype": "float", "unit": "deg", "ucd": "pos.eq.dec;meta.main", "nullvalue": null}, + + "ra_frb": {"label": "RA_frb", "desc": "FRB right ascension", "datatype": "float", "unit": "deg", "ucd": "pos.eq.ra", "nullvalue": null}, + "dec_frb": {"label": "Dec_frb", "desc": "FRB declination", "datatype": "float", "unit": "deg", "ucd": "pos.eq.dec", "nullvalue": null}, + + "eellipse": { + "a": {"label": "a", "desc": "FRB localization semi-major axis", "datatype": "float", "unit": "arcsec", "ucd": "phys.angSize", "nullvalue": null}, + "b": {"label": "b", "desc": "FRB localization semi-minor axis", "datatype": "float", "unit": "arcsec", "ucd": "phys.angSize", "nullvalue": null}, + "theta": {"label": "PA", "desc": "FRB localization position angle", "datatype": "float", "unit": "deg", "ucd": "pos.posAng", "nullvalue": null} + }, + + "DM": {"label": "DM", "desc": "Observed FRB dispersion measure", "datatype": "float", "unit": "pc/cm3", "ucd": "phys.dispMeasure"}, + "DM_err": {"label": "DM_err", "desc": "Error on observed FRB dispersion measure", "datatype": "float", "unit": "pc/cm3", "ucd": "stat.error;phys.dispMeasure", "nullvalue": null}, + "DM_ref": {"label": "DM_ref", "desc": "Reference for FRB dispersion measure", "datatype": "string", "unit": "", "ucd": "meta.ref", "nullvalue": ""}, + + "RM": {"label": "RM", "desc": "Observed FRB rotation measure", "datatype": "float", "unit": "rad/m2", "ucd": "phys.polarization.rotMeasure", "nullvalue": null}, + "RM_err": {"label": "RM_err", "desc": "Error on observed FRB rotation measure", "datatype": "float", "unit": "rad/m2", "ucd": "stat.error;phys.polarization.rotMeasure", "nullvalue": null}, + "RM_ref": {"label": "RM_ref", "desc": "Reference for FRB rotation measure", "datatype": "string", "unit": "", "ucd": "meta.ref", "nullvalue": ""}, + + "offsets": { + "ang_avg": {"label": "Ang_offset", "desc": "FRB angular offset from host galaxy centre", "datatype": "float", "unit": "arcsec", "ucd": "phys.angSize;stat.mean", "nullvalue": null}, + "ang_avg_err": {"label": "Ang_offset_err", "desc": "Error on FRB angular offset from host galaxy centre", "datatype": "float", "unit": "arcsec", "ucd": "stat.error;phys.angSize", "nullvalue": null}, + "physical": {"label": "Phys_offset", "desc": "FRB physical offset from host galaxy", "datatype": "float", "unit": "kpc", "ucd": "pos.distance;stat.mean", "nullvalue": null}, + "physical_err": {"label": "Phys_offset_err", "desc": "Error on FRB physical offset from host galaxy", "datatype": "float", "unit": "kpc", "ucd": "stat.error;pos.distance", "nullvalue": null}, + "offset_ref": {"label": "Offset_ref", "desc": "Reference for FRB offset", "datatype": "string", "unit": "", "ucd": "meta.ref", "nullvalue": ""} + }, + + "path": { + "P_Ox": {"label": "P_Ox", "desc": "PATH association probability for host galaxy", "datatype": "float", "unit": "", "ucd": "stat.probability", "nullvalue": null}, + "P_cc" : {"label": "P_cc", "desc": "Chance coincidence probability for host galaxy", "datatype": "float", "unit": "", "ucd": "stat.probability", "nullvalue": null}, + "P_ref": {"label": "P_ref", "desc": "Reference for FRB host association probability", "datatype": "string", "unit": "", "ucd": "meta.ref", "nullvalue": ""} + }, + + "repeater": {"label": "Repeater", "desc": "True/False if FRB is a repeater or not", "datatype": "bool", "unit": "", "ucd": "meta.code.status", "nullvalue": ""}, + "z": {"label": "z", "desc": "FRB host galaxy redshift", "datatype": "float", "unit": "", "ucd": "src.redshift", "nullvalue": null}, + "z_err": {"label": "z_err", "desc": "Error on FRB host galaxy redshift", "datatype": "float", "unit": "", "ucd": "stat.error;src.redshift", "nullvalue": null}, + "z_ref": {"label": "z_ref", "desc": "Reference for FRB host galaxy redshift", "datatype": "string", "unit": "", "ucd": "meta.ref", "nullvalue": ""}, + + "photom": { + "r_mag": {"label": "r_mag", "desc": "Apparent r-band magnitude of host galaxy", "datatype": "float", "unit": "AB", "ucd": "phot.mag;em.opt.R", "nullvalue": null}, + "r_mag_err": {"label": "r_mag_err", "desc": "Error on r-band magnitude", "datatype": "float", "unit": "", "ucd": "stat.error;phot.mag;em.opt.R", "nullvalue": null}, + "r_mag_ref": {"label": "r_mag_ref", "desc": "Reference for r-band magnitude", "datatype": "string", "unit": "", "ucd": "meta.ref", "nullvalue": ""} + }, + + "derived": { + "Mstar": {"label": "Mstar", "desc": "Host galaxy stellar mass", "datatype": "float", "unit": "M_sun", "ucd": "phys.mass", "nullvalue": null}, + "Mstar_err": {"label": "Mstar_err", "desc": "Error on host galaxy stellar mass", "datatype": "float", "unit": "M_sun", "ucd": "stat.error;phys.mass", "nullvalue": null}, + "Mstar_ref": {"label": "Mstar_ref", "desc": "Reference for host galaxy stellar mass", "datatype": "string", "unit": "", "ucd": "meta.ref", "nullvalue": ""}, + "SFR_nebular": {"label": "SFR_nebular", "desc": "Host galaxy SFR from Halpha", "datatype": "float", "unit": "M_sun/yr", "ucd": "phys.SFR", "nullvalue": null}, + "SFR_nebular_err": {"label": "SFR_nebular_err", "desc": "Error on host galaxy SFR from Halpha", "datatype": "float", "unit": "M_sun/yr", "ucd": "stat.error;phys.SFR", "nullvalue": null}, + "SFR_nebular_ref": {"label": "SFR_nebular_ref", "desc": "Reference for host galaxy SFR from Halpha", "datatype": "string", "unit": "", "ucd": "meta.ref", "nullvalue": ""}, + "SFR_SED": {"label": "SFR_SED", "desc": "Host galaxy SFR from SED", "datatype": "float", "unit": "M_sun/yr", "ucd": "phys.SFR", "nullvalue": null}, + "SFR_SED_err": {"label": "SFR_SED_err", "desc": "Error on host galaxy SFR from SED", "datatype": "float", "unit": "M_sun/yr", "ucd": "stat.error;phys.SFR", "nullvalue": null}, + "SFR_SED_ref": {"label": "SFR_SED_ref", "desc": "Reference for host galaxy SFR from SED", "datatype": "string", "unit": "", "ucd": "meta.ref", "nullvalue": ""} + } +} diff --git a/frb/data/Galaxies/host_master_template.json b/frb/data/Galaxies/host_master_template.json new file mode 100644 index 00000000..ba07d2f1 --- /dev/null +++ b/frb/data/Galaxies/host_master_template.json @@ -0,0 +1,248 @@ +{ + "FRB": { + "name": "", + "PA": , + "PA_err": , + "b/a": , + "b/a_err": , + "dec": , + "dec_err": , + "n": , + "n_err": , + "ra": , + "ra_err": , + "DM": , + "DM_loerr": , + "DM_uperr": + }, + + "cosmo": "Planck18", + "GalID": "", + "P_host": , + "ref_P_host": "", + "dec": , + "ref_coord": "", + "offsets": { + "ang_avg": , + "ang_avg_err": , + "ang_best": , + "ang_best_err": , + "physical": , + "physical_err": + }, + + "morphology": { + "PA": , + "PA_err": , + "b/a": , + "b/a_err": , + "dec": , + "dec_err": , + "n": , + "n_err": , + "ra": , + "ra_err": , + "reff_ang": , + "reff_ang_err": , + "reff_kpc": , + "reff_kpc_err": , + "ref_morph": "" + }, + + "photom": { + "DECaL_g": , + "DECaL_g_err": , + "DECaL_g_flux": , + "DECaL_g_flux_err": , + "DECaL_r": , + "DECaL_r_err": , + "DECaL_r_flux": , + "DECaL_r_flux_err": , + "DECaL_z": , + "DECaL_z_err": , + "DECaL_z_flux": , + "DECaL_z_flux_err": , + "DELVE_g": , + "DELVE_g_err": , + "DELVE_g_flux": , + "DELVE_g_flux_err": , + "DELVE_i": , + "DELVE_i_err": , + "DELVE_i_flux": , + "DELVE_i_flux_err": , + "DELVE_r": , + "DELVE_r_err": , + "DELVE_r_flux": , + "DELVE_r_flux_err": , + "DELVE_z": , + "DELVE_z_err": , + "DELVE_z_flux": , + "DELVE_z_flux_err": , + "DES_Y": , + "DES_Y_err": , + "DES_Y_flux":, + "DES_Y_flux_err": , + "DES_g": , + "DES_g_err": , + "DES_g_flux": , + "DES_g_flux_err": , + "DES_i": , + "DES_i_err": , + "DES_i_flux": , + "DES_i_flux_err": , + "DES_r": , + "DES_r_err": , + "DES_r_flux": , + "DES_r_flux_err": , + "DES_z": , + "DES_z_err": , + "DES_z_flux": , + "DES_z_flux_err": , + "EBV": , + "Pan-STARRS_g": , + "Pan-STARRS_g_err": , + "Pan-STARRS_g_flux": , + "Pan-STARRS_g_flux_err": , + "Pan-STARRS_i": , + "Pan-STARRS_i_err": , + "Pan-STARRS_i_flux": , + "Pan-STARRS_i_flux_err": , + "Pan-STARRS_r": , + "Pan-STARRS_r_err": , + "Pan-STARRS_r_flux": , + "Pan-STARRS_r_flux_err": , + "Pan-STARRS_y": , + "Pan-STARRS_y_err": , + "Pan-STARRS_y_flux": , + "Pan-STARRS_y_flux_err": , + "Pan-STARRS_z": , + "Pan-STARRS_z_err": , + "Pan-STARRS_z_flux": , + "Pan-STARRS_z_flux_err": , + "TMASS_J": , + "TMASS_J_err": , + "TMASS_H": , + "TMASS_H_err": , + "TMASS_K": , + "TMASS_K_err": , + "WISE_W1": , + "WISE_W1_err": , + "WISE_W1_flux": , + "WISE_W1_flux_err": , + "WISE_W2": , + "WISE_W2_err": , + "WISE_W2_flux": , + "WISE_W2_flux_err": , + "WISE_W3": , + "WISE_W3_err": , + "WISE_W3_flux": , + "WISE_W3_flux_err": , + "WISE_W4": , + "WISE_W4_err": , + "WISE_W4_flux": , + "WISE_W4_flux_err": + }, + + "neb_lines": { + "Halpha": , + "Halpha_err": , + "Halpha_ref": "", + "Hbeta": , + "Hbeta_err": , + "Hbeta_ref": "", + "Hgamma": , + "Hgamma_err": , + "Hgamma_ref": , + "[NII] 6549": , + "[NII] 6549_err": , + "[NII] 6549_ref": "", + "[NII] 6584": , + "[NII] 6584_err": , + "[NII] 6584_ref": "", + "[OIII] 5007": , + "[OIII] 5007_err": , + "[OIII] 5007_ref": "", + "[OIII] 4959": , + "[OIII] 4959_err": , + "[OIII] 4959_ref": "", + "[OII] 3726": , + "[OII] 3726_err": , + "[OII] 3726_ref": , + "[OII] 3729": , + "[OII] 3729_err":, + "[OII] 3729_ref": , + "[SII] 6717": , + "[SII] 6717_err":, + "[SII] 6717_ref": , + "[SII] 6731": , + "[SII] 6731_err":, + "[SII] 6731_ref": + }, + + "positional_error": { + "dec_astrometric": , + "dec_source": , + "ra_astrometric": , + "ra_source": + }, + + "derived": { + "AV_old": , + "AV_old_loerr": , + "AV_old_ref": "", + "AV_old_uperr":, + "AV_young": , + "AV_young_loerr": , + "AV_young_ref": "", + "AV_young_uperr": , + "Mstar": , + "Mstar_loerr": , + "Mstar_ref": "", + "Mstar_uperr": , + "Mtotal": , + "Mtotal_loerr": , + "Mtotal_ref": "", + "Mtotal_uperr": , + "SFR_SED": , + "SFR_SED_loerr": , + "SFR_SED_ref": "", + "SFR_SED_uperr": , + "Z_gas": , + "Z_gas_loerr": , + "Z_gas_ref": "", + "Z_gas_uperr": , + "Z_stellar": , + "Z_stellar_loerr": , + "Z_stellar_ref": "", + "Z_stellar_uperr": , + "age_mass": , + "age_mass_loerr": , + "age_mass_ref": "", + "age_mass_uperr": , + "agn_tau": , + "agn_tau_loerr": , + "agn_tau_ref": "", + "agn_tau_uperr": , + "f_AGN": , + "f_AGN_loerr": , + "f_AGN_ref": "", + "f_AGN_uperr": , + "lg_sSFR": , + "lg_sSFR_loerr": , + "lg_sSFR_ref": "", + "lg_sSFR_uperr": , + "z_SED": , + "z_SED_loerr": , + "z_SED_ref": "", + "z_SED_uperr": + }, + + "ra": , + "redshift": { + "z": , + "z_FRB":, + "z_spec": , + "z_spec_err": , + "ref_z_spec": "" + } +} diff --git a/frb/data/Galaxies/public_hosts.csv b/frb/data/Galaxies/public_hosts.csv index f049e5bc..deb41bf7 100644 --- a/frb/data/Galaxies/public_hosts.csv +++ b/frb/data/Galaxies/public_hosts.csv @@ -1,6 +1,6 @@ FRB,Coord,P_Ox,z,Projects,References,CIGALE_photom,Spectrum,ppxf_cuts,Galfit_filter,Bad_EM_lines,Reviewer,Comments 20121102A,05h31m58.686s +33d08m52.433s,1.00E+00,0.19273,"Repeater,F4,F4","Bassa2017,mannings2021,gordon2023",,GMOS-N,,WFC3_F160W,,SS, -20171020,22h15m24.48s -19d35m09.6s,0,0.00867,CRAFT,Mahony2018,,,,,,X, +20171020A,22h15m24.48s -19d35m09.6s,0,0.00867,CRAFT,Mahony2018,,,,,,X, 20180301A,06h12m54.4392s +04d40m13.717s,0.9999999977,0.3305,"Realfast,F4","Bhandari2021,gordon2023",,DEIMOS,"3000., 5000.;6675., 6725.;7150., 7300.;7580, 7750.",,,CRM, 20180916B,01h58m00.28s +65d42m53.0s,1.00E+00,0.0337,"CHIME,F4,F4","Marcote2020,mannings2021,gordon2023",,LRIS,,WFC3_F110W,,NT, 20180924B,21h44m25.256s -40d54m00.81s,1.00000,0.32120,"CRAFT,CRAFT,F4,F4,F4,CRAFT","Bannister2019,Heintz2020,mannings2021,simha2021a,gordon2023,shannon2024",,MUSE,,WFC3_F160W,,AM,"dispersion measure is different in almost every paper (by ~1 or 2), rotation measure 14 in bhandari 2020 & bannister, metallicity slightly off Bhandari20, DES photom off by tenths of mag, WISE photom off by a few magnitudes" diff --git a/frb/galaxies/defs.py b/frb/galaxies/defs.py index 5967eb99..e1b417ed 100644 --- a/frb/galaxies/defs.py +++ b/frb/galaxies/defs.py @@ -233,6 +233,13 @@ 'dec_source', # Dec error for source position; arcsec ] +############################################################## +# P_Ox +valid_path = [ + 'P_Ox', # Posterior for PATH + 'PATH_Ref', # Reference for PATH +] + ############################################################## # Derived quantities diff --git a/frb/galaxies/frbgalaxy.py b/frb/galaxies/frbgalaxy.py index b7c5ee9d..61680794 100644 --- a/frb/galaxies/frbgalaxy.py +++ b/frb/galaxies/frbgalaxy.py @@ -150,8 +150,10 @@ def __init__(self, ra, dec, frb, cosmo=None): self.derived = {} self.offsets = {} self.positional_error = {} + self.path = {} self.main_attr = ('photom', 'redshift', 'morphology', 'neb_lines', - 'kinematics', 'derived', 'offsets', 'positional_error') + 'kinematics', 'derived', 'offsets', 'positional_error', + 'path') # Angular offset self.offsets['ang_avg'], self.offsets['ang_avg_err'], \ @@ -716,6 +718,8 @@ def vet_one(self, attr): defs_list = defs.valid_offsets elif attr == 'positional_error': defs_list = defs.valid_positional_error + elif attr == 'path': + defs_list = defs.valid_path else: return True # Vet diff --git a/setup.cfg b/setup.cfg index b7e47898..26f759b2 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,6 @@ [aliases] test=pytest +[options.entry_points] +console_scripts = + frb_build_web = frb.builds.build_web:main + frb_build_web_hosts = frb.builds.build_hosts_web:main \ No newline at end of file