jaxdem.utils.particleCreation#

Utility functions for creating states of GA particles (rigid/DP).

Functions

build_ga_system(particle_radii, ...[, ...])

Catch-all builder: polydisperse GA/DP particles at a target packing fraction.

build_sphere_system(particle_radii, phi, dim, *)

Catch-all builder for a polydisperse sphere packing at a target phi.

create_dp_container(state, *[, em, ec, eb, ...])

Build a DeformableParticleModel (or a plastic variant) from a DP state.

create_ga_state(N, nv, dim, particle_radius, ...)

Build a State of N geometric-asperity bodies.

create_sphere_state(radii, dim, *[, pos, mass])

Build a State of N simple spheres.

distribute_bodies(state, phi, *[, ...])

Randomly distribute each body's centroid in a box sized for phi.

ga_surface_mask(state, *[, group_by])

Per-body surface mask: True iff the node lies on the convex hull of its body.

jaxdem.utils.particleCreation.create_sphere_state(radii: float | Sequence[float] | ndarray | Array, dim: int, *, pos: Sequence[Sequence[float]] | ndarray | Array | None = None, mass: float | Sequence[float] | ndarray | Array = 1.0) State[source]#

Build a State of N simple spheres.

No Thomson mesh, no Monte-Carlo union-property integration — this is the lightweight sphere primitive. Each sphere is a single-particle body with its own clump_id; inertia defaults to the solid-disk (2D) / solid-sphere (3D) formula from the given mass and radius (handled inside State.create()).

Parameters:
  • radii – Scalar or length-N per-sphere radii.

  • dim – Spatial dimension (2 or 3).

  • pos – Optional (N, dim) centers. If None, all spheres are stacked at the origin (handy for a single tracer).

  • mass – Scalar or length-N per-sphere masses. Default 1.0.

jaxdem.utils.particleCreation.create_ga_state(N: int, nv: int, dim: int, particle_radius: float, asperity_radius: float, *, particle_type: str = 'clump', core_type: str = 'hollow', aspect_ratio: float | Sequence[float] | None = None, particle_mass: float = 1.0, n_samples: int = 10000000, seed: int | None = None, mesh_type: str = 'thomson', mesh_kwargs: dict[str, Any] | None = None, asperity_dispersity_type: str = 'constant', asperity_dispersity_kwargs: dict[str, Any] | None = None) State[source]#

Build a State of N geometric-asperity bodies.

Surface asperities are placed on a unit shape (set by mesh_type) and an optional central core sphere is added. The union of asperities (and, if present, the core) defines the body volume that the rigid-body / deformable-body properties are derived from.

Parameters:
  • particle_type ({"clump", "dp"}) – “clump” produces rigid bodies with a computed COM / principal-axis orientation / inertia. “dp” produces deformable particles whose nodes share a common bond_id and each carry an equal share of the body mass and union volume. “sphere”-like bodies are the nv = 1 degenerate case of “clump”.

  • core_type ({"hollow", "solid", "phantom"}) – “hollow” uses only the surface asperities. “solid” adds a central core sphere that is kept in the final state. “phantom” adds the core only for the property calculation and strips it from the returned state.

  • mesh_type ({"thomson", "icosphere", "fibonacci", "torus", "helix", "arclength", "faceted"}) –

    How asperity positions are generated on the unit shape.

    • "thomson" (default): generalized Thomson problem on a hyper-ellipsoid (Riesz-energy minimization). Stochastic, near-uniform result seeded by seed.

    • "icosphere": recursive icosahedron subdivision (3D) or regular polygon (2D); deterministic; nv must be a valid icosphere count.

    • "fibonacci": golden-angle spiral sphere / evenly-spaced circle; deterministic; any nv.

    • "torus": 3D genus-1 torus surface.

    • "helix": 3D chiral helix / 2D Archimedean spiral.

    • "arclength": 2D only — equal arc-length spacing on the ellipse/circle perimeter. Closed-form analogue of the converged Thomson ground state in 2D; deterministic, any nv.

    • "faceted": regular polygon (2D) or icosahedron (3D) with vertex asperities + face/edge-interior fillers. Produces genuinely angular (flat-faced, sharp-cornered) particles rather than smooth spheres.

  • mesh_kwargs (dict, optional) – Mesh-specific keyword arguments forwarded to the underlying generator. See each generator in jaxdem.utils.meshes for accepted keys. Examples: {"steps": 5_000, "alpha": 1.0} (thomson — defaults to steps=10_000); {"tube_ratio": 0.25} (torus); {"n_turns": 3, "helix_radius": 0.3} (helix); {"n_facets": 8} (faceted, 2D only).

  • asperity_dispersity_type ({"constant", "bidisperse", "uniform", "truncated_gaussian"}) – How asperity radii are drawn. Every distribution is parameterized so its theoretical mean is asperity_radius, which keeps core_radius = particle_radius - asperity_radius deterministic across runs and dispersity settings — only the asperity-to-asperity variation changes. With dispersity, the largest asperities naturally protrude beyond particle_radius and the smallest recede; this is by design. See _generate_disperse_asperity_radii() for the per-distribution sampling formulas.

  • asperity_dispersity_kwargs (dict, optional) – Distribution-specific parameters. Defaults: {} (no effect for "constant"); {"size_ratio": 1.4, "fraction_small": 0.5} for "bidisperse"; {"size_ratio": 2.0} for "uniform"; {"cv": 0.2, "n_sigma": 2.5} for "truncated_gaussian" (where cv = std / mean and cv * n_sigma must be < 1 to keep radii positive).

