Tilbake
8.1

8.1 Fra ODE til kode: Euler–Cromer-løkka

Diskretiser tiden, skriv akselerasjonen fra ODE-en, oppdater fart så posisjon — og begrunn hvorfor Euler–Cromer er sensorens foretrukne metode.

70 min
8 oppgaver
Fra ODE til kodeEuler–Cromer-løkka
Din fremgang i kapitlet
0 / 8 oppgaver
Kapitlets plass i kurset

Forkunnskaper — sist du var her

Dette kapitlet bygger på kap. 7.1. Det du trenger derfra, ferdig oppfrisket:

Bevegelseslikningen er Newtons 2. lov med akselerasjonen skrevet som andrederivert av posisjonen:

md2xdt2=F(x,dxdt,t)m\frac{d^2x}{dt^2} = \sum F\left(x, \frac{dx}{dt}, t\right)

Høyresiden er summen av kreftene, og den kan avhenge av hvor legemet er, hvor fort det går, og hva klokka er.

Tilstandsform — én andreordens likning skrives om til to førsteordens:

dxdt=v,dvdt=1mF\frac{dx}{dt} = v, \qquad \frac{dv}{dt} = \frac{1}{m}\sum F

Denne omskrivingen er hele broa til koden: de to likningene blir de to oppdateringslinjene i løkka.

Initialbetingelser. En likning av orden nn trenger nn opplysninger om starttilstanden. For en andreordens bevegelseslikning betyr det posisjonen x(0)x(0) og farten v(0)v(0) — akkurat de to tallene som blir x[0] og v[0] i koden.

Fra matematikken: Numerisk integrasjon og Programmering av integrasjon gir ideen om å erstatte en kontinuerlig størrelse med en sum over små steg. Numerisk modellering av bevegelse viser den samme løkka i en enklere innpakning — du kan ha møtt den i Fysikk 1 eller Fysikk 2 på videregående.

Python-grunnlaget — lister, for-løkker og numpy-tabeller — hører hjemme i programmeringsemnet IN1900, som ikke er bygget i dette systemet. Alt du faktisk trenger her, blir forklart underveis: fire linjer kode og én løkke.

Notasjonsavtale for kapitlet. Vi bruker xx om posisjonen langs én akse, vv om farten med fortegn, aa om akselerasjonen, tt om tiden, dtdt om steglengden i tid og NN om antallet lagrede tidspunkt. Variabelnavnene i koden er de samme bokstavene, slik at fysikken og koden ser like ut. Vi bruker g=9,81 m/s2g = 9{,}81\ \text{m/s}^2.

Løkke 1 — tiden i skiver (~14 min)

Et videokamera som filmer en fallende ball, ser ikke en kontinuerlig bevegelse. Det tar 50 bilder i sekundet, og mellom to bilder vet kameraet ingenting. Likevel klarer du å lese av hele bevegelsen fra bildeserien, fordi bildene ligger tett nok.

En numerisk løsning gjør nøyaktig det samme. I stedet for en formel x(t)x(t) som gjelder for alle tidspunkt, regner vi ut posisjonen i en serie tidspunkt som ligger tett nok. Svaret er ikke en funksjon, men en tabell av tall.

Intuisjon: analytisk løsning gir deg fasiten i én formel. Numerisk løsning gir deg fasiten som en filmstrimmel. Er strimmelen tett nok, ser du det samme.

Det er tre ting som må på plass før løkka kan starte: et tidsrutenett, en steglengde, og plass til å lagre svaret.

Tidsrutenettet
Rekka av tidspunkt der vi faktisk regner ut svaret, i stedet for å regne på alle tidspunkt.

t0=0,t1=dt,t2=2dt,,ti=idtt_0 = 0, \quad t_1 = dt, \quad t_2 = 2\,dt, \quad \dots, \quad t_i = i \cdot dt

Tallet ii kalles indeksen, og det er den som teller hvilket tidspunkt vi står i. I koden svarer tit_i til t[i].

Å erstatte kontinuerlig tid med et slikt rutenett kalles å diskretisere. Ordet er ikke mer mystisk enn «å dele opp i skiver» — og det er hele grunnideen bak numerisk løsning av en differensiallikning.

Steglengden dtdt

Tidsavstanden mellom to nabotidspunkt i rutenettet, målt i sekunder.

Steglengden er den ene knappen du kan skru på i en numerisk beregning. Liten dtdt gir et tettere rutenett og et mer nøyaktig svar, men flere steg og lengre regnetid. Stor dtdt går fort, men svaret kan bli ubrukelig.

En brukbar tommelfingerregel for svingninger og kast er at dtdt skal være et par tusen ganger mindre enn den karakteristiske tiden i problemet — svingeperioden, falltiden eller tidskonstanten. Har systemet en periode på ett sekund, er dt=0,001 sdt = 0{,}001\ \text{s} et trygt valg.

Sammenhengen mellom antall punkt og sluttiden er tslutt=(N1)dtt_{\text{slutt}} = (N-1)\,dt: velger du to av de tre størrelsene, er den tredje låst.

Forhåndsallokerte arrays

Tabeller som opprettes med full lengde før løkka starter, slik at hvert tidspunkt har sin ferdige plass å skrive svaret sitt inn i.

t = np.zeros(N)
x = np.zeros(N)
v = np.zeros(N)

np.zeros(N) lager en tabell med NN nuller. Etter løkka inneholder x posisjonen ved hvert av de NN tidspunktene, og du kan lese ut, plotte eller lete i hele forløpet.

Hvorfor gjøre det på forhånd? Fordi løkka da bare skal fylle inn, ikke utvide. Det er raskere, og — viktigere for eksamen — det tvinger fram indeksskrivemåten x[i], som er nettopp det sensor ser etter.

