PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
Modules | Functions
Particle-to-Grid Scattering

Functions for scattering particle data (scalar or vector) onto Eulerian grid fields by averaging contributions within each cell. More...

Collaboration diagram for Particle-to-Grid Scattering:

Modules

 Internal Scattering Helpers
 Lower-level functions used by the main scattering routines.
 

Functions

PetscErrorCode ScatterParticleFieldToEulerField (UserCtx *user, ParticleFieldId particle_field_id, Vec eulerFieldAverageVec)
 Scatters a particle field (scalar or vector) to the corresponding Eulerian field average.
 
PetscErrorCode ScatterAllParticleFieldsToEulerFields (UserCtx *user)
 Scatters a predefined set of particle fields to their corresponding Eulerian fields.
 
static PetscErrorCode ScatterParticleFieldToEulerField_Internal (UserCtx *user, ParticleFieldId particle_field_id, DM targetDM, PetscInt expected_dof, Vec eulerFieldAverageVec)
 Accumulate one particle field onto the Eulerian grid using the selected scatter stencil.
 

Detailed Description

Functions for scattering particle data (scalar or vector) onto Eulerian grid fields by averaging contributions within each cell.

This file provides a modular set of functions to perform particle-to-grid projection, specifically calculating cell-averaged quantities from particle properties. It assumes a PETSc environment using DMDA for the grids and DMSwarm for particles.

Key Features:

Dependencies:

Function Documentation

◆ ScatterParticleFieldToEulerField()

PetscErrorCode ScatterParticleFieldToEulerField ( UserCtx user,
ParticleFieldId  particle_field_id,
Vec  eulerFieldAverageVec 
)

Scatters a particle field (scalar or vector) to the corresponding Eulerian field average.

This is the main user-facing function. It determines the target Eulerian DM from the particle catalog, validates the provided eulerFieldAverageVec against the target DM, and then orchestrates the scatter operation by calling the internal helper function ScatterParticleFieldToEulerField_Internal. The final averaged result is stored IN-PLACE in eulerFieldAverageVec.

Parameters
[in]userPointer to UserCtx containing da, fda, swarm, ParticleCount.
[in]particle_field_idTyped identity of the DMSwarm field.
[in,out]eulerFieldAverageVecPre-created Vec associated with the correct target DM (implicitly da or fda). Result stored here.
Returns
PetscErrorCode 0 on success. Errors on NULL input, unrecognized field name, or incompatible target vector.

Definition at line 1802 of file interpolation.c.