jaxdem.utils.particleCreation.distribute_bodies(state: State, phi: float, *, domain_type: str = 'periodic', box_aspect: Sequence[float] | None = None, seed: int | None = None, max_avg_pe: float | None = 1e-16, randomize_orientation: bool = True, group_by: str = 'auto', collider_type: str = 'naive') tuple[State, Array][source]#

Randomly distribute each body’s centroid in a box sized for phi.

For each body in state (sphere, rigid clump, or deformable particle), build a bounding sphere from its centroid and the max |node - centroid| + rad. Place those bounding spheres uniformly at random in a periodic (or reflective) box sized to the target packing fraction, FIRE-minimize so nothing overlaps, and translate every body so its centroid lands at the minimized bounding-sphere center. Optionally apply a uniformly random per-body rotation.

Note that phi here is the bounding-sphere packing fraction, not the true body packing fraction (which is lower for non-convex clumps / DPs). The target box volume is sum(bounding_sphere_volume) / phi.

Parameters:
  • state – Input state containing one or more bodies. Body grouping is inferred from clump_id / bond_id; use group_by to force one.

  • phi – Target bounding-sphere packing fraction. Must be in (0, 1).

  • domain_type"periodic" or "reflect" for the analogue sphere minimization domain.

  • box_aspect – Aspect ratios for the box, shape (dim,). Defaults to isotropic.

  • seed – RNG seed for both the initial random centroid placement and the per-body rotation. Drawn randomly if None.

  • max_avg_pe – Convergence tolerance for the FIRE minimizer.

  • randomize_orientation – If True, apply a uniformly-random per-body rotation after placement.

  • group_by"auto" (default), "clump", or "bond". See _resolve_body_grouping().

  • collider_type – Collider for the analogue sphere minimization, "naive" or "celllist".

Returns:

The input state with per-body translation and (optionally) per-body random orientation applied, and the periodic box size vector.

Return type:

state, box_size

jaxdem.utils.particleCreation.ga_surface_mask(state: State, *, group_by: str = 'bond') Array[source]#

Per-body surface mask: True iff the node lies on the convex hull of its body.

For each body, runs scipy.spatial.ConvexHull on the body’s node positions and flags the hull vertices as surface. Bodies with too few nodes to form a hull (<= dim + 1) or that trigger a Qhull numerical failure are treated conservatively — all their nodes marked surface.

Exact for convex (or near-convex) bodies: a node is interior iff it lies strictly inside the convex hull of its body. For Thomson-mesh asperity bodies the surface vs. core separation is unambiguous. Non-convex bodies with concave pockets can misclassify pocket nodes as interior (they sit off the global hull).

Parameters:
  • state – State with one or more bodies.

  • group_by"auto" (default), "clump", or "bond" — see _resolve_body_grouping().

Returns:

(N,) bool mask, True for surface nodes.

Return type:

jax.Array

jaxdem.utils.particleCreation.create_dp_container(state: State, *, em: float | Array | None = None, ec: float | Array | None = None, eb: float | Array | None = None, el: float | Array | None = None, gamma: float | Array | None = None, tau_s: float | Array | None = None, plasticity_type: Literal['edge', 'perimeter', 'bending', 'none', None] = None, group_by: str = 'bond', is_surface: Array | None = None, interior_edges: str | int = 'fan') Any[source]#

Build a DeformableParticleModel (or a plastic variant) from a DP state.

For each body (grouped by group_by) the surface topology is computed from the node positions: polar-angle-ordered polygon segments in 2D, or scipy.spatial.ConvexHull triangulation in 3D. Interior nodes (those flagged False in is_surface) are wired to surface nodes as extra edges carrying the body’s el coefficient; they never participate in surface elements / element_adjacency / initial_bendings.

Energy coefficients (em, ec, eb, el, gamma) are per-body scalars or per-body arrays of shape (n_bodies,), matching the existing generate_ga_deformable_state convention. If plasticity_type is given, tau_s is required and interpreted per-body (perimeter), per- edge (edge), or per-adjacency (bending) as appropriate.

