diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 1aa0181..b3914e9 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -41,6 +41,7 @@ git clone https://github.com/ProjectPhysX/FluidX3D.git chmod +x make.sh ./make.sh ``` +- [`INTERACTIVE_GRAPHICS`](src/defines.hpp) mode is not supported on macOS, as no X11 is available. You can still use [`INTERACTIVE_GRAPHICS_ASCII`](src/defines.hpp) preview though, or [render video](#video-rendering) to the hard drive with regular [`GRAPHICS`](src/defines.hpp) mode. ### Android - Select [`TARGET=Android`](make.sh#L6) in [`make.sh`](make.sh#L6). @@ -49,6 +50,7 @@ git clone https://github.com/ProjectPhysX/FluidX3D.git chmod +x make.sh ./make.sh ``` +- [`INTERACTIVE_GRAPHICS`](src/defines.hpp) mode is not supported on Android, as no X11 is available. You can still use [`INTERACTIVE_GRAPHICS_ASCII`](src/defines.hpp) preview though, or [render video](#video-rendering) to the hard drive with regular [`GRAPHICS`](src/defines.hpp) mode.
@@ -115,7 +117,10 @@ git clone https://github.com/ProjectPhysX/FluidX3D.git ```c const uint3 lbm_N = resolution(float3(1.0f, 2.0f, 0.5f), 2000u); ``` - This takes as inputs the desired aspect ratio of the simulation box and the VRAM occupation in MB, and returns the grid resolution as a `uint3` with `.x`/`.y`/`.z` components. You can also directly feed the `uint3` into the LBM constructor as resolution: `LBM lbm(lbm_N, nu, ...);` + This takes as inputs the desired aspect ratio of the simulation box and the VRAM occupation in MB, and returns the grid resolution as a `uint3` with `.x`/`.y`/`.z` components. You can also directly feed the `uint3` into the LBM constructor as resolution: + ```c + LBM lbm(lbm_N, nu, ...); + ``` ### Unit Conversion - The LBM simulation uses a different unit system from SI units, where density `rho=1` and velocity `u≈0.001-0.1`, because floating-point arithmetic is most accurate close to `1`. @@ -235,6 +240,7 @@ git clone https://github.com/ProjectPhysX/FluidX3D.git lbm.write_mesh_to_vtk(const Mesh* mesh); // for exporting triangle meshes ``` - These functions first pull the data from the GPU(s) into CPU RAM, and then write it to the hard drive. +- If [unit conversion](#unit-conversion) with `units.set_m_kg_s(...)` was specified, the data in exported `.vtk` files is automaticlally converted to SI units. - Exported files will automatically be assigned the current simulation time step in their name, in the format `bin/export/u-123456789.vtk`. - Be aware that these volumetric files can be gigantic in file size, tens of GigaByte for a single file. - You can view/evaluate the `.vtk` files for example in [ParaView](https://www.paraview.org/). diff --git a/README.md b/README.md index 4ea0e68..e533720 100644 --- a/README.md +++ b/README.md @@ -113,9 +113,20 @@ The fastest and most memory efficient lattice Boltzmann CFD software, running on - fixed flickering with frustrum culling at very small field of view - fixed bug where rendered/exported frame was not updated when `visualization_modes` changed - v2.12 (18.01.2024) - - significantly (~3x) faster source code compiling on Linux using multiple CPU cores if [`make`](https://www.gnu.org/software/make/) is installed + - ~3x faster source code compiling on Linux using multiple CPU cores if [`make`](https://www.gnu.org/software/make/) is installed - significantly faster simulation initialization (~40% single-GPU, ~15% multi-GPU) - minor bug fix in `Memory_Container::reset()` function +- v2.13 (11.02.2023) + - data in exported `.vtk` files is now automatically converted to SI units + - ~2x faster `.vtk` export with multithreading + - added unit conversion functions for `TEMPERATURE` extension + - fixed graphical artifacts with axis-aligned camera in raytracing + - fixed `get_exe_path()` for macOS + - fixed X11 multi-monitor issues on Linux + - workaround for Nvidia driver bug: `enqueueFillBuffer` is broken for large buffers on Nvidia GPUs + - fixed slow numeric drift issues caused by `-cl-fast-relaxed-math` + - fixed wrong Maximum Allocation Size reporting in `LBM::write_status()` + - fixed missing scaling of coordinates to SI units in `LBM::write_mesh_to_vtk()` @@ -218,7 +229,7 @@ $$f_j(i\\%2\\ ?\\ \vec{x}+\vec{e}_i\\ :\\ \vec{x},\\ t+\Delta t)=f_i^\textrm{tem - domain decomposition allows pooling VRAM from multiple GPUs for much larger grid resolution - each domain (GPU) can hold up to 4.29 billion (2³², 1624³) lattice points (225 GB memory) - - GPUs don't have to be identical (not even from the same vendor), but similar VRAM capacity/bandwidth is recommended + - GPUs don't have to be identical (not even from the same vendor), but similar VRAM capacity/bandwidth is recommended - domain communication architecture (simplified) ```diff ++ .-----------------------------------------------------------------. ++ @@ -339,7 +350,8 @@ $$f_j(i\\%2\\ ?\\ \vec{x}+\vec{e}_i\\ :\\ \vec{x},\\ t+\Delta t)=f_i^\textrm{tem - FluidX3D can do simulations so large that storing the volumetric data for later rendering becomes unmanageable (like 120GB for a single frame, hundreds of TeraByte for a video) - instead, FluidX3D allows [rendering raw simulation data directly in VRAM](https://www.researchgate.net/publication/360501260_Combined_scientific_CFD_simulation_and_interactive_raytracing_with_OpenCL), so no large volumetric files have to be exported to the hard disk (see my [technical talk](https://youtu.be/pD8JWAZ2f8o)) - the rendering is so fast that it works interactively in real time for both rasterization and raytracing -- if no monitor is available (like on a remote Linux server), there is an ASCII rendering mode to interactively visualize the simulation in the terminal (even in WSL and/or through SSH) +- rasterization and raytracing are done in OpenCL and work on all GPUs, even the ones without RTX/DXR raytracing cores or without any rendering hardware at all (like A100, MI200, ...) +- if no monitor is available (like on a remote Linux server), there is an [ASCII rendering mode](https://youtu.be/pD8JWAZ2f8o&t=1456) to interactively visualize the simulation in the terminal (even in WSL and/or through SSH) - rendering is fully multi-GPU-parallelized via seamless domain decomposition rasterization - with interactive graphics mode disabled, image resolution can be as large as VRAM allows for (4K/8K/16K and above) - (interacitive) visualization modes: @@ -365,7 +377,7 @@ $$f_j(i\\%2\\ ?\\ \vec{x}+\vec{e}_i\\ :\\ \vec{x},\\ t+\Delta t)=f_i^\textrm{tem - native cross-vendor multi-GPU implementation - uses PCIe communication, so no SLI/Crossfire/NVLink/InfinityFabric required - single-node parallelization, so no MPI installation required - - GPUs don't even have to be from the same vendor, but similar memory capacity and bandwidth are recommended + - [GPUs don't even have to be from the same vendor](https://youtu.be/PscbxGVs52o), but similar memory capacity and bandwidth are recommended - works in [Windows](DOCUMENTATION.md#windows) and [Linux](DOCUMENTATION.md#linux) with C++17, with limited support also for [macOS](DOCUMENTATION.md#macos) and [Android](DOCUMENTATION.md#android) - supports [importing and voxelizing triangle meshes](DOCUMENTATION.md#loading-stl-files) from binary `.stl` files, with fast GPU voxelization - supports [exporting volumetric data](DOCUMENTATION.md#data-export) as binary `.vtk` files diff --git a/src/info.cpp b/src/info.cpp index 43c408a..bb84042 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -40,23 +40,23 @@ double Info::time() const { // returns either elapsed time or remaining time void Info::print_logo() const { const int a=color_light_blue, b=color_orange, c=color_pink; print(".-----------------------------------------------------------------------------.\n"); - print("| "); print("______________ ", a); print("______________", b); print(" |\n"); - print("| "); print("\\ ________ | ", a); print("| ________ /", b); print(" |\n"); + print("| "); print( " ______________ ", a); print(" ______________ ", b); print(" |\n"); + print("| "); print( "\\ ________ | ", a); print("| ________ /", b); print(" |\n"); print("| "); print("\\ \\ | | ", a); print("| | / /", b); print(" |\n"); print("| "); print("\\ \\ | | ", a); print("| | / /", b); print(" |\n"); print("| "); print("\\ \\ | | ", a); print("| | / /", b); print(" |\n"); - print("| "); print("\\ \\_.-\" | ", a); print("| \"-._/ /", b); print(" |\n"); + print("| "); print("\\ \\_.-\" | ", a); print("| \"-._/ /", b); print(" |\n"); print("| "); print("\\ _.-\" ", a); print("_ ", c); print("\"-._ /", b); print(" |\n"); print("| "); print("\\.-\" ", a); print("_.-\" \"-._ ", c); print("\"-./", b); print(" |\n"); - print("| "); print(".-\" .-\"-. \"-.", c); print(" |\n"); - print("| "); print("\\ v\" \"v /", c); print(" |\n"); - print("| "); print("\\ \\ / /", c); print(" |\n"); - print("| "); print("\\ \\ / /", c); print(" |\n"); - print("| "); print("\\ \\ / /", c); print(" |\n"); - print("| "); print("\\ ' /", c); print(" |\n"); - print("| "); print("\\ /", c); print(" |\n"); - print("| "); print("\\ /", c); print(" FluidX3D Version 2.12 |\n"); - print("| "); print("'", c); print(" Copyright (c) Dr. Moritz Lehmann |\n"); + print("| "); print(" .-\" .-\"-. \"-. ", c); print(" |\n"); + print("| "); print("\\ v\" \"v /", c); print(" |\n"); + print("| "); print("\\ \\ / /", c); print(" |\n"); + print("| "); print("\\ \\ / /", c); print(" |\n"); + print("| "); print("\\ \\ / /", c); print(" |\n"); + print("| "); print("\\ ' /", c); print(" |\n"); + print("| "); print("\\ /", c); print(" |\n"); + print("| "); print("\\ /", c); print(" FluidX3D Version 2.13 |\n"); + print("| "); print( "'", c); print(" Copyright (c) Dr. Moritz Lehmann |\n"); print("|-----------------------------------------------------------------------------|\n"); } void Info::print_initialize() { diff --git a/src/lbm.cpp b/src/lbm.cpp index 660b3ea..17b1acf 100644 --- a/src/lbm.cpp +++ b/src/lbm.cpp @@ -989,26 +989,28 @@ void LBM::unvoxelize_mesh_on_device(const Mesh* mesh, const uchar flag) { // rem for(uint d=0u; denqueue_unvoxelize_mesh_on_device(mesh, flag); for(uint d=0u; dfinish_queue(); } -void LBM::write_mesh_to_vtk(const Mesh* mesh, const string& path) const { // write mesh to binary .vtk file +void LBM::write_mesh_to_vtk(const Mesh* mesh, const string& path, const bool convert_to_si_units) const { // write mesh to binary .vtk file const string header_1 = "# vtk DataFile Version 3.0\nData\nBINARY\nDATASET POLYDATA\nPOINTS "+to_string(3u*mesh->triangle_number)+" float\n"; const string header_2 = "POLYGONS "+to_string(mesh->triangle_number)+" "+to_string(4u*mesh->triangle_number)+"\n"; float* points = new float[9u*mesh->triangle_number]; int* triangles = new int[4u*mesh->triangle_number]; - for(uint i=0u; itriangle_number; i++) { - points[9u*i ] = reverse_bytes(mesh->p0[i].x-center().x); - points[9u*i+1u] = reverse_bytes(mesh->p0[i].y-center().y); - points[9u*i+2u] = reverse_bytes(mesh->p0[i].z-center().z); - points[9u*i+3u] = reverse_bytes(mesh->p1[i].x-center().x); - points[9u*i+4u] = reverse_bytes(mesh->p1[i].y-center().y); - points[9u*i+5u] = reverse_bytes(mesh->p1[i].z-center().z); - points[9u*i+6u] = reverse_bytes(mesh->p2[i].x-center().x); - points[9u*i+7u] = reverse_bytes(mesh->p2[i].y-center().y); - points[9u*i+8u] = reverse_bytes(mesh->p2[i].z-center().z); + const float spacing = convert_to_si_units ? units.si_x(1.0f) : 1.0f; + const float3 offset = center(); + parallel_for(mesh->triangle_number, [&](uint i) { + points[9u*i ] = reverse_bytes(spacing*(mesh->p0[i].x-offset.x)); + points[9u*i+1u] = reverse_bytes(spacing*(mesh->p0[i].y-offset.y)); + points[9u*i+2u] = reverse_bytes(spacing*(mesh->p0[i].z-offset.z)); + points[9u*i+3u] = reverse_bytes(spacing*(mesh->p1[i].x-offset.x)); + points[9u*i+4u] = reverse_bytes(spacing*(mesh->p1[i].y-offset.y)); + points[9u*i+5u] = reverse_bytes(spacing*(mesh->p1[i].z-offset.z)); + points[9u*i+6u] = reverse_bytes(spacing*(mesh->p2[i].x-offset.x)); + points[9u*i+7u] = reverse_bytes(spacing*(mesh->p2[i].y-offset.y)); + points[9u*i+8u] = reverse_bytes(spacing*(mesh->p2[i].z-offset.z)); triangles[4u*i ] = reverse_bytes(3); // 3 vertices per triangle triangles[4u*i+1u] = reverse_bytes(3*(int)i ); // vertex 0 triangles[4u*i+2u] = reverse_bytes(3*(int)i+1); // vertex 1 triangles[4u*i+3u] = reverse_bytes(3*(int)i+2); // vertex 2 - } + }); const string filename = default_filename(path, "mesh", ".vtk", get_t()); create_folder(filename); std::ofstream file(filename, std::ios::out|std::ios::binary); diff --git a/src/lbm.hpp b/src/lbm.hpp index a466375..fc11760 100644 --- a/src/lbm.hpp +++ b/src/lbm.hpp @@ -27,7 +27,7 @@ class LBM_Domain { float nu = 1.0f/6.0f; // kinematic shear viscosity float fx=0.0f, fy=0.0f, fz=0.0f; // global force per volume float sigma=0.0f; // surface tension coefficient - float alpha=1.0f, beta=1.0f, T_avg=1.0f; // alpha = thermal diffusion coefficient, beta = thermal expansion coefficient, T_avg = 1 = average temperature + float alpha=1.0f, beta=1.0f, T_avg=1.0f; // alpha = thermal diffusion coefficient, beta = (volumetric) thermal expansion coefficient, T_avg = 1 = average temperature uint particles_N = 0u; float particles_rho = 1.0f; @@ -279,8 +279,16 @@ class LBM { else print_error("Error in vtk_type(): Type not supported."); return ""; } - inline void write_vtk(const string& path) { // write binary .vtk file - const float spacing = units.si_x(1.0f); + inline void write_vtk(const string& path, const bool convert_to_si_units=true) { // write binary .vtk file + float spacing = 1.0f; + T unit_conversion_factor = (T)1; + if(convert_to_si_units) { + spacing = units.si_x(1.0f); + if(name=="rho") unit_conversion_factor = (T)units.si_rho(1.0f); + if(name=="u" ) unit_conversion_factor = (T)units.si_u (1.0f); + if(name=="F" ) unit_conversion_factor = (T)units.si_F (1.0f); + if(name=="T" ) unit_conversion_factor = (T)units.si_T (1.0f); + } const float3 origin = spacing*float3(0.5f-0.5f*(float)Nx, 0.5f-0.5f*(float)Ny, 0.5f-0.5f*(float)Nz); const string header = "# vtk DataFile Version 3.0\nData\nBINARY\nDATASET STRUCTURED_POINTS\n" @@ -290,11 +298,11 @@ class LBM { "POINT_DATA "+to_string((ulong)Nx*(ulong)Ny*(ulong)Nz)+"\nSCALARS data "+vtk_type()+" "+to_string(dimensions())+"\nLOOKUP_TABLE default\n" ; T* data = new T[range()]; - for(uint d=0u; denqueue_write_to_device(); for(uint domain=0u; domainfinish_queue(); } - inline void write_host_to_vtk(const string& path="") { // write binary .vtk file - write_vtk(default_filename(path, name, ".vtk", lbm->get_t())); + inline void write_host_to_vtk(const string& path="", const bool convert_to_si_units=true) { // write binary .vtk file + write_vtk(default_filename(path, name, ".vtk", lbm->get_t()), convert_to_si_units); } - inline void write_device_to_vtk(const string& path="") { // write binary .vtk file + inline void write_device_to_vtk(const string& path="", const bool convert_to_si_units=true) { // write binary .vtk file read_from_device(); - write_host_to_vtk(path); + write_host_to_vtk(path, convert_to_si_units); } }; @@ -502,7 +510,7 @@ class LBM { void voxelize_mesh_on_device(const Mesh* mesh, const uchar flag=TYPE_S, const float3& rotation_center=float3(0.0f), const float3& linear_velocity=float3(0.0f), const float3& rotational_velocity=float3(0.0f)); // voxelize mesh void unvoxelize_mesh_on_device(const Mesh* mesh, const uchar flag=TYPE_S); // remove voxelized triangle mesh from LBM grid - void write_mesh_to_vtk(const Mesh* mesh, const string& path="") const; // write mesh to binary .vtk file + void write_mesh_to_vtk(const Mesh* mesh, const string& path="", const bool convert_to_si_units=true) const; // write mesh to binary .vtk file void voxelize_stl(const string& path, const float3& center, const float3x3& rotation, const float size=0.0f, const uchar flag=TYPE_S); // read and voxelize binary .stl file void voxelize_stl(const string& path, const float3x3& rotation, const float size=0.0f, const uchar flag=TYPE_S); // read and voxelize binary .stl file (place in box center) void voxelize_stl(const string& path, const float3& center, const float size=0.0f, const uchar flag=TYPE_S); // read and voxelize binary .stl file (no rotation) diff --git a/src/units.hpp b/src/units.hpp index f5a822a..1dbb34c 100644 --- a/src/units.hpp +++ b/src/units.hpp @@ -2,58 +2,78 @@ #include "utilities.hpp" -class Units { // contains the 3 base units m, kg, s for unit conversions and vtk output +class Units { // contains the 4 base units [m], [kg], [s], [K] for unit conversions and vtk output private: - float m=1.0f, kg=1.0f, s=1.0f; // 1 lattice unit times m/kg/s is meter/kilogram/seconds + float unit_m=1.0f, unit_kg=1.0f, unit_s=1.0f, unit_K=1.0f; // 1 lattice unit times [m]/[kg]/[s]/[K] is [meter]/[kilogram]/[second]/[Kelvin] public: - void set_m_kg_s(const float x, const float u, const float rho, const float si_x, const float si_u, const float si_rho) { // length x, velocity u, density rho in both simulation and SI units - m = si_x/x; // length si_x = x*[m] - kg = si_rho/rho*cb(m); // density si_rho = rho*[kg/m^3] - s = u/si_u*m; // velocity si_u = u*[m/s] + void set_m_kg_s(const float x, const float u, const float rho/*=1.0f*/, const float si_x, const float si_u, const float si_rho) { // length x, velocity u, density rho in both simulation and SI units + unit_m = si_x/x; // length si_x = x*[m] + unit_kg = si_rho/rho*cb(unit_m); // density si_rho = rho*[kg/m^3] + unit_s = u/si_u*unit_m; // velocity si_u = u*[m/s] print_info("Unit Conversion: 1 cell = "+to_string(1000.0f*this->si_x(1.0f), 3u)+" mm, 1 s = "+to_string(this->t(1.0f))+" time steps"); } void set_m_kg_s(const float m, const float kg, const float s) { // do unit conversion manually - this->m = m; - this->kg = kg; - this->s = s; + this->unit_m = m; + this->unit_kg = kg; + this->unit_s = s; + print_info("Unit Conversion: 1 cell = "+to_string(1000.0f*this->si_x(1.0f), 3u)+" mm, 1 s = "+to_string(this->t(1.0f))+" time steps"); + } + void set_m_kg_s_K(const float x, const float u, const float rho/*=1.0f*/, const float T/*=1.0f*/, const float si_x, const float si_u, const float si_rho, const float si_T) { // length x, velocity u, density rho, temperature T in both simulation and SI units + unit_m = si_x/x; // length si_x = x*[m] + unit_kg = si_rho/rho*cb(unit_m); // density si_rho = rho*[kg/m^3] + unit_s = u/si_u*unit_m; // velocity si_u = u*[m/s] + unit_K = si_T/T; // length si_T = T*[K] + print_info("Unit Conversion: 1 cell = "+to_string(1000.0f*this->si_x(1.0f), 3u)+" mm, 1 s = "+to_string(this->t(1.0f))+" time steps"); + } + void set_m_kg_s_K(const float m, const float kg, const float s, const float K) { // do unit conversion manually + this->unit_m = m; + this->unit_kg = kg; + this->unit_s = s; + this->unit_K = K; print_info("Unit Conversion: 1 cell = "+to_string(1000.0f*this->si_x(1.0f), 3u)+" mm, 1 s = "+to_string(this->t(1.0f))+" time steps"); } // the following methods convert SI units into simulation units (have to be called after set_m_kg_s(...);) - float x(const float si_x) const { return si_x/m; } // length si_x = x*[m] - float M(const float si_M) const { return si_M/kg; } // mass si_M = M*[kg] - ulong t(const float si_t) const { return to_ulong(si_t/s); } // time si_t = t*[s] - float frequency(const float si_frequency) const { return si_frequency*s; } // frequency si_frequency = frequency*[1/s] - float omega(const float si_omega) const { return si_omega*s; } // frequency si_omega = omega/[s] - float u(const float si_u) const { return si_u*s/m; } // velocity si_u = u*[m/s] - float rho(const float si_rho) const { return si_rho*cb(m)/kg; } // density si_rho = rho*[kg/m^3] - float Q(const float si_Q) const { return si_Q*s/cb(m); } // flow rate si_Q = Q*[m^3/s] - float nu(const float si_nu) const { return si_nu*s/sq(m); } // kinematic shear viscosity si_nu = nu*[m^2/s] - float mu(const float si_mu) const { return si_mu*s*m/kg; } // dynamic shear viscosity si_mu = mu*[kg/(m*s)] - float g(const float si_g) const { return si_g/m*sq(s); } // gravitational acceleration si_g = g*[m/s^2] - float f(const float si_f) const { return si_f*sq(m*s)/kg; } // force per volume si_f = f*[kg/(m*s)^2] - float f(const float si_rho, const float si_g) const { return si_rho*si_g*sq(m*s)/kg; } // force per volume f = rho*g = si_rho/[kg/m^3]*si_g/[m/s^2] = si_rho*si_g*[(m*s)^2/kg] - float F(const float si_F) const { return si_F*sq(s)/(kg*m); } // force si_F = F*[kg*m/s^2] - float T(const float si_T) const { return si_T*sq(s)/(kg*sq(m)); } // torque si_T = T*[kg*m^2/s^2] - float sigma(const float si_sigma) const { return si_sigma*sq(s)/kg; } // surface tension si_sigma = sigma*[kg/s^2] + float x(const float si_x) const { return si_x/unit_m; } // length si_x = x*[m] + float m(const float si_m) const { return si_m/unit_kg; } // mass si_m = m*[kg] + ulong t(const float si_t) const { return to_ulong(si_t/unit_s); } // time si_t = t*[s] + float frequency(const float si_frequency) const { return si_frequency*unit_s; } // frequency si_frequency = frequency*[1/s] + float omega(const float si_omega) const { return si_omega*unit_s; } // frequency si_omega = omega/[s] + float u(const float si_u) const { return si_u*unit_s/unit_m; } // velocity si_u = u*[m/s] + float rho(const float si_rho) const { return si_rho*cb(unit_m)/unit_kg; } // density si_rho = rho*[kg/m^3] + float Q(const float si_Q) const { return si_Q*unit_s/cb(unit_m); } // flow rate si_Q = Q*[m^3/s] + float nu(const float si_nu) const { return si_nu*unit_s/sq(unit_m); } // kinematic shear viscosity si_nu = nu*[m^2/s] + float mu(const float si_mu) const { return si_mu*unit_s*unit_m/unit_kg; } // dynamic shear viscosity si_mu = mu*[kg/(m*s)] + float g(const float si_g) const { return si_g/unit_m*sq(unit_s); } // gravitational acceleration si_g = g*[m/s^2] + float f(const float si_f) const { return si_f*sq(unit_m*unit_s)/unit_kg; } // force per volume si_f = f*[kg/(m*s)^2] + float f(const float si_rho, const float si_g) const { return si_rho*si_g*sq(unit_m*unit_s)/unit_kg; } // force per volume f = rho*g = si_rho/[kg/m^3]*si_g/[m/s^2] = si_rho*si_g*[(m*s)^2/kg] + float F(const float si_F) const { return si_F*sq(unit_s)/(unit_kg*unit_m); } // force si_F = F*[kg*m/s^2] + float M(const float si_M) const { return si_M*sq(unit_s)/(unit_kg*sq(unit_m)); } // torque si_M = M*[kg*m^2/s^2] + float sigma(const float si_sigma) const { return si_sigma*sq(unit_s)/unit_kg; } // surface tension si_sigma = sigma*[kg/s^2] + float T(const float si_T) { return si_T/unit_K; } // temperature si_T = T*[K] + float alpha(const float si_alpha) { return si_alpha*unit_s/sq(unit_m); } // thermal diffusivity si_alpha = alpha*[m^2/s] + float beta(const float si_beta) { return si_beta*unit_K/cb(unit_m); } // (volumetric) thermal expansion coefficient si_beta = beta*[m^3/K] // the following methods convert simulation units into SI units (have to be called after set_m_kg_s(...);) - float si_x(const uint x) const { return (float)x*m; } // length si_x = x*[m] - float si_x(const float x) const { return x*m; } // length si_x = x*[m] - float si_M(const float M) const { return M*kg; } // mass si_M = M*[kg] - float si_t(const ulong t) const { return (float)t*s; } // time si_t = t*[s] - float si_frequency(const float frequency) const { return frequency/s; } // frequency si_frequency = frequency*[1/s] - float si_V(const float V) const { return V*cb(m); } // volume si_V = V*[m^3] - float si_u(const float u) const { return u*m/s; } // velocity si_u = u*[m/s] - float si_rho(const float rho) const { return rho*kg/cb(m); } // density si_rho = rho*[kg/m^3] - float si_p(const float p) const { return p*kg/(m*sq(s)); } // pressure si_p = p*[kg/(m*s^2)] - float si_Q(const float Q) const { return Q*cb(m)/s; } // flow rate si_Q = Q*[m^3/s] - float si_nu(const float nu) const { return nu*sq(m)/s; } // kinematic shear viscosity si_nu = nu*[m^2/s] - float si_g(const float g) const { return g*m/sq(s); } // gravitational acceleration si_g = g*[m/s^2] - float si_f(const float f) const { return f*kg/sq(m*s); } // force per volume si_f = f*[kg/(m*s)^2] - float si_F(const float F) const { return F*kg*m/sq(s); } // force si_F = F*[kg*m/s^2] - float si_T(const float T) const { return T*kg*sq(m)/sq(s); } // torque si_T = T*[kg*m^2/s^2] - float si_sigma(const float sigma) const { return sigma*kg/sq(s); } // surface tension si_sigma = sigma*[kg/s^2] + float si_x(const uint x) const { return (float)x*unit_m; } // length si_x = x*[m] + float si_x(const float x) const { return x*unit_m; } // length si_x = x*[m] + float si_m(const float m) const { return m*unit_kg; } // mass si_m = m*[kg] + float si_t(const ulong t) const { return (float)t*unit_s; } // time si_t = t*[s] + float si_frequency(const float frequency) const { return frequency/unit_s; } // frequency si_frequency = frequency*[1/s] + float si_V(const float V) const { return V*cb(unit_m); } // volume si_V = V*[m^3] + float si_u(const float u) const { return u*unit_m/unit_s; } // velocity si_u = u*[m/s] + float si_rho(const float rho) const { return rho*unit_kg/cb(unit_m); } // density si_rho = rho*[kg/m^3] + float si_p(const float p) const { return p*unit_kg/(unit_m*sq(unit_s)); } // pressure si_p = p*[kg/(m*s^2)] + float si_Q(const float Q) const { return Q*cb(unit_m)/unit_s; } // flow rate si_Q = Q*[m^3/s] + float si_nu(const float nu) const { return nu*sq(unit_m)/unit_s; } // kinematic shear viscosity si_nu = nu*[m^2/s] + float si_g(const float g) const { return g*unit_m/sq(unit_s); } // gravitational acceleration si_g = g*[m/s^2] + float si_f(const float f) const { return f*unit_kg/sq(unit_m*unit_s); } // force per volume si_f = f*[kg/(m*s)^2] + float si_F(const float F) const { return F*unit_kg*unit_m/sq(unit_s); } // force si_F = F*[kg*m/s^2] + float si_M(const float M) const { return M*unit_kg*sq(unit_m)/sq(unit_s); } // torque si_M = M*[kg*m^2/s^2] + float si_sigma(const float sigma) const { return sigma*unit_kg/sq(unit_s); } // surface tension si_sigma = sigma*[kg/s^2] + float si_T(const float T) { return T*unit_K; } // temperature si_T = T*[K] + float si_alpha(const float alpha) { return alpha*sq(unit_m)/unit_s; } // thermal diffusion coefficient si_alpha = alpha*[m^2/s] + float si_beta(const float beta) { return beta*cb(unit_m)/unit_K; } // (volumetric) thermal expansion coefficient si_beta = beta*[m^3/K] // other conversions in simulation units (can be called before set_m_kg_s(...);) float Re(const float si_Re) const { return si_Re; } // Reynolds number Re = x*u/nu = [1] no unit @@ -72,7 +92,7 @@ class Units { // contains the 3 base units m, kg, s for unit conversions and vtk float rho_laplace(const float sigma, const float R) { return 6.0f*sigma/R; } // sphere laplace pressure p = 2*sigma/R, density rho = 3*p float rho_hydrostatic(const float f, const float z, const float h) { return 3.0f*f*(h-z)+1.0f; } // hydrostatic pressure p = rho*g*h+p0 = f*h+1/3, density rho = 3*p, force per volume f = rho*g, rho0 = 1 float nu_from_mu(const float mu, const float rho) const { return mu/rho; } // kinematic shear viscosity nu = mu/rho = [m^2/s] - float nu_from_tau(const float tau) const { return (tau-0.5f)/3.0f; } // kinematic shear viscosity nu = (tau-1/2)/3 = [m^2/s] + float nu_from_tau(const float tau) const { return (tau-0.5f)/3.0f; } // kinematic shear viscosity nu = (tau-1/2)/3 = [m^2/s], LBM relaxation time tau float nu_from_Re(const float Re, const float x, const float u) const { return x*u/Re; } // kinematic shear viscosity nu = x*u/Re = [m^2/s] float f_from_F(const float F, const float V) const { return F/V; } // force per volume f = F/V = [kg/(m*s)^2] float f_from_g(const float g, const float rho) const { return rho*g; } // force per volume f = rho*g = [kg/(m*s)^2]