Harden fallback density estimate

This commit is contained in:
2026-04-14 17:01:12 +02:00
parent 93c00e8b60
commit 04272a0fc5
+152 -20
View File
@@ -3080,28 +3080,40 @@ function rho_fun()
if (isFinite(rhos[chem])) {
// Found in densities list of compositions
rho = rhos[chem];
} else {
// suggest a material density based on composition (when it's undefined)
let layer_formula = parse_formula(chem);
} else {
// suggest a material density based on composition (when it's undefined)
let layer_formula = parse_formula(chem);
// determine the stoichiometry sum (for normalization)
let stoichiometry_sum = 0;
for (key of Object.keys(layer_formula)) {
stoichiometry_sum = stoichiometry_sum + layer_formula[key];
// determine the stoichiometry sum (for normalization)
let stoichiometry_sum = 0;
for (const key of Object.keys(layer_formula)) {
const amount = Number(layer_formula[key]);
if (isFinite(amount) && amount > 0) {
stoichiometry_sum += amount;
}
}
// determine the density using a weighted average of the elemental densities
let density_estimate = 0;
for (const key of Object.keys(layer_formula)) {
const amount = Number(layer_formula[key]);
if (!isFinite(amount) || amount <= 0) {
continue;
}
if (!elemPars[key] || !isFinite(elemPars[key].rho)) {
continue;
}
if (stoichiometry_sum > 0) {
density_estimate += (amount / stoichiometry_sum) * elemPars[key].rho;
}
}
rho = density_estimate;
if (rho > 0) {
alert("Warning: Estimated density from elemental constituents; please verify manually.");
}
}
}
// determine the density using on a weighted average of the elemental densities
let density_estimate = 0;
for (key of Object.keys(layer_formula)) {
if (layer_formula[key] != null) {
density_estimate = density_estimate + (layer_formula[key] / stoichiometry_sum) * elemPars[key].rho;
}
}
rho = density_estimate;
alert("Warning: The density for this layer is only an estimate!")
}
}
// Set value in appropriate cell
document.getElementById(rhoLi).value = rho;
}
@@ -3769,3 +3781,123 @@ function amIWeb() {
}
}
async function getDensity(formula) {
function normalizeCodFormula(codFormula) {
return codFormula.replace(/-/g, ' ').replace(/\s+/g, '').trim();
}
function sameFormula(formulaA, formulaB) {
const a = parse_formula(formulaA);
const b = parse_formula(formulaB);
const aKeys = Object.keys(a).sort();
const bKeys = Object.keys(b).sort();
if (aKeys.length !== bKeys.length) {
return false;
}
for (let i = 0; i < aKeys.length; i++) {
const key = aKeys[i];
if (key !== bKeys[i] || Math.abs(a[key] - b[key]) > 1e-8) {
return false;
}
}
return true;
}
function scaleFormula(formulaObj, factor) {
const scaled = {};
for (const key of Object.keys(formulaObj)) {
scaled[key] = formulaObj[key] * factor;
}
return scaled;
}
function sameFormulaObject(formulaA, formulaB) {
const aKeys = Object.keys(formulaA).sort();
const bKeys = Object.keys(formulaB).sort();
if (aKeys.length !== bKeys.length) {
return false;
}
for (let i = 0; i < aKeys.length; i++) {
const key = aKeys[i];
if (key !== bKeys[i] || Math.abs(formulaA[key] - formulaB[key]) > 1e-8) {
return false;
}
}
return true;
}
function molarMass(formulaObj) {
let mass = 0;
for (const key of Object.keys(formulaObj)) {
if (!elemPars[key] || !elemPars[key].A) {
return null;
}
mass += formulaObj[key] * elemPars[key].A;
}
return mass;
}
try {
const parsedFormula = parse_formula(formula);
const formulaMass = molarMass(parsedFormula);
if (formulaMass == null) {
return null;
}
const encodedFormula = encodeURIComponent(formula);
const codRes = await fetch(`https://www.crystallography.net/cod/result?formula=${encodedFormula}&format=json`);
if (!codRes.ok) {
throw new Error(`COD lookup failed with status ${codRes.status}`);
}
let records = await codRes.json();
if ((!Array.isArray(records) || records.length === 0) && Object.keys(parsedFormula).length > 0) {
const params = new URLSearchParams();
const elements = Object.keys(parsedFormula).sort();
elements.forEach((element, index) => {
params.append(`el${index + 1}`, element);
});
params.append('strictmin', elements.length);
params.append('strictmax', elements.length);
params.append('format', 'json');
const fallbackRes = await fetch(`https://www.crystallography.net/cod/result?${params.toString()}`);
if (!fallbackRes.ok) {
throw new Error(`COD fallback lookup failed with status ${fallbackRes.status}`);
}
records = await fallbackRes.json();
}
if (!Array.isArray(records) || records.length === 0) {
return null;
}
for (const record of records) {
const z = parseFloat(record.Z);
const vol = parseFloat(record.vol);
if (!isFinite(z) || !isFinite(vol) || z <= 0 || vol <= 0) {
continue;
}
const recordFormula = normalizeCodFormula(record.formula || '');
if (!sameFormula(formula, recordFormula)) {
continue;
}
const cellFormula = normalizeCodFormula(record.cellformula || '');
if (cellFormula !== '') {
const parsedCellFormula = parse_formula(cellFormula);
const expectedCellFormula = scaleFormula(parsedFormula, z);
if (!sameFormulaObject(parsedCellFormula, expectedCellFormula)) {
continue;
}
}
// Convert unit-cell data (A^3, Z) into density in g/cm^3.
return 1.66053906660 * z * formulaMass / vol;
}
} catch (error) {
console.error("Error fetching data from COD:", error);
}
return null;
}