pyfracval.utils =============== .. py:module:: pyfracval.utils .. autoapi-nested-parse:: Utility functions for vector operations and array manipulation. Module Contents --------------- .. py:function:: shuffle_array(arr) Randomly shuffle elements of a 1D array in-place (Fisher-Yates). Modifies the input array directly. Mimics Fortran randsample behavior. :param arr: The 1D NumPy array to shuffle. :returns: The input `arr`, modified in-place. .. py:function:: sort_clusters(i_orden) Sort cluster information array `i_orden` by cluster size (count). :param i_orden: The Mx3 NumPy array [start_idx, end_idx, count]. :type i_orden: np.ndarray :returns: A new Mx3 array sorted by the 'count' column (column index 2). :rtype: np.ndarray :raises ValueError: If `i_orden` is not an Mx3 array. .. py:function:: cross_product(a, b) Calculate the cross product of two 3D vectors. :param a: The first 3D vector. :type a: np.ndarray :param b: The second 3D vector. :type b: np.ndarray :returns: The 3D cross product vector. :rtype: np.ndarray .. seealso:: :py:obj:`numpy.cross` NumPy's implementation. .. py:function:: normalize(v) Normalize a vector to unit length. Returns a zero vector if the input vector's norm is close to zero. :param v: The vector (1D array) to normalize. :type v: np.ndarray :returns: The normalized vector, or a zero vector if the norm is negligible. :rtype: np.ndarray .. py:function:: calculate_mass(radii) Calculate particle mass from radii assuming constant density (prop. to R^3). :param radii: Array of particle radii. :type radii: np.ndarray :returns: Array of corresponding particle masses. :rtype: np.ndarray .. py:function:: calculate_rg(radii, npp, df, kf) 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 :cite:p:`Moran2019FracVAL`. :param radii: Array of radii of particles in the cluster/aggregate. :type radii: np.ndarray :param npp: Number of primary particles (N) in the cluster. :type npp: int :param df: Fractal dimension (Df). :type df: float :param kf: Fractal prefactor (kf). :type kf: float :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). :rtype: float .. py:function:: calculate_cluster_properties(coords, radii, df, kf) Calculate aggregate properties: total mass, Rg, center of mass, Rmax. :param coords: Nx3 array of particle coordinates. :type coords: np.ndarray :param radii: N array of particle radii. :type radii: np.ndarray :param df: Fractal dimension used for Rg calculation. :type df: float :param kf: Fractal prefactor used for Rg calculation. :type kf: float :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). :rtype: tuple[float, float | None, np.ndarray | None, float] .. py:function:: rodrigues_rotation(vectors, axis, angle) Rotate vector(s) around an axis using Rodrigues' rotation formula. :param vectors: A single 3D vector or an Nx3 array of vectors to rotate. :type vectors: np.ndarray :param axis: The 3D rotation axis (does not need to be normalized). :type axis: np.ndarray :param angle: The rotation angle in radians. :type angle: float :returns: The rotated vector or Nx3 array of rotated vectors. Returns the original vectors if the axis norm is near zero. :rtype: np.ndarray :raises ValueError: If input `vectors` is not 1D (3,) or 2D (N, 3). .. py:function:: two_sphere_intersection(sphere_1, sphere_2) Find the intersection circle of two spheres and pick a random point. Calculates the center (x0, y0, z0) and radius (r0) of the intersection circle, defines basis vectors (i_vec, j_vec) for the circle's plane, and returns a random point (x, y, z) on that circle based on a random angle (theta). Handles edge cases: spheres too far, one contained, coincidence, touching. :param sphere_1: Definition of the first sphere: [x1, y1, z1, r1]. :type sphere_1: np.ndarray :param sphere_2: Definition of the second sphere: [x2, y2, z2, r2]. :type sphere_2: np.ndarray :returns: A tuple containing: - x, y, z (float): Coordinates of a random point on the intersection. - theta (float): Random angle (radians) used to generate the point. - vec_0 (np.ndarray): [x0, y0, z0, r0] - center and radius of the intersection circle (r0=0 if spheres touch at a point). - i_vec (np.ndarray): First basis vector of the intersection plane. - j_vec (np.ndarray): Second basis vector of the intersection plane. - valid (bool): True if a valid intersection (circle or point) exists, False otherwise (e.g., separate, contained, coincident). :rtype: tuple[float, float, float, float, np.ndarray, np.ndarray, np.ndarray, bool] .. py:function:: calculate_max_overlap_cca(coords1, radii1, coords2, radii2) Calculate max overlap between two particle clusters (Numba optimized). Overlap is defined as `1 - distance / (radius1 + radius2)` for overlapping pairs, max(0). :param coords1: Nx3 coordinates of cluster 1. :type coords1: np.ndarray :param radii1: N radii of cluster 1. :type radii1: np.ndarray :param coords2: Mx3 coordinates of cluster 2. :type coords2: np.ndarray :param radii2: M radii of cluster 2. :type radii2: np.ndarray :returns: Maximum overlap fraction found between any particle in cluster 1 and any particle in cluster 2. Returns 0.0 if no overlap. :rtype: float .. py:function:: calculate_max_overlap_pca(coords_agg, radii_agg, coord_new, radius_new) Calculate max overlap between a new particle and an aggregate (Numba). Overlap is defined as `1 - distance / (radius_new + radius_agg)` for overlapping pairs, max(0). :param coords_agg: Nx3 coordinates of the existing aggregate. :type coords_agg: np.ndarray :param radii_agg: N radii of the aggregate particles. :type radii_agg: np.ndarray :param coord_new: 3D coordinates of the new particle. :type coord_new: np.ndarray :param radius_new: Radius of the new particle. :type radius_new: float :returns: Maximum overlap fraction found between the new particle and any particle in the aggregate. Returns 0.0 if no overlap. :rtype: float