tmmcore 0.3.0 → 0.3.1
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/index.js +1 -1
- package/src/phase.js +20 -9
- package/src/taylorJet.js +5 -0
- package/src/tmm.js +33 -8
- package/src/tmm_kernel.c +47 -17
- package/src/tmm_kernel.wasm +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tmmcore",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Transfer-matrix method for multilayer thin-film optics, with exact analytic derivatives and phase dispersion. JavaScript, C and WebAssembly.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"LICENSE"
|
|
21
21
|
],
|
|
22
22
|
"scripts": {
|
|
23
|
-
"test": "node tests/equivalence.mjs && node tests/growing_equivalence.mjs && node tests/growing_eval_equivalence.mjs",
|
|
23
|
+
"test": "node tests/equivalence.mjs && node tests/growing_equivalence.mjs && node tests/growing_eval_equivalence.mjs && node tests/absorbing_incident.mjs",
|
|
24
24
|
"compare": "node benchmarks/compare.mjs",
|
|
25
25
|
"examples": "node examples/01-single-layer.mjs && node examples/02-ar-coating.mjs && node examples/03-metal-mirror.mjs && node examples/04-refine.mjs && node examples/05-angle-map.mjs && node examples/06-needle.mjs && node examples/07-jacobian-map.mjs && node examples/08-group-delay.mjs",
|
|
26
26
|
"build:wasm": "emcc src/tmm_kernel.c -O3 --no-entry -sSTANDALONE_WASM=1 -sALLOW_MEMORY_GROWTH=1 -sEXPORTED_FUNCTIONS=_tmm_one,_tmm_spectrum,_tmm_jacobian,_tmm_needle_scan,_tmm_hessian,_tmm_phase_one,_tmm_phase_spectrum,_tmm_phase_jacobian,_tmm_monitor_curve,_tmm_deposition_spectra,_tmm_growing_eval_create,_tmm_growing_eval_set_top,_tmm_growing_eval_sample,_tmm_growing_eval_free,_malloc,_free -o src/tmm_kernel.wasm",
|
package/src/index.js
CHANGED
|
@@ -29,7 +29,7 @@ export {
|
|
|
29
29
|
tmmNeedleScan,
|
|
30
30
|
// Low-level primitives, for building variants on the same conventions
|
|
31
31
|
cadd, csub, cmul, cdiv, cabs2, cconj, csqrt, ccos, csin, creal, cimag,
|
|
32
|
-
matmul, rescaleMatrix, snellCosTheta, layerMatrix, cmatvec,
|
|
32
|
+
matmul, rescaleMatrix, snellCosTheta, incidentCosTheta, layerMatrix, cmatvec,
|
|
33
33
|
} from './tmm.js';
|
|
34
34
|
|
|
35
35
|
export {
|
package/src/phase.js
CHANGED
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
jetDerivatives,
|
|
47
47
|
jetDivide,
|
|
48
48
|
jetMultiply,
|
|
49
|
+
jetRealPart,
|
|
49
50
|
jetScale,
|
|
50
51
|
jetSinCos,
|
|
51
52
|
jetSqrt,
|
|
@@ -124,11 +125,25 @@ function rescaleMatrix(matrix, threshold) {
|
|
|
124
125
|
return Math.log(scale);
|
|
125
126
|
}
|
|
126
127
|
|
|
128
|
+
// The transverse invariant is Re(n0) sinθ0, as in tmm.js: an absorbing incident
|
|
129
|
+
// medium carries a wave whose amplitude falls along the normal only.
|
|
127
130
|
function snellCosine(incidentIndex, incidentSine, layerIndex) {
|
|
128
|
-
const layerSine = jetDivide(jetMultiply(incidentIndex, incidentSine), layerIndex);
|
|
131
|
+
const layerSine = jetDivide(jetMultiply(jetRealPart(incidentIndex), incidentSine), layerIndex);
|
|
129
132
|
return jetSqrt(jetSubtract(jetConstant(1), jetMultiply(layerSine, layerSine)));
|
|
130
133
|
}
|
|
131
134
|
|
|
135
|
+
// cosθ0 of the incident medium: the plain cosine while the index is real at
|
|
136
|
+
// every order, otherwise from the same real invariant as the layers, so the
|
|
137
|
+
// incident admittance is sqrt(N0² − n0² sin²θ0) and matches tmm.js.
|
|
138
|
+
function incidentCosine(incidentIndex, incidentSine, incidentSineJet, thetaDeg) {
|
|
139
|
+
if (incidentIndex.some(coefficient => coefficient[1] !== 0)) {
|
|
140
|
+
return snellCosine(incidentIndex, incidentSine, incidentIndex);
|
|
141
|
+
}
|
|
142
|
+
return incidentSineJet
|
|
143
|
+
? jetSqrt(jetSubtract(jetConstant(1), jetMultiply(incidentSine, incidentSine)))
|
|
144
|
+
: jetConstant(Math.cos(thetaDeg * Math.PI / 180));
|
|
145
|
+
}
|
|
146
|
+
|
|
132
147
|
function admittance(index, cosine, polarization) {
|
|
133
148
|
return polarization === 's'
|
|
134
149
|
? jetMultiply(index, cosine)
|
|
@@ -249,10 +264,8 @@ export function tmmCoefficientJets({
|
|
|
249
264
|
layers,
|
|
250
265
|
}) {
|
|
251
266
|
const incidentSine = incidentSineJet || jetConstant(Math.sin(thetaDeg * Math.PI / 180));
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
: jetConstant(Math.cos(thetaDeg * Math.PI / 180));
|
|
255
|
-
const incidentEta = admittance(incidentIndexJet, incidentCosine, polarization);
|
|
267
|
+
const incidentEta = admittance(incidentIndexJet,
|
|
268
|
+
incidentCosine(incidentIndexJet, incidentSine, incidentSineJet, thetaDeg), polarization);
|
|
256
269
|
const substrateCosine = snellCosine(incidentIndexJet, incidentSine, substrateIndexJet);
|
|
257
270
|
const substrateEta = admittance(substrateIndexJet, substrateCosine, polarization);
|
|
258
271
|
|
|
@@ -301,10 +314,8 @@ export function tmmCoefficientThicknessJets(options) {
|
|
|
301
314
|
layers,
|
|
302
315
|
} = options;
|
|
303
316
|
const incidentSine = incidentSineJet || jetConstant(Math.sin(thetaDeg * Math.PI / 180));
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
: jetConstant(Math.cos(thetaDeg * Math.PI / 180));
|
|
307
|
-
const incidentEta = admittance(incidentIndexJet, incidentCosine, polarization);
|
|
317
|
+
const incidentEta = admittance(incidentIndexJet,
|
|
318
|
+
incidentCosine(incidentIndexJet, incidentSine, incidentSineJet, thetaDeg), polarization);
|
|
308
319
|
const substrateCosine = snellCosine(incidentIndexJet, incidentSine, substrateIndexJet);
|
|
309
320
|
const substrateEta = admittance(substrateIndexJet, substrateCosine, polarization);
|
|
310
321
|
const layerData = layers.map((layer) => {
|
package/src/taylorJet.js
CHANGED
|
@@ -233,6 +233,11 @@ export function jetWithImaginaryPart(realJet, imaginaryJet) {
|
|
|
233
233
|
return realJet.map((coefficient, index) => [coefficient[0], imaginaryJet[index][0]]);
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
/** The real part of a complex jet, order by order. */
|
|
237
|
+
export function jetRealPart(jet) {
|
|
238
|
+
return jet.map(coefficient => [coefficient[0], 0]);
|
|
239
|
+
}
|
|
240
|
+
|
|
236
241
|
/** Floor the value at `minimum`, flattening the jet to a constant when it bites. */
|
|
237
242
|
export function jetClampRealMinimum(jet, minimum) {
|
|
238
243
|
return jet[0][0] >= minimum ? jet : jetConstant(minimum);
|
package/src/tmm.js
CHANGED
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
* it to float64 round-off (see tests/).
|
|
21
21
|
*
|
|
22
22
|
* References:
|
|
23
|
-
* • Macleod, Thin-Film Optical Filters 5th ed., §2.4, Eqs. 2.111, 2.123–2.125
|
|
23
|
+
* • Macleod, Thin-Film Optical Filters 5th ed., §2.4, Eqs. 2.111, 2.123–2.125;
|
|
24
|
+
* Eq. 2.83 (transmittance out of an absorbing incident medium);
|
|
25
|
+
* §10.2, Eqs. 10.11–10.13 (tilted admittances from the real invariant n0 sinθ0)
|
|
24
26
|
* • Sullivan & Dobrowolski, Appl. Opt. 35, 5484 (1996), Eqs. (3)–(6)
|
|
25
27
|
* • Tikhonravov, Trubetskov & DeBell, Appl. Opt. 35, 5493 (1996)
|
|
26
28
|
*/
|
|
@@ -90,14 +92,37 @@ function rescaleMatrix(M) {
|
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
// ── Snell's law ───────────────────────────────────────────────────────────────
|
|
95
|
+
//
|
|
96
|
+
// Every wave in the stack shares one transverse wavevector, n0 sinθ0 with n0
|
|
97
|
+
// the REAL part of the incident index (Macleod 5th ed., §10.2, Eqs. 10.11 to
|
|
98
|
+
// 10.13). An absorbing incident medium then carries an inhomogeneous wave whose
|
|
99
|
+
// amplitude falls along the normal only, and each medium's cosθ follows from
|
|
100
|
+
// that one real invariant. Putting the complex index into the invariant would
|
|
101
|
+
// make the amplitude vary along the interface, so energy would flow sideways
|
|
102
|
+
// inside lossless layers and R + T would be wrong by a term linear in the
|
|
103
|
+
// incident k at oblique incidence.
|
|
93
104
|
|
|
94
105
|
function snellCosTheta(n0, sinTheta0, nj) {
|
|
95
|
-
// sinThetaJ = n0 * sinTheta0 / nj (complex)
|
|
96
|
-
const sinThetaJ = cdiv(cmul(n0, sinTheta0), nj);
|
|
106
|
+
// sinThetaJ = Re(n0) * sinTheta0 / nj (complex)
|
|
107
|
+
const sinThetaJ = cdiv(cmul([n0[0], 0], sinTheta0), nj);
|
|
97
108
|
// cosTheta = sqrt(1 - sin²θ)
|
|
98
109
|
return csqrt(csub([1, 0], cmul(sinThetaJ, sinThetaJ)));
|
|
99
110
|
}
|
|
100
111
|
|
|
112
|
+
// cosθ0 of the incident medium. A transparent medium keeps the plain
|
|
113
|
+
// sqrt(1 − sin²θ0). An absorbing one takes its cosine from the same real
|
|
114
|
+
// invariant as the layers, so its tilted admittance is sqrt(N0² − n0² sin²θ0)
|
|
115
|
+
// and the incident wave belongs to the same boundary problem as the rest of
|
|
116
|
+
// the stack. R + T then departs from 1 for lossless layers only by the
|
|
117
|
+
// interference of the incident and reflected waves in the absorbing medium,
|
|
118
|
+
// the term Macleod's Eq. 2.83 carries, which is second order in k0 for a
|
|
119
|
+
// bare interface.
|
|
120
|
+
function incidentCosTheta(n0, sinTheta0) {
|
|
121
|
+
return n0[1] === 0
|
|
122
|
+
? csqrt(csub([1, 0], cmul(sinTheta0, sinTheta0)))
|
|
123
|
+
: snellCosTheta(n0, sinTheta0, n0);
|
|
124
|
+
}
|
|
125
|
+
|
|
101
126
|
// ── Layer characteristic matrix ───────────────────────────────────────────────
|
|
102
127
|
|
|
103
128
|
function layerMatrix(nj, dj_nm, lambda_nm, cosTheta_j, pol) {
|
|
@@ -150,7 +175,7 @@ function layerMatrix(nj, dj_nm, lambda_nm, cosTheta_j, pol) {
|
|
|
150
175
|
*/
|
|
151
176
|
export function tmm(lambda_nm, theta_deg, pol, n0, ns, layers) {
|
|
152
177
|
const sinTheta0 = [Math.sin(theta_deg * Math.PI / 180), 0];
|
|
153
|
-
const cosTheta0 =
|
|
178
|
+
const cosTheta0 = incidentCosTheta(n0, sinTheta0);
|
|
154
179
|
|
|
155
180
|
// Admittance of incident medium
|
|
156
181
|
const eta0 = pol === 's'
|
|
@@ -241,7 +266,7 @@ function cmatvec(M, v) {
|
|
|
241
266
|
export function tmmNeedleScan(lambda_nm, theta_deg, pol, n0, ns, layers,
|
|
242
267
|
candidateNs, intraFracs = []) {
|
|
243
268
|
const sinTheta0 = [Math.sin(theta_deg * Math.PI / 180), 0];
|
|
244
|
-
const cosTheta0 =
|
|
269
|
+
const cosTheta0 = incidentCosTheta(n0, sinTheta0);
|
|
245
270
|
const eta0 = pol === 's' ? cmul(n0, cosTheta0) : cdiv(n0, cosTheta0);
|
|
246
271
|
const cosThetaS = snellCosTheta(n0, sinTheta0, ns);
|
|
247
272
|
const etaS = pol === 's' ? cmul(ns, cosThetaS) : cdiv(ns, cosThetaS);
|
|
@@ -368,7 +393,7 @@ export function tmmNeedleScan(lambda_nm, theta_deg, pol, n0, ns, layers,
|
|
|
368
393
|
// `metrics()` in tmmNeedleScan.
|
|
369
394
|
export function tmmThicknessJacobian(lambda_nm, theta_deg, pol, n0, ns, layers) {
|
|
370
395
|
const sinTheta0 = [Math.sin(theta_deg * Math.PI / 180), 0];
|
|
371
|
-
const cosTheta0 =
|
|
396
|
+
const cosTheta0 = incidentCosTheta(n0, sinTheta0);
|
|
372
397
|
const eta0 = pol === 's' ? cmul(n0, cosTheta0) : cdiv(n0, cosTheta0);
|
|
373
398
|
const cosThetaS = snellCosTheta(n0, sinTheta0, ns);
|
|
374
399
|
const etaS = pol === 's' ? cmul(ns, cosThetaS) : cdiv(ns, cosThetaS);
|
|
@@ -464,7 +489,7 @@ export function tmmThicknessJacobian(lambda_nm, theta_deg, pol, n0, ns, layers)
|
|
|
464
489
|
// (tests/hessian_fd_validation.mjs).*
|
|
465
490
|
export function tmmThicknessHessian(lambda_nm, theta_deg, pol, n0, ns, layers) {
|
|
466
491
|
const sinTheta0 = [Math.sin(theta_deg * Math.PI / 180), 0];
|
|
467
|
-
const cosTheta0 =
|
|
492
|
+
const cosTheta0 = incidentCosTheta(n0, sinTheta0);
|
|
468
493
|
const eta0 = pol === 's' ? cmul(n0, cosTheta0) : cdiv(n0, cosTheta0);
|
|
469
494
|
const cosThetaS = snellCosTheta(n0, sinTheta0, ns);
|
|
470
495
|
const etaS = pol === 's' ? cmul(ns, cosThetaS) : cdiv(ns, cosThetaS);
|
|
@@ -598,5 +623,5 @@ export function tmmThicknessHessian(lambda_nm, theta_deg, pol, n0, ns, layers) {
|
|
|
598
623
|
|
|
599
624
|
export {
|
|
600
625
|
cadd, csub, cmul, cdiv, cabs2, cconj, csqrt, ccos, csin, creal, cimag,
|
|
601
|
-
matmul, rescaleMatrix, snellCosTheta, layerMatrix, cmatvec
|
|
626
|
+
matmul, rescaleMatrix, snellCosTheta, incidentCosTheta, layerMatrix, cmatvec
|
|
602
627
|
};
|
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);
|
|
@@ -1313,9 +1345,7 @@ void tmm_phase_jacobian(double lambda, double omega, double theta_deg, int pol,
|
|
|
1313
1345
|
|
|
1314
1346
|
jet wavelength = jwavelength(lambda, omega);
|
|
1315
1347
|
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);
|
|
1348
|
+
jet incidentCosine = jincident_cos(n0, incidentSine, sinePtr != NULL, theta_deg);
|
|
1319
1349
|
jet incidentEta = jadmittance(n0, incidentCosine, pol);
|
|
1320
1350
|
jet substrateCosine = jsnell_cos(n0, incidentSine, ns);
|
|
1321
1351
|
jet substrateEta = jadmittance(ns, substrateCosine, pol);
|
package/src/tmm_kernel.wasm
CHANGED
|
Binary file
|