dropletEquation.f90 Source File


This file depends on

sourcefile~~dropletequation.f90~~EfferentGraph sourcefile~dropletequation.f90 dropletEquation.f90 sourcefile~simplefile_reader.f90 simpleFile_reader.f90 sourcefile~dropletequation.f90->sourcefile~simplefile_reader.f90 sourcefile~vector.f90 vector.f90 sourcefile~dropletequation.f90->sourcefile~vector.f90

Files dependent on this one

sourcefile~~dropletequation.f90~~AfferentGraph sourcefile~dropletequation.f90 dropletEquation.f90 sourcefile~dropletgenerator.f90 dropletGenerator.f90 sourcefile~dropletgenerator.f90->sourcefile~dropletequation.f90 sourcefile~dropletmotionsimulation.f90 dropletMotionSimulation.f90 sourcefile~dropletmotionsimulation.f90->sourcefile~dropletequation.f90 sourcefile~dropletmotionsimulation.f90->sourcefile~dropletgenerator.f90 sourcefile~main.f90 MAIN.f90 sourcefile~main.f90->sourcefile~dropletmotionsimulation.f90

Contents

Source Code


Source Code

module dropletEquation_m
    !!飛沫の方程式モジュール
    implicit none
    private

    !>基礎変数クラス
    !>時間間隔や代表値を格納
    type, public :: BasicParameter
        private
        double precision dt !無次元時間間隔
        double precision L, U, Re

        ! integer, public, target :: timeStep = 0   !構造体の要素はtarget属性にできないぽい

        contains
        procedure :: repValue => representativeValue
        procedure TimeStep2RealTime
    end type

    double precision, parameter :: Rho = 1.205d0          ! 空気の密度[kg/m3]
    double precision, parameter :: Mu = 1.822d-5          ! 空気の粘性係数[Pa・sec]
    double precision, parameter :: Rho_d = 0.99822d3          ! 飛沫(水)の密度[kg/m3]
    double precision, parameter :: gamma = Rho / Rho_d      !密度比(空気密度 / 飛沫(水)密度)

    !>飛沫方程式ソルバクラス
    !>蒸発方程式や運動方程式を解くメソッドを保持
    type, public, extends(BasicParameter) :: DropletEquationSolver
        private
        double precision coeff_drdt
            !!半径変化率の無次元係数

        double precision G(3)
            !!無次元重力加速度

        real T, RH
        double precision minimumRadiusRatio
        double precision, allocatable :: minimumRadiusMatrix(:,:)

        contains
        procedure set_gravity_acceleration, set_dropletEnvironment, dropletEnvironment
        procedure set_coeff_drdt, set_minimumRadiusRatio
        procedure next_position, next_velocity
        procedure get_radiusLowerLimitRatio

        procedure :: evaporationEq => evaporationEquation
        procedure solve_motionEquation

    end type

    ! double precision :: Re_d_min = 1.d2

    public BasicParameter_, DropletEquationSolver_

    contains

    type(BasicParameter) function BasicParameter_(delta_t, L_represent, U_represent)
        double precision, intent(in) :: delta_t, L_represent, U_represent

        BasicParameter_%dt = delta_t
        BasicParameter_%L = L_represent
        BasicParameter_%U = U_represent

        print*, 'Delta_Time =', BasicParameter_%dt

        BasicParameter_%Re = BasicParameter_%U*BasicParameter_%L*Rho / Mu

    end function

    type(DropletEquationSolver) function DropletEquationSolver_(delta_t, L_represent, U_represent, &
                                                                direction_g, Temperature, RelativeHumidity)
        double precision, intent(in) :: delta_t, L_represent, U_represent
        double precision, intent(in) :: direction_g(3)
        real, intent(in) :: Temperature, RelativeHumidity

        DropletEquationSolver_%BasicParameter = BasicParameter_(delta_t, L_represent, U_represent)

        call DropletEquationSolver_%set_gravity_acceleration(direction_g)
        call DropletEquationSolver_%set_dropletEnvironment(Temperature, RelativeHumidity)

    end function

    subroutine set_gravity_acceleration(self, direction_g)
        use vector_m
        class(DropletEquationSolver) self
        double precision, intent(in) :: direction_g(3)
        double precision, parameter :: G_dim = 9.806650d0                          ! 重力加速度[m/s2]
        double precision norm

        norm = G_dim * self%L/(self%U*self%U)
        self%G(:) = norm * normalize_vector(direction_g(:))    !無次元重力加速度
        print*, 'Dimensionless Acceleration of Gravity :'
        print*, self%G(:)

    end subroutine

    subroutine set_dropletEnvironment(self, Temperature, RelativeHumidity)
        class(DropletEquationSolver) self
        real, intent(in) :: Temperature, RelativeHumidity

        self%T = Temperature
        self%RH = RelativeHumidity

        call self%set_coeff_drdt()          !温湿度依存の係数の設定
        call self%set_minimumRadiusRatio()

    end subroutine

    real function dropletEnvironment(self, name)
        class(DropletEquationSolver) self
        character(*), intent(in) :: name

        select case(name)
            case('Temperature')
                dropletEnvironment = self%T
            case('RelativeHumidity')
                dropletEnvironment = self%RH
            case default
                dropletEnvironment = -1.e20
        end select

    end function

    subroutine set_coeff_drdt(self)
        class(DropletEquationSolver) self
        !=====================================================================================
        double precision Es, TK
        double precision, parameter :: Rv = 461.51d0                           ! 水蒸気の気体定数[J/(kg.K)]
        double precision, parameter :: T0 = 273.15d0                               ! [K]
        double precision, parameter :: D = 0.2564d-4           ! 水蒸気の拡散定数[m2/s]
        double precision, parameter :: Lv = 2.451d6                  ! 水の蒸発潜熱[J/kg]
        double precision, parameter :: Es0 = 6.11d2                  ! 基準温度における飽和蒸気圧[Pa]
        !=====================================================================================  
        TK = dble(self%T) + T0                                    ! 室温を絶対温度[K]に変換
        Es = Es0*exp((Lv/Rv)*(1.0d0/T0 - 1.0d0/TK))       ! 室温における飽和蒸気圧

        self%coeff_drdt = -D/(self%U*self%L) * (1.0d0 - dble(self%RH)/100.d0)*Es / (Rho_d*Rv*TK) ! dr/dt の無次元係数
        print*, 'coeff_drdt=', self%coeff_drdt

    end subroutine

    subroutine set_minimumRadiusRatio(self)
        !!湿度から、飛沫の下限半径と初期半径との比を求める。
        use simpleFile_reader
        class(DropletEquationSolver) self
        integer i, i_max

        call read_CSV('data/minimum_radius.csv', self%minimumRadiusMatrix)
        
        i_max = size(self%minimumRadiusMatrix, dim=2)
        i = 1
        do while(self%minimumRadiusMatrix(1,i) < self%RH)
            i = i + 1
            if(i == i_max) exit
        end do
        self%minimumRadiusRatio = self%minimumRadiusMatrix(2,i)

        print*, 'Dmin/D0 =', self%minimumRadiusRatio, self%RH

    end subroutine

    double precision function get_radiusLowerLimitRatio(self)
        !!下限半径と初期半径との比を返す
        class(DropletEquationSolver), intent(in) :: self

        ! if(RH < 64) then
        !     minimum_radius(:) = initial_radius(:)*0.19d0
        ! else if(RH < 90) then
        !     minimum_radius(:) = initial_radius(:)*(0.073*exp(0.014*dble(RH)))
        ! else if(RH == 90) then
        !     minimum_radius(:) = initial_radius(:)*0.28d0
        ! else if(RH < 100) then
        !     minimum_radius(:) = initial_radius(:)*(0.0001*exp(0.0869*dble(RH)))
        ! else
        !     minimum_radius(:) = initial_radius(:)
        ! end if

        get_radiusLowerLimitRatio = self%minimumRadiusRatio

    end function

    function evaporationEquation(self, radius) result(dr)
        !!蒸発方程式。半径変化量を返す。
        class(DropletEquationSolver) self
        double precision, intent(in) :: radius
        double precision drdt1,dr1, drdt2,dr2, r_approxi, dr

        !========= 飛沫半径の変化の計算 (2次精度ルンゲクッタ(ホイン)) ===========================
    
        drdt1 = self%coeff_drdt / radius
        dr1 = drdt1 * self%dt

        r_approxi = radius + dr1

        drdt2 = self%coeff_drdt / r_approxi
        dr2 = drdt2 * self%dt

        dr = (dr1 + dr2)*0.5d0

    end function

    subroutine solve_motionEquation(self, X, V, Va, R)
        !!運動方程式を解く。
        !!引数の値が上書きされる。
        class(DropletEquationSolver) self
        double precision, intent(inout) :: X(3), V(3)
        double precision, intent(in) :: Va(3), R
        double precision V_now(3)

        V_now(:) = V(:)

        V(:) = self%next_velocity(V_now(:), Va(:), R)

        X(:) = self%next_position(X(:), V_now(:), V(:))
    
    end subroutine

    function next_velocity(self, vel_d, vel_a, radius_d) result(vel_d_next)
        !!次時刻における速度を計算
        class(DropletEquationSolver) self
        double precision, intent(in) :: vel_d(3), vel_a(3), radius_d
        double precision speed_r, Re_d, CD, C, vel_d_next(3)

        if(radius_d <= 0.d0) then
            print*, '**ZeroRadius ERROR**', radius_d
            error stop
        end if

        speed_r = norm2(vel_a(:) - vel_d(:))    !相対速度の大きさ
        Re_d = (speed_r * 2.0d0*radius_d) * self%Re

        CD = DragCoefficient(Re_d) !抗力係数

        C = (3.0d0*CD*gamma*speed_r)/(8.0d0*radius_d)

        vel_d_next(:) = ( vel_d(:) + ( self%G(:) + C*vel_a(:) )* self%dt ) / ( 1.0d0 + C*self%dt )

    end function

    function next_position(self, x1, v1, v2) result(x2)
        !!次時刻における位置を返す
        class(DropletEquationSolver) self
        double precision, intent(in) :: x1(3), v1(3), v2(3)
        double precision x2(3)

        x2(:) = x1(:) + (v1(:) + v2(:))* 0.5d0 * self%dt

    end function

    double precision function DragCoefficient(Re_d)
        !!球体の抗力係数を返す
        double precision, intent(in) :: Re_d
            !!球体周りのレイノルズ数
        double precision Re_d_

        ! Re_d_min = min(Re_d_min, Re_d)
        Re_d_ = Re_d + 1.d-16
        !ゼロ割回避のため、小さな値を足す
        !この値は経験則でしかないが、球体周りのレイノルズ数の最小値はだいたい10のマイナス9乗

        DragCoefficient = (24.d0/Re_d_)*(1.d0 + 0.15d0*(Re_d_**0.687d0))

    end function
    
    ! double precision function survival_rate(step)
    !     integer, intent(in) :: step
    !     double precision time

    !     !このへんはインフルエンザのデータ(現在不使用)
    !     ! if(RH == 80)then  ! 相対湿度80%の時使用
    !     !     survival_rate = 0.67d0*0.5102d0**(((L/U)*dt*dble(step-1))/3600.0d0)
    !     ! else if(RH == 50)then  ! 相対湿度50%の時使用
    !     !     survival_rate = 0.84d0*0.5735d0**(((L/U)*dt*dble(step-1))/3600.0d0)
    !     ! else if(RH == 35)then  ! 相対湿度35%の時使用
    !     !     survival_rate = 0.86d0*0.9240d0**(((L/U)*dt*dble(step-1))/3600.0d0)
    !     ! end if

    !     time = TimeOnSimu(step, dimension=.true.)
    !     !新型コロナウイルス(1.1時間で半減)
    !     survival_rate = 0.999825d0**(time)
    ! end function

    ! elemental double precision function virusDeadline(self, deathParameter)
    !     class(DropletEquationSolver), intent(in) :: self
    !     double precision, intent(in) :: deathParameter
    !     double precision, parameter :: halfLife = 3960.d0   !半減期 1.1 h ( = 3960 sec)
    !     double precision, parameter :: alpha = log(2.d0) / halfLife

    !     virusDeadline = - log(deathParameter) / alpha
    !     virusDeadline = virusDeadline * self%U/self%L !無次元化

    ! end function

    double precision function representativeValue(self, name)
        class(BasicParameter) self
        character(*), intent(in) :: name

        ! if(L*U <= 0.d0) print*, '**WARNING** ZeroRepresentativeValue :', L, U   !代表値がゼロなら警告

        select case(name)
            case('length')
                representativeValue = self%L
            case('speed')
                representativeValue = self%U
            case('time')
                representativeValue = self%L / self%U

            case default
                print*, 'RepresentativeValueERROR : ', name
                error stop

        end select

        ! print*, 'Re_d_min=', Re_d_min

    end function

    double precision function TimeStep2RealTime(self, step, dimension)
        class(BasicParameter) self
        integer, intent(in) :: step
        logical, intent(in) :: dimension

        TimeStep2RealTime = step * self%dt
        
        if(dimension) TimeStep2RealTime = TimeStep2RealTime * self%repValue('time')

    end function

    ! double precision function deltaTime()

    !     deltaTime = dt
        
    ! end function

end module dropletEquation_m