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 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: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./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';