115 Commits

Author SHA1 Message Date
0053ef002a CMakeList modified for automatic compile of Zoltan 2025-05-16 18:55:25 +03:30
ad5233bb77 Merge branch 'main' into local-MPI 2025-05-16 10:10:46 +03:30
d7479cf1bd Zoltan is added as thirdParty package 2025-05-15 21:58:43 +03:30
83a6e4baa1 Merge branch 'main' of github.com:PhasicFlow/phasicFlow 2025-05-15 12:01:40 +03:30
bf0197f643 change in the type name of timeValue and sample dictionaries for settingsDict and domain 2025-05-15 12:00:13 +03:30
5a149f3d85 Merge pull request #224 from wanqing0421/postMesh
fixed the cuda bug
2025-05-15 03:35:08 +03:30
80df6bfc9b fixed the cuda bug 2025-05-11 02:29:28 +08:00
b2581cc2a9 Merge pull request #223 from wanqing0421/patch-1
fix the warning messages
2025-05-09 09:19:05 +03:30
1831c2c6c5 fix the warning messages 2025-05-06 10:48:06 +08:00
14731a6c31 This is the first merge from main into MPI branch
Merge branch 'main' into local-MPI
2025-05-03 16:40:46 +03:30
ab856e1df2 move of markdonwList 2025-05-03 12:48:42 +03:30
b4bc724a68 readme helical 2025-05-02 22:28:56 +03:30
ee33469295 readme helical 2025-05-02 22:26:38 +03:30
3933d65303 yaml update5 2025-05-02 22:03:16 +03:30
cf4d22c963 yaml update4 2025-05-02 21:59:31 +03:30
86367c7e2c yaml update3 2025-05-02 21:51:03 +03:30
a7e51a91aa yaml update2 2025-05-02 21:46:43 +03:30
5e56bf1b8c yaml update1 2025-05-02 21:28:40 +03:30
343ac1fc04 yaml update 2025-05-02 21:27:23 +03:30
6b04d17c7f sync-wiki to process img<> tags 2025-05-02 20:47:21 +03:30
97f46379c7 image resize 2025-05-02 20:25:20 +03:30
32fd6cb12e features update 2025-05-02 20:06:49 +03:30
be16fb0684 tutorials link added 2025-05-02 18:29:08 +03:30
4c96c6fa1e test 2025-04-30 19:01:51 +03:30
196b7a1833 how to build readme.md to wiki 2025-04-30 18:52:15 +03:30
316e71ff7a test readme.md 2025-04-30 18:36:53 +03:30
7a4a33ef37 a new workflow for readme.md files to wiki 2025-04-30 18:34:53 +03:30
edfbdb22e9 readmd.md update8 2025-04-30 08:56:11 +03:30
c6725625b3 readmd.md update7 2025-04-30 08:45:28 +03:30
253d6fbaf7 readmd.md update6 2025-04-30 08:40:46 +03:30
701baf09e6 readmd.md update5 2025-04-30 08:37:17 +03:30
20c94398a9 readmd.md update4 2025-04-30 08:34:51 +03:30
dd36e32da4 readmd.md update3 2025-04-30 08:31:19 +03:30
a048c2f5d7 readmd.md update2 2025-04-30 08:27:07 +03:30
8b324bc2b6 readmd.md update1 2025-04-30 08:18:29 +03:30
c7f790a1fa readmd.md update 2025-04-30 08:14:10 +03:30
166d7e72c2 rrr 2025-04-29 20:23:08 +03:30
c126f9a8a3 rr 2025-04-29 20:19:25 +03:30
7104a33a4b r 2025-04-29 20:14:34 +03:30
16b6084d98 readme update 2025-04-29 20:10:06 +03:30
2afea7b273 workflow update 2025-04-29 20:09:22 +03:30
2c5b4f55d1 readme.test 2025-04-29 20:01:13 +03:30
a7dc69a801 Merge branch 'main' of github.com:PhasicFlow/phasicFlow 2025-04-29 19:59:36 +03:30
32287404fa workflow update 2025-04-29 19:54:20 +03:30
8b3530c289 Merge pull request #221 from wanqing0421/benchmarks
update phasicFlow snapshot
2025-04-29 19:47:25 +03:30
d8c3fc02d5 update phasicFlow snapshot 2025-04-29 20:46:30 +08:00
4dab700a47 update image 2025-04-29 20:30:10 +08:00
a50ceeee2c update readme and figure 2025-04-29 20:25:00 +08:00
468730289b test for wiki 2025-04-28 23:06:29 +03:30
27f0202002 workflow for wiki 2025-04-28 23:04:42 +03:30
c69bfc79e1 endsolid bug fix for space separated names 2025-04-28 19:42:49 +03:30
69909b3c01 bug fix in reading stl file 2025-04-28 13:56:21 +03:30
8986c47b69 readmd.md for benchmark is updated 2025-04-28 12:25:53 +03:30
37282f16ac Merge branch 'PhasicFlow:main' into importStl 2025-04-28 09:35:49 +08:00
cd051a6497 Merge pull request #220 from wanqing0421/benchmarks
update readme
2025-04-27 21:57:40 +03:30
8b5d14afe6 update readme figure 2025-04-28 02:20:42 +08:00
eb37affb94 update readme 2025-04-28 02:17:04 +08:00
c0d12f4243 Merge pull request #219 from PhasicFlow/postprocessPhasicFlow
diameter -> distance for benchmarks
2025-04-27 21:08:04 +03:30
a1b5a9bd5d Merge pull request #218 from wanqing0421/benchmarks
upload readme for benchmarks
2025-04-27 20:59:37 +03:30
dc0edbc845 diameter -> distance for benchmarks 2025-04-26 21:22:59 +03:30
b423b6ceb7 upload readme for benchmarks 2025-04-26 15:17:57 +08:00
1f6a953154 fix bug when endsolid with a suffix name 2025-04-26 14:58:56 +08:00
bbd3afea0e Merge pull request #216 from PhasicFlow/postprocessPhasicFlow
readme.md for geometryPhasicFlow
2025-04-25 21:04:53 +03:30
53f0e959b0 readme.md for geometryPhasicFlow 2025-04-25 21:04:18 +03:30
c12022fb19 Merge pull request #215 from wanqing0421/importStl
add scale and transform function during the stl model importing process
2025-04-25 20:45:53 +03:30
d876bb6246 correction for tab 2025-04-26 01:13:42 +08:00
cb40e01b7e Merge pull request #206 from wanqing0421/main
fixed selectorStride bug
2025-04-25 20:35:11 +03:30
5f6400c032 add scale and transform function during the stl model importing process 2025-04-26 00:43:56 +08:00
8863234c1c update stride selector 2025-04-25 23:11:19 +08:00
1cd64fb2ec Merge branch 'PhasicFlow:main' into main 2025-04-25 23:00:10 +08:00
3fc121ef2b Merge pull request #214 from PhasicFlow/postprocessPhasicFlow
readme.md files update
2025-04-25 16:42:06 +03:30
953059cec5 tutorials readme.md 2025-04-25 16:37:24 +03:30
2593e2acf1 diameter->distance, update in tutorials, v-Blender readme.md 2025-04-25 16:14:16 +03:30
7c3b90a22d tutorials-1 after diameter->distance 2025-04-25 14:17:09 +03:30
72b9b74cc9 Merge pull request #213 from PhasicFlow/postprocessPhasicFlow
readme.md for particlesPhasicFlow and change diameter to distance in …
2025-04-25 11:38:49 +03:30
a545acb374 readme.md for particlesPhasicFlow and change diameter to distance in dict files 2025-04-25 11:36:46 +03:30
59fbee9711 Merge pull request #212 from PhasicFlow/postprocessPhasicFlow
Postprocess phasic flow
2025-04-25 09:29:02 +03:30
6cc4b3954a readme.md file for pFlowToVTK 2025-04-25 09:26:56 +03:30
d8c9135700 readme.md file for postprocessPhasicFlow 2025-04-25 00:40:41 +03:30
544624d579 Merge pull request #211 from PhasicFlow/postProcessing
pFlow -> pFlow::postprocessData
2025-04-24 23:40:34 +03:30
cbac1e97b5 pFlow -> pFlow::postprocessData 2025-04-24 23:39:31 +03:30
8c543e1649 Merge pull request #210 from PhasicFlow/postProcessing
Post processing readme.md
2025-04-24 23:32:46 +03:30
be807e4a71 change of namespace from pFlow to pFlow::postprocessData 2025-04-24 23:31:43 +03:30
d5ea338ab3 spell check readme.md 2025-04-24 14:41:31 +03:30
a448ce5f8d minor changes to readme.md 2025-04-24 14:28:28 +03:30
e2582f5fd9 minor change to readme.md 2025-04-24 14:18:08 +03:30
f2e8e69899 prime2 is added and readme update 2025-04-24 14:08:17 +03:30
5f8ea2d841 fixed selectorStride bug 2025-04-22 14:46:12 +08:00
3d0f31a2b2 Merge pull request #3 from hamidrezanorouzi/MPIdev
Mp idev
2024-11-22 22:40:25 +03:30
HRN
ba8f307c56 Code recovery MPI part 2024-10-18 23:10:42 +03:30
HRN
f317cef0ef Merge branch 'develop' into MPIdev after first commit after code loss 2024-09-21 13:39:17 +03:30
HRN
93617a6ee5 MPI developement zeroTime 2024-08-07 15:13:15 +03:30
HRN
d7e6292e41 Merge branch 'develop' into MPIdev 2024-05-24 20:51:13 +03:30
HRN
5a25556b8a changing blocking-recive to non-blocking (in boundaryProcessor) to improve MPI run 2024-05-24 00:10:15 +03:30
HRN
a0c51e94e1 Merge branch 'develop' into MPIdev 2024-05-23 22:04:47 +03:30
74c281617b Merge pull request #2 from hamidrezanorouzi/MPIdev
Mp idev
2024-05-22 10:08:38 +03:30
d2003b35e6 bug fix to work with CUDA run 2024-05-22 09:52:48 +03:30
e05bd2c350 Merge branch 'develop' into MPIdev 2024-05-18 19:14:01 +03:30
HRN
665879f8ca Data transfer between processors
- Data transfer is done
- contact search at processor boundary is done
- contact force calculation at processor boundary is done
- tests have been done on rotating drum using serial and openMP
2024-05-12 19:06:53 +03:30
HRN
e756d471ba Merge branch 'develop' into MPIdev 2024-05-05 23:07:48 +03:30
30d5349fcf Merge pull request #1 from hamidrezanorouzi/MPIdev
Mp idev
2024-05-05 23:02:16 +03:30
HRN
68b7d141fa boundaryProcessor -> transferData
- point data is being transferred (no notification yet).
- field data should be transferred
2024-05-05 22:54:12 +03:30
HRN
525e972c20 Merge branch 'develop' into MPIdev 2024-05-05 18:46:33 +03:30
HRN
6f48eca95b The problem with memory leak in MPI data transfer fixed and tested. 2024-04-30 00:28:29 +03:30
HRN
b5a81bc0fc Merge branch 'MPI' of github.com:hamidrezanorouzi/phasicFlowMPI into MPIdev 2024-04-28 19:13:54 +03:30
d0798dfc0b clean up of un-used codes 2024-04-27 09:11:09 -07:00
729c088c9f Merge branch 'develop' into MPI 2024-04-27 09:05:49 -07:00
6241fa6dd3 MPI particle id handler 2024-04-27 08:59:13 -07:00
5f90605a41 MPI-boundaries for processor 2024-04-27 08:55:00 -07:00
94fcc3d01b MPI devleopment with boundaries for contact search and data communication, memory leak 2024-04-27 08:44:35 -07:00
1321e6340e Merge branch 'develop' into MPI 2024-04-20 06:16:28 -07:00
5864cea7e8 Merge branch 'develop' into MPI
- This is the merge from develop branch to get latest update from the this branch to start MPI developemet.
- This stage boundaries and data exchange between processors through boundaries are handled.
2024-04-18 10:19:19 -07:00
656e03de36 bug fix for empty domains in partitioning (tested) 2024-01-21 13:23:45 -08:00
41187b0e03 Merge branch 'develop' into MPI 2024-01-20 12:34:26 -08:00
6969b71cc5 MPI code 2024-01-20 11:30:49 -08:00
3634 changed files with 328496 additions and 2046 deletions

153
.github/scripts/sync-wiki.py vendored Executable file
View File

@ -0,0 +1,153 @@
#!/usr/bin/env python3
import os
import re
import yaml
import sys
# Constants
REPO_URL = "https://github.com/PhasicFlow/phasicFlow"
REPO_PATH = os.path.join(os.environ.get("GITHUB_WORKSPACE", ""), "repo")
WIKI_PATH = os.path.join(os.environ.get("GITHUB_WORKSPACE", ""), "wiki")
MAPPING_FILE = os.path.join(REPO_PATH, "doc/mdDocs/markdownList.yml")
def load_mapping():
"""Load the markdown to wiki page mapping file."""
try:
with open(MAPPING_FILE, 'r') as f:
data = yaml.safe_load(f)
return data.get('mappings', [])
except Exception as e:
print(f"Error loading mapping file: {e}")
return []
def convert_relative_links(content, source_path):
"""Convert relative links in markdown content to absolute URLs."""
# Find markdown links with regex pattern [text](url)
md_pattern = r'\[([^\]]+)\]\(([^)]+)\)'
# Find HTML img tags
img_pattern = r'<img\s+src=[\'"]([^\'"]+)[\'"]'
def replace_link(match):
link_text = match.group(1)
link_url = match.group(2)
# Skip if already absolute URL or anchor
if link_url.startswith(('http://', 'https://', '#', 'mailto:')):
return match.group(0)
# Get the directory of the source file
source_dir = os.path.dirname(source_path)
# Create absolute path from repository root
if link_url.startswith('/'):
# If link starts with /, it's already relative to repo root
abs_path = link_url
else:
# Otherwise, it's relative to the file location
abs_path = os.path.normpath(os.path.join(source_dir, link_url))
if not abs_path.startswith('/'):
abs_path = '/' + abs_path
# Convert to GitHub URL
github_url = f"{REPO_URL}/blob/main{abs_path}"
return f"[{link_text}]({github_url})"
def replace_img_src(match):
img_src = match.group(1)
# Skip if already absolute URL
if img_src.startswith(('http://', 'https://')):
return match.group(0)
# Get the directory of the source file
source_dir = os.path.dirname(source_path)
# Create absolute path from repository root
if img_src.startswith('/'):
# If link starts with /, it's already relative to repo root
abs_path = img_src
else:
# Otherwise, it's relative to the file location
abs_path = os.path.normpath(os.path.join(source_dir, img_src))
if not abs_path.startswith('/'):
abs_path = '/' + abs_path
# Convert to GitHub URL (use raw URL for images)
github_url = f"{REPO_URL}/raw/main{abs_path}"
return f'<img src="{github_url}"'
# Replace all markdown links
content = re.sub(md_pattern, replace_link, content)
# Replace all img src tags
content = re.sub(img_pattern, replace_img_src, content)
return content
def process_file(source_file, target_wiki_page):
"""Process a markdown file and copy its contents to a wiki page."""
source_path = os.path.join(REPO_PATH, source_file)
target_path = os.path.join(WIKI_PATH, f"{target_wiki_page}.md")
print(f"Processing {source_path} -> {target_path}")
try:
# Check if source exists
if not os.path.exists(source_path):
print(f"Source file not found: {source_path}")
return False
# Read source content
with open(source_path, 'r') as f:
content = f.read()
# Convert relative links
content = convert_relative_links(content, source_file)
# Write to wiki page
with open(target_path, 'w') as f:
f.write(content)
return True
except Exception as e:
print(f"Error processing {source_file}: {e}")
return False
def main():
# Check if wiki directory exists
if not os.path.exists(WIKI_PATH):
print(f"Wiki path not found: {WIKI_PATH}")
sys.exit(1)
# Load mapping
mappings = load_mapping()
if not mappings:
print("No mappings found in the mapping file")
sys.exit(1)
print(f"Found {len(mappings)} mappings to process")
# Process each mapping
success_count = 0
for mapping in mappings:
source = mapping.get('source')
target = mapping.get('target')
if not source or not target:
print(f"Invalid mapping: {mapping}")
continue
if process_file(source, target):
success_count += 1
print(f"Successfully processed {success_count} of {len(mappings)} files")
# Exit with error if any file failed
if success_count < len(mappings):
sys.exit(1)
if __name__ == "__main__":
main()

60
.github/workflows/sync-wiki.yml vendored Normal file
View File

@ -0,0 +1,60 @@
name: Sync-Wiki
on:
push:
branches:
- main
paths:
- "**/*.md"
- ".github/workflows/sync-wiki.yml"
- "doc/mdDocs/markdownList.yml"
- ".github/scripts/sync-wiki.py"
workflow_dispatch:
jobs:
sync-wiki:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
with:
path: repo
- name: Checkout Wiki
uses: actions/checkout@v3
with:
repository: ${{ github.repository }}.wiki
path: wiki
continue-on-error: true
- name: Create Wiki Directory if Not Exists
run: |
if [ ! -d "wiki" ]; then
mkdir -p wiki
cd wiki
git init
git config user.name "${{ github.actor }}"
git config user.email "${{ github.actor }}@users.noreply.github.com"
git remote add origin "https://github.com/${{ github.repository }}.wiki.git"
fi
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: pip install pyyaml
- name: Sync markdown files to Wiki
run: |
python $GITHUB_WORKSPACE/repo/.github/scripts/sync-wiki.py
env:
GITHUB_REPOSITORY: ${{ github.repository }}
- name: Push changes to wiki
run: |
cd wiki
git config user.name "${{ github.actor }}"
git config user.email "${{ github.actor }}@users.noreply.github.com"
git add .
if git status --porcelain | grep .; then
git commit -m "Auto sync wiki from main repository"
git push --set-upstream https://${{ github.actor }}:${{ github.token }}@github.com/${{ github.repository }}.wiki.git master -f
else
echo "No changes to commit"
fi

8
.gitignore vendored
View File

@ -37,11 +37,15 @@
*.out
*.app
# directories
# Exclude specific directories wherever they appear
build/**
include/**
bin/**
lib/**
**/build/
**/include/
**/bin/
**/lib/
test*/**
**/**notnow
doc/code-documentation/
@ -61,3 +65,5 @@ doc/DTAGS
**/[0-9]*.[0-9][0-9][0-9][0-9][0-9][0-9][0-9]
**/[0-9]*.[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]
**/VTK

View File

@ -96,7 +96,7 @@ public:
return Control_();
}
auto inline constexpr usingDoulle()const
auto inline constexpr usingDouble()const
{
return pFlow::usingDouble__;
}

View File

@ -0,0 +1 @@
# Helical Mixer Benchmark (phasicFlow v-1.0)

7
benchmarks/readme.md Normal file
View File

