Skip to content

Commit cf0dc96

Browse files
committed
TL: finalized interpolation features
1 parent bb47a2b commit cf0dc96

File tree

3 files changed

+132
-95
lines changed

3 files changed

+132
-95
lines changed
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
*.pdf
22
demos/*.png
3-
problems/test*
3+
problems/test_*
4+
problems/run_*

pySDC/playgrounds/dedalus/problems/rbc.py

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ def runSimulation(cls, dirName, tEnd, baseDt, tBeg=0, logEvery=100,
223223
if float(tEnd-tBeg) != round(nSteps*dt, ndigits=3):
224224
raise ValueError(f"{tEnd=} is not divisible by timestep {dt=} ({nSteps=})")
225225
nSteps = int(nSteps)
226-
p.infos.update(dt=dt, nSteps=nSteps)
226+
p.infos.update(tEnd=tEnd, dt=dt, nSteps=nSteps)
227227

228228
if os.path.isfile(f"{dirName}/01_finalized.txt"):
229229
if MPI_RANK == 0:
@@ -366,6 +366,8 @@ def printSpaceDistr(self):
366366

367367

368368
if __name__ == "__main__":
369+
import scipy.optimize as sco
370+
369371
from pySDC.playgrounds.dedalus.problems.utils import OutputFiles
370372
from qmat.lagrange import LagrangeApproximation
371373
import matplotlib.pyplot as plt
@@ -379,20 +381,26 @@ def printSpaceDistr(self):
379381
output = OutputFiles(dirName)
380382
approx = LagrangeApproximation(output.z)
381383

382-
uAll = output.vData(0)[:]
383-
bAll = output.bData(0)[:]
384-
u, w = uAll[:, 0], uAll[:, 1]
384+
nThrow = 40
385+
u = output.vData(0)[nThrow:]
386+
b = output.bData(0)[nThrow:]
387+
ux, uz = u[:, 0], u[:, 1]
385388
nX, nZ = output.nX, output.nZ
386389

387-
# Kinetic energy
390+
# RMS quantities
388391
mIz = approx.getIntegrationMatrix([(0, 1)])
389-
ke = (u-u.mean(axis=(-1, -2))[:, None, None])**2 \
390-
+ (w-w.mean(axis=(-1, -2))[:, None, None])**2
392+
uRMS = (ux**2 + uz**2).mean(axis=(0, 1))**0.5
393+
bRMS = ((b - b.mean(axis=(0, 1)))**2).mean(axis=(0, 1))**0.5
394+
395+
396+
plt.figure("z-profile")
397+
xOptU = sco.minimize_scalar(lambda z: -approx(z, fValues=uRMS), bounds=[0, 0.5])
398+
xOptB = sco.minimize_scalar(lambda z: -approx(z, fValues=bRMS), bounds=[0, 0.5])
391399

392-
nThrow = 20
393-
keProfile = ke[nThrow:].mean(axis=(0, 1))
394-
plt.figure("keProfile")
395-
plt.plot(keProfile, output.z, label=dirName)
400+
plt.plot(uRMS, output.z, label=f"uRMS[{nX=},{nZ=}]")
401+
plt.hlines(xOptU.x, uRMS.min(), uRMS.max(), linestyles="--", colors="black")
402+
plt.plot(bRMS, output.z, label=f"bRMS[{nX=},{nZ=}]")
403+
plt.hlines(xOptB.x, bRMS.min(), bRMS.max(), linestyles="--", colors="black")
396404
plt.legend()
397405

398406

@@ -441,28 +449,28 @@ def printSpaceDistr(self):
441449
# plt.loglog(wavenumbers, spectrum)
442450

443451

444-
# 1D spectrum (average)
445-
spectrum1D = []
446-
for i in range(2):
447-
u = uAll[:, i] # (nT, Nx, Nz)
448-
s = np.fft.rfft(u, axis=-2) # over Nx --> (nT, Kx, Nz)
449-
s *= np.conj(s) # (nT, Kx, Nz)
450-
s = np.sum(mIz*s.real, axis=-1) # integrate over Nz --> (nT, Kx)
451-
s = s[40:].mean(axis=0) # mean over nT -> (Kx,)
452-
s /= nX**2
452+
# # 1D spectrum (average)
453+
# spectrum1D = []
454+
# for i in range(2):
455+
# u = uAll[:, i] # (nT, Nx, Nz)
456+
# s = np.fft.rfft(u, axis=-2) # over Nx --> (nT, Kx, Nz)
457+
# s *= np.conj(s) # (nT, Kx, Nz)
458+
# s = np.sum(mIz*s.real, axis=-1) # integrate over Nz --> (nT, Kx)
459+
# s = s[40:].mean(axis=0) # mean over nT -> (Kx,)
460+
# s /= nX**2
453461

454-
spectrum1D.append(s)
455-
waveNum = np.fft.rfftfreq(nX, 1/nX)+0.5
462+
# spectrum1D.append(s)
463+
# waveNum = np.fft.rfftfreq(nX, 1/nX)+0.5
456464

457-
plt.figure("spectrum-1D")
458-
plt.loglog(waveNum, spectrum1D[0], label="ux")
459-
plt.loglog(waveNum, spectrum1D[1], label="uz")
460-
plt.loglog(waveNum, waveNum**(-5/3), '--k')
461-
plt.legend()
465+
# plt.figure("spectrum-1D")
466+
# plt.loglog(waveNum, spectrum1D[0], label="ux")
467+
# plt.loglog(waveNum, spectrum1D[1], label="uz")
468+
# plt.loglog(waveNum, waveNum**(-5/3), '--k')
469+
# plt.legend()
462470

463-
print(f"iS[ux] : {float(spectrum1D[0].sum())}")
464-
print(f"iS[uz] : {float(spectrum1D[1].sum())}")
471+
# print(f"iS[ux] : {float(spectrum1D[0].sum())}")
472+
# print(f"iS[uz] : {float(spectrum1D[1].sum())}")
465473

466474
plt.figure(f"contour-{dirName}")
467-
plt.pcolormesh(output.x, output.z, ke[-1].T)
475+
plt.pcolormesh(output.x, output.z, b[-1].T)
468476
plt.colorbar()

pySDC/playgrounds/dedalus/problems/utils.py

Lines changed: 92 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,49 @@ def rbc3dInterpolation(coarseFields):
137137

138138
return fineFields
139139

140+
141+
def rbc2dInterpolation(coarseFields):
142+
"""
143+
Interpolate a RBC2D field to twice its space resolution
144+
145+
Parameters
146+
----------
147+
coarseFields : np.3darray
148+
The fields values on the coarse grid, with shape [nV,nX,nZ].
149+
The last dimension (z) uses a chebychev grid,
150+
while x is uniform periodic.
151+
152+
Returns
153+
-------
154+
fineFields : np.4darray
155+
The interpolated fields, with shape [nV,2*nX,2*nZ]
156+
"""
157+
coarseFields = np.asarray(coarseFields)
158+
assert coarseFields.ndim == 3, "requires 3D array"
159+
160+
nV, nX, nZ = coarseFields.shape
161+
162+
# Chebychev grids and interpolation matrix for z
163+
zC = NodesGenerator("CHEBY-1", "GAUSS").getNodes(nZ)
164+
zF = NodesGenerator("CHEBY-1", "GAUSS").getNodes(2*nZ)
165+
Pz = LagrangeApproximation(zC, weightComputation="STABLE").getInterpolationMatrix(zF)
166+
167+
# Fourier interpolation in x
168+
print(" -- computing 1D FFT ...")
169+
uFFT = np.fft.fftshift(np.fft.fft(coarseFields, axis=1), axes=1)
170+
print(" -- padding in Fourier space ...")
171+
uPadded = np.zeros_like(uFFT, shape=(nV, 2*nX, nZ))
172+
uPadded[:, nX//2:-nX//2] = uFFT
173+
print(" -- computing 1D IFFT ...")
174+
uXY = np.fft.ifft(np.fft.ifftshift(uPadded, axes=1), axis=1).real*2
175+
176+
# Polynomial interpolation in z
177+
print(" -- interpolating in z direction ...")
178+
fineFields = (Pz @ uXY.reshape(-1, nZ).T).T.reshape(nV, 2*nX, 2*nZ)
179+
180+
return fineFields
181+
182+
140183
def decomposeRange(iBeg, iEnd, step, maxSize):
141184
if iEnd is None:
142185
raise ValueError("need to provide iEnd for range decomposition")
@@ -218,7 +261,7 @@ def shape(self):
218261
if self.dim == 2:
219262
return (4, self.nX, self.nZ)
220263
elif self.dim == 3:
221-
return (4, self.nX, self.nY, self.nZ)
264+
return (5, self.nX, self.nY, self.nZ)
222265

223266
@property
224267
def k(self):
@@ -442,27 +485,66 @@ def toVTR(self, idxFormat="{:06d}"):
442485
writeToVTR(template.format(i), u, coords, varNames)
443486

444487

445-
def interpolate(self, iFile:int, fileName:str, iField:int=-1):
446-
assert self.dim == 3, "interpolation only possible for 3D RBC fields"
488+
def toPySDC(self, iFile:int, fileName:str, iField:int=-1, interpolate=False):
447489

448490
fields = self.file(iFile)["tasks"]
449491

450492
velocity = fields["velocity"][iField]
451493
buoyancy = fields["buoyancy"][iField]
452494
pressure = fields["pressure"][iField]
453495

454-
uCoarse = np.concat([velocity, buoyancy[None, ...], pressure[None, ...]])
455-
uFine = rbc3dInterpolation(uCoarse)
496+
uAll = np.concat([velocity, buoyancy[None, ...], pressure[None, ...]])
497+
498+
if self.dim == 3:
499+
if interpolate:
500+
uAll = rbc3dInterpolation(uAll)
501+
nX, nY, nZ = uAll.shape[1:]
502+
xCoord = np.linspace(0, 1, nX, endpoint=False)
503+
yCoord = np.linspace(0, 1, nY, endpoint=False)
504+
zCoord = NodesGenerator("CHEBY-1", "GAUSS").getNodes(nZ)
505+
header = (5, [xCoord, yCoord, zCoord])
506+
507+
elif self.dim == 2:
508+
if interpolate:
509+
uAll = rbc2dInterpolation(uAll)
510+
nX, nZ = uAll.shape[1:]
511+
xCoord = np.linspace(0, 1, nX, endpoint=False)
512+
zCoord = NodesGenerator("CHEBY-1", "GAUSS").getNodes(nZ)
513+
header = (4, [xCoord, zCoord])
514+
else:
515+
raise NotImplementedError(f"dim={self.dim}")
516+
517+
output = Rectilinear(np.float64, fileName)
518+
output.setHeader(*header)
519+
output.initialize()
520+
output.addField(0, uAll)
521+
522+
523+
def interpolateRBCFile(fileName, outputFile, idxField=-1):
524+
525+
inpt:Rectilinear = Rectilinear.fromFile(fileName)
526+
_, uC = inpt.readField(idxField)
456527

457-
nX, nY, nZ = uFine.shape[1:]
528+
if inpt.dim == 2:
529+
uF = rbc2dInterpolation(uC)
530+
nX, nZ = uF.shape[1:]
531+
xCoord = np.linspace(0, 1, nX, endpoint=False)
532+
zCoord = NodesGenerator("CHEBY-1", "GAUSS").getNodes(nZ)
533+
header = (4, [xCoord, zCoord])
534+
elif inpt.dim == 3:
535+
uF = rbc3dInterpolation(uC)
536+
nX, nY, nZ = uF.shape[1:]
458537
xCoord = np.linspace(0, 1, nX, endpoint=False)
459538
yCoord = np.linspace(0, 1, nY, endpoint=False)
460539
zCoord = NodesGenerator("CHEBY-1", "GAUSS").getNodes(nZ)
540+
header = (5, [xCoord, yCoord, zCoord])
541+
else:
542+
raise NotImplementedError(f"dim={inpt.dim}")
461543

462-
output = Rectilinear(np.float64, fileName)
463-
output.setHeader(5, [xCoord, yCoord, zCoord])
464-
output.initialize()
465-
output.addField(0, uFine)
544+
output = Rectilinear(np.float64, outputFile)
545+
output.setHeader(*header)
546+
output.initialize()
547+
output.addField(0, uF)
466548

467549

468550
def checkDNS(sMean:np.ndarray, k:np.ndarray, vRatio:int=4, nThrow:int=1):
@@ -495,60 +577,6 @@ def fun(coeffs):
495577

496578
return status, [a, b, c], x, y, nValues
497579

498-
def generateChunkPairs(folder:str, N:int, M:int,
499-
tStep:int=1, xStep:int=1, zStep:int=1,
500-
shuffleSeed=None
501-
):
502-
"""
503-
Function to generate chunk pairs
504-
505-
Args:
506-
folder (str): path to dedalus hdf5 data
507-
N (int): timesteps of dt
508-
M (int): size of chunk
509-
tStep (int, optional): time slicing. Defaults to 1.
510-
xStep (int, optional): x-grid slicing. Defaults to 1.
511-
zStep (int, optional): z-grid slicing. Defaults to 1.
512-
shuffleSeed (int, optional): seed for random shuffle. Defaults to None.
513-
514-
Returns:
515-
pairs (list): chunk pairs
516-
"""
517-
out = OutputFiles(folder)
518-
519-
pairs = []
520-
vxData, vzData, bData, pData = [], [], [], []
521-
for iFile in range(0, out.nFiles):
522-
vxData.append(out.vData(iFile)[:, 0, ::xStep, ::zStep])
523-
vzData.append(out.vData(iFile)[:, 1, ::xStep, ::zStep])
524-
bData.append(out.bData(iFile)[:, ::xStep, ::zStep])
525-
pData.append(out.pData(iFile)[:, ::xStep, ::zStep])
526-
# stack all arrays
527-
vxData = np.concatenate(vxData)
528-
vzData = np.concatenate(vzData)
529-
bData = np.concatenate(bData)
530-
pData = np.concatenate(pData)
531-
532-
assert vxData.shape[0] == vzData.shape[0]
533-
assert vzData.shape[0] == pData.shape[0]
534-
assert vzData.shape[0] == bData.shape[0]
535-
nTimes = vxData.shape[0]
536-
537-
for i in range(0, nTimes-M-N+1, tStep):
538-
chunk1 = np.stack((vxData[i:i+M],vzData[i:i+M],bData[i:i+M],pData[i:i+M]), axis=1)
539-
chunk2 = np.stack((vxData[i+N:i+N+M],vzData[i+N:i+N+M], bData[i+N:i+N+M],
540-
pData[i+N:i+N+M]), axis=1)
541-
# chunks are shape (M, 4, Nx//xStep, Nz//zStep)
542-
assert chunk1.shape == chunk2.shape
543-
pairs.append((chunk1, chunk2))
544-
545-
# shuffle if a seed is given
546-
if shuffleSeed is not None:
547-
random.seed(shuffleSeed)
548-
random.shuffle(pairs)
549-
550-
return pairs
551-
552580

553581
def contourPlot(field, x, y, time=None,
554582
title=None, refField=None, refTitle=None, saveFig=False,

0 commit comments

Comments
 (0)