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]