tmmcore 0.3.0 → 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 +3 -3
- package/src/index.js +1 -1
- package/src/phase.js +29 -12
- package/src/taylorJet.js +5 -0
- package/src/tmm.js +33 -8
- package/src/tmmWasm.js +931 -839
- package/src/tmm_kernel.c +144 -44
- package/src/tmm_kernel.wasm +0 -0
package/src/tmm_kernel.c
CHANGED
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
* Builds with any C99 compiler; no dependencies beyond libm.
|
|
24
24
|
*
|
|
25
25
|
* References:
|
|
26
|
-
* • Macleod, Thin-Film Optical Filters 5th ed., §2.4, Eqs. 2.111, 2.123–2.125
|
|
26
|
+
* • Macleod, Thin-Film Optical Filters 5th ed., §2.4, Eqs. 2.111, 2.123–2.125;
|
|
27
|
+
* Eq. 2.83; §10.2, Eqs. 10.11–10.13 (tilted admittances, real invariant)
|
|
27
28
|
* • Sullivan & Dobrowolski, Appl. Opt. 35, 5484 (1996), Eqs. (3)–(6)
|
|
28
29
|
* • Tikhonravov, Trubetskov & DeBell, Appl. Opt. 35, 5493 (1996)
|
|
29
30
|
* • Birge & Kärtner, Appl. Opt. 45, 1478 (2006) [phase dispersion]
|
|
@@ -110,13 +111,24 @@ static inline vec2 cmatvec(mat2 M, vec2 v) {
|
|
|
110
111
|
return o;
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
/* ── Snell's law: cosθ_j from incident (n0, sinθ0) into medium nj ───────────
|
|
114
|
+
/* ── Snell's law: cosθ_j from incident (n0, sinθ0) into medium nj ───────────
|
|
115
|
+
* The transverse invariant is Re(n0) sinθ0 (Macleod 5th ed., §10.2, Eqs.
|
|
116
|
+
* 10.11–10.13): an absorbing incident medium carries a wave whose amplitude
|
|
117
|
+
* falls along the normal only, and every medium's cosθ follows from that one
|
|
118
|
+
* real invariant. Mirrors snellCosTheta / incidentCosTheta in tmm.js. */
|
|
114
119
|
|
|
115
120
|
static inline cx snellCosTheta(cx n0, cx sinTheta0, cx nj) {
|
|
116
|
-
cx sinThetaJ = cdiv(cmul(n0, sinTheta0), nj);
|
|
121
|
+
cx sinThetaJ = cdiv(cmul(cmk(n0.re, 0.0), sinTheta0), nj);
|
|
117
122
|
return csqrt_(csub(cmk(1.0, 0.0), cmul(sinThetaJ, sinThetaJ)));
|
|
118
123
|
}
|
|
119
124
|
|
|
125
|
+
/* cosθ0 of the incident medium: the plain cosine for a transparent medium,
|
|
126
|
+
* otherwise from the same real invariant as the layers. */
|
|
127
|
+
static inline cx incidentCosTheta(cx n0, cx sinTheta0) {
|
|
128
|
+
if (n0.im == 0.0) return csqrt_(csub(cmk(1.0, 0.0), cmul(sinTheta0, sinTheta0)));
|
|
129
|
+
return snellCosTheta(n0, sinTheta0, n0);
|
|
130
|
+
}
|
|
131
|
+
|
|
120
132
|
/* ── Layer characteristic matrix (pol: 0 = s, 1 = p) ─────────────────────── */
|
|
121
133
|
|
|
122
134
|
static inline mat2 layerMatrix(cx nj, double dj_nm, double lambda_nm, cx cosTheta_j, int pol) {
|
|
@@ -149,7 +161,7 @@ static void tmm_core(double lambda_nm, double theta_deg, int pol,
|
|
|
149
161
|
cx n0, cx ns, const double *layers, int N,
|
|
150
162
|
double *outR, double *outT, double *outA) {
|
|
151
163
|
cx sinTheta0 = cmk(sin(theta_deg * PI / 180.0), 0.0);
|
|
152
|
-
cx cosTheta0 =
|
|
164
|
+
cx cosTheta0 = incidentCosTheta(n0, sinTheta0);
|
|
153
165
|
|
|
154
166
|
cx eta0 = (pol == 0) ? cmul(n0, cosTheta0) : cdiv(n0, cosTheta0);
|
|
155
167
|
|
|
@@ -314,7 +326,7 @@ void tmm_monitor_curve(double lambda_nm, double theta_deg,
|
|
|
314
326
|
cx ns = cmk(ns_re, ns_im);
|
|
315
327
|
cx ng = cmk(ng_re, ng_im);
|
|
316
328
|
cx sinTheta0 = cmk(sin(theta_deg * PI / 180.0), 0.0);
|
|
317
|
-
cx cosTheta0 =
|
|
329
|
+
cx cosTheta0 = incidentCosTheta(n0, sinTheta0);
|
|
318
330
|
|
|
319
331
|
for (int pol = 0; pol < 2; pol++) {
|
|
320
332
|
cx eta0 = (pol == 0) ? cmul(n0, cosTheta0) : cdiv(n0, cosTheta0);
|
|
@@ -392,11 +404,11 @@ int tmm_deposition_spectra(const double *lambdas, int nLam,
|
|
|
392
404
|
}
|
|
393
405
|
|
|
394
406
|
cx sinTheta0 = cmk(sin(theta_deg * PI / 180.0), 0.0);
|
|
395
|
-
cx cosTheta0 = csqrt_(csub(cmk(1.0, 0.0), cmul(sinTheta0, sinTheta0)));
|
|
396
407
|
|
|
397
408
|
for (int li = 0; li < nLam; li++) {
|
|
398
409
|
cx n0 = cmk(n0arr[2 * li + 0], n0arr[2 * li + 1]);
|
|
399
410
|
cx ns = cmk(nsarr[2 * li + 0], nsarr[2 * li + 1]);
|
|
411
|
+
cx cosTheta0 = incidentCosTheta(n0, sinTheta0);
|
|
400
412
|
for (int pol = 0; pol < 2; pol++) {
|
|
401
413
|
size_t at = (size_t)pol * nLam + li;
|
|
402
414
|
eta0v[at] = (pol == 0) ? cmul(n0, cosTheta0) : cdiv(n0, cosTheta0);
|
|
@@ -503,13 +515,13 @@ growing_eval *tmm_growing_eval_create(const double *lambdas, int nLam,
|
|
|
503
515
|
}
|
|
504
516
|
|
|
505
517
|
h->sinTheta0 = cmk(sin(theta_deg * PI / 180.0), 0.0);
|
|
506
|
-
cx cosTheta0 = csqrt_(csub(cmk(1.0, 0.0), cmul(h->sinTheta0, h->sinTheta0)));
|
|
507
518
|
|
|
508
519
|
for (int li = 0; li < nLam; li++) {
|
|
509
520
|
h->lam[li] = lambdas[li];
|
|
510
521
|
cx n0 = cmk(n0arr[2 * li + 0], n0arr[2 * li + 1]);
|
|
511
522
|
cx ns = cmk(nsarr[2 * li + 0], nsarr[2 * li + 1]);
|
|
512
523
|
h->n0[li] = n0;
|
|
524
|
+
cx cosTheta0 = incidentCosTheta(n0, h->sinTheta0);
|
|
513
525
|
for (int pol = 0; pol < 2; pol++) {
|
|
514
526
|
size_t at = (size_t)pol * nLam + li;
|
|
515
527
|
h->eta0[at] = (pol == 0) ? cmul(n0, cosTheta0) : cdiv(n0, cosTheta0);
|
|
@@ -593,7 +605,7 @@ void tmm_jacobian(double lambda_nm, double theta_deg, int pol,
|
|
|
593
605
|
cx ns = cmk(ns_re, ns_im);
|
|
594
606
|
|
|
595
607
|
cx sinTheta0 = cmk(sin(theta_deg * PI / 180.0), 0.0);
|
|
596
|
-
cx cosTheta0 =
|
|
608
|
+
cx cosTheta0 = incidentCosTheta(n0, sinTheta0);
|
|
597
609
|
cx eta0 = (pol == 0) ? cmul(n0, cosTheta0) : cdiv(n0, cosTheta0);
|
|
598
610
|
cx cosThetaS = snellCosTheta(n0, sinTheta0, ns);
|
|
599
611
|
cx etaS = (pol == 0) ? cmul(ns, cosThetaS) : cdiv(ns, cosThetaS);
|
|
@@ -696,7 +708,7 @@ void tmm_needle_scan(double lambda_nm, double theta_deg, int pol,
|
|
|
696
708
|
double *base, double *gaps, double *intra) {
|
|
697
709
|
cx n0 = cmk(n0_re, n0_im), ns = cmk(ns_re, ns_im);
|
|
698
710
|
cx sinTheta0 = cmk(sin(theta_deg * PI / 180.0), 0.0);
|
|
699
|
-
cx cosTheta0 =
|
|
711
|
+
cx cosTheta0 = incidentCosTheta(n0, sinTheta0);
|
|
700
712
|
cx eta0 = (pol == 0) ? cmul(n0, cosTheta0) : cdiv(n0, cosTheta0);
|
|
701
713
|
cx cosThetaS = snellCosTheta(n0, sinTheta0, ns);
|
|
702
714
|
cx etaS = (pol == 0) ? cmul(ns, cosThetaS) : cdiv(ns, cosThetaS);
|
|
@@ -805,7 +817,7 @@ void tmm_hessian(double lambda_nm, double theta_deg, int pol,
|
|
|
805
817
|
cx ns = cmk(ns_re, ns_im);
|
|
806
818
|
|
|
807
819
|
cx sinTheta0 = cmk(sin(theta_deg * PI / 180.0), 0.0);
|
|
808
|
-
cx cosTheta0 =
|
|
820
|
+
cx cosTheta0 = incidentCosTheta(n0, sinTheta0);
|
|
809
821
|
cx eta0 = (pol == 0) ? cmul(n0, cosTheta0) : cdiv(n0, cosTheta0);
|
|
810
822
|
cx cosThetaS = snellCosTheta(n0, sinTheta0, ns);
|
|
811
823
|
cx etaS = (pol == 0) ? cmul(ns, cosThetaS) : cdiv(ns, cosThetaS);
|
|
@@ -1083,10 +1095,32 @@ static double jmatmag(jmat2 M) {
|
|
|
1083
1095
|
return magnitude;
|
|
1084
1096
|
}
|
|
1085
1097
|
|
|
1098
|
+
/* The real part of a jet, order by order. */
|
|
1099
|
+
static inline jet jreal(jet a) {
|
|
1100
|
+
jet o;
|
|
1101
|
+
for (int i = 0; i < JET_N; i++) o.c[i] = cmk(a.c[i].re, 0.0);
|
|
1102
|
+
return o;
|
|
1103
|
+
}
|
|
1104
|
+
static inline int jhas_imag(jet a) {
|
|
1105
|
+
for (int i = 0; i < JET_N; i++) if (a.c[i].im != 0.0) return 1;
|
|
1106
|
+
return 0;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
/* Same real invariant Re(n0) sinθ0 as snellCosTheta, in jet arithmetic. */
|
|
1086
1110
|
static inline jet jsnell_cos(jet n0, jet sin0, jet nj) {
|
|
1087
|
-
jet s = jdiv(jmul(n0, sin0), nj);
|
|
1111
|
+
jet s = jdiv(jmul(jreal(n0), sin0), nj);
|
|
1088
1112
|
return jsqrt_j(jsub(jconst(1.0, 0.0), jmul(s, s)));
|
|
1089
1113
|
}
|
|
1114
|
+
/* cosθ0 of the incident medium: from the same real invariant as the layers
|
|
1115
|
+
* when the medium absorbs at any order, otherwise the plain cosine, taken
|
|
1116
|
+
* from the sine jet when the caller supplied one. Mirrors incidentCosine in
|
|
1117
|
+
* phase.js. */
|
|
1118
|
+
static inline jet jincident_cos(jet n0, jet incidentSine, int fromSineJet, double theta_deg) {
|
|
1119
|
+
if (jhas_imag(n0)) return jsnell_cos(n0, incidentSine, n0);
|
|
1120
|
+
return fromSineJet
|
|
1121
|
+
? jsqrt_j(jsub(jconst(1.0, 0.0), jmul(incidentSine, incidentSine)))
|
|
1122
|
+
: jconst(cos(theta_deg * PI / 180.0), 0.0);
|
|
1123
|
+
}
|
|
1090
1124
|
static inline jet jadmittance(jet n, jet cosv, int pol) {
|
|
1091
1125
|
return (pol == 0) ? jmul(n, cosv) : jdiv(n, cosv);
|
|
1092
1126
|
}
|
|
@@ -1207,9 +1241,7 @@ static void jphase_core(double lambda, double omega, double theta_deg, int pol,
|
|
|
1207
1241
|
const jet *sinJet, double *out10) {
|
|
1208
1242
|
jet wavelength = jwavelength(lambda, omega);
|
|
1209
1243
|
jet incidentSine = sinJet ? *sinJet : jconst(sin(theta_deg * PI / 180.0), 0.0);
|
|
1210
|
-
jet incidentCosine = sinJet
|
|
1211
|
-
? jsqrt_j(jsub(jconst(1.0, 0.0), jmul(incidentSine, incidentSine)))
|
|
1212
|
-
: jconst(cos(theta_deg * PI / 180.0), 0.0);
|
|
1244
|
+
jet incidentCosine = jincident_cos(n0, incidentSine, sinJet != NULL, theta_deg);
|
|
1213
1245
|
jet incidentEta = jadmittance(n0, incidentCosine, pol);
|
|
1214
1246
|
jet substrateCosine = jsnell_cos(n0, incidentSine, ns);
|
|
1215
1247
|
jet substrateEta = jadmittance(ns, substrateCosine, pol);
|
|
@@ -1292,37 +1324,54 @@ void tmm_phase_spectrum(const double *lambdas, const double *omegas, int nLam,
|
|
|
1292
1324
|
* derivative, matching the point evaluator's base result.
|
|
1293
1325
|
*
|
|
1294
1326
|
* out : 10 as tmm_phase_one
|
|
1295
|
-
* deriv :
|
|
1296
|
-
* 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
|
|
1297
1334
|
*
|
|
1298
1335
|
* The prefix/suffix decomposition cannot carry a rescaling, so if the matrix
|
|
1299
1336
|
* product overflows, `out` is still filled from the plain path and every entry
|
|
1300
1337
|
* of `deriv` is set to NaN. */
|
|
1301
1338
|
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
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;
|
|
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
|
+
}
|
|
1313
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;
|
|
1314
1368
|
jet wavelength = jwavelength(lambda, omega);
|
|
1315
1369
|
jet incidentSine = sinePtr ? *sinePtr : jconst(sin(theta_deg * PI / 180.0), 0.0);
|
|
1316
|
-
jet incidentCosine = sinePtr
|
|
1317
|
-
? jsqrt_j(jsub(jconst(1.0, 0.0), jmul(incidentSine, incidentSine)))
|
|
1318
|
-
: jconst(cos(theta_deg * PI / 180.0), 0.0);
|
|
1370
|
+
jet incidentCosine = jincident_cos(n0, incidentSine, sinePtr != NULL, theta_deg);
|
|
1319
1371
|
jet incidentEta = jadmittance(n0, incidentCosine, pol);
|
|
1320
1372
|
jet substrateCosine = jsnell_cos(n0, incidentSine, ns);
|
|
1321
1373
|
jet substrateEta = jadmittance(ns, substrateCosine, pol);
|
|
1322
1374
|
|
|
1323
|
-
int M = (N > 0 ? N : 1);
|
|
1324
|
-
jmat2 *layerM = (jmat2 *)malloc(sizeof(jmat2) * M);
|
|
1325
|
-
jmat2 *layerDM = (jmat2 *)malloc(sizeof(jmat2) * M);
|
|
1326
1375
|
for (int k = 0; k < N; k++) {
|
|
1327
1376
|
/* Match jphase_core's skip rule for invalid negative/NaN thicknesses,
|
|
1328
1377
|
* while retaining the useful derivative of a zero-thickness layer. */
|
|
@@ -1336,8 +1385,6 @@ void tmm_phase_jacobian(double lambda, double omega, double theta_deg, int pol,
|
|
|
1336
1385
|
&layerM[k], &layerDM[k]);
|
|
1337
1386
|
}
|
|
1338
1387
|
|
|
1339
|
-
jmat2 *prefix = (jmat2 *)malloc(sizeof(jmat2) * (N + 1));
|
|
1340
|
-
jmat2 *suffix = (jmat2 *)malloc(sizeof(jmat2) * (N + 1));
|
|
1341
1388
|
prefix[0] = jidentity();
|
|
1342
1389
|
int overflowed = 0;
|
|
1343
1390
|
for (int k = 0; k < N; k++) {
|
|
@@ -1347,8 +1394,7 @@ void tmm_phase_jacobian(double lambda, double omega, double theta_deg, int pol,
|
|
|
1347
1394
|
|
|
1348
1395
|
if (overflowed) {
|
|
1349
1396
|
jphase_core(lambda, omega, theta_deg, pol, n0, ns, layerN, thick, N, sinePtr, out);
|
|
1350
|
-
for (long i = 0; i <
|
|
1351
|
-
free(layerN); free(layerM); free(layerDM); free(prefix); free(suffix);
|
|
1397
|
+
for (long i = 0; i < 10L * N; i++) deriv[i] = NAN;
|
|
1352
1398
|
return;
|
|
1353
1399
|
}
|
|
1354
1400
|
|
|
@@ -1366,15 +1412,69 @@ void tmm_phase_jacobian(double lambda, double omega, double theta_deg, int pol,
|
|
|
1366
1412
|
&dReflection, &dTransmission);
|
|
1367
1413
|
const jet sides[2] = { dReflection, dTransmission };
|
|
1368
1414
|
const jet base[2] = { coefficients.reflection, coefficients.transmission };
|
|
1369
|
-
for (int
|
|
1415
|
+
for (int s2 = 0; s2 < 2; s2++) {
|
|
1370
1416
|
cx dd[4];
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
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;
|
|
1376
1426
|
}
|
|
1377
1427
|
}
|
|
1428
|
+
}
|
|
1378
1429
|
|
|
1379
|
-
|
|
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);
|
|
1380
1480
|
}
|
package/src/tmm_kernel.wasm
CHANGED
|
Binary file
|