Initialbetingelsene bor i indeks 0. De to opplysningene om starttilstanden — hvor legemet er, og hvor fort det går — skrives rett inn i første plass i tabellene:

x[0] = 0.100        # m
v[0] = 0.0          # m/s

Her ligger ett av de fem poengene i en typisk kodedeloppgave. Det er også det billigste poenget i hele oppgaven: to linjer, og de kan skrives selv om du står helt fast på resten.

Intuisjon: differensiallikningen er loven som styrer bevegelsen. Initialbetingelsene er den ene bevegelsen loven faktisk beskriver. Uten dem har koden ingenting å begynne med.

📝Oppgave 1

Forklar med egne ord, i to–tre setninger hver:

a) hva som er forskjellen på en analytisk og en numerisk løsning av en bevegelseslikning,

b) hva np.zeros(N) gjør, og hvorfor tabellene lages før løkka starter,

c) hvorfor en andreordens bevegelseslikning trenger nøyaktig to initialbetingelser i koden.

Løkke 2 — akselerasjonslinja (~14 min)

Nå kommer den delen som gir flest poeng. Av de fem poengene i en typisk kodedeloppgave ligger tre i én eneste linje: uttrykket for akselerasjonen.

Grunnen er enkel. Akselerasjonslinja er det eneste stedet i koden der fysikken i akkurat ditt problem står. Resten av løkka er den samme uansett om du modellerer en fjær, en pendel, et kast eller en komet.

Akselerasjonslinja
Den ene kodelinja som oversetter bevegelseslikningen til noe maskinen kan regne på: akselerasjonen isolert alene på venstre side, uttrykt ved tilstanden i det tidspunktet du står i.

d2xdt2=1mFa = ...\frac{d^2x}{dt^2} = \frac{1}{m}\sum F \qquad \longrightarrow \qquad \texttt{a = ...}

Alt på høyresiden må være noe koden allerede kjenner: konstantene (mm, kk, gg, DD) og tilstanden i indeks ii (x[i] og v[i]).

Dette er de tre poengene. En akselerasjonslinje med riktig fortegn, riktige krefter og riktig masse-deling er selve fysikken i deloppgaven — resten av løkka er mal.

📜Fra differensiallikning til kode — fire steg
Steg 1 — skriv bevegelseslikningen fra frilegemediagrammet. Newtons 2. lov med alle kreftene, i den positive retningen du har valgt.

Intuisjon: dette er det samme steget som i kap. 7.1. Koden gjør ikke fysikken for deg.

Steg 2 — isolér den høyeste deriverte. Del på massen, slik at d2x/dt2d^2x/dt^2 står alene.

Intuisjon: maskinen kan bare oppdatere det den får servert direkte. En likning der akselerasjonen er blandet inn i et større uttrykk, kan ikke oversettes linje for linje.

Steg 3 — bytt symbol med indeks. Alt som er xx, blir x[i]. Alt som er vv, blir v[i]. Konstantene beholder navnene sine.

Intuisjon: indeksen sier «slik ser høyresiden ut akkurat nå», og det er det eneste maskinen kan vite i det øyeblikket.

Steg 4 — sett akselerasjonen inn i løkka, oppdater farten, og deretter posisjonen.

Intuisjon: likningen forteller hvordan tilstanden endrer seg. Løkka gjør endringen om til en ny tilstand.

Tre eksempler på steg 1–3:

BevegelseslikningAkselerasjonslinje
md2x/dt2=kxm\,d^2x/dt^2 = -kxa = -(k / m) * x[i]
mdv/dt=mgkvvm\,dv/dt = mg - k_v va = g - (kv / m) * v[i]
md2θ/dt2=mgsinθm\ell\,d^2\theta/dt^2 = -mg\sin\thetaa = -(g / L) * np.sin(theta[i])

Legg merke til at massen faller ut i den siste — men det er noe du ser etter å ha delt, ikke noe du antar på forhånd.
✏️Eksempel 1: Fjærpendelen, fra likning til kode

En vogn med masse m=1,00 kgm = 1{,}00\ \text{kg} ruller uten friksjon på en luftputebane og er festet til en fjær med fjærkonstant k=25,0 N/mk = 25{,}0\ \text{N/m}. Ved t=0t = 0 trekkes vogna 0,100 m0{,}100\ \text{m} ut fra likevekt og slippes fra ro.

a) Sett opp bevegelseslikningen og akselerasjonslinja.

b) Skriv en fullstendig kode som løser likningen numerisk over 20 sekunder, og kjør den.

c) Kontroller resultatet mot den analytiske løsningen fra kap. 7.2.

Metodevalg: likningen er andreordens, lineær og homogen, og har derfor en kjent analytisk løsning — det er nettopp derfor den egner seg som kontrollcase for koden. Vi bruker Euler–Cromer, som er den metoden emnet bygger på; begrunnelsen kommer i løkke 3–4.

a) Vogna er ett legeme. Tyngden og normalkraften er loddrette og opphever hverandre, så fjærkraften kx-kx er den eneste vannrette kraften:

md2xdt2=kxd2xdt2=kmx=25,0xm\frac{d^2x}{dt^2} = -kx \qquad \Longrightarrow \qquad \frac{d^2x}{dt^2} = -\frac{k}{m}x = -25{,}0\,x

Akselerasjonslinja blir dermed a = -(k / m) * x[i].

b) Vi velger dt=0,0010 sdt = 0{,}0010\ \text{s}. Svingeperioden er Tp=2πm/k=1,26 sT_p = 2\pi\sqrt{m/k} = 1{,}26\ \text{s}, så steglengden er om lag 1 300 ganger mindre enn perioden — godt innenfor tommelfingerregelen. Med N=20001N = 20\,001 punkt blir sluttiden (N1)dt=20,0 s(N-1)\,dt = 20{,}0\ \text{s}, altså snaut 16 svingninger.

