Example #1
0
    def filter_kernel(self,
                      kf,
                      Gtype='spectral',
                      k_kf=None,
                      dtype=np.complex128):
        """
        kf    - input cutoff wavenumber for ensured isotropic filtering
        Gtype - (Default='spectral') filter kernel type
        k_kf  - (Default=None) spectral-space wavenumber field pre-
                normalized by filter cutoff wavenumber. Pass this into
                FILTER_KERNEL for anisotropic filtering since this
                function generates isotropic filter kernels by default.
                If not None, kf is ignored.
        """

        if k_kf is None:
            A = self.L / self.L.min()  # domain size aspect ratios
            A.resize((3, 1, 1, 1))  # ensure proper array broadcasting
            kmag = np.sqrt(np.sum(np.square(self.K / A), axis=0))
            k_kf = kmag / kf

        Ghat = np.empty(k_kf.shape, dtype=dtype)

        if Gtype == 'spectral':
            Ghat[:] = (np.abs(k_kf) < 1.0).astype(dtype)

        elif Gtype == 'tophat':
            Ghat[:] = np.sin(pi * k_kf) / (pi * k_kf**2)

        elif Gtype == 'comp_exp':
            # A Compact Exponential filter that:
            #   1) has compact support in _both_ physical and spectral space
            #   2) is strictly positive in _both_ spaces
            #   3) is smooth (infinitely differentiable) in _both_ spaces
            #   4) has simply-connected support in spectral space with
            #      an outer radius kf, and
            #   5) has disconnected (lobed) support in physical space
            #      with an outer radius of 2*pi/kf
            with np.errstate(divide='ignore'):
                Ghat[:] = np.exp(-k_kf**2 / (0.25 - k_kf**2),
                                 where=k_kf < 0.5,
                                 out=np.zeros_like(k_kf)).astype(dtype)

            G = irfft3(self.comm, Ghat)
            G[:] = np.square(G)
            rfft3(self.comm, G, Ghat)
            Ghat *= 1.0 / self.comm.allreduce(Ghat[0, 0, 0], op=MPI.MAX)
            Ghat -= 1j * np.imag(Ghat)

        elif Gtype == 'inv_comp_exp':
            # Same as 'comp_exp' but the physical-space and
            # spectral-space kernels are swapped so that the
            # physical-space support is a simply-connected ball
            raise ValueError('inv_comp_exp not yet implemented!')

        else:
            raise ValueError('did not understand filter type')

        return Ghat
Example #2
0
    def filter_kernel(self, kf, Gtype='spectral', k_kf=None,
                      dtype=np.complex128):
        """
        kf    - input cutoff wavenumber for ensured isotropic filtering
        Gtype - (Default='spectral') filter kernel type
        k_kf  - (Default=None) spectral-space wavenumber field pre-
                normalized by filter cutoff wavenumber. Pass this into
                FILTER_KERNEL for anisotropic filtering since this
                function generates isotropic filter kernels by default.
                If not None, kf is ignored.
        """

        if k_kf is None:
            A = self.L/self.L.min()  # domain size aspect ratios
            A.resize((3, 1, 1, 1))   # ensure proper array broadcasting
            kmag = np.sqrt(np.sum(np.square(self.K/A), axis=0))
            k_kf = kmag/kf

        Ghat = np.empty(k_kf.shape, dtype=dtype)

        if Gtype == 'spectral':
            Ghat[:] = (np.abs(k_kf) < 1.0).astype(dtype)

        elif Gtype == 'comp_exp':
            # A 'COMPact EXPonential' filter which:
            #   1) has compact support in a ball of spectral (physical)
            #       radius kf (1/kf)
            #   2) is strictly positive, and
            #   3) is smooth (infinitely differentiable)
            #      in _both_ physical and spectral space!
            with np.errstate(divide='ignore'):
                Ghat[:] = np.exp(-k_kf**2/(0.25-k_kf**2), where=k_kf < 0.5,
                                 out=np.zeros_like(k_kf)).astype(dtype)

            G = irfft3(self.comm, Ghat)
            G[:] = np.square(G)
            rfft3(self.comm, G, Ghat)
            Ghat *= 1.0/self.comm.allreduce(Ghat[0, 0, 0], op=MPI.MAX)
            Ghat -= 1j*np.imag(Ghat)

            # elif Gtype == 'inv_comp_exp':
            #     # Same as 'comp_exp' but the physical-space and spectral-
            #     # space kernels are swapped so that the physical-space kernel
            #     # has only a central lobe of support.
            #     H = np.exp(-r_rf**2/(1.0-r_rf**2))
            #     G = np.where(r_rf < 1.0, H, 0.0)
            #     rfft3(self.comm, G, Ghat)
            #     Ghat[:] = Ghat**2
            #     G[:] = irfft3(self.comm, Ghat)
            #     G /= self.comm.allreduce(psum(G), op=MPI.SUM)
            #     rfft3(self.comm, G, Ghat)

        elif Gtype == 'tophat':
            Ghat[:] = np.sin(pi*k_kf)/(pi*k_kf**2)

        else:
            raise ValueError('did not understand filter type')

        return Ghat
