Hi,
Hereâs a rough Python translation of Laurentâs C# code:
def modulo(n, size):
if n < 0:
return n + size
elif n >= size:
return n % size
else:
return n
n_points = _n * _n
A = list(_A)
B = list(_B)
Aprime = [0.0 for _ in range(n_points)]
Bprime = [0.0 for _ in range(n_points)]
A_stacked = []
B_stacked = []
for i_p in range(n_points):
A_stacked.append(1.0)
B_stacked.append(1.0)
for i in xrange(_n_step):
for j in xrange(_n_jump):
for i_x in xrange(_n):
for i_y in xrange(_n):
# Lacplacian calculation using convolution matrix
dxA = 0.0
dxB = 0.0
dxA = dxA + 0.05 * A[modulo(i_x - 1 + (i_y - 1) * _n, n_points)]
dxB = dxB + 0.05 * B[modulo(i_x - 1 + (i_y - 1) * _n, n_points)]
dxA = dxA + 0.20 * A[modulo(i_x - 0 + (i_y - 1) * _n, n_points)]
dxB = dxB + 0.20 * B[modulo(i_x - 0 + (i_y - 1) * _n, n_points)]
dxA = dxA + 0.05 * A[modulo(i_x + 1 + (i_y - 1) * _n, n_points)]
dxB = dxB + 0.05 * B[modulo(i_x + 1 + (i_y - 1) * _n, n_points)]
dxA = dxA + 0.2 * A[modulo(i_x - 1 + (i_y ) * _n, n_points)]
dxB = dxB + 0.2 * B[modulo(i_x - 1 + (i_y ) * _n, n_points)]
dxA = dxA - 1.0 * A[modulo(i_x - 0 + (i_y ) * _n, n_points)]
dxB = dxB - 1.0 * B[modulo(i_x - 0 + (i_y ) * _n, n_points)]
dxA = dxA + 0.2 * A[modulo(i_x + 1 + (i_y ) * _n, n_points)]
dxB = dxB + 0.2 * B[modulo(i_x + 1 + (i_y ) * _n, n_points)]
dxA = dxA + 0.05 * A[modulo(i_x - 1 + (i_y + 1) * _n, n_points)]
dxB = dxB + 0.05 * B[modulo(i_x - 1 + (i_y + 1) * _n, n_points)]
dxA = dxA + 0.20 * A[modulo(i_x - 0 + (i_y + 1) * _n, n_points)]
dxB = dxB + 0.20 * B[modulo(i_x - 0 + (i_y + 1) * _n, n_points)]
dxA = dxA + 0.05 * A[modulo(i_x + 1 + (i_y + 1) * _n, n_points)]
dxB = dxB + 0.05 * B[modulo(i_x + 1 + (i_y + 1) * _n, n_points)]
# Reaction-diffusion equation
i_point = modulo(i_x + i_y * _n, n_points)
AB2 = A[i_point] * B[i_point] * B[i_point]
Aprime[i_point] = A[i_point] + (_Da * dxA - AB2 + _f * (1.0 - A[i_point])) * _dt
Bprime[i_point] = B[i_point] + (_Db[i_point] * dxB + AB2 - (_k + _f) * B[i_point]) * _dt
for i_p in xrange(n_points):
A[i_p] = min(max(Aprime[i_p], 0.0), 1.0);
B[i_p] = min(max(Bprime[i_p], 0.0), 1.0);
if j == 0:
for i_p in xrange(n_points):
A_stacked.append(A[i_p])
B_stacked.append(B[i_p])
for i_p in xrange(n_points):
A_stacked.append(1.0)
B_stacked.append(1.0)
Aout = A
Bout = B
Astack = A_stacked
Bstack = B_stacked
It was pretty straightforward to port, - even-though what happens is pretty cryptic to me - and it works, however the performance is inferior to the C# version.
This is due to C# being far more forgiving, meaning that you donât have to write super optimised code (no offence Laurent) in order for things to run rather smoothly. You see, C# is a semi-compiled language, which makes it by nature faster than Python.
Furthermore, Python is performance-wise pretty âbadlyâ implemented in Grasshopper, and Rhino probably isnât meant to deal with large amounts of information and calculations in the first place.
I came to this conclusion, because in comparison to other CG apps (i.e. Maya, Houdini) that I also write Python scripts for, the performance of Rhino pales. This doesnât mean that Rhino is an inferior application, quite the contrary, itâs probably just not what it is meant to do. And its price-performance-ratio is much better than these other applications!
Grasshopper just helped opening a window into the dark, bottomless pit that is parametric, avant-garde design. And in the innovation and novelty driven culture of today, it mostly canât keep up performance-wise with what it has participated in kickstarting in the first place.
Of course you can always write superior, optimised (Python) code, and things will run more smoothly (buy a faster computer, or learn C#, or even better C++*). Doing that is up to you now!
Thanks for sharing the script, Laurent!
*C++ is fully compiled, which means itâs even faster than C# and it isnât the baby of Micro$oft