Vediamo questo caso interessante, il linguaggio di programmazione Nim è piuttosto recente, general purpose, dalla sintassi leggera e pulita, diciamo un misto di Python e JavaScript. Ha il vantaggio di essere un linguaggio compilato con focus all'efficienza, le performance sono molto buone, quasi comparabili con i soliti leader C/C++. Il che, ripeto, è un risultato eccellente!
Il caso di studio riguarda l'algoritmo BTCS per sistema tridiagonale, un metodo numerico implicito per risolvere l'equazione della diffusione, caso 1D. Nota, ci concentriamo sull'implementazione, quindi l'aspetto matematico (analisi numerica) e informatico ovviamente (linguaggio Nim), l'equazione della diffusione dal punto di vista fisico potrebbe essere poi applicata come si preferisce (diffusione della temperatura, di una sostanza, ecc).
Per prima cosa, vediamo come installare Nim:
- da terminale,
git clone https://github.com/nim-lang/Nim
(nell'eventualità prima installiamo GIT tramite sudo apt install git
, se stiamo usando una distro Linux debian-based); è una delle possibili strade per installare Nim, io ho scelto questa
- nella directory di Nim eseguiamo
build_all.sh
per Unix (Linux, macOS) oppure build_all.bat
se stiamo usando Windows
- ora scriviamo il nostro codice sorgente, da salvare con estensione .nim
- compilazione: standard tramite comando da terminale
nim compile nomefile.nim
, oppure nim c -d:release nomefile.nim
attiva modalità di ottimizzazione della compilazione, prestazioni migliori (vedi un altro caso interessante, simile: Ottimizzazioni di compilazione C: benchmark prestazioni)
- esecuzione: da terminale, dopo la compilazione semplicemente
./nomefile
Vediamo ora il codice di questo caso di studio, con i commenti al codice. Nota, è impossibile non fare caso alla sintassi simile a Python (if, for, ...) e JavaScript per la dichiarazione delle variabili.
import sequtils, math
import times
let L = 20 # lunghezza totale
let T = 20 # tempo totale di simulazione
let D = 0.1 # coefficiente di diffusione termica
let dx=0.1 # passo discretizzazione spaziale
let delta = 0.05 # fattore di stabilità
let dt = delta/D*dx*dx # passo discretizzazione temporale
let Nx = int(L.float/dx) # Numero di divisioni spaziali
let Nt = int(T.float/dt) # Numero di divisioni temporali
# Funzione per le condizioni iniziali
proc initialCondition(x: float): float =
if x >= 0.4 and x <= 0.6:
return 100.0
else:
return 0.0
# Funzione per generare la matrice tridiagonale per il BTCS
proc generateMatrix(N: int, r: float): seq[seq[float]] =
result = newSeq[seq[float]](N)
for i in 0..N-1:
result[i] = newSeq[float](N)
for j in 0..N-1:
if i == j:
result[i][j] = 1 + 2 * delta
elif i == j - 1 or i == j + 1:
result[i][j] = -delta
else:
result[i][j] = 0.0
# Funzione per la risoluzione del sistema lineare Ax = b (eliminazione di Gauss)
proc solveTridiagonal(A: seq[seq[float]], b: seq[float]): seq[float] =
let N = b.len
var x = newSeq[float](N)
var A2 = A
var b2 = b
# Eliminazione in avanti
for i in 1..<N:
let factor = A2[i][i-1] / A2[i-1][i-1]
A2[i][i-1] = 0.0
A2[i][i] -= factor * A2[i-1][i]
b2[i] -= factor * b2[i-1]
# Sostituzione all'indietro
x[N-1] = b2[N-1] / A2[N-1][N-1]
for i in countdown(N-2, 0):
x[i] = (b2[i] - A2[i][i+1] * x[i+1]) / A2[i][i]
return x
# Funzione principale per la soluzione dell'equazione di diffusione
proc diffusionBTCS() =
var u = newSeq[float](Nx) # Soluzione al tempo corrente
var uNew = newSeq[float](Nx) # Soluzione al tempo successivo
# Condizioni iniziali
for i in 0..<Nx:
let x = i.float * dx
u[i] = initialCondition(x)
# Generare la matrice per il metodo BTCS
let A = generateMatrix(Nx-2, delta)
# Loop temporale
for t in 0..<Nt:
var b = newSeq[float](Nx-2)
# Imposta il vettore dei termini noti, esclusi i valori al bordo
for i in 0..<Nx-2:
b[i] = u[i+1]
# Risolvi il sistema lineare
let u_inner = solveTridiagonal(A, b)
# Aggiorna la soluzione
for i in 0..<Nx-2:
uNew[i+1] = u_inner[i]
# Condizioni al contorno (temperatura fissa ai bordi)
uNew[0] = 0.0
uNew[Nx-1] = 0.0
# Aggiorna u
u = uNew
# Stampa la soluzione finale
echo "Soluzione finale (temperatura lungo la barra):"
for i in 0..<Nx:
echo u[i]
let inizio = cpuTime()
# Esegui la simulazione
diffusionBTCS()
let fine = cpuTime()
echo "Tempo di esecuzione: ", fine - inizio, " secondi"
Un commento su questo linguaggio, non essendo io ancora pratico, occorre prestare attenzione ad esempio al caso di divisione fra Int e Float, che non viene gestita in automatico, occorre specificarlo (ad esempio scrivendo i.float * dx
oppure let Nt = int(T.float/dt)
). Quindi per chi non è pratico, potrebbe risultare un po' macchinoso. Per altri aspetti, nel complesso è un linguaggio di programmazione molto valido ed efficiente!
Il tempo di esecuzione dopo la compilazione standard nim compile nomefile.nim
è pari a 0,59 secondi; attivando la modalità di compilazione ottimizzata, quindi nim c -d:release nomefile.nim
, il tempo di esecuzione è pari a 0,14 secondi, quindi un miglioramento notevole (4,2 volte più veloce!). Specialmente non avendo pratica di questo linguaggio, attivare la modalità di compilazione ottimizzata, specie per problemi che richiedono un certo sforzo computazionale, è conveniente.