import numpy as np

m = 1.00            # kg
k = 25.0            # N/m
N = 20001           # antall tidspunkt
dt = 0.0010         # s

t = np.zeros(N)
x = np.zeros(N)
v = np.zeros(N)

x[0] = 0.100        # m, utslag ved t = 0
v[0] = 0.0          # m/s, sluppet fra ro

for i in range(N - 1):
    a = -(k / m) * x[i]
    v[i + 1] = v[i] + a * dt
    x[i + 1] = x[i] + v[i + 1] * dt
    t[i + 1] = t[i] + dt

print("sluttid        ", round(t[-1], 3))
print("x ved slutt    ", round(x[-1], 5))
print("v ved slutt    ", round(v[-1], 5))
print("storste utslag ", round(np.max(np.abs(x)), 7))

Utskrift:

sluttid         20.0
x ved slutt     0.08636
v ved slutt     0.25314
storste utslag  0.1000003

c) Den analytiske løsningen er x(t)=0,100cos(ω0t)x(t) = 0{,}100\cos(\omega_0 t) med ω0=k/m=5,00 rad/s\omega_0 = \sqrt{k/m} = 5{,}00\ \text{rad/s}, altså

x(20,0)=0,100cos(100)=0,08623 mx(20{,}0) = 0{,}100\cos(100) = 0{,}08623\ \text{m}

Koden gir 0,08636 m0{,}08636\ \text{m}. Avviket er 1,3104 m1{,}3\cdot 10^{-4}\ \text{m}, altså 0,13 %0{,}13\ \% av amplituden — etter nesten seksten svingninger.

Legg merke til hva slags feil det er. Det største utslaget er 0,1000003 m0{,}1000003\ \text{m} mot eksakt 0,100000 m0{,}100000\ \text{m}: amplituden er praktisk talt uendret. Hele avviket er en liten faseforskyvning — den numeriske svingningen går et ubetydelig lite hakk i utakt med den eksakte. Det er nøyaktig den oppførselen som gjør Euler–Cromer brukbar over lange tider, og vi kommer tilbake til den i løkke 4.

Sensorblikk: oppgaveteksten på eksamen ber om «en kode» eller «pseudokode», ikke om et kjørbart program. De fem poengene sitter i x[0] og v[0], i akselerasjonslinja, og i de to oppdateringslinjene i riktig rekkefølge. Import-linja og print gir ingen poeng — men de koster heller ingenting.

Slik ser den håndskrevne eksamensversjonen ut
📝Oppgave 2

Skriv akselerasjonslinja i kode for hver av bevegelseslikningene. Bruk variabelnavnene som står i parentes, og anta at tilstanden i tidspunkt ii heter x[i] og v[i].

a) md2xdt2=kxmgm\dfrac{d^2x}{dt^2} = -kx - mg (variabler m, k, g)

b) mdvdt=mgkvvm\dfrac{dv}{dt} = mg - k_v v (variabler m, g, kv)

c) md2ydt2=mg+F0cos(ωt)m\dfrac{d^2y}{dt^2} = -mg + F_0\cos(\omega t), med tiden tilgjengelig som t[i] (variabler m, g, F0, w)

d) Hvilken av de tre linjene er den eneste der massen ikke faller ut? Hva betyr det praktisk?

Løkke 3 — selve løkka, og den ene indeksen som avgjør alt (~16 min)

Nå står akselerasjonen der. Spørsmålet er hvordan vi kommer fra tilstanden i tidspunkt ii til tilstanden i tidspunkt i+1i+1.

Ideen er den enkleste tenkelige. Over et lite tidssteg dtdt er akselerasjonen omtrent konstant, så farten endrer seg med adta\,dt. Og over det samme steget er farten omtrent konstant, så posisjonen endrer seg med vdtv\,dt:

vi+1=vi+aidt,xi+1=xi+vdtv_{i+1} = v_i + a_i\,dt, \qquad x_{i+1} = x_i + v\,dt

Og så kommer spørsmålet som hele dette kapitlet handler om: hvilken vv skal stå i den andre linja — den gamle viv_i, eller den nye vi+1v_{i+1}?

Svaret virker som en detalj. Det er det ikke.

Euler–Cromer-metoden
Den numeriske metoden der farten oppdateres først, og den nye farten deretter brukes til å oppdatere posisjonen:

vi+1=vi+aidt,xi+1=xi+vi+1dtv_{i+1} = v_i + a_i\,dt, \qquad x_{i+1} = x_i + v_{i+1}\,dt

I kode:

v[i+1] = v[i] + a*dt
x[i+1] = x[i] + v[i+1]*dt

Metoden er oppkalt etter Alar Cromer, som beskrev den i 1981 som en «forbløffende enkel» rettelse av standardmetoden for mekanikk-simuleringer.

Dette er metoden dette emnet bygger på, og den sensor forventer. Grunnen er at den holder energien i sjakk over lange tider — se løkke 4.

Forward Euler-metoden
Den numeriske metoden der begge oppdateringene bruker tilstanden i det gamle tidspunktet:

xi+1=xi+vidt,vi+1=vi+aidtx_{i+1} = x_i + v_i\,dt, \qquad v_{i+1} = v_i + a_i\,dt

I kode:

x[i+1] = x[i] + v[i]*dt
v[i+1] = v[i] + a*dt

Metoden er den mest opplagte oversettelsen av differensiallikningen, og den brukes mye i andre sammenhenger. For svingninger og baner er den systematisk dårlig: den tilfører energi til systemet steg for steg, slik at amplituden vokser og banene spiraler utover.