@ -0,0 +1,7 @@
# Benchmarks
Benchmakrs has been done on two different simulations: a simulation with simple geometry (rotating drum) and a simulation with complex geometry (helical mixer).
- [rotating drum](./rotatingDrum/readme.md)
- [helical mixer](./helicalMixer/readme.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

View File

@ -0,0 +1,96 @@
# Rotating Drum Benchmark (phasicFlow v-1.0)
## Overview
This benchmark compares the performance of phasicFlow with a well-stablished commercial DEM software for simulating a rotating drum with varying particle counts (250k to 8M particles). The benchmark measures both computational efficiency and memory usage across different hardware configurations.
## Simulation Setup
<div align="center">
<img src="./images/commericalDEMsnapshot.png"/>
<div align="center">
<p>Figure 1. Commercial DEM simulation snapshot</p>
</div>
</div>
<div align="center">
<img src="./images/phasicFlow_snapshot.png"/>
<div align="center">
<p>Figure 2. phasicFlow simulation snapshot and visualized using Paraview</p>
</div>
</div>
### Hardware Specifications
<div align="center">
Table 1. Hardware specifications used for benchmarking.
</div>
| System | CPU | GPU | Operating System |
| :---------: | :----------------------: | :--------------------------: | :--------------: |
| Laptop | Intel i9-13900HX 2.2 GHz | NVIDIA GeForce RTX 4050Ti 6G | Windows 11 24H2 |
| Workstation | Intel Xeon 4210 2.2 GHz | NVIDIA RTX A4000 16G | Ubuntu 22.04 |
### Simulation Parameters
<div align="center">
Table 2. Parameters for rotating drum simulations.
</div>
| Case | Particle Diameter | Particle Count | Drum Length | Drum Radius |
| :-------: | :---------------: | :--------------: | :------------------: | :------------------: |
| 250k | 6 mm | 250,000 | 0.8 m | 0.2 m |
| 500k | 5 mm | 500,000 | 0.8 m | 0.2 m |
| 1M | 4 mm | 1,000,000 | 0.8 m | 0.2 m |
| 2M | 3 mm | 2,000,000 | 1.2 m | 0.2 m |
| 4M | 3 mm | 4,000,000 | 1.6 m | 0.2 m |
| 8M | 2 mm | 8,000,000 | 1.6 m | 0.2 m |
The time step for all simulations was set to 1.0e-5 seconds and the simulation ran for 4 seconds.
## Performance Comparison
### Execution Time
<div align="center">
Table 3. Total calculation time (minutes) for different configurations.
</div>
| Software | 250k | 500k | 1M | 2M | 4M | 8M |
| :---------------: | :----: | :-----: | :-----: | :-----: | :-----: | :------: |
| phasicFlow-4050Ti | 54 min | 111 min | 216 min | 432 min | - | - |
| Commercial DEM-4050Ti | 68 min | 136 min | 275 min | 570 min | - | - |
| phasicFlow-A4000 | 38 min | 73 min | 146 min | 293 min | 589 min | 1188 min |
The execution time scales linearly with particle count. phasicFlow demonstrates approximately:
- 20% faster calculation than the well-established commercial DEM software on the same hardware
- 30% performance improvement when using the NVIDIA RTX A4000 compared to the RTX 4050Ti
<div align="center">
<img src="./images/performance1.png"/>
<p>Figure 3. Calculation time comparison between phasicFlow and the well-established commercial DEM software.</p>
</div>
### Memory Usage
<div align="center">
Table 4. Memory consumption for different configurations.
</div>
| Software | 250k | 500k | 1M | 2M | 4M | 8M |
| :---------------: | :-----: | :-----: | :-----: | :-----: | :-----: | :-----: |
| phasicFlow-4050Ti | 252 MB | 412 MB | 710 MB | 1292 MB | - | - |
| Commercial DEM-4050Ti | 485 MB | 897 MB | 1525 MB | 2724 MB | - | - |
| phasicFlow-A4000 | 344 MB | 480 MB | 802 MB | 1386 MB | 2590 MB | 4966 MB |
Memory efficiency comparison:
- phasicFlow uses approximately 0.7 GB of memory per million particles
- Commercial DEM software uses approximately 1.2 GB of memory per million particles
- phasicFlow shows ~42% lower memory consumption compared to the commercial alternative
- The memory usage scales linearly with particle count in both software packages. But due to memory limitations on GPUs, it is possible to run larger simulation on GPUs with phasicFlow.
## Run Your Own Benchmarks
The simulation case setup files are available in this folder for users interested in performing similar benchmarks on their own hardware. These files can be used to reproduce the tests and compare performance across different systems.

View File

@ -35,7 +35,7 @@ surfaces
radius2 0.2; // radius at p2
resolution 24; // number of divisions
resolution 60; // number of divisions
material wallMat; // material name of this wall

View File

@ -27,7 +27,7 @@ positionParticles
orderedInfo
{
diameter 0.004; // minimum space between centers of particles
distance 0.004; // minimum space between centers of particles
numPoints 1000000; // number of particles in the simulation

View File

@ -35,7 +35,7 @@ surfaces
radius2 0.2; // radius at p2
resolution 24; // number of divisions
resolution 60; // number of divisions
material wallMat; // material name of this wall

View File

@ -27,7 +27,7 @@ positionParticles
orderedInfo
{
diameter 0.006; // minimum space between centers of particles
distance 0.006; // minimum space between centers of particles
numPoints 250000; // number of particles in the simulation

View File

@ -35,7 +35,7 @@ surfaces
radius2 0.2; // radius at p2
resolution 24; // number of divisions
resolution 60; // number of divisions
material wallMat; // material name of this wall

View File

@ -27,7 +27,7 @@ positionParticles
orderedInfo
{
diameter 0.003; // minimum space between centers of particles
distance 0.003; // minimum space between centers of particles
numPoints 2000000; // number of particles in the simulation

View File

@ -35,7 +35,7 @@ surfaces
radius2 0.2; // radius at p2
resolution 24; // number of divisions
resolution 60; // number of divisions
material wallMat; // material name of this wall

View File

@ -27,7 +27,7 @@ positionParticles
orderedInfo
{
diameter 0.003; // minimum space between centers of particles
distance 0.003; // minimum space between centers of particles
numPoints 4000000; // number of particles in the simulation

View File

@ -35,7 +35,7 @@ surfaces
radius2 0.2; // radius at p2
resolution 24; // number of divisions
resolution 60; // number of divisions
material wallMat; // material name of this wall

View File

@ -27,7 +27,7 @@ positionParticles
orderedInfo
{
diameter 0.005; // minimum space between centers of particles
distance 0.005; // minimum space between centers of particles
numPoints 500000; // number of particles in the simulation

View File

@ -35,7 +35,7 @@ surfaces
radius2 0.2; // radius at p2
resolution 24; // number of divisions
resolution 60; // number of divisions
material wallMat; // material name of this wall

View File

@ -27,7 +27,7 @@ positionParticles
orderedInfo
{
diameter 0.003; // minimum space between centers of particles
distance 0.003; // minimum space between centers of particles
numPoints 6000000; // number of particles in the simulation

View File

@ -19,7 +19,7 @@ export pFlow_SRC_DIR="$pFlow_PROJECT_DIR/src"
export Kokkos_DIR="$kokkosDir"
export Zoltan_DIR="$projectDir/Zoltan"
#export Zoltan_DIR="$projectDir/Zoltan"
# Cleanup variables (done as final statement for a clean exit code)
unset projectDir

View File

@ -0,0 +1,44 @@
# Macro to check for Zoltan installation and build it if needed
# Usage: zoltan_find_or_build(ZOLTAN_DIR)
# Returns: ZOLTAN_INCLUDE_DIR, ZOLTAN_LIBRARY
macro(zoltan_find_or_build ZOLTAN_DIR)
# Set the Zoltan directory
set(ZOLTAN_PREFIX "${ZOLTAN_DIR}" CACHE STRING "Zoltan install directory")
message(STATUS "Zoltan install directory is ${ZOLTAN_PREFIX}")
# Check if the Zoltan library is already built
find_path(ZOLTAN_INCLUDE_DIR zoltan.h PATHS "${ZOLTAN_PREFIX}/include")
message(STATUS "Zoltan include path: ${ZOLTAN_INCLUDE_DIR}")
find_library(ZOLTAN_LIBRARY zoltan PATHS "${ZOLTAN_PREFIX}/lib")
message(STATUS "Zoltan lib path: ${ZOLTAN_LIBRARY}")
# Check if Zoltan library exists, if not compile it using buildlib script
if(NOT ZOLTAN_LIBRARY)
message(STATUS "Zoltan library not found. Compiling from source using buildlib script...")
# Execute the buildlib bash script
execute_process(
COMMAND bash ${ZOLTAN_PREFIX}/buildlib
WORKING_DIRECTORY ${ZOLTAN_PREFIX}
RESULT_VARIABLE ZOLTAN_BUILD_RESULT
OUTPUT_VARIABLE ZOLTAN_BUILD_OUTPUT
ERROR_VARIABLE ZOLTAN_BUILD_ERROR
)
if(NOT ZOLTAN_BUILD_RESULT EQUAL 0)
message(FATAL_ERROR "Failed to build Zoltan library using buildlib script. Error: ${ZOLTAN_BUILD_ERROR}")
endif()
# Try to find the library again after building
find_library(ZOLTAN_LIBRARY zoltan PATHS "${ZOLTAN_PREFIX}/lib" NO_DEFAULT_PATH)
find_path(ZOLTAN_INCLUDE_DIR zoltan.h PATHS "${ZOLTAN_PREFIX}/include" NO_DEFAULT_PATH)
if(NOT ZOLTAN_LIBRARY)
message(FATAL_ERROR "Failed to locate Zoltan library after building")
endif()
message(STATUS "Successfully built Zoltan library at ${ZOLTAN_LIBRARY}")
endif()
endmacro()

View File

@ -0,0 +1,136 @@
# How to build PhasicFlow-v-1.0
You can build PhasicFlow for CPU or GPU. You can have a single build or oven multiple builds on a machine. Here you learn how to have a single build of PhasicFlow, in various modes of execution. You can install PhasicFlow-v-1.0 on **Ubuntu-22.04 LTS** and **Ubuntu-24.04 LTS**. Installing it on older versions of Ubuntu needs some additional steps to meet the requirements which are not covered here.
If you want to install PhasicFlow on **Windows OS**, just see [this page](https://www.cemf.ir/installing-phasicflow-v-1-0-on-ubuntu/) for more information.
# Required packages
You need a list of packages installed on your computer before building PhasicFlow:
* git, for cloning the code and package management
* g++, for compiling the code
* cmake, for generating build system
* Cuda-12.x or above (if GPU is targeted), for compiling the code for CUDA execution.
### Installing packages
Execute the following commands to install the required packages (Except Cuda). tbb is installed automatically.
```bash
sudo apt update
sudo apt install -y git g++ cmake cmake-qt-gui
```
### Installing Cuda for GPU execution
If you want to build PhasicFlow to be executed on an nvidia-GPU, you need to install the latest version of Cuda compiler (Version 12.x or above), which is compatible with your hardware and OS, on your computer.
# How to build?
Here you will learn how to build PhasicFlow for single execution mode. Follow the steps below to install it on your computer.
Tested operating systems are:
* Ubuntu-22.04 LTS
* Ubuntu-24.04 LTS
### Step 1: Package check
Make sure that you have installed all the required packages on your computer. See above for more information.
### Step 2: Cloning PhasicFlow
Create the PhasicFlow folder in your home folder and then clone the source code into that folder:
```bash
cd ~
mkdir PhasicFlow
cd PhasicFlow
git clone https://github.com/PhasicFlow/phasicFlow.git
mv phasicFlow phasicFlow-v-1.0
```
### Step 3: Environmental variables
Opne the bashrc file using the following command:
```bash
$ gedit ~/.bashrc
```
and add the following line to the end of the file, **save** and **close** it.
```bash
source $HOME/PhasicFlow/phasicFlow-v-1.0/cmake/bashrc
```
this will introduce a new source file for setting the environmental variables of PhasicFlow. If you want to load these variables in the current open terminal, you need to source it. Or, simply **close the terminal** and **open a new terminal**.
### Step 4: Building PhasicFlow
Follow one of the followings to build PhasicFlow for one mode of execution.
#### Serial build for CPU
In a **new terminal** enter the following commands:
```bash
cd ~/PhasicFlow/phasicFlow-v-1.0
mkdir build
cd build
cmake ../ -DpFlow_Build_Serial=On -DCMAKE_BUILD_TYPE=Release
make install -j4
```
For faster builds, use `make install -j`. This will use all the CPU cores on your computer for building.
#### OpenMP build for CPU
```bash
cd ~/PhasicFlow/phasicFlow-v-1.0
mkdir build
cd build
cmake ../ -DpFlow_Build_OpenMP=On -DCMAKE_BUILD_TYPE=Release
make install -j4
```
#### GPU build for parallel execution on CUDA-enabled GPUs
```bash
cd ~/PhasicFlow/phasicFlow-v-1.0
mkdir build
cd build
cmake ../ -DpFlow_Build_Cuda=On -DCMAKE_BUILD_TYPE=Release
cmake ../ -DpFlow_Build_Cuda=On -DCMAKE_BUILD_TYPE=Release
make install -j4
```
After building, `bin`, `include`, and `lib` folders will be created in `~/PhasicFlow/phasicFlow-v-1.0/` folder. Now you are ready to use PhasicFlow.
**note 1**: When compiling the code in parallel, you need to have enough RAM on your computer. As a rule, you need 1 GB free RAM per each processor on your computer for compiling in parallel.
You may want to use fewer number of cores on your computer by using the following command:
```bash
make install -j3
```
the above command only uses 3 cores for compiling.
**note 2**: By default PhasicFlow is compiled with **double** as floating point variable. You can compile it with **float**. Just in the command line of camke added `-DpFlow_Build_Double=Off` flag to compile it with float. For example if you are building for cuda, you can enter the following command:
```bash
cmake ../ -DpFlow_Build_Cuda=On -DpFlow_Build_Double=Off
```
### Step 5: Testing
In the current terminal or a new terminal enter the following command:
```bash
checkPhasicFlow
```
This command shows the host and device environments and software version. If PhasicFlow was build correctly, you would get the following output:
```
Initializing host/device execution spaces . . .
Host execution space is Serial
Device execution space is Serial
You are using phasicFlow v-1.0 (copyright(C): www.cemf.ir)
In this build, double is used for floating point operations and uint32for indexing.
This is not a build for MPI execution
Finalizing host/device execution space ....
```

View File

@ -1,151 +0,0 @@
# How to build PhasicFlow {#howToBuildPhasicFlow}
You can build PhasicFlow for CPU or GPU. You can have a single build or oven multiple builds on a machine. Here you learn how to have a single build of PhasicFlow, in various modes of execution.
# Required packages
You need a list of packaged installed on your computer before building PhasicFlow:
* git, for cloning the code and package management
* g++, for compiling the code
* cmake, for generating build system
* tbb, a parallel library for STL algorithms
* Cuda (if GPU is targeted), for compiling the code for CUDA execution.
* Kokkos, the parallelization backend of PhasicFlow
### git
if git is not installed on your computer, enter the following commands
```
$ sudo apt update
$ sudo apt install git
```
### g++ (C++ compiler)
The code is tested with g++ (gnu C++ compiler). The default version of g++ on Ubuntu 18.04 LTS or upper is sufficient for compiling. If it is not installed on your operating system, enter the following command:
```
$ sudo apt update
$ sudo apt install g++
```
### CMake
You also need to have CMake-3.22 or higher installed on your computer.
```
$ sudo apt update
$ sudo apt install cmake
```
### tbb (2020.1-2 or higher)
For **Ubuntu 20.04 LTS or higher versions**, you can install tbb using apt. For now, some parallel algorithms on host side rely on tbb parallel library (C++ parallel backend). Use e following commands to install it:
```
$ sudo apt update
$ sudo apt install libtbb-dev
```
If you are compiling on **Ubuntu-18.04 LTS**, you need to enter the following commands to get the right version (2020.1-2 or higher) of tbb:
```
$ wget "http://archive.ubuntu.com/ubuntu/pool/universe/t/tbb/libtbb2_2020.1-2_amd64.deb"
$ sudo dpkg --install libtbb2_2020.1-2_amd64.deb
$ wget "http://archive.ubuntu.com/ubuntu/pool/universe/t/tbb/libtbb-dev_2020.1-2_amd64.deb"
$ sudo dpkg --install libtbb-dev_2020.1-2_amd64.deb
```
### Cuda
If you want to build PhasicFlow to be executed on an nvidia-GPU, you need to install the latest version of Cuda compiler, which is compatible with your hardware and OS, on your computer.
# How to build?
Here you will learn how to build PhasicFlow for single execution mode. Follow the steps below to install it on your computer.
Tested operating systems are:
* Ubuntu 18.04 LTS
* Ubuntu 20.04 LTS
* Ubuntu 22.04 LTS
### Step 1: Package check
Make sure that you have installed all the required packages on your computer. See above for more information.
### Step 2: Cloning Kokkos
It is assumed that Kokkos source is located in the home folder of your computer. Clone the latest version of Kokkos into your home folder:
```
$ cd ~
$ mkdir Kokkos
$ cd Kokkos
$ git clone https://github.com/kokkos/kokkos.git
```
or simply download and extract the source code of Kokkos in `~/Kokkos` folder. In the end, the top level CMakeLists.txt file should be located in `~/Kokkos/kokkos` folder.
### Step 3: Cloning PhasicFlow
Create the PhasicFlow folder in your home folder and then clone the source code into that folder:
```
$ cd ~
$ mkdir PhasicFlow
$ cd PhasicFlow
$ git clone https://github.com/PhasicFlow/phasicFlow.git
```
### Step 4: Environmental variables
Opne the bashrc file using the following command:
`$ gedit ~/.bashrc`
and add the following line to the end of the file, **save** and **close** it.
`source $HOME/PhasicFlow/phasicFlow/cmake/bashrc`
this will introduce a new source file for setting the environmental variables of PhasicFlow. If you want to load these variables in the current open terminal, you need to source it. Or, simply **close the terminal** and **open a new terminal**.
### Step 5: Building PhasicFlow
Follow one of the followings to build PhasicFlow for one mode of execution.
#### Serial build for CPU
In a **new terminal** enter the following commands:
```
$ cd ~/PhasicFlow/phasicFlow
$ mkdir build
$ cd build
$ cmake ../ -DpFlow_Build_Serial=On
$ make install
```
For faster builds, use `make install -j`. This will use all the CPU cores on your computer for building.
#### OpenMP build for CPU
```
$ cd ~/PhasicFlow/phasicFlow
$ mkdir build
$ cd build
$ cmake ../ -DpFlow_Build_OpenMP=On
$ make install
```
#### GPU build for parallel execution on CUDA-enabled GPUs
```
$ cd ~/PhasicFlow/phasicFlow
$ mkdir build
$ cd build
$ cmake ../ -DpFlow_Build_Cuda=On
$ make install
```
After building, `bin`, `include`, and `lib` folders will be created in `~/PhasicFlow/phasicFlow/` folder. Now you are ready to use PhasicFlow.
**note 1**: When compiling the code in parallel, you need to have enough RAM on your computer. As a rule, you need 1 GB free RAM per each processor in your computer for compiling in parallel.
You may want to use fewer number of cores on your computer by using the following command:
`$ make install -j 3`
the above command only uses 3 cores for compiling.
**note 2**: By default PhasicFlow is compiled with **double** as floating point variable. You can compile it with **float**. Just in the command line of camke added `-DpFlow_Build_Double=Off` flag to compile it with float. For example if you are building for cuda, you can enter the following command:
`$ cmake ../ -DpFlow_Build_Cuda=On -DpFlow_Build_Double=Off`
### Step 6: Testing
In the current terminal or a new terminal enter the following command:
`$ checkPhasicFlow`
This command shows the host and device environments and software version. If PhasicFlow was build correctly, you would get the following output:
```
Initializing host/device execution spaces . . .
Host execution space is Serial
Device execution space is Cuda
ou are using phasicFlow v-0.1 (copyright(C): www.cemf.ir)
In this build, double is used for floating point operations.
Finalizing host/device execution space ....
```

View File

@ -0,0 +1,18 @@
# This file maps source markdown files to their target wiki pages
# format:
# - source: path/to/markdown/file.md
# target: Wiki-Page-Name
mappings:
- source: benchmarks/readme.md
target: Performance-of-phasicFlow
- source: benchmarks/helicalMixer/readme.md
target: Helical-Mixer-Benchmark
- source: benchmarks/rotatingDrum/readme.md
target: Rotating-Drum-Benchmark
- source: doc/mdDocs/howToBuild-V1.0.md
target: How-to-build-PhasicFlowv1.0
- source: tutorials/README.md
target: Tutorials
- source: doc/mdDocs/phasicFlowFeatures.md
target: Features-of-PhasicFlow
# Add more mappings as needed

View File

@ -1,64 +1,116 @@
# PhasicFlow Features {#phasicFlowFeatures}
# PhasicFlow Features (v-1.0)
The features of PhasicFlow described here are the main features that are implemented in the code for version 1.0. This document is not a complete list of all the features of PhasicFlow. The features are being added to the code continuously and this document may be behind the latest updates. Of course, the code review will give you the complete list.
## Table of Contents
- [1. Building options](#1-building-options)
- [2. Preprocessing tools](#2-preprocessing-tools)
- [3. Solvers for simulations](#3-solvers-for-simulations)
- [4. Postprocessing tools](#4-postprocessing-tools)
- [5. Models and features for simulations](#5-models-and-features-for-simulations)
- [5.1. General representation of walls](#51-general-representation-of-walls)
- [5.2. High precision integeration methods](#52-high-precision-integeration-methods)
- [5.3. Contact force models](#53-contact-force-models-needs-improvement)
- [5.4. Particle insertion](#54-particle-insertion)
- [5.5. Restarting/resuming a simulation](#55-restartingresuming-a-simulation)
- [5.6. Postprocessing data during simulation](#56-postprocessing-data-during-simulation)
## 1. Building options
You can build PhasicFlow to be executed on multi-core CPUs or GPUs. It is also possible to select the type of floating point variables in PhasicFlow: double or float. float type requires less memory and mostly consumes less time of a processor to complete a mathematical operation. So, there is a benefit for using floats in DEM simulation specially when GPU is targeted for computations.
## Building options
You can build PhasicFlow to be executed on multi-core CPUs or GPUs. It is also possible to select the type of floating point variables in PhasicFlow: double or float. float type requires less memory and mostly consumes less time of a processor to complete a mathematical operation. So, there is a benefit for using floats in DEM simulation specially when GPU is targeted for computations.
Build options for PhasicFlow:
* **serial (double or float type)**: execution on one cpu core
* **OpenMp (double or float type)**: execution on multiple cores of a CPU
* **cuda (double or float type)**: execution on cuda-enabled GPUs
- **serial (double or float type)**: execution on one cpu core
- **OpenMp (double or float type)**: execution on multiple cores of a CPU
- **cuda (double or float type)**: execution on cuda-enabled GPUs
for more information on building PhasicFlow, please refer to the [installation guide](./howToBuild-V1.0.md).
## Preprocessing tools
Preprocessing tools are used to facilitate the process of case setup. They include tools for defining initial state of particles and geometry conversion.
* **particlesPhasicFlow** tool can be used to define the initial position of particles (for example at t = 0 s) and to set the initial field values for particles (like velocity, orientation, acceleration and etc).
* **geometryPhasicFlow** converts user inputs for walls into a data structures that is used by PhasicFlow.
## 2. Preprocessing tools
PhasicFlow provides a set of tools for preprocessing the simulation case. These tools are used to define the initial state of particles, walls and other parameters that are required for running a simulation.
- [**particlesPhasicFlow**](./../../utilities/particlesPhasicFlow/) tool can be used to define the initial position of particles (for example at t = 0 s) and to set the initial field values for particles (like velocity, orientation, acceleration, etc.).
## Models and features for simulations
- [**geometryPhasicFlow**](./../../utilities/geometryPhasicFlow/) converts user inputs for walls into a data structure that is used by PhasicFlow.
## 3. Solvers for simulations
### General representation of walls
- [**sphereGranFlow**](./../../solvers/sphereGranFlow/) is a solver for simulating the flow of spherical particles with particle insertion mechanism. A full set of tutorial on various possible simulations can be found here: [sphereGranFlow tutorial](./../../tutorials/sphereGranFlow/).
- [**grainGranFlow**](./../../solvers/grainGranFlow/) is a solver for simulating the flow of course-grained particles with particle insertion mechanism. A full set of tutorial on various possible simulations can be found here: [grainGranFlow tutorial](./../../tutorials/grainGranFlow/).
- [**iterateGeometry**](./../../solvers/iterateGeometry/) is a solver testing motion of walls without simulating particles. Since simulating with particles may take a long time and we may want to check the motion of geometry to be correct before actual simulation, we created this utility to test the motion of walls. A set of tutorial on various possible simulations can be found here: [iterateGeometry tutorial](./../../tutorials/iterateGeometry/).
## 4. Postprocessing tools
- [**pFlowToVTK**](./../../utilities/pFlowToVTK) is used to convert simulation results into vtk file format. vtk file format can be read by Paraview for visualizing the results.
- [**postprocessPhasicFlow**](./../../utilities/postprocessPhasicFlow/) is a tool for performing various averaging and summation on the fields. Particle probing is also possible.
## 5. Models and features for simulations
### 5.1. General representation of walls
Walls can be defined in three ways in PhasicFlow:
* **Builtin walls** in PhasicFlow that include plane wall, cylinder/cone wall, cuboid, circle.
* **stl wall** that reads the data of the wall from an ASCII stl file.
* **foamPatch wall** that reads the OpenFOAM mesh and converts the boundary patches into PhasicFlow walls (this feature is only available when performing CFD-DEM simulation using OpenFOAM).
Walls can be fixed or in motion during simulations. Various motion models are implemented to cover most of the wall motions in phasicFlow ([see the source code] (./../../../src/MotionModel/)):
* **fixedWall** model, in which all walls are fixed. This model is mostly useful for granular flow under gravity or gas-solid flows (CFD-DEM).
* **rotatingAxisMotion** model, in which walls are rotating around an axis of rotation with specified rotation speed. This model covers a wide range of granular flows in which the whole or a part of geometry is rotating, like mixers.
* **multiRotatingAxisMotion** model, in which a combination of rotations can be specified. One axis of rotation can itself have another axis of rotation, and so on. This creates the possibility of defining very complex motion pattern for walls, like what we see in Nauta blenders.
* **vibratingMotion** model, in which walls vibrates based on a sinusoidal model with specified frequency and amplitude.
In addition to these models, the user can add other motion models to the code based on their need.
- **Builtin walls** in PhasicFlow that include plane wall, cylinder/cone wall, cuboid, circle.
- **stl wall** that reads the data of the wall from an ASCII stl file.
- **foamPatch wall** that reads the OpenFOAM mesh and converts the boundary patches into PhasicFlow walls (this feature is only available when performing CFD-DEM simulation using OpenFOAM).
Walls can be fixed or in motion during simulations. Various motion models are implemented to cover most of the wall motions in phasicFlow ([see the source code](./../../src/MotionModel/)):
- **stationay** model, in which all walls are fixed. This model is mostly useful for granular flow under gravity or gas-solid flows (CFD-DEM).
- **rotatingAxis** model, in which walls are rotating around an axis of rotation with specified rotation speed. This model covers a wide range of granular flows in which the whole or a part of geometry is rotating, like mixers.
- **multiRotatingAxis** model, in which a combination of rotations can be specified. One axis of rotation can itself have another axis of rotation, and so on. This creates the possibility of defining very complex motion pattern for walls, like what we see in Nauta blenders.
- **vibrating** model, in which walls vibrates based on a sinusoidal model with specified frequency and amplitude.
In addition to these models, the user can add other motion models to the code based on their need.
### 5.2. High precision integeration methods
The precision of integration in a DEM simulation is very important. Since sudden changes in the interaction forces occur during simulations (when objects contact or when they rebound). High precision integration methods makes it possible to accurately track position and velocity of objects (specially when they are in contact). When using these methods, it is possible to choose larger time steps for integration without loosing accuracy and causing instability in the simulation. Although a high-precision integration requires more computations, but the benefits of choosing larger time steps in simulation can totally compensate it.
### High precision integeration methods
The precision of integration in a DEM simulation is very important. Since sudden changes in the interaction forces occur during simulations (when objects contact or when they rebound). High precision integration methods makes it possible to accurately track position and velocity of objects (specially when they are in contact). When using these methods, it is possible to choose larger time steps for integration without loosing accuracy and causing instability in the simulation. Although a high-precision integration requires more computations, but the benefits of choosing larger time steps in simulation can totally compensate it.
Various integration methods are implemented in PhasicFlow:
| Integration Method | Order | Type|
|Integration Method | Order | Type|
| :--- | :---: | :---: |
| AdamsBashforth2 | 2 | one-step |
| AdamsBashforth3 | 3 | one-step |
| AdamsBashforth4 | 4 | one-step |
| AdamsBashforth5 | 5 | one-step |
| AdamsMoulton3 | 3 | predictor-corrector |
| AdamsMoulton4 | 4 | predictor-corrector |
| AdamsMoulton5 | 5 | predictor-corrector |
| AdamsMoulton3 | 3 | predictor-corrector (not active)|
| AdamsMoulton4 | 4 | predictor-corrector (not active)|
| AdamsMoulton5 | 5 | predictor-corrector (not active)|
### 5.3. Contact force models (needs improvement)
### Contact force models
Linear and non-linear visco-elastic contact force models are considered in the simulation. In addition to these, limited and non-limited Coulomb's friction model can be used to account for the friction between objects. For spherical objects, rolling friction can also be specified between bodies in contact.
Linear and non-linear visco-elastic contact force models are considered in the simulation. In addition to these, limited and non-limited Coulomb's friction model can be used to account for the friction between objects. For spherical objects, rolling friction can also be specified between bodies in contact.
In addition, for course-grained particles simulation, we developed a speciall set of***
### Particle insertion
Particles can be inserted during simulation from specified region at specified rate and time interval. Any number of insertion regions can be defined in a simulation. Various region types are considered here: box, cylinder and sphere. Particles are inserted into the simulation through the specified region.
### 5.4. Particle insertion
### restarting/resuming a simulation
It is possible to resume a simulation fron any time-folder that is avaiable in the simulation case setup directory. PhasicFlow restart the simulation from that time folder.
Particles can be inserted during simulation from specified region at specified rate and time interval. Any number of insertion regions can be defined in a simulation. Various region types are considered here: `box`, `cylinder` and `sphere`. Particles are inserted into the simulation through the specified region.
## Postprocessing tools
### 5.5. restarting/resuming a simulation
* **pFlowToVTK** is used to convert simulation results into vtk file format. vtk file format can be read by Paraview for visualizing the results.
* **postprocessPhasicFlow** is a tool for performing various cell-based averaging on the fields.
It is possible to resume a simulation from any time-folder that is available in the simulation case setup directory. PhasicFlow restarts the simulation from that time folder.
### 5.6. Postprocessing data during simulation
PhasicFlow provides a powerful in-simulation postprocessing module that allows users to analyze particle data in real-time while the simulation is running. This feature enables:
- **Real-time data analysis** without waiting for simulation completion
- **Region-based processing** in spheres, along lines, or at specific points
- **Various statistical operations** including weighted averages and sums of particle properties
- **Individual particle tracking** to monitor specific particles throughout simulation
- **Multiple processing methods** including arithmetic mean, uniform distribution, and Gaussian distribution
- **Particle filtering** based on properties like diameter, mass, etc.
- **Flexible time control** options for when postprocessing should be executed
To activate in-simulation postprocessing, users need to:
1. Create a `postprocessDataDict` file in the `settings` directory with appropriate configurations
2. Add `libs ("libPostprocessData.so")` and `auxFunctions postprocessData` to the `settings/settingsDict` file
Results are written to output files in the case directory with timestamps, allowing users to monitor simulation behavior as it progresses without interrupting the simulation. for more information on how to use this feature, please refer to the [PostprocessData](./../../src/PostprocessData/) module.
The same postprocessing module can also be used after simulation completion through the [`postprocessPhasicFlow`](./../../utilities/postprocessPhasicFlow/) utility.

View File

@ -0,0 +1,71 @@
#include "processorAB2BoundaryIntegration.hpp"
#include "AdamsBashforth2.hpp"
#include "AB2Kernels.hpp"
#include "boundaryConfigs.hpp"
pFlow::processorAB2BoundaryIntegration::processorAB2BoundaryIntegration(
const boundaryBase &boundary,
const pointStructure &pStruct,
const word &method,
integration& intgrtn
)
:
boundaryIntegration(boundary, pStruct, method, intgrtn)
{}
bool pFlow::processorAB2BoundaryIntegration::correct(
real dt,
const realx3PointField_D& y,
const realx3PointField_D& dy
)
{
#ifndef BoundaryModel1
if(this->isBoundaryMaster())
{
const uint32 thisIndex = thisBoundaryIndex();
const auto& AB2 = static_cast<const AdamsBashforth2&>(Integration());
const auto& dy1View = AB2.BoundaryField(thisIndex).neighborProcField().deviceView();
const auto& dyView = dy.BoundaryField(thisIndex).neighborProcField().deviceView();
const auto& yView = y.BoundaryField(thisIndex).neighborProcField().deviceView();
const rangeU32 aRange(0u, dy1View.size());
return AB2Kernels::intAllActive(
"AB2Integration::correct."+this->boundaryName(),
dt,
aRange,
yView,
dyView,
dy1View
);
}
#endif //BoundaryModel1
return true;
}
bool pFlow::processorAB2BoundaryIntegration::correctPStruct(real dt, const realx3PointField_D &vel)
{
#ifndef BoundaryModel1
if(this->isBoundaryMaster())
{
const uint32 thisIndex = thisBoundaryIndex();
const auto& AB2 = static_cast<const AdamsBashforth2&>(Integration());
const auto& dy1View = AB2.BoundaryField(thisIndex).neighborProcField().deviceView();
const auto& velView = vel.BoundaryField(thisIndex).neighborProcField().deviceView();
const auto& xposView = boundary().neighborProcPoints().deviceView();
const rangeU32 aRange(0u, dy1View.size());
return AB2Kernels::intAllActive(
"AB2Integration::correctPStruct."+this->boundaryName(),
dt,
aRange,
xposView,
velView,
dy1View
);
}
#endif //BoundaryModel1
return true;
}

View File

@ -0,0 +1,51 @@
#ifndef __processorAB2BoundaryIntegration_hpp__
#define __processorAB2BoundaryIntegration_hpp__
#include "boundaryIntegration.hpp"
namespace pFlow
{
class processorAB2BoundaryIntegration
:
public boundaryIntegration
{
public:
TypeInfo("boundaryIntegration<processor,AdamsBashforth2>");
processorAB2BoundaryIntegration(
const boundaryBase& boundary,
const pointStructure& pStruct,
const word& method,
integration& intgrtn
);
~processorAB2BoundaryIntegration()override=default;
bool correct(
real dt,
const realx3PointField_D& y,
const realx3PointField_D& dy)override;
bool correctPStruct(real dt, const realx3PointField_D& vel)override;
add_vCtor(
boundaryIntegration,
processorAB2BoundaryIntegration,
boundaryBase
);
};
}
#endif

View File

@ -0,0 +1,111 @@
/*------------------------------- phasicFlow ---------------------------------
O C enter of
O O E ngineering and
O O M ultiscale modeling of
OOOOOOO F luid flow
------------------------------------------------------------------------------
Copyright (C): www.cemf.ir
email: hamid.r.norouzi AT gmail.com
------------------------------------------------------------------------------
Licence:
This file is part of phasicFlow code. It is a free software for simulating
granular and multiphase flows. You can redistribute it and/or modify it under
the terms of GNU General Public License v3 or any other later versions.
phasicFlow is distributed to help others in their research in the field of
granular and multiphase flows, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----------------------------------------------------------------------------*/
#include "processorBoundaryContactSearch.hpp"
#include "contactSearch.hpp"
#include "particles.hpp"
//#include "pointStructure.hpp"
//#include "geometry.hpp"
void pFlow::processorBoundaryContactSearch::setSearchBox()
{
auto l = boundary().neighborLength();
auto n = boundary().boundaryPlane().normal();
auto pp1 = boundary().boundaryPlane().parallelPlane(l);
auto pp2 = boundary().boundaryPlane().parallelPlane(-l);
realx3 minP1 = min(min(min(pp1.p1(), pp1.p2()), pp1.p3()), pp1.p4());
realx3 maxP1 = max(max(max(pp1.p1(), pp1.p2()), pp1.p3()), pp1.p4());
realx3 minP2 = min(min(min(pp2.p1(), pp2.p2()), pp2.p3()), pp2.p4());
realx3 maxP2 = max(max(max(pp2.p1(), pp2.p2()), pp2.p3()), pp2.p4());
auto minP = min(minP1, minP2) - l*(realx3(1.0)-abs(n));
auto maxP = max(maxP1, maxP2) + l*(realx3(1.0)-abs(n));
searchBox_={minP, maxP};
}
pFlow::processorBoundaryContactSearch::processorBoundaryContactSearch(
const dictionary &dict,
const boundaryBase &boundary,
const contactSearch &cSearch)
:
boundaryContactSearch(dict, boundary, cSearch),
diameter_(cSearch.Particles().boundingSphere()),
masterSearch_(this->isBoundaryMaster()),
sizeRatio_(dict.getVal<real>("sizeRatio"))
{
if(masterSearch_)
{
setSearchBox();
real minD;
real maxD;
cSearch.Particles().boundingSphereMinMax(minD, maxD);
ppContactSearch_ = makeUnique<twoPartContactSearch>(
searchBox_,
maxD,
sizeRatio_);
}
else
{
searchBox_={{0,0,0},{0,0,0}};
}
}
bool pFlow::processorBoundaryContactSearch::broadSearch
(
uint32 iter,
real t,
real dt,
csPairContainerType &ppPairs,
csPairContainerType &pwPairs,
bool force
)
{
if(masterSearch_)
{
const auto thisPoints = boundary().thisPoints();
const auto& neighborProcPoints = boundary().neighborProcPoints();
const auto& bDiams = diameter_.BoundaryField(thisBoundaryIndex());
const auto thisDiams = bDiams.thisField();
const auto& neighborProcDiams = bDiams.neighborProcField();
ppContactSearch_().broadSearchPP(
ppPairs,
thisPoints,
thisDiams,
neighborProcPoints,
neighborProcDiams,
boundaryName()
);
//pOutput<<"ppSize "<< ppPairs.size()<<endl;
return true;
}else
{
return true;
}
}

View File

@ -0,0 +1,76 @@
/*------------------------------- phasicFlow ---------------------------------
O C enter of
O O E ngineering and
O O M ultiscale modeling of
OOOOOOO F luid flow
------------------------------------------------------------------------------
Copyright (C): www.cemf.ir
email: hamid.r.norouzi AT gmail.com
------------------------------------------------------------------------------
Licence:
This file is part of phasicFlow code. It is a free software for simulating
granular and multiphase flows. You can redistribute it and/or modify it under
the terms of GNU General Public License v3 or any other later versions.
phasicFlow is distributed to help others in their research in the field of
granular and multiphase flows, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----------------------------------------------------------------------------*/
#ifndef __processorBoundaryContactSearch_hpp__
#define __processorBoundaryContactSearch_hpp__
#include "boundaryContactSearch.hpp"
#include "pointFields.hpp"
#include "twoPartContactSearch.hpp"
namespace pFlow
{
class processorBoundaryContactSearch : public boundaryContactSearch
{
private:
box searchBox_;
uniquePtr<twoPartContactSearch> ppContactSearch_ = nullptr;
const realPointField_D& diameter_;
bool masterSearch_;
real sizeRatio_;
void setSearchBox();
public:
TypeInfo("boundaryContactSearch<MPI,processor>")
processorBoundaryContactSearch(
const dictionary& dict,
const boundaryBase& boundary,
const contactSearch& cSearch
);
~processorBoundaryContactSearch() override = default;
add_vCtor(
boundaryContactSearch,
processorBoundaryContactSearch,
boundaryBase
);
bool broadSearch(
uint32 iter,
real t,
real dt,
csPairContainerType& ppPairs,
csPairContainerType& pwPairs,
bool force = false
) override;
};
}
#endif //__processorBoundaryContactSearch_hpp__

View File

@ -0,0 +1,163 @@
#include "twoPartContactSearch.hpp"
#include "twoPartContactSearchKernels.hpp"
#include "phasicFlowKokkos.hpp"
#include "streams.hpp"
void pFlow::twoPartContactSearch::checkAllocateNext(uint32 n)
{
if( nextCapacity_ < n)
{
nextCapacity_ = n;
reallocNoInit(next_, n);
}
}
void pFlow::twoPartContactSearch::nullifyHead()
{
fill(head_, static_cast<uint32>(-1));
}
void pFlow::twoPartContactSearch::nullifyNext(uint32 n)
{
fill(next_, 0u, n, static_cast<uint32>(-1));
}
void pFlow::twoPartContactSearch::buildList(
const deviceScatteredFieldAccess<realx3> &points)
{
if(points.empty())return;
uint32 n = points.size();
checkAllocateNext(n);
nullifyNext(n);
nullifyHead();
pFlow::twoPartContactSearchKernels::buildNextHead(
points,
searchCells_,
head_,
next_
);
}
pFlow::twoPartContactSearch::twoPartContactSearch
(
const box &domain,
real cellSize,
real sizeRatio
)
:
searchCells_(domain, cellSize),
head_("periodic:head",searchCells_.nx(), searchCells_.ny(), searchCells_.nz()),
sizeRatio_(sizeRatio)
{
}
bool pFlow::twoPartContactSearch::broadSearchPP
(
csPairContainerType &ppPairs,
const deviceScatteredFieldAccess<realx3> &points1,
const deviceScatteredFieldAccess<real>& diams1,
const deviceScatteredFieldAccess<realx3> &points2,
const deviceScatteredFieldAccess<real>& diams2,
const realx3& transferVec
)
{
if(points1.empty())return true;
if(points2.empty()) return true;
buildList(points1);
uint32 nNotInserted = 1;
// loop until the container size fits the numebr of contact pairs
while (nNotInserted > 0)
{
nNotInserted = pFlow::twoPartContactSearchKernels::broadSearchPP
(
ppPairs,
points1,
diams1,
points2,
diams2,
transferVec,
head_,
next_,
searchCells_,
sizeRatio_
);
if(nNotInserted)
{
// - resize the container
// note that getFull now shows the number of failed insertions.
uint32 len = max(nNotInserted,100u) ;
auto oldCap = ppPairs.capacity();
ppPairs.increaseCapacityBy(len);
INFORMATION<< "Particle-particle contact pair container capacity increased from "<<
oldCap << " to "<<ppPairs.capacity()<<" in contact search in boundary region."<<END_INFO;
}
}
return true;
}
bool pFlow::twoPartContactSearch::broadSearchPP
(
csPairContainerType &ppPairs,
const deviceScatteredFieldAccess<realx3> &points1,
const deviceScatteredFieldAccess<real> &diams1,
const realx3Vector_D& points2,
const realVector_D& diams2,
const word& name
)
{
buildList(points1);
uint32 nNotInserted = 1;
// loop until the container size fits the numebr of contact pairs
while (nNotInserted > 0)
{
nNotInserted = pFlow::twoPartContactSearchKernels::broadSearchPP
(
ppPairs,
points1,
diams1,
points2,
diams2,
head_,
next_,
searchCells_,
sizeRatio_
);
if(nNotInserted)
{
// - resize the container
// note that getFull now shows the number of failed insertions.
uint32 len = max(nNotInserted,100u) ;
auto oldCap = ppPairs.capacity();
ppPairs.increaseCapacityBy(len);
INFORMATION<< "Particle-particle contact pair container capacity increased from "<<
oldCap << " to "<<ppPairs.capacity()<<" in boundary contact search in "<< name <<END_INFO;
}
}
return true;
}

View File

@ -0,0 +1,104 @@
/*------------------------------- phasicFlow ---------------------------------
O C enter of
O O E ngineering and
O O M ultiscale modeling of
OOOOOOO F luid flow
------------------------------------------------------------------------------
Copyright (C): www.cemf.ir
email: hamid.r.norouzi AT gmail.com
------------------------------------------------------------------------------
Licence:
This file is part of phasicFlow code. It is a free software for simulating
granular and multiphase flows. You can redistribute it and/or modify it under
the terms of GNU General Public License v3 or any other later versions.
phasicFlow is distributed to help others in their research in the field of
granular and multiphase flows, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----------------------------------------------------------------------------*/
#ifndef __twoPartContactSearch_hpp__
#define __twoPartContactSearch_hpp__
#include "contactSearchGlobals.hpp"
#include "scatteredFieldAccess.hpp"
#include "cells.hpp"
#include "VectorSingles.hpp"
namespace pFlow
{
class twoPartContactSearch
{
public:
using HeadType = deviceViewType3D<uint32>;
using NextType = deviceViewType1D<uint32>;
private:
cells searchCells_;
HeadType head_{ "periodic::head", 1, 1, 1 };
NextType next_{ "periodic::next", 1 };
real sizeRatio_ = 1.0;
uint32 nextCapacity_ = 0;
void checkAllocateNext(uint32 n);
void nullifyHead();
void nullifyNext(uint32 n);
void buildList(
const deviceScatteredFieldAccess<realx3> &points);
public:
twoPartContactSearch(
const box &domain,
real cellSize,
real sizeRatio = 1.0);
/// @brief Perform a broad-search for spheres in two adjacent regions.
/// Region 1 is considered as the master (primary) region and region 2 as slave
/// @param ppPairs pairs container which holds i and j
/// @param points1 point positions in region 1
/// @param diams1 diameter of spheres in region 1
/// @param points2 point positions in region 2
/// @param diams2 diameter of spheres in region 2
/// @param transferVec a vector to transfer points from region 2 to region 1
/// @return true if it is successful
bool broadSearchPP(
csPairContainerType &ppPairs,
const deviceScatteredFieldAccess<realx3> &points1,
const deviceScatteredFieldAccess<real> &diams1,
const deviceScatteredFieldAccess<realx3> &points2,
const deviceScatteredFieldAccess<real> &diams2,
const realx3 &transferVec);
bool broadSearchPP(
csPairContainerType &ppPairs,
const deviceScatteredFieldAccess<realx3> &points1,
const deviceScatteredFieldAccess<real> &diams1,
const realx3Vector_D& points2,
const realVector_D& diams2,
const word& name);
const auto& searchCells()const
{
return searchCells_;
}
real sizeRatio()const
{
return sizeRatio_;
}
};
}
#endif //__twoPartContactSearch_hpp__

View File

@ -0,0 +1,186 @@
#include "twoPartContactSearchKernels.hpp"
INLINE_FUNCTION_HD
bool
sphereSphereCheckB(
const pFlow::realx3& p1,
const pFlow::realx3 p2,
pFlow::real d1,
pFlow::real d2
)
{
return pFlow::length(p2 - p1) < 0.5 * (d2 + d1);
}
void
pFlow::twoPartContactSearchKernels::buildNextHead(
const deviceScatteredFieldAccess<realx3>& points,
const cells& searchCells,
deviceViewType3D<uint32>& head,
deviceViewType1D<uint32>& next
)
{
uint32 n = points.size();
Kokkos::parallel_for(
"pFlow::ppwBndryContactSearch::buildList",
deviceRPolicyStatic(0, n),
LAMBDA_HD(uint32 i) {
int32x3 ind;
if (searchCells.pointIndexInDomain(points[i], ind))
{
// discards points out of searchCell
uint32 old =
Kokkos::atomic_exchange(&head(ind.x(), ind.y(), ind.z()), i);
next[i] = old;
}
}
);
Kokkos::fence();
}
pFlow::uint32
pFlow::twoPartContactSearchKernels::broadSearchPP(
csPairContainerType& ppPairs,
const deviceScatteredFieldAccess<realx3>& points,
const deviceScatteredFieldAccess<real>& diams,
const deviceScatteredFieldAccess<realx3>& mirrorPoints,
const deviceScatteredFieldAccess<real>& mirrorDiams,
const realx3& transferVec,
const deviceViewType3D<uint32>& head,
const deviceViewType1D<uint32>& next,
const cells& searchCells,
const real sizeRatio
)
{
if (points.empty())
return 0;
if (mirrorPoints.empty())
return 0;
auto nMirror = mirrorPoints.size();
uint32 getFull = 0;
Kokkos::parallel_reduce(
"pFlow::twoPartContactSearchKernels::broadSearchPP",
deviceRPolicyStatic(0, nMirror),
LAMBDA_HD(const uint32 mrrI, uint32& getFullUpdate) {
realx3 p_m = mirrorPoints(mrrI) + transferVec;
int32x3 ind_m;
if (!searchCells.pointIndexInDomain(p_m, ind_m))
return;
real d_m = sizeRatio * mirrorDiams[mrrI];
for (int ii = -1; ii < 2; ii++)
{
for (int jj = -1; jj < 2; jj++)
{
for (int kk = -1; kk < 2; kk++)
{
auto ind = ind_m + int32x3{ ii, jj, kk };
if (!searchCells.inCellRange(ind))
continue;
uint32 thisI = head(ind.x(), ind.y(), ind.z());
while (thisI != static_cast<uint32>(-1))
{
auto d_n = sizeRatio * diams[thisI];
// first item is for this boundary and second itme,
// for mirror
if(sphereSphereCheckB(p_m, points[thisI], d_m, d_n)&&
ppPairs.insert(thisI,mrrI) == static_cast<uint32>(-1))
{
getFullUpdate++;
}
thisI = next(thisI);
}
}
}
}
},
getFull
);
return getFull;
}
pFlow::uint32
pFlow::twoPartContactSearchKernels::broadSearchPP(
csPairContainerType& ppPairs,
const deviceScatteredFieldAccess<realx3>& points1,
const deviceScatteredFieldAccess<real>& diams1,
const realx3Vector_D& points2,
const realVector_D& diams2,
const deviceViewType3D<uint32>& head,
const deviceViewType1D<uint32>& next,
const cells& searchCells,
real sizeRatio
)
{
if (points1.empty())
return 0;
if (points2.empty())
return 0;
auto nP2 = points2.size();
auto points2View = points2.deviceView();
auto diams2View = diams2.deviceView();
uint32 getFull = 0;
Kokkos::parallel_reduce(
"pFlow::twoPartContactSearchKernels::broadSearchPP",
deviceRPolicyStatic(0, nP2),
LAMBDA_HD(const uint32 i2, uint32& getFullUpdate) {
realx3 p_m = points2View(i2);
int32x3 ind_m;
if (!searchCells.pointIndexInDomain(p_m, ind_m))
return;
real d_m = sizeRatio * diams2View[i2];
for (int ii = -1; ii < 2; ii++)
{
for (int jj = -1; jj < 2; jj++)
{
for (int kk = -1; kk < 2; kk++)
{
auto ind = ind_m + int32x3{ ii, jj, kk };
if (!searchCells.inCellRange(ind))
{
continue;
}
uint32 i1 = head(ind.x(), ind.y(), ind.z());
while (i1 != static_cast<uint32>(-1))
{
auto d_n = sizeRatio * diams1[i1];
// first item is for this boundary and second itme,
// for mirror
if(sphereSphereCheckB(p_m, points1[i1], d_m, d_n)&&
ppPairs.insert(i1,i2) == static_cast<uint32>(-1))
{
getFullUpdate++;
}
i1 = next(i1);
}
}
}
}
},
getFull
);
return getFull;
}

View File

@ -0,0 +1,49 @@
#ifndef __twoPartContactSearchKernels_hpp__
#define __twoPartContactSearchKernels_hpp__
#include "contactSearchGlobals.hpp"
#include "cells.hpp"
#include "contactSearchFunctions.hpp"
#include "scatteredFieldAccess.hpp"
#include "VectorSingles.hpp"
namespace pFlow::twoPartContactSearchKernels
{
void buildNextHead(
const deviceScatteredFieldAccess<realx3> &points,
const cells &searchCells,
deviceViewType3D<uint32> &head,
deviceViewType1D<uint32> &next );
uint32 broadSearchPP
(
csPairContainerType &ppPairs,
const deviceScatteredFieldAccess<realx3> &points,
const deviceScatteredFieldAccess<real> &diams,
const deviceScatteredFieldAccess<realx3> &mirrorPoints,
const deviceScatteredFieldAccess<real> &mirrorDiams,
const realx3 &transferVec,
const deviceViewType3D<uint32> &head,
const deviceViewType1D<uint32> &next,
const cells &searchCells,
real sizeRatio
);
uint32
broadSearchPP(
csPairContainerType& ppPairs,
const deviceScatteredFieldAccess<realx3>& points1,
const deviceScatteredFieldAccess<real>& diams1,
const realx3Vector_D& points2,
const realVector_D& diams2,
const deviceViewType3D<uint32>& head,
const deviceViewType1D<uint32>& next,
const cells& searchCells,
real sizeRatio
);
}
#endif //__twoPartContactSearchKernels_hpp__

View File

@ -0,0 +1,132 @@
#ifndef __processorBoundarySIKernels_hpp__
#define __processorBoundarySIKernels_hpp__
namespace pFlow::MPI::processorBoundarySIKernels
{
template<typename ContactListType, typename ContactForceModel>
inline
void sphereSphereInteraction
(
const word& kernalName,
real dt,
const ContactListType& cntctList,
const ContactForceModel& forceModel,
const deviceScatteredFieldAccess<realx3>& thisPoints,
const deviceViewType1D<real>& thisDiam,
const deviceViewType1D<uint32>& thisPropId,
const deviceViewType1D<realx3>& thisVel,
const deviceViewType1D<realx3>& thisRVel,
const deviceViewType1D<realx3>& thisCForce,
const deviceViewType1D<realx3>& thisCTorque,
const deviceViewType1D<realx3>& neighborPoints,
const deviceViewType1D<real>& neighborDiam,
const deviceViewType1D<uint32>& neighborPropId,
const deviceViewType1D<realx3>& neighborVel,
const deviceViewType1D<realx3>& neighborRVel,
const deviceViewType1D<realx3>& neighborCForce,
const deviceViewType1D<realx3>& neighborCTorque
)
{
using ValueType = typename ContactListType::ValueType;
uint32 ss = cntctList.size();
if(ss == 0u)return;
uint32 lastItem = cntctList.loopCount();
Kokkos::parallel_for(
kernalName,
deviceRPolicyDynamic(0,lastItem),
LAMBDA_HD(uint32 n)
{
if(!cntctList.isValid(n))return;
auto [i,j] = cntctList.getPair(n);
uint32 ind_i = thisPoints.index(i);
uint32 ind_j = j;
real Ri = 0.5*thisDiam[ind_i];
real Rj = 0.5*neighborDiam[ind_j];
realx3 xi = thisPoints.field()[ind_i];
realx3 xj = neighborPoints[ind_j];
real dist = length(xj-xi);
real ovrlp = (Ri+Rj) - dist;
if( ovrlp >0.0 )
{
auto Nij = (xj-xi)/max(dist,smallValue);
auto wi = thisRVel[ind_i];
auto wj = neighborRVel[ind_j];
auto Vr = thisVel[ind_i] - neighborVel[ind_j] + cross((Ri*wi+Rj*wj), Nij);
auto history = cntctList.getValue(n);
int32 propId_i = thisPropId[ind_i];
int32 propId_j = neighborPropId[ind_j];
realx3 FCn, FCt, Mri, Mrj, Mij, Mji;
// calculates contact force
forceModel.contactForce(
dt, i, j,
propId_i, propId_j,
Ri, Rj,
ovrlp,
Vr, Nij,
history,
FCn, FCt);
forceModel.rollingFriction(
dt, i, j,
propId_i, propId_j,
Ri, Rj,
wi, wj,
Nij,
FCn,
Mri, Mrj);
auto M = cross(Nij,FCt);
Mij = Ri*M+Mri;
Mji = Rj*M+Mrj;
auto FC = FCn + FCt;
Kokkos::atomic_add(&thisCForce[ind_i].x_,FC.x_);
Kokkos::atomic_add(&thisCForce[ind_i].y_,FC.y_);
Kokkos::atomic_add(&thisCForce[ind_i].z_,FC.z_);
Kokkos::atomic_add(&neighborCForce[ind_j].x_,-FC.x_);
Kokkos::atomic_add(&neighborCForce[ind_j].y_,-FC.y_);
Kokkos::atomic_add(&neighborCForce[ind_j].z_,-FC.z_);
Kokkos::atomic_add(&thisCTorque[ind_i].x_, Mij.x_);
Kokkos::atomic_add(&thisCTorque[ind_i].y_, Mij.y_);
Kokkos::atomic_add(&thisCTorque[ind_i].z_, Mij.z_);
Kokkos::atomic_add(&neighborCTorque[ind_j].x_, Mji.x_);
Kokkos::atomic_add(&neighborCTorque[ind_j].y_, Mji.y_);
Kokkos::atomic_add(&neighborCTorque[ind_j].z_, Mji.z_);
cntctList.setValue(n,history);
}
else
{
cntctList.setValue(n, ValueType());
}
});
Kokkos::fence();
}
} //pFlow::MPI::processorBoundarySIKernels
#endif //__processorBoundarySIKernels_hpp__

View File

@ -0,0 +1,256 @@
/*------------------------------- phasicFlow ---------------------------------
O C enter of
O O E ngineering and
O O M ultiscale modeling of
OOOOOOO F luid flow
------------------------------------------------------------------------------
Copyright (C): www.cemf.ir
email: hamid.r.norouzi AT gmail.com
------------------------------------------------------------------------------
Licence:
This file is part of phasicFlow code. It is a free software for simulating
granular and multiphase flows. You can redistribute it and/or modify it under
the terms of GNU General Public License v3 or any other later versions.
phasicFlow is distributed to help others in their research in the field of
granular and multiphase flows, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----------------------------------------------------------------------------*/
#include "processorBoundarySIKernels.hpp"
template <typename cFM, typename gMM>
pFlow::MPI::processorBoundarySphereInteraction<cFM, gMM>::processorBoundarySphereInteraction(
const boundaryBase &boundary,
const sphereParticles &sphPrtcls,
const GeometryMotionModel &geomMotion)
:
boundarySphereInteraction<cFM,gMM>(
boundary,
sphPrtcls,
geomMotion
),
masterInteraction_(boundary.isBoundaryMaster())
{
if(masterInteraction_)
{
this->allocatePPPairs();
this->allocatePWPairs();
}
}
#ifdef BoundaryModel1
template <typename cFM, typename gMM>
bool pFlow::MPI::processorBoundarySphereInteraction<cFM, gMM>::sphereSphereInteraction
(
real dt,
const ContactForceModel &cfModel,
uint32 step
)
{
// master processor calculates the contact force/torque and sends data back to the
// neighbor processor (slave processor).
// slave processor recieves the data and adds the data to the internalField
if(masterInteraction_)
{
if(step==1)return true;
const auto & sphPar = this->sphParticles();
uint32 thisIndex = this->boundary().thisBoundaryIndex();
const auto& cfBndry = static_cast<const processorBoundaryField<realx3>&> (
sphPar.contactForce().BoundaryField(thisIndex));
const auto& ctBndry = static_cast<const processorBoundaryField<realx3>&> (
sphPar.contactTorque().BoundaryField(thisIndex));
if(step == 2 )
{
iter++;
pFlow::MPI::processorBoundarySIKernels::sphereSphereInteraction(
"ppBoundaryInteraction."+this->boundaryName(),
dt,
this->ppPairs(),
cfModel,
this->boundary().thisPoints(),
sphPar.diameter().deviceViewAll(),
sphPar.propertyId().deviceViewAll(),
sphPar.velocity().deviceViewAll(),
sphPar.rVelocity().deviceViewAll(),
sphPar.contactForce().deviceViewAll(),
sphPar.contactTorque().deviceViewAll(),
this->boundary().neighborProcPoints().deviceViewAll(),
sphPar.diameter().BoundaryField(thisIndex).neighborProcField().deviceViewAll(),
sphPar.propertyId().BoundaryField(thisIndex).neighborProcField().deviceViewAll(),
sphPar.velocity().BoundaryField(thisIndex).neighborProcField().deviceViewAll(),
sphPar.rVelocity().BoundaryField(thisIndex).neighborProcField().deviceViewAll(),
cfBndry.neighborProcField().deviceViewAll(),
ctBndry.neighborProcField().deviceViewAll()
);
return true;
}
else if(step == 3 )
{
cfBndry.sendBackData();
ctBndry.sendBackData();
return true;
}
return false;
}
else
{
if(step == 1 )
{
const auto & sphPar = this->sphParticles();
uint32 thisIndex = this->boundary().thisBoundaryIndex();
const auto& cfBndry = static_cast<const processorBoundaryField<realx3>&>(
sphPar.contactForce().BoundaryField(thisIndex));
const auto& ctBndry = static_cast<const processorBoundaryField<realx3>&> (
sphPar.contactTorque().BoundaryField(thisIndex));
cfBndry.recieveBackData();
ctBndry.recieveBackData();
return false;
}
else if(step == 11)
{
const auto & sphPar = this->sphParticles();
uint32 thisIndex = this->boundary().thisBoundaryIndex();
const auto& cfBndry = static_cast<const processorBoundaryField<realx3>&>(
sphPar.contactForce().BoundaryField(thisIndex));
const auto& ctBndry = static_cast<const processorBoundaryField<realx3>&> (
sphPar.contactTorque().BoundaryField(thisIndex));
cfBndry.addBufferToInternalField();
ctBndry.addBufferToInternalField();
return true;
}
return false;
}
return false;
}
#else
template <typename cFM, typename gMM>
bool pFlow::MPI::processorBoundarySphereInteraction<cFM, gMM>::sphereSphereInteraction
(
real dt,
const ContactForceModel &cfModel,
uint32 step
)
{
// master processor calculates the contact force/torque and sends data back to the
// neighbor processor (slave processor).
// slave processor recieves the data and adds the data to the internalField
if(masterInteraction_)
{
if(step==1)return true;
const auto & sphPar = this->sphParticles();
uint32 thisIndex = this->boundary().thisBoundaryIndex();
const auto& cfBndry = static_cast<const processorBoundaryField<realx3>&> (
sphPar.contactForce().BoundaryField(thisIndex));
const auto& ctBndry = static_cast<const processorBoundaryField<realx3>&> (
sphPar.contactTorque().BoundaryField(thisIndex));
if(step == 2 )
{
pFlow::MPI::processorBoundarySIKernels::sphereSphereInteraction(
"ppBoundaryInteraction."+this->boundaryName(),
dt,
this->ppPairs(),
cfModel,
this->boundary().thisPoints(),
sphPar.diameter().deviceViewAll(),
sphPar.propertyId().deviceViewAll(),
sphPar.velocity().deviceViewAll(),
sphPar.rVelocity().deviceViewAll(),
sphPar.contactForce().deviceViewAll(),
sphPar.contactTorque().deviceViewAll(),
this->boundary().neighborProcPoints().deviceViewAll(),
sphPar.diameter().BoundaryField(thisIndex).neighborProcField().deviceViewAll(),
sphPar.propertyId().BoundaryField(thisIndex).neighborProcField().deviceViewAll(),
sphPar.velocity().BoundaryField(thisIndex).neighborProcField().deviceViewAll(),
sphPar.rVelocity().BoundaryField(thisIndex).neighborProcField().deviceViewAll(),
cfBndry.neighborProcField().deviceViewAll(),
ctBndry.neighborProcField().deviceViewAll()
);
return true;
}
else if(step == 3 )
{
cfBndry.sendBackData();
ctBndry.sendBackData();
return true;
}
else if(step == 11 )
{
cfBndry.updateBoundaryFromSlave();
ctBndry.updateBoundaryFromSlave();
return true;
}
return false;
}
else
{
if(step == 1 )
{
const auto & sphPar = this->sphParticles();
uint32 thisIndex = this->boundary().thisBoundaryIndex();
const auto& cfBndry = static_cast<const processorBoundaryField<realx3>&>(
sphPar.contactForce().BoundaryField(thisIndex));
const auto& ctBndry = static_cast<const processorBoundaryField<realx3>&> (
sphPar.contactTorque().BoundaryField(thisIndex));
cfBndry.recieveBackData();
ctBndry.recieveBackData();
return false;
}
else if(step == 11)
{
const auto & sphPar = this->sphParticles();
uint32 thisIndex = this->boundary().thisBoundaryIndex();
const auto& cfBndry = static_cast<const processorBoundaryField<realx3>&>(
sphPar.contactForce().BoundaryField(thisIndex));
const auto& ctBndry = static_cast<const processorBoundaryField<realx3>&> (
sphPar.contactTorque().BoundaryField(thisIndex));
cfBndry.addBufferToInternalField();
cfBndry.updateBoundaryToMaster();
ctBndry.addBufferToInternalField();
ctBndry.updateBoundaryToMaster();
return true;
}
return false;
}
return false;
}
#endif

View File

@ -0,0 +1,93 @@
/*------------------------------- phasicFlow ---------------------------------
O C enter of
O O E ngineering and
O O M ultiscale modeling of
OOOOOOO F luid flow
------------------------------------------------------------------------------
Copyright (C): www.cemf.ir
email: hamid.r.norouzi AT gmail.com
------------------------------------------------------------------------------
Licence:
This file is part of phasicFlow code. It is a free software for simulating
granular and multiphase flows. You can redistribute it and/or modify it under
the terms of GNU General Public License v3 or any other later versions.
phasicFlow is distributed to help others in their research in the field of
granular and multiphase flows, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----------------------------------------------------------------------------*/
#ifndef __processorBoundarySphereInteraction_hpp__
#define __processorBoundarySphereInteraction_hpp__
#include "boundarySphereInteraction.hpp"
#include "processorBoundaryField.hpp"
#include "boundaryProcessor.hpp"
namespace pFlow::MPI
{
template<typename contactForceModel,typename geometryMotionModel>
class processorBoundarySphereInteraction
:
public boundarySphereInteraction<contactForceModel, geometryMotionModel>
{
public:
using PBSInteractionType =
processorBoundarySphereInteraction<contactForceModel,geometryMotionModel>;
using BSInteractionType =
boundarySphereInteraction<contactForceModel, geometryMotionModel>;
using GeometryMotionModel = typename BSInteractionType::GeometryMotionModel;
using ContactForceModel = typename BSInteractionType::ContactForceModel;
using MotionModel = typename geometryMotionModel::MotionModel;
using ModelStorage = typename ContactForceModel::contactForceStorage;
using IdType = typename BSInteractionType::IdType;
using IndexType = typename BSInteractionType::IndexType;
using ContactListType = typename BSInteractionType::ContactListType;
private:
bool masterInteraction_;
public:
TypeInfoTemplate22("boundarySphereInteraction", "processor",ContactForceModel, MotionModel);
processorBoundarySphereInteraction(
const boundaryBase& boundary,
const sphereParticles& sphPrtcls,
const GeometryMotionModel& geomMotion
);
add_vCtor
(
BSInteractionType,
PBSInteractionType,
boundaryBase
);
~processorBoundarySphereInteraction()override = default;
bool sphereSphereInteraction(
real dt,
const ContactForceModel& cfModel,
uint32 step)override;
};
}
#include "processorBoundarySphereInteraction.cpp"
#endif //__processorBoundarySphereInteraction_hpp__

View File

@ -0,0 +1,17 @@
#include "processorBoundarySphereInteraction.hpp"
#include "geometryMotions.hpp"
#include "contactForceModels.hpp"
template class pFlow::MPI::processorBoundarySphereInteraction
<
pFlow::cfModels::limitedNonLinearNormalRolling,
pFlow::rotationAxisMotionGeometry
>;
template class pFlow::MPI::processorBoundarySphereInteraction
<
pFlow::cfModels::nonLimitedNonLinearNormalRolling,
pFlow::rotationAxisMotionGeometry
>;

View File

@ -359,7 +359,7 @@ bool pFlow::sphereInteraction<cFM,gMM, cLT>::hearChanges
if(msg.equivalentTo(message::ITEMS_REARRANGE))
{
notImplementedFunction;
return false;
return true;
}
fatalErrorInFunction<<"Event "<< msg.eventNames()<<

View File

@ -0,0 +1,46 @@
#include "processorBoundarySphereParticles.hpp"
#include "sphereParticles.hpp"
#include "boundaryProcessor.hpp"
pFlow::processorBoundarySphereParticles::processorBoundarySphereParticles(
const boundaryBase &boundary,
sphereParticles &prtcls
)
:
boundarySphereParticles(boundary, prtcls)
{
}
bool pFlow::processorBoundarySphereParticles::acceleration(const timeInfo &ti, const realx3& g)
{
#ifndef BoundaryModel1
if(isBoundaryMaster())
{
auto thisIndex = thisBoundaryIndex();
auto mass = Particles().mass().BoundaryField(thisIndex).neighborProcField().deviceView();
auto I = Particles().I().BoundaryField(thisIndex).neighborProcField().deviceView();
auto cf = Particles().contactForce().BoundaryField(thisIndex).neighborProcField().deviceView();
auto ct = Particles().contactTorque().BoundaryField(thisIndex).neighborProcField().deviceView();
auto acc = Particles().accelertion().BoundaryField(thisIndex).neighborProcField().deviceView();
auto rAcc = Particles().rAcceleration().BoundaryField(thisIndex).neighborProcField().deviceView();
Kokkos::parallel_for(
"processorBoundary::acceleration."+this->boundaryName(),
deviceRPolicyStatic(0,mass.size()),
LAMBDA_HD(uint32 i){
acc[i] = cf[i]/mass[i] + g;
rAcc[i] = ct[i]/I[i];
});
Kokkos::fence();
}
#endif
return true;
}

View File

@ -0,0 +1,38 @@
#ifndef __processorBoundarySphereParticles_hpp__
#define __processorBoundarySphereParticles_hpp__
#include "boundarySphereParticles.hpp"
namespace pFlow
{
class processorBoundarySphereParticles
:
public boundarySphereParticles
{
public:
/// type info
TypeInfo("boundarySphereParticles<MPI,processor>");
processorBoundarySphereParticles(
const boundaryBase &boundary,
sphereParticles& prtcls
);
add_vCtor(
boundarySphereParticles,
processorBoundarySphereParticles,
boundaryBase
);
bool acceleration(const timeInfo& ti, const realx3& g)override;
};
}
#endif

View File

@ -0,0 +1,70 @@
#include "MPIParticleIdHandler.hpp"
#include "procCommunication.hpp"
pFlow::MPI::MPIParticleIdHandler::MPIParticleIdHandler
(
pointStructure& pStruct
)
:
particleIdHandler(pStruct)
{
initialIdCheck();
}
pFlow::Pair<pFlow::uint32, pFlow::uint32>
pFlow::MPI::MPIParticleIdHandler::getIdRange(uint32 nNewParticles)
{
uint32 startId;
if(maxId_==-1)
{
startId = 0;
}
else
{
startId = maxId_+1;
}
uint32 endId = startId+nNewParticles-1;
maxId_ = endId;
return {startId, endId};
}
bool pFlow::MPI::MPIParticleIdHandler::initialIdCheck()
{
/// empty point structure / no particles in simulation
uint32 maxId = -1;
if( !pStruct().empty() )
{
maxId = max( *this );
}
auto maxIdAll = procVector<uint32>(pFlowProcessors());
auto numAll = procVector<uint32>(pFlowProcessors());
auto comm = procCommunication(pFlowProcessors());
comm.collectAllToAll(maxId, maxIdAll);
comm.collectAllToAll(size(),numAll);
uint32 n = 0;
for(uint32 i=0; i<maxIdAll.size(); i++)
{
if( maxIdAll[i]==-1 && numAll[i]!= 0)
{
if(comm.localRank() == i)
{
fillSequence(*this, n);
maxId_ = size()-1 + n;
}
}
else
{
if(comm.localRank() == i)
{
maxId_ = maxIdAll[i];
}
}
n += numAll[i];
}
return true;
}

View File

@ -0,0 +1,60 @@
/*------------------------------- phasicFlow ---------------------------------
O C enter of
O O E ngineering and
O O M ultiscale modeling of
OOOOOOO F luid flow
------------------------------------------------------------------------------
Copyright (C): www.cemf.ir
email: hamid.r.norouzi AT gmail.com
------------------------------------------------------------------------------
Licence:
This file is part of phasicFlow code. It is a free software for simulating
granular and multiphase flows. You can redistribute it and/or modify it under
the terms of GNU General Public License v3 or any other later versions.
phasicFlow is distributed to help others in their research in the field of
granular and multiphase flows, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----------------------------------------------------------------------------*/
#ifndef __MPIParticleIdHandler_hpp__
#define __MPIParticleIdHandler_hpp__
#include "particleIdHandler.hpp"
namespace pFlow::MPI
{
class MPIParticleIdHandler : public particleIdHandler
{
private:
uint32 maxId_ = -1;
bool initialIdCheck() override;
public:
ClassInfo("particleIdHandler<MPI>");
explicit MPIParticleIdHandler(pointStructure& pStruct);
~MPIParticleIdHandler() override = default;
add_vCtor(
particleIdHandler,
MPIParticleIdHandler,
pointStructure
);
Pair<uint32, uint32> getIdRange(uint32 nNewParticles) override;
uint32 maxId() const override
{
return maxId_;
}
};
}
#endif //__MPIParticleIdHandler_hpp__

View File

@ -24,7 +24,7 @@ Licence:
#include "types.hpp"
#include "span.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
template<typename T>

View File

@ -26,20 +26,18 @@ Licence:
#include "fieldFunctions.hpp"
#include "dictionary.hpp"
namespace pFlow
{
bool pointFieldGetType(const word& TYPENAME, word& fieldType, word& fieldSpace);
}
bool pFlow::fieldsDataBase::loadPointStructureToTime()
bool pFlow::postprocessData::fieldsDataBase::loadPointStructureToTime()
{
return false;
}
bool pFlow::fieldsDataBase::checkForUpdate(const word &compoundName, bool forceUpdate)
bool pFlow::postprocessData::fieldsDataBase::checkForUpdate
(
const word &compoundName,
bool forceUpdate
)
{
auto t = currentTime();
bool shouldUpdate = false;
@ -58,7 +56,10 @@ bool pFlow::fieldsDataBase::checkForUpdate(const word &compoundName, bool forceU
return shouldUpdate;
}
pFlow::span<pFlow::real> pFlow::fieldsDataBase::createOrGetRealField(const word &name)
pFlow::span<pFlow::real> pFlow::postprocessData::fieldsDataBase::createOrGetRealField
(
const word &name
)
{
bool shouldUpdate = checkForUpdate(name);
@ -83,7 +84,10 @@ pFlow::span<pFlow::real> pFlow::fieldsDataBase::createOrGetRealField(const word
field.size());
}
pFlow::span<pFlow::real> pFlow::fieldsDataBase::createOrGetVolume(bool forceUpdate)
pFlow::span<pFlow::real> pFlow::postprocessData::fieldsDataBase::createOrGetVolume
(
bool forceUpdate
)
{
const word fName = "volume";
bool shouldUpdate = checkForUpdate(fName, forceUpdate);
@ -119,7 +123,7 @@ pFlow::span<pFlow::real> pFlow::fieldsDataBase::createOrGetVolume(bool forceUpda
}
pFlow::span<pFlow::real> pFlow::fieldsDataBase::createOrGetDensity(bool forceUpdate)
pFlow::span<pFlow::real> pFlow::postprocessData::fieldsDataBase::createOrGetDensity(bool forceUpdate)
{
const word fName = "density";
@ -155,7 +159,7 @@ pFlow::span<pFlow::real> pFlow::fieldsDataBase::createOrGetDensity(bool forceUpd
field.size());
}
pFlow::span<pFlow::real> pFlow::fieldsDataBase::createOrGetOne(bool forceUpdate)
pFlow::span<pFlow::real> pFlow::postprocessData::fieldsDataBase::createOrGetOne(bool forceUpdate)
{
const word fName = "one";
@ -182,7 +186,7 @@ pFlow::span<pFlow::real> pFlow::fieldsDataBase::createOrGetOne(bool forceUpdate)
field.size());
}
pFlow::span<pFlow::real> pFlow::fieldsDataBase::createOrGetMass(bool forceUpdate)
pFlow::span<pFlow::real> pFlow::postprocessData::fieldsDataBase::createOrGetMass(bool forceUpdate)
{
const word fName = "mass";
@ -218,7 +222,7 @@ pFlow::span<pFlow::real> pFlow::fieldsDataBase::createOrGetMass(bool forceUpdate
field.size());
}
pFlow::span<pFlow::real> pFlow::fieldsDataBase::createOrGetI(bool forceUpdate)
pFlow::span<pFlow::real> pFlow::postprocessData::fieldsDataBase::createOrGetI(bool forceUpdate)
{
const word fName = "I";
@ -254,7 +258,7 @@ pFlow::span<pFlow::real> pFlow::fieldsDataBase::createOrGetI(bool forceUpdate)
field.size());
}
bool pFlow::fieldsDataBase::findFunction(
bool pFlow::postprocessData::fieldsDataBase::findFunction(
const word &compoundFieldName,
word &fieldName,
fieldsDataBase::Functions &func)
@ -360,7 +364,7 @@ bool pFlow::fieldsDataBase::findFunction(
return false; // No match
}
bool pFlow::fieldsDataBase::inputOutputType
bool pFlow::postprocessData::fieldsDataBase::inputOutputType
(
fieldsDataBase::Functions func,
const word &inputType,
@ -458,12 +462,12 @@ bool pFlow::fieldsDataBase::inputOutputType
return false;
}
pFlow::fieldsDataBase::fieldsDataBase
pFlow::postprocessData::fieldsDataBase::fieldsDataBase
(
systemControl& control,
const dictionary& postDict,
bool inSimulation,
timeValue startTime
TimeValueType startTime
)
:
time_(control.time()),
@ -488,12 +492,12 @@ pFlow::fieldsDataBase::fieldsDataBase
}
}
pFlow::timeValue pFlow::fieldsDataBase::currentTime() const
pFlow::TimeValueType pFlow::postprocessData::fieldsDataBase::currentTime() const
{
return time_.currentTime();
}
bool pFlow::fieldsDataBase::getFieldTypeNameFunction
bool pFlow::postprocessData::fieldsDataBase::getFieldTypeNameFunction
(
const word& compoundName,
word& pointFieldName,
@ -543,7 +547,7 @@ bool pFlow::fieldsDataBase::getFieldTypeNameFunction
return true;
}
bool pFlow::fieldsDataBase::getFieldType
bool pFlow::postprocessData::fieldsDataBase::getFieldType
(
const word& compoundName,
word& originalType,
@ -559,7 +563,7 @@ bool pFlow::fieldsDataBase::getFieldType
return true;
}
bool pFlow::fieldsDataBase::getFieldType
bool pFlow::postprocessData::fieldsDataBase::getFieldType
(
const word &compoundName,
word &typeAfterFunction
@ -575,7 +579,7 @@ bool pFlow::fieldsDataBase::getFieldType
return true;
}
pFlow::span<pFlow::realx3> pFlow::fieldsDataBase::updatePoints(bool forceUpdate)
pFlow::span<pFlow::realx3> pFlow::postprocessData::fieldsDataBase::updatePoints(bool forceUpdate)
{
const word fName = "position";
bool shouldUpdate = checkForUpdate(fName, forceUpdate);
@ -604,7 +608,7 @@ pFlow::span<pFlow::realx3> pFlow::fieldsDataBase::updatePoints(bool forceUpdate)
}
pFlow::span<pFlow::realx3> pFlow::fieldsDataBase::updateFieldRealx3
pFlow::span<pFlow::realx3> pFlow::postprocessData::fieldsDataBase::updateFieldRealx3
(
const word &compoundName,
bool forceUpdate
@ -640,7 +644,7 @@ pFlow::span<pFlow::realx3> pFlow::fieldsDataBase::updateFieldRealx3
}
pFlow::span<pFlow::realx4> pFlow::fieldsDataBase::updateFieldRealx4
pFlow::span<pFlow::realx4> pFlow::postprocessData::fieldsDataBase::updateFieldRealx4
(
const word &compoundName,
bool forceUpdate
@ -676,7 +680,7 @@ pFlow::span<pFlow::realx4> pFlow::fieldsDataBase::updateFieldRealx4
}
pFlow::span<pFlow::real> pFlow::fieldsDataBase::updateFieldReal
pFlow::span<pFlow::real> pFlow::postprocessData::fieldsDataBase::updateFieldReal
(
const word &compoundName,
bool forceUpdate
@ -843,7 +847,7 @@ pFlow::span<pFlow::real> pFlow::fieldsDataBase::updateFieldReal
return span<real>(nullptr, 0);
}
pFlow::span<pFlow::uint32> pFlow::fieldsDataBase::updateFieldUint32
pFlow::span<pFlow::uint32> pFlow::postprocessData::fieldsDataBase::updateFieldUint32
(
const word& name,
bool forceUpdate
@ -852,7 +856,7 @@ pFlow::span<pFlow::uint32> pFlow::fieldsDataBase::updateFieldUint32
return updateField<uint32>(name, forceUpdate);
}
pFlow::allPointFieldTypes pFlow::fieldsDataBase::updateFieldAll
pFlow::postprocessData::allPointFieldTypes pFlow::postprocessData::fieldsDataBase::updateFieldAll
(
const word &compoundName,
bool forceUpdate
@ -888,15 +892,29 @@ pFlow::allPointFieldTypes pFlow::fieldsDataBase::updateFieldAll
}
}
bool pFlow::postprocessData::fieldsDataBase::pointFieldGetType
(
const word& TYPENAME,
word& fieldType,
word& fieldSpace
)
{
std::regex match("pointField\\<([A-Za-z1-9_]*)\\,([A-Za-z1-9_]*)\\>");
std::smatch search;
if(!std::regex_match(TYPENAME, search, match)) return false;
if(search.size()!= 3) return false;
fieldType = search[1];
fieldSpace = search[2];
return true;
}
pFlow::uniquePtr<pFlow::fieldsDataBase>
pFlow::fieldsDataBase::create
pFlow::uniquePtr<pFlow::postprocessData::fieldsDataBase>
pFlow::postprocessData::fieldsDataBase::create
(
systemControl& control,
const dictionary& postDict,
bool inSimulation,
timeValue startTime
TimeValueType startTime
)
{
word dbType;
@ -931,14 +949,5 @@ pFlow::uniquePtr<pFlow::fieldsDataBase>
return nullptr;
}
bool pFlow::pointFieldGetType(const word& TYPENAME, word& fieldType, word& fieldSpace)
{
std::regex match("pointField\\<([A-Za-z1-9_]*)\\,([A-Za-z1-9_]*)\\>");
std::smatch search;
if(!std::regex_match(TYPENAME, search, match)) return false;
if(search.size()!= 3) return false;
fieldType = search[1];
fieldSpace = search[2];
return true;
}

View File

@ -29,13 +29,15 @@ Licence:
#include "Map.hpp"
#include "shape.hpp"
namespace pFlow
{
class dictionary;
class systemControl;
class Time;
}
class dictionary;
class systemControl;
class Time;
namespace pFlow::postprocessData
{
class fieldsDataBase
@ -76,7 +78,7 @@ private:
anyList allFields_;
/// Map to store the last capture time of each field
wordMap<timeValue> captureTime_;
wordMap<TimeValueType> captureTime_;
/// Reference to the Time object
Time& time_;
@ -176,7 +178,7 @@ public:
systemControl& control,
const dictionary& postDict,
bool inSimulation,
timeValue startTime);
TimeValueType startTime);
/// no copy constructor
fieldsDataBase(const fieldsDataBase&) = delete;
@ -201,7 +203,7 @@ public:
systemControl& control,
const dictionary& postDict,
bool inSimulation,
timeValue startTime
TimeValueType startTime
),
(control, postDict, inSimulation, startTime)
);
@ -209,7 +211,7 @@ public:
// - Public Access Functions
/// returns the current time
timeValue currentTime()const;
TimeValueType currentTime()const;
/// const ref to object Time
const Time& time()const
@ -280,7 +282,7 @@ public:
/// Get the next avaiable time folder after the current time folder
/// This is only used for post-simulation processing
virtual
timeValue getNextTimeFolder()const
TimeValueType getNextTimeFolder()const
{
return -1.0;
}
@ -289,7 +291,7 @@ public:
/// This is used only for post-simulation processing
/// @returns the time value of the next folder.
virtual
timeValue setToNextTimeFolder()
TimeValueType setToNextTimeFolder()
{
return -1.0;
}
@ -298,20 +300,26 @@ public:
/// This is used only for post-simulation processing
/// @returns the time value of the skipped folder
virtual
timeValue skipNextTimeFolder()
TimeValueType skipNextTimeFolder()
{
return -1.0;
}
static
bool pointFieldGetType(
const word& TYPENAME,
word& fieldType,
word& fieldSpace);
static
uniquePtr<fieldsDataBase> create(
systemControl& control,
const dictionary& postDict,
bool inSimulation,
timeValue startTime);
TimeValueType startTime);
};
} // namespace pFlow
} // namespace pFlow::postprocessData
#include "fieldsDataBaseTemplates.cpp"

View File

@ -9,7 +9,7 @@
#include "types.hpp"
#include "span.hpp"
namespace pFlow
namespace pFlow::postprocessData
{

View File

@ -23,9 +23,9 @@ Licence:
#include "fieldsDataBase.hpp"
template<pFlow::ValidFieldType T>
template<pFlow::postprocessData::ValidFieldType T>
inline
pFlow::span<T> pFlow::fieldsDataBase::updateField(const word& name, bool forceUpdate)
pFlow::span<T> pFlow::postprocessData::fieldsDataBase::updateField(const word& name, bool forceUpdate)
{
bool shouldUpdate = checkForUpdate(name, forceUpdate);
@ -40,8 +40,8 @@ pFlow::span<T> pFlow::fieldsDataBase::updateField(const word& name, bool forceUp
{
if( loadPointFieldToTime(name) )
{
const auto& pField = time_.lookupObject<pointField_D<T>>(name);
allFields_.emplaceBackOrReplace<FieldTypeHost<T>>(
const auto& pField = time_.template lookupObject<pointField_D<T>>(name);
allFields_.template emplaceBackOrReplace<FieldTypeHost<T>>(
name,
pField.activeValuesHost());
}
@ -63,9 +63,9 @@ pFlow::span<T> pFlow::fieldsDataBase::updateField(const word& name, bool forceUp
}
template<pFlow::ValidFieldType T>
template<pFlow::postprocessData::ValidFieldType T>
inline
pFlow::span<T> pFlow::fieldsDataBase::updateReservedField
pFlow::span<T> pFlow::postprocessData::fieldsDataBase::updateReservedField
(
const word& name,
bool forceUpdate

View File

@ -3,38 +3,38 @@
#include "dynamicPointStructure.hpp"
#include "vocabs.hpp"
namespace pFlow
{
bool pointFieldGetType(const word& TYPENAME, word& fieldType, word& fieldSpace);
}
bool pFlow::simulationFieldsDataBase::pointFieldNameExists(const word &name) const
bool pFlow::postprocessData::simulationFieldsDataBase::pointFieldNameExists(const word &name) const
{
return time().lookupObjectName(name);
}
bool pFlow::simulationFieldsDataBase::loadPointFieldToTime(const word &name)
bool pFlow::postprocessData::simulationFieldsDataBase::loadPointFieldToTime(const word &name)
{
return time().lookupObjectName(name);
}
bool pFlow::simulationFieldsDataBase::loadPointStructureToTime()
bool pFlow::postprocessData::simulationFieldsDataBase::loadPointStructureToTime()
{
// it is already in the Time object
return time().lookupObjectName(pointStructureFile__);
}
const pFlow::shape& pFlow::simulationFieldsDataBase::getShape() const
const pFlow::shape& pFlow::postprocessData::simulationFieldsDataBase::getShape() const
{
return shape_;
}
pFlow::word pFlow::simulationFieldsDataBase::getPointFieldType(const word &name) const
pFlow::word pFlow::postprocessData::simulationFieldsDataBase::getPointFieldType
(
const word &name
) const
{
word pfType = time().lookupObjectTypeName(name);
word type, space;
if(!pointFieldGetType(pfType, type, space))
if(!fieldsDataBase::pointFieldGetType(pfType, type, space))
{
fatalErrorInFunction
<<"Error in retriving the type of pointField "
@ -44,12 +44,12 @@ pFlow::word pFlow::simulationFieldsDataBase::getPointFieldType(const word &name)
return type;
}
pFlow::simulationFieldsDataBase::simulationFieldsDataBase
pFlow::postprocessData::simulationFieldsDataBase::simulationFieldsDataBase
(
systemControl &control,
const dictionary& postDict,
bool inSimulation,
timeValue startTime
TimeValueType startTime
)
:
fieldsDataBase(control, postDict, inSimulation, startTime),
@ -60,7 +60,7 @@ pFlow::simulationFieldsDataBase::simulationFieldsDataBase
{
}
const pFlow::pointStructure &pFlow::simulationFieldsDataBase::pStruct() const
const pFlow::pointStructure &pFlow::postprocessData::simulationFieldsDataBase::pStruct() const
{
return
static_cast<const pointStructure&>

View File

@ -23,7 +23,7 @@ Licence:
#include "fieldsDataBase.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
class simulationFieldsDataBase
@ -60,7 +60,7 @@ public:
systemControl& control,
const dictionary& postDict,
bool inSimulation,
timeValue startTime);
TimeValueType startTime);
~simulationFieldsDataBase() override = default;

View File

@ -1,6 +1,9 @@
#include "PostprocessOperationAvMassVelocity.hpp"
pFlow::PostprocessOperationAvMassVelocity::PostprocessOperationAvMassVelocity
namespace pFlow::postprocessData
{
PostprocessOperationAvMassVelocity::PostprocessOperationAvMassVelocity
(
const dictionary &opDict,
const regionPoints &regPoints,
@ -17,4 +20,6 @@ pFlow::PostprocessOperationAvMassVelocity::PostprocessOperationAvMassVelocity
fieldsDB
)
{
}
}

View File

@ -132,7 +132,7 @@ Licence:
#include "regionField.hpp"
#include "includeMask.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
@ -168,6 +168,6 @@ public:
};
}
} // namespace pFlow::postprocessData
#endif //__PostprocessOperationAvMassVelocity_hpp__

View File

@ -3,8 +3,11 @@
#include "fieldsDataBase.hpp"
#include "operationFunctions.hpp"
namespace pFlow::postprocessData
{
/// Constructs average processor and initializes result field based on input field type
pFlow::PostprocessOperationAverage::PostprocessOperationAverage
PostprocessOperationAverage::PostprocessOperationAverage
(
const dictionary &opDict,
const regionPoints &regPoints,
@ -39,7 +42,7 @@ pFlow::PostprocessOperationAverage::PostprocessOperationAverage
}
}
pFlow::PostprocessOperationAverage::PostprocessOperationAverage
PostprocessOperationAverage::PostprocessOperationAverage
(
const dictionary &opDict,
const word &fieldName,
@ -77,8 +80,9 @@ pFlow::PostprocessOperationAverage::PostprocessOperationAverage
}
}
/// Performs weighted average of field values within each region
bool pFlow::PostprocessOperationAverage::execute
bool PostprocessOperationAverage::execute
(
const std::vector<span<real>>& weights,
const regionField<real>& volFactor
@ -109,7 +113,7 @@ bool pFlow::PostprocessOperationAverage::execute
allField)
);
if(calculateFluctuation2_)
if(calculateFluctuation2_())
{
auto& processedRegField = processedRegFieldPtr_();
fluctuation2FieldPtr_ = makeUnique<processedRegFieldType>
@ -137,4 +141,41 @@ bool pFlow::PostprocessOperationAverage::execute
return true;
}
}
bool PostprocessOperationAverage::write(const fileSystem &parDir) const
{
if(! postprocessOperation::write(parDir))
{
return false;
}
if(!calculateFluctuation2_())
{
return true;
}
auto ti = time().TimeInfo();
if(!os2Ptr_)
{
fileSystem path = parDir+(
processedFieldName()+"_prime2" + ".Start_" + ti.timeName());
os2Ptr_ = makeUnique<oFstream>(path);
regPoints().write(os2Ptr_());
}
std::visit
(
[&](auto&& arg)->bool
{
return writeField(os2Ptr_(), ti.t(), arg, threshold());
},
fluctuation2FieldPtr_()
);
return true;
}
} // namespace pFlow::postprocessData

View File

@ -132,10 +132,9 @@ Licence:
#include "regionField.hpp"
#include "includeMask.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
class PostprocessOperationAverage
:
public postprocessOperation
@ -150,6 +149,9 @@ private:
uniquePtr<processedRegFieldType> fluctuation2FieldPtr_ = nullptr;
/// Pointer to the output stream for writing fluctuation2 results
mutable uniquePtr<oFstream> os2Ptr_ = nullptr;
public:
TypeInfo("PostprocessOperation<average>");
@ -190,6 +192,10 @@ public:
return processedRegFieldPtr_();
}
/// write to os stream
bool write(const fileSystem &parDir)const override;
/// @brief Execute average operation on field values
/// @param weights Weight factors for particles
/// @return True if successful
@ -200,6 +206,6 @@ public:
};
}
} // namespace pFlow::postprocessData
#endif //__PostprocessOperationAverage_hpp__

View File

@ -3,8 +3,11 @@
#include "fieldsDataBase.hpp"
#include "operationFunctions.hpp"
namespace pFlow::postprocessData
{
/// Constructs sum processor and initializes result field based on input field type
pFlow::PostprocessOperationSum::PostprocessOperationSum
PostprocessOperationSum::PostprocessOperationSum
(
const dictionary &opDict,
const regionPoints &regPoints,
@ -39,7 +42,7 @@ pFlow::PostprocessOperationSum::PostprocessOperationSum
}
/// Performs weighted sum of field values within each region
bool pFlow::PostprocessOperationSum::execute
bool PostprocessOperationSum::execute
(
const std::vector<span<real>>& weights,
const regionField<real>& volFactor
@ -72,3 +75,6 @@ bool pFlow::PostprocessOperationSum::execute
return true;
}
}

View File

@ -129,7 +129,7 @@ Licence:
#include "regionField.hpp"
#include "includeMask.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
@ -182,6 +182,6 @@ public:
};
}
} // namespace pFlow::postprocessData
#endif //__PostprocessOperationSum_hpp__

View File

@ -28,7 +28,7 @@ Licence:
#include "regionField.hpp"
#include "includeMask.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
template<typename T>
@ -190,6 +190,6 @@ regionField<T> executeFluctuation2Operation
return processedField;
}
} // namespace pFlow
} // namespace pFlow::postprocessData
#endif //__operationFunctions_hpp__

