Programming numeric simulations of thermodynamic systems in Julia

Hi Julia Community,

I’m really new to the Julia language and this is my first post in the forum. Moreover english is not my mother tongue, so I hope you can understand my question and help me to increase my skills with Julia.

I try to use the Julia language to calculate numeric simulations of different thermodynamic systems. I have thought of a possibility to structure my script, but I’m not really sure, if it’s the way to go in Julia to reach a good performance. Here is a little example what I wanna do:

Part 1:
I want to simulate different types of thermodynamic systems with different kinds of fluids and connections between the systems. So in the first step I have to create my system. I think that structs will be a good option to structure my system:

abstract type System end
abstract type Fluid end

struct Air <: Fluid end

struct Container <: System
    fluid::Fluid
    T::Vector{Float64}
    m::Vector{Float64}    
    p::Vector{Float64}
    V::Vector{Float64}
end

struct ThermoSys
    systems::Vector{System}
    connections::Vector{Any}
end

function calc_mass(V, T)
    return 1*10^5 * V / (0.287069 *10^3 * T)
end

function create_thermo_systems()
    system1 = Container(Air(), [300], [calc_mass(0.01, 300)], [1*10^5], [0.01])
    system2 = Container(Air(), [300], [calc_mass(0.05, 300)], [1*10^5], [0.05])
    ExampleSystem = ThermoSys([system1, system2], [1, 2, 0.05])
    return ExampleSystem
end

Thats just a small example. The systems will be much more complex in the final program. In this example is a function which creates two containers of different volume and filled with air. The fluid has a temperature, a pressure and a mass. The containers are connected with each other. I decided to use vectors for the definition of temperature, mass, etc… to update the data in the simulation and use the created system as a data storage.

Part 2:
Now, I hand the created System over to the simulation module. In this part, the program iterates through all systems and the solver does a stepwise calculation of it with some differential equations. The right calculations will be dependent of the type of the system or the chosen fluid with multiple dispatch. After the calculation all data vectors will be updated with the new values.

function calc_system(system_i::System)
    T_new = calc_new_temperature_of_fluid(system_i.fluid)
    push!(system_i.T, T_new)
end

In this example is a functions, which will be called of an argument of the type system and calculates a new temperature step for the fluid in the system. The new value is added to the vector after the calculation and can be used in the next calculation as starting value with “system.T[end]”. Moreover I’m able to plot the changes of all values in every system after the calculation.

Question:
I’m really sure that my chosen way to structure the simulation is possible. But I’m struggling with the performance, if the systems will become more complex and maybe there are multiple fluids in one zone. Is it a good option to create my systems with the structs and use it as data storage in the stepwise calculation? Will it become slow in complex calculations? How can I change it to perform better? I’m really happy about help and thank you in advance.

Greetings
Eulerian

Hi and welcome!

Fluid is an abstract type, which is bad for performance.
It is better to use a parametric type here, so that the compiler has the type information available:

struct Container{T <: Fluid} <: System
    fluid:: T

If your Fluid structs have no fields (like in your example), you could also have them only as type parameters, but not as fields (i.e. removing the fluid:: T field above).

For the storage of the state in different time steps it may be better (depending on the details of your implementation) to store only a single time step in your Container struct (i.e. replacing your Vectors with Float64) and use an array of Containers.

1 Like