Legg merke til at forskjellen fra Euler–Cromer er én indeks. Bytter du rekkefølgen på de to linjene uten å endre indeksene, har du byttet metode.

Indeksdisiplin

Kravet om at hver størrelse i løkka skal skrives med den indeksen som sier hvilket tidspunkt den hører til: v[i], v[i+1], x[i], x[i+1].

Skriver du v = v + a*dt uten indeks, forsvinner nettopp den opplysningen løkka handler om. På eksamen koster det typisk 2 poeng, selv når meningen er åpenbar for en leser — og i en kode som faktisk skal kjøre, ødelegger det lagringen av forløpet, siden bare den siste verdien overlever.

Unntaket er akselerasjonen. a regnes ut på nytt i hvert eneste gjennomløp og brukes umiddelbart, så den trenger ikke lagres. Vil du likevel ha den som array, skriver du a[i] — det er også riktig, bare mer arbeid for hånd.

📝Oppgave 3

En medstudent har skrevet denne løkka for fjærpendelen i eksempel 1. Den kjører uten feilmelding, men gir gale svar.

N = 20001
dt = 0.0010
x = np.zeros(N)
v = np.zeros(N)

for i in range(N - 1):
    a = (k / m) * x[i]
    x[i + 1] = x[i] + v[i] * dt
    v = v[i] + a * dt

a) Finn de tre feilene, og si for hver av dem hva den gjør med resultatet.

b) Hvorfor gir koden bare nuller uansett, slik den står?

c) Skriv den rettede løkka.

Løkke 4 — hvorfor Euler–Cromer, og hvorfor det er verdt poeng (~14 min)

Begge metodene gjør en feil i hvert steg, og feilen er like stor i begge. Forskjellen ligger i hvordan feilene legger seg sammen over tid.

Tenk på en svingende fjær. Halve tiden går klossen utover, halve tiden innover.

- Euler–Cromer bruker den nye farten i posisjonsoppdateringen. Den overvurderer forflytningen i noen deler av svingningen og undervurderer den i andre — den tar av og til for mye og av og til for lite. Over en hel periode kansellerer bidragene hverandre nesten fullstendig, og energien vandrer bare litt fram og tilbake rundt riktig verdi.
- Forward Euler bruker den gamle farten. På vei utover er den gamle farten større enn gjennomsnittsfarten i steget, så klossen flyttes for langt. På vei innover er den gamle farten også større i tallverdi, så klossen flyttes for langt inn — men fjæren rekker å presse hardere. Nettoeffekten er systematisk: energien vokser i hvert eneste steg.

Intuisjon: Euler–Cromer gjør en feil som svinger; Forward Euler gjør en feil som drar. Det er drivingen som ødelegger, ikke størrelsen på enkeltfeilen.

Energidrift

At den beregnede mekaniske energien i et system endrer seg systematisk gjennom simuleringen, selv om den fysisk skal være bevart.

Energidrift er en metodefeil, ikke fysikk. Symptomene er lette å kjenne igjen:

- en oscillator der amplituden vokser jevnt periode for periode;
- en planet- eller kometbane som spiraler utover i stedet for å lukke seg;
- en pendel som svinger høyere og høyere uten at noe tilfører den energi.

Forward Euler har energidrift oppover for svingninger og baner. Euler–Cromer har det ikke: energien svinger rundt riktig verdi uten å vandre av gårde. Det er derfor metoden er standard i mekanikk-simuleringer, og det er nettopp dette du skal kunne si i de to setningene som begrunner metodevalget.

✏️Eksempel 2: Hva én indeks koster over 20 perioder

Kjør fjærpendelen fra eksempel 1 med begge metodene i 20 svingeperioder, med samme steglengde dt=0,0010 sdt = 0{,}0010\ \text{s}. Sammenlikn amplituden og den mekaniske energien til slutt.

Den mekaniske energien er E=12mv2+12kx2E = \tfrac12 mv^2 + \tfrac12 kx^2, og amplituden kan leses ut av den: A=2E/kA = \sqrt{2E/k}.

Metodevalg: vi bruker energien som måltall, fordi den er den skarpeste indikatoren på metodefeil. Fysisk skal EE være konstant, siden ingen friksjon virker og fjærkraften er konservativ. Alt avvik fra konstant EE er metodefeil, ikke fysikk.

Perioden er Tp=2πm/k=1,2566 sT_p = 2\pi\sqrt{m/k} = 1{,}2566\ \text{s}, så 20 perioder er 25,13 s25{,}13\ \text{s}, altså N=25133N = 25\,133 punkt med dt=0,0010 sdt = 0{,}0010\ \text{s}.

import numpy as np

m = 1.00
k = 25.0
dt = 0.0010
N = 25133           # 20 perioder

for metode in ["Euler-Cromer", "Forward Euler"]:
    x = np.zeros(N)
    v = np.zeros(N)
    x[0] = 0.100
    for i in range(N - 1):
        a = -(k / m) * x[i]
        if metode == "Euler-Cromer":
            v[i + 1] = v[i] + a * dt
            x[i + 1] = x[i] + v[i + 1] * dt
        else:
            x[i + 1] = x[i] + v[i] * dt
            v[i + 1] = v[i] + a * dt
    E = 0.5 * m * v**2 + 0.5 * k * x**2
    print(metode, " amplitude:", round(np.sqrt(2 * E[-1] / k), 6),
          " E/E0:", round(E[-1] / E[0], 6))

Utskrift:

Euler-Cromer  amplitude: 0.100001  E/E0: 1.000018
Forward Euler  amplitude: 0.136909  E/E0: 1.874407