View File

@ -61,7 +61,7 @@ Licence:
#include "Time.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
template<typename T, typename Operator>
@ -86,12 +86,12 @@ private:
word fieldName_;
/// Timestamp when mask was last updated (-1 indicates never updated)
timeValue lastUpdated_ = -1;
TimeValueType lastUpdated_ = -1;
/// Updates the mask based on current field values if needed, returns true if successful
bool updateMask()
{
timeValue t = database().currentTime();
TimeValueType t = database().currentTime();
if( equal( t, lastUpdated_)) return true;
@ -206,11 +206,11 @@ private:
std::vector<bool> mask_;
timeValue lastUpdated_ = -1;
TimeValueType lastUpdated_ = -1;
bool updateMask()
{
timeValue t = database().currentTime();
TimeValueType t = database().currentTime();
if( equal( t, lastUpdated_)) return true;
@ -270,7 +270,7 @@ public:
};
} // pFlow
} // pFlow::postprocessData
#endif //__IncludeMask_hpp__

View File

@ -20,31 +20,34 @@ Licence:
#include "IncludeMask.hpp"
namespace pFlow::postprocessData
{
// real
template class pFlow::IncludeMask<pFlow::real, pFlow::compareOne<pFlow::real, pFlow::lessThanOp> >;
template class pFlow::IncludeMask<pFlow::real, pFlow::compareOne<pFlow::real, pFlow::lessThanEqOp> >;
template class IncludeMask<real, compareOne<real, lessThanOp> >;
template class IncludeMask<real, compareOne<real, lessThanEqOp> >;
template class pFlow::IncludeMask<pFlow::real, pFlow::compareOne<pFlow::real, pFlow::greaterThanOp> >;
template class pFlow::IncludeMask<pFlow::real, pFlow::compareOne<pFlow::real, pFlow::greaterThanEqOp> >;
template class IncludeMask<real, compareOne<real, greaterThanOp> >;
template class IncludeMask<real, compareOne<real, greaterThanEqOp> >;
template class pFlow::IncludeMask<pFlow::real, pFlow::compareOne<pFlow::real, pFlow::equalOp> >;
template class IncludeMask<real, compareOne<real, equalOp> >;
template class pFlow::IncludeMask<pFlow::real, pFlow::compareTwo<pFlow::real, pFlow::betweenOp> >;
template class pFlow::IncludeMask<pFlow::real, pFlow::compareTwo<pFlow::real, pFlow::betweenEqOp> >;
template class IncludeMask<real, compareTwo<real, betweenOp> >;
template class IncludeMask<real, compareTwo<real, betweenEqOp> >;
template class pFlow::IncludeMask<pFlow::real, pFlow::allOp<pFlow::real>>;
template class IncludeMask<real, allOp<real>>;
// realx3
template class pFlow::IncludeMask<pFlow::realx3, pFlow::compareOne<pFlow::realx3, pFlow::lessThanOp> >;
template class pFlow::IncludeMask<pFlow::realx3, pFlow::compareOne<pFlow::realx3, pFlow::lessThanEqOp> >;
template class IncludeMask<realx3, compareOne<realx3, lessThanOp> >;
template class IncludeMask<realx3, compareOne<realx3, lessThanEqOp> >;
template class pFlow::IncludeMask<pFlow::realx3, pFlow::compareOne<pFlow::realx3, pFlow::greaterThanOp> >;
template class pFlow::IncludeMask<pFlow::realx3, pFlow::compareOne<pFlow::realx3, pFlow::greaterThanEqOp> >;
template class IncludeMask<realx3, compareOne<realx3, greaterThanOp> >;
template class IncludeMask<realx3, compareOne<realx3, greaterThanEqOp> >;
template class pFlow::IncludeMask<pFlow::realx3, pFlow::compareOne<pFlow::realx3, pFlow::equalOp> >;
template class IncludeMask<realx3, compareOne<realx3, equalOp> >;
template class pFlow::IncludeMask<pFlow::realx3, pFlow::compareTwo<pFlow::realx3, pFlow::betweenOp> >;
template class pFlow::IncludeMask<pFlow::realx3, pFlow::compareTwo<pFlow::realx3, pFlow::betweenEqOp> >;
template class IncludeMask<realx3, compareTwo<realx3, betweenOp> >;
template class IncludeMask<realx3, compareTwo<realx3, betweenEqOp> >;
// realx4
} // postprocessData