1805{
1806 PetscErrorCode ierr;
1807 DM targetDM = NULL; // Will point to user->da or user->fda
1808 PetscInt expected_dof = 0; // Will be 1 or 3
1809 char msg[ERROR_MSG_BUFFER_SIZE]; // Buffer for formatted error messages
1810 const ParticleFieldDescriptor *particle_descriptor = NULL;
1811 const FieldDescriptor *eulerian_descriptor = NULL;
1812 FieldView target_view;
1813 const char *particleFieldName = NULL;
1814
1815 PetscFunctionBeginUser;
1816
1818
1819 // --- Essential Input Validation ---
1820 if (!user) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "UserCtx pointer is NULL.");
1821 if (!user->swarm) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "UserCtx->swarm is NULL.");
1822 if (!user->ParticleCount) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "UserCtx->ParticleCount is NULL.");
1823 if (!eulerFieldAverageVec) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Output eulerFieldAverageVec is NULL.");
1824 ierr = ParticleFieldGetDescriptor(particle_field_id, &particle_descriptor); CHKERRQ(ierr);
1825 PetscCheck((particle_descriptor->capabilities & PARTICLE_FIELD_CAPABILITY_EULERIAN_SCATTER) != 0,
1826 PETSC_COMM_SELF, PETSC_ERR_SUP,
1827 "Particle field '%s' has no registered Eulerian scatter target.",
1828 particle_descriptor->canonical_name);
1829 PetscCheck(particle_descriptor->eulerian_scatter_target != FIELD_ID_INVALID,
1830 PETSC_COMM_SELF, PETSC_ERR_PLIB,
1831 "Particle field '%s' advertises scatter support without an Eulerian target.",
1832 particle_descriptor->canonical_name);
1833 particleFieldName = particle_descriptor->canonical_name;
1834 expected_dof = particle_descriptor->components;
1835 ierr = FieldGetDescriptor(particle_descriptor->eulerian_scatter_target, &eulerian_descriptor); CHKERRQ(ierr);
1836 PetscCheck(eulerian_descriptor->dof == expected_dof, PETSC_COMM_SELF, PETSC_ERR_PLIB,
1837 "Particle field '%s' has %d components but Eulerian target '%s' has %d.",
1838 particleFieldName, expected_dof, eulerian_descriptor->canonical_name, eulerian_descriptor->dof);
1839 ierr = FieldGetView(user, particle_descriptor->eulerian_scatter_target, &target_view); CHKERRQ(ierr);
1840 targetDM = target_view.dm;
1841
1842 // --- Validate the provided Target Vec's Compatibility ---
1843 DM vec_dm;
1844 PetscInt vec_dof;
1845 // Check that the provided average vector has a DM associated with it
1846 ierr = VecGetDM(eulerFieldAverageVec, &vec_dm); CHKERRQ(ierr);
1847 if (!vec_dm) {
1848 PetscSNPrintf(msg, sizeof(msg), "Provided eulerFieldAverageVec for field '%s' does not have an associated DM.", particleFieldName);
1849 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "%s", msg);
1850 }
1851 // Get the block size (DOF) of the provided vector
1852 ierr = VecGetBlockSize(eulerFieldAverageVec, &vec_dof); CHKERRQ(ierr);
1853 // Compare the vector's associated DM with the one determined by the field name
1854 if (vec_dm != targetDM) {
1855 const char *target_dm_name = "targetDM", *vec_dm_name = "vec_dm";
1856 // Get actual names if possible for a more informative error message
1857 PetscObjectGetName((PetscObject)targetDM, &target_dm_name);
1858 PetscObjectGetName((PetscObject)vec_dm, &vec_dm_name);
1859 PetscSNPrintf(msg, sizeof(msg), "Provided eulerFieldAverageVec associated with DM '%s', but field '%s' requires scatter to DM '%s'.", vec_dm_name, particleFieldName, target_dm_name);
1860 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "%s", msg);
1861 }
1862 // Compare the vector's DOF with the one expected for the field name
1863 if (vec_dof != expected_dof) {
1864 PetscSNPrintf(msg, sizeof(msg), "Field '%s' requires DOF %d, but provided eulerFieldAverageVec has DOF %d.", particleFieldName, expected_dof, vec_dof);
1865 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "%s", msg);
1866 }
1867
1868 // --- Perform Scatter using Internal Helper ---
1869 // Log intent before calling the core logic
1870 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Scattering field '%s' (DOF=%d).\n", particleFieldName, expected_dof);
1871 ierr = ScatterParticleFieldToEulerField_Internal(user, // Pass user context
1872 particle_field_id,
1873 targetDM, // Determined target DM (da or fda)
1874 expected_dof, // Determined DOF (1 or 3)
1875 eulerFieldAverageVec); // The output vector
1876 CHKERRQ(ierr); // Handle potential errors from the internal function
1877
1878 LOG_ALLOW(GLOBAL, LOG_INFO, "Successfully scattered field '%s'.\n", particleFieldName);
1879
1881
1882 PetscFunctionReturn(0);
1883}
PetscErrorCode FieldGetView(UserCtx *user, FieldId field_id, FieldView *view)
Resolve the existing DM and global/local vectors for one field.
const char * canonical_name
PetscErrorCode FieldGetDescriptor(FieldId field_id, const FieldDescriptor **descriptor)
Return immutable metadata for a valid field identifier.
@ FIELD_ID_INVALID
Immutable metadata for one field identity.
Non-owning runtime objects resolved for one field and UserCtx.
static PetscErrorCode ScatterParticleFieldToEulerField_Internal(UserCtx *user, ParticleFieldId particle_field_id, DM targetDM, PetscInt expected_dof, Vec eulerFieldAverageVec)
Accumulate one particle field onto the Eulerian grid using the selected scatter stencil.
#define ERROR_MSG_BUFFER_SIZE
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:46
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
Definition logging.h:200
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:859
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:31
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:32
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled code block (typically a function).
Definition logging.h:850
@ PARTICLE_FIELD_CAPABILITY_EULERIAN_SCATTER
PetscErrorCode ParticleFieldGetDescriptor(ParticleFieldId field_id, const ParticleFieldDescriptor **descriptor)
Return immutable metadata for a valid particle field ID.
Immutable metadata for one persistent particle field.
Vec ParticleCount
Definition variables.h:996
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ScatterAllParticleFieldsToEulerFields()

PetscErrorCode ScatterAllParticleFieldsToEulerFields ( UserCtx user)

Scatters a predefined set of particle fields to their corresponding Eulerian fields.

This convenience function calls the unified ScatterParticleFieldToEulerField for a standard set of fields ("P", potentially others). It assumes the target Eulerian Vec objects (e.g., user->P, user->Ucat) exist in the UserCtx structure and are correctly associated with their respective DMs (user->da or user->fda). It zeros the target Vecs before scattering.

