{
 "format": "jekpack",
 "version": 2,
 "id": "jek.example",
 "name": "Example mod",
 "author": "JEKsys",
 "packVersion": "1.0",
 "licence": "CC0-1.0",
 "description": "One part of each kind a mod can add: a metalens and a spiral phase plate (plates), a quarter-wave plate (a polarisation plate), a ring resonator (a fibre component), a lead-lag network (an electronics block) and a D4σ beam width (a readout for screens). Read its code to see how each is written.",
 "code": "// An example mod for JEKray: one part of each kind a mod can add.\n// Lengths are in mm, wavelengths (nm) in nm, frequencies (f) in Hz.\n\n// A metalens: a flat lens, its phase written wrapped into one wave, as a metasurface's is.\n// A delay of d waves multiplies the light by exp(-2 pi i d); a delay falling away from the\n// axis converges. Wrapped, it must change by less than half a wave between samples.\njek.element('metalens', {\n  name: 'Metalens', summary: 'A flat lens: a wrapped hyperbolic phase, as a metasurface writes it.', colour: 'green',\n  aperture: 3, samples: 512,\n  params: [{ key: 'f', label: 'Focal length (mm)', value: 100, min: 1 }, { key: 'design', label: 'Design wavelength (nm)', value: 633, min: 100 }],\n  transmit(x, y, nm, p) {\n    // the phase is right at the design wavelength; elsewhere a metasurface's is the same\n    // number of waves, so it focuses there with a chromatic shift\n    const lam = p.design * 1e-6, d = -(Math.sqrt(x * x + y * y + p.f * p.f) - p.f) / lam;\n    return [1, d - Math.floor(d)];\n  },\n});\n\n// A spiral phase plate: a vortex of charge l, the phase winding l waves round the axis.\njek.element('spiral', {\n  name: 'Spiral phase plate', summary: 'Gives a beam orbital angular momentum: a vortex of charge l.', colour: 'purple',\n  aperture: 4, samples: 512,\n  params: [{ key: 'l', label: 'Charge, l', value: 1 }],\n  transmit(x, y, nm, p) { const t = Math.atan2(y, x) / (2 * Math.PI); const d = p.l * t; return [1, d - Math.floor(d)]; },\n});\n\n// A quarter-wave plate whose retardance follows its wavelength (zero order, quartz-like\n// dispersion left out): its fast axis at angle a from x (p), in the beam's own frame.\njek.plate('qwp', {\n  name: 'Quarter-wave plate (mod)', summary: 'A zero-order quarter-wave plate, its retardance scaling with 1/wavelength.', colour: 'blue',\n  aperture: 12.7,\n  params: [{ key: 'a', label: 'Fast axis from p (°)', value: 45 }, { key: 'design', label: 'Design wavelength (nm)', value: 633 }],\n  jones(nm, p) {\n    const d = 2 * Math.PI * 0.25 * p.design / nm, th = p.a * Math.PI / 180, c = Math.cos(th), s = Math.sin(th);\n    const e = [Math.cos(d), Math.sin(d)];            // exp(i d), the slow axis's extra phase\n    // R(-th) diag(1, e) R(th)\n    return [[[c * c + s * s * e[0], s * s * e[1]], [c * s * (1 - e[0]), -c * s * e[1]]],\n            [[c * s * (1 - e[0]), -c * s * e[1]], [s * s + c * c * e[0], c * c * e[1]]]];\n  },\n});\n\n// An all-pass ring resonator: light into \"in\" leaves by \"through\", with the ring's notches.\n// Its field through: (r - a e^{-i phi}) / (1 - r a e^{-i phi}), phi = 2 pi n L / lambda.\njek.component('ring', {\n  name: 'Ring resonator', summary: 'An all-pass ring: notches every free spectral range.', tag: 'RING',\n  ports: ['in', 'through'],\n  params: [{ key: 'radius', label: 'Radius (µm)', value: 50 }, { key: 'n', label: 'Group index', value: 4.2 },\n    { key: 'r', label: 'Self-coupling r', value: 0.98 }, { key: 'loss', label: 'Loss (dB/cm)', value: 3 }],\n  S(nm, p) {\n    const L = 2 * Math.PI * p.radius * 1e-3, phi = 2 * Math.PI * p.n * L / (nm * 1e-6);   // mm\n    const a = Math.pow(10, -p.loss * L / 10 / 20);\n    const ec = [Math.cos(phi), -Math.sin(phi)];                                         // e^{-i phi}\n    const num = [p.r - a * ec[0], -a * ec[1]], den = [1 - p.r * a * ec[0], -p.r * a * ec[1]];\n    const dd = den[0] * den[0] + den[1] * den[1];\n    const t = [(num[0] * den[0] + num[1] * den[1]) / dd, (num[1] * den[0] - num[0] * den[1]) / dd];\n    return [[0, 1, t], [1, 0, t]];\n  },\n});\n\n// A lead-lag network: H = (1 + i f/f1) / (1 + i f/f2), with a little noise of its own.\njek.block('leadlag', {\n  name: 'Lead-lag', summary: 'A zero at f1 and a pole at f2: phase lead between them.', tag: 'LEAD',\n  ins: ['in'], outs: ['out'], fmin: 0.01, fmax: 1e9, perDecade: 40,\n  params: [{ key: 'f1', label: 'Zero (Hz)', value: 100 }, { key: 'f2', label: 'Pole (Hz)', value: 10000 }, { key: 'en', label: 'Output noise (nV/√Hz)', value: 10 }],\n  H(f, p) {\n    const n = [1, f / p.f1], d = [1, f / p.f2], dd = d[0] * d[0] + d[1] * d[1];\n    return [(n[0] * d[0] + n[1] * d[1]) / dd, (n[1] * d[0] - n[0] * d[1]) / dd];\n  },\n  noise(f, p) { return p.en * 1e-9; },\n});\n\n// A readout: a screen's beam width by its second moments (D4σ, as ISO 11146 defines it),\n// and the intensity along the row through its centroid.\njek.readout('d4s', {\n  name: 'D4σ beam width', for: ['screen'],\n  compute(d) {\n    const M = d.map, n = M.n, du = (M.u1 - M.u0) / (n - 1), dv = (M.v1 - M.v0) / (n - 1);\n    let S = 0, su = 0, sv = 0;\n    for (let j = 0; j < n; j++) for (let i = 0; i < n; i++) { const w = M.I[j * n + i]; S += w; su += w * (M.u0 + i * du); sv += w * (M.v0 + j * dv); }\n    if (!(S > 0)) return { rows: [['D4σ', 'no light']] };\n    const cu = su / S, cv = sv / S;\n    let quu = 0, qvv = 0;\n    for (let j = 0; j < n; j++) for (let i = 0; i < n; i++) { const w = M.I[j * n + i], u = M.u0 + i * du - cu, v = M.v0 + j * dv - cv; quu += w * u * u; qvv += w * v * v; }\n    const Du = 4 * Math.sqrt(quu / S), Dv = 4 * Math.sqrt(qvv / S), row = Math.round((cv - M.v0) / dv);\n    return {\n      rows: [['Centroid (mm)', cu.toPrecision(4) + ', ' + cv.toPrecision(4)], ['D4σ across (mm)', Du], ['D4σ up (mm)', Dv]],\n      series: [{ name: 'Through the centroid', xLabel: 'across (mm)', yLabel: 'intensity',\n        x: Array.from({ length: n }, (_, i) => M.u0 + i * du), y: Array.from(M.I.slice(row * n, row * n + n)) }],\n    };\n  },\n});\n"
}