Tolkning. Euler–Cromer har en amplitude på 0,100001 m0{,}100001\ \text{m} mot eksakt 0,100000 m0{,}100000\ \text{m}, og energien er 0,002 %0{,}002\ \% for høy — altså praktisk talt bevart. Forward Euler har en amplitude på 0,1369 m0{,}1369\ \text{m}, som er 37 %37\ \% for stor, og en energi som er 87 %87\ \% for høy. Systemet har fått nesten dobbelt så mye energi som det startet med, av ingenting.

Poenget er ikke at Forward Euler er unøyaktig — begge metodene har feil av samme størrelsesorden i ett enkelt steg. Poenget er at Euler–Cromers feil svinger rundt null mens Forward Eulers feil peker samme vei hver gang. Etter 25 000 steg er forskjellen mellom en feil som kansellerer og en som akkumulerer, forskjellen mellom et brukbart og et ubrukelig svar.

Kontroll av rimeligheten: amplitudeveksten hos Forward Euler kan anslås. Energien vokser med om lag en faktor (1+ω02dt2)(1 + \omega_0^2 dt^2) per steg, altså 1+251061 + 25\cdot 10^{-6}. Over 2513325\,133 steg gir det (1,000025)25133=1,87(1{,}000025)^{25133} = 1{,}87 — nøyaktig det utskriften viser. At et anslag treffer på to desimaler, er den beste kontrollen du kan få på at koden gjør det du tror.

📝Oppgave 4
(Kvalitativ.) Svar i to–fire setninger per punkt.

a) En medstudent sier: «Forward Euler og Euler–Cromer er like nøyaktige, siden begge er førsteordens metoder. Forskjellen betyr ingenting i praksis.» Er påstanden riktig? Begrunn.

b) Du simulerer en pendel og ser at utslaget vokser sakte gjennom hele simuleringen. Nevn to mulige forklaringer, og si hvordan du skiller dem fra hverandre.

c) Formuler de to setningene du ville skrevet på eksamen for å begrunne metodevalget.

Løkke 5 — kontroll av et numerisk svar (~14 min)

En kode som kjører, er ikke en kode som er riktig. Den kan gi et helt korrekt svar på et annet problem enn det du stilte — som i oppgave 3, der de manglende initialbetingelsene ga en perfekt beregnet nulløsning.

Det finnes to kontroller som til sammen fanger nesten alt, og begge tar under et minutt.

Analytisk kontrollcase

Et spesialtilfelle av det samme problemet der du kjenner svaret fra en formel, og som du kjører koden på før du stoler på den.

For en kode som skal håndtere en fjær med luftmotstand, er kontrollcaset å sette motstanden til null: da må koden gi x(t)=Acos(ω0t)x(t) = A\cos(\omega_0 t) med ω0=k/m\omega_0 = \sqrt{k/m}. For en kode som skal håndtere et kast med drag, er kontrollcaset D=0D = 0: da må rekkevidden bli (v02/g)sin2θ(v_0^2/g)\sin 2\theta.

Regelen er enkel: slå av det leddet som gjør problemet vanskelig, og sjekk at koden gjenskaper det du kan regne ut for hånd. Gjør den ikke det, er feilen i infrastrukturen — og da hjelper det ikke å finpusse på det vanskelige leddet.

Konvergenstest

Å kjøre den samme koden på nytt med halvert steglengde og se om svaret endrer seg vesentlig.

Endrer svaret seg lite, er dtdt liten nok. Endrer det seg mye, er den ikke det — og da forteller ingen enkeltkjøring deg hva som er riktig.

Testen skiller også metodefeil fra modellfeil: en feil som skyldes for grov diskretisering, krymper når dtdt krymper. En feil som ligger i fysikken — et tapt kraftledd, et galt fortegn, en feil initialbetingelse — står helt uendret.

Dette er den mest undervurderte kontrollen som finnes, og den er verdt en setning i en besvarelse selv når du ikke har en maskin å kjøre på: «steglengden bør kontrolleres ved å halvere dtdt og se at svaret er stabilt».

Førsteordens metode

En metode der feilen i sluttsvaret er tilnærmet proporsjonal med steglengden: halverer du dtdt, halveres feilen.

Både Euler–Cromer og Forward Euler er førsteordens i denne forstand. For fjærpendelen i eksempel 1, målt mot den analytiske løsningen ved t=10,0 st = 10{,}0\ \text{s}:

dtdtavvik i xx
0,05 s0{,}05\ \text{s}4,3103 m4{,}3\cdot 10^{-3}\ \text{m}
0,01 s0{,}01\ \text{s}7,8104 m7{,}8\cdot 10^{-4}\ \text{m}
0,001 s0{,}001\ \text{s}6,7105 m6{,}7\cdot 10^{-5}\ \text{m}
0,0001 s0{,}0001\ \text{s}6,6106 m6{,}6\cdot 10^{-6}\ \text{m}

Ti ganger mindre steg gir om lag ti ganger mindre feil — akkurat som en førsteordens metode skal. Dette er også den enkleste måten å bevise at et avvik er en diskretiseringsfeil og ikke en modellfeil.

✏️Eksempel 3 (eksamensnivå): Pendelen uten småvinkeltilnærming
En kule henger i en masseløs snor med lengde =0,800 m\ell = 0{,}800\ \text{m} og svinger i et loddrett plan. Luftmotstand kan neglisjeres. Bevegelseslikningen for utslagsvinkelen er

d2θdt2=gsinθ\frac{d^2\theta}{dt^2} = -\frac{g}{\ell}\sin\theta

a) Forklar hvorfor denne likningen ikke kan løses analytisk, og hva småvinkeltilnærmingen fra kap. 7.2 gjør med den.

