import pandas as pd, json
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 = pd.DataFrame(rows_known, columns=[“Element (Z)”, “Known”, “Stable”])
df[“Unstable”] = df[“Known”] – df[“Stable”]
TARGET_TOTAL = 7759
scale = TARGET_TOTAL / int(df[“Known”].sum())
df[“Predicted”] = (df[“Known”] * scale).round().astype(int)
drift = TARGET_TOTAL – int(df[“Predicted”].sum())
if drift != 0:
frac = (df[“Known”] * scale) – df[“Predicted”]
idx = frac.idxmax() if drift > 0 else frac.idxmin()
df.loc[idx, “Predicted”] += drift
df[“Gap”] = df[“Predicted”] – df[“Known”]
def parse_symbol(s): return s.split(” “)[0]
def parse_z(s): return int(s.split(“(“)[1].split(“)”)[0])
df[“element_symbol”] = df[“Element (Z)”].apply(parse_symbol)
df[“element_z”] = df[“Element (Z)”].apply(parse_z)
json_out = df[[“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)
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)
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
}
lines = []
lines.append(“# Master Isotope Table — Known vs Predicted (Z = 1–118)\n”)
lines.append(“This table lists the experimentally known isotope counts (strict stable/unstable) and a predicted count per element scaled to the Neufcourt et al. (2020) central estimate of 7,759 bound nuclides. The Gap column shows Predicted − Known.”)
lines.append(“\n## Totals\n”)
for k,v in totals.items():
if k == “Scale factor”:
lines.append(f”- {k}: {v:.6f}”)
else:
lines.append(f”- {k}: {v}”)
lines.append(“\n## Table (Z = 1–118)\n”)
lines.append(table_md)
lines.append(“\n—\nMethod: per-element prediction by proportional scaling so that the sum across all Z equals 7,759. For model-specific drip-line maps, swap in counts from a chosen mass model.\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(lines))
from caas_jupyter_tools import display_dataframe_to_user
display_dataframe_to_user(“Master Isotopes — WordPress Markdown Preview”, json_out)
json_path, wp_md_path
# Re-run the WordPress Markdown + JSON generation after reset
import pandas as pd, json
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 = pd.DataFrame(rows_known, columns=["Element (Z)", "Known", "Stable"])
df["Unstable"] = df["Known"] - df["Stable"]
TARGET_TOTAL = 7759
scale = TARGET_TOTAL / int(df["Known"].sum())
df["Predicted"] = (df["Known"] * scale).round().astype(int)
drift = TARGET_TOTAL - int(df["Predicted"].sum())
if drift != 0:
frac = (df["Known"] * scale) - df["Predicted"]
idx = frac.idxmax() if drift > 0 else frac.idxmin()
df.loc[idx, "Predicted"] += drift
df["Gap"] = df["Predicted"] - df["Known"]
def parse_symbol(s): return s.split(" ")[0]
def parse_z(s): return int(s.split("(")[1].split(")")[0])
df["element_symbol"] = df["Element (Z)"].apply(parse_symbol)
df["element_z"] = df["Element (Z)"].apply(parse_z)
json_out = df[["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)
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)
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
}
lines = []
lines.append("# Master Isotope Table — Known vs Predicted (Z = 1–118)\n")
lines.append("This table lists the **experimentally known** isotope counts (strict stable/unstable) and a **predicted** count per element scaled to the Neufcourt et al. (2020) central estimate of **7,759** bound nuclides. The **Gap** column shows Predicted − Known.")
lines.append("\n## Totals\n")
for k,v in totals.items():
if k == "Scale factor":
lines.append(f"- {k}: {v:.6f}")
else:
lines.append(f"- {k}: {v}")
lines.append("\n## Table (Z = 1–118)\n")
lines.append(table_md)
lines.append("\n---\n*Method:* per-element prediction by proportional scaling so that the sum across all Z equals 7,759. For model-specific drip-line maps, swap in counts from a chosen mass model.\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(lines))
from caas_jupyter_tools import display_dataframe_to_user
display_dataframe_to_user("Master Isotopes — WordPress Markdown Preview", json_out)
json_path, wp_md_path