Parameters:
  • state – State whose nodes make up one or more DPs. Typically produced by create_ga_state(..., particle_type="dp") and optionally placed via distribute_bodies.

  • em – Per-body elastic coefficients; None disables the term.

  • ec – Per-body elastic coefficients; None disables the term.

  • eb – Per-body elastic coefficients; None disables the term.

  • el – Per-body elastic coefficients; None disables the term.

  • gamma – Per-body elastic coefficients; None disables the term.

  • tau_s – Per-body plastic yield threshold; required whenever plasticity_type is not None.

  • plasticity_type – One of "edge", "perimeter", "bending", or None / "none" for the elastic container.

  • group_by – Body grouping for topology; default "bond".

  • is_surface – Optional (N,) bool mask of surface nodes. When None (the default) the mask is computed automatically via ga_surface_mask() (per-body convex hull), which is correct for hollow, solid, and phantom core-type DPs from create_ga_state() and for any other approximately-convex body. Pass an explicit mask to override — useful for non-convex bodies where the convex-hull test would misclassify pocket nodes.

  • interior_edges – How to connect interior nodes to surface nodes. "fan" (default) connects every interior node to every surface node in its body. An int K connects each interior node to its K nearest surface nodes.

Return type:

DeformableParticleModel or one of its plastic subclasses.

jaxdem.utils.particleCreation.build_ga_system(particle_radii: Sequence[float] | ndarray, vertex_counts: Sequence[int] | ndarray, asperity_radius: float | Sequence[float] | ndarray, phi: float, dim: int, *, particle_type: str = 'clump', core_type: str = 'hollow', aspect_ratio: Any = None, particle_mass: float | Sequence[float] = 1.0, n_property_samples: int = 1000000, mesh_type: str = 'thomson', mesh_kwargs: dict[str, Any] | None = None, asperity_dispersity_type: str = 'constant', asperity_dispersity_kwargs: dict[str, Any] | None = None, domain_type: str = 'periodic', box_aspect: Sequence[float] | None = None, randomize_orientation: bool = True, compression_step: float = 0.001, compression_pe_tol: float = 1e-16, compression_pe_diff_tol: float = 1e-16, max_n_min_steps_per_outer: int = 200000, compression_progress: bool = False, dt: float = 0.001, linear_integrator_type: str = 'verlet', rotation_integrator_type: str = 'verletspiral', force_model_type: str = 'spring', collider_type: str = 'neighborlist', collider_kw: dict[str, Any] | None = None, minimizer: Any = None, minimizer_kw: dict[str, Any] | None = None, target_fn: Any = None, mat_table: Any = None, e_int: float = 1.0, material_type: str = 'elastic', material_kwargs: dict[str, Any] | None = None, matcher_type: str = 'harmonic', matcher_kwargs: dict[str, Any] | None = None, dp_em: float | None = 1.0, dp_ec: float | None = 1.0, dp_eb: float | None = 1.0, dp_el: float | None = 1.0, dp_gamma: float | None = None, dp_tau_s: float | None = None, dp_plasticity_type: Literal['edge', 'perimeter', 'bending', 'none', None] = None, dp_interior_edges: str | int = 'fan', dp_is_surface: Array | None = None, seed: int | None = None) tuple[Any, ...][source]#

Catch-all builder: polydisperse GA/DP particles at a target packing fraction.

Given per-body polydispersity (radii, vertex counts, aspect ratios, etc.), builds the state, randomly places each body’s bounding sphere at initial_phi_bb, energy-minimizes the analogue sphere system, transfers the new centroids back to the bodies, builds a System (initially with FIRE for minimization), quasistatically compresses to the target true-body packing fraction phi, and returns the result wrapped in a System built with the user-requested integrator, collider, and material.

For particle_type="dp" a DeformableParticleModel (or a plastic variant) is also built and wired into the returned System as the bonded_force_model. In that case returns (state, system, container); for clumps/spheres returns (state, system).

