tmmcore 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/phase.js +9 -3
- package/src/tmmWasm.js +931 -839
- package/src/tmm_kernel.c +97 -27
- package/src/tmm_kernel.wasm +0 -0
package/src/tmm_kernel.c
CHANGED
|
@@ -1324,25 +1324,47 @@ void tmm_phase_spectrum(const double *lambdas, const double *omegas, int nLam,
|
|
|
1324
1324
|
* derivative, matching the point evaluator's base result.
|
|
1325
1325
|
*
|
|
1326
1326
|
* out : 10 as tmm_phase_one
|
|
1327
|
-
* deriv :
|
|
1328
|
-
* quantity 0 = dPhaseDeg, 1 = dGD, 2 = dGDD, 3 = dTOD
|
|
1327
|
+
* deriv : 10 × N [side][quantity][layer], side 0 = r, 1 = t,
|
|
1328
|
+
* quantity 0 = dPhaseDeg, 1 = dGD, 2 = dGDD, 3 = dTOD,
|
|
1329
|
+
* 4 = d(ln |coefficient|²)/dd, the relative intensity
|
|
1330
|
+
* derivative, which the amplitude ratio in ellipsometry
|
|
1331
|
+
* needs and which costs nothing here: it is the real part
|
|
1332
|
+
* of the same logarithmic derivative the phase is the
|
|
1333
|
+
* imaginary part of
|
|
1329
1334
|
*
|
|
1330
1335
|
* The prefix/suffix decomposition cannot carry a rescaling, so if the matrix
|
|
1331
1336
|
* product overflows, `out` is still filled from the plain path and every entry
|
|
1332
1337
|
* of `deriv` is set to NaN. */
|
|
1333
1338
|
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
for (int k = 0; k < N; k++) layerN[k] = jread(&layerJets[8 * k]);
|
|
1342
|
-
jet sine;
|
|
1343
|
-
if (sinJet) sine = jread(sinJet);
|
|
1344
|
-
const jet *sinePtr = sinJet ? &sine : NULL;
|
|
1339
|
+
/* Scratch for one wavelength of the phase Jacobian: per-layer matrices with
|
|
1340
|
+
* their thickness derivatives, and the prefix/suffix products. Allocated once
|
|
1341
|
+
* per call by the entry points below, so the batched one reuses it across the
|
|
1342
|
+
* whole grid. */
|
|
1343
|
+
typedef struct {
|
|
1344
|
+
jmat2 *layerM, *layerDM, *prefix, *suffix;
|
|
1345
|
+
} jphase_jacobian_scratch;
|
|
1345
1346
|
|
|
1347
|
+
static jphase_jacobian_scratch jphase_jacobian_alloc(int N) {
|
|
1348
|
+
int M = (N > 0 ? N : 1);
|
|
1349
|
+
jphase_jacobian_scratch s;
|
|
1350
|
+
s.layerM = (jmat2 *)malloc(sizeof(jmat2) * M);
|
|
1351
|
+
s.layerDM = (jmat2 *)malloc(sizeof(jmat2) * M);
|
|
1352
|
+
s.prefix = (jmat2 *)malloc(sizeof(jmat2) * (N + 1));
|
|
1353
|
+
s.suffix = (jmat2 *)malloc(sizeof(jmat2) * (N + 1));
|
|
1354
|
+
return s;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
static void jphase_jacobian_free(jphase_jacobian_scratch s) {
|
|
1358
|
+
free(s.layerM); free(s.layerDM); free(s.prefix); free(s.suffix);
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
/* One wavelength of the phase Jacobian; see tmm_phase_jacobian for the layout
|
|
1362
|
+
* of `out` and `deriv`. */
|
|
1363
|
+
static void jphase_jacobian_core(double lambda, double omega, double theta_deg, int pol,
|
|
1364
|
+
jet n0, jet ns, const jet *layerN, const double *thick, int N,
|
|
1365
|
+
const jet *sinePtr, double *out, double *deriv,
|
|
1366
|
+
jphase_jacobian_scratch s) {
|
|
1367
|
+
jmat2 *layerM = s.layerM, *layerDM = s.layerDM, *prefix = s.prefix, *suffix = s.suffix;
|
|
1346
1368
|
jet wavelength = jwavelength(lambda, omega);
|
|
1347
1369
|
jet incidentSine = sinePtr ? *sinePtr : jconst(sin(theta_deg * PI / 180.0), 0.0);
|
|
1348
1370
|
jet incidentCosine = jincident_cos(n0, incidentSine, sinePtr != NULL, theta_deg);
|
|
@@ -1350,9 +1372,6 @@ void tmm_phase_jacobian(double lambda, double omega, double theta_deg, int pol,
|
|
|
1350
1372
|
jet substrateCosine = jsnell_cos(n0, incidentSine, ns);
|
|
1351
1373
|
jet substrateEta = jadmittance(ns, substrateCosine, pol);
|
|
1352
1374
|
|
|
1353
|
-
int M = (N > 0 ? N : 1);
|
|
1354
|
-
jmat2 *layerM = (jmat2 *)malloc(sizeof(jmat2) * M);
|
|
1355
|
-
jmat2 *layerDM = (jmat2 *)malloc(sizeof(jmat2) * M);
|
|
1356
1375
|
for (int k = 0; k < N; k++) {
|
|
1357
1376
|
/* Match jphase_core's skip rule for invalid negative/NaN thicknesses,
|
|
1358
1377
|
* while retaining the useful derivative of a zero-thickness layer. */
|
|
@@ -1366,8 +1385,6 @@ void tmm_phase_jacobian(double lambda, double omega, double theta_deg, int pol,
|
|
|
1366
1385
|
&layerM[k], &layerDM[k]);
|
|
1367
1386
|
}
|
|
1368
1387
|
|
|
1369
|
-
jmat2 *prefix = (jmat2 *)malloc(sizeof(jmat2) * (N + 1));
|
|
1370
|
-
jmat2 *suffix = (jmat2 *)malloc(sizeof(jmat2) * (N + 1));
|
|
1371
1388
|
prefix[0] = jidentity();
|
|
1372
1389
|
int overflowed = 0;
|
|
1373
1390
|
for (int k = 0; k < N; k++) {
|
|
@@ -1377,8 +1394,7 @@ void tmm_phase_jacobian(double lambda, double omega, double theta_deg, int pol,
|
|
|
1377
1394
|
|
|
1378
1395
|
if (overflowed) {
|
|
1379
1396
|
jphase_core(lambda, omega, theta_deg, pol, n0, ns, layerN, thick, N, sinePtr, out);
|
|
1380
|
-
for (long i = 0; i <
|
|
1381
|
-
free(layerN); free(layerM); free(layerDM); free(prefix); free(suffix);
|
|
1397
|
+
for (long i = 0; i < 10L * N; i++) deriv[i] = NAN;
|
|
1382
1398
|
return;
|
|
1383
1399
|
}
|
|
1384
1400
|
|
|
@@ -1396,15 +1412,69 @@ void tmm_phase_jacobian(double lambda, double omega, double theta_deg, int pol,
|
|
|
1396
1412
|
&dReflection, &dTransmission);
|
|
1397
1413
|
const jet sides[2] = { dReflection, dTransmission };
|
|
1398
1414
|
const jet base[2] = { coefficients.reflection, coefficients.transmission };
|
|
1399
|
-
for (int
|
|
1415
|
+
for (int s2 = 0; s2 < 2; s2++) {
|
|
1400
1416
|
cx dd[4];
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
deriv[((long)
|
|
1417
|
+
/* d(ln coefficient)/dd: the imaginary part is the phase derivative
|
|
1418
|
+
* (negated for Macleod's sign), the real part is d(ln |c|)/dd, so
|
|
1419
|
+
* twice it is the relative derivative of |c|². */
|
|
1420
|
+
jderivs(jdiv(sides[s2], base[s2]), dd);
|
|
1421
|
+
deriv[((long)s2 * 5 + 0) * N + k] = -dd[0].im * 180.0 / PI;
|
|
1422
|
+
deriv[((long)s2 * 5 + 1) * N + k] = dd[1].im;
|
|
1423
|
+
deriv[((long)s2 * 5 + 2) * N + k] = dd[2].im;
|
|
1424
|
+
deriv[((long)s2 * 5 + 3) * N + k] = dd[3].im;
|
|
1425
|
+
deriv[((long)s2 * 5 + 4) * N + k] = 2.0 * dd[0].re;
|
|
1406
1426
|
}
|
|
1407
1427
|
}
|
|
1428
|
+
}
|
|
1408
1429
|
|
|
1409
|
-
|
|
1430
|
+
TMM_EXPORT("tmm_phase_jacobian")
|
|
1431
|
+
void tmm_phase_jacobian(double lambda, double omega, double theta_deg, int pol,
|
|
1432
|
+
const double *n0jet, const double *nsjet,
|
|
1433
|
+
const double *layerJets, const double *thick, int N,
|
|
1434
|
+
const double *sinJet, double *out, double *deriv) {
|
|
1435
|
+
jet *layerN = (jet *)malloc(sizeof(jet) * (N > 0 ? N : 1));
|
|
1436
|
+
for (int k = 0; k < N; k++) layerN[k] = jread(&layerJets[8 * k]);
|
|
1437
|
+
jet sine;
|
|
1438
|
+
if (sinJet) sine = jread(sinJet);
|
|
1439
|
+
jphase_jacobian_scratch scratch = jphase_jacobian_alloc(N);
|
|
1440
|
+
jphase_jacobian_core(lambda, omega, theta_deg, pol, jread(n0jet), jread(nsjet),
|
|
1441
|
+
layerN, thick, N, sinJet ? &sine : NULL, out, deriv, scratch);
|
|
1442
|
+
jphase_jacobian_free(scratch);
|
|
1443
|
+
free(layerN);
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
/* ── Exported: batched phase Jacobian over a wavelength grid ─────────────────
|
|
1447
|
+
* tmm_phase_jacobian at every wavelength of a grid in one call, for a caller
|
|
1448
|
+
* fitting a whole measured spectrum of phase-derived quantities: ellipsometric
|
|
1449
|
+
* Ψ and Δ, or group delay. Inputs are laid out as for tmm_phase_spectrum.
|
|
1450
|
+
*
|
|
1451
|
+
* out : nLam × 10 as tmm_phase_spectrum
|
|
1452
|
+
* deriv : nLam × 10 × N per λ, the 10 × N block of tmm_phase_jacobian
|
|
1453
|
+
*
|
|
1454
|
+
* A wavelength whose matrix product overflows has its `out` filled from the
|
|
1455
|
+
* plain path and its whole `deriv` block set to NaN, as the single-wavelength
|
|
1456
|
+
* entry point does. */
|
|
1457
|
+
|
|
1458
|
+
TMM_EXPORT("tmm_phase_jacobian_spectrum")
|
|
1459
|
+
void tmm_phase_jacobian_spectrum(const double *lambdas, const double *omegas, int nLam,
|
|
1460
|
+
const double *n0jets, const double *nsjets,
|
|
1461
|
+
const double *matJets, const double *thick, int N,
|
|
1462
|
+
double theta_deg, int pol,
|
|
1463
|
+
const double *sinJets, double *out, double *deriv) {
|
|
1464
|
+
jet *layerN = (jet *)malloc(sizeof(jet) * (N > 0 ? N : 1));
|
|
1465
|
+
jphase_jacobian_scratch scratch = jphase_jacobian_alloc(N);
|
|
1466
|
+
for (int li = 0; li < nLam; li++) {
|
|
1467
|
+
for (int k = 0; k < N; k++) {
|
|
1468
|
+
long base = ((long)k * nLam + li) * 8;
|
|
1469
|
+
layerN[k] = jread(&matJets[base]);
|
|
1470
|
+
}
|
|
1471
|
+
jet sine;
|
|
1472
|
+
if (sinJets) sine = jread(&sinJets[8 * (long)li]);
|
|
1473
|
+
jphase_jacobian_core(lambdas[li], omegas[li], theta_deg, pol,
|
|
1474
|
+
jread(&n0jets[8 * (long)li]), jread(&nsjets[8 * (long)li]),
|
|
1475
|
+
layerN, thick, N, sinJets ? &sine : NULL,
|
|
1476
|
+
&out[10 * (long)li], &deriv[10L * N * li], scratch);
|
|
1477
|
+
}
|
|
1478
|
+
jphase_jacobian_free(scratch);
|
|
1479
|
+
free(layerN);
|
|
1410
1480
|
}
|
package/src/tmm_kernel.wasm
CHANGED
|
Binary file
|