View File

@ -24,7 +24,10 @@ Licence:
#include "fieldsDataBase.hpp"
pFlow::includeMask::includeMask
namespace pFlow::postprocessData
{
includeMask::includeMask
(
const dictionary& dict,
fieldsDataBase& fieldDB
@ -33,7 +36,7 @@ pFlow::includeMask::includeMask
database_(fieldDB)
{}
pFlow::includeMask::includeMask
includeMask::includeMask
(
const word &type,
const dictionary &opDict,
@ -44,7 +47,7 @@ pFlow::includeMask::includeMask
{
}
pFlow::uniquePtr<pFlow::includeMask> pFlow::includeMask::create
uniquePtr<includeMask> includeMask::create
(
const dictionary& opDict,
fieldsDataBase& fieldsDB
@ -97,8 +100,7 @@ pFlow::uniquePtr<pFlow::includeMask> pFlow::includeMask::create
return nullptr;
}
pFlow::uniquePtr<pFlow::includeMask>
pFlow::includeMask::create
uniquePtr<includeMask> includeMask::create
(
const word &type,
const dictionary &opDict,
@ -150,3 +152,5 @@ pFlow::uniquePtr<pFlow::includeMask>
}
return nullptr;
}
}

View File

@ -47,11 +47,15 @@ Licence:
#include "virtualConstructor.hpp"
namespace pFlow
{
class dictionary;
}
namespace pFlow::postprocessData
{
// forward declaration
class fieldsDataBase;
class dictionary;
class includeMask
@ -188,7 +192,7 @@ public:
} // pFlow
} // pFlow::postprocessData
#endif //__IncludeMask_hpp__