Example #3
0
    def computeSource_ales244_SGS(self, H_244, **ignored):
        """
        H_244 - ALES coefficients h_ij for 244-term Volterra series
                truncation. H_244.shape = (6, 244)
        """
        tau_hat = self.tau_hat
        UU_hat = self.UU_hat

        irfft3(self.comm, self.les_filter * self.U_hat[0], self.W[0])
        irfft3(self.comm, self.les_filter * self.U_hat[1], self.W[1])
        irfft3(self.comm, self.les_filter * self.U_hat[2], self.W[2])

        m = 0
        for j in range(3):
            for i in range(j, 3):
                rfft3(self.comm, self.W[i] * self.W[j], UU_hat[m])
                m += 1

        # loop over 6 stress tensor components
        for m in range(6):
            tau_hat[5 - m] = H_244[m, 0]  # constant coefficient

            # loop over 27 stencil points
            n = 1
            for z in range(-1, 2):
                for y in range(-1, 2):
                    for x in range(-1, 2):
                        # compute stencil shift operator.
                        # NOTE: dx = 2*pi/N for standard incompressible HIT
                        # but really shift theorem needs 2*pi/N, not dx
                        pos = np.array([z, y, x]) * self.dx
                        pos.resize((3, 1, 1, 1))
                        shift = np.exp(1j * np.sum(self.K * pos, axis=0))

                        # 3 ui Volterra series components
                        for i in range(2, 0, -1):
                            tau_hat[5 -
                                    m] += H_244[m, n] * shift * self.U_hat[i]
                            n += 1

                        # 6 uiuj collocated Volterra series components
                        for p in range(6):
                            tau_hat[5 -
                                    m] += H_244[m, n] * shift * UU_hat[5 - p]
                            n += 1

        self.W_hat[:] = 0.0
        m = 0
        for j in range(3):
            for i in range(j, 3):
                self.W_hat[i] += 1j * (1 + (i != j)) * self.K[j] * tau_hat[m]
                m += 1

        self.dU += self.W_hat

        return
Example #4
0
    def computeSource_Smagorinsky_SGS(self, C1=-6.39e-2, **ignored):
        """
        SPARSE SYMMETRIC TENSOR INDEXING:
        m == 0 -> ij == 00
        m == 1 -> ij == 01
        m == 2 -> ij == 02
        m == 3 -> ij == 11
        m == 4 -> ij == 12
        m == 5 -> ij == 22
        """

        # --------------------------------------------------------------
        # Explicitly filter the solution field
        self.W_hat[:] = self.les_filter*self.U_hat

        # --------------------------------------------------------------
        # Compute S_ij and |S|^2
        S_sqr = self.W[0]
        S_sqr[:] = 0.0
        m = 0
        for i in range(3):
            for j in range(i, 3):
                self.Aij[:] = 0.5j*self.K[j]*self.W_hat[i]

                if i == j:
                    self.S[m] = irfft3(self.comm, 2*self.Aij)
                    S_sqr += self.S[m]**2

                else:
                    self.Aji[:] = 0.5j*self.K[i]*self.W_hat[j]
                    self.S[m] = irfft3(self.comm, self.Aij + self.Aji)
                    S_sqr += 2*self.S[m]**2

                m+=1

        # --------------------------------------------------------------
        # Compute C_1 Delta^2 |S| S_ij
        coef = self.W[1]
        coef[:] = C1*self.D_les**2*np.sqrt(2.0*S_sqr)

        # --------------------------------------------------------------
        # Compute FFT{div(tau)} and add to RHS update
        m = 0
        for i in range(3):
            for j in range(i, 3):
                rfft3(self.comm, coef*self.S[m], self.tau_ij_hat)

                self.dU[i] -= 1j*self.K[j]*self.tau_ij_hat
                if i != j:
                    self.dU[j] -= 1j*self.K[i]*self.tau_ij_hat

                m+=1

        return