Parameters:
  • particle_radii – Per-body arrays (shape (M,)) or scalars for the last two.

  • vertex_counts – Per-body arrays (shape (M,)) or scalars for the last two.

  • asperity_radius – Per-body arrays (shape (M,)) or scalars for the last two.

  • phi – Target true-body packing fraction (uses each body’s union volume, not its bounding sphere).

  • dim – Spatial dimension (2 or 3).

  • particle_type"clump" (default) or "dp". nv=1 clumps are effectively spheres.

  • core_type"hollow" (default), "solid", or "phantom".

  • aspect_ratioNone (isotropic), scalar, (dim,), (M,), or (M, dim).

  • asperity_dispersity_type – Asperity-radius polydispersity. Forwarded as-is to every per-body create_ga_state() call. Theoretical mean of the chosen distribution always equals each body’s asperity_radius, so the bounding/core geometry is unchanged. See create_ga_state() for the supported distributions and kwargs.

  • asperity_dispersity_kwargs – Asperity-radius polydispersity. Forwarded as-is to every per-body create_ga_state() call. Theoretical mean of the chosen distribution always equals each body’s asperity_radius, so the bounding/core geometry is unchanged. See create_ga_state() for the supported distributions and kwargs.

  • domain_type"periodic" or "reflect" (closed box).

  • compression_step – Packing-fraction increment for quasistatic compression.

  • compression_pe_tol – Energy tolerances passed to quasistatic_compress_to_packing_fraction().

  • compression_pe_diff_tol – Energy tolerances passed to quasistatic_compress_to_packing_fraction().

  • max_n_min_steps_per_outer – Passed to quasistatic_compress_to_packing_fraction().

  • compression_progress – Passed to quasistatic_compress_to_packing_fraction().

  • dt – Time step for the returned System.

  • linear_integrator_type – Integrator types for the final (returned) System.

  • rotation_integrator_type – Integrator types for the final (returned) System.

  • force_model_type – Parameters of the final (returned) System.

  • collider_type – Parameters of the final (returned) System.

  • collider_kw – Parameters of the final (returned) System.

  • mat_table – Optional pre-built material table. If supplied, material and matcher specs are ignored.

  • material_type – Material / matcher spec. When mat_table is None a single material_type is created with material_kwargs (defaulting to young=e_int, poisson=0.5, density=1.0) and paired with the given matcher.

  • material_kwargs – Material / matcher spec. When mat_table is None a single material_type is created with material_kwargs (defaulting to young=e_int, poisson=0.5, density=1.0) and paired with the given matcher.

  • matcher_type – Material / matcher spec. When mat_table is None a single material_type is created with material_kwargs (defaulting to young=e_int, poisson=0.5, density=1.0) and paired with the given matcher.

  • matcher_kwargs – Material / matcher spec. When mat_table is None a single material_type is created with material_kwargs (defaulting to young=e_int, poisson=0.5, density=1.0) and paired with the given matcher.

  • e_int – Material / matcher spec. When mat_table is None a single material_type is created with material_kwargs (defaulting to young=e_int, poisson=0.5, density=1.0) and paired with the given matcher.

  • dp_em – DP energy parameters. Unused for particle_type="clump".

  • dp_ec – DP energy parameters. Unused for particle_type="clump".

  • dp_eb – DP energy parameters. Unused for particle_type="clump".

  • dp_el – DP energy parameters. Unused for particle_type="clump".

  • dp_gamma – DP plasticity parameters. Unused for particle_type="clump".

  • dp_tau_s – DP plasticity parameters. Unused for particle_type="clump".

  • dp_plasticity_type – DP plasticity parameters. Unused for particle_type="clump".

  • dp_interior_edges – DP energy / topology parameters. Unused for particle_type="clump".

  • dp_is_surface – DP energy / topology parameters. Unused for particle_type="clump".

Returns:

(state, system) for clumps / spheres, or (state, system, container) for DPs.

Return type:

tuple

jaxdem.utils.particleCreation.build_sphere_system(particle_radii: Sequence[float] | ndarray, phi: float, dim: int, *, particle_mass: float | Sequence[float] = 1.0, domain_type: str = 'periodic', box_aspect: Sequence[float] | None = None, compression_step: float = 0.001, compression_pe_tol: float = 1e-16, compression_pe_diff_tol: float = 1e-16, max_n_min_steps_per_outer: int = 200000, compression_progress: bool = False, dt: float = 0.001, linear_integrator_type: str = 'verlet', rotation_integrator_type: str = '', force_model_type: str = 'spring', collider_type: str = 'neighborlist', collider_kw: dict[str, Any] | None = None, mat_table: Any = None, e_int: float = 1.0, material_type: str = 'elastic', material_kwargs: dict[str, Any] | None = None, matcher_type: str = 'harmonic', matcher_kwargs: dict[str, Any] | None = None, seed: int | None = None, minimizer: Any = None, minimizer_kw: dict[str, Any] | None = None, target_fn: Any = None) tuple[State, Any][source]#

Catch-all builder for a polydisperse sphere packing at a target phi.

Sphere counterpart to build_ga_system(). Random positions are drawn loose (phi = 0.3 or the target, whichever is smaller) via random_sphere_configuration(), then quasistatically compressed to phi under the same FIRE/spring setup as build_ga_system. Returns (state, system) built with the user-requested integrator, collider, and material.

Parameters mirror build_ga_system() (minus all the GA/DP-specific knobs that don’t apply to bare spheres).