View File

@ -24,7 +24,7 @@ Licence:
#include "types.hpp"
#include "dictionary.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
template<typename T>
@ -176,6 +176,6 @@ public:
}
};
}
} // namespace pFlow::postprocessData
#endif //__maskOperation_hpp__

View File

@ -23,66 +23,11 @@ Licence:
#include "regionPoints.hpp"
#include "fieldsDataBase.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
template<typename T>
inline
bool writeField
(
iOstream& os,
timeValue t,
const regionField<T> field,
uint32 threshold,
const T& defValue=T{}
)
{
const auto& regPoints = field.regPoints();
const uint32 n = field.size();
os<<t<<tab;
for(uint32 i=0; i<n; i++)
{
auto numPar = regPoints.indices(i).size();
if(numPar >= threshold)
{
if constexpr(std::is_same_v<T,realx3>)
{
os<<field[i].x()<<' '<<field[i].y()<<' '<<field[i].z()<<tab;
}
else if constexpr( std::is_same_v<T,realx4>)
{
os << field[i].x() << ' ' << field[i].y() << ' ' << field[i].z() << ' ' << field[i].w() << tab;
}
else
{
os<<field[i]<<tab;
}
}
else
{
if constexpr(std::is_same_v<T,realx3>)
{
os<<defValue.x()<<' '<<defValue.y()<<' '<<defValue.z()<<tab;
}
else if constexpr( std::is_same_v<T,realx4>)
{
os << defValue.x() << ' ' << defValue.y() << ' ' << defValue.z() << ' ' << defValue.w() << tab;
}
else
{
os<<defValue<<tab;
}
}
}
os<<endl;
return true;
}
}
pFlow::postprocessOperation::postprocessOperation
postprocessOperation::postprocessOperation
(
const dictionary &opDict,
const regionPoints& regPoints,
@ -100,7 +45,7 @@ pFlow::postprocessOperation::postprocessOperation
)
{}
pFlow::postprocessOperation::postprocessOperation
postprocessOperation::postprocessOperation
(
const dictionary &opDict,
const word &fieldName,
@ -147,12 +92,12 @@ pFlow::postprocessOperation::postprocessOperation
fatalExit;
}
}
const pFlow::Time& pFlow::postprocessOperation::time() const
const Time& postprocessOperation::time() const
{
return database_.time();
}
bool pFlow::postprocessOperation::write(const fileSystem &parDir) const
bool postprocessOperation::write(const fileSystem &parDir) const
{
auto ti = time().TimeInfo();
@ -179,11 +124,12 @@ bool pFlow::postprocessOperation::write(const fileSystem &parDir) const
return true;
}
pFlow::uniquePtr<pFlow::postprocessOperation>
pFlow::postprocessOperation::create(
uniquePtr<postprocessOperation> postprocessOperation::create
(
const dictionary &opDict,
const regionPoints &regPoints,
fieldsDataBase &fieldsDB)
fieldsDataBase &fieldsDB
)
{
word func = opDict.getVal<word>("function");
word method = angleBracketsNames("PostprocessOperation", func);
@ -209,3 +155,5 @@ pFlow::postprocessOperation::create(
return nullptr;
}
}
}