Example #5
0
    def computeSource_Smagorinsky_SGS(self, Cs=1.2, **ignored):
        """
        Smagorinsky Model (takes Cs as input)

        Takes one keyword argument:
        Cs: (float, optional), Smagorinsky constant
        """

        # --------------------------------------------------------------
        # Explicitly filter the solution field
        self.W_hat[:] = self.les_filter * self.U_hat

        for i in range(3):
            for j in range(3):
                self.S[i, j] = irfft3(
                    self.comm, 1j * self.K[j] * self.W_hat[i] +
                    1j * self.K[i] * self.W_hat[j])

        # --------------------------------------------------------------
        # compute the leading coefficient, nu_T = 2|S|(Cs*D)**2
        nuT = self.W[0]
        nuT[:] = np.sqrt(np.sum(np.square(self.S), axis=(0, 1)))
        nuT *= (Cs * self.D_les)**2

        # --------------------------------------------------------------
        # Compute FFT{div(tau)} and add to RHS update
        self.W_hat[:] = 0.0
        for i in range(3):
            for j in range(3):
                self.W_hat[i] += 1j * self.K[j] * rfft3(
                    self.comm, nuT * self.S[i, j])

        self.dU += self.W_hat

        return
Example #6
0
    def computeSource_Smagorinksy_SGS(self, Cs=1.2, **ignored):
        """
        Smagorinsky Model (takes Cs as input)

        Takes one keyword argument:
        Cs: (float, optional), Smagorinsky constant
        """
        self.W_hat[:] = self.les_filter*self.U_hat
        for i in range(3):
            for j in range(3):
                self.A[j, i] = 0.5*irfft3(self.comm,
                                          1j*(self.K[j]*self.W_hat[i]
                                              +self.K[i]*self.W_hat[j]))

        # compute SGS flux tensor, nuT = 2|S|(Cs*D)**2
        nuT = self.W[0]
        nuT = np.sqrt(2.0*np.sum(np.square(self.A), axis=(0, 1)))
        nuT*= 2.0*(Cs*self.D_les)**2

        self.W_hat[:] = 0.0
        for i in range(3):
            for j in range(3):
                self.W_hat[i]+= 1j*self.K[j]*rfft3(self.comm, self.A[j, i]*nuT)

        self.dU += self.W_hat

        return
Example #7
0
    def compute_pressure(self):
        K = self.K
        U_hat = self.U_hat
        U = self.U
        omega = self.omega
        comm = self.comm
        # P_hat   = np.empty_like(U_hat)  # this is a vector field!
        # P       = np.empty_like(U)  # this is a vector field!

        # --------------------------------------------------------------
        # take curl of velocity and inverse transform to get vorticity
        irfft3(comm, 1j * (K[1] * U_hat[2] - K[2] * U_hat[1]), omega[0])
        irfft3(comm, 1j * (K[2] * U_hat[0] - K[0] * U_hat[2]), omega[1])
        irfft3(comm, 1j * (K[0] * U_hat[1] - K[1] * U_hat[0]), omega[2])

        # --------------------------------------------------------------
        # compute the convective transport as the physical-space
        # cross-product of vorticity and velocity
        rfft3(comm, U[1] * omega[2] - U[2] * omega[1], self.dU[0])
        rfft3(comm, U[2] * omega[0] - U[0] * omega[2], self.dU[1])
        rfft3(comm, U[0] * omega[1] - U[1] * omega[0], self.dU[2])

        P_hat = -1j * np.sum(
            self.dU * self.K_Ksq, axis=0
        )  # this doesn't fill P_hat, it points the name "P_hat" to a new scalar field array!
        # irfft3(comm, P_hat, P) this is the wrong array size (see above)!

        return irfft3(comm, P_hat, self.Pres)
