fix: findings from the independent review

Scenario runner: stops when overridden base parameters leave the start out
of balance (new field startlikevekt: krev, kalibrer, ubalanse); stops on
non-finite results; says that models with a court queue drift slowly even
without a shock and prints the drift before the shock; queue in months of
inflow; never deletes files it did not write; stricter limits.
Check: anchored on the note's own checksums for core and reference files,
runs the scenario files too, exact comparison for the diagnosis, honest
wording when parts were skipped or an extension is broken.
Launcher: unknown field names in input are rejected with a suggestion;
unwritable folders give a message, not a traceback.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-11-11 15:27:14 +01:00
commit 776325c680
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
14 changed files with 1524 additions and 199 deletions

View file

@ -36,12 +36,13 @@ regime A og scenario 2.
| `beskrivelse` | tekst | tom | alle | hvilket spørsmål scenarioet svarer på; står øverst i sammendraget |
| `merknad` | tekst | ingen | alle | fritekst for deg selv; brukes ikke |
| `modell` | tekst | **må oppgis** | | hvilken simulator, se under |
| `startlikevekt` | `"krev"`, `"kalibrer"` eller `"ubalanse"` | `"krev"` | alle | hva som skjer når parametrene gjør at startpunktet ikke er en likevekt, se under |
| `styring` | objekt | regime A | alle | regulator, forsinkelse og reaktivitet for politiets innsats |
| `parametere` | objekt | notatets verdier | alle | overstyring av kjernens parametere, gruppert som i kjernen |
| `regel` | tekst | **må oppgis** for `simulate_endo` | `simulate_endo` | beslutningsregel for andel utenom og kapasitet |
| `sjokk` | objekt | notatets sjokk | alle unntatt utvidelser | størrelse og tidspunkt for makrosjokket |
| `tiltak` | objekt | ingen tiltak | `simulate_chain`, `simulate_chain2`, `simulate_endo` med regel `sched`, utvidelser med kjede | andel utenom, kapasitet, trommel–buffer–tau |
| `slutt` | tall | 120 | alle | horisont i måneder (høyst 2 400) |
| `slutt` | tall | 120 | alle | horisont i måneder (minst 1, høyst 2 400) |
| `sveip` | objekt eller liste | ingen | alle | én parameter over en liste verdier, eller to (rutenett) |
| `utdata` | objekt | CSV med alle tidsserier, én rad per måned | alle | format, kolonner og hvor tett radene står |
| `utvidelse` | objekt | utvidelsens `PARAMETERE` | `utvidelse:<navn>` | utvidelsens egne parametere |
@ -101,7 +102,9 @@ Typen følger kjernens standardverdi: tall, heltall (`CH2.c`, `SOC.pexp`) eller
`ENDO.T_clear` (restansen avvikles ikke);
- andeler ligger mellom 0 og 1 (`U0`, `theta`, `xi`, `xi_min`, …), og `Lam0`, `piA` og `Lam_star` strengt mellom;
- tider, forsterkninger og rater er ikke negative, og tidskonstanter og kapasiteter er større enn 0;
- `ENDO.L1` < `ENDO.L2` og `ENDO.ups_lo` < `ENDO.ups_hi`.
- `ENDO.L1` < `ENDO.L2` og `ENDO.ups_lo` < `ENDO.ups_hi`;
- `ENDO.period` (budsjettsyklusen) er et helt antall måneder, minst 1: kjernen gjør budsjettvedtak bare i hele
måneder (`sim.py:614`), så en annen verdi ville gitt en regel som aldri gjør vedtak.
`CH2.m` kan settes til `"kalibrer"`: da regnes m ut med kjernens `calibrate_m`, slik at saksbehandlingstiden i
utgangslikevekten blir 3 måneder (det notatet gjør for c = 10 spor). Med flere spor (`c` > 1) og uten kalibrering
@ -109,9 +112,60 @@ er utgangslikevekten en annen enn notatets.
Funksjonene i kjedene (`Zfun`, `xifun`, `xiBfun`) og `dbr` settes ikke her, men med `tiltak`.
Viktig: `CH0.Z` og `CH0.m` (og `CH2.Z`, `CH2.m`) er regnet ut fra `P` da `sim.py` ble lest inn (`ZD0`, `MD`). Endrer
du `P.gamma`, `P.Lam0` eller `P.T0`, regnes de ikke om; domstolen er da ikke lenger 95 % utnyttet før sjokket. Oppgi
`CH0.Z` og `CH0.m` selv om du vil ha det.
Endrer du en av parametrene som bestemmer likevekten før sjokket, sjekker kjøreren om startpunktet fortsatt er en
likevekt; se `startlikevekt` under.
### `startlikevekt`
Notatets parametere er kalibrert slik at kriminaliteten K = 1 og innsatsen T = T₀ er en likevekt i grunnmodellen, og
slik at domstolen er 95 % utnyttet med 3 måneders saksbehandlingstid. I modellene med kø i domstolen er
startpunktet nesten, men ikke helt, en likevekt (se under). Endrer du for eksempel `P.gamma` («hva om politiet blir
20 % mer effektivt»), stemmer kalibreringen ikke lenger: modellen driver bort fra startpunktet før sjokket kommer, og
det ser ut som en virkning av endringen. Derfor sjekker kjøreren startpunktet for hver kjøring, med kjernens egne
funksjoner:
| Hva sjekkes | Betingelse | Kan kalibreres |
|---|---|---|
| pådraget mot fjerning og frafall | `Omega0` = `removal(1, Lam0, T0, P)` + `delta` (tillatt rest 10⁻¹²); berøres av `gamma`, `eta`, `delta`, `Omega0`, `T0`, `Lam0` og `ksat` | ja, `P.Omega0` regnes ut på nytt (ikke hvis fila setter `Omega0` selv) |
| målet for styringen | `Kstar` = 1 (når regelen virker: P med reaktivitet over 0, eller MPC) | nei |
| taket på innsatsen | `T0` ≤ `Tmax` | nei |
| forholdsmessighet | `T0` ≤ `Tprop` · 1 (ellers faller tilliten fra start) | nei |
| domstolen mot strømmen (`simulate_chain`, `simulate_chain2`, `simulate_endo`, utvidelser med kjede) | `CH0.Z`/`CH0.m` (eller `CH2.Z`/`CH2.m`) er notatets, regnet for strømmen J0 = `gamma`·`Lam0`·`T0` = 0,1. Endres `gamma`, `Lam0` eller `T0` uten at fila setter både `Z` og `m`, passer de ikke lenger | ja: `Z` = J0/0,95 og `m` = 3 − J0/(Z·(1/3 + `varsigma`)), som `sim.py:151-155` (for `CH2` med flere spor: `calibrate_m`); bare det fila ikke setter selv |
| domstolen er ikke overbelastet ved start | strømmen til domstolen ved start er mindre enn kapasiteten `Z` | bare når `Z` kalibreres |
| `eta` i kjedemodellene | `eta` = 1: kjernen regner strømmen til domstolen ved start uten η (`init_chain`, `simulate_chain2`) | nei |
`ksat` og rekrutteringen i `simulate_soc` (`SOC`) rekalibrerer kjernen selv (`removal` og `Omega0 − eps·H(1)`), og
sjekken bekrefter det.
**Modellene med kø i domstolen står ikke helt i ro, heller ikke med notatets verdier.** Ved start er
dK/dt = ϑ·ς·Q₀ > 0: det som faller fra køen (henleggelse, foreldelse), blir aldri fjernet fra kriminaliteten. Notatets
koblede modell uten sjokk (`eksempler/scenarioer/uten_sjokk_kjede.json`) gir K 1,007 i måned 11, 1,015 i måned 120 og
1,018 i måned 240, og saksbehandlingstiden 3,1, 4,2 og 4,6 måneder, fortsatt stigende. Det gjelder `simulate_chain`,
`simulate_chain2`, `simulate_endo` og utvidelser med kjede, og det er notatets referanse; sjekken over gjelder
kalibreringen, ikke denne driften. Derfor:
- sammendraget for disse modellene kaller 1,00 nivået ved start (notatets referansetilstand), sier at det er nesten,
men ikke helt, en likevekt, og viser hvor langt K, T, tilliten og saksbehandlingstiden har drevet fra start til
måneden sjokket begynner. Uten sjokk, eller med sjokk fra måned 0, sier det i én setning at modellen kryper sakte
oppover;
- når små virkninger betyr noe, sammenlign med en kjøring uten sjokk (`"sjokk": {"faktor": 1}`, samme parametere og
horisont) og trekk driften fra.
| Verdi | Hva skjer når startpunktet ikke er en likevekt |
|---|---|
| `"krev"` (standard) | kjøringen stopper (exit 2) med en melding som sier hva som er galt og de to veiene ut under |
| `"kalibrer"` | kjøreren regner om det som kan kalibreres (tabellen over) som i notatets kalibrering, slik at startpunktet blir notatets referansetilstand for de nye verdiene (med samme lille drift i modellene med kø), og sammendraget sier hvilke verdier som ble endret, fra hva og til hva. Er noe som ikke kan kalibreres galt, stopper den |
| `"ubalanse"` | kjøringen går fra ubalansen med vilje. Forklaringen under tabellen («1,00 er nivået i referanselikevekten …») byttes ut med hva som er galt og hvor langt K, T, tilliten og saksbehandlingstiden har drevet fram til måneden sjokket begynner (måned 12 når det ikke er noe sjokk, høyst `slutt`) |
`resultat.json` har for hver kjøring `startlikevekt` med valget, de kalibrerte verdiene (`for` og `etter`) og grunnene
til ubalansen.
**`simulate_endo` har én grense til.** Regelen i kjernen bruker 0,1 som observert strøm de første månedene
(`sim.py:588`). Er strømmen ved start (`removal(1, Lam0, T0, P)`) en annen, stopper kjøringen uansett valg. Målt: med
`gamma` 0,11 og 0,10 (og `Omega0`, `CH0.Z` og `CH0.m` kalibrert) gir den koordinerte regelen en annen bane enn med
riktig observasjon (største avvik 0,33 og 0,44 i tidsseriene, andre budsjettvedtak); de desentraliserte reglene og
`sched` endret seg ikke. Å endre `gamma`, `Lam0`, `T0`, `eta` eller `ksat` i `simulate_endo` er derfor en endring i
kjernen (arbeidskopi og ferdigheten `forbedre`).
### `regel` (bare `simulate_endo`)
@ -162,9 +216,12 @@ Hvor tiltakene virker:
### `slutt`
Horisont i måneder, større enn 0 og høyst 2 400. Standard er 120 (`TEND` i `sim.py`); notatets kjøringer med
Horisont i måneder, minst 1 og høyst 2 400. Standard er 120 (`TEND` i `sim.py`); notatets kjøringer med
`simulate_endo` bruker 180. For simulatorene som leser den globale `TEND`, setter kjøreren `sim.TEND` midlertidig og
setter den tilbake etterpå; `simulate_endo` får den som argumentet `tend`. Tidssteget er alltid `DT` = 0,05 måned.
Er horisonten kortere enn forsinkelsen i styringen, stopper kjøreren med en melding om `slutt`. Kommer sjokket ved
eller etter `slutt` (eller varer det i `simulate_soc` lenger enn `slutt`), kjøres det, men sammendraget har en
merknad om det.
### `sveip`
@ -184,7 +241,7 @@ To sveip i en liste gir et rutenett (alle kombinasjoner, den første parameteren
```
`parameter` er en sti med punktum inn i scenariofila. Stien kan begynne med `styring`, `parametere`, `regel`,
`sjokk`, `tiltak`, `slutt` eller `utvidelse`, og må ende i én verdi, for eksempel:
`sjokk`, `tiltak`, `slutt`, `utvidelse` eller `startlikevekt`, og må ende i én verdi, for eksempel:
| Sti | Hva |
|---|---|
@ -229,7 +286,7 @@ LaTeX-kilden leser filene direkte).
|---|---|
| `simulate`, `simulate_soc` | `t K U L T` |
| `simulate_chain` | `t K U L T Q tauD JD RD xi`, og `Z Z_rel` når kapasiteten er endelig |
| `simulate_chain2` | `t K KA KB U L T QA QB tauA tauB RA RB` |
| `simulate_chain2` | `t K KA KB U L T QA QB tauA tauB RA RB Z Z_rel xi` |
| `simulate_endo` | `t K U L T Q tauD JD RD Z xi q Z_rel` |
| `utvidelse:paadrag_tidsserie` | som `simulate_chain` (med kjede) eller `simulate` (uten), og `Omega_faktor` |
@ -238,8 +295,10 @@ politiets innsats (1 = før sjokket), `Q` køen i domstolen, `tauD` saksbehandli
saker avgjort per måned, `xi` andelen som går til domstolen (1 − andel utenom), `Z` kapasiteten og `Z_rel` kapasiteten
delt på kapasiteten ved start, `q` prosessuell kvalitet. I `simulate_chain2` er `KA`/`KB`, `QA`/`QB`, `tauA`/`tauB` og
`RA`/`RB` det samme per sakstype. Kø, strøm og kapasitet er i modellens normaliserte enheter. `Z`, `xi` og `Z_rel` i
`simulate_chain` regnes ut av kjøreren fra kjernens `chain_at`; `Z_rel` i `simulate_endo` er det notatets `endo_*.dat`
kaller `Z`.
`simulate_chain` regnes ut av kjøreren fra kjernens `chain_at`, og i `simulate_chain2` fra `Zfun` og `xiBfun` slik
`court2` bruker dem (der er `xi` andelen av alle saker som går til domstolen, `piA`·`xiA` + (1 − `piA`)·ξ_B).
`Z_rel` i `simulate_endo` er det notatets `endo_*.dat` kaller `Z`. I datafilene står køen i modellens enheter; i
sammendraget står den i måneder av innstrømningen før sjokket (se under).
## Hva kjøreren skriver
@ -248,15 +307,22 @@ I mappa `resultater/<navn>/` under mappa du står i, eller i mappa du gir med `-
- `sammendrag.md`: hva som ble kjørt, hvilke felt fila satte selv, nøkkeltallene i en tabell (én kolonne per
kjøring, høyst seks per tabell), for et rutenett også en tabell med ett nøkkeltall over begge parametrene (spennet
i K de siste 60 månedene når horisonten er over 120 mnd, ellers spennet etter måned 60), og for
`simulate_endo` budsjettvedtakene fra regelen. Det samme skrives til skjermen, sammen med stien til mappa.
`simulate_endo` budsjettvedtakene fra regelen. Med `"kalibrer"` står de kalibrerte verdiene øverst, og med
`"ubalanse"` driften før sjokket. Under «Merknader» står forhold som ikke stopper kjøringen: at K går utenfor
0,01–12 (området kjernens `soc_equilibria` leter i; notatets egne kjøringer ligger mellom 0,83 og 9,82), og at
sjokket ligger utenfor horisonten. Det samme skrives til skjermen, sammen med stien til mappa.
- én CSV (eller `.dat`) per kjøring;
- `resultat.json`: scenarioets navn og fil med sha256, Python-, numpy- og scipy-versjon, sha256 for de tre filene i
kjernen (og for utvidelsen), og for hver kjøring alle parametere slik simulatoren fikk dem (fullt utfylt, også det
fila ikke satte), nøkkeltallene med full presisjon og budsjettvedtakene. Uendelig står som `"inf"`.
fila ikke satte), nøkkeltallene med full presisjon, budsjettvedtakene og `startlikevekt`. Uendelig står som
`"inf"`.
Samme scenariofil gir samme bytes i alle tre filene på samme maskin og med samme versjoner: det står ingen
tidspunkter i dem. Finnes mappa fra før med et tidligere resultat, tas bare filene det resultatet laget bort. Finnes
den med andre filer, stopper kjøreren uten å skrive noe.
tidspunkter i dem. Finnes mappa fra før med et tidligere resultat (en `resultat.json` med kjørerens egne nøkler), tas
bare filene det resultatet laget bort: sammendraget, `resultat.json` og `.csv`/`.dat`-filer uten mappe i navnet. Alle
andre filer blir stående. Finnes mappa med andre filer og uten et slikt resultat, stopper kjøreren uten å skrive noe.
Kan mappa ikke lages eller skrives i, stopper den med «Kan ikke skrive i <mappe>: <grunn>. Velg en annen mappe med
--ut.» (exit 2).
## Nøkkeltallene
@ -273,15 +339,17 @@ nøkkeltall vises bare når modellen har tidsserien det bygger på.
| `T_maks`, `T_slutt` | politiets innsats |
| `L_min`, `L_slutt` | tillit |
| `tauD_maks`, `tauD_slutt` (og `tauA_*`, `tauB_*`) | saksbehandlingstid, mnd |
| `Q_slutt` (og `QA_slutt`, `QB_slutt`) | kø ved slutt |
| `Q_slutt_mnd` (og `QA_slutt_mnd`, `QB_slutt_mnd`) | kø ved slutt, i måneder av innstrømningen før sjokket (Q delt på J0 = `gamma`·`Lam0`·`T0`, 0,1 med notatets tall; køen før sjokket er om lag 2,9) |
| `inn_ut_slutt` | saker inn delt på saker avgjort ved slutt (`ups_end` i `summary`) |
| `Z_maks`, `Z_slutt` | kapasitet, 1,00 = ved start (når kapasiteten endres) |
| `utenom_maks_pst`, `utenom_slutt_pst` | andel ledet utenom, prosent (når den endres) |
| `q_maks` | prosessuell kvalitet (når kvalitetssløyfen er på) |
| `frafall`, `kapasitetsaar` | frafall fra køen summert over tid, i måneder av innstrømningen før sjokket (som i notatets tabell i avsnitt 8.5; `summary_endo` skriver det i modellens enheter, en tidel av dette), og kapasitetsår over utgangsnivået (`simulate_endo`) |
Under tabellen står: 1,00 er nivået i referanselikevekten før sjokket for K og T, tilliten er 0,80 der, kapasitet
1,00 er kapasiteten ved start, tider er i måneder.
Under tabellen står: 1,00 er nivået i referanselikevekten før sjokket for K og T (i modellene med kø: nivået ved
start, nesten, men ikke helt, en likevekt, med driften før sjokket), tilliten er `Lam0` der (0,80 med notatets tall),
kapasitet 1,00 er kapasiteten ved start, tider er i måneder, og (når modellen har kø) at køen er målt i måneder av
innstrømningen før sjokket. Med `"startlikevekt": "ubalanse"` byttes den første delen ut, se over.
## Feil og exit-koder
@ -289,13 +357,15 @@ Exit-kode 0 når alt gikk, 2 ved feil i scenariofila eller bruken. Meldingen nev
```
Feil i scenariofila mitt.json: parametere.P.gama: ukjent parameter i P. Mente du "gamma"? Gyldige: gamma, eta, ...
Feil i scenariofila mitt.json: styring.forsinkelse: må være et tall minst 0 og høyst 120, ikke "tre".
Feil i scenariofila mitt.json: styring.forsinkelse: må være et tall minst 0, ikke "tre".
Feil i scenariofila mitt.json: sveip[0].parameter: "styring.reaktivtet": ukjent felt "reaktivtet" under styring. ...
Fila mitt.json er ikke gyldig JSON: feil på linje 2, kolonne 15.
```
Stopper en kjøring i kjernen (en kombinasjon uten likevekt ved start eller med en løsning som vokser over alle
grenser), sier meldingen hvilken kjøring det var, og ingenting skrives.
Stopper en kjøring i kjernen (en regnefeil, for eksempel en kombinasjon der kjernen ikke finner køen ved start), eller
gir den verdier som ikke er endelige tall (uendelig eller NaN, en løsning som har vokst over alle grenser), stopper
kjøreren med exit 2. Meldingen sier hvilken kjøring det var og hvilke tidsserier, og ingenting skrives. Store, men
endelige utslag stopper ikke; de får en merknad i sammendraget.
## Eksempelfilene
@ -325,3 +395,4 @@ simuleringer, og har ingen scenariofil.
| `sjokkstorrelse_kjede.json` | hvor mye betyr størrelsen på sjokket når domstolen er en flaskehals? |
| `kvalitetssloyfe.json` | hva gjør kvalitetssløyfen med desentraliserte og koordinerte regler? |
| `utvidelse_bolge.json` | hva etterlater en forbigående bølge i pådraget, med og uten flaskehals (utvidelsen `paadrag_tidsserie`)? |
| `uten_sjokk_kjede.json` | hvor mye driver modellen med kø av seg selv uten sjokk (240 mnd)? Nullinjen å sammenligne små virkninger med |

View file

@ -28,6 +28,7 @@ gjort i en annen rekkefølge, og da er det ikke lenger kjernens modell.
| `grensetilfelle() -> list[tuple[str, float]]` | funksjon | minst ett tilfelle: (beskrivelse, største absolutte avvik mot kjernen). Hvert avvik skal være `0.0` |
| `BRUKER` | tuple, valgfri | hvilke av kjernens dicts (`"P"`, `"CH0"`, `"CH2"`, `"ENDO"`, `"SOC"`) som kan overstyres under `parametere`. Uten den: alle fem |
| `sjekk(scenario) -> None` | funksjon, valgfri | kalles før kjøringen; en `ValueError("felt: hva som er galt")` gir exit-kode 2 med meldingen din |
| `GJENSKAPER_NOTATET` | tuple, valgfri | navn på scenariofiler i `eksempler/scenarioer/notatet/` (uten `.json`) som utvidelsen med standardverdiene skal gjenskape byte for byte når `"modell"` byttes til `"utvidelse:<navn>"`. Testene kjører dem gjennom kjøreren og sammenligner med `fasit/data` |
### Hva `simuler` får
@ -52,6 +53,11 @@ Dictene kan sendes rett til kjernens funksjoner (`sim.deriv_chain(..., p, ch, ..
sjekket mot standardverdiene: tall (også `"inf"`), heltall, true/false, tekst eller liste. Innholdet i en liste
sjekker du selv i `sjekk`. Feltet `sjokk` i scenariofila er ikke tillatt for utvidelser: pådraget er utvidelsens sak.
Før `simuler` kalles, sjekker kjøreren at startpunktet er en likevekt for dictene i `BRUKER`, på samme måte som for
kjernens modeller (`startlikevekt` i [scenarioformat.md](scenarioformat.md)): `P` for grunnmodellen, og `CH0` eller
`CH2` for domstolen når kapasiteten er endelig. Med `"startlikevekt": "kalibrer"` får `simuler` de kalibrerte
verdiene i `parametere`.
### Hva `simuler` returnerer
En dict med tidsserien `"t"` (numpy-array) og andre tidsserier med samme lengde, med kjernens navn der det gir mening
@ -67,6 +73,10 @@ vedtakslinjer i sammendraget.
(...)», eller hvilket tilfelle som ikke gjør det.
- `tests/test_utvidelser.py` går gjennom alle utvidelsene: grensesnittet finnes, og hvert avvik er `0.0`. En ny
utvidelse er med uten at testen endres.
- Avviket `grensetilfelle()` rapporterer, er utvidelsens egen beregning; en utvidelse som bare returnerer `0.0`,
ville gått gjennom. Derfor bør utvidelsen oppgi `GJENSKAPER_NOTATET`: da kjører testen de scenariofilene gjennom
scenariokjøreren med utvidelsen og sammenligner datafilene byte for byte med `fasit/data`, uavhengig av
`grensetilfelle()`. Mangler den, sier testen fra (hoppet over, med grunn).
- `resultat.json` har sha256 for utvidelsens fil, så det er sporbart hvilken versjon en kjøring brukte.
## Gjennomført eksempel: `paadrag_tidsserie`
@ -97,8 +107,9 @@ Grensetilfellene, som `grensetilfelle()` returnerer:
| trapp med ett punkt `[12, 1.3]`, notatets `CH0` | `simulate_chain("P", 3.0, CH0)` | 0.0 (t, K, U, L, T, Q, tauD, JD, RD og tau_ref) |
| samme trapp, `CH0.Z` = inf | `simulate("P", 3.0)` | 0.0 (t, K, U, L, T) |
Testene viser i tillegg at utvidelsen kjørt gjennom scenariokjøreren med standardverdiene gir notatets
`kjede_s2.dat` og `p3.dat` byte for byte, og med tiltakene fra scenario 5 notatets `kjede_s5.dat`.
Utvidelsen oppgir `GJENSKAPER_NOTATET = ("kjede_s2", "kjede_s3", "kjede_s4", "kjede_s5")`: kjørt gjennom
scenariokjøreren med standardverdiene (og tiltakene fra scenario 3–5) gir den notatets datafiler byte for byte. Testene
viser i tillegg at den med `CH0.Z` = inf gir `p3.dat`.
Eksempel: `eksempler/scenarioer/utvidelse_bolge.json` kjører en bølge (40 % høyere pådrag fra måned 18 til 36, med
lineær stigning og fall) med og uten flaskehals i domstolen.
@ -218,8 +229,10 @@ den kan kjøres med en scenariofil, så malen virker slik den står her.
returner største absolutte avvik over alle tidsseriene. Ta gjerne med flere tilfeller (andre forsinkelser, med og
uten kjede).
6. **Kontroller**: `python3 modell/kjor.py kontroll --rask` skal si at grensetilfellene gjenskaper kjernen eksakt.
Kjør testene (`python3 -m pytest tests/test_utvidelser.py`); utvidelsen er med automatisk. Skriv gjerne en egen
test for det mekanismen skal gjøre utenfor grensetilfellet.
Kjør testene (`python3 -m pytest tests/test_utvidelser.py`); utvidelsen er med automatisk. Oppgi
`GJENSKAPER_NOTATET` hvis standardverdiene gjenskaper en av notatets scenariofiler, så sjekkes grensetilfellet
også uavhengig av din egen `grensetilfelle()`. Skriv gjerne en egen test for det mekanismen skal gjøre utenfor
grensetilfellet.
7. **Bruk den**: skriv en scenariofil med `"modell": "utvidelse:<navn>"` og parametrene under `"utvidelse"`, og kjør
`python3 modell/kjor.py scenario <fil.json>`. Sveip virker som ellers, også over `utvidelse.<parameter>`.
8. **Send den tilbake** hvis du vil: se `BIDRA.md` i pluginen.