pyfracval.fractal#

Fractal metrics and validation for PyFracVAL.

Functions for computing mass, radius of gyration, gamma values, cluster properties, and validating fractal structure.

Functions#

calculate_mass

Compute total particle mass (proportional to r^3).

calculate_rg

Compute theoretical radius of gyration from fractal scaling law.

gamma_calculation

Compute the centre-to-centre distance (gamma) for PCA/CCA sticking.

calculate_cluster_properties

Compute total mass, Rg, centre-of-mass, and max radius of a cluster.

compute_empirical_rg

Compute empirical Rg from actual particle coordinates.

compute_pair_correlation_dimensions

Estimate fractal dimension from pair-correlation (mass-radius) scaling.

validate_fractal_structure

Validate that generated aggregate matches target fractal parameters.

Module Contents#

pyfracval.fractal.calculate_mass(radii, densities=None)[source]#

Calculate particle mass from radii and (optionally) per-particle density.

m_i = (4/3) pi r_i^3 rho_i.

Parameters:
  • radii (np.ndarray) – Array of particle radii.

  • densities (np.ndarray, optional) – Per-particle densities. None (the default) means uniform density, in which case mass is proportional to r^3 and every density-aware quantity reduces exactly to its single-material form. Supplying densities is what makes heterogeneous aggregates - different materials, not just different sizes - physically meaningful, since the center of mass, radius of gyration and the Gamma equation are all mass-weighted.

Returns:

Array of corresponding particle masses.

Return type:

np.ndarray

pyfracval.fractal.resolve_densities(densities, n, context='densities')[source]#

Validate an optional per-particle density array against a count.

Returns the array unchanged (as float) when supplied, or None for the uniform-density case so downstream code can keep taking the cheaper None path rather than carrying an array of ones.

pyfracval.fractal.compute_empirical_rg_polydisperse(coords, radii, densities=None)[source]#

Radius of gyration of actual coordinates, including each primary particle’s own gyration radius (paper Eq. 4).

Differs from compute_empirical_rg(), which treats particles as point masses and so omits the \(r_{g,i}^2\) term below. That omission is a constant offset for monodisperse input but grows with polydispersity, and it makes the point-mass form inconsistent with the Gamma equation, whose derivation (paper Appendix A, Eq. A.5) carries the term throughout. Anything that has to agree with Gamma - the measured-Rg feedback in cca/pairing.py, the per-aggregate quality record - must use this function; use compute_empirical_rg() only where a point-mass Rg is what is actually wanted.

The counterpart to calculate_rg(), which returns the radius of gyration the fractal scaling law prescribes for a given particle count. This one measures what a built aggregate actually has:

\[R_g^2 = \frac{1}{m_a}\sum_i m_{p,i}[(R_i - R_c)^2 + r_{g,i}^2]\]

with \(r_{g,i}^2 = \frac{3}{5}r_{p,i}^2\) the primary particle’s own gyration radius and \(R_c\) the mass-weighted center of mass (Eq. 5). The \(r_{g,i}^2\) term is what makes this valid for polydisperse primary particles [Morán et al., 2019]; dropping it (as monodisperse treatments do) underestimates Rg for wide size distributions.

Parameters:
  • coords (np.ndarray) – Nx3 particle coordinates.

  • radii (np.ndarray) – N particle radii.

  • densities (np.ndarray, optional) – Per-particle densities; None means uniform. Both the center of mass and the mass weighting below use these, so a heterogeneous aggregate’s Rg is only correct when they are supplied.

Returns:

Measured radius of gyration; 0.0 for an empty aggregate.

Return type:

float

pyfracval.fractal.calculate_rg(radii, npp, df, kf)[source]#

Calculate the radius of gyration using the fractal scaling law.

Implements the formula Rg = a * (N / kf)^(1/Df), where ‘a’ is the geometric mean radius calculated from the input radii array. See [Morán et al., 2019] and morphology context [Filippov et al., 2000].

Parameters:
  • radii (np.ndarray) – Array of radii of particles in the cluster/aggregate.

  • npp (int) – Number of primary particles (N) in the cluster.

  • df (float) – Fractal dimension (Df).

  • kf (float) – Fractal prefactor (kf).

Returns:

The calculated radius of gyration (Rg). Returns 0.0 if npp is 0, kf or df is zero, or if calculation fails (e.g., log error).

Return type:

float

pyfracval.fractal.gamma_calculation(m1, rg1, radii1, m2, rg2, radii2, df, kf, use_mass=False, all_radii=None)[source]#

Calculates Gamma_pc for adding the next monomer (aggregate 2).

The Gamma_pc relation follows the FracVAL CCA/PCA formulation [Morán et al., 2019].