View File

@ -76,23 +76,19 @@ Licence:
#include "oFstream.hpp"
#include "regionField.hpp"
#include "includeMask.hpp"
#include "postprocessOperationFunctions.hpp"
namespace pFlow
{
class Time;
}
/// Type alias for processed region field types.
/// Only regionField<real>, regionField<realx3>, and regionField<realx4> are supported
/// in the postprocessOperation class.
using processedRegFieldType = std::variant
<
regionField<real>,
regionField<realx3>,
regionField<realx4>
>;
namespace pFlow::postprocessData
{
/// - forward declaration
class fieldsDataBase;
class Time;
class postprocessOperation
{
@ -103,7 +99,7 @@ public:
private:
/// Dictionary containing operation-specific parameters.
dictionary operationDict_;
pFlow::dictionary operationDict_;
/// This Threshold is used to exclude the regions which contain
/// fewer than this value.
@ -273,6 +269,6 @@ public:
};
}
} // namespace pFlow::postprocessData
#endif //__postprocessOperation_hpp__

View File

@ -0,0 +1,100 @@
/*------------------------------- phasicFlow ---------------------------------
O C enter of
O O E ngineering and
O O M ultiscale modeling of
OOOOOOO F luid flow
------------------------------------------------------------------------------
Copyright (C): www.cemf.ir
email: hamid.r.norouzi AT gmail.com
------------------------------------------------------------------------------
Licence:
This file is part of phasicFlow code. It is a free software for simulating
granular and multiphase flows. You can redistribute it and/or modify it under
the terms of GNU General Public License v3 or any other later versions.
phasicFlow is distributed to help others in their research in the field of
granular and multiphase flows, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----------------------------------------------------------------------------*/
#ifndef __postprocessOperationFunctions_hpp__
#define __postprocessOperationFunctions_hpp__
#include <variant>
#include "types.hpp"
#include "iOstream.hpp"
#include "regionField.hpp"
namespace pFlow::postprocessData
{
/// Type alias for processed region field types.
/// Only regionField<real>, regionField<realx3>, and regionField<realx4> are supported
/// in the postprocessOperation class.
using processedRegFieldType = std::variant
<
regionField<real>,
regionField<realx3>,
regionField<realx4>
>;
template<typename T>
inline
bool writeField
(
iOstream& os,
TimeValueType t,
const regionField<T> field,
uint32 threshold,
const T& defValue=T{}
)
{
const auto& regPoints = field.regPoints();
const uint32 n = field.size();
os<<t<<tab;
for(uint32 i=0; i<n; i++)
{
auto numPar = regPoints.indices(i).size();
if(numPar >= threshold)
{
if constexpr(std::is_same_v<T,realx3>)
{
os<<field[i].x()<<' '<<field[i].y()<<' '<<field[i].z()<<tab;
}
else if constexpr( std::is_same_v<T,realx4>)
{
os << field[i].x() << ' ' << field[i].y() << ' ' << field[i].z() << ' ' << field[i].w() << tab;
}
else
{
os<<field[i]<<tab;
}
}
else
{
if constexpr(std::is_same_v<T,realx3>)
{
os<<defValue.x()<<' '<<defValue.y()<<' '<<defValue.z()<<tab;
}
else if constexpr( std::is_same_v<T,realx4>)
{
os << defValue.x() << ' ' << defValue.y() << ' ' << defValue.z() << ' ' << defValue.w() << tab;
}
else
{
os<<defValue<<tab;
}
}
}
os<<endl;
return true;
}
} // namespace pFlow::postprocessData
#endif //__postprocessOperationFunctions_hpp__

