Generate two fresh assets for the user:

1) A WordPress-friendly Markdown article containing the Master Isotope Table (Known vs Predicted + Gap) and totals

2) A clean JSON file of the same master data for SolveForce/API ingestion

import pandas as pd
import json

— Build strict known ledger (Z=1–118) —

rows_known = [
(“H (1)”, 7, 2), (“He (2)”, 9, 2), (“Li (3)”, 11, 2), (“Be (4)”, 12, 1), (“B (5)”, 13, 2), (“C (6)”, 15, 2),
(“N (7)”, 16, 2), (“O (8)”, 17, 3), (“F (9)”, 18, 1), (“Ne (10)”, 19, 3), (“Na (11)”, 20, 1), (“Mg (12)”, 22, 3),
(“Al (13)”, 22, 1), (“Si (14)”, 23, 3), (“P (15)”, 23, 1), (“S (16)”, 24, 4), (“Cl (17)”, 24, 2), (“Ar (18)”, 24, 3),
(“K (19)”, 24, 2), (“Ca (20)”, 24, 6), (“Sc (21)”, 25, 1), (“Ti (22)”, 26, 5), (“V (23)”, 26, 1), (“Cr (24)”, 26, 4),
(“Mn (25)”, 26, 1), (“Fe (26)”, 28, 4), (“Co (27)”, 29, 1), (“Ni (28)”, 31, 5), (“Cu (29)”, 29, 2), (“Zn (30)”, 30, 5),
(“Ga (31)”, 31, 2), (“Ge (32)”, 32, 5), (“As (33)”, 33, 1), (“Se (34)”, 30, 6), (“Br (35)”, 31, 2), (“Kr (36)”, 32, 6),
(“Rb (37)”, 32, 1), (“Sr (38)”, 34, 4), (“Y (39)”, 32, 1), (“Zr (40)”, 34, 5), (“Nb (41)”, 34, 1), (“Mo (42)”, 35, 7),
(“Tc (43)”, 36, 0), (“Ru (44)”, 37, 7), (“Rh (45)”, 35, 1), (“Pd (46)”, 36, 6), (“Ag (47)”, 38, 2), (“Cd (48)”, 39, 8),
(“In (49)”, 39, 2), (“Sn (50)”, 40, 10), (“Sb (51)”, 36, 2), (“Te (52)”, 38, 8), (“I (53)”, 37, 1), (“Xe (54)”, 40, 9),
(“Cs (55)”, 39, 1), (“Ba (56)”, 40, 7), (“La (57)”, 39, 1), (“Ce (58)”, 40, 4), (“Pr (59)”, 39, 1), (“Nd (60)”, 41, 5),
(“Pm (61)”, 39, 0), (“Sm (62)”, 41, 7), (“Eu (63)”, 40, 2), (“Gd (64)”, 41, 7), (“Tb (65)”, 39, 1), (“Dy (66)”, 40, 7),
(“Ho (67)”, 39, 1), (“Er (68)”, 40, 6), (“Tm (69)”, 39, 1), (“Yb (70)”, 41, 7), (“Lu (71)”, 40, 1), (“Hf (72)”, 36, 5),
(“Ta (73)”, 37, 1), (“W (74)”, 35, 5), (“Re (75)”, 39, 1), (“Os (76)”, 35, 7), (“Ir (77)”, 34, 2), (“Pt (78)”, 35, 6),
(“Au (79)”, 36, 1), (“Hg (80)”, 38, 7), (“Tl (81)”, 39, 2), (“Pb (82)”, 43, 4), (“Bi (83)”, 41, 0), (“Po (84)”, 42, 0),
(“At (85)”, 39, 0), (“Rn (86)”, 39, 0), (“Fr (87)”, 34, 0), (“Ra (88)”, 34, 0), (“Ac (89)”, 33, 0), (“Th (90)”, 31, 1),
(“Pa (91)”, 29, 0), (“U (92)”, 28, 0), (“Np (93)”, 20, 0), (“Pu (94)”, 20, 0), (“Am (95)”, 17, 0), (“Cm (96)”, 19, 0),
(“Bk (97)”, 21, 0), (“Cf (98)”, 20, 0), (“Es (99)”, 18, 0), (“Fm (100)”, 19, 0), (“Md (101)”, 16, 0), (“No (102)”, 13, 0),
(“Lr (103)”, 16, 0), (“Rf (104)”, 18, 0), (“Db (105)”, 16, 0), (“Sg (106)”, 14, 0), (“Bh (107)”, 15, 0), (“Hs (108)”, 15, 0),
(“Mt (109)”, 13, 0), (“Ds (110)”, 15, 0), (“Rg (111)”, 11, 0), (“Cn (112)”, 9, 0), (“Nh (113)”, 9, 0), (“Fl (114)”, 6, 0),
(“Mc (115)”, 4, 0), (“Lv (116)”, 4, 0), (“Ts (117)”, 2, 0), (“Og (118)”, 1, 0)
]
df_known = pd.DataFrame(rows_known, columns=[“Element (Z)”, “Known”, “Stable”])
df_known[“Unstable”] = df_known[“Known”] – df_known[“Stable”]