Example #8
0
    def computeAD_vorticity_form(self, **ignored):
        """
        Computes right-hand-side (RHS) advection and diffusion term of
        the incompressible Navier-Stokes equations using a vorticity
        formulation for the advection term.

        This function overwrites the previous contents of self.dU.
        """
        K = self.K
        U_hat = self.U_hat
        U = self.U
        omega = self.omega
        comm = self.comm

        # --------------------------------------------------------------#
        # take curl of velocity and inverse transform to get vorticity  #
        # --------------------------------------------------------------#
        irfft3(comm, 1j * (K[1] * U_hat[2] - K[2] * U_hat[1]), omega[0])
        irfft3(comm, 1j * (K[2] * U_hat[0] - K[0] * U_hat[2]), omega[1])
        irfft3(comm, 1j * (K[0] * U_hat[1] - K[1] * U_hat[0]), omega[2])

        # --------------------------------------------------------------
        # compute the convective transport as the physical-space
        # cross-product of vorticity and velocity
        rfft3(comm, U[1] * omega[2] - U[2] * omega[1], self.dU[0])
        rfft3(comm, U[2] * omega[0] - U[0] * omega[2], self.dU[1])
        rfft3(comm, U[0] * omega[1] - U[1] * omega[0], self.dU[2])

        # --------------------------------------------------------------
        # add the diffusive transport term
        self.dU -= self.nu * self.Ksq * self.U_hat

        return
