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, 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)
- esecuzione: da terminale, dopo la compilazione semplicemente
./nomefile
Vediamo ora il codice di questo caso di studio, con i commenti al codice (revisionato tramite IA, Qwen3.8-Max, Qwen AI). Si nota chiaramente la sintassi simile a Python (if, for, ...) e JavaScript per la dichiarazione delle variabili.
import times
import strformat
# Parametri Fisici e di Discretizzazione
let L = 20.0 # Dichiarato come float per evitare casting ripetuti
let T = 20.0
let D = 0.1
let dx = 0.1
let r = 0.05 # Fattore 'r' (BTCS è incondizionatamente stabile)
let dt = r / D * dx * dx
let Nx = int(L / dx)
let Nt = int(T / dt)
proc initialCondition(x: float): float =
# Spostata al centro per una diffusione simmetrica
if x >= 9.8 and x <= 10.2:
return 100.0
else:
return 0.0
proc diffusionBTCS() =
var u = newSeq[float](Nx)
var uNew = newSeq[float](Nx)
# Condizioni iniziali
for i in 0 ..< Nx:
u[i] = initialCondition(float(i) * dx)
let N = Nx - 2 # Dimensione del sistema interno (esclusi i bordi)
# 1. Inizializzazione Vettori per Thomas (O(N) in memoria invece di O(N^2))
var mainDiag = newSeq[float](N)
var upperDiag = newSeq[float](N)
var lowerDiag = newSeq[float](N)
for i in 0 ..< N:
mainDiag[i] = 1.0 + 2.0 * r
upperDiag[i] = -r
lowerDiag[i] = -r
# 2. PRE-CALCOLO: Fattorizzazione della Matrice (UNA SOLA VOLTA)
var c_prime = newSeq[float](N) # Diagonale modificata
var factors = newSeq[float](N) # Moltiplicatori
c_prime[0] = mainDiag[0]
for i in 1 ..< N:
factors[i] = lowerDiag[i] / c_prime[i-1]
c_prime[i] = mainDiag[i] - factors[i] * upperDiag[i-1]
# Pre-allocazione vettori di lavoro fuori dal loop
var b = newSeq[float](N)
var uInner = newSeq[float](N)
let startTime = cpuTime()
# 3. Loop Temporale (Altamente Ottimizzato)
for t in 0 ..< Nt:
# Aggiorna termini noti
for i in 0 ..< N:
b[i] = u[i+1]
# Forward sweep solo sul vettore b (la matrice è già fattorizzata)
for i in 1 ..< N:
b[i] -= factors[i] * b[i-1]
# Backward substitution
uInner[N-1] = b[N-1] / c_prime[N-1]
for i in countdown(N-2, 0):
uInner[i] = (b[i] - upperDiag[i] * uInner[i+1]) / c_prime[i]
# Aggiornamento e Condizioni al Contorno
uNew[0] = 0.0
uNew[Nx-1] = 0.0
for i in 0 ..< N:
uNew[i+1] = uInner[i]
# Scambio O(1) dei puntatori invece di copiare l'intero array
swap(u, uNew)
let endTime = cpuTime()
echo "Soluzione finale (Campionamento ogni 20 nodi):"
for i in countup(0, Nx-1, 20):
echo fmt"x = {float(i)*dx:.1f}, u = {u[i]:.4f}"
echo fmt"Tempo di esecuzione (Ottimizzato): {endTime - startTime:.6f} secondi"
# Esecuzione
diffusionBTCS()
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 (anche se è stato volutamente creato lato sicurezza, per evitare troncamenti "silenziosi" e dunque errori matematici di approssimazione, potenzialmente grandi). Per altri aspetti, nel complesso è un linguaggio di programmazione molto valido ed efficiente!
Bonus: con Nim si possono anche creare grafici, seguendo strade diverse: ad esempio installando il pacchetto gnuplot oppure tramite Python-Matplotlib, installando la libreria nimpy (codice Nim, la libreria Nimpy richiama Python+Matplotlib e consente quindi di scrivere lo stesso codice che avremmo scritto in linguaggio Python).