Parameters
[in,out]userPointer to the UserCtx structure containing all required DMs, Vecs (ParticleCount, target Eulerian fields like P, Ucat), and swarm.
Returns
PetscErrorCode 0 on success. Errors if prerequisites (like ParticleCount) are missing or if underlying scatter calls fail.

Definition at line 1888 of file interpolation.c.

1889{
1890 PetscErrorCode ierr;
1891 PetscFunctionBeginUser;
1893
1894 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting scattering of specified particle fields to Eulerian grids.\n");
1895
1896 // --- Pre-computation Check: Ensure Particle Counts are Ready ---
1897 if (!user->ParticleCount) {
1898 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "UserCtx->ParticleCount is NULL. Compute counts before calling ScatterAllParticleFieldsToEulerFields.");
1899 }
1900
1901 // --- Scatter Particle Field "Psi" -> Eulerian Field user->Psi (on da) ---
1902 // Check if the target Eulerian vector 'user->Psi' exists.
1903 if (user->Psi) {
1904
1905 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Scattering particle field 'Psi' to user->Psi.\n");
1906 // Zero the target vector before accumulating the new average for this step/call.
1907
1908 // Debug Verification ------------------------------------------------
1909 Vec swarm_Psi;
1910 PetscReal Avg_Psi,Avg_swarm_Psi;
1911
1912 ierr = VecMean(user->Psi,&Avg_Psi);
1913 LOG_ALLOW(GLOBAL,LOG_DEBUG," Average of Scalar(Psi) before scatter: %.4f.\n",Avg_Psi);
1914
1915 ierr = DMSwarmCreateGlobalVectorFromField(user->swarm, ParticleFieldName(PARTICLE_FIELD_ID_PSI), &swarm_Psi);
1916 ierr = VecMean(swarm_Psi,&Avg_swarm_Psi);
1917
1918 LOG_ALLOW(GLOBAL,LOG_DEBUG," Average of Particle Scalar(Psi): %.4f.\n",Avg_swarm_Psi);
1919
1920 ierr = DMSwarmDestroyGlobalVectorFromField(user->swarm, ParticleFieldName(PARTICLE_FIELD_ID_PSI), &swarm_Psi);
1921 // Debug----------------------------------------------------------------
1922
1923 //ierr = VecSet(user->P, 0.0); CHKERRQ(ierr);
1924 // Call the unified scatter function. It will handle DM determination and validation.
1925 // It will also error out if the *particle* field "Psi" doesn't exist in the swarm.
1926 ierr = ScatterParticleFieldToEulerField(user, PARTICLE_FIELD_ID_PSI, user->Psi); CHKERRQ(ierr);
1927 ierr = VecMean(user->Psi,&Avg_Psi);
1928
1929 LOG_ALLOW(GLOBAL,LOG_DEBUG," Average of Scalar(Psi) after scatter: %.4f.\n",Avg_Psi);
1930 } else {
1931 // Only log a warning if the target Eulerian field is missing in the context.
1932 LOG_ALLOW(GLOBAL, LOG_WARNING, "Skipping scatter for 'Psi': UserCtx->Psi is NULL.\n");
1933 }
1934
1935 // Additional scatterable particle fields require an explicit catalog entry
1936 // with a compatible persistent Eulerian target.
1937
1938 LOG_ALLOW(GLOBAL, LOG_INFO, "Finished scattering specified particle fields.\n");
1940 PetscFunctionReturn(0);
1941}
PetscErrorCode ScatterParticleFieldToEulerField(UserCtx *user, ParticleFieldId particle_field_id, Vec eulerFieldAverageVec)
Scatters a particle field (scalar or vector) to the corresponding Eulerian field average.
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:30
const char * ParticleFieldName(ParticleFieldId field_id)
Return the canonical PETSc DMSwarm name for an ID.
@ PARTICLE_FIELD_ID_PSI
Vec Psi
Definition variables.h:997
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ScatterParticleFieldToEulerField_Internal()

static PetscErrorCode ScatterParticleFieldToEulerField_Internal ( UserCtx user,
ParticleFieldId  particle_field_id,
DM  targetDM,
PetscInt  expected_dof,
Vec  eulerFieldAverageVec 
)
static

Accumulate one particle field onto the Eulerian grid using the selected scatter stencil.

Definition at line 1715 of file interpolation.c.

