driftlet 1.2.1 → 1.2.3
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/README.md +2 -2
- package/docs/device.md +5 -0
- package/docs/internals.md +93 -0
- package/docs/numerics.md +12 -1
- package/docs/reliability.md +53 -4
- package/llms.txt +6 -6
- package/package.json +2 -1
- package/src/lawRows.js +564 -0
- package/src/laws.js +693 -0
- package/src/solver.js +110 -1006
- package/types/lawRows.d.ts +5 -0
- package/types/laws.d.ts +86 -0
- package/types/solver.d.ts +78 -43
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ import { bandDiagram } from 'driftlet/plot'; // a level diagram as an SVG str
|
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
Or straight from a CDN in a page, pinned to a version:
|
|
59
|
-
`https://cdn.jsdelivr.net/npm/driftlet@1.2.
|
|
59
|
+
`https://cdn.jsdelivr.net/npm/driftlet@1.2.3/src/index.js` (and `src/kit.js`, `src/plot.js`).
|
|
60
60
|
TypeScript declarations ship in the package. Writing a demo with an LLM agent? Point it at
|
|
61
61
|
[`llms.txt`](llms.txt).
|
|
62
62
|
|
|
@@ -300,7 +300,7 @@ npm run shots # retake the demos' screenshots, each caught at a set state (to
|
|
|
300
300
|
|
|
301
301
|
Design notes and plans: [reliability](docs/reliability.md), [numerics](docs/numerics.md), [conventions](docs/conventions.md),
|
|
302
302
|
[statistics](docs/statistics.md), [the kit](docs/kit.md), [reading level diagrams](docs/visualization.md),
|
|
303
|
-
[data](docs/data.md), [roadmap](ROADMAP.md).
|
|
303
|
+
[data](docs/data.md), [internals](docs/internals.md) (the source, module by module), [roadmap](ROADMAP.md).
|
|
304
304
|
|
|
305
305
|
## Credits
|
|
306
306
|
|
package/docs/device.md
CHANGED
|
@@ -110,6 +110,11 @@ regions: [
|
|
|
110
110
|
reactions) does: its `c0` fixes the amount it conserves. Concentrations are positive ($`\bar\mu`$ is
|
|
111
111
|
logarithmic in them); to have none of a species in a region, leave it out of that region's
|
|
112
112
|
material. In a strictly neutral region, $`\sum z \cdot c_0`$ must be zero.
|
|
113
|
+
Where two strictly neutral regions meet at a face that holds ions in equilibrium and their
|
|
114
|
+
`c0` leaves it out of equilibrium (a different fixed charge or immobile ion on each side), the
|
|
115
|
+
two nodes at the face start in it, as the face reaches it at $`t = 0^+`$: the ions it passes
|
|
116
|
+
shared between them so their $`\bar\mu`$ is equal across it and each side neutral, each
|
|
117
|
+
one's amount over the two nodes kept, everything else as `c0` has it.
|
|
113
118
|
An entry can instead be a profile, `{ x: [...], values: [...] }`, against the device's x (m, not
|
|
114
119
|
the region's own), piecewise linear between its points and constant beyond its ends, so a
|
|
115
120
|
transient can start from a state it didn't simulate reaching: a packet already injected, a
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Internals
|
|
2
|
+
|
|
3
|
+
A map of the source for anyone reading or reviewing it: what each module is for, the layout of
|
|
4
|
+
the solver's state that the rest assumes, and which modules state a contract. The user-facing
|
|
5
|
+
reference is the other docs; this one is about the code.
|
|
6
|
+
|
|
7
|
+
## The rule: a module states its contract
|
|
8
|
+
|
|
9
|
+
A module whose correctness can't be seen from its call sites says at its head what it reads,
|
|
10
|
+
what it returns or writes, and what must hold of the result, so that it can be checked on its
|
|
11
|
+
own, without the rest of the library in mind. Where the contract and the code disagree, one of
|
|
12
|
+
them is wrong: fix both, with a test, in the same commit.
|
|
13
|
+
|
|
14
|
+
| Module | Contract |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `src/laws.js` | yes: the steady system's conservation laws |
|
|
17
|
+
| `src/lawRows.js` | yes: the laws written into the Newton system, and the bordered solve |
|
|
18
|
+
| `src/difference.js` | header: the dilute kernels in difference form |
|
|
19
|
+
| `src/blockTridiagonal.js` | header: the block Thomas solver |
|
|
20
|
+
| `src/statistics.js` | header: each model's `evaluate` / `invert` |
|
|
21
|
+
| `src/solver.js` | not yet (below) |
|
|
22
|
+
|
|
23
|
+
## From a definition to a solution
|
|
24
|
+
|
|
25
|
+
- `src/device.js`: `normalizeDevice(def)` validates a plain definition and returns the
|
|
26
|
+
normalised `model` (species, materials, regions, interfaces, contacts, ports, terminals, the
|
|
27
|
+
grid from `src/grid.js`). Every later stage reads the model and never the definition.
|
|
28
|
+
- `src/index.js`: `Device`, the public object. It holds the model, builds a `Solver` on first
|
|
29
|
+
use, and turns solver states into `Solution`s (`src/solution.js`).
|
|
30
|
+
- `src/solver.js`: `Solver`, the numerical core (below).
|
|
31
|
+
- The kit (`src/kit.js` and what it re-exports: stack, data, sources, channels, checks,
|
|
32
|
+
describe, traces, live, recorder, particles, polarization, junction, devices) works on
|
|
33
|
+
definitions and solutions only, never on a solver's internals. `src/plot.js` draws
|
|
34
|
+
solutions.
|
|
35
|
+
|
|
36
|
+
## The solver's state
|
|
37
|
+
|
|
38
|
+
- **Blocks.** One per grid node and one per face between regions: node g is block
|
|
39
|
+
`blockOfNode[g]`, face f's flux block is `blockOfFace[f]`, in order along x.
|
|
40
|
+
- **Slots.** Each block has `M = 1 + n + extra` slots: slot 0 is φ̂ (at a face its
|
|
41
|
+
displacement D, at a metal's node its carriers' segment flux J), slots `1 … n` the species'
|
|
42
|
+
η = μ̄/RT, then a face's reaction rates and gates' fractions, or at an electrode port's nodes
|
|
43
|
+
its surface's coverages (as η) and a membrane port's gates. Row and unknown `o = b·M + slot`.
|
|
44
|
+
- **Compact indices.** Only active slots enter the linear system: `loc[o]` is a slot's place in
|
|
45
|
+
its block (−1: inactive), `rix[o]` its index in compact vectors, with `rix = N` (the sink) for
|
|
46
|
+
inactive ones. `res`, `delta` and the Jacobian (`sys`, a `BlockTridiagonal`) are compact.
|
|
47
|
+
- **Unknowns** are `u + uLo`, a double-double, so that differences of η keep their digits; the
|
|
48
|
+
Jacobian and solves are plain doubles. Concentrations `c[g·n + i]` and coverages
|
|
49
|
+
`th[g·nSurf + q]` follow from `u` (`computeConcentrations`).
|
|
50
|
+
- **Terminals:** 0 the left contact, 1 the right, `2 + k` port k. A floating terminal (driven by
|
|
51
|
+
a current, or behind a resistance) has its voltage as an extra unknown and its circuit row
|
|
52
|
+
beside the block system, solved by bordering (`solveBordered` in `src/lawRows.js`: `termB`
|
|
53
|
+
its column, `termC` its row, `termDI` and `termDX` its own and other terminals' ∂/∂V).
|
|
54
|
+
- **Stretches:** each connected run of regions where a species is present
|
|
55
|
+
(`_findStretches`), the unit that conservation counts in.
|
|
56
|
+
|
|
57
|
+
## The solver's sections
|
|
58
|
+
|
|
59
|
+
By method, in file order:
|
|
60
|
+
|
|
61
|
+
- **Setup:** the constructor (layout, per-node data, φ-undefined regions, terminals),
|
|
62
|
+
`_activeSlots`, `_findStretches` (stretches, flat stretches), `_laws` and `_structure` (the
|
|
63
|
+
laws, from `src/laws.js`), `_directSteady`.
|
|
64
|
+
- **Applying the laws** is `src/lawRows.js` (`captureLaws`, `captureLocal`, `snapFlat`,
|
|
65
|
+
`applyLaws`, `solveBordered`), called from `assemble`, `newton`, `_refine` and the steady
|
|
66
|
+
solves; `_replaceRow` and `_j`, which it uses, stay in the solver.
|
|
67
|
+
- **Starting state:** `initFromComposition`, `_surfaceStart`.
|
|
68
|
+
- **Statistics and assembly:** `computeConcentrations`, `_nodeStatistics`, `assemble` and the
|
|
69
|
+
kernels it calls (nodes, segments, faces, bulk reactions, ports, contacts, charge rows),
|
|
70
|
+
`_assembleDifference` (with `src/difference.js`).
|
|
71
|
+
- **Newton:** `newton`, `_refine`, `_equilibrate`, `_kirchhoff`, `_noteConditioning`.
|
|
72
|
+
- **Time steps:** `advance`, `step`, `integrate` (BDF with error control, breakpoints from the
|
|
73
|
+
sources), `_predict`, `_errorNorm`, `_snapshot` / `_restore`.
|
|
74
|
+
- **Small signal:** `impedance` and the helpers after it.
|
|
75
|
+
- **Bookkeeping:** `segmentFluxes`, `faceOneWay`, `_assembleBookkeeping`, the contacts' intake.
|
|
76
|
+
- **Steady solves:** `solveSteady`, `_steadyFromHere`, the continuations (floating, generation,
|
|
77
|
+
a terminal's level), `_steadySteps`.
|
|
78
|
+
|
|
79
|
+
The rest of `src/solver.js` is to be split along these lines, each piece with its contract.
|
|
80
|
+
|
|
81
|
+
## Reviewing one module
|
|
82
|
+
|
|
83
|
+
A review of one module reads its contract, its code and its tests, and nothing else unless the
|
|
84
|
+
contract points there. Each claim cites `file:line` with the line quoted, so it can be checked
|
|
85
|
+
mechanically, and a finding comes with the device or script that shows it. A claim about code
|
|
86
|
+
not in view is a question, not a finding. Two instruments check a change to the core beyond the tests'
|
|
87
|
+
answers: `test/jacobian.test.js` (every row's derivatives against finite differences of its
|
|
88
|
+
residual) and, for a move or refactor meant to change nothing, `tools/fingerprint.js`: a hash of
|
|
89
|
+
the solver's state after every solve in the suite (on a fixed clock, so runs stopped by a time
|
|
90
|
+
budget repeat), recorded before and after and compared. `tools/check-hook.js`, loaded into any run (`node --import
|
|
91
|
+
./tools/check-hook.js …`: the tests, the law sweep, a fuzzer), checks the laws' cheap invariants
|
|
92
|
+
as the solver goes: no row taken twice, none inactive, no stray ∂/∂V in a law's row, findLaws'
|
|
93
|
+
structure unwritten.
|
package/docs/numerics.md
CHANGED
|
@@ -730,18 +730,29 @@ the terms that cancel:
|
|
|
730
730
|
port or contact at no current that passes K⁺ keeps $`n_K + Q/F`$ (a contact's Q its
|
|
731
731
|
displacement $`D_\text{in}`$), and two floating electrodes sharing a closed Fe³⁺ keep the iron
|
|
732
732
|
with both their charges, the law in one's circuit row moving with the other's voltage too.
|
|
733
|
+
A charge is kept only in a circuit row, once: the first combination through it takes one,
|
|
734
|
+
and the rest have its multiple taken off (a capacitor passing K⁺ under K⁺ + L⁻ = KL keeps
|
|
735
|
+
$`n_K - n_L + D/F`$ there, and $`n_L + n_{KL}`$ in a balance row).
|
|
733
736
|
At any other current such a terminal only stores what comes in, which has no steady state,
|
|
734
737
|
and the solve says so at once.
|
|
735
738
|
- an **immobile** combination (trap states X⁰ and X⁻ under e⁻ + X⁰ = X⁻, with D = 0) keeps it
|
|
736
739
|
node by node, since nothing carries it anywhere, as do an electrode's surface species
|
|
737
740
|
(adsorbates that only turn into each other keep their total on every site), its row local,
|
|
738
741
|
one per node in its own block, a coverage counted as the sites it fills per volume,
|
|
739
|
-
$`\Gamma a \theta`$, beside the concentrations.
|
|
742
|
+
$`\Gamma a \theta`$, beside the concentrations. Each node's rows are chosen by elimination
|
|
743
|
+
over the slots there, as below. Immobile means D = 0 in one region with no terminal feeding
|
|
744
|
+
it: a species linked across a face is carried between the face's two nodes, and one a
|
|
745
|
+
port links is carried between the port's nodes, kept with the port's charge in its circuit
|
|
746
|
+
row. (The null space is taken with the immobile and
|
|
740
747
|
surface columns first, so that these come out apart from the combinations that move.) A combination of immobile
|
|
741
748
|
stretches with a moving one (the electrons the traps hold, $`e^- - X^0`$, in a closed film) is
|
|
742
749
|
an amount like any other, its immobile parts summed over their nodes;
|
|
743
750
|
- a **floating metal** keeps the charge on its faces, its sheets' displacement.
|
|
744
751
|
|
|
752
|
+
A combination that none of these rows can keep (through a stretch immobile in part only, a
|
|
753
|
+
face reaction joining two immobile stretches at a face, a charge with no circuit row left) is
|
|
754
|
+
set aside, and the steady solve then goes by time steps, which keep every amount themselves.
|
|
755
|
+
|
|
745
756
|
A closed stretch keeps the amount it was given (its `c0`, or what it held when `set()`
|
|
746
757
|
closed it); a combination behind a terminal at no current keeps what the state holds when the
|
|
747
758
|
solve begins. The amount's row is dense, so it's bordered (see [terminals](#terminals)): the
|
package/docs/reliability.md
CHANGED
|
@@ -146,14 +146,17 @@ $`J \cdot v`$.
|
|
|
146
146
|
A steady solve replaces one redundant equation per conserved combination with the combination's
|
|
147
147
|
amount (see [numerics](numerics.md#the-steady-system)). A combination left out, or weighed wrongly,
|
|
148
148
|
gives a state that converges and is steady but holds the wrong amounts, which only a transient
|
|
149
|
-
from the same start shows. `npm run lawsweep` builds
|
|
149
|
+
from the same start shows. `npm run lawsweep` builds 870 small devices in two families. The first, 810, combine the features at the
|
|
150
150
|
edges of that bookkeeping: a floating, resistive or held electrode, with or without a
|
|
151
151
|
capacitance, with adsorbates or an immobile species that adsorbs; a second floating electrode
|
|
152
152
|
or a membrane capacitor; a capacitive or ion-passing contact at no current; a face that blocks
|
|
153
153
|
an ion or makes an immobile species. Each is solved directly and run as a transient for 1e6 s,
|
|
154
154
|
and where the transient converged, finished and moves no further over 100× as long, the two
|
|
155
155
|
must agree on every species' amount, every surface's sites and every floating terminal's
|
|
156
|
-
voltage.
|
|
156
|
+
voltage. The second, 60, put an immobile S⁺ beside what carries it (a face that passes it, a port
|
|
157
|
+
at I = 0 that links it, an electrode it adsorbs on) with a capacitive contact passing K⁺ (at
|
|
158
|
+
I = 0 or behind a resistance) and K⁺'s complex with a closed L⁻, each where a cold review of
|
|
159
|
+
`src/laws.js` found a law kept wrongly.
|
|
157
160
|
|
|
158
161
|
Every reaction starts at its equilibrium. On 1.2.1, 675 devices are built (135 combinations
|
|
159
162
|
repeat others); 579 agree, and 83 transients fail to converge. In the other 13 the transient
|
|
@@ -161,8 +164,54 @@ is the one that's off: it loses a conserved K⁺ total in proportion to its tole
|
|
|
161
164
|
at tol 1e-9) when a capacitive contact, a membrane capacitor and a held electrode share the
|
|
162
165
|
device, where the steady solve keeps it to round-off. The sweep found its first law the day it
|
|
163
166
|
was written (an immobile species adsorbing on an electrode over part of its region); Gemini 4
|
|
164
|
-
Argon's review found nine more by hand (one through a device it suggested),
|
|
165
|
-
`
|
|
167
|
+
Argon's review found nine more by hand (one through a device it suggested), and a cold review
|
|
168
|
+
of `src/laws.js` against its contract alone five more (an immobile species linked across a face,
|
|
169
|
+
or by a port; two node-by-node laws sharing a row; a capacitive contact's charge in a second law,
|
|
170
|
+
and in a time step's reading of its current), none of them in the sweep's devices. Each is now in
|
|
171
|
+
`test/laws.test.js`. On the code before those fixes the second family has 14 devices that
|
|
172
|
+
disagree (a port's voltage at 0 for −1.42 V, θ 0.05 for 0.015, a contact 24 mV off); after, none,
|
|
173
|
+
26 agree and 34 transients fail (see the roadmap). The first family's laws come out unchanged by
|
|
174
|
+
the fixes, device by device, so its results above stand. On 1.2.2 the whole sweep, 870 devices,
|
|
175
|
+
gives the same: 605 agree, 117 transients fail, and 13 differ, each a capacitive contact beside a
|
|
176
|
+
membrane capacitor where the transient loses K⁺. Since a face out of equilibrium in `c0` starts
|
|
177
|
+
in it (1.2.3), 607 agree and 115 fail, the same 13 differing.
|
|
178
|
+
|
|
179
|
+
## Consistent: the derivatives
|
|
180
|
+
|
|
181
|
+
A Jacobian entry that's wrong still converges to the right answer, its residual being right, so
|
|
182
|
+
no test of answers sees it: it shows only as Newton taking longer, or failing on a harder device.
|
|
183
|
+
`test/jacobian.test.js` compares the whole Newton system, column by column, with central
|
|
184
|
+
differences of its own residual, on ten devices that between them write every law's row (flat
|
|
185
|
+
stretches, affinities, amounts kept in balance rows and in circuit rows with an electrode's or a
|
|
186
|
+
contact's charge, node-by-node laws with a surface, metals' charges), crowded statistics, face
|
|
187
|
+
and port reactions and floating terminals. It checks inside a real `solve()` and a real
|
|
188
|
+
`advance()`, at their second assembly, each in the unknowns Newton solves for there (a time step's
|
|
189
|
+
charge rows included). It came from a cold review of `src/lawRows.js`, which found a node-by-node
|
|
190
|
+
law's row keeping a port reaction's ∂/∂V (an extra Newton iteration, the answer the same); broken
|
|
191
|
+
once each way (that entry back, a storage term 0.1% off in either time-step path), it fails.
|
|
192
|
+
|
|
193
|
+
## Measured: how much the tests see
|
|
194
|
+
|
|
195
|
+
A mutation run makes one small slip at a time in a module (an operator flipped, a comparison
|
|
196
|
+
loosened, a statement dropped, a 0 for a 1), runs the tests on it, and counts the slips no test
|
|
197
|
+
fails. On 18 Oct 2026, 120 of them, sampled from `src/laws.js` and `src/lawRows.js`'s 1420: 69
|
|
198
|
+
failed a test, and 51 got through. Each of the 51 was then looked at:
|
|
199
|
+
- In `src/laws.js` (29): 20 change no law on any of the law sweep's 735 devices, 2 only a
|
|
200
|
+
workspace's size, and 6 change laws on 12 to 635 of them while every answer stays right (the
|
|
201
|
+
islands', which Newton seldom needs; a λ read only by long steps). One changed the answers: it
|
|
202
|
+
held a capacitive contact's charge in the wrong law, and 85 of the sweep's devices came out
|
|
203
|
+
wrong, with all 470 tests passing. Its device is now in `test/laws.test.js`.
|
|
204
|
+
- In `src/lawRows.js` (22): the Jacobian test had never seen islands' rows, a long step that
|
|
205
|
+
reads a law by its change, or a law with a surface or a capacitive electrode in a time step.
|
|
206
|
+
With them in (a charged pore, three of the sweep's devices), 6 more fail. Of the 16 left, 6
|
|
207
|
+
change nothing (a bound the loop never reaches), 1 only a heuristic's estimate, 2 the pivoting
|
|
208
|
+
of the small dense solve (robustness only), and 7 are narrow paths no device here takes: two
|
|
209
|
+
floating electrodes whose law counts the other's charge, an equilibrium face inside a flat
|
|
210
|
+
stretch, a contact's law read by its change.
|
|
211
|
+
|
|
212
|
+
A slip that gives the right answer by another route isn't a gap; what the run found that was,
|
|
213
|
+
was one wrong answer the sweep sees and the suite didn't, and a test that had never looked at
|
|
214
|
+
two of the paths it claimed.
|
|
166
215
|
|
|
167
216
|
## Hard cases
|
|
168
217
|
|
package/llms.txt
CHANGED
|
@@ -30,9 +30,9 @@ materials, insertion hosts), data.md and reliability.md (what's validated, and t
|
|
|
30
30
|
From a CDN, pin the version you tested against:
|
|
31
31
|
|
|
32
32
|
```js nocheck
|
|
33
|
-
import { Device, units } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.
|
|
34
|
-
import { build, layer, ohmic, semiconductor, live } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.
|
|
35
|
-
import { bandDiagram } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.
|
|
33
|
+
import { Device, units } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.3/src/index.js';
|
|
34
|
+
import { build, layer, ohmic, semiconductor, live } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.3/src/kit.js';
|
|
35
|
+
import { bandDiagram } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.3/src/plot.js';
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
From npm: `driftlet`, `driftlet/kit`, `driftlet/plot`. Every export is listed at the end.
|
|
@@ -219,9 +219,9 @@ a CDN needs a long virtual-time budget, 30 s.)
|
|
|
219
219
|
<span id="readout"></span>
|
|
220
220
|
<div id="figure"></div>
|
|
221
221
|
<script type="module">
|
|
222
|
-
import { units } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.
|
|
223
|
-
import { build, layer, ohmic, semiconductor, live } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.
|
|
224
|
-
import { bandDiagram } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.
|
|
222
|
+
import { units } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.3/src/index.js';
|
|
223
|
+
import { build, layer, ohmic, semiconductor, live } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.3/src/kit.js';
|
|
224
|
+
import { bandDiagram } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.3/src/plot.js';
|
|
225
225
|
|
|
226
226
|
const def = build({
|
|
227
227
|
T: 300,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "driftlet",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "Dependency-free 1D drift–diffusion–reaction (Poisson–Nernst–Planck) solver for semiconductors, electrochemistry and nerves, fast enough for live web demos",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"smoke": "node tools/smoke.js",
|
|
35
35
|
"shots": "node tools/shots.js",
|
|
36
36
|
"lawsweep": "node tools/lawsweep.js",
|
|
37
|
+
"fingerprint": "node tools/fingerprint.js",
|
|
37
38
|
"prepublishOnly": "npm test && npm run types",
|
|
38
39
|
"types": "rm -rf types && npx -y -p typescript@5 tsc --allowJs --declaration --emitDeclarationOnly --target es2022 --module nodenext --moduleResolution nodenext --outDir types src/index.js src/kit.js src/plot.js"
|
|
39
40
|
},
|