Example #9
0
    def initialize_HIT_random_spectrum(self, Einit=None, kexp=-5./6.,
                                       kpeak=None, rseed=None):
        """
        Generates a random, incompressible, velocity initial condition
        with a scaled Gamie-Ostriker isotropic turbulence spectrum
        """
        if Einit is None:
            Einit = 0.72*(self.epsilon*self.L.max())**(2./3.)
            # the constant of 0.72 is empirically-based
        if kpeak is None:
            a = self.L/self.L.min()         # domain size aspect ratios
            kpeak = np.max((self.nx//8)/a)  # this gives kmax/4

        self.compute_random_HIT_spectrum(kexp, kpeak, rseed)

        # Solenoidally-project, U_hat*(1-ki*kj/k^2)
        self.W_hat -= np.sum(self.W_hat*self.K_Ksq, axis=0)*self.K

        # - Third, scale to Einit
        irfft3(self.comm, self.W_hat[0], self.U[0])
        irfft3(self.comm, self.W_hat[1], self.U[1])
        irfft3(self.comm, self.W_hat[2], self.U[2])

        Urms = sqrt(2.0*Einit)
        self.U *= Urms*sqrt(self.Nx/self.comm.allreduce(psum(self.U**2)))

        # transform to finish initial conditions
        rfft3(self.comm, self.U[0], self.U_hat[0])
        rfft3(self.comm, self.U[1], self.U_hat[1])
        rfft3(self.comm, self.U[2], self.U_hat[2])

        return
    def compute_pressure(self):
        K = self.K
        U_hat = self.U_hat
        U = self.U
        omega = self.omega
        comm = self.comm
        P_hat = np.empty_like(U_hat)
        P = np.empty_like(U)

        # --------------------------------------------------------------
        # take curl of velocity and inverse transform to get vorticity
        irfft3(comm, 1j * (K[1] * U_hat[2] - K[2] * U_hat[1]), omega[0])
        irfft3(comm, 1j * (K[2] * U_hat[0] - K[0] * U_hat[2]), omega[1])
        irfft3(comm, 1j * (K[0] * U_hat[1] - K[1] * U_hat[0]), omega[2])

        # --------------------------------------------------------------
        # compute the convective transport as the physical-space
        # cross-product of vorticity and velocity
        rfft3(comm, U[1] * omega[2] - U[2] * omega[1], self.dU[0])
        rfft3(comm, U[2] * omega[0] - U[0] * omega[2], self.dU[1])
        rfft3(comm, U[0] * omega[1] - U[1] * omega[0], self.dU[2])

        P_hat = -1j * np.sum(self.dU * self.K_Ksq, axis=0)
        irfft3(comm, P_hat, P)

        return P
Example #11
0
    def initialize_Taylor_Green_vortex(self):
        """
        Generates the Taylor-Green vortex velocity initial condition
        """
        self.U[0] = np.sin(self.X[0])*np.cos(self.X[1])*np.cos(self.X[2])
        self.U[1] =-np.cos(self.X[0])*np.sin(self.X[1])*np.cos(self.X[2])
        self.U[2] = 0.0
        rfft3(self.comm, self.U[0], self.U_hat[0])
        rfft3(self.comm, self.U[1], self.U_hat[1])
        rfft3(self.comm, self.U[2], self.U_hat[2])

        return
Example #12
0
    def computeSource_4termGEV_SGS(self, C=None, **ignored):
        """
        Empty Docstring!
        """

        # --------------------------------------------------------------
        # Explicitly filter the solution field
        self.W_hat[:] = self.les_filter*self.U_hat

        # --------------------------------------------------------------
        # Compute S_ij, R_ij, S_kl S_kl, R_kl R_kl, and |S|
        S = self.S
        R = self.R

        S_mod = self.W[0]
        S_sqr = self.W[1]
        R_sqr = self.W[2]

        S_sqr[:] = 0.0
        R_sqr[:] = 0.0
        for i in range(3):
            for j in range(3):
                self.Aij[:] = 0.5j*self.K[j]*self.W_hat[i]

                if i == j:
                    S[i, j] = irfft3(self.comm, 2*self.Aij)
                    R[i, j] = 0.0

                else:
                    self.Aji[:] = 0.5j*self.K[i]*self.W_hat[j]

                    S[i, j] = irfft3(self.comm, self.Aij + self.Aji)
                    R[i, j] = irfft3(self.comm, self.Aij - self.Aji)

                S_sqr += S[i, j]**2
                R_sqr += R[i, j]**2

        S_mod[:] = np.sqrt(2.0*S_sqr)

        # --------------------------------------------------------------
        # Compute tau_ij = Delta**2 C_m G_ij^m and update RHS
        for i in range(3):
            for j in range(3):

                # G_ij^1 = |S| S_ij
                self.tau_ij[:] = C[0]*S_mod*S[i, j]

                # G_ij^2 = -(S_ik R_jk + R_ik S_jk)
                self.tau_ij -= C[1]*np.sum(S[i]*R[j] + S[j]*R[i], axis=0)

                # G_ij^3 = S_ik S_jk - 1/3 delta_ij S_kl S_kl
                self.tau_ij += C[2]*np.sum(S[i]*S[j], axis=0)
                if i == j:
                    self.tau_ij -= C[2]*(1/3)*S_sqr

                # G_ij^4 = - R_ik R_jk - 1/3 delta_ij R_kl R_kl
                self.tau_ij -= C[3]*np.sum(R[i]*R[j], axis=0)
                if i == j:
                    self.tau_ij -= C[3]*(1/3)*R_sqr

                rfft3(self.comm, self.tau_ij, self.tau_ij_hat)
                self.dU[i] -= 1j*self.K[j]*self.tau_ij_hat

        return
Example #13
0
    def computeSource_ales244_SGS(self, H_244, **ignored):
        """
        h_ij Fortran column-major ordering:  11,12,13,22,23,33
        equivalent ordering for spectralLES: 22,21,20,11,10,00

        sparse tensor indexing for ales244_solver UU_hat and tau_hat:
        m == 0 -> ij == 22
        m == 1 -> ij == 21
        m == 2 -> ij == 20
        m == 3 -> ij == 11
        m == 4 -> ij == 10
        m == 5 -> ij == 00

        H_244 - ALES coefficients h_ij for 244-term Volterra series
                truncation. H_244.shape = (6, 244)
        """
        tau_hat = self.tau_hat
        UU_hat = self.UU_hat
        W_hat = self.W_hat

        W_hat[:] = self.les_filter * self.U_hat
        irfft3(self.comm, W_hat[0], self.W[0])
        irfft3(self.comm, W_hat[1], self.W[1])
        irfft3(self.comm, W_hat[2], self.W[2])

        m = 0
        for i in range(2, -1, -1):
            for j in range(i, -1, -1):
                rfft3(self.comm, self.W[i] * self.W[j], UU_hat[m])
                m += 1

        # loop over 6 stress tensor components
        for m in range(6):
            tau_hat[m] = H_244[m, 0]  # constant coefficient

            # loop over 27 stencil points
            n = 1
            for z in range(-1, 2):
                for y in range(-1, 2):
                    for x in range(-1, 2):
                        # compute stencil shift operator.
                        # NOTE: dx = 2*pi/N for standard incompressible HIT
                        # but really shift theorem needs 2*pi/N, not dx
                        pos = np.array([z, y, x]) * self.dx
                        pos.resize((3, 1, 1, 1))
                        shift = np.exp(1j * np.sum(self.K * pos, axis=0))

                        # 3 ui Volterra series components
                        for i in range(2, -1, -1):
                            tau_hat[m] += H_244[m, n] * shift * W_hat[i]
                            n += 1

                        # 6 uiuj collocated Volterra series components
                        for p in range(6):
                            tau_hat[m] += H_244[m, n] * shift * UU_hat[p]
                            n += 1

        m = 0
        for i in range(2, -1, -1):
            for j in range(i, -1, -1):
                self.dU[i] -= 1j * self.K[j] * tau_hat[m]
                if i != j:
                    self.dU[j] -= 1j * self.K[i] * tau_hat[m]

                m += 1

        return