diff --git a/src/flow_coupler.jl b/src/flow_coupler.jl index 9b7e1bb..e31affb 100644 --- a/src/flow_coupler.jl +++ b/src/flow_coupler.jl @@ -99,7 +99,7 @@ end vol_v = V*molar_volume(eos, p, Temp, f.vapor) vol_l = L*molar_volume(eos, p, Temp, f.liquid) S_v = vol_v/(vol_v + vol_l) - return S_v + return convert(T, S_v) end "Compute molar volume of a flashed phase" diff --git a/src/flow_coupler_types.jl b/src/flow_coupler_types.jl index 2816f47..ca88fb7 100644 --- a/src/flow_coupler_types.jl +++ b/src/flow_coupler_types.jl @@ -34,15 +34,20 @@ function Base.convert(::Type{FlashedPhase{T, Vector{T}}}, ph::FlashedPhase{K, Ve return FlashedPhase(mf, Z) end +function Base.convert(::Type{FlashedPhase{T, A}}, ph::FlashedPhase) where {T, A<:AbstractVector{T}} + fractions = convert(A, map(x -> convert(T, x), ph.mole_fractions)) + return FlashedPhase(fractions, convert(T, ph.Z)) +end + "Type that holds liquid and vapor phase states together with their state" -struct FlashedMixture2Phase{T, A<:AbstractVector{T}, E} +struct FlashedMixture2Phase{T, A<:AbstractVector{T}, E, R} state::PhaseState2Phase K::E # Equilibrium constants V::T # Vapor mole fraction liquid::FlashedPhase{T, A} vapor::FlashedPhase{T, A} - critical_distance::Float64 - flash_cond::@NamedTuple{p::Float64, T::Float64, z::E} + critical_distance::R + flash_cond::NamedTuple{(:p, :T, :z), Tuple{R, R, E}} flash_stability::StabilityReport function FlashedMixture2Phase(state::PhaseState2Phase, K::K_t, V::V_t, liquid::FlashedPhase{V_t, A}, vapor::FlashedPhase{V_t, A}; @@ -54,15 +59,18 @@ struct FlashedMixture2Phase{T, A<:AbstractVector{T}, E} # The phase vector type is part of the result type. Infer it from the # phases instead of using the keyword value as a type parameter. vec_type === A || throw(ArgumentError("vec_type must match the phase vectors")) + R = eltype(K_t) if ismissing(cond) z0 = convert(K_t, fill(NaN, length(K))) - cond = (p = NaN, T = NaN, z = z0) + cond = (p = R(NaN), T = R(NaN), z = z0) end - new{V_t, A, K_t}(state, K, V, liquid, vapor, critical_distance, cond, stability_report) + cond = (p = R(cond.p), T = R(cond.T), z = convert(K_t, cond.z)) + new{V_t, A, K_t, R}(state, K, V, liquid, vapor, + R(critical_distance), cond, stability_report) end end -function Base.convert(::Type{FlashedMixture2Phase{T, Vector{T}, F}}, mixture::FlashedMixture2Phase{K, Vector{K}, F}) where {T<:ForwardDiff.Dual, K, F} +function Base.convert(::Type{FlashedMixture2Phase{T, Vector{T}, F, R}}, mixture::FlashedMixture2Phase{K, Vector{K}, F}) where {T<:ForwardDiff.Dual, K, F, R} T_phase = FlashedPhase{T, Vector{T}} liquid = convert(T_phase, mixture.liquid) vapor = convert(T_phase, mixture.vapor) @@ -84,9 +92,31 @@ function Base.convert(::Type{FlashedMixture2Phase{T, Vector{T}, F}}, mixture::Fl return converted_mixture end +function Base.convert(::Type{FlashedMixture2Phase{T, A, E, R}}, + mixture::FlashedMixture2Phase) where {T, A<:AbstractVector{T}, E, R} + liquid = convert(FlashedPhase{T, A}, mixture.liquid) + vapor = convert(FlashedPhase{T, A}, mixture.vapor) + K = convert(E, mixture.K) + cond = mixture.flash_cond + flash_cond = (p = cond.p, T = cond.T, z = convert(E, cond.z)) + return FlashedMixture2Phase(mixture.state, K, convert(T, mixture.V), + liquid, vapor; + vec_type = A, + critical_distance = mixture.critical_distance, + cond = flash_cond, + stability_report = mixture.flash_stability) +end + +function Base.convert(::Type{FlashedMixture2Phase{T, A, E}}, + mixture::FlashedMixture2Phase) where {T, A<:AbstractVector{T}, E} + R = eltype(E) + return convert(FlashedMixture2Phase{T, A, E, R}, mixture) +end + function FlashedMixture2Phase(state, K, V, x, y, Z_L, Z_V, b = NaN, cond = missing, stability = StabilityReport()) - liquid = FlashedPhase(x, Z_L) - vapor = FlashedPhase(y, Z_V) + T = typeof(V) + liquid = FlashedPhase(map(a -> convert(T, a), x), convert(T, Z_L)) + vapor = FlashedPhase(map(a -> convert(T, a), y), convert(T, Z_V)) return FlashedMixture2Phase(state, K, V, liquid, vapor, vec_type = typeof(liquid.mole_fractions), critical_distance = b, cond = cond, stability_report = stability) @@ -95,7 +125,6 @@ end function FlashedMixture2Phase(eos::AbstractEOS, T = Float64, T_num = Float64, b = NaN, cond = missing, stability = StabilityReport()) n = number_of_components(eos) V = zero(T) - # K values are always doubles K = zeros(T_num, n) liquid = FlashedPhase(n, T) vapor = FlashedPhase(n, T) diff --git a/src/static.jl b/src/static.jl index 7bd8273..e66e329 100644 --- a/src/static.jl +++ b/src/static.jl @@ -69,13 +69,30 @@ end @inline immutable_flash_output(V, K, stability, ::Val{true}) = (V, K, stability) """Return an isbits representation of a mixture for accelerator kernels.""" -function static_mixture(mixture::MultiComponentMixture{R, N}) where {R, N} +function static_mixture(mixture::MultiComponentMixture{R_old, N}; float_type = missing) where {R_old, N} + if ismissing(float_type) + R = R_old + properties = mixture.properties + else + R = float_type + C(x) = convert(R, x) + properties = map( + x -> MolecularProperty( + C(x.mw), + C(x.p_c), + C(x.T_c), + C(x.V_c), + C(x.ω) + ), + mixture.properties + ) + end names = ntuple(_ -> nothing, Val(N)) bic = mixture.binary_interaction if !isnothing(bic) bic = SMatrix{N, N, R}(bic) end - return MultiComponentMixture(mixture.properties; A_ij = bic, names = names, name = nothing) + return MultiComponentMixture(properties; A_ij = bic, names = names, name = nothing) end """ @@ -83,11 +100,16 @@ end Convert a generic cubic EOS to an isbits representation for accelerator kernels. """ -function make_eos_immutable(eos::GenericCubicEOS{T, R, N}) where {T, R, N} +function make_eos_immutable(eos::GenericCubicEOS{T, R, N}; float_type = missing) where {T, R, N} mixture = static_mixture(eos.mixture) volume_shift = eos.volume_shift if !isnothing(volume_shift) - volume_shift = SVector{N, eltype(volume_shift)}(volume_shift) + if ismissing(float_type) + v_type = eltype(volume_shift) + else + v_type = float_type + end + volume_shift = SVector{N, v_type}(volume_shift) end return GenericCubicEOS( eos.type, @@ -135,8 +157,8 @@ end return (A_ij = A_ij_static, A_i = A_i_static, B_i = B_i_static) end -function make_eos_immutable(eos::KValuesEOS{T, R, N}) where {T, R, N} - mixture = static_mixture(eos.mixture) +function make_eos_immutable(eos::KValuesEOS{T, R, N}; float_type = missing) where {T, R, N} + mixture = static_mixture(eos.mixture; float_type = float_type) evaluator = eos.K_values_evaluator if evaluator isa AbstractVector evaluator = SVector{N, eltype(evaluator)}(evaluator) diff --git a/src/utils.jl b/src/utils.jl index d9aeaf1..18c87d1 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -52,7 +52,7 @@ function lbc_viscosity(eos, p, temperature, ph::FlashedPhase{T}; coeff = (0.1023 # Final expression is compound and given in centi poise. We convert to Pa s # instead for strict SI outputs. mu = 1e-3*(mu_atm + mu_correction) - return mu::T + return convert(T, mu) end function atmospheric_mu_estimate(props, z::V, temperature) where V<:AbstractVector{T} where T diff --git a/test/runtests.jl b/test/runtests.jl index b7c4334..4fdcd41 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -219,6 +219,44 @@ end @test phase_data(flashed, Val(:vapor)).mole_fractions === y end +@testset "Float32 flashed mixture storage" begin + x = SVector(0.8f0, 0.2f0) + y = SVector(0.1f0, 0.9f0) + K = SVector(0.125, 4.5) + flashed = FlashedMixture2Phase( + MultiComponentFlash.two_phase_lv, K, 0.4f0, + x, y, 0.9, 1.1) + @test flashed.V isa Float32 + @test flashed.liquid.Z isa Float32 + @test eltype(flashed.liquid.mole_fractions) === Float32 + + widened = FlashedMixture2Phase( + MultiComponentFlash.two_phase_lv, K, 0.4, + SVector(0.8, 0.2), SVector(0.1, 0.9), 0.9, 1.1) + @test isbitstype(typeof(widened)) + target = typeof(flashed) + converted = convert(target, widened) + @test converted.V isa Float32 + @test converted.liquid.mole_fractions == x + @test isequal(converted.flash_cond.z, widened.flash_cond.z) + + narrow_K = SVector(0.125f0, 4.5f0) + narrow = FlashedMixture2Phase( + MultiComponentFlash.two_phase_lv, narrow_K, 0.4f0, + x, y, 0.9f0, 1.1f0, Float32(NaN), + (p = 1.0f6, T = 300.0f0, z = narrow_K)) + @test narrow.critical_distance isa Float32 + @test isbitstype(typeof(narrow)) + @test narrow.flash_cond.p isa Float32 + @test narrow.flash_cond.T isa Float32 + @test eltype(narrow.flash_cond.z) === Float32 + converted_narrow = convert(typeof(narrow), widened) + @test converted_narrow.K == narrow_K + @test converted_narrow.flash_cond.p isa Float32 + partial_target = FlashedMixture2Phase{Float32, typeof(x), typeof(narrow_K)} + @test convert(partial_target, widened) isa typeof(narrow) +end + using ForwardDiff @testset "Static flashed mixture promotion" begin x = @SVector [0.8, 0.2]