pyFracAggregate 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/.gitignore +1 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/CLAUDE.md +1 -1
- pyfracaggregate-0.3.0/PKG-INFO +133 -0
- pyfracaggregate-0.3.0/README.md +95 -0
- pyfracaggregate-0.3.0/docs/_static/.gitkeep +0 -0
- pyfracaggregate-0.3.0/docs/_static/tutorial_fracval_render.png +0 -0
- pyfracaggregate-0.3.0/docs/_static/tutorial_pca_pcf.png +0 -0
- pyfracaggregate-0.3.0/docs/_static/tutorial_pca_render.png +0 -0
- pyfracaggregate-0.3.0/docs/api-reference/index.md +116 -0
- pyfracaggregate-0.3.0/docs/architecture/index.md +205 -0
- pyfracaggregate-0.3.0/docs/background/index.md +375 -0
- pyfracaggregate-0.3.0/docs/conf.py +83 -0
- pyfracaggregate-0.3.0/docs/contributing.md +75 -0
- pyfracaggregate-0.3.0/docs/index.md +17 -0
- pyfracaggregate-0.3.0/docs/tutorials/basic_usage.md +289 -0
- pyfracaggregate-0.3.0/docs/user-guide/analysis.md +128 -0
- pyfracaggregate-0.3.0/docs/user-guide/generators.md +187 -0
- pyfracaggregate-0.3.0/docs/user-guide/installation.md +78 -0
- pyfracaggregate-0.3.0/docs/user-guide/io.md +106 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/pyproject.toml +32 -1
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/__init__.py +12 -1
- pyfracaggregate-0.2.0/PKG-INFO +0 -24
- pyfracaggregate-0.2.0/README.md +0 -3
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/LICENSE +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/benchmarks/__init__.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/benchmarks/analyze.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/benchmarks/grids.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/benchmarks/metrics.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/benchmarks/run.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/benchmarks/tests/__init__.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/benchmarks/tests/test_analyze.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/benchmarks/tests/test_grids.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/benchmarks/tests/test_metrics.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/benchmarks/tests/test_run.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/examples/pyFracAggregate_demo.ipynb +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/analysis/__init__.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/analysis/correlation.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/analysis/morphology.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/core/__init__.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/core/aggregate.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/core/distributions.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/core/math_utils.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/generators/__init__.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/generators/base.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/generators/cca.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/generators/factory.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/generators/fracval.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/generators/optimizer_flage.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/generators/pca.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/generators/placement/__init__.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/generators/placement/_helpers.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/generators/placement/algebraic.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/generators/placement/base.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/generators/placement/random_.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/generators/tdcca.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/io/__init__.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/io/data.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/io/visualization.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/src/pyFracAggregate/io/vtk.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/fixtures/surface_beta_snapshot.npy +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_analysis/test_correlation.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_analysis/test_morphology.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_core/test_aggregate.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_core/test_distributions.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_core/test_math_utils.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_density.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_generators/test_benchmark.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_generators/test_cca.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_generators/test_cca_fracval.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_generators/test_factory.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_generators/test_optimizer_flage.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_generators/test_pca.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_generators/test_placement.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_generators/test_surface_beta.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_generators/test_tdcca_thouy.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_io/test_data.py +0 -0
- {pyfracaggregate-0.2.0 → pyfracaggregate-0.3.0}/tests/test_io/test_visualization.py +0 -0
|
@@ -30,7 +30,7 @@ No CLI entry point exists — the package is a library used via `import pyFracAg
|
|
|
30
30
|
- `'pca'` → `PCAGenerator` — particle-cluster aggregation
|
|
31
31
|
- `'cca'` → `CCAGenerator` — cluster-cluster aggregation
|
|
32
32
|
- `'fracval'` → `FracVALGenerator` — FracVAL algorithm
|
|
33
|
-
- `'tdcca'` → `ThouyJullienGenerator` — Thouy & Jullien (
|
|
33
|
+
- `'tdcca'` → `ThouyJullienGenerator` — Thouy & Jullien (1994) algorithm
|
|
34
34
|
- All generators share the same constructor signature from `BaseGenerator`.
|
|
35
35
|
|
|
36
36
|
#### Placement Strategy Layer (`src/pyFracAggregate/generators/placement/`)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pyFracAggregate
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: 通用分形团簇生成框架 (General Fractal Aggregate Generation Framework)
|
|
5
|
+
Project-URL: Homepage, https://github.com/vanvonzhang/pyFracAggregate
|
|
6
|
+
Project-URL: Documentation, https://vanvonzhang.github.io/pyFracAggregate/
|
|
7
|
+
Project-URL: Repository, https://github.com/vanvonzhang/pyFracAggregate
|
|
8
|
+
Project-URL: Issues, https://github.com/vanvonzhang/pyFracAggregate/issues
|
|
9
|
+
Author-email: Fan Zhang <vanvonzhang@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: DLCA,aerosol,cluster-cluster aggregation,fractal aggregate,fractal dimension,nanoparticles,soot
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
18
|
+
Requires-Python: >=3.13
|
|
19
|
+
Requires-Dist: imageio[ffmpeg]>=2.9.0
|
|
20
|
+
Requires-Dist: mathutils>=3.0.0
|
|
21
|
+
Requires-Dist: numpy>=1.21.0
|
|
22
|
+
Requires-Dist: pyvista
|
|
23
|
+
Requires-Dist: pyyaml
|
|
24
|
+
Requires-Dist: scipy>=1.7.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
29
|
+
Provides-Extra: docs
|
|
30
|
+
Requires-Dist: myst-parser>=2.0; extra == 'docs'
|
|
31
|
+
Requires-Dist: sphinx-copybutton>=0.5.0; extra == 'docs'
|
|
32
|
+
Requires-Dist: sphinx-rtd-theme>=2.0; extra == 'docs'
|
|
33
|
+
Requires-Dist: sphinx>=7.0; extra == 'docs'
|
|
34
|
+
Requires-Dist: sphinxcontrib-mermaid>=1.0; extra == 'docs'
|
|
35
|
+
Provides-Extra: plot
|
|
36
|
+
Requires-Dist: matplotlib>=3.5.0; extra == 'plot'
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
# pyFracAggregate
|
|
40
|
+
|
|
41
|
+
[](https://github.com/vanvonzhang/pyFracAggregate/actions/workflows/test.yml)
|
|
42
|
+
[](https://pypi.org/project/pyFracAggregate/)
|
|
43
|
+
[](https://pypi.org/project/pyFracAggregate/)
|
|
44
|
+
[](https://vanvonzhang.github.io/pyFracAggregate/)
|
|
45
|
+
[](LICENSE)
|
|
46
|
+
|
|
47
|
+
A Python library for generating synthetic fractal aggregates — clusters of
|
|
48
|
+
spherical primary particles with a tunable morphology, such as soot and other
|
|
49
|
+
aerosols — unified across four classical generation algorithms behind one API,
|
|
50
|
+
with built-in morphological analysis and export to common scientific formats.
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
- **Four generation algorithms, one API** — particle-cluster aggregation
|
|
55
|
+
(`'pca'`), cluster-cluster aggregation (`'cca'`), FracVAL (`'fracval'`), and
|
|
56
|
+
the Thouy & Jullien tunable CCA (`'tdcca'`), all selected with a single
|
|
57
|
+
`method=` keyword.
|
|
58
|
+
- **Two placement strategies** — FLAGE-style algebraic touching-point
|
|
59
|
+
computation (default) or Monte Carlo random placement with tolerance
|
|
60
|
+
relaxation.
|
|
61
|
+
- **Monodisperse and lognormal primary particles** — `Monodisperse` and
|
|
62
|
+
`LognormalDistribution` size distributions feed any generator.
|
|
63
|
+
- **Built-in morphology analysis** — radius of gyration, center of mass, pair
|
|
64
|
+
correlation function, and fractal-dimension estimation with fit quality
|
|
65
|
+
(`pfa.analyze`).
|
|
66
|
+
- **Rich exports** — YAML snapshot, VTK point cloud and VTM multiblock (via
|
|
67
|
+
pyvista, ready for ParaView), off-screen static render, and rotation video.
|
|
68
|
+
- **Fully typed library with tests** — type hints throughout the source, and a
|
|
69
|
+
pytest suite mirroring the package layout.
|
|
70
|
+
|
|
71
|
+
## Installation
|
|
72
|
+
|
|
73
|
+
```console
|
|
74
|
+
$ pip install pyFracAggregate
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
> **Requires Python ≥ 3.13.** The 3D math dependency `mathutils` only has
|
|
78
|
+
> usable wheels for the 3.13 ABI on several platforms; older interpreters can
|
|
79
|
+
> fail at compile time. See the
|
|
80
|
+
> [installation guide](https://vanvonzhang.github.io/pyFracAggregate/user-guide/installation.html)
|
|
81
|
+
> for details and platform notes.
|
|
82
|
+
|
|
83
|
+
To install from source for development:
|
|
84
|
+
|
|
85
|
+
```console
|
|
86
|
+
$ pip install -e ".[dev]"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Quick start
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
import numpy as np
|
|
93
|
+
import pyFracAggregate as pfa
|
|
94
|
+
|
|
95
|
+
np.random.seed(0)
|
|
96
|
+
agg = pfa.generate(200, 1.8, 1.9, method='pca') # N=200, Df=1.8, kf=1.9
|
|
97
|
+
|
|
98
|
+
summary = pfa.analyze(agg) # Rg=13.274 nm, Df_estimated=1.714, R2=0.964
|
|
99
|
+
print(agg.current_size, summary['Df_estimated']) # 200 1.714241520287105
|
|
100
|
+
|
|
101
|
+
pfa.export_yaml(agg, 'aggregate.yaml')
|
|
102
|
+
pfa.export_vtk(agg, 'aggregate.vtk')
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Generation is stochastic and draws from NumPy's global legacy random state:
|
|
106
|
+
call `np.random.seed(...)` immediately before `pfa.generate(...)` for
|
|
107
|
+
reproducible aggregates. The single-realization `Df_estimated` scatters
|
|
108
|
+
around the requested `df`; average over realizations for ensemble statements.
|
|
109
|
+
|
|
110
|
+
## Methods
|
|
111
|
+
|
|
112
|
+
| Keyword | Algorithm | Family | Polydispersity | Reference |
|
|
113
|
+
|---|---|---|---|---|
|
|
114
|
+
| [`pca`](https://vanvonzhang.github.io/pyFracAggregate/background/index.html#pca-particle-cluster-aggregation) | Particle-cluster aggregation | particle-cluster | approximate (mean radius) | Skorupski et al., 2014 |
|
|
115
|
+
| [`cca`](https://vanvonzhang.github.io/pyFracAggregate/background/index.html#cca-cluster-cluster-aggregation) | Cluster-cluster aggregation | cluster-cluster | approximate (number-weighted) | Filippov et al., 2000 |
|
|
116
|
+
| [`fracval`](https://vanvonzhang.github.io/pyFracAggregate/background/index.html#fracval-tunable-cca-for-polydisperse-primaries) | FracVAL tunable CCA | cluster-cluster | native (mass-weighted) | Morán et al., 2019 |
|
|
117
|
+
| [`tdcca`](https://vanvonzhang.github.io/pyFracAggregate/background/index.html#tdcca-thouy-jullien-tunable-cca) | Thouy & Jullien tunable CCA | cluster-cluster | supported (mass-weighted Rg) | Thouy & Jullien, 1994 |
|
|
118
|
+
|
|
119
|
+
Each keyword links to the corresponding section of the
|
|
120
|
+
[background chapter](https://vanvonzhang.github.io/pyFracAggregate/background/index.html)
|
|
121
|
+
on the documentation site, which derives each algorithm's principle,
|
|
122
|
+
guarantees, and limits.
|
|
123
|
+
|
|
124
|
+
## Documentation
|
|
125
|
+
|
|
126
|
+
Full documentation — background theory, user guide, tutorial, API reference,
|
|
127
|
+
architecture notes, and contributing instructions — is hosted at:
|
|
128
|
+
|
|
129
|
+
[Documentation](https://vanvonzhang.github.io/pyFracAggregate/)
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# pyFracAggregate
|
|
2
|
+
|
|
3
|
+
[](https://github.com/vanvonzhang/pyFracAggregate/actions/workflows/test.yml)
|
|
4
|
+
[](https://pypi.org/project/pyFracAggregate/)
|
|
5
|
+
[](https://pypi.org/project/pyFracAggregate/)
|
|
6
|
+
[](https://vanvonzhang.github.io/pyFracAggregate/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
A Python library for generating synthetic fractal aggregates — clusters of
|
|
10
|
+
spherical primary particles with a tunable morphology, such as soot and other
|
|
11
|
+
aerosols — unified across four classical generation algorithms behind one API,
|
|
12
|
+
with built-in morphological analysis and export to common scientific formats.
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- **Four generation algorithms, one API** — particle-cluster aggregation
|
|
17
|
+
(`'pca'`), cluster-cluster aggregation (`'cca'`), FracVAL (`'fracval'`), and
|
|
18
|
+
the Thouy & Jullien tunable CCA (`'tdcca'`), all selected with a single
|
|
19
|
+
`method=` keyword.
|
|
20
|
+
- **Two placement strategies** — FLAGE-style algebraic touching-point
|
|
21
|
+
computation (default) or Monte Carlo random placement with tolerance
|
|
22
|
+
relaxation.
|
|
23
|
+
- **Monodisperse and lognormal primary particles** — `Monodisperse` and
|
|
24
|
+
`LognormalDistribution` size distributions feed any generator.
|
|
25
|
+
- **Built-in morphology analysis** — radius of gyration, center of mass, pair
|
|
26
|
+
correlation function, and fractal-dimension estimation with fit quality
|
|
27
|
+
(`pfa.analyze`).
|
|
28
|
+
- **Rich exports** — YAML snapshot, VTK point cloud and VTM multiblock (via
|
|
29
|
+
pyvista, ready for ParaView), off-screen static render, and rotation video.
|
|
30
|
+
- **Fully typed library with tests** — type hints throughout the source, and a
|
|
31
|
+
pytest suite mirroring the package layout.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```console
|
|
36
|
+
$ pip install pyFracAggregate
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
> **Requires Python ≥ 3.13.** The 3D math dependency `mathutils` only has
|
|
40
|
+
> usable wheels for the 3.13 ABI on several platforms; older interpreters can
|
|
41
|
+
> fail at compile time. See the
|
|
42
|
+
> [installation guide](https://vanvonzhang.github.io/pyFracAggregate/user-guide/installation.html)
|
|
43
|
+
> for details and platform notes.
|
|
44
|
+
|
|
45
|
+
To install from source for development:
|
|
46
|
+
|
|
47
|
+
```console
|
|
48
|
+
$ pip install -e ".[dev]"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Quick start
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import numpy as np
|
|
55
|
+
import pyFracAggregate as pfa
|
|
56
|
+
|
|
57
|
+
np.random.seed(0)
|
|
58
|
+
agg = pfa.generate(200, 1.8, 1.9, method='pca') # N=200, Df=1.8, kf=1.9
|
|
59
|
+
|
|
60
|
+
summary = pfa.analyze(agg) # Rg=13.274 nm, Df_estimated=1.714, R2=0.964
|
|
61
|
+
print(agg.current_size, summary['Df_estimated']) # 200 1.714241520287105
|
|
62
|
+
|
|
63
|
+
pfa.export_yaml(agg, 'aggregate.yaml')
|
|
64
|
+
pfa.export_vtk(agg, 'aggregate.vtk')
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Generation is stochastic and draws from NumPy's global legacy random state:
|
|
68
|
+
call `np.random.seed(...)` immediately before `pfa.generate(...)` for
|
|
69
|
+
reproducible aggregates. The single-realization `Df_estimated` scatters
|
|
70
|
+
around the requested `df`; average over realizations for ensemble statements.
|
|
71
|
+
|
|
72
|
+
## Methods
|
|
73
|
+
|
|
74
|
+
| Keyword | Algorithm | Family | Polydispersity | Reference |
|
|
75
|
+
|---|---|---|---|---|
|
|
76
|
+
| [`pca`](https://vanvonzhang.github.io/pyFracAggregate/background/index.html#pca-particle-cluster-aggregation) | Particle-cluster aggregation | particle-cluster | approximate (mean radius) | Skorupski et al., 2014 |
|
|
77
|
+
| [`cca`](https://vanvonzhang.github.io/pyFracAggregate/background/index.html#cca-cluster-cluster-aggregation) | Cluster-cluster aggregation | cluster-cluster | approximate (number-weighted) | Filippov et al., 2000 |
|
|
78
|
+
| [`fracval`](https://vanvonzhang.github.io/pyFracAggregate/background/index.html#fracval-tunable-cca-for-polydisperse-primaries) | FracVAL tunable CCA | cluster-cluster | native (mass-weighted) | Morán et al., 2019 |
|
|
79
|
+
| [`tdcca`](https://vanvonzhang.github.io/pyFracAggregate/background/index.html#tdcca-thouy-jullien-tunable-cca) | Thouy & Jullien tunable CCA | cluster-cluster | supported (mass-weighted Rg) | Thouy & Jullien, 1994 |
|
|
80
|
+
|
|
81
|
+
Each keyword links to the corresponding section of the
|
|
82
|
+
[background chapter](https://vanvonzhang.github.io/pyFracAggregate/background/index.html)
|
|
83
|
+
on the documentation site, which derives each algorithm's principle,
|
|
84
|
+
guarantees, and limits.
|
|
85
|
+
|
|
86
|
+
## Documentation
|
|
87
|
+
|
|
88
|
+
Full documentation — background theory, user guide, tutorial, API reference,
|
|
89
|
+
architecture notes, and contributing instructions — is hosted at:
|
|
90
|
+
|
|
91
|
+
[Documentation](https://vanvonzhang.github.io/pyFracAggregate/)
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
[MIT](LICENSE)
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
Complete reference for the 21 public symbols exported by `pyFracAggregate`
|
|
4
|
+
(the package's `__all__`), grouped by layer: the top-level facade, the core
|
|
5
|
+
data structures, the four generation algorithms, the analysis functions, the
|
|
6
|
+
I/O exporters, and the placement strategies.
|
|
7
|
+
|
|
8
|
+
## Top-level API
|
|
9
|
+
|
|
10
|
+
The facade functions cover the common workflow: build an aggregate with
|
|
11
|
+
`generate()` (which dispatches to one of the four generation methods), then
|
|
12
|
+
summarize its morphology with `analyze()`.
|
|
13
|
+
|
|
14
|
+
```{eval-rst}
|
|
15
|
+
.. autofunction:: pyFracAggregate.generate
|
|
16
|
+
|
|
17
|
+
.. autofunction:: pyFracAggregate.analyze
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Core
|
|
21
|
+
|
|
22
|
+
`Aggregate` is the central data structure every other layer produces or
|
|
23
|
+
consumes: a pre-allocated `(max_particles, 5)` NumPy array of
|
|
24
|
+
`[x, y, z, radius, mass]` rows whose `positions`, `radii`, and `masses`
|
|
25
|
+
properties are zero-copy views. The two distribution classes describe
|
|
26
|
+
primary-particle sizes and are passed to generators via the `particle_dist`
|
|
27
|
+
argument.
|
|
28
|
+
|
|
29
|
+
```{eval-rst}
|
|
30
|
+
.. autoclass:: pyFracAggregate.core.aggregate.Aggregate
|
|
31
|
+
:members:
|
|
32
|
+
|
|
33
|
+
.. autoclass:: pyFracAggregate.core.distributions.Monodisperse
|
|
34
|
+
:members:
|
|
35
|
+
|
|
36
|
+
.. autoclass:: pyFracAggregate.core.distributions.LognormalDistribution
|
|
37
|
+
:members:
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Generators
|
|
41
|
+
|
|
42
|
+
Four algorithms share the `BaseGenerator` constructor contract
|
|
43
|
+
`(n_particles, df, kf, particle_dist, overlap_tolerance, placement)` and each
|
|
44
|
+
returns an `Aggregate` from its `generate()` method. Users normally reach them
|
|
45
|
+
through `generate(method=...)`; the classes are public for direct use and
|
|
46
|
+
subclassing.
|
|
47
|
+
|
|
48
|
+
```{eval-rst}
|
|
49
|
+
.. autoclass:: pyFracAggregate.generators.pca.PCAGenerator
|
|
50
|
+
:members:
|
|
51
|
+
|
|
52
|
+
.. autoclass:: pyFracAggregate.generators.cca.CCAGenerator
|
|
53
|
+
:members:
|
|
54
|
+
|
|
55
|
+
.. autoclass:: pyFracAggregate.generators.fracval.FracVALGenerator
|
|
56
|
+
:members:
|
|
57
|
+
|
|
58
|
+
.. autoclass:: pyFracAggregate.generators.tdcca.ThouyJullienGenerator
|
|
59
|
+
:members:
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Analysis
|
|
63
|
+
|
|
64
|
+
Morphological descriptors computed from an `Aggregate`: global quantities
|
|
65
|
+
(radius of gyration, center of mass) and the two-point pair correlation
|
|
66
|
+
function `C(r)`, from which the fractal dimension is estimated by log-log
|
|
67
|
+
regression. `analyze()` bundles the main ones into a summary dict, and
|
|
68
|
+
`plot_pair_correlation()` visualizes the fit (requires matplotlib).
|
|
69
|
+
|
|
70
|
+
```{eval-rst}
|
|
71
|
+
.. autofunction:: pyFracAggregate.analysis.morphology.radius_of_gyration
|
|
72
|
+
|
|
73
|
+
.. autofunction:: pyFracAggregate.analysis.morphology.center_of_mass
|
|
74
|
+
|
|
75
|
+
.. autofunction:: pyFracAggregate.analysis.correlation.pair_correlation_function
|
|
76
|
+
|
|
77
|
+
.. autofunction:: pyFracAggregate.analysis.correlation.estimate_fractal_dimension
|
|
78
|
+
|
|
79
|
+
.. autofunction:: pyFracAggregate.analysis.correlation.plot_pair_correlation
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## I/O
|
|
83
|
+
|
|
84
|
+
Export an `Aggregate` for downstream use: a YAML snapshot bundling the particle
|
|
85
|
+
data with generation parameters and analysis results, VTK/VTM files built with
|
|
86
|
+
pyvista for ParaView and other tools, and off-screen rendered PNG images or
|
|
87
|
+
MP4 rotation videos. The render and video exporters require a working pyvista
|
|
88
|
+
3D backend (see the user guide for headless-environment notes).
|
|
89
|
+
|
|
90
|
+
```{eval-rst}
|
|
91
|
+
.. autofunction:: pyFracAggregate.io.data.export_yaml
|
|
92
|
+
|
|
93
|
+
.. autofunction:: pyFracAggregate.io.visualization.export_render
|
|
94
|
+
|
|
95
|
+
.. autofunction:: pyFracAggregate.io.visualization.export_rotation_video
|
|
96
|
+
|
|
97
|
+
.. autofunction:: pyFracAggregate.io.vtk.export_vtm
|
|
98
|
+
|
|
99
|
+
.. autofunction:: pyFracAggregate.io.vtk.export_vtk
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Placement
|
|
103
|
+
|
|
104
|
+
Placement strategies decide where a new particle or cluster touches the
|
|
105
|
+
existing structure while respecting the overlap tolerance; every generator
|
|
106
|
+
selects one via `placement=`. Both classes implement the same two entry
|
|
107
|
+
points: `place_particle()` for particle-cluster stages and `merge_clusters()`
|
|
108
|
+
for cluster-cluster stages.
|
|
109
|
+
|
|
110
|
+
```{eval-rst}
|
|
111
|
+
.. autoclass:: pyFracAggregate.generators.placement.algebraic.AlgebraicPlacement
|
|
112
|
+
:members:
|
|
113
|
+
|
|
114
|
+
.. autoclass:: pyFracAggregate.generators.placement.random_.RandomPlacement
|
|
115
|
+
:members:
|
|
116
|
+
```
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
pyFracAggregate is a four-layer library: a **core** data structure
|
|
4
|
+
(`Aggregate`, primary-particle distributions), a **generators** layer holding
|
|
5
|
+
the four aggregation algorithms and their shared placement sublayer, an
|
|
6
|
+
**analysis** layer computing morphological descriptors, and an **io** layer
|
|
7
|
+
exporting aggregates. A thin top-level facade (`pfa.generate` /
|
|
8
|
+
`pfa.analyze`) ties the layers together, and a factory function dispatches
|
|
9
|
+
generation requests to one of the four algorithm classes.
|
|
10
|
+
|
|
11
|
+
```{mermaid}
|
|
12
|
+
flowchart TB
|
|
13
|
+
user(["user code"])
|
|
14
|
+
|
|
15
|
+
facade["top-level facade<br/>generate() / analyze()"]
|
|
16
|
+
factory["generators/factory.py<br/>get_generator(method, ...)"]
|
|
17
|
+
|
|
18
|
+
subgraph GENLAYER ["generators/"]
|
|
19
|
+
BASE["BaseGenerator (ABC)"]
|
|
20
|
+
PCA["PCAGenerator"]
|
|
21
|
+
CCA["CCAGenerator"]
|
|
22
|
+
FRACVAL["FracVALGenerator"]
|
|
23
|
+
TJ["ThouyJullienGenerator"]
|
|
24
|
+
subgraph PLACE ["generators/placement/"]
|
|
25
|
+
PABC["PlacementStrategy (ABC)"]
|
|
26
|
+
ALG["AlgebraicPlacement<br/>(FLAGE, default)"]
|
|
27
|
+
RND["RandomPlacement<br/>(Monte Carlo)"]
|
|
28
|
+
MC["_helpers.py<br/>shared Monte Carlo"]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
subgraph CORELAYER ["core/"]
|
|
33
|
+
AGG["Aggregate<br/>pre-allocated (max_particles, 5)<br/>[x, y, z, radius, mass]"]
|
|
34
|
+
DIST["ParticleDistribution<br/>Monodisperse / LognormalDistribution"]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
subgraph ANALYSIS ["analysis/"]
|
|
38
|
+
MORPH["morphology.py<br/>radius_of_gyration / center_of_mass"]
|
|
39
|
+
CORR["correlation.py<br/>pair correlation / Df estimation"]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
subgraph IOLAYER ["io/"]
|
|
43
|
+
YAMLIO["data.py<br/>export_yaml"]
|
|
44
|
+
VTKIO["vtk.py<br/>export_vtk / export_vtm"]
|
|
45
|
+
VISIO["visualization.py<br/>export_render / export_rotation_video"]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
user --> facade
|
|
49
|
+
facade -->|"'method' keyword"| factory
|
|
50
|
+
factory --> PCA
|
|
51
|
+
factory --> CCA
|
|
52
|
+
factory --> FRACVAL
|
|
53
|
+
factory --> TJ
|
|
54
|
+
|
|
55
|
+
PCA & CCA & FRACVAL & TJ -.->|"subclass"| BASE
|
|
56
|
+
DIST -->|"sample() radii"| BASE
|
|
57
|
+
PCA -.->|"place_particle()"| PABC
|
|
58
|
+
CCA -.->|"merge_clusters()"| PABC
|
|
59
|
+
PABC --> ALG
|
|
60
|
+
PABC --> RND
|
|
61
|
+
ALG --> MC
|
|
62
|
+
RND --> MC
|
|
63
|
+
|
|
64
|
+
PCA & CCA & FRACVAL & TJ -->|"generate() returns"| AGG
|
|
65
|
+
|
|
66
|
+
AGG --> MORPH
|
|
67
|
+
AGG --> CORR
|
|
68
|
+
facade -.->|"analyze()"| MORPH
|
|
69
|
+
facade -.->|"analyze()"| CORR
|
|
70
|
+
|
|
71
|
+
AGG --> YAMLIO
|
|
72
|
+
AGG --> VTKIO
|
|
73
|
+
AGG --> VISIO
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Only `pca` and `cca` route through the placement sublayer; `fracval` and
|
|
77
|
+
`tdcca` embed their own contact logic (see below).
|
|
78
|
+
|
|
79
|
+
## The `Aggregate` data structure
|
|
80
|
+
|
|
81
|
+
Every layer produces or consumes one central type,
|
|
82
|
+
[`Aggregate`](/api-reference/index.md#core). It stores all particle data in a
|
|
83
|
+
**single pre-allocated NumPy array** of shape `(max_particles, 5)`, one row
|
|
84
|
+
per particle holding `[x, y, z, radius, mass]`:
|
|
85
|
+
|
|
86
|
+
- **Why pre-allocation.** The array is allocated once, contiguously, at
|
|
87
|
+
construction. Growing the cluster is a row write plus a counter increment
|
|
88
|
+
(`add_particle` is O(1)); there are no per-particle Python objects and no
|
|
89
|
+
list reallocations or copies during generation. Data locality keeps the
|
|
90
|
+
tight numeric loops in the generators and analysis fast.
|
|
91
|
+
- **Zero-copy views.** The properties `positions` (`(N, 3)`), `radii`
|
|
92
|
+
(`(N,)`), and `masses` (`(N,)`) return NumPy *views* slicing that one
|
|
93
|
+
backing array, not copies — mutating them mutates the aggregate.
|
|
94
|
+
`to_numpy()` returns a copy of the valid `(N, 5)` block when ownership of
|
|
95
|
+
the data must leave the aggregate.
|
|
96
|
+
- **`.current_size` semantics.** The backing array is over-provisioned to
|
|
97
|
+
`max_particles` rows; `.current_size` counts how many are valid. The views
|
|
98
|
+
always expose exactly the valid prefix, so `.current_size` is "the N of
|
|
99
|
+
the cluster" (the analysis helper `pfa.analyze` returns it as `"N"`). The
|
|
100
|
+
capacity is readable as `.max_size`.
|
|
101
|
+
- **Units.** `length_unit`, `mass_unit`, and `density` travel with the
|
|
102
|
+
aggregate (set by the generator; defaults `'nm'`, `'g'`, `1.0`) so exports
|
|
103
|
+
and analysis can label quantities.
|
|
104
|
+
|
|
105
|
+
Primary-particle sizes are supplied by a `ParticleDistribution` —
|
|
106
|
+
`Monodisperse(radius)` or `LognormalDistribution(mean, std)` — whose
|
|
107
|
+
`sample(n)` the generators call to draw radii.
|
|
108
|
+
|
|
109
|
+
## The generator contract
|
|
110
|
+
|
|
111
|
+
All four algorithms implement the abstract
|
|
112
|
+
[`BaseGenerator`](/api-reference/index.md#generators) with a single
|
|
113
|
+
constructor signature:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
BaseGenerator(
|
|
117
|
+
n_particles, # target number of primary particles
|
|
118
|
+
df, kf, # fractal dimension and prefactor
|
|
119
|
+
particle_dist, # ParticleDistribution for primary radii
|
|
120
|
+
overlap_tolerance=0.0,
|
|
121
|
+
placement='algebraic',
|
|
122
|
+
# plus length_unit / mass_unit / density
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`generate()` then returns a populated `Aggregate`. Because the contract is
|
|
127
|
+
identical, the factory `get_generator(method, ...)` (in
|
|
128
|
+
`generators/factory.py`) can dispatch on a string — `'pca'`, `'cca'`,
|
|
129
|
+
`'fracval'`, `'tdcca'` — and that factory is exactly what the top-level
|
|
130
|
+
`pfa.generate()` wraps. Keyword-only extras (`surface_beta`) are validated
|
|
131
|
+
there and rejected for methods that do not support them.
|
|
132
|
+
|
|
133
|
+
`pfa.analyze()` is the facade's read-side: it bundles
|
|
134
|
+
`radius_of_gyration`, `center_of_mass`, and the pair-correlation fit into
|
|
135
|
+
one summary dict (`Rg`, `CoM`, `N`, `Df_estimated`, `R2`).
|
|
136
|
+
|
|
137
|
+
## The placement strategy layer
|
|
138
|
+
|
|
139
|
+
The scaling law fixes *where* the center of each added particle or cluster
|
|
140
|
+
must sit (distance `L` or `Γ` from the cluster center) but not *which*
|
|
141
|
+
particles touch. Resolving that contact problem is delegated to a strategy
|
|
142
|
+
object, selected by name:
|
|
143
|
+
|
|
144
|
+
- [`PlacementStrategy`](/api-reference/index.md#placement) — the ABC. Its two
|
|
145
|
+
abstract methods mirror the two aggregation stages: `place_particle()`
|
|
146
|
+
(single particle onto a cluster, the PCA stage) and `merge_clusters()`
|
|
147
|
+
(two clusters onto a common `Γ`, the CCA stage).
|
|
148
|
+
- `AlgebraicPlacement` (default) — FLAGE, Skorupski et al. (2014). The
|
|
149
|
+
analytical solver in `generators/optimizer_flage.py` intersects the target
|
|
150
|
+
sphere with a reference particle's contact sphere to get exact touching
|
|
151
|
+
points; candidates are overlap-filtered, with a Monte Carlo fallback.
|
|
152
|
+
- `RandomPlacement` — Filippov et al. (2000). Pure Monte Carlo sampling on
|
|
153
|
+
the target sphere with gradual tolerance relaxation until a candidate is
|
|
154
|
+
accepted (typically several times slower).
|
|
155
|
+
- `get_placement(name)` — the factory. `BaseGenerator.__init__` resolves the
|
|
156
|
+
`placement` string through it and stores the strategy instance, injecting
|
|
157
|
+
the generator's `overlap_tolerance` into it.
|
|
158
|
+
|
|
159
|
+
Both implementations share their Monte Carlo machinery
|
|
160
|
+
(`random_monte_carlo_place`, `random_monte_carlo_merge`) through
|
|
161
|
+
`generators/placement/_helpers.py`, so the strategies differ only in their
|
|
162
|
+
deterministic precomputation, not in their fallbacks.
|
|
163
|
+
|
|
164
|
+
`FracVALGenerator` and `ThouyJullienGenerator` do **not** route through the
|
|
165
|
+
placement layer: FracVAL's merge has its own deterministic contact search
|
|
166
|
+
(sphere-sphere intersection with overlap-resolving rotations), and Thouy &
|
|
167
|
+
Jullien selects among lattice-seeded orientations directly. The `placement`
|
|
168
|
+
argument is accepted for constructor uniformity but ignored by both.
|
|
169
|
+
|
|
170
|
+
## Analysis
|
|
171
|
+
|
|
172
|
+
The analysis layer is a set of pure functions over an `Aggregate`:
|
|
173
|
+
`morphology.py` provides `radius_of_gyration` (mass-weighted, including each
|
|
174
|
+
sphere's intrinsic gyration, per Morán et al. 2019 Eq. (3)) and
|
|
175
|
+
`center_of_mass`; `correlation.py` provides `pair_correlation_function`,
|
|
176
|
+
`estimate_fractal_dimension` (log-log regression of `C(r)` over the fractal
|
|
177
|
+
regime), and the matplotlib-based `plot_pair_correlation` for diagnosing the
|
|
178
|
+
fit. No function mutates the aggregate.
|
|
179
|
+
|
|
180
|
+
## I/O
|
|
181
|
+
|
|
182
|
+
The io layer serializes an `Aggregate` for downstream use: `data.py` writes
|
|
183
|
+
a YAML snapshot bundling particle data with generation parameters and
|
|
184
|
+
analysis results; `vtk.py` builds the pyvista point cloud (`export_vtk`) and
|
|
185
|
+
MultiBlock dataset (`export_vtm`) for ParaView; `visualization.py` performs
|
|
186
|
+
off-screen pyvista rendering (`export_render`) and assembles MP4 rotation
|
|
187
|
+
videos (`export_rotation_video`). Rendering exporters need a working 3D
|
|
188
|
+
backend — see the [io guide](/user-guide/io.md) for headless-environment
|
|
189
|
+
notes.
|
|
190
|
+
|
|
191
|
+
## Tests
|
|
192
|
+
|
|
193
|
+
The test suite mirrors the source layout:
|
|
194
|
+
|
|
195
|
+
- `tests/test_core/` — `Aggregate`, distributions, 3D math helpers
|
|
196
|
+
- `tests/test_generators/` — the four algorithms, factory, placement
|
|
197
|
+
strategies, FLAGE optimizer
|
|
198
|
+
- `tests/test_analysis/` — morphology and correlation functions
|
|
199
|
+
- `tests/test_io/` — YAML and visualization exports
|
|
200
|
+
- `tests/test_density.py` — root-level density test
|
|
201
|
+
- `tests/fixtures/` — shared fixture data (`surface_beta_snapshot.npy`)
|
|
202
|
+
|
|
203
|
+
Slow performance tests carry the `benchmark` pytest marker and can be
|
|
204
|
+
deselected with `-m "not benchmark"` (see
|
|
205
|
+
[Contributing](/contributing.md#running-the-tests)).
|