1720{
1721 PetscErrorCode ierr;
1722 PetscInt target_dof = 0;
1723 Vec globalsumVec = NULL;
1724 Vec localsumVec = NULL;
1725 char msg[ERROR_MSG_BUFFER_SIZE]; // Buffer for formatted error messages
1726 const char *particleFieldName = ParticleFieldName(particle_field_id);
1727
1728 PetscFunctionBeginUser;
1729
1731
1732 if (!user || !user->swarm || !user->ParticleCount || !targetDM || !eulerFieldAverageVec)
1733 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "NULL input provided to ScatterParticleFieldToEulerField_Internal.");
1734
1735 ierr = DMDAGetInfo(targetDM, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &target_dof, NULL, NULL, NULL, NULL, NULL); CHKERRQ(ierr);
1736 if (target_dof != expected_dof) {
1737 PetscSNPrintf(msg, sizeof(msg),
1738 "Field '%s' expects DOF %d but targetDM reports DOF %d.",
1739 particleFieldName, expected_dof, target_dof);
1740 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "%s", msg);
1741 }
1742
1743 // --- Check if Particle Field Exists ---
1744 // Attempt a GetField call; if it fails, the field doesn't exist.
1745 // We let CHKERRQ handle the error directly if the field doesn't exist OR
1746 // we catch it specifically to provide a more tailored message.
1747
1748 /*
1749 LOG_ALLOW(GLOBAL,LOG_DEBUG,"Field %s being accessed to check existence \n",particleFieldName);
1750 ierr = DMSwarmGetField(user->swarm, particleFieldName, NULL, NULL, NULL);
1751 if (ierr) { // If GetField returns an error
1752 PetscSNPrintf(msg, sizeof(msg), "Particle field '%s' not found in DMSwarm for scattering.", particleFieldName);
1753 // Directly set the error, overwriting the one from GetField
1754 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, msg);
1755 }
1756 ierr = DMSwarmRestoreField(user->swarm, particleFieldName, NULL, NULL, NULL);
1757 */
1758
1759 // --- Setup Temporary Sum Vector ---
1760 ierr = VecDuplicate(eulerFieldAverageVec, &globalsumVec); CHKERRQ(ierr);
1761 ierr = VecSet(globalsumVec, 0.0); CHKERRQ(ierr);
1762 ierr = PetscSNPrintf(msg, sizeof(msg), "TempSum_%s", particleFieldName); CHKERRQ(ierr);
1763 ierr = PetscObjectSetName((PetscObject)globalsumVec, msg); CHKERRQ(ierr);
1764
1765 // create local vector for accumulation
1766 ierr = DMGetLocalVector(targetDM, &localsumVec); CHKERRQ(ierr);
1767 ierr = VecSet(localsumVec, 0.0); CHKERRQ(ierr); // Must be zeroed before accumulation
1768 ierr = PetscSNPrintf(msg, sizeof(msg), "LocalTempSum_%s", particleFieldName); CHKERRQ(ierr);
1769 ierr = PetscObjectSetName((PetscObject)localsumVec, msg); CHKERRQ(ierr);
1770
1771 // --- Accumulate ---
1772 // This will call DMSwarmGetField again. If it failed above, it will likely fail here too,
1773 // unless the error was cleared somehow between the check and here (unlikely).
1774 // If the check above was skipped (Option 1), this is where the error for non-existent
1775 // field will be caught by CHKERRQ.
1776 ierr = AccumulateParticleField(user->swarm, particle_field_id, targetDM, localsumVec); CHKERRQ(ierr);
1777
1778 // --- Local to Global Sum ---
1779 ierr = DMLocalToGlobalBegin(targetDM, localsumVec, ADD_VALUES, globalsumVec); CHKERRQ(ierr);
1780 ierr = DMLocalToGlobalEnd(targetDM, localsumVec, ADD_VALUES, globalsumVec); CHKERRQ(ierr);
1781 // Return local vector to DM
1782 ierr = DMRestoreLocalVector(targetDM, &localsumVec); CHKERRQ(ierr);
1783
1784 // Calculate the number of particles per cell.
1785 ierr = CalculateParticleCountPerCell(user); CHKERRQ(ierr);
1786 // --- Normalize ---
1787 ierr = NormalizeGridVectorByCount(user->da, user->ParticleCount, targetDM, globalsumVec, eulerFieldAverageVec); CHKERRQ(ierr);
1788
1789 // --- Cleanup ---
1790 ierr = VecDestroy(&globalsumVec); CHKERRQ(ierr);
1791
1792
1794
1795 PetscFunctionReturn(0);
1796}
PetscErrorCode CalculateParticleCountPerCell(UserCtx *user)
Counts particles in each cell of the DMDA 'da' and stores the result in user->ParticleCount.
PetscErrorCode AccumulateParticleField(DM swarm, ParticleFieldId particle_field_id, DM gridSumDM, Vec gridSumVec)
Accumulates a particle field (scalar or vector) into a target grid sum vector.
PetscErrorCode NormalizeGridVectorByCount(DM countDM, Vec countVec, DM dataDM, Vec sumVec, Vec avgVec)
Normalizes a grid vector of sums by a grid vector of counts to produce an average.
Here is the call graph for this function:
Here is the caller graph for this function: