Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Buckingham PI implementation with DynamicQuantities #3443

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 210 additions & 0 deletions src/systems/buckinghum.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
using ModelingToolkit
using DynamicQuantities;
using Unitful
using LinearAlgebra


"""
no_of_fundamental_dims(mp::Vector{Any})
Finds number of fundamental dimensions
"""
function no_of_fundamental_dims(mp)
fundamental_dimensions = 0
for val in mp
if val == 1
fundamental_dimensions += 1
end
end

return fundamental_dimensions
end


"""
get_dims_of_vars(Vector{Any}, Number, Vector{Any})

Gets units of each variable in an array. Returns a matrix where each row corresponds
to units represented in binary values
Example : kg*m^3 - [3, 1, 0, 0, 0, 0]
In the returned value each row corresponds to length, mass, time, current, luminosity
and temperature respectively.
"""
function get_dims_of_vars(dims_vars, total_vars, dim_map)
# For every single variable it contains row of 1s and 0s mentioning which unit is present
dims_of_all_vars = zeros(total_vars, 6)

for (ind, dim) in enumerate(dims_vars)
temp_dims = 0
temp_dims_arr = zeros(6)
if ulength(dim) != 0
dim_map[1] = 1
temp_dims_arr[1] = ulength(dim)
temp_dims += 1
end
if umass(dim) != 0
dim_map[2] = 1
temp_dims_arr[2] = umass(dim)
temp_dims += 1
end
if utime(dim) != 0
dim_map[3] = 1
temp_dims_arr[3] = utime(dim)
temp_dims +=1
end
if ucurrent(dim) != 0
dim_map[4] = 1
temp_dims_arr[4] = ucurrent(dim)
temp_dims +=1
end
if utemperature(dim) != 0
dim_map[5] = 1
temp_dims_arr[5] = utemperature(dim)

temp_dims +=1
end
if uluminosity(dim) != 0
dim_map[6] = 1
temp_dims_arr[6] = uluminosity(dim)
temp_dims +=1
end
dims_of_all_vars[ind,:] = temp_dims_arr'
end

return dims_of_all_vars
end


"""
# find_pi_term_exponents(Matrix{Float64}, Number, Number)

Finds PI terms and returns the exponents and indices of repeating variables and
non repeating index in the form of a dictionary
"""
function find_pi_term_exponents(dims_of_all_vars, total_vars, fundamental_dimensions)
pi_terms_data = []

# We are considering the repeating variables as starting variables for now.
repeating_idx = []
for i in 1:fundamental_dimensions
push!(repeating_idx, i)
end
non_repeating_idx = [] # V1
for i in fundamental_dimensions+1:total_vars
push!(non_repeating_idx, i)
end

for idx in non_repeating_idx
# Form system of equations for exponents
A = dims_of_all_vars[repeating_idx, 1:fundamental_dimensions]' # Transpose for solving
b = -dims_of_all_vars[idx, 1:fundamental_dimensions]

exponents = A \ b # Linear solve

pi_term = Dict("var_idx" => idx, "exponents" => exponents, "repeating_idx" => repeating_idx)
push!(pi_terms_data, pi_term)
end

return pi_terms_data
end


"""
retrieve_pi_terms(Dict{Any}, Vector{Num})

Gets the actual PI term provided non repeating index, repeating indices and the original variables
"""
function retrieve_pi_terms(pi_term, var_names)
repeating_idx = pi_term["repeating_idx"]
exp_arr = pi_term["exponents"]
final_pi_term = 1

@. exp_arr = round(exp_arr, digits=2)

for (ind, val) in enumerate(repeating_idx)
final_pi_term *= var_names[val]^exp_arr[ind]
end

# multiplying with non repeating variable for the pi term
final_pi_term *= var_names[pi_term["var_idx"]]

return final_pi_term
end


"""
buckinghumFun(Vector{DynamicQuantities.Quantity}, Vector{Num})

Takes an array of DynamicQuantities.Quantity type and variable names separately. Gets the buckinghum
PI terms and returns the array of PI terms
"""
function buckinghumFun(vars_quants, var_names)

# vars_quants : [m^3/kg, s^-1, ms^-1]
# var_names : [ρ, μ, u, v]

# Number of variables
total_vars = length(vars_quants)

# Required for counting fundamental dimensions
# says whicheever units are in picture. In this case : length, mass, time
# [1, 1, 1, 0, 0, 0]
dim_map = zeros(6)


# [kg^-3* m, kg*s, s^-1]
dims_vars = []
for u_arr in vars_quants
push!(dims_vars, u_arr.dimensions)
end

dims_of_all_vars = get_dims_of_vars(dims_vars, total_vars, dim_map)

fundamental_dimensions = no_of_fundamental_dims(dim_map)

pi_terms = find_pi_term_exponents(dims_of_all_vars, total_vars, fundamental_dimensions)

pis_arr = []
for val in pi_terms
push!(pis_arr, retrieve_pi_terms(val, var_names))
end

return pis_arr
end


"""
transform_sys(Vector{Any}, system::ODESystem)

Substitutes the PI terms in the equations to form new set of equations and returns the
new ODESystem
"""

function transform_sys(pi_eqs, sys::ODESystem, pis_vars)
original_equations = equations(sys)
orginal_eqs_mp = Dict{Any, Any}(eq.lhs => eq.rhs for eq in original_equations)
transformed_eqs = []
for eq in pi_eqs
pi_term = eq
for each_var in get_variables(pi_term)
println(each_var)
if haskey(orginal_eqs_mp, D(each_var)) != true
orginal_eqs_mp[D(each_var)] = 0
end
end

der_eq = expand_derivatives(D(pi_term))
sub_eq = substitute(der_eq, orginal_eqs_mp)
push!(transformed_eqs, sub_eq)
end

equations_for_system = Equation[]
dependent_vars = Any[unknowns(sys)...]
for (i, eq) in pairs(transformed_eqs)
push!(dependent_vars, pis_vars[i])
push!(equations_for_system, D(pis_vars[i]) ~ eq)
end

@named new_sys = ODESystem(equations_for_system, ModelingToolkit.get_iv(sys),dependent_vars, [α]; defaults=defaults(sys))

return new_sys
end
31 changes: 31 additions & 0 deletions test/buckinghum.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
include("../src/systems/buckinghum.jl")
using DynamicQuantities
using LinearAlgebra
using Test

@variables f, d, v, ρ, μ
vars1_arr = [ρ, d, v, μ, f]

vars1_quants = [DynamicQuantities.Quantity(0, mass=1, length=-1, time=-1), DynamicQuantities.Quantity(0, length=1) , DynamicQuantities.Quantity(0, length=1, time=-1), DynamicQuantities.Quantity(0, mass=1, length=1, time=-2), DynamicQuantities.Quantity(0, mass=1, length=-1, time=-1)]

@test isequal(buckinghumFun(vars1_quants, vars1_arr)[1], μ/(d*v*ρ))
@test isequal(buckinghumFun(vars1_quants, vars1_arr)[2], f/(ρ))


@parameters t, α
@variables a(t), b(t), c(t), d(t)
@variables π1(t) π2(t) π3(t) π4(t) π5(t)
D = ModelingToolkit.Differential(t)
vars2_arr = [a, b, c, d]
vars2_quants =[DynamicQuantities.Quantity(0, mass=1, length=-3), DynamicQuantities.Quantity(0, mass=1, length=-1, time=-1), DynamicQuantities.Quantity(0, length=1, time=-1), DynamicQuantities.Quantity(0, length=1)]

original_equations_map = Dict(D(a) => α*t + b, D(b) => c*d/a)
original_equations = [k ~ v for (k,v) in original_equations_map]

@named sys = ODESystem(original_equations, t, vars2_arr, [α];defaults=Dict(α => 0.5))
pi_terms = buckinghumFun(vars2_quants, vars2_arr)
new_sys = transform_sys(pi_terms, sys, [π1, π2, π3, π4, π5])

@test isequal(pi_terms[1], (a*c*d)/b)
@test isequal(equations(new_sys)[1].rhs ,((b + t*α)*c*d) / b + (-(c^2)*(d^2)) / (b^2))