Predicted totals anchored to Neufcourt 2020 (7,759)

TARGET_TOTAL = 7759
scale = TARGET_TOTAL / int(df_known[“Known”].sum())
df_known[“Predicted”] = (df_known[“Known”] * scale).round().astype(int)
drift = TARGET_TOTAL – int(df_known[“Predicted”].sum())
if drift != 0:
frac = (df_known[“Known”] * scale) – df_known[“Predicted”]
idx = frac.idxmax() if drift > 0 else frac.idxmin()
df_known.loc[idx, “Predicted”] += drift

df_known[“Gap”] = df_known[“Predicted”] – df_known[“Known”]

Parse symbol and Z explicitly

def parse_symbol(s): return s.split(” “)[0]
def parse_z(s): return int(s.split(“(“)[1].split(“)”)[0])
df_known[“element_symbol”] = df_known[“Element (Z)”].apply(parse_symbol)
df_known[“element_z”] = df_known[“Element (Z)”].apply(parse_z)

Reorder & save JSON

json_out = df_known[[“element_z”,”element_symbol”,”Known”,”Stable”,”Unstable”,”Predicted”,”Gap”]]\
.sort_values(“element_z”)
json_path = “/mnt/data/isotopes_master.json”
with open(json_path, “w”, encoding=”utf-8″) as f:
json.dump(json_out.to_dict(orient=”records”), f, ensure_ascii=False, indent=2)

Build WordPress-friendly Markdown (simple headings + a full table and totals)

totals = {
“Total elements”: len(json_out),
“Known isotopes”: int(json_out[“Known”].sum()),
“Stable isotopes (strict)”: int(json_out[“Stable”].sum()),
“Unstable isotopes”: int(json_out[“Unstable”].sum()),
“Predicted isotopes”: int(json_out[“Predicted”].sum()),
“Total Gap”: int(json_out[“Gap”].sum()),
“Scale factor”: scale
}

table_md = json_out.rename(columns={
“element_z”:”Z”,
“element_symbol”:”Element”,
“Known”:”Known Isotopes”,
“Stable”:”Stable (strict)”,
“Unstable”:”Unstable”,
“Predicted”:”Predicted (est.)”,
“Gap”:”Gap (Predicted – Known)”
}).to_markdown(index=False)