b) Skisser en Euler–Cromer-kode som løser den fulle likningen med startutslaget θ0=0,600 rad\theta_0 = 0{,}600\ \text{rad} og null starthastighet. Begrunn metodevalget.

c) Kjør koden, og kontroller resultatet mot energibevaring.

d) Kjøringen gir en svingeperiode på 1,8355 s1{,}8355\ \text{s}. Sammenlikn med småvinkelformelen og kommenter.

Metodevalg: likningen er andreordens og ikke lineær, siden den ukjente står inne i en sinusfunksjon. Ingen lukket løsning i elementære funksjoner finnes, og numerisk integrasjon er derfor ikke en snarvei — det er den eneste veien. Vi bruker Euler–Cromer fordi systemet er konservativt og skal simuleres over flere perioder.

a) Ulineariteten kommer av sinθ\sin\theta. Småvinkeltilnærmingen er førsteordensleddet i Taylor-rekka, sinθθ\sin\theta \approx \theta for små θ\theta i radianer, og den gjør likningen om til

d2θdt2=gθ\frac{d^2\theta}{dt^2} = -\frac{g}{\ell}\theta

som er den harmoniske oscillatoren med ω0=g/\omega_0 = \sqrt{g/\ell} og perioden Tp=2π/gT_p = 2\pi\sqrt{\ell/g}. Dette er en tilnærming, ikke en omskriving: den er god for små utslag og blir gradvis dårligere når utslaget vokser.

b) Vinkelen svarer til posisjonen og vinkelfarten til farten, så malen er den samme — bare med nye navn:

import numpy as np

g = 9.81
L = 0.800
N = 5001
dt = 0.0010

t = np.zeros(N)
theta = np.zeros(N)
omega = np.zeros(N)

theta[0] = 0.600    # rad, om lag 34 grader
omega[0] = 0.0

for i in range(N - 1):
    a = -(g / L) * np.sin(theta[i])
    omega[i + 1] = omega[i] + a * dt
    theta[i + 1] = theta[i] + omega[i + 1] * dt
    t[i + 1] = t[i] + dt

print("storste utslag  ", round(np.max(np.abs(theta)), 4))
print("storste omega   ", round(np.max(np.abs(omega)), 4))
print("omega fra energi", round(np.sqrt(2 * g / L * (1 - np.cos(0.600))), 4))

Utskrift:

storste utslag   0.6
storste omega    2.0697
omega fra energi 2.0697

Begrunnelse for metodevalget: pendelen er konservativ — bare tyngden gjør arbeid, og snordraget står vinkelrett på bevegelsen. Euler–Cromer oppdaterer farten først og bruker den oppdaterte farten i posisjonsoppdateringen, og feilen kansellerer derfor over hver periode. Forward Euler ville lagt til energi i hvert steg, og utslaget ville vokst gjennom simuleringen — som ville sett ut som fysikk, men vært en metodefeil.

c) To kontroller:

Amplitude. Det største utslaget er 0,600 rad0{,}600\ \text{rad}, altså nøyaktig startutslaget. En konservativ pendel som slippes fra ro, skal komme like høyt opp hver gang, og gjør det.

Energi. Energibevaring fra ytterstilling til bunnpunkt gir

12m2ωmax2=mg(1cosθ0)ωmax=2g(1cosθ0)\tfrac12 m\ell^2\omega_{\max}^2 = mg\ell(1-\cos\theta_0) \quad \Longrightarrow \quad \omega_{\max} = \sqrt{\frac{2g}{\ell}\left(1-\cos\theta_0\right)}

ωmax=29,810,800(1cos0,600)=2,0697 rad/s\omega_{\max} = \sqrt{\frac{2\cdot 9{,}81}{0{,}800}\left(1-\cos 0{,}600\right)} = 2{,}0697\ \text{rad/s}

Koden gir 2,0697 rad/s2{,}0697\ \text{rad/s} — samsvar på fire desimaler. Dette er en ekte kontroll, ikke en sirkelslutning: energibevaringen er et fysisk argument som ikke inngår i koden noe sted. At de to veiene lander på samme tall, betyr at både akselerasjonslinja og løkka er riktige.

d) Småvinkelformelen gir

Tp=2πg=2π0,8009,81=1,7943 sT_p = 2\pi\sqrt{\frac{\ell}{g}} = 2\pi\sqrt{\frac{0{,}800}{9{,}81}} = 1{,}7943\ \text{s}

Den fulle likningen gir 1,8355 s1{,}8355\ \text{s}, altså 2,3 %2{,}3\ \% lengre. Perioden er ikke uavhengig av amplituden for en ekte pendel — den vokser med utslaget, fordi sinθ\sin\theta er mindre enn θ\theta, slik at den gjenopprettende kraften er svakere enn den lineære modellen sier. Ved θ0=1,500 rad\theta_0 = 1{,}500\ \text{rad} (om lag 8686^\circ) er avviket 16 %16\ \%.

Sensorblikk: en deloppgave som ber om en kode, gir uttelling for løkka. En deloppgave som ber om en sammenlikning med den analytiske modellen, gir uttelling for tolkningen — og her er tolkningen at småvinkeltilnærmingen er god til noen få grader og systematisk for kort periode utover det. Å oppgi tallet 1,8355 s1{,}8355\ \text{s} uten den setningen er å la poeng ligge.

— naturlig pausepunkt —

📝Oppgave 5

En kloss med masse m=0,500 kgm = 0{,}500\ \text{kg} ligger på et vannrett, friksjonsfritt bord festet til en fjær med k=12,5 N/mk = 12{,}5\ \text{N/m}. Ved t=0t = 0 står klossen i likevekt og får et slag som gir den farten 0,400 m/s0{,}400\ \text{m/s} mot høyre.