Parameters:
  • use_mass (bool, default False) – Which form of the Gamma equation to solve. False substitutes particle counts for the masses, giving Filippov et al. (2000) Eq. 7 - what the Fortran PCA does (PCA_cca.f90’s Gamma_calculation takes n1, n2, n3) and what this port has historically done everywhere. True uses the true masses passed in as m1/m2, giving Moran et al. (2019) Eq. 6 - the paper’s central polydisperse contribution, and what the Fortran CCA actually does (CCA_module.f90:301). Identical for monodisperse primary particles; they diverge as polydispersity grows. See NOTE.md 1.2.

  • all_radii (np.ndarray, optional) – If provided, the geometric mean radius for rg3 is computed from this full set of radii (matching Fortran behaviour where R contains all N particles). When None the geometric mean is taken from the local combined set (radii1 + radii2).

  • rg3_override (float, optional) – Use this radius of gyration for the combined aggregate instead of deriving it from the scaling law. Only meaningful together with measured (rather than scaling-law) rg1/rg2 - see cca/pairing.py’s measured-Rg feedback.

pyfracval.fractal.calculate_cluster_properties(coords, radii, df, kf, densities=None)[source]#

Calculate aggregate properties: total mass, Rg, center of mass, Rmax.

Parameters:
  • coords (np.ndarray) – Nx3 array of particle coordinates.

  • radii (np.ndarray) – N array of particle radii.

  • df (float) – Fractal dimension used for Rg calculation.

  • kf (float) – Fractal prefactor used for Rg calculation.

  • densities (np.ndarray, optional) – Per-particle densities; None means uniform. Affects the total mass and the center of mass (and hence r_max), which in turn feed the Gamma equation.

Returns:

A tuple containing:
  • total_mass (float): Sum of individual particle masses.

  • rg (float | None): Radius of gyration calculated via calculate_rg, or None if calculation failed.

  • cm (np.ndarray | None): 3D center of mass coordinates, or None if calculation failed.

  • r_max (float): Maximum distance from the center of mass to any particle center in the aggregate.

Returns (0.0, 0.0, np.zeros(3), 0.0) for empty inputs (N=0).

Return type:

tuple[float, float | None, np.ndarray | None, float]

pyfracval.fractal.compute_empirical_rg(coords, radii)[source]#

Compute empirical Rg directly from particle coordinates (mass-weighted).

Unlike calculate_rg which uses the fractal scaling law Rg = a*(N/kf)^(1/Df), this function measures Rg from the actual spatial distribution of particles.

Treats each particle as a point mass. See compute_empirical_rg_polydisperse() for the form that also carries each particle’s own gyration radius (paper Eq. 4) - required wherever the result has to be consistent with the Gamma equation.

Parameters:
  • coords (np.ndarray) – Nx3 array of particle center coordinates.

  • radii (np.ndarray) – N array of particle radii. Mass is proportional to r^3.

Returns:

Empirical (mass-weighted) radius of gyration.

Return type:

float

pyfracval.fractal.compute_pair_correlation_dimensions(coords, radii, n_bins=50)[source]#

Estimate fractal dimension from pair-correlation (mass-radius) scaling.

Computes the cumulative mass M(r) as a function of radial distance from the centre of mass. For a fractal aggregate, M(r) ~ r^Df, so a log-log fit gives the empirical Df.

The fit uses raw (un-normalised) cumulative mass because normalised mass fractions are in [0,1] whose logs are non-positive, breaking the log-linear regression.

Parameters:
  • coords (np.ndarray) – Nx3 array of particle center coordinates.

  • radii (np.ndarray) – N array of particle radii.

  • n_bins (int) – Number of radial bins (default 50).

Returns:

r_bins : np.ndarray — bin edge radii (n_bins+1,) r_centers : np.ndarray — bin centre radii (n_bins,) M_r : np.ndarray — cumulative normalised mass fraction within each radius (n_bins+1,) empirical_Df : float — slope of log(M) vs log(r) fit fit_r_squared : float — R^2 of the linear fit empirical_kf : float — estimated kf from the fit

Return type:

dict with keys

pyfracval.fractal.validate_fractal_structure(coords, radii, target_df, target_kf, rg_rtol=0.05)[source]#

Validate that generated aggregate matches target fractal parameters.

Compares theoretical Rg (from scaling law) vs empirical Rg (from coordinates), and estimates the actual fractal dimension from mass-radius scaling.

Parameters:
  • coords (np.ndarray) – Nx3 array of particle center coordinates.

  • radii (np.ndarray) – N array of particle radii.

  • target_df (float) – Target fractal dimension.

  • target_kf (float) – Target fractal prefactor.

  • rg_rtol (float) – Relative tolerance for Rg agreement (default 5%).

Returns:

N : int — number of particles theoretical_rg : float — Rg from scaling law Rg = a*(N/kf)^(1/Df) empirical_rg : float — Rg measured from coordinates rg_error_pct : float — (empirical - theoretical)/theoretical * 100 rg_ok : bool — rg_error_pct < rg_rtol * 100 empirical_Df : float — Df estimated from mass-radius scaling target_Df : float — target fractal dimension df_error : float — empirical_Df - target_Df fit_r_squared : float — goodness of fit for Df estimation empirical_kf : float — estimated fractal prefactor target_kf : float — target fractal prefactor

Return type:

dict with keys