tmmcore 0.1.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/LICENSE +21 -0
- package/README.md +119 -0
- package/package.json +53 -0
- package/src/build.ps1 +121 -0
- package/src/index.js +35 -0
- package/src/tmm.js +602 -0
- package/src/tmmWasm.js +398 -0
- package/src/tmm_kernel.c +589 -0
- package/src/tmm_kernel.wasm +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Andrey Achapovsky
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)"
|
|
5
|
+
srcset="https://raw.githubusercontent.com/aai2k/tmmcore/main/docs/img/banner-on-dark.png">
|
|
6
|
+
<img alt="tmmcore"
|
|
7
|
+
src="https://raw.githubusercontent.com/aai2k/tmmcore/main/docs/img/banner-on-light.png"
|
|
8
|
+
width="640">
|
|
9
|
+
</picture>
|
|
10
|
+
|
|
11
|
+
**Transfer-matrix method for multilayer thin-film optics, with exact analytic derivatives.**
|
|
12
|
+
|
|
13
|
+
[](./LICENSE)
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
Takes a stack of layers and returns reflectance, transmittance and absorptance
|
|
18
|
+
for absorbing and dispersive materials, at any angle of incidence, in s and p
|
|
19
|
+
polarization. Alongside the spectra it returns the exact thickness Jacobian, the
|
|
20
|
+
exact thickness Hessian, and the needle-insertion P-function, computed
|
|
21
|
+
analytically rather than by finite differences.
|
|
22
|
+
|
|
23
|
+
Ships as JavaScript, as C, and as a WebAssembly build of the C. The JavaScript
|
|
24
|
+
has no dependencies and works on import. WebAssembly is opt-in and roughly an
|
|
25
|
+
order of magnitude faster.
|
|
26
|
+
|
|
27
|
+
**[Documentation](https://aai2k.github.io/tmmcore/)**
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install tmmcore
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The `.wasm` is prebuilt and included, so no Emscripten toolchain is required.
|
|
36
|
+
|
|
37
|
+
## Use
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
import { tmm } from 'tmmcore';
|
|
41
|
+
|
|
42
|
+
// A quarter-wave MgF2 layer on glass, at 550 nm, normal incidence.
|
|
43
|
+
const { R, T, A } = tmm(
|
|
44
|
+
550, // wavelength, nm
|
|
45
|
+
0, // angle of incidence, degrees from normal
|
|
46
|
+
's', // polarization: 's' or 'p'
|
|
47
|
+
[1.0, 0], // incident medium, ñ = [n, k]
|
|
48
|
+
[1.52, 0], // substrate
|
|
49
|
+
[{ n: [1.38, 0], d: 550 / (4 * 1.38) }] // quarter wave, thickness in nm
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
console.log(R); // 0.012600790214630274
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Layers run from the incident medium toward the substrate.
|
|
56
|
+
|
|
57
|
+
## Conventions
|
|
58
|
+
|
|
59
|
+
Mismatched conventions are the most common cause of two TMM codes disagreeing,
|
|
60
|
+
so check these first.
|
|
61
|
+
|
|
62
|
+
| | |
|
|
63
|
+
|---|---|
|
|
64
|
+
| Refractive index | ñ = n + i·k, with **k ≥ 0** for absorbing media |
|
|
65
|
+
| Time factor | exp(−iωt), so a wave exp(i(kz − ωt)) decays for k > 0 |
|
|
66
|
+
| Wavelength, thickness | nanometres |
|
|
67
|
+
| Angle | degrees from normal |
|
|
68
|
+
| Complex numbers | `[re, im]` pairs |
|
|
69
|
+
| Layer order | incident medium → substrate |
|
|
70
|
+
|
|
71
|
+
This is the complex conjugate of Macleod's convention. R, T and A are identical
|
|
72
|
+
under conjugation; phase-sensitive quantities are not.
|
|
73
|
+
|
|
74
|
+
## Verify it yourself
|
|
75
|
+
|
|
76
|
+
Two commands, neither needing anything but Node:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm test # the JavaScript and the C agree, to float64 round-off
|
|
80
|
+
npm run compare # and both agree with an independent implementation
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The first drives both implementations with identical inputs across absorbing,
|
|
84
|
+
dispersive and oblique-incidence cases and compares every returned quantity.
|
|
85
|
+
64,416 comparisons, worst disagreement 4.4e-16.
|
|
86
|
+
|
|
87
|
+
The second checks them against [Steven Byrnes'
|
|
88
|
+
`tmm`](https://github.com/sbyrnes321/tmm), written independently in Python under
|
|
89
|
+
the same complex-index convention, so only the mathematics is under test. 12,352
|
|
90
|
+
values, worst disagreement 8.6e-14, which is float64 accumulation noise over a
|
|
91
|
+
forty-layer matrix product.
|
|
92
|
+
|
|
93
|
+
Timing comparisons against four other packages are in the
|
|
94
|
+
[documentation](https://aai2k.github.io/tmmcore/comparison/), and need a Python
|
|
95
|
+
environment.
|
|
96
|
+
|
|
97
|
+
## Documentation
|
|
98
|
+
|
|
99
|
+
- [Getting started](https://aai2k.github.io/tmmcore/getting-started/)
|
|
100
|
+
- [API reference](https://aai2k.github.io/tmmcore/api/)
|
|
101
|
+
- [Examples](https://aai2k.github.io/tmmcore/examples/)
|
|
102
|
+
- [Validation](https://aai2k.github.io/tmmcore/validation/)
|
|
103
|
+
- [Comparison with other TMM packages](https://aai2k.github.io/tmmcore/comparison/)
|
|
104
|
+
|
|
105
|
+
## Using the C directly
|
|
106
|
+
|
|
107
|
+
`src/tmm_kernel.c` is C99 with no dependencies beyond libm. Drop it into a
|
|
108
|
+
project and compile:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
cc -std=c99 -O2 -c src/tmm_kernel.c
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Licence
|
|
115
|
+
|
|
116
|
+
[MIT](./LICENSE) © Andrey Achapovsky
|
|
117
|
+
|
|
118
|
+
Built for and used by [TFStudio](https://github.com/aai2k/TFStudio), an
|
|
119
|
+
open-source optical coating design application.
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tmmcore",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Transfer-matrix method for multilayer thin-film optics, with exact analytic derivatives. JavaScript, C and WebAssembly.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js",
|
|
9
|
+
"./tmm.js": "./src/tmm.js",
|
|
10
|
+
"./tmmWasm.js": "./src/tmmWasm.js",
|
|
11
|
+
"./tmm_kernel.wasm": "./src/tmm_kernel.wasm",
|
|
12
|
+
"./tmm_kernel.c": "./src/tmm_kernel.c",
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src/",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"test": "node tests/equivalence.mjs",
|
|
22
|
+
"compare": "node benchmarks/compare.mjs",
|
|
23
|
+
"examples": "node examples/01-single-layer.mjs && node examples/02-ar-coating.mjs && node examples/03-metal-mirror.mjs && node examples/04-refine.mjs",
|
|
24
|
+
"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,_malloc,_free -o src/tmm_kernel.wasm",
|
|
25
|
+
"build:native": "cc -std=c99 -pedantic -Wall -Wextra -O2 -c src/tmm_kernel.c -o tmm_kernel.o"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"thin-film",
|
|
29
|
+
"optics",
|
|
30
|
+
"transfer-matrix",
|
|
31
|
+
"tmm",
|
|
32
|
+
"multilayer",
|
|
33
|
+
"optical-coating",
|
|
34
|
+
"reflectance",
|
|
35
|
+
"transmittance",
|
|
36
|
+
"photonics",
|
|
37
|
+
"webassembly"
|
|
38
|
+
],
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"author": "Andrey Achapovsky",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/aai2k/tmmcore.git"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://aai2k.github.io/tmmcore/",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/aai2k/tmmcore/issues"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18"
|
|
51
|
+
},
|
|
52
|
+
"sideEffects": false
|
|
53
|
+
}
|
package/src/build.ps1
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Build the tmmcore WASM kernel with Emscripten (Windows / PowerShell).
|
|
2
|
+
#
|
|
3
|
+
# Usage:
|
|
4
|
+
# npm run build:wasm # build the kernel (auto-finds emsdk)
|
|
5
|
+
# .\src\build.ps1 # same, directly
|
|
6
|
+
# .\src\build.ps1 -InstallEmsdk # clone+install emsdk first if missing
|
|
7
|
+
#
|
|
8
|
+
# emcc discovery order (first hit wins):
|
|
9
|
+
# 1. emcc already on PATH
|
|
10
|
+
# 2. $env:EMSDK
|
|
11
|
+
# 3. a list of common install locations (any drive) -- see $candidates below
|
|
12
|
+
# 4. with -InstallEmsdk (or $env:TFS_INSTALL_EMSDK=1): git-clone + install emsdk
|
|
13
|
+
# into %USERPROFILE%\emsdk, then activate it
|
|
14
|
+
#
|
|
15
|
+
# One-time manual install (if you prefer): https://emscripten.org/docs/getting_started/downloads.html
|
|
16
|
+
# git clone https://github.com/emscripten-core/emsdk
|
|
17
|
+
# cd emsdk; .\emsdk install latest; .\emsdk activate latest
|
|
18
|
+
# Then either add it to PATH, set $env:EMSDK to that folder, or just put it in one
|
|
19
|
+
# of the searched locations below and re-run -- no path is hardcoded anymore.
|
|
20
|
+
#
|
|
21
|
+
# NOTE: keep this file ASCII-only. PowerShell 5.1 reads -File scripts as the
|
|
22
|
+
# system codepage, so non-ASCII bytes (em dashes, etc.) break string parsing.
|
|
23
|
+
|
|
24
|
+
param([switch]$InstallEmsdk)
|
|
25
|
+
|
|
26
|
+
$ErrorActionPreference = 'Stop'
|
|
27
|
+
$root = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
|
28
|
+
Set-Location $root
|
|
29
|
+
|
|
30
|
+
# --- Try to activate emsdk from a known root (returns $true if emcc appears) ---
|
|
31
|
+
function Activate-Emsdk([string]$emsdkRoot) {
|
|
32
|
+
if (-not $emsdkRoot) { return $false }
|
|
33
|
+
if (-not (Test-Path $emsdkRoot)) { return $false }
|
|
34
|
+
$envScript = Join-Path $emsdkRoot 'emsdk_env.ps1'
|
|
35
|
+
if (-not (Test-Path $envScript)) { return $false }
|
|
36
|
+
Write-Host "Activating emsdk from $emsdkRoot ..."
|
|
37
|
+
try { & $envScript | Out-Null } catch { }
|
|
38
|
+
return [bool](Get-Command emcc -ErrorAction SilentlyContinue)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# --- Ensure emcc is available --------------------------------------------------
|
|
42
|
+
if (-not (Get-Command emcc -ErrorAction SilentlyContinue)) {
|
|
43
|
+
|
|
44
|
+
# Candidate emsdk roots, in priority order. NONE is hardcoded as the only
|
|
45
|
+
# option: we search every common spot across drives so a build on a fresh PC
|
|
46
|
+
# finds an existing install wherever it was put. Add your own via $env:EMSDK.
|
|
47
|
+
$candidates = New-Object System.Collections.Generic.List[string]
|
|
48
|
+
if ($env:EMSDK) { [void]$candidates.Add($env:EMSDK) }
|
|
49
|
+
foreach ($p in @(
|
|
50
|
+
(Join-Path $env:USERPROFILE 'emsdk'),
|
|
51
|
+
(Join-Path $root '..\emsdk'),
|
|
52
|
+
(Join-Path $root '..\..\emsdk'),
|
|
53
|
+
'C:\emsdk', 'D:\emsdk', 'X:\emsdk',
|
|
54
|
+
(Join-Path $env:LOCALAPPDATA 'emsdk'),
|
|
55
|
+
(Join-Path ${env:ProgramFiles} 'emsdk')
|
|
56
|
+
)) { if ($p) { [void]$candidates.Add($p) } }
|
|
57
|
+
|
|
58
|
+
$activated = $false
|
|
59
|
+
foreach ($c in $candidates) {
|
|
60
|
+
if (Activate-Emsdk $c) { $activated = $true; break }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Optional one-shot install (flag or env). Clones into %USERPROFILE%\emsdk.
|
|
64
|
+
if (-not $activated -and ($InstallEmsdk -or $env:TFS_INSTALL_EMSDK -eq '1')) {
|
|
65
|
+
$target = Join-Path $env:USERPROFILE 'emsdk'
|
|
66
|
+
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
|
|
67
|
+
Write-Error "Cannot auto-install emsdk: git is not on PATH. Install Git, or install emsdk manually."
|
|
68
|
+
exit 1
|
|
69
|
+
}
|
|
70
|
+
if (-not (Test-Path $target)) {
|
|
71
|
+
Write-Host "Cloning emsdk into $target ..." -ForegroundColor Cyan
|
|
72
|
+
& git clone --depth 1 https://github.com/emscripten-core/emsdk "$target"
|
|
73
|
+
if ($LASTEXITCODE -ne 0) { Write-Error "git clone of emsdk failed."; exit 1 }
|
|
74
|
+
}
|
|
75
|
+
Write-Host "Installing + activating emsdk 'latest' (one-time, downloads the toolchain) ..." -ForegroundColor Cyan
|
|
76
|
+
& (Join-Path $target 'emsdk.bat') install latest
|
|
77
|
+
if ($LASTEXITCODE -ne 0) { Write-Error "emsdk install failed."; exit 1 }
|
|
78
|
+
& (Join-Path $target 'emsdk.bat') activate latest
|
|
79
|
+
if ($LASTEXITCODE -ne 0) { Write-Error "emsdk activate failed."; exit 1 }
|
|
80
|
+
$activated = Activate-Emsdk $target
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (-not $activated) {
|
|
84
|
+
Write-Error @"
|
|
85
|
+
emcc (Emscripten) not found. The WASM TMM kernel needs it to (re)build.
|
|
86
|
+
|
|
87
|
+
A prebuilt src/tmm_kernel.wasm is committed to the repo, so the desktop
|
|
88
|
+
build (npm run dist) will STILL succeed without emcc -- it just reuses that
|
|
89
|
+
artifact. Only run this when you actually changed tmm_kernel.c.
|
|
90
|
+
|
|
91
|
+
To build the kernel here, do ONE of:
|
|
92
|
+
* install emsdk and re-run with auto-detect:
|
|
93
|
+
.\src\build.ps1 -InstallEmsdk (clones into %USERPROFILE%\emsdk)
|
|
94
|
+
* or install it yourself, then point at it:
|
|
95
|
+
`$env:EMSDK = 'C:\path\to\emsdk'; npm run build:wasm
|
|
96
|
+
* or just put the emsdk folder in one of: %USERPROFILE%\emsdk, C:\emsdk,
|
|
97
|
+
a sibling of the project, etc. (searched automatically).
|
|
98
|
+
|
|
99
|
+
Searched: $($candidates -join '; ')
|
|
100
|
+
"@
|
|
101
|
+
exit 1
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# Each emcc flag is a quoted token. The EXPORTED_FUNCTIONS value contains commas;
|
|
106
|
+
# unquoted, PowerShell parses them as its array operator and errors ("Missing
|
|
107
|
+
# argument in parameter list"). Quoting passes the literal string through to emcc.
|
|
108
|
+
$emccArgs = @(
|
|
109
|
+
'src/tmm_kernel.c'
|
|
110
|
+
'-O3'
|
|
111
|
+
'--no-entry'
|
|
112
|
+
'-sSTANDALONE_WASM=1'
|
|
113
|
+
'-sALLOW_MEMORY_GROWTH=1'
|
|
114
|
+
'-sEXPORTED_FUNCTIONS=_tmm_one,_tmm_spectrum,_tmm_jacobian,_tmm_needle_scan,_tmm_hessian,_malloc,_free'
|
|
115
|
+
'-o'
|
|
116
|
+
'src/tmm_kernel.wasm'
|
|
117
|
+
)
|
|
118
|
+
& emcc @emccArgs
|
|
119
|
+
|
|
120
|
+
Write-Host "Built src/tmm_kernel.wasm"
|
|
121
|
+
node tests/equivalence.mjs
|
package/src/index.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tmmcore — transfer-matrix method for multilayer thin films.
|
|
3
|
+
*
|
|
4
|
+
* Two interchangeable implementations of the same physics:
|
|
5
|
+
*
|
|
6
|
+
* • a JavaScript reference implementation, always available, no dependencies
|
|
7
|
+
* • a C kernel compiled to WebAssembly, roughly an order of magnitude faster
|
|
8
|
+
*
|
|
9
|
+
* The JavaScript path works immediately on import. WebAssembly is opt-in: load
|
|
10
|
+
* the `.wasm` bytes, instantiate, and call the instance methods. Results agree
|
|
11
|
+
* to float64 round-off either way — see `tests/equivalence.mjs`.
|
|
12
|
+
*
|
|
13
|
+
* Conventions, which matter more than anything else here:
|
|
14
|
+
* ñ = n + ik k ≥ 0 for absorbing media
|
|
15
|
+
* time factor exp(−iωt)
|
|
16
|
+
* wavelengths and thicknesses in nm, angles in degrees from normal
|
|
17
|
+
* complex numbers are [re, im] pairs
|
|
18
|
+
* layers are ordered from the incident medium toward the substrate
|
|
19
|
+
*
|
|
20
|
+
* MIT licensed.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
// Spectral quantities
|
|
25
|
+
tmm,
|
|
26
|
+
// Exact analytic derivatives
|
|
27
|
+
tmmThicknessJacobian,
|
|
28
|
+
tmmThicknessHessian,
|
|
29
|
+
tmmNeedleScan,
|
|
30
|
+
// Low-level primitives, for building variants on the same conventions
|
|
31
|
+
cadd, csub, cmul, cdiv, cabs2, cconj, csqrt, ccos, csin, creal, cimag,
|
|
32
|
+
matmul, rescaleMatrix, snellCosTheta, layerMatrix, cmatvec,
|
|
33
|
+
} from './tmm.js';
|
|
34
|
+
|
|
35
|
+
export * from './tmmWasm.js';
|