View File

@ -20,7 +20,7 @@ Licence:
-----------------------------------------------------------------------------*/
template<typename RegionType, typename ProcessMethodType>
pFlow::PostprocessComponent<RegionType,ProcessMethodType>::PostprocessComponent
pFlow::postprocessData::PostprocessComponent<RegionType,ProcessMethodType>::PostprocessComponent
(
const dictionary& dict,
fieldsDataBase& fieldsDB,
@ -61,7 +61,7 @@ pFlow::PostprocessComponent<RegionType,ProcessMethodType>::PostprocessComponent
template <typename RegionType, typename ProcessMethodType>
bool pFlow::PostprocessComponent<RegionType, ProcessMethodType>::execute
bool pFlow::postprocessData::PostprocessComponent<RegionType, ProcessMethodType>::execute
(
const timeInfo &ti,
bool forceUpdate
@ -129,7 +129,7 @@ bool pFlow::PostprocessComponent<RegionType, ProcessMethodType>::execute
template <typename RegionType, typename ProcessMethodType>
inline
bool pFlow::PostprocessComponent<RegionType, ProcessMethodType>::write
bool pFlow::postprocessData::PostprocessComponent<RegionType, ProcessMethodType>::write
(
const fileSystem &parDir
) const

View File

@ -32,7 +32,7 @@ Licence:
#include "regionPoints.hpp"
#include "regionField.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
template<typename RegionType, typename ProcessMethodType>

View File

@ -24,7 +24,7 @@ Licence:
#include "PostprocessComponent.hpp"
#include "arithmetic.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
template<typename RegionType>

View File

@ -25,7 +25,7 @@ Licence:
#include "GaussianDistribution.hpp"
#include "numericConstants.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
template<typename RegionType>

View File

@ -24,7 +24,7 @@ Licence:
#include "PostprocessComponent.hpp"
#include "uniformDistribution.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
template<typename RegionType>

View File

@ -27,16 +27,18 @@ Licence:
#include "lineRegionPoints.hpp"
#include "multipleSpheresRegionPoints.hpp"
namespace pFlow::postprocessData
{
template class PostprocessComponentGaussian<sphereRegionPoints>;
template class PostprocessComponentUniform<sphereRegionPoints>;
template class PostprocessComponentArithmetic<sphereRegionPoints>;
template class pFlow::PostprocessComponentGaussian<pFlow::sphereRegionPoints>;
template class pFlow::PostprocessComponentUniform<pFlow::sphereRegionPoints>;
template class pFlow::PostprocessComponentArithmetic<pFlow::sphereRegionPoints>;
template class PostprocessComponentGaussian<multipleSpheresRegionPoints>;
template class PostprocessComponentUniform<multipleSpheresRegionPoints>;
template class PostprocessComponentArithmetic<multipleSpheresRegionPoints>;
template class pFlow::PostprocessComponentGaussian<pFlow::multipleSpheresRegionPoints>;
template class pFlow::PostprocessComponentUniform<pFlow::multipleSpheresRegionPoints>;
template class pFlow::PostprocessComponentArithmetic<pFlow::multipleSpheresRegionPoints>;
template class pFlow::PostprocessComponentGaussian<pFlow::lineRegionPoints>;
template class pFlow::PostprocessComponentUniform<pFlow::lineRegionPoints>;
template class pFlow::PostprocessComponentArithmetic<pFlow::lineRegionPoints>;
template class PostprocessComponentGaussian<lineRegionPoints>;
template class PostprocessComponentUniform<lineRegionPoints>;
template class PostprocessComponentArithmetic<lineRegionPoints>;
}

View File

@ -1,7 +1,27 @@
/*------------------------------- phasicFlow ---------------------------------
O C enter of
O O E ngineering and
O O M ultiscale modeling of
OOOOOOO F luid flow
------------------------------------------------------------------------------
Copyright (C): www.cemf.ir
email: hamid.r.norouzi AT gmail.com
------------------------------------------------------------------------------
Licence:
This file is part of phasicFlow code. It is a free software for simulating
granular and multiphase flows. You can redistribute it and/or modify it under
the terms of GNU General Public License v3 or any other later versions.
phasicFlow is distributed to help others in their research in the field of
granular and multiphase flows, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----------------------------------------------------------------------------*/
#include "particleProbePostprocessComponent.hpp"
#include "Time.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
template<typename T>
@ -32,7 +52,7 @@ template<typename T>
inline bool writeField
(
iOstream& os,
timeValue t,
TimeValueType t,
const regionField<T>& field,
const regionPoints& regPoints,
const T& invalidVal = T{}
@ -62,7 +82,7 @@ inline bool writeField
}
pFlow::particleProbePostprocessComponent::particleProbePostprocessComponent
pFlow::postprocessData::particleProbePostprocessComponent::particleProbePostprocessComponent
(
const dictionary &dict,
fieldsDataBase &fieldsDB,
@ -81,7 +101,7 @@ pFlow::particleProbePostprocessComponent::particleProbePostprocessComponent
name_(dict.name())
{}
bool pFlow::particleProbePostprocessComponent::execute
bool pFlow::postprocessData::particleProbePostprocessComponent::execute
(
const timeInfo &ti,
bool forceExecute
@ -126,7 +146,7 @@ bool pFlow::particleProbePostprocessComponent::execute
}
bool pFlow::particleProbePostprocessComponent::write(const fileSystem& parDir)const
bool pFlow::postprocessData::particleProbePostprocessComponent::write(const fileSystem& parDir)const
{
if(! executed_ ) return true;

View File

@ -27,7 +27,7 @@ Licence:
#include "regionField.hpp"
#include "oFstream.hpp"
namespace pFlow
namespace pFlow::postprocessData
{

View File

@ -1,9 +1,29 @@
/*------------------------------- phasicFlow ---------------------------------
O C enter of
O O E ngineering and
O O M ultiscale modeling of
OOOOOOO F luid flow
------------------------------------------------------------------------------
Copyright (C): www.cemf.ir
email: hamid.r.norouzi AT gmail.com
------------------------------------------------------------------------------
Licence:
This file is part of phasicFlow code. It is a free software for simulating
granular and multiphase flows. You can redistribute it and/or modify it under
the terms of GNU General Public License v3 or any other later versions.
phasicFlow is distributed to help others in their research in the field of
granular and multiphase flows, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----------------------------------------------------------------------------*/
#include "postprocessComponent.hpp"
#include "fieldsDataBase.hpp"
#include "Time.hpp"
pFlow::postprocessComponent::postprocessComponent
pFlow::postprocessData::postprocessComponent::postprocessComponent
(
const dictionary &dict,
fieldsDataBase &fieldsDB,
@ -23,7 +43,7 @@ pFlow::postprocessComponent::postprocessComponent
}
pFlow::uniquePtr<pFlow::postprocessComponent> pFlow::postprocessComponent::create
pFlow::uniquePtr<pFlow::postprocessData::postprocessComponent> pFlow::postprocessData::postprocessComponent::create
(
const dictionary& dict,
fieldsDataBase& fieldsDB,

View File

@ -25,12 +25,17 @@ Licence:
#include "dictionary.hpp"
#include "virtualConstructor.hpp"
namespace pFlow
namespace
{
class dictionary;
}
namespace pFlow::postprocessData
{
class fieldsDataBase;
class regionPoints;
class dictionary;
class fieldsDataBase;
class postprocessComponent
{
@ -112,6 +117,6 @@ public:
};
} // namespace pFlow
} // namespace pFlow::postprocessData
#endif // __postprocessComponent_hpp__

View File

@ -1,4 +1,3 @@
/*------------------------------- phasicFlow ---------------------------------
O C enter of
O O E ngineering and
@ -25,10 +24,10 @@ Licence:
#include "postprocessGlobals.hpp"
#include "postprocessComponent.hpp"
pFlow::postprocessData::postprocessData
pFlow::postprocessData::postprocessData::postprocessData
(
const systemControl &control,
timeValue startTime
TimeValueType startTime
)
:
auxFunctions(control),
@ -45,13 +44,13 @@ pFlow::postprocessData::postprocessData
)
)
{
postProcessGlobals::defaultDir__ = CWD()/pFlow::postProcessGlobals::defaultRelDir__;
defaultDir__ = CWD()/defaultRelDir__;
// if dictionary is not provided, no extra action is required.
if( !dict_.fileExist() || !dict_.headerOk() )
{
WARNING<<"You requested postprocessData function while,"
<<" the dictionary system/postprocessDataDict does not exist."
<<" the dictionary settings/postprocessDataDict does not exist."
<<" This feature is disabled in the current run."<<END_WARNING;
return;
}
@ -98,7 +97,7 @@ pFlow::postprocessData::postprocessData
}
bool pFlow::postprocessData::execute()
bool pFlow::postprocessData::postprocessData::execute()
{
if( inSimulation_ && !activeInSimulation_() ) return true;
@ -118,7 +117,7 @@ bool pFlow::postprocessData::execute()
return true;
}
bool pFlow::postprocessData::write() const
bool pFlow::postprocessData::postprocessData::write() const
{
if( inSimulation_ && !activeInSimulation_() ) return true;
@ -129,7 +128,7 @@ bool pFlow::postprocessData::write() const
continue;
}
if(!component->write(postProcessGlobals::defaultDir__/component->name()))
if(!component->write(defaultDir__/component->name()))
{
fatalErrorInFunction
<<"Error occured in writing postprocess component: "
@ -140,7 +139,7 @@ bool pFlow::postprocessData::write() const
return true;
}
void pFlow::postprocessData::setOutputDirectory(const fileSystem &path) const
void pFlow::postprocessData::postprocessData::setOutputDirectory(const fileSystem &path) const
{
postProcessGlobals::defaultDir__ = path;
defaultDir__ = path;
}

View File

@ -28,14 +28,18 @@ Licence:
#include "fieldsDataBase.hpp"
#include "postprocessComponent.hpp"
namespace pFlow
namespace pFlow
{
class systemControl;
class Time;
class timeInfo;
}
namespace pFlow::postprocessData
{
/**
* @class postprocessData
@ -81,7 +85,7 @@ public:
/// this constructor is used when postprocesing is active
/// during simulation.
/// @param control const reference to systemControl
postprocessData(const systemControl& control, timeValue startTime = -1.0);
postprocessData(const systemControl& control, TimeValueType startTime = -1.0);
~postprocessData()override = default;
@ -109,6 +113,6 @@ public:
void setOutputDirectory(const fileSystem& path)const;
};
} // namespace pFlow
} // namespace pFlow::postprocessData
#endif // __postprocessData_hpp__

View File

@ -23,7 +23,7 @@ Licence:
#include "fileSystem.hpp"
namespace pFlow::postProcessGlobals
namespace pFlow::postprocessData
{
static fileSystem defaultDir__;

View File

@ -22,9 +22,11 @@ Licence:
#define __postprocessTimeControl_hpp__
#include "baseTimeControl.hpp"
#include "dictionary.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
class postprocessTimeControl
:
public baseTimeControl
@ -60,6 +62,6 @@ postprocessTimeControl(
// Additional methods and members can be added here
};
} // namespace pFlow
} // namespace pFlow::postprocessData
#endif // __postprocessTimeControl_hpp__

View File

@ -28,7 +28,7 @@ Licence:
#include "span.hpp"
#include "numericConstants.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
class GaussianDistribution

View File

@ -32,6 +32,9 @@ namespace pFlow
class dictionary;
namespace postprocessData
{
class arithmetic
{
private:
@ -86,6 +89,8 @@ public:
};
}
} // namespace postprocessData
} // namespace pFlow
#endif //__arithmetic_hpp__

View File

@ -32,6 +32,9 @@ namespace pFlow
class dictionary;
namespace postprocessData
{
class uniformDistribution
{
private:
@ -86,6 +89,8 @@ public:
};
}
} // namespace postprocessData
} // namespace pFlow
#endif //__uniformDistribution_hpp__

View File

@ -2,7 +2,10 @@
The `PostprocessData` module in phasicFlow provides powerful tools for analyzing particle-based simulations both during runtime (in-simulation) and after simulation completion (post-simulation). This document explains how to configure and use the postprocessing features through the dictionary-based input system.
## Overview
- in-simulation: this is postprocessing that is active during simulation. When running a solver, it allows for real-time data analysis and adjustments based on the simulation's current state. See below to see how you can activate in-simulation postprocessing.
- post-simulation: this is postprocessing that is done after the simulation is completed. It allows for detailed analysis of the simulation results, including data extraction and visualization based on the results that are stored in time-folders. If you want to use post-simulation, you need to run utility `postprocessPhasicFlow` in terminal (in the simulation case setup folder) to run the postprocessing. This utility reads the `postprocessDataDict` file and performs the specified operations on the simulation data.
## 1. Overview
Postprocessing in phasicFlow allows you to:
@ -12,11 +15,36 @@ Postprocessing in phasicFlow allows you to:
- Apply different weighing methods when calculating statistics
- Perform postprocessing at specific time intervals
## Setting Up Postprocessing
## Table of Contents
- [1. Overview](#1-overview)
- [2. Setting Up Postprocessing](#2-setting-up-postprocessing)
- [2.1. Basic Configuration](#21-basic-configuration)
- [3. Time Control Options](#3-time-control-options)
- [4. Processing Methods](#4-processing-methods)
- [5. Region Types](#5-region-types)
- [6. Processing Operations](#6-processing-operations)
- [6.1. Available Functions in average](#61-available-functions-in-average)
- [6.2. About fluctuation2 in average function](#62-about-fluctuation2-in-average-function)
- [6.3. Derived Functions](#63-derived-functions)
- [6.4. Available Fields](#64-available-fields)
- [6.5. Optional Parameters](#65-optional-parameters)
- [7. Examples](#7-examples)
- [7.1. Example 1: Probing Individual Particles](#71-example-1-probing-individual-particles)
- [7.2. Example 2: Processing in a Spherical Region](#72-example-2-processing-in-a-spherical-region)
- [7.3. Example 3: Processing Along a Line](#73-example-3-processing-along-a-line)
- [8. Advanced Features](#8-advanced-features)
- [8.1. Special functions applied on fields](#81-special-functions-applied-on-fields)
- [8.2. Particle Filtering with includeMask](#82-particle-filtering-with-includemask)
- [8.3. Implementation Notes](#83-implementation-notes)
- [9. Mathematical Formulations](#9-mathematical-formulations)
- [10. A complete dictionary file (postprocessDataDict)](#10-a-complete-dictionary-file-postprocessdatadict)
## 2. Setting Up Postprocessing
Postprocessing is configured through a dictionary file named `postprocessDataDict` which should be placed in the `settings` directory. Below is a detailed explanation of the configuration options.
### Basic Configuration
### 2.1. Basic Configuration
The input dictionary, **settings/postprocessDataDict**, may look like this:
@ -46,7 +74,8 @@ components
);
```
If you want to activate in-simulaiton postprocessing, you need to add these lines to the `settings/settingsDict` file:
If you want to activate in-simulation postprocessing, you need to add these lines to the `settings/settingsDict` file:
```cpp
libs ("libPostprocessData.so");
@ -56,7 +85,7 @@ auxFunctions postprocessData;
This will link the postprocessing library to your simulation, allowing you to use its features. Note that, anytime you want to deactivate the in-simulation postprocessing, you can simply change the `runTimeActive` option to `no` in `postprocessDataDict` file.
## Time Control Options
## 3. Time Control Options
Each postprocessing component can either use the default time control settings or define its own. There are three main options for time control:
@ -69,7 +98,7 @@ Each postprocessing component can either use the default time control settings o
If no time control is specified, the `default` option is used automatically.
## Processing Methods
## 4. Processing Methods
The postprocessing module provides several methods for processing particle data. They are categorized into two main groups: bulk and individual methods.
@ -83,7 +112,7 @@ The postprocessing module provides several methods for processing particle data.
| `GaussianDistribution` | bulk | Weight contribution based on distance from center with Gaussian falloff | $w_i = \exp(-\|x_i - c\|^2/(2\sigma^2))/\sqrt{2\pi\sigma^2}$ |
| `particleProbe` | individual | Extracts values from specific particles | Direct access to particle properties |
## Region Types
## 5. Region Types
Regions define where in the domain the postprocessing operations are applied:
@ -94,24 +123,24 @@ Regions define where in the domain the postprocessing operations are applied:
| `line` | Spheres along a line with specified radius | `p1`, `p2`, `nSpheres`, `radius` | bulk |
| `centerPoints` | Specific particles selected by ID | `ids` | individual |
## Processing Operations
## 6. Processing Operations
Within each processing region of type `bulk`, you can define multiple operations to be performed:
### Available Functions
### 6.1. Available Functions in average
| Function | Property type | Description | Formula | Required Parameters |
|----------|---------------|-------------|---------|---------------------|
| `average` | bulk | Weighted average of particle field values | see Equation 1 | `field`, `phi` (optional), `threshold` (optional), `includeMask` (optional), `divideByVolume` (optional) |
| `average` | bulk | Weighted average of particle field values | see Equation 1 | `field`, `phi` (optional), `threshold` (optional), `includeMask` (optional), `divideByVolume` (optional), `fluctuation2` (optional) |
| `sum` | bulk | Weighted sum of particle field values | see Equation 2 | `field`, `phi` (optional),`threshold` (optional), `includeMask` (optional), `divideByVolume` (optional) |
Equation 1:
$$\text{result} = \frac{\sum_j w_j \cdot \phi_j \cdot \text{field}_j}{\sum_i w_i \cdot \phi_i}$$
$$\text{mean} = \frac{\sum_j w_j \cdot \phi_j \cdot \text{field}_j}{\sum_i w_i \cdot \phi_i}$$
Equation 2:
$$\text{result} = \sum_j w_j \cdot \phi_j \cdot \text{field}_j$$
$$\text{sum} = \sum_j w_j \cdot \phi_j \cdot \text{field}_j$$
where:
@ -121,16 +150,28 @@ where:
- $\phi_j$ is the value of the `phi` field for particle $j$ (default is 1)
- $field_j$ is the value of the specified field for particle $j$
### 6.2. About fluctuation2 in average function
### Derived Functions
Fluctuation2 is an optional parameter that can be used to account for fluctuations in the particle field values with respect to mean value of the field.
It is used in the `average` function to calculate the fluctuation of the field values around the mean. The formula for fluctuation2 is:
$$\text{fluctuation}^2 = \frac{\sum_j w_j \cdot \phi_j \cdot (\text{field}_j - \text{mean})^2}{\sum_i w_i \cdot \phi_i}$$
where:
- `mean`: is the average value of the field in the region.
- `field`: The field to be processed (e.g., `velocity`, `mass`, etc.)
- `fluctuation2`: Optional parameter to account for fluctuations in the particle field values.
### 6.3. Derived Functions
In addition to the above basic functions, some derived functions are available for specific calculations:
| Function | Property type | Description | Formula | Required Parameters |
|----------|---------------|-------------|---------|---------------------|
|`avMassVelocity` | bulk | Average velocity weighted by mass | $\frac{\sum_{i \in \text{region}} m_i \cdot v_i}{\sum_{i \in \text{region}} m_i}$ | - |
|`avMassVelocity` | bulk | Average velocity weighted by mass | $\frac{\sum_{i \in \text{region}} w_i \cdot m_i \cdot v_i}{\sum_{i \in \text{region}} w_i \cdot m_i}$ | - |
### Available Fields
### 6.4. Available Fields
All the pointFields in the simulation database (for in-simulation processing), or the ones stored in the time folders (for post-simulation processing) can be referenced in the operations. In addition to them, some extra fields are available for use in the operations. The following fields are available for use in the operations:
@ -161,17 +202,19 @@ All the pointFields in the simulation database (for in-simulation processing), o
The above fields may vary from one type of simulation to other. Pleas note that this is only a tentative list.
### Optional Parameters
### 6.5. Optional Parameters
| Parameter | Description | Default | Options |
|-----------|-------------|---------|---------|
| `divideByVolume` | Divide result by region volume | `no` | `yes`, `no` |
| `divideByVolume` | Divide result by region volume | `no` | `yes` or `no` |
| `threshold` | Exclude regions with fewer particles | 1 | Integer value |
| `includeMask` | Filter particles based on a field value | `all` | `all`, `lessThan`, `greaterThan`, `between`, `lessThanOrEq`, `greaterThanEq`, `betweenEq` |
| `includeMask` | Filter particles based on a field value | `all` | `all`, `lessThan`, `greaterThan`, `between`, `lessThanOrEq`, `greaterThanOrEq`, `betweenEq` |
|`fluctuation2` (in average only)| Calculate fluctuation of field values | `no` | `yes` or `no` |
| `phi` | Field to be used for weighted averaging | `one` | Any valid field name |
## Examples
## 7. Examples
### Example 1: Probing Individual Particles
### 7.1. Example 1: Probing Individual Particles
```cpp
velocityProb
@ -187,7 +230,7 @@ velocityProb
This example extracts the y-component of the position for particles with IDs 0, 10, and 100.
### Example 2: Processing in a Spherical Region
### 7.2. Example 2: Processing in a Spherical Region
```cpp
on_single_sphere
@ -210,6 +253,7 @@ on_single_sphere
function average;
field mag(velocity);
divideByVolume no;
fluctuation2 yes;
threshold 3;
includeMask all;
}
@ -246,9 +290,9 @@ This example defines a sphere region and performs three operations:
2. Calculate the fraction of particles with diameter less than 0.0031
3. Calculate the number density by summing and dividing by volume
### Example 3: Processing Along a Line
### 7.3. Example 3: Processing Along a Line
In this example, a line region is defined. The `lineInfo` section specifies the start and end points of the line, the number of spheres to create along the line, and the radius of each point. Bulk properties are calculated in each sphere, based on the properties of particles contained in each sphere.
In this example, a line region is defined. The `lineInfo` section specifies the start and end points of the line, the number of spheres to create along the line, and the radius of each point. Bulk properties are calculated in each sphere, based on the properties of particles contained in each sphere.
```cpp
along_a_line
@ -291,9 +335,9 @@ along_a_line
This example creates 10 spherical regions along a line from (0,0,0) to (0,0.15,0.15) and calculates the bulk density and volume density in each region.
## Advanced Features
## 8. Advanced Features
### Special functions applied on fields
### 8.1. Special functions applied on fields
You can access specific components of vector fields (`realx3`) using the `component` function:
@ -315,8 +359,7 @@ Here is a complete list of these special functions:
| `magnitude cube` | `realx3` | `magCube(velocity)` |
| `magnitude square root` | `realx3` | `magSqrt(acceleration)` |
### Particle Filtering with includeMask
### 8.2. Particle Filtering with includeMask
The `includeMask` parameter allows you to filter particles based on field values:
@ -339,7 +382,7 @@ Supported masks:
- `greaterThanOrEq`: Include particles where field value
- `betweenEq`: Include particles where value1 field value2
## Implementation Notes
### 8.3. Implementation Notes
- The postprocessing system can work both during simulation (`runTimeActive yes`) or after simulation completion.
- When using post-simulation mode, you must specify the correct `shapeType` to properly initialize the shape objects.
@ -347,17 +390,180 @@ Supported masks:
- The `threshold` parameter helps eliminate statistical noise in regions with few particles.
- Setting `divideByVolume` to `yes` normalizes results by the volume of the region, useful for calculating densities.
## Mathematical Formulations
## 9. Mathematical Formulations
For weighted `bulk` properties calculation:
For weighted `bulk` properties calculation, we have these two general formulations:
$$ \text{average} = \frac{\sum_{i \in \text{region and includeMask}} w_i \cdot \phi_i \cdot \text{field}_i}{\sum_{i \in \text{region}} w_i \cdot \phi_i} $$
- For weighted averaging:
For weighted summing:
$$ \text{average} = \frac{\sum_j w_j \cdot \phi_j \cdot \text{field}_j}{\sum_i w_i \cdot \phi_i} $$
$$ \text{sum} = \sum_{i \in \text{region and includeMask}} w_i \cdot \phi_i \cdot \text{field}_i $$
- For weighted summing:
$$ \text{sum} = \sum_j w_j \cdot \phi_j \cdot \text{field}_j $$
If `divideByVolume` is set to `yes`, the result is divided by the volume of the region:
$$ \text{volumetric result} = \frac{\text{result}}{V_{\text{region}}} $$
## 10. A complete dictionary file (postprocessDataDict)
```C++
/* -------------------------------*- C++ -*--------------------------------- *\
| phasicFlow File |
| copyright: www.cemf.ir |
\* ------------------------------------------------------------------------- */
objectName postprocessDataDict;
objectType dictionary;;
fileFormat ASCII;
/*---------------------------------------------------------------------------*/
// Yes: postprocessing is active during the simulation
// No: postprocessing is not active during the simulation
// and it can be done after simulation
runTimeActive yes;
// shapeType: defines the type of the shape that is used in the simulation
// (for example: sphere, grain, etc).
// shapeType is only used when postprocessing is done after simulation
// to initialize the shape object for post processing operatoins
shapeType sphere;
// default time control to be used in the postprocessing components
defaultTimeControl
{
timeControl timeStep; // timeStep, simulationTime are the options here
startTime 0;
endTime 1000;
executionInterval 150;
}
// list of postprocessing components
components
(
// probing particles for their state variables, like velocity, position, etc
velocityProb
{
processMethod particleProbe;
processRegion centerPoints;
selector id;
field component(position,y);
ids (0 10 100);
timeControl default; // other options are settings, timeStep, simulationTime
// settings: uses parameters from settingsDict file
// timeStep: uses the time step of the simulation controlling the execution of postprocessing
// simulationTime: uses the simulation time of the simulation controlling the execution of postprocessing
// default: uses the default time control (defined in defaultTimeControl).
// default behavior: if you do not specify it, parameters in defaultTimeControl is used.
}
on_single_sphere
{
// method of performing the sum (arithmetic, uniformDistribution, GaussianDistribution)
processMethod arithmetic;
// Postprocessing is done on particles whose centers are inside this spehre
processRegion sphere;
sphereInfo
{
radius 0.01; // radius of sphere
center (-0.08 -0.08 0.015); // center of sphere
}
timeControl default;
/// all the postprocess operations to be done on sphere region
operations
(
// computes the arithmetic mean of particle velocity
averageVel
{
function average;
field velocity;
fluctuation2 yes;
divideByVolume no; // default is no
threshold 3; // default is 1
includeMask all; // default is all
}
// - function: average, sum, and other derived ones from sum and average
// - field: names of the fields in the simulation. Some special fields
// are: mass, density, volume, position, one, I.
// - divideByVolume: whether the result is divided by the volume of the region
// - threshold: exclude regions that contains particles less than threshold
// - includeMask: all, lessThan, greaterThan, between, lessThanOrEq, greaterThanEq, betweenEq
// computes the fraction of par1 in the region
par1Fraction
{
function average;
field one; // default
phi one; // default
divideByVolume no;
includeMask lessThan;
// diameter of par1 is 0.003, so these settings
// will select only particles of type par1
lessThanInfo
{
field diameter;
value 0.0031;
}
}
numberDensity
{
function sum;
field one;
phi one;
divideByVolume yes;
}
);
}
along_a_line
{
processMethod arithmetic;
processRegion line;
// the time interval for executing the post-processing
// other options: timeStep, default, and settings
timeControl simulationTime;
startTime 1.0;
endTime 3.0;
executionInterval 0.1;
// 10 spheres with radius 0.01 along the straight line defined by p1 and p2
lineInfo
{
p1 (0 0 0);
p2 (0 0.15 0.15);
nSpheres 10;
radius 0.01;
}
operations
(
// computes the arithmetic mean of particle velocity
numberDensity
{
function sum;
field one;
divideByVolume yes; //default is no
}
volumeDensity
{
function sum;
field volume; //
divideByVolume yes; //default is no
}
);
}
);
```

View File

@ -25,7 +25,7 @@ Licence:
#include "regionPoints.hpp"
#include "Field.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
template<typename T>
@ -119,7 +119,7 @@ public:
};
} // namespace pFlow
} // namespace pFlow::postprocessData
#include "regionFieldTemplate.cpp"

View File

@ -1,10 +1,14 @@
namespace pFlow::postprocessData
{
template<typename T>
pFlow::regionField<T>::regionField(
regionField<T>::regionField(
const word& name,
const regionPoints& rPoints,
const T defaultVal)
:
field_(name, "regionFieldValue", rPoints.size(), rPoints.size(), defaultVal),
regionPoints_(rPoints)
{}
{}
} // End namespace pFlow::postprocessData

View File

@ -3,7 +3,10 @@
#include "Set.hpp"
#include "pStructSelector.hpp"
bool pFlow::centerPointsRegionPoints::selectIds()
namespace pFlow::postprocessData
{
bool centerPointsRegionPoints::selectIds()
{
if(!firstTimeUpdate_) return true;
firstTimeUpdate_ = false;
@ -42,7 +45,7 @@ bool pFlow::centerPointsRegionPoints::selectIds()
return true;
}
pFlow::centerPointsRegionPoints::centerPointsRegionPoints(
centerPointsRegionPoints::centerPointsRegionPoints(
const dictionary &dict,
fieldsDataBase &fieldsDataBase)
: regionPoints(dict, fieldsDataBase),
@ -50,7 +53,7 @@ pFlow::centerPointsRegionPoints::centerPointsRegionPoints(
probDict_(dict)
{}
bool pFlow::centerPointsRegionPoints::update()
bool centerPointsRegionPoints::update()
{
if(!selectIds()) return false;
@ -74,7 +77,7 @@ bool pFlow::centerPointsRegionPoints::update()
return true;
}
bool pFlow::centerPointsRegionPoints::write(iOstream &os) const
bool centerPointsRegionPoints::write(iOstream &os) const
{
if(firstTimeUpdate_)
{
@ -95,3 +98,5 @@ bool pFlow::centerPointsRegionPoints::write(iOstream &os) const
return true;
}
} // End namespace pFlow::postprocessData

View File

@ -23,7 +23,7 @@ Licence:
#include "regionPoints.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
/**
@ -163,7 +163,7 @@ public:
}; // class centerPointsRegionPoints
} // namespace pFlow
} // namespace pFlow::postprocessData

View File

@ -1,7 +1,10 @@
#include "lineRegionPoints.hpp"
#include "fieldsDataBase.hpp"
pFlow::lineRegionPoints::lineRegionPoints
namespace pFlow::postprocessData
{
lineRegionPoints::lineRegionPoints
(
const dictionary &dict,
fieldsDataBase &fieldsDataBase
@ -50,7 +53,7 @@ pFlow::lineRegionPoints::lineRegionPoints
}
}
pFlow::span<const pFlow::uint32> pFlow::lineRegionPoints::indices(uint32 elem) const
pFlow::span<const pFlow::uint32> lineRegionPoints::indices(uint32 elem) const
{
if(elem >= size())
{
@ -65,7 +68,7 @@ pFlow::span<const pFlow::uint32> pFlow::lineRegionPoints::indices(uint32 elem) c
selectedPoints_[elem].size());
}
pFlow::span<pFlow::uint32> pFlow::lineRegionPoints::indices(uint32 elem)
pFlow::span<pFlow::uint32> lineRegionPoints::indices(uint32 elem)
{
if(elem >= size())
{
@ -80,7 +83,7 @@ pFlow::span<pFlow::uint32> pFlow::lineRegionPoints::indices(uint32 elem)
selectedPoints_[elem].size());
}
bool pFlow::lineRegionPoints::update()
bool lineRegionPoints::update()
{
const auto points = database().updatePoints();
for(auto& elem : selectedPoints_)
@ -101,7 +104,7 @@ bool pFlow::lineRegionPoints::update()
return true;
}
bool pFlow::lineRegionPoints::write(iOstream &os) const
bool lineRegionPoints::write(iOstream &os) const
{
os << "# Spheres along a straight line \n";
os << "# No." << tab << "centerPoint" << tab << "diameter" << endl;
@ -118,3 +121,5 @@ bool pFlow::lineRegionPoints::write(iOstream &os) const
os << endl;
return true;
}
} // End namespace pFlow::postprocessData

View File

@ -54,7 +54,7 @@ Licence:
#include "Vectors.hpp"
namespace pFlow
namespace pFlow::postprocessData
{
class lineRegionPoints

View File

@ -1,7 +1,10 @@
#include "multipleSpheresRegionPoints.hpp"
#include "fieldsDataBase.hpp"
pFlow::multipleSpheresRegionPoints::multipleSpheresRegionPoints
namespace pFlow::postprocessData
{
multipleSpheresRegionPoints::multipleSpheresRegionPoints
(
const dictionary &dict,
fieldsDataBase &fieldsDataBase
@ -46,7 +49,7 @@ pFlow::multipleSpheresRegionPoints::multipleSpheresRegionPoints
}
}
pFlow::span<const pFlow::uint32> pFlow::multipleSpheresRegionPoints::indices(uint32 elem) const
pFlow::span<const pFlow::uint32> multipleSpheresRegionPoints::indices(uint32 elem) const
{
if (elem >= size())
{
@ -59,7 +62,7 @@ pFlow::span<const pFlow::uint32> pFlow::multipleSpheresRegionPoints::indices(uin
return span<const uint32>(selectedPoints_[elem].data(), selectedPoints_[elem].size());
}
pFlow::span<pFlow::uint32> pFlow::multipleSpheresRegionPoints::indices(uint32 elem)
pFlow::span<pFlow::uint32> multipleSpheresRegionPoints::indices(uint32 elem)
{
if (elem >= size())
{
@ -73,7 +76,7 @@ pFlow::span<pFlow::uint32> pFlow::multipleSpheresRegionPoints::indices(uint32 el
}
bool pFlow::multipleSpheresRegionPoints::update()
bool multipleSpheresRegionPoints::update()
{
const auto points = database().updatePoints();
for (auto& elem : selectedPoints_)
@ -94,7 +97,7 @@ bool pFlow::multipleSpheresRegionPoints::update()
return true;
}
bool pFlow::multipleSpheresRegionPoints::write(iOstream &os) const
bool multipleSpheresRegionPoints::write(iOstream &os) const
{
os << "# Multiple spheres region points\n";
os << "# No." << tab << "centerPoint" << tab << "diameter" << endl;
@ -110,3 +113,5 @@ bool pFlow::multipleSpheresRegionPoints::write(iOstream &os) const
os << endl;
return true;
}
} // End namespace pFlow::postprocessData

Some files were not shown because too many files have changed in this diff Show More