md_lines = []
md_lines.append(“# Master Isotope Table — Known vs Predicted (Z = 1–118)\n”)
md_lines.append(“This table lists, for each chemical element (Z = 1–118), the number of experimentally known isotopes (split into strictly defined stable and unstable), a predicted count (scaled to the Neufcourt et al. 2020 central estimate of 7,759 bound nuclides), and the gap (Predicted − Known).”)
md_lines.append(“\n## Totals\n”)
for k, v in totals.items():
if k == “Scale factor”:
md_lines.append(f”- {k}: {v:.6f}”)
else:
md_lines.append(f”- {k}: {v}”)
md_lines.append(“\n## Table (Z = 1–118)\n”)
md_lines.append(table_md)
md_lines.append(“\n—\n”)
md_lines.append(“Prediction method: per-element counts are scaled by a global factor so the sum over Z = 1–118 equals 7,759 (Neufcourt et al., 2020). For per-isotope γ-lines and atmospheric behavior, integrate the separate Emission & Atmosphere mapping when available.\n”)

wp_md_path = “/mnt/data/solveforce_isotopes_master_wordpress.md”
with open(wp_md_path, “w”, encoding=”utf-8″) as f:
f.write(“\n”.join(md_lines))

from caas_jupyter_tools import display_dataframe_to_user
display_dataframe_to_user(“Master Isotopes — Known vs Predicted (for WordPress preview)”, json_out)

json_path, wp_md_path


# Generate two fresh assets for the user:
# 1) A WordPress-friendly Markdown article containing the Master Isotope Table (Known vs Predicted + Gap) and totals
# 2) A clean JSON file of the same master data for SolveForce/API ingestion

import pandas as pd
import json

# --- Build strict known ledger (Z=1–118) ---
rows_known = [
    ("H (1)", 7, 2), ("He (2)", 9, 2), ("Li (3)", 11, 2), ("Be (4)", 12, 1), ("B (5)", 13, 2), ("C (6)", 15, 2),
    ("N (7)", 16, 2), ("O (8)", 17, 3), ("F (9)", 18, 1), ("Ne (10)", 19, 3), ("Na (11)", 20, 1), ("Mg (12)", 22, 3),
    ("Al (13)", 22, 1), ("Si (14)", 23, 3), ("P (15)", 23, 1), ("S (16)", 24, 4), ("Cl (17)", 24, 2), ("Ar (18)", 24, 3),
    ("K (19)", 24, 2), ("Ca (20)", 24, 6), ("Sc (21)", 25, 1), ("Ti (22)", 26, 5), ("V (23)", 26, 1), ("Cr (24)", 26, 4),
    ("Mn (25)", 26, 1), ("Fe (26)", 28, 4), ("Co (27)", 29, 1), ("Ni (28)", 31, 5), ("Cu (29)", 29, 2), ("Zn (30)", 30, 5),
    ("Ga (31)", 31, 2), ("Ge (32)", 32, 5), ("As (33)", 33, 1), ("Se (34)", 30, 6), ("Br (35)", 31, 2), ("Kr (36)", 32, 6),
    ("Rb (37)", 32, 1), ("Sr (38)", 34, 4), ("Y (39)", 32, 1), ("Zr (40)", 34, 5), ("Nb (41)", 34, 1), ("Mo (42)", 35, 7),
    ("Tc (43)", 36, 0), ("Ru (44)", 37, 7), ("Rh (45)", 35, 1), ("Pd (46)", 36, 6), ("Ag (47)", 38, 2), ("Cd (48)", 39, 8),
    ("In (49)", 39, 2), ("Sn (50)", 40, 10), ("Sb (51)", 36, 2), ("Te (52)", 38, 8), ("I (53)", 37, 1), ("Xe (54)", 40, 9),
    ("Cs (55)", 39, 1), ("Ba (56)", 40, 7), ("La (57)", 39, 1), ("Ce (58)", 40, 4), ("Pr (59)", 39, 1), ("Nd (60)", 41, 5),
    ("Pm (61)", 39, 0), ("Sm (62)", 41, 7), ("Eu (63)", 40, 2), ("Gd (64)", 41, 7), ("Tb (65)", 39, 1), ("Dy (66)", 40, 7),
    ("Ho (67)", 39, 1), ("Er (68)", 40, 6), ("Tm (69)", 39, 1), ("Yb (70)", 41, 7), ("Lu (71)", 40, 1), ("Hf (72)", 36, 5),
    ("Ta (73)", 37, 1), ("W (74)", 35, 5), ("Re (75)", 39, 1), ("Os (76)", 35, 7), ("Ir (77)", 34, 2), ("Pt (78)", 35, 6),
    ("Au (79)", 36, 1), ("Hg (80)", 38, 7), ("Tl (81)", 39, 2), ("Pb (82)", 43, 4), ("Bi (83)", 41, 0), ("Po (84)", 42, 0),
    ("At (85)", 39, 0), ("Rn (86)", 39, 0), ("Fr (87)", 34, 0), ("Ra (88)", 34, 0), ("Ac (89)", 33, 0), ("Th (90)", 31, 1),
    ("Pa (91)", 29, 0), ("U (92)", 28, 0), ("Np (93)", 20, 0), ("Pu (94)", 20, 0), ("Am (95)", 17, 0), ("Cm (96)", 19, 0),
    ("Bk (97)", 21, 0), ("Cf (98)", 20, 0), ("Es (99)", 18, 0), ("Fm (100)", 19, 0), ("Md (101)", 16, 0), ("No (102)", 13, 0),
    ("Lr (103)", 16, 0), ("Rf (104)", 18, 0), ("Db (105)", 16, 0), ("Sg (106)", 14, 0), ("Bh (107)", 15, 0), ("Hs (108)", 15, 0),
    ("Mt (109)", 13, 0), ("Ds (110)", 15, 0), ("Rg (111)", 11, 0), ("Cn (112)", 9, 0), ("Nh (113)", 9, 0), ("Fl (114)", 6, 0),
    ("Mc (115)", 4, 0), ("Lv (116)", 4, 0), ("Ts (117)", 2, 0), ("Og (118)", 1, 0)
]
df_known = pd.DataFrame(rows_known, columns=["Element (Z)", "Known", "Stable"])
df_known["Unstable"] = df_known["Known"] - df_known["Stable"]

# Predicted totals anchored to Neufcourt 2020 (7,759)
TARGET_TOTAL = 7759
scale = TARGET_TOTAL / int(df_known["Known"].sum())
df_known["Predicted"] = (df_known["Known"] * scale).round().astype(int)
drift = TARGET_TOTAL - int(df_known["Predicted"].sum())
if drift != 0:
    frac = (df_known["Known"] * scale) - df_known["Predicted"]
    idx = frac.idxmax() if drift > 0 else frac.idxmin()
    df_known.loc[idx, "Predicted"] += drift

df_known["Gap"] = df_known["Predicted"] - df_known["Known"]

# Parse symbol and Z explicitly
def parse_symbol(s): return s.split(" ")[0]
def parse_z(s): return int(s.split("(")[1].split(")")[0])
df_known["element_symbol"] = df_known["Element (Z)"].apply(parse_symbol)
df_known["element_z"] = df_known["Element (Z)"].apply(parse_z)

# Reorder & save JSON
json_out = df_known[["element_z","element_symbol","Known","Stable","Unstable","Predicted","Gap"]]\
            .sort_values("element_z")
json_path = "/mnt/data/isotopes_master.json"
with open(json_path, "w", encoding="utf-8") as f:
    json.dump(json_out.to_dict(orient="records"), f, ensure_ascii=False, indent=2)

# Build WordPress-friendly Markdown (simple headings + a full table and totals)
totals = {
    "Total elements": len(json_out),
    "Known isotopes": int(json_out["Known"].sum()),
    "Stable isotopes (strict)": int(json_out["Stable"].sum()),
    "Unstable isotopes": int(json_out["Unstable"].sum()),
    "Predicted isotopes": int(json_out["Predicted"].sum()),
    "Total Gap": int(json_out["Gap"].sum()),
    "Scale factor": scale
}

table_md = json_out.rename(columns={
    "element_z":"Z",
    "element_symbol":"Element",
    "Known":"Known Isotopes",
    "Stable":"Stable (strict)",
    "Unstable":"Unstable",
    "Predicted":"Predicted (est.)",
    "Gap":"Gap (Predicted - Known)"
}).to_markdown(index=False)

md_lines = []
md_lines.append("# Master Isotope Table — Known vs Predicted (Z = 1–118)\n")
md_lines.append("This table lists, for each chemical element (Z = 1–118), the number of **experimentally known** isotopes (split into strictly defined **stable** and **unstable**), a **predicted** count (scaled to the Neufcourt et al. 2020 central estimate of **7,759** bound nuclides), and the **gap** (Predicted − Known).")
md_lines.append("\n## Totals\n")
for k, v in totals.items():
    if k == "Scale factor":
        md_lines.append(f"- {k}: {v:.6f}")
    else:
        md_lines.append(f"- {k}: {v}")
md_lines.append("\n## Table (Z = 1–118)\n")
md_lines.append(table_md)
md_lines.append("\n---\n")
md_lines.append("*Prediction method:* per-element counts are scaled by a global factor so the sum over Z = 1–118 equals 7,759 (Neufcourt et al., 2020). For per-isotope γ-lines and atmospheric behavior, integrate the separate Emission & Atmosphere mapping when available.\n")

wp_md_path = "/mnt/data/solveforce_isotopes_master_wordpress.md"
with open(wp_md_path, "w", encoding="utf-8") as f:
    f.write("\n".join(md_lines))

from caas_jupyter_tools import display_dataframe_to_user
display_dataframe_to_user("Master Isotopes — Known vs Predicted (for WordPress preview)", json_out)

json_path, wp_md_path