a) Sett opp bevegelseslikningen og akselerasjonslinja.

b) Skriv initialbetingelsene i kode, og forklar hver av dem fysisk.

c) Hvilken dtdt ville du valgt, og hvorfor? Hvor mange punkt trengs for å dekke fem svingeperioder?

d) Uten å kjøre koden: hva blir det største utslaget? Bruk energibevaring, og forklar hvorfor dette er et godt kontrolltall for en kode.

📝Oppgave 6
(Eksamensnivå, sjanger H — den numeriske deloppgaven.) En kule med masse mm er festet til en fjær med fjærkonstant kk og henger loddrett i lufta. I tillegg til tyngden og fjærkraften virker en lineær luftmotstand kvdy/dt-k_v\,dy/dt. Vi regner yy positivt oppover, med y=0y = 0 i fjærens avspente lengde.

a) Sett opp bevegelseslikningen, og skriv den på eksplisitt form med akselerasjonen isolert.

b) Skisser en Python-kode som løser likningen numerisk. Angi initialbetingelsene for en kule som slippes fra ro i y=0y = 0, og skriv Euler–Cromer-løkka.

c) Begrunn i to setninger hvorfor du velger Euler–Cromer framfor Forward Euler her.

d) Hvordan ville du kontrollert at koden er riktig, uten å kjenne den analytiske løsningen av den fulle likningen?

e) Hva blir likevektsposisjonen, og hva skjer med den hvis luftmotstanden dobles?

📝Oppgave 7

En student simulerer den udempede fjærpendelen fra eksempel 1 og lurer på hvor liten dtdt må være.

a) Kjøringene med Euler–Cromer gir disse avvikene fra den analytiske løsningen ved t=10,0 st = 10{,}0\ \text{s}: dt=0,05 sdt = 0{,}05\ \text{s} gir 4,3103 m4{,}3\cdot 10^{-3}\ \text{m}, dt=0,01 sdt = 0{,}01\ \text{s} gir 7,8104 m7{,}8\cdot 10^{-4}\ \text{m}, og dt=0,001 sdt = 0{,}001\ \text{s} gir 6,7105 m6{,}7\cdot 10^{-5}\ \text{m}. Hva slags sammenheng er dette, og hva forteller den?

b) Anslå hvilken dtdt som trengs for å komme under 106 m10^{-6}\ \text{m} ved t=10,0 st = 10{,}0\ \text{s}, og hvor mange steg det blir.

c) Studenten foreslår i stedet å kjøre med dt=0,2 sdt = 0{,}2\ \text{s} «siden det er tjue ganger mindre enn perioden». Vurder forslaget.

d) Hvorfor hjelper det ikke å redusere dtdt hvis akselerasjonslinja mangler et kraftledd?

📝Oppgave 8
(Kvalitativ.) En student skriver i en besvarelse: «Jeg valgte Euler–Cromer fordi den er mest nøyaktig.»

a) Er begrunnelsen god nok? Hva mangler?

b) Ville argumentet vært annerledes hvis oppgaven gjaldt et legeme i fritt fall uten luftmotstand, over ett sekund? Begrunn.

c) Nevn én situasjon der Forward Euler og Euler–Cromer gir praktisk talt samme svar, og forklar hvorfor.

Begrepsbank

Begrepsbanken er flashcard- og repetisjonsstoff — den gjentar det du nettopp har lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet gjelder kjernestoffet.

Initialbetingelser i koden

De to tallene som beskriver starttilstanden, skrevet inn i indeks 0 av tabellene:

x[0] = ...      # hvor legemet er ved t = 0
v[0] = ...      # hvor fort det gaar ved t = 0

De er verdt 1 poeng i en typisk kodedeloppgave, og de er det eneste stedet i koden der opplysningene fra oppgaveteksten om starttilstanden dukker opp.

Den vanligste feilen er å sette begge til null av vane. «Slippes fra ro i utslaget AA» gir x[0] = A, v[0] = 0; «passerer likevekt med farten v0v_0» gir x[0] = 0, v[0] = v0. Les setningen to ganger.

`range(N - 1)` og ikke `range(N)`
Løkka går til og med indeks N2N-2, fordi den skriver til indeks i+1i+1 — og den siste plassen som finnes, er N1N-1.

i=0,1,,N2i+1=1,2,,N1i = 0, 1, \dots, N-2 \quad \Longrightarrow \quad i+1 = 1, 2, \dots, N-1

Med range(N) ville siste gjennomløp prøvd å skrive til x[N], som ikke finnes, og programmet stopper. På papiret straffes det ikke, men det er et unødvendig signal om at løkka ikke er tenkt igjennom.

Huskeregelen: antall steg er alltid én mindre enn antall punkt. Med NN punkt går det N1N-1 mellomrom.

Eksplisitt metode

En metode der den nye tilstanden regnes ut direkte fra den gamle, uten at man må løse en likning underveis.

Både Euler–Cromer og Forward Euler er eksplisitte: høyresiden inneholder bare størrelser du allerede kjenner, og oppdateringen er ren innsetting.

Alternativet er implisitte metoder, der den nye tilstanden står på begge sider og må løses ut. De er mer stabile for enkelte problemer, men langt tyngre å skrive for hånd, og de er ikke pensum i dette emnet.

Tilstandsform i koden
Skrivemåten der én andreordens likning erstattes av to førsteordens, som er nøyaktig det de to oppdateringslinjene i løkka gjør:

dvdt=1mFv[i+1] = v[i] + a*dt\frac{dv}{dt} = \frac{1}{m}\sum F \quad \longrightarrow \quad \texttt{v[i+1] = v[i] + a*dt}

