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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmmcore",
3
- "version": "0.3.0",
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 incidentCosine = incidentSineJet
253
- ? jetSqrt(jetSubtract(jetConstant(1), jetMultiply(incidentSine, incidentSine)))
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 incidentCosine = incidentSineJet
305
- ? jetSqrt(jetSubtract(jetConstant(1), jetMultiply(incidentSine, incidentSine)))
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 = csqrt(csub([1, 0], cmul(sinTheta0, sinTheta0)));
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 = csqrt(csub([1, 0], cmul(sinTheta0, sinTheta0)));
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 = csqrt(csub([1, 0], cmul(sinTheta0, sinTheta0)));
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 = csqrt(csub([1, 0], cmul(sinTheta0, sinTheta0)));
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 = csqrt_(csub(cmk(1.0, 0.0), cmul(sinTheta0, sinTheta0)));
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 = csqrt_(csub(cmk(1.0, 0.0), cmul(sinTheta0, sinTheta0)));
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 = csqrt_(csub(cmk(1.0, 0.0), cmul(sinTheta0, sinTheta0)));
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 = csqrt_(csub(cmk(1.0, 0.0), cmul(sinTheta0, sinTheta0)));
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 = csqrt_(csub(cmk(1.0, 0.0), cmul(sinTheta0, sinTheta0)));
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);
Binary file