Spaces:
Running
Running
update app to include latest features available locally
Browse files- src/streamlit_app.py +128 -9
src/streamlit_app.py
CHANGED
|
@@ -905,10 +905,14 @@ MACE_MODELS = {
|
|
| 905 |
"MACE MP 0b2 Medium": "https://github.com/ACEsuit/mace-foundations/releases/download/mace_mp_0b2/mace-medium-density-agnesi-stress.model",
|
| 906 |
"MACE MP 0b2 Large": "https://github.com/ACEsuit/mace-foundations/releases/download/mace_mp_0b2/mace-large-density-agnesi-stress.model",
|
| 907 |
"MACE MP 0b3 Medium": "https://github.com/ACEsuit/mace-foundations/releases/download/mace_mp_0b3/mace-mp-0b3-medium.model",
|
|
|
|
|
|
|
|
|
|
| 908 |
}
|
| 909 |
|
| 910 |
FAIRCHEM_MODELS = {
|
| 911 |
-
"UMA Small": "uma-s-1",
|
|
|
|
| 912 |
"ESEN MD Direct All OMOL": "esen-md-direct-all-omol",
|
| 913 |
"ESEN SM Conserving All OMOL": "esen-sm-conserving-all-omol",
|
| 914 |
"ESEN SM Direct All OMOL": "esen-sm-direct-all-omol"
|
|
@@ -936,8 +940,8 @@ SEVEN_NET_MODELS = {
|
|
| 936 |
"7net-mf-ompa": "7net-mf-ompa"
|
| 937 |
}
|
| 938 |
@st.cache_resource
|
| 939 |
-
def get_mace_model(model_path, device, selected_default_dtype):
|
| 940 |
-
return mace_mp(model=model_path, device=device, default_dtype=selected_default_dtype)
|
| 941 |
|
| 942 |
@st.cache_resource
|
| 943 |
def get_fairchem_model(selected_model_name, model_path_or_name, device, selected_task_type_fc): # Renamed args to avoid conflict
|
|
@@ -999,7 +1003,7 @@ if atoms is not None:
|
|
| 999 |
if not hasattr(atoms, 'info'):
|
| 1000 |
atoms.info = {}
|
| 1001 |
atoms.info["charge"] = atoms.info.get("charge", 0) # Default charge
|
| 1002 |
-
atoms.info["spin"] = atoms.info.get("spin",
|
| 1003 |
|
| 1004 |
|
| 1005 |
st.sidebar.markdown("## Model Selection")
|
|
@@ -1016,6 +1020,16 @@ if model_type == "MACE":
|
|
| 1016 |
st.sidebar.warning("Using model under Academic Software License (ASL).")
|
| 1017 |
# selected_default_dtype = st.sidebar.selectbox("Select Precision (default_dtype):", ['float32', 'float64'])
|
| 1018 |
selected_default_dtype = 'float64'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1019 |
if model_type == "FairChem":
|
| 1020 |
selected_model = st.sidebar.selectbox("Select FairChem Model:", list(FAIRCHEM_MODELS.keys()))
|
| 1021 |
model_path = FAIRCHEM_MODELS[selected_model]
|
|
@@ -1024,7 +1038,7 @@ if model_type == "FairChem":
|
|
| 1024 |
selected_task_type = st.sidebar.selectbox("Select UMA Model Task Type:", ["omol", "omat", "omc", "odac", "oc20"])
|
| 1025 |
if selected_task_type == "omol" and atoms is not None:
|
| 1026 |
charge = st.sidebar.number_input("Total Charge", min_value=-10, max_value=10, value=atoms.info.get("charge",0))
|
| 1027 |
-
spin_multiplicity = st.sidebar.number_input("Spin Multiplicity (2S + 1)", min_value=1, max_value=11, step=
|
| 1028 |
atoms.info["charge"] = charge
|
| 1029 |
atoms.info["spin"] = spin_multiplicity # FairChem expects multiplicity
|
| 1030 |
if model_type == "ORB":
|
|
@@ -1061,7 +1075,9 @@ task = st.sidebar.selectbox("Select Calculation Task:",
|
|
| 1061 |
"Atomization/Cohesive Energy", # New Task Added
|
| 1062 |
"Geometry Optimization",
|
| 1063 |
"Cell + Geometry Optimization",
|
| 1064 |
-
"Vibrational Mode Analysis"
|
|
|
|
|
|
|
| 1065 |
|
| 1066 |
if "Optimization" in task:
|
| 1067 |
st.sidebar.markdown("### Optimization Parameters")
|
|
@@ -1100,6 +1116,8 @@ if atoms is not None:
|
|
| 1100 |
st.write(f"**Model:** {selected_model}")
|
| 1101 |
if model_type == "FairChem" and selected_model == "UMA Small":
|
| 1102 |
st.write(f"**UMA Task Type:** {selected_task_type}")
|
|
|
|
|
|
|
| 1103 |
st.write(f"**Device:** {device}")
|
| 1104 |
|
| 1105 |
st.markdown("### Selected Task")
|
|
@@ -1129,7 +1147,7 @@ if atoms is not None:
|
|
| 1129 |
|
| 1130 |
if model_type == "MACE":
|
| 1131 |
# st.write("Setting up MACE calculator...")
|
| 1132 |
-
calc = get_mace_model(model_path, device, 'float32')
|
| 1133 |
elif model_type == "FairChem": # FairChem
|
| 1134 |
# st.write("Setting up FairChem calculator...")
|
| 1135 |
# Workaround for potential dtype issues when switching models
|
|
@@ -1377,6 +1395,15 @@ if atoms is not None:
|
|
| 1377 |
|
| 1378 |
show_trajectory_and_controls()
|
| 1379 |
elif task == "Vibrational Mode Analysis":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1380 |
|
| 1381 |
st.write("Running vibrational mode analysis using finite differences...")
|
| 1382 |
|
|
@@ -1391,9 +1418,21 @@ if atoms is not None:
|
|
| 1391 |
with st.spinner("Calculating vibrational modes... This may take a few minutes."):
|
| 1392 |
vib.run()
|
| 1393 |
freqs = vib.get_frequencies()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1394 |
|
| 1395 |
-
# Convert frequencies to cm⁻¹
|
| 1396 |
-
freqs_cm = freqs #/ cm
|
| 1397 |
|
| 1398 |
# Classify frequencies
|
| 1399 |
mode_data = []
|
|
@@ -1443,6 +1482,86 @@ if atoms is not None:
|
|
| 1443 |
file_name="vibrational_modes.csv",
|
| 1444 |
mime="text/csv"
|
| 1445 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1446 |
except Exception as e:
|
| 1447 |
st.error(f"🔴 Calculation error: {str(e)}")
|
| 1448 |
st.error("Please check the structure, model compatibility, and parameters. For FairChem UMA, ensure the task type (omol, omat etc.) is appropriate for your system (e.g. omol for molecules, omat for materials).")
|
|
|
|
| 905 |
"MACE MP 0b2 Medium": "https://github.com/ACEsuit/mace-foundations/releases/download/mace_mp_0b2/mace-medium-density-agnesi-stress.model",
|
| 906 |
"MACE MP 0b2 Large": "https://github.com/ACEsuit/mace-foundations/releases/download/mace_mp_0b2/mace-large-density-agnesi-stress.model",
|
| 907 |
"MACE MP 0b3 Medium": "https://github.com/ACEsuit/mace-foundations/releases/download/mace_mp_0b3/mace-mp-0b3-medium.model",
|
| 908 |
+
"MACE ANI-CC Large (500k)": "https://github.com/ACEsuit/mace/raw/main/mace/calculators/foundations_models/ani500k_large_CC.model",
|
| 909 |
+
"MACE OMOL-0 XL 4M": "https://github.com/ACEsuit/mace-foundations/releases/download/mace_omol_0/mace-omol-0-extra-large-4M.model",
|
| 910 |
+
"MACE OMOL-0 XL 1024": "https://github.com/ACEsuit/mace-foundations/releases/download/mace_omol_0/MACE-omol-0-extra-large-1024.model"
|
| 911 |
}
|
| 912 |
|
| 913 |
FAIRCHEM_MODELS = {
|
| 914 |
+
"UMA Small 1": "uma-s-1",
|
| 915 |
+
"UMA Small 1.1": "uma-s-1p1",
|
| 916 |
"ESEN MD Direct All OMOL": "esen-md-direct-all-omol",
|
| 917 |
"ESEN SM Conserving All OMOL": "esen-sm-conserving-all-omol",
|
| 918 |
"ESEN SM Direct All OMOL": "esen-sm-direct-all-omol"
|
|
|
|
| 940 |
"7net-mf-ompa": "7net-mf-ompa"
|
| 941 |
}
|
| 942 |
@st.cache_resource
|
| 943 |
+
def get_mace_model(model_path, dispersion, device, selected_default_dtype):
|
| 944 |
+
return mace_mp(model=model_path, dispersion=True, device=device, default_dtype=selected_default_dtype)
|
| 945 |
|
| 946 |
@st.cache_resource
|
| 947 |
def get_fairchem_model(selected_model_name, model_path_or_name, device, selected_task_type_fc): # Renamed args to avoid conflict
|
|
|
|
| 1003 |
if not hasattr(atoms, 'info'):
|
| 1004 |
atoms.info = {}
|
| 1005 |
atoms.info["charge"] = atoms.info.get("charge", 0) # Default charge
|
| 1006 |
+
atoms.info["spin"] = atoms.info.get("spin", 1) # Default spin (usually 2S for ASE, model might want 2S+1)
|
| 1007 |
|
| 1008 |
|
| 1009 |
st.sidebar.markdown("## Model Selection")
|
|
|
|
| 1020 |
st.sidebar.warning("Using model under Academic Software License (ASL).")
|
| 1021 |
# selected_default_dtype = st.sidebar.selectbox("Select Precision (default_dtype):", ['float32', 'float64'])
|
| 1022 |
selected_default_dtype = 'float64'
|
| 1023 |
+
dispersion = st.sidebar.checkbox("Dispersion correction?", value=False)
|
| 1024 |
+
if selected_model == "MACE OMOL-0 XL 4M" or selected_model == "MACE OMOL-0 XL 1024":
|
| 1025 |
+
|
| 1026 |
+
charge = st.sidebar.number_input("Total Charge", min_value=-10, max_value=10, value=atoms.info.get("charge",0))
|
| 1027 |
+
spin_multiplicity = st.sidebar.number_input("Spin Multiplicity (2S + 1)", min_value=1, max_value=11, step=1, value=int(atoms.info.get("spin",0) if atoms.info.get("spin",0) is not None else 1)) # Assuming spin in atoms.info is S
|
| 1028 |
+
atoms.info["charge"] = charge
|
| 1029 |
+
atoms.info["spin"] = spin_multiplicity # FairChem expects multiplicity
|
| 1030 |
+
# else:
|
| 1031 |
+
# atoms.info["charge"] = 0
|
| 1032 |
+
# atoms.info["spin"] = 1 # FairChem expects multiplicity
|
| 1033 |
if model_type == "FairChem":
|
| 1034 |
selected_model = st.sidebar.selectbox("Select FairChem Model:", list(FAIRCHEM_MODELS.keys()))
|
| 1035 |
model_path = FAIRCHEM_MODELS[selected_model]
|
|
|
|
| 1038 |
selected_task_type = st.sidebar.selectbox("Select UMA Model Task Type:", ["omol", "omat", "omc", "odac", "oc20"])
|
| 1039 |
if selected_task_type == "omol" and atoms is not None:
|
| 1040 |
charge = st.sidebar.number_input("Total Charge", min_value=-10, max_value=10, value=atoms.info.get("charge",0))
|
| 1041 |
+
spin_multiplicity = st.sidebar.number_input("Spin Multiplicity (2S + 1)", min_value=1, max_value=11, step=1, value=int(atoms.info.get("spin",0) if atoms.info.get("spin",0) is not None else 1)) # Assuming spin in atoms.info is S
|
| 1042 |
atoms.info["charge"] = charge
|
| 1043 |
atoms.info["spin"] = spin_multiplicity # FairChem expects multiplicity
|
| 1044 |
if model_type == "ORB":
|
|
|
|
| 1075 |
"Atomization/Cohesive Energy", # New Task Added
|
| 1076 |
"Geometry Optimization",
|
| 1077 |
"Cell + Geometry Optimization",
|
| 1078 |
+
"Vibrational Mode Analysis",
|
| 1079 |
+
#"Phonons"
|
| 1080 |
+
])
|
| 1081 |
|
| 1082 |
if "Optimization" in task:
|
| 1083 |
st.sidebar.markdown("### Optimization Parameters")
|
|
|
|
| 1116 |
st.write(f"**Model:** {selected_model}")
|
| 1117 |
if model_type == "FairChem" and selected_model == "UMA Small":
|
| 1118 |
st.write(f"**UMA Task Type:** {selected_task_type}")
|
| 1119 |
+
if model_type == "MACE":
|
| 1120 |
+
st.write(f"**Dispersion:** {dispersion}")
|
| 1121 |
st.write(f"**Device:** {device}")
|
| 1122 |
|
| 1123 |
st.markdown("### Selected Task")
|
|
|
|
| 1147 |
|
| 1148 |
if model_type == "MACE":
|
| 1149 |
# st.write("Setting up MACE calculator...")
|
| 1150 |
+
calc = get_mace_model(model_path, dispersion, device, 'float32')
|
| 1151 |
elif model_type == "FairChem": # FairChem
|
| 1152 |
# st.write("Setting up FairChem calculator...")
|
| 1153 |
# Workaround for potential dtype issues when switching models
|
|
|
|
| 1395 |
|
| 1396 |
show_trajectory_and_controls()
|
| 1397 |
elif task == "Vibrational Mode Analysis":
|
| 1398 |
+
# Conversion factors
|
| 1399 |
+
|
| 1400 |
+
from ase.units import kB as kB_eVK, _Nav, J # ASE's constants
|
| 1401 |
+
|
| 1402 |
+
from scipy.constants import physical_constants
|
| 1403 |
+
|
| 1404 |
+
kB_JK = physical_constants["Boltzmann constant"][0] # J/K
|
| 1405 |
+
|
| 1406 |
+
is_periodic = any(calc_atoms.pbc)
|
| 1407 |
|
| 1408 |
st.write("Running vibrational mode analysis using finite differences...")
|
| 1409 |
|
|
|
|
| 1418 |
with st.spinner("Calculating vibrational modes... This may take a few minutes."):
|
| 1419 |
vib.run()
|
| 1420 |
freqs = vib.get_frequencies()
|
| 1421 |
+
energies = vib.get_energies()
|
| 1422 |
+
|
| 1423 |
+
|
| 1424 |
+
|
| 1425 |
+
print('\n\n\n\n\n\n\n\n')
|
| 1426 |
+
# vib.get_hessian_2d()
|
| 1427 |
+
# st.write(vib.summary())
|
| 1428 |
+
# print('\n')
|
| 1429 |
+
# vib.tabulate()
|
| 1430 |
+
|
| 1431 |
+
|
| 1432 |
+
|
| 1433 |
+
freqs_cm = freqs
|
| 1434 |
+
freqs_eV = energies
|
| 1435 |
|
|
|
|
|
|
|
| 1436 |
|
| 1437 |
# Classify frequencies
|
| 1438 |
mode_data = []
|
|
|
|
| 1482 |
file_name="vibrational_modes.csv",
|
| 1483 |
mime="text/csv"
|
| 1484 |
)
|
| 1485 |
+
# -------- Thermodynamic Analysis for Molecules --------
|
| 1486 |
+
if not is_periodic:
|
| 1487 |
+
|
| 1488 |
+
# Filter physical frequencies > 1 cm⁻¹ (to avoid numerical issues)
|
| 1489 |
+
physical_freqs_eV = np.array([f for f in freqs_eV if f > 1e-5])
|
| 1490 |
+
|
| 1491 |
+
# Zero-point vibrational energy (ZPE)
|
| 1492 |
+
ZPE = 0.5 * np.sum(physical_freqs_eV) # in eV
|
| 1493 |
+
|
| 1494 |
+
# Vibrational entropy (in eV/K)
|
| 1495 |
+
vib_entropy = 0.0
|
| 1496 |
+
for f in physical_freqs_eV:
|
| 1497 |
+
x = f / (kB_eVK * T)
|
| 1498 |
+
vib_entropy += (x / (np.exp(x) - 1) - np.log(1 - np.exp(-x)))
|
| 1499 |
+
|
| 1500 |
+
S_vib_eVK = kB_eVK * vib_entropy # eV/K
|
| 1501 |
+
S_vib_JmolK = S_vib_eVK * J * _Nav # J/mol·K
|
| 1502 |
+
|
| 1503 |
+
results["ZPE (eV)"] = ZPE.real
|
| 1504 |
+
results["Vibrational Entropy (eV/K)"] = S_vib_eVK
|
| 1505 |
+
results["Vibrational Entropy (J/mol·K)"] = S_vib_JmolK
|
| 1506 |
+
|
| 1507 |
+
st.write(f"**Zero-point vibrational energy (ZPE)**: {ZPE.real:.6f} eV")
|
| 1508 |
+
st.write(f"**Vibrational entropy**: {S_vib_eVK:.6f} eV/K")
|
| 1509 |
+
|
| 1510 |
+
else:
|
| 1511 |
+
st.info("Thermodynamic properties like ZPE and entropy are currently only meaningful for isolated molecules (non-periodic systems).")
|
| 1512 |
+
elif task == "Phonons":
|
| 1513 |
+
from ase.phonons import Phonons
|
| 1514 |
+
|
| 1515 |
+
st.write("### Phonon Band Structure and Density of States")
|
| 1516 |
+
is_periodic = any(calc_atoms.pbc)
|
| 1517 |
+
|
| 1518 |
+
if not is_periodic:
|
| 1519 |
+
st.error("Phonon calculations require a periodic structure. Please use a periodic system.")
|
| 1520 |
+
else:
|
| 1521 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 1522 |
+
st.info("Running phonon calculation using finite displacements...")
|
| 1523 |
+
sc = (7, 7, 7)
|
| 1524 |
+
|
| 1525 |
+
# Create phonon object
|
| 1526 |
+
ph = Phonons(calc_atoms, calc_atoms.calc, supercell=sc, delta=0.001, name=os.path.join(tmpdir, 'phonon'))
|
| 1527 |
+
|
| 1528 |
+
with st.spinner("Displacing atoms and computing forces..."):
|
| 1529 |
+
ph.run()
|
| 1530 |
+
|
| 1531 |
+
# Build dynamical matrix
|
| 1532 |
+
ph.read(acoustic=True)
|
| 1533 |
+
ph.clean()
|
| 1534 |
+
|
| 1535 |
+
# Band path and DOS
|
| 1536 |
+
# path = calc_atoms.cell.bandpath('GXULGK', npoints=100)
|
| 1537 |
+
path = calc_atoms.cell.bandpath('GXKGL', npoints=100)
|
| 1538 |
+
# path = calc_atoms.cell.bandpath(eps=0.00001)
|
| 1539 |
+
bs = ph.get_band_structure(path)
|
| 1540 |
+
dos = ph.get_dos(kpts=(20, 20, 20)).sample_grid(npts=100, width=1e-3)
|
| 1541 |
+
|
| 1542 |
+
# Plotting
|
| 1543 |
+
fig = plt.figure(figsize=(7, 4))
|
| 1544 |
+
ax = fig.add_axes([0.12, 0.07, 0.67, 0.85])
|
| 1545 |
+
|
| 1546 |
+
emax = 0.075
|
| 1547 |
+
bs.plot(ax=ax, emin=0.0, emax=emax)
|
| 1548 |
+
|
| 1549 |
+
dosax = fig.add_axes([0.8, 0.07, 0.17, 0.85])
|
| 1550 |
+
dosax.fill_between(
|
| 1551 |
+
dos.get_weights(),
|
| 1552 |
+
dos.get_energies(),
|
| 1553 |
+
y2=0,
|
| 1554 |
+
color='grey',
|
| 1555 |
+
edgecolor='k',
|
| 1556 |
+
lw=1,
|
| 1557 |
+
)
|
| 1558 |
+
dosax.set_ylim(0, emax)
|
| 1559 |
+
dosax.set_yticks([])
|
| 1560 |
+
dosax.set_xticks([])
|
| 1561 |
+
dosax.set_xlabel('DOS', fontsize=14)
|
| 1562 |
+
|
| 1563 |
+
st.pyplot(fig)
|
| 1564 |
+
st.success("Phonon band structure and DOS successfully plotted.")
|
| 1565 |
except Exception as e:
|
| 1566 |
st.error(f"🔴 Calculation error: {str(e)}")
|
| 1567 |
st.error("Please check the structure, model compatibility, and parameters. For FairChem UMA, ensure the task type (omol, omat etc.) is appropriate for your system (e.g. omol for molecules, omat for materials).")
|