Skip to content
Snippets Groups Projects
gas_dynamics_initials.py 7.44 KiB
Newer Older
# Copyright (C) 2008 Andreas Kloeckner
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.




from __future__ import division
Andreas Klöckner's avatar
Andreas Klöckner committed
from __future__ import absolute_import
import numpy
import numpy.linalg as la
Andreas Klöckner's avatar
Andreas Klöckner committed
from six.moves import range




class UniformMachFlow:
    def __init__(self, mach=0.1, p=1, rho=1, reynolds=100,
            gamma=1.4, prandtl=0.72, char_length=1, spec_gas_const=287.1,
            angle_of_attack=None, direction=None, gaussian_pulse_at=None,
            pulse_magnitude=0.1):
        """
        :param direction: is a vector indicating the direction of the
          flow. Only one of angle_of_attack and direction may be
          specified. Only the direction, not the magnitude, of
          direction is taken into account.

        :param angle_of_attack: if not None, specifies the angle of
          the flow along the Y axis, where the flow is
          directed along the X axis.
        """
        if angle_of_attack is not None and direction is not None:
            raise ValueError("Only one of angle_of_attack and "
                    "direction may be specified.")

        if angle_of_attack is None and direction is None:
            angle_of_attack = 0

        if direction is not None:
            self.direction = direction/la.norm(direction)
        else:
            self.direction = None

        self.mach = mach
        self.p = p
        self.rho = rho

        self.gamma = gamma
        self.prandtl = prandtl
        self.reynolds = reynolds
        self.length = char_length
        self.spec_gas_const = spec_gas_const

        self.angle_of_attack = angle_of_attack

        self.gaussian_pulse_at = gaussian_pulse_at
        self.pulse_magnitude = pulse_magnitude

        self.c = (self.gamma * p / rho)**0.5
        u = self.velocity = mach * self.c
        self.e = p / (self.gamma - 1) + rho / 2 * u**2

        if numpy.isinf(self.reynolds):
            self.mu = 0
        else:
            self.mu = u * self.length * rho / self.reynolds

    def direction_vector(self, dimensions):
        # this must be done here because dimensions is not known above
        if self.direction is None:
            assert self.angle_of_attack is not None
            direction = numpy.zeros(dimensions, dtype=numpy.float64)
            direction[0] = numpy.cos(
                    self.angle_of_attack / 180. * numpy.pi)
            direction[1] = numpy.sin(
                    self.angle_of_attack / 180. * numpy.pi)
            return direction
        else:
            return self.direction

    def __call__(self, t, x_vec):
        ones = numpy.ones_like(x_vec[0])
        rho_field = ones*self.rho

        if self.gaussian_pulse_at is not None:
            rel_to_pulse = [x_vec[i] - self.gaussian_pulse_at[i]
                    for i in range(len(x_vec))]
            rho_field +=  self.pulse_magnitude * self.rho * numpy.exp(
                - sum(rtp_i**2 for rtp_i in rel_to_pulse)/2)

        direction = self.direction_vector(x_vec.shape[0])

Andreas Klöckner's avatar
Andreas Klöckner committed
        from grudge.tools import make_obj_array
        u_field = make_obj_array([ones*self.velocity*dir_i
            for dir_i in direction])

Andreas Klöckner's avatar
Andreas Klöckner committed
        from grudge.tools import join_fields
        return join_fields(rho_field, self.e*ones, self.rho*u_field)

    def volume_interpolant(self, t, discr):
        return discr.convert_volume(
                        self(t, discr.nodes.T),
                        kind=discr.compute_kind,
                        dtype=discr.default_scalar_type)

    def boundary_interpolant(self, t, discr, tag):
        return discr.convert_boundary(
                        self(t, discr.get_boundary(tag).nodes.T),
                         tag=tag, kind=discr.compute_kind,
                         dtype=discr.default_scalar_type)

class Vortex:
    def __init__(self):
        self.beta = 5
        self.gamma = 1.4
        self.center = numpy.array([5, 0])
        self.velocity = numpy.array([1, 0])

        self.mu = 0
        self.prandtl = 0.72
        self.spec_gas_const = 287.1

    def __call__(self, t, x_vec):
        vortex_loc = self.center + t*self.velocity

        # coordinates relative to vortex center
        x_rel = x_vec[0] - vortex_loc[0]
        y_rel = x_vec[1] - vortex_loc[1]

        # Y.C. Zhou, G.W. Wei / Journal of Computational Physics 189 (2003) 159
        # also JSH/TW Nodal DG Methods, p. 209

        from math import pi
        r = numpy.sqrt(x_rel**2+y_rel**2)
        expterm = self.beta*numpy.exp(1-r**2)
        u = self.velocity[0] - expterm*y_rel/(2*pi)
        v = self.velocity[1] + expterm*x_rel/(2*pi)
        rho = (1-(self.gamma-1)/(16*self.gamma*pi**2)*expterm**2)**(1/(self.gamma-1))
        p = rho**self.gamma

        e = p/(self.gamma-1) + rho/2*(u**2+v**2)

Andreas Klöckner's avatar
Andreas Klöckner committed
        from grudge.tools import join_fields
        return join_fields(rho, e, rho*u, rho*v)

    def volume_interpolant(self, t, discr):
        return discr.convert_volume(
                        self(t, discr.nodes.T
                            .astype(discr.default_scalar_type)),
                        kind=discr.compute_kind)

    def boundary_interpolant(self, t, discr, tag):
        return discr.convert_boundary(
                        self(t, discr.get_boundary(tag).nodes.T
                            .astype(discr.default_scalar_type)),
                         tag=tag, kind=discr.compute_kind)







class Vortex:
    def __init__(self):
        self.beta = 5
        self.gamma = 1.4
        self.center = numpy.array([5, 0])
        self.velocity = numpy.array([1, 0])
        self.final_time = 0.5

        self.mu = 0
        self.prandtl = 0.72
        self.spec_gas_const = 287.1

    def __call__(self, t, x_vec):
        vortex_loc = self.center + t*self.velocity

        # coordinates relative to vortex center
        x_rel = x_vec[0] - vortex_loc[0]
        y_rel = x_vec[1] - vortex_loc[1]

        # Y.C. Zhou, G.W. Wei / Journal of Computational Physics 189 (2003) 159
        # also JSH/TW Nodal DG Methods, p. 209

        from math import pi
        r = numpy.sqrt(x_rel**2+y_rel**2)
        expterm = self.beta*numpy.exp(1-r**2)
        u = self.velocity[0] - expterm*y_rel/(2*pi)
        v = self.velocity[1] + expterm*x_rel/(2*pi)
        rho = (1-(self.gamma-1)/(16*self.gamma*pi**2)*expterm**2)**(1/(self.gamma-1))
        p = rho**self.gamma

        e = p/(self.gamma-1) + rho/2*(u**2+v**2)

Andreas Klöckner's avatar
Andreas Klöckner committed
        from grudge.tools import join_fields
        return join_fields(rho, e, rho*u, rho*v)

    def volume_interpolant(self, t, discr):
        return discr.convert_volume(
                        self(t, discr.nodes.T
                            .astype(discr.default_scalar_type)),
                        kind=discr.compute_kind)

    def boundary_interpolant(self, t, discr, tag):
        return discr.convert_boundary(
                        self(t, discr.get_boundary(tag).nodes.T
                            .astype(discr.default_scalar_type)),
                         tag=tag, kind=discr.compute_kind)