dxdt=vx[i+1] = x[i] + v[i+1]*dt\frac{dx}{dt} = v \quad \longrightarrow \quad \texttt{x[i+1] = x[i] + v[i+1]*dt}

Å se koblingen mellom de to formene er selve broa fra kap. 7.1 til dette kapitlet: hver førsteordens likning i tilstandsformen blir én oppdateringslinje.

Pseudokode

Kode skrevet for å bli lest av et menneske, ikke kjørt av en maskin: riktig struktur og riktige uttrykk, men uten krav om at hvert komma sitter.

På eksamen er dette formen som forventes. Syntaksfeil straffes ikke, alle programmeringsspråk godtas, og du skriver ikke et helt program — bare initialbetingelser og løkke.

Det som likevel må være presist, er indeksene og rekkefølgen: v[i+1] før x[i+1], og den oppdaterte farten i posisjonslinja. Der er pseudokode like streng som ekte kode.

Trunkeringsfeil per steg

Feilen som oppstår i ett enkelt tidssteg, fordi vi behandler akselerasjonen som konstant gjennom steget mens den i virkeligheten endrer seg.

Feilen kommer av at vi kutter Taylor-rekka for posisjonen etter førsteordensleddet: det neste leddet er 12adt2\tfrac12 a\,dt^2, og det er størrelsesordenen på feilen per steg.

Feilen per steg går altså som dt2dt^2, mens den samlede feilen etter t/dtt/dt steg går som dtdt. Det er nettopp derfor metodene kalles førsteordens, selv om enkeltsteget er andreordens nøyaktig.

Sammenhengen mellom NN, dtdt og sluttiden
De tre størrelsene er låst til hverandre:

tslutt=(N1)dtt_{\text{slutt}} = (N-1)\,dt

Velger du hvor lenge simuleringen skal vare og hvor fint du vil regne, er antall punkt bestemt. Vil du dekke fem svingeperioder à 1,26 s1{,}26\ \text{s} med dt=0,0010 sdt = 0{,}0010\ \text{s}, trenger du N=6284N = 6284.

Den praktiske konsekvensen er at finere oppløsning koster proporsjonalt med regnetid: halverer du dtdt, dobles antall steg for samme sluttid.

Stoppkriterium i løkka

Et krav om at løkka skal avsluttes når noe fysisk inntreffer, i stedet for etter et fast antall steg.

For et kast er det naturlige kriteriet at legemet treffer bakken:

if y[i+1] < 0:
    break

Alternativet er å velge NN stort nok og lete opp nedslaget i etterkant. Begge deler godtas.

Vær oppmerksom på at et stoppkriterium alltid treffer litt for sent: løkka oppdager først at yy er blitt negativ, altså i steget etter nedslaget. Trenger du nedslagspunktet nøyaktig, interpolerer du mellom de to siste punktene.

Enhetsdisiplin i koden

Regelen om at alle tall som mates inn i koden, skal være i SI-enheter — meter, kilogram, sekund — og at svaret dermed også kommer i SI.

Koden regner uten å vite noe om enheter. Blander du g\text{g} og kg\text{kg}, eller cm\text{cm} og m\text{m}, kommer det ingen feilmelding: du får et pent tall som er galt med en faktor tusen.

Det praktiske grepet er å skrive enheten som kommentar bak hver konstant når du setter opp koden. Det tar to sekunder og fanger den ene feilen ingen kontroll ellers oppdager.

Lagring kontra oppdatering

Skillet mellom størrelser som må lagres for hvert tidspunkt, og størrelser som bare brukes og kastes.

Lagres (egen array): posisjon, fart, tid — alt du vil kunne plotte, lete i eller lese av til slutt.

Kastes (vanlig variabel): akselerasjonen, farten regnet ut av komponentene, avstanden til sentrum. De regnes på nytt i hvert gjennomløp og trengs ikke etterpå.

Denne inndelingen forklarer hvorfor a skrives uten indeks mens v alltid har indeks — og det er verdt å si i en besvarelse, slik at den manglende indeksen på a ikke leses som slurv.

Metodebegrunnelsen som poenggiver

Kravet om at du skriver hvorfor du valgte Euler–Cromer, ikke bare at du gjorde det.

Sensorveiledningene i H2023, V2024 og V2025 trekker dette fram som et skille mot toppkarakter. En fullgod begrunnelse har to ledd: hva Euler–Cromer gjør (oppdaterer farten først og bruker den oppdaterte farten videre, slik at feilen kansellerer over en periode), og hva alternativet gjør galt (Forward Euler bruker den gamle farten og tilfører energi systematisk).

To setninger. De koster et halvt minutt og er blant de billigste poengene i hele settet.

Analytisk kontroll av et numerisk svar

Å regne ut én egenskap ved bevegelsen på en helt annen måte enn koden gjør, og sammenlikne.

De tre beste kontrollene i mekanikk er:

Energi. Er systemet konservativt, skal E=12mv2+UE = \tfrac12 mv^2 + U være konstant. Amplituder og maksfarter kan regnes ut av energibevaring uten å løse likningen.

Grensetilfelle. Slå av det vanskelige leddet og sammenlikn med den kjente løsningen.

Størrelsesorden. Er tallet fysisk rimelig? En pendel med sekundperiode, en ball som når tjue meter, en terminalfart på noen titalls meter per sekund.

Ingen av dem krever en maskin, og alle tre kan skrives inn i en håndskrevet besvarelse som en kontrollsetning.

Symbol- og formelliste
Repetisjonsoppgaver

Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.

Skolesaga er en uavhengig læringsressurs og er ikke tilknyttet eller godkjent av den aktuelle utdanningsinstitusjonen. Dette er ikke offisielt studiemateriell. Les mer.