/* Browser-side counterpart of magnet.predictors.predict_loss for static hosting. */ (function (root) { "use strict"; function numericValue(value, name) { if (typeof value !== "number" && (typeof value !== "string" || value.trim() === "")) { throw new TypeError(`${name} must be a finite number`); } const number = Number(value); if (!Number.isFinite(number)) { throw new TypeError(`${name} must be a finite number`); } return number; } function predictLoss(request, catalog) { if (!request || typeof request !== "object" || Array.isArray(request)) { throw new TypeError("prediction request must be an object"); } if (!catalog || !Array.isArray(catalog.models) || !Array.isArray(catalog.materials)) { throw new TypeError("catalog must contain models and materials arrays"); } const modelId = request.model_id === undefined ? "igse-v1" : request.model_id; const materialId = request.material_id; if (typeof modelId !== "string" || typeof materialId !== "string") { throw new TypeError("model_id and material_id must be strings"); } const frequencyHz = numericValue(request.frequency_hz, "frequency_hz"); if (frequencyHz <= 0) { throw new RangeError("frequency_hz must be greater than 0"); } const temperatureC = numericValue( request.temperature_c === undefined ? 25.0 : request.temperature_c, "temperature_c", ); const waveform = request.b_waveform_t; if (!Array.isArray(waveform) || waveform.length < 8 || waveform.length > 16_384) { throw new RangeError("b_waveform_t must contain between 8 and 16384 samples"); } const values = new Array(waveform.length); let minimum = Infinity; let maximum = -Infinity; for (let index = 0; index < waveform.length; index += 1) { const value = numericValue(waveform[index], `b_waveform_t[${index}]`); values[index] = value; if (value < minimum) minimum = value; if (value > maximum) maximum = value; } if (maximum === minimum) { throw new RangeError("waveform must vary over the cycle"); } const model = catalog.models.find((item) => item && item.id === modelId); if (!model) throw new Error(`unknown model: ${modelId}`); if (model.task !== "steady-state-loss") { throw new Error(`model ${modelId} does not support steady-state loss`); } if (model.status !== "available") { throw new Error(`model ${modelId} is not available for online inference`); } const material = catalog.materials.find( (item) => item && typeof item.id === "string" && item.id.toLowerCase() === materialId.toLowerCase(), ); if (!material) throw new Error(`unknown material: ${materialId}`); if (!material.igse) { throw new Error(`material ${materialId} has no iGSE coefficients`); } const { ki, alpha, beta } = material.igse; if (![ki, alpha, beta].every((value) => typeof value === "number" && Number.isFinite(value))) { throw new Error(`material ${materialId} has invalid iGSE coefficients`); } const warnings = []; const operatingRange = material.frequency_range_hz; if (operatingRange && !(operatingRange[0] <= frequencyHz && frequencyHz <= operatingRange[1])) { warnings.push("Frequency is outside the documented measurement range."); } if (temperatureC !== 25.0) { warnings.push("The iGSE baseline does not model temperature dependence."); } const deltaB = maximum - minimum; if (deltaB <= 0) { throw new RangeError("waveform peak-to-peak flux density must be positive"); } const periodS = 1.0 / frequencyHz; const dtS = periodS / values.length; const deltaFactor = Math.pow(deltaB, beta - alpha); let integrandSum = 0; for (let index = 0; index < values.length; index += 1) { const next = values[(index + 1) % values.length]; const dbDt = (next - values[index]) / dtS; integrandSum += ki * Math.pow(Math.abs(dbDt), alpha) * deltaFactor; } const energyPerM3 = integrandSum * dtS; const loss = frequencyHz * energyPerM3; if (!Number.isFinite(loss) || loss < 0) { throw new Error("prediction produced a non-finite result"); } return { task: "steady-state-loss", model_id: model.id, model_version: model.version, material_id: material.id, loss_w_per_m3: loss, frequency_hz: frequencyHz, temperature_c: temperatureC, waveform_points: values.length, units: { flux_density: "T", frequency: "Hz", temperature: "degC", loss: "W/m^3", }, warnings, citation: model.citation, }; } const api = Object.freeze({ predictLoss }); if (typeof module !== "undefined" && module.exports) module.exports = api; if (root) root.MagNetStaticPredictor = api; })(typeof window !== "undefined" ? window : undefined);