From 74180e5512b577da5d8203afcd422625c1829b34 Mon Sep 17 00:00:00 2001 From: David Bold <dave@ipp.mpg.de> Date: Wed, 16 Aug 2023 14:34:57 +0200 Subject: [PATCH 1/5] Add new FV stencil generation code --- zoidberg/stencil_dagp_fv.py | 228 ++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 zoidberg/stencil_dagp_fv.py diff --git a/zoidberg/stencil_dagp_fv.py b/zoidberg/stencil_dagp_fv.py new file mode 100644 index 0000000..1b8e56c --- /dev/null +++ b/zoidberg/stencil_dagp_fv.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python +# coding: utf-8 + +from boututils.datafile import DataFile as DF +import numpy as np +import sys + +# In[161]: + + +fn = "v13/W7X-conf0-8x36x24.emc3.inner:f.vessel:f.island:f.fci.nc" +fn = "v13/W7X-conf0-12x36x48.emc3.inner:f.vessel:f.island:f.fci.nc" +fn = "v13/W7X-conf0-20x36x96.emc3.inner:f.vessel:f.island:f.fci.nc" +fn = "v13/W7X-conf0-36x36x192.emc3.inner:f.vessel:f.island:f.v2.fci.nc" + + +def toCent(RZ): + RZc = RZ + np.roll(RZ, 1, axis=-1) + RZc = RZc[:, 1:] + RZc[:, :-1] + return RZc / 4 + + +def l2(x): + return np.sqrt(np.sum(x**2, axis=0)) + + +def doit(fn): + with DF(fn) as f: + RZ = [f[k] for k in "RZ"] + + RZ = np.array(RZ) + + cent = toCent(RZ) + + # In[150]: + + A = None + nx = 50 + spc = np.linspace(0, 0.5, nx, endpoint=True) + spc2 = np.linspace(0.5, 1, nx, endpoint=True) + + for xf, yf, i, j in ( + (spc2, 0.5, 1, 1), + (0.5, spc2[::-1], 1, 1), + (0.5, spc[::-1], 1, 0), + (spc2[::-1], 0.5, 1, 0), + (spc[::-1], 0.5, 0, 0), + (0.5, spc, 0, 0), + (spc, 0.5, 0, 1), + (0.5, spc2, 0, 1), + ): + dx = np.arange(2) + i + dy = np.arange(2) + j + + def slc(i): + i1 = -2 + i + return slice(i, i1 or None) + + xii = [ + [np.roll(RZ[:, slc(i)], -j + 1, axis=-1)[..., None] for i in dy] for j in dx + ] + xa, xb = xii + x00, x01 = xa + x10, x11 = xb + x = yf * (xf * x00 + (1 - xf) * x10) + (1 - yf) * (xf * x01 + (1 - xf) * x11) + y = x[1] + dy = (y[..., 1] - y[..., 0])[..., None] + xx = (x[0, ..., :-1] + x[0, ..., 1:]) / 2 + if A is None: + A = np.zeros(xx[..., 0].shape) + Ar = np.zeros(xx[..., 0].shape) + A -= np.sum(xx * dy, axis=-1) + Ar -= np.sum(0.5 * xx * xx * dy, axis=-1) + # plt.plot(x[0], x[1], "gx") + volume = Ar + A.shape, Ar.shape, RZ.shape + + def toCent2(RZ): + RZ = toCent(RZ) + RZ = np.concatenate((RZ, RZ[..., :1]), axis=-1) + return RZ + + # AreaXplus + nx = 8 + spc3 = np.linspace(0, 1, nx) + cent = toCent(RZ) + # pos goes in +z direction + pos = spc3 * np.roll(cent, -1, axis=-1)[..., None] + (1 - spc3) * cent[..., None] + # direction of edge, length ~ 1/nx + dR = pos[..., 0] - pos[..., 1] + dX = np.sqrt(np.sum(dR**2, axis=0)) + area = np.sum(dX[..., None] * (pos[0, ..., 1:] + pos[0, ..., :-1]) * 0.5, axis=-1) + assert area.shape[0] > 2 + # Vector in normal direction + assert dR.shape[0] == 2 + dRr = np.array((-dR[1], dR[0])) + dRr /= np.sqrt(np.sum(dRr**2, axis=0)) + dRr *= area + + # vector in derivative direction + dxR = RZ[:, 1:] - RZ[:, :-1] + print("Maximum radial grid distance:", np.max(l2(dxR))) + dzR = np.roll(RZ, -1, axis=-1) - np.roll(RZ, 1, axis=-1) + dzR = 0.5 * (dzR[:, 1:] + dzR[:, :-1]) + + # dxR /= (np.sum(dxR**2, axis=0)) + # dzR /= (np.sum(dzR**2, axis=0)) + dxzR = np.array((dxR, dzR)).transpose(2, 3, 4, 1, 0) + coefs = np.linalg.solve(dxzR, dRr.transpose(1, 2, 3, 0)) + + areaX = area + coefsX = coefs + + # In[154]: + + # AreaZplus + nx = 8 + spc3 = np.linspace(0, 1, nx) + pos = ( + spc3 * np.roll(cent, -1, axis=-1)[:, 1:, ..., None] + + (1 - spc3) * np.roll(cent, -1, axis=-1)[:, :-1][..., None] + ) + dR = pos[..., 0] - pos[..., 1] + dX = np.sqrt(np.sum(dR**2, axis=0)) + area = np.sum(dX[..., None] * (pos[0, ..., 1:] + pos[0, ..., :-1]) * 0.5, axis=-1) + # Vector in normal direction + dRr = np.array((dR[1], -dR[0])) + dRr /= np.sqrt(np.sum(dRr**2, axis=0)) + dRr *= area + # vector in derivative direction + dxR = RZ[:, 2:] - RZ[:, :-2] + dxR = 0.5 * (np.roll(dxR, -1, axis=-1) + dxR) + dzR = (np.roll(RZ, -1, axis=-1) - RZ)[:, 1:-1] + dxzR = np.array((dxR, dzR)).transpose(2, 3, 4, 1, 0) + coefs = np.linalg.solve(dxzR, dRr.transpose(1, 2, 3, 0)) + coefs.shape + + areaZ = area + coefsZ = coefs + + # In[155]: + + inp = np.sin(RZ[1]) + ana = -np.sin(RZ[1]) # + np.cos(RZ[0])/RZ[0] + if 0: + inp = RZ[1] ** 2 + ana = RZ[1] ** 0 + if 1: + inp = np.sin(RZ[0]) + ana = -np.sin(RZ[0]) + np.cos(RZ[0]) / RZ[0] + if 0: + inp = RZ[0] ** 3 + ana = 6 * np.sin(RZ[0]) + + # In[156]: + + def xp(ijk): + return (ijk[0] + 1, *ijk[1:]) + + def xm(ijk): + return (ijk[0] - 1, *ijk[1:]) + + def zp(ijk): + return _zp(*ijk) + + def _zp(i, j, k): + return (i, j, (k + 1) % zmax) + + def zm(i, j, k): + return (i, j, (k - 1) % zmax) + + result = np.zeros(inp.shape) + results = [np.zeros(inp.shape) for _ in range(4)] + # dx = + dz2 = np.roll(inp, -1, axis=-1) - np.roll(inp, 1, axis=-1) + i, j, k = inp.shape + zmax = k + dx = inp[1:] - inp[:-1] + dz = 0.5 * (dz2[1:] + dz2[:-1]) + this = -(coefsX[..., 0] * dx + coefsX[..., 1] * dz) + result[:-1] -= this + result[1:] += this + for r, t in zip(results, (coefsX[..., 0] * dx, coefsX[..., 1] * dz)): + r[:-1] -= t + r[1:] += t + results[0] + + if 1: + dx2 = inp[2:] - inp[:-2] + dx2 = 0.5 * (np.roll(dx2, -1, axis=-1) + dx2) + dz2 = (np.roll(inp, -1, axis=-1) - inp)[1:-1] + t1 = coefsZ[..., 0] * dx2 + t2 = coefsZ[..., 1] * dz2 + this = -(t1 + t2) + result[1:-1] -= this + result[1:-1] += np.roll(this, 1, -1) + for r, t in zip(results[2:], (t1, t2)): + r[1:-1] -= t + np.roll(r, -1, -1)[1:-1] += t + + result[0] = 0 + for r in results: + r[0] = 0 + r[-1] = 0 + + print(fn, "error:", np.mean(l2(result[1:-1] / volume - ana[1:-1]))) + + def fixup(d): + c1 = np.zeros(RZ[0].shape) + if c1.shape[0] == d.shape[0] + 1: + c1[1:] = d + else: + c1[1:-1] = d + return c1 + + with DF(fn, write=True) as f: + for d, x1 in zip((coefsX, coefsZ), "XZ"): + for i, x2 in enumerate("XZ"): + key = f"dagp_fv_{x1}{x2}" + f.write(key, fixup(d[..., i])) + key = "dagp_fv_volume" + f.write(key, fixup(volume)) + + +if __name__ == "__main__": + for fn in sys.argv[1:]: + print(fn) + doit(fn) From 021710b6dcb79bc29ee26d67efc96378df9b0301 Mon Sep 17 00:00:00 2001 From: David Bold <dave@ipp.mpg.de> Date: Thu, 30 May 2024 13:36:43 +0200 Subject: [PATCH 2/5] always run dagp --- zoidberg/stencil_dagp_fv.py | 143 +++++++++++++++++++++++++++++------- 1 file changed, 115 insertions(+), 28 deletions(-) mode change 100644 => 100755 zoidberg/stencil_dagp_fv.py diff --git a/zoidberg/stencil_dagp_fv.py b/zoidberg/stencil_dagp_fv.py old mode 100644 new mode 100755 index 1b8e56c..9459965 --- a/zoidberg/stencil_dagp_fv.py +++ b/zoidberg/stencil_dagp_fv.py @@ -1,17 +1,16 @@ #!/usr/bin/env python # coding: utf-8 +import time + +time_start = time.time() + from boututils.datafile import DataFile as DF import numpy as np import sys -# In[161]: - -fn = "v13/W7X-conf0-8x36x24.emc3.inner:f.vessel:f.island:f.fci.nc" -fn = "v13/W7X-conf0-12x36x48.emc3.inner:f.vessel:f.island:f.fci.nc" -fn = "v13/W7X-conf0-20x36x96.emc3.inner:f.vessel:f.island:f.fci.nc" -fn = "v13/W7X-conf0-36x36x192.emc3.inner:f.vessel:f.island:f.v2.fci.nc" +verbose = 1 def toCent(RZ): @@ -24,21 +23,41 @@ def l2(x): return np.sqrt(np.sum(x**2, axis=0)) +org_print = print + + +def my_print(*args): + args = f"{time.time() - time_start:10.3f} s: ", *args + org_print(*args) + + +def log(*args): + if verbose > 1: + my_print(*args) + + +def print(*args): + if verbose: + my_print(*args) + + def doit(fn): with DF(fn) as f: RZ = [f[k] for k in "RZ"] - + log("opened") RZ = np.array(RZ) - - cent = toCent(RZ) - - # In[150]: + log("read") A = None - nx = 50 + nx = 2 spc = np.linspace(0, 0.5, nx, endpoint=True) spc2 = np.linspace(0.5, 1, nx, endpoint=True) + ### Go in a line around the cell + # xf is the normalised coordinates in x + # yf is the normalised coordinates in z + # i and j are the offset in x and z + # i,j = 0 -> in x and z in lower direction for xf, yf, i, j in ( (spc2, 0.5, 1, 1), (0.5, spc2[::-1], 1, 1), @@ -49,6 +68,7 @@ def doit(fn): (spc, 0.5, 0, 1), (0.5, spc2, 0, 1), ): + log(f"doing offset ({i}, {j})") dx = np.arange(2) + i dy = np.arange(2) + j @@ -75,27 +95,56 @@ def slc(i): volume = Ar A.shape, Ar.shape, RZ.shape - def toCent2(RZ): - RZ = toCent(RZ) - RZ = np.concatenate((RZ, RZ[..., :1]), axis=-1) - return RZ - # AreaXplus - nx = 8 + # Z + # Λ + # | a b c + # | + # | ------------ + # | | X + # | | X + # | h | o X d + # | | X + # | | X + # | ------------ + # | + # | g f e + # | + # |------------------------> x + # + # We are calculating the area of the surface denoted by X + # Note that we need to split it in two parts. Maybe? + nx = 2 spc3 = np.linspace(0, 1, nx) - cent = toCent(RZ) # pos goes in +z direction - pos = spc3 * np.roll(cent, -1, axis=-1)[..., None] + (1 - spc3) * cent[..., None] + # First segments starts at the centre of b,c,d,o + # and goes to centre of o,d + startpoint = toCent(RZ) + midpoint = (RZ[:, :-1] + RZ[:, 1:]) / 2 + endpoint = np.roll(startpoint, -1, -1) + + log("calculating pos ...") + pos = np.concatenate( + [ + (1 - spc3[:-1]) * startpoint[..., None] + spc3[:-1] * midpoint[..., None], + (1 - spc3) * midpoint[..., None] + spc3 * endpoint[..., None], + ], + axis=-1, + ) + log("done") # direction of edge, length ~ 1/nx - dR = pos[..., 0] - pos[..., 1] + dR = pos[..., :-1] - pos[..., 1:] dX = np.sqrt(np.sum(dR**2, axis=0)) - area = np.sum(dX[..., None] * (pos[0, ..., 1:] + pos[0, ..., :-1]) * 0.5, axis=-1) + area = dX * (pos[0, ..., 1:] + pos[0, ..., :-1]) * 0.5 assert area.shape[0] > 2 # Vector in normal direction assert dR.shape[0] == 2 dRr = np.array((-dR[1], dR[0])) dRr /= np.sqrt(np.sum(dRr**2, axis=0)) + print(dRr.shape, area.shape) dRr *= area + dRr = np.sum(dRr, axis=-1) + print(np.nanmean(dRr)) # vector in derivative direction dxR = RZ[:, 1:] - RZ[:, :-1] @@ -105,8 +154,10 @@ def toCent2(RZ): # dxR /= (np.sum(dxR**2, axis=0)) # dzR /= (np.sum(dzR**2, axis=0)) + log("starting solve") dxzR = np.array((dxR, dzR)).transpose(2, 3, 4, 1, 0) coefs = np.linalg.solve(dxzR, dRr.transpose(1, 2, 3, 0)) + log("done") areaX = area coefsX = coefs @@ -114,25 +165,52 @@ def toCent2(RZ): # In[154]: # AreaZplus - nx = 8 + # Z + # Λ + # | a b c + # | + # | -XXXXXXXXXX- + # | | | + # | | | + # | h | o | d + # | | | + # | | | + # | ------------ + # | + # | g f e + # | + # |------------------------> x + nx = 3 spc3 = np.linspace(0, 1, nx) - pos = ( - spc3 * np.roll(cent, -1, axis=-1)[:, 1:, ..., None] - + (1 - spc3) * np.roll(cent, -1, axis=-1)[:, :-1][..., None] + cent = np.roll(toCent(RZ), -1, -1) + startpoint = cent[:, :-1] + midpoint = (RZ[:, 1:-1] + np.roll(RZ[:, 1:-1], -1, -1)) / 2 + endpoint = cent[:, 1:] + log("concatenate") + pos = np.concatenate( + [ + (1 - spc3[:-1]) * startpoint[..., None] + spc3[:-1] * midpoint[..., None], + (1 - spc3) * midpoint[..., None] + spc3 * endpoint[..., None], + ], + axis=-1, ) - dR = pos[..., 0] - pos[..., 1] + dR = pos[..., :-1] - pos[..., 1:] dX = np.sqrt(np.sum(dR**2, axis=0)) - area = np.sum(dX[..., None] * (pos[0, ..., 1:] + pos[0, ..., :-1]) * 0.5, axis=-1) + area = dX * (pos[0, ..., 1:] + pos[0, ..., :-1]) * 0.5 + log("get normal vector") # Vector in normal direction dRr = np.array((dR[1], -dR[0])) dRr /= np.sqrt(np.sum(dRr**2, axis=0)) dRr *= area + dRr = np.sum(dRr, axis=-1) # vector in derivative direction dxR = RZ[:, 2:] - RZ[:, :-2] dxR = 0.5 * (np.roll(dxR, -1, axis=-1) + dxR) dzR = (np.roll(RZ, -1, axis=-1) - RZ)[:, 1:-1] dxzR = np.array((dxR, dzR)).transpose(2, 3, 4, 1, 0) + log("solving again") coefs = np.linalg.solve(dxzR, dRr.transpose(1, 2, 3, 0)) + log("done") coefs.shape areaZ = area @@ -169,6 +247,7 @@ def _zp(i, j, k): def zm(i, j, k): return (i, j, (k - 1) % zmax) + log("testing") result = np.zeros(inp.shape) results = [np.zeros(inp.shape) for _ in range(4)] # dx = @@ -211,8 +290,10 @@ def fixup(d): c1[1:] = d else: c1[1:-1] = d + # c1 = np.roll(c1, -1, -1) return c1 + log("writing") with DF(fn, write=True) as f: for d, x1 in zip((coefsX, coefsZ), "XZ"): for i, x2 in enumerate("XZ"): @@ -220,9 +301,15 @@ def fixup(d): f.write(key, fixup(d[..., i])) key = "dagp_fv_volume" f.write(key, fixup(volume)) + log("done") if __name__ == "__main__": + for flag, step in (("-v", +1), ("-q", -1)): + while flag in sys.argv: + verbose += step + sys.argv.remove(flag) + for fn in sys.argv[1:]: print(fn) doit(fn) From 07f221d554e687b1a531c63de7f02d74b86613ec Mon Sep 17 00:00:00 2001 From: David Bold <dave@ipp.mpg.de> Date: Fri, 12 Jul 2024 14:59:19 +0200 Subject: [PATCH 3/5] dagp_fv: fix off-by-one bug --- zoidberg/stencil_dagp_fv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zoidberg/stencil_dagp_fv.py b/zoidberg/stencil_dagp_fv.py index 9459965..cc1e5ce 100755 --- a/zoidberg/stencil_dagp_fv.py +++ b/zoidberg/stencil_dagp_fv.py @@ -287,7 +287,7 @@ def zm(i, j, k): def fixup(d): c1 = np.zeros(RZ[0].shape) if c1.shape[0] == d.shape[0] + 1: - c1[1:] = d + c1[:-1] = d else: c1[1:-1] = d # c1 = np.roll(c1, -1, -1) From a2fee3c2c17558ab6432d5176e5e75f1dbd687ec Mon Sep 17 00:00:00 2001 From: David Bold <dave@ipp.mpg.de> Date: Tue, 16 Jul 2024 10:45:37 +0200 Subject: [PATCH 4/5] Add some comments --- zoidberg/stencil_dagp_fv.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/zoidberg/stencil_dagp_fv.py b/zoidberg/stencil_dagp_fv.py index cc1e5ce..d31cac5 100755 --- a/zoidberg/stencil_dagp_fv.py +++ b/zoidberg/stencil_dagp_fv.py @@ -53,6 +53,8 @@ def doit(fn): spc = np.linspace(0, 0.5, nx, endpoint=True) spc2 = np.linspace(0.5, 1, nx, endpoint=True) + ### Calculate Volume of the cell + # ### Go in a line around the cell # xf is the normalised coordinates in x # yf is the normalised coordinates in z @@ -183,9 +185,9 @@ def slc(i): nx = 3 spc3 = np.linspace(0, 1, nx) cent = np.roll(toCent(RZ), -1, -1) - startpoint = cent[:, :-1] - midpoint = (RZ[:, 1:-1] + np.roll(RZ[:, 1:-1], -1, -1)) / 2 - endpoint = cent[:, 1:] + startpoint = cent[:, :-1] # a + midpoint = (RZ[:, 1:-1] + np.roll(RZ[:, 1:-1], -1, -1)) / 2 # b + endpoint = cent[:, 1:] # c log("concatenate") pos = np.concatenate( [ From 2578ad97f4346688294d289a4635524b7b0bd976 Mon Sep 17 00:00:00 2001 From: David Bold <dave@ipp.mpg.de> Date: Tue, 16 Jul 2024 10:46:22 +0200 Subject: [PATCH 5/5] improvements for debugging --- zoidberg/stencil_dagp_fv.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/zoidberg/stencil_dagp_fv.py b/zoidberg/stencil_dagp_fv.py index d31cac5..26a33e9 100755 --- a/zoidberg/stencil_dagp_fv.py +++ b/zoidberg/stencil_dagp_fv.py @@ -261,11 +261,10 @@ def zm(i, j, k): this = -(coefsX[..., 0] * dx + coefsX[..., 1] * dz) result[:-1] -= this result[1:] += this - for r, t in zip(results, (coefsX[..., 0] * dx, coefsX[..., 1] * dz)): + for r, t in zip(results, (-coefsX[..., 0] * dx, -coefsX[..., 1] * dz)): r[:-1] -= t r[1:] += t - results[0] - + print("expect 0:", np.max(np.abs((result - results[0] - results[1])))) if 1: dx2 = inp[2:] - inp[:-2] dx2 = 0.5 * (np.roll(dx2, -1, axis=-1) + dx2) @@ -275,11 +274,13 @@ def zm(i, j, k): this = -(t1 + t2) result[1:-1] -= this result[1:-1] += np.roll(this, 1, -1) - for r, t in zip(results[2:], (t1, t2)): + for r, t in zip(results[2:], (-t1, -t2)): r[1:-1] -= t - np.roll(r, -1, -1)[1:-1] += t + # np.roll(r, -1, -1)[1:-1] += t + r[1:-1] += np.roll(t, 1, -1) result[0] = 0 + result[-1] = 0 for r in results: r[0] = 0 r[-1] = 0