135 PetscFunctionBeginUser;
138 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
143 const PetscReal Re = (simCtx->
ren > 0.0) ? simCtx->
ren : 1.0;
145 const PetscReal D = 1.0 / (Re * Sc);
146 const PetscReal t = (PetscReal)ti * simCtx->
dt;
147 const PetscReal x0 = simCtx->
psrc_x;
148 const PetscReal y0 = simCtx->
psrc_y;
149 const PetscReal z0 = simCtx->
psrc_z;
155 const PetscReal (*pos_arr)[3];
157 ierr = DMSwarmGetLocalSize(user->
swarm, &n_local); CHKERRQ(ierr);
158 ierr = DMSwarmGetField(user->
swarm,
"position", NULL, NULL, (
void**)&pos_arr); CHKERRQ(ierr);
160 PetscReal local_sq_x = 0.0, local_sq_y = 0.0, local_sq_z = 0.0;
161 PetscReal local_sx = 0.0, local_sy = 0.0, local_sz = 0.0;
163 for (PetscInt p = 0; p < n_local; p++) {
164 const PetscReal dx = pos_arr[p][0] - x0;
165 const PetscReal dy = pos_arr[p][1] - y0;
166 const PetscReal dz = pos_arr[p][2] - z0;
167 local_sq_x += dx * dx;
168 local_sq_y += dy * dy;
169 local_sq_z += dz * dz;
175 ierr = DMSwarmRestoreField(user->
swarm,
"position", NULL, NULL, (
void**)&pos_arr); CHKERRQ(ierr);
180 PetscReal local_buf[7] = { local_sq_x, local_sq_y, local_sq_z,
181 local_sx, local_sy, local_sz,
182 (PetscReal)n_local };
183 PetscReal global_buf[7] = {0};
184 ierr = MPI_Allreduce(local_buf, global_buf, 7, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
186 const PetscReal N_total = global_buf[6];
187 if (N_total < 1.0) PetscFunctionReturn(0);
189 const PetscReal MSD_x = global_buf[0] / N_total;
190 const PetscReal MSD_y = global_buf[1] / N_total;
191 const PetscReal MSD_z = global_buf[2] / N_total;
192 const PetscReal com_x = global_buf[3] / N_total;
193 const PetscReal com_y = global_buf[4] / N_total;
194 const PetscReal com_z = global_buf[5] / N_total;
196 const PetscReal MSD_total = MSD_x + MSD_y + MSD_z;
197 const PetscReal r_rms_meas = PetscSqrtReal(MSD_total);
198 const PetscReal r_theory = (t > 1e-300) ? PetscSqrtReal(6.0 * D * t) : 0.0;
199 const PetscReal rel_err_pct = (r_theory > 1e-12)
200 ? PetscAbsReal(r_rms_meas - r_theory) / r_theory * 100.0
206 ierr = DMSwarmGetField(user->
swarm,
"position", NULL, NULL, (
void**)&pos_arr); CHKERRQ(ierr);
208 PetscInt local_n1 = 0, local_n2 = 0, local_n3 = 0;
209 const PetscReal r1 = r_theory;
210 const PetscReal r2 = 2.0 * r_theory;
211 const PetscReal r3 = 3.0 * r_theory;
213 for (PetscInt p = 0; p < n_local; p++) {
214 const PetscReal dx = pos_arr[p][0] - x0;
215 const PetscReal dy = pos_arr[p][1] - y0;
216 const PetscReal dz = pos_arr[p][2] - z0;
217 const PetscReal r = PetscSqrtReal(dx*dx + dy*dy + dz*dz);
218 if (r < r1) local_n1++;
219 if (r < r2) local_n2++;
220 if (r < r3) local_n3++;
223 ierr = DMSwarmRestoreField(user->
swarm,
"position", NULL, NULL, (
void**)&pos_arr); CHKERRQ(ierr);
225 PetscReal local_counts[3] = { (PetscReal)local_n1,
227 (PetscReal)local_n3 };
228 PetscReal global_counts[3] = {0};
229 ierr = MPI_Allreduce(local_counts, global_counts, 3, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD); CHKERRMPI(ierr);
231 const PetscReal frac_1s = global_counts[0] / N_total * 100.0;
232 const PetscReal frac_2s = global_counts[1] / N_total * 100.0;
233 const PetscReal frac_3s = global_counts[2] / N_total * 100.0;
239 char csv_path[PETSC_MAX_PATH_LEN];
241 PetscSNPrintf(csv_path,
sizeof(csv_path),
"%s_msd.csv", stats_prefix);
245 "%d,%.6e,%.0f,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.4f,%.6e,%.6e,%.6e,%.2f,%.2f,%.2f\n",
247 MSD_x, MSD_y, MSD_z, MSD_total,
248 r_rms_meas, r_theory, rel_err_pct,
250 frac_1s, frac_2s, frac_3s
255 "[MSD ti=%d t=%.4f] total=%.4e theory=%.4e err=%.2f%% | "
256 "fracs: %.1f/%.1f/%.1f%% | COM: (%.2e,%.2e,%.2e)\n",
257 (
int)ti, t, MSD_total, r_theory * r_theory, rel_err_pct,
258 frac_1s, frac_2s, frac_3s,
259 com_x, com_y, com_z);
263 PetscFunctionReturn(0);
static PetscErrorCode RewriteParticleMSDCSV(const char *csv_path, PetscInt ti, const char *row_line)
Rewrite the MSD CSV so the requested timestep appears exactly once.
static PetscBool MSDCSVLineMatchesStep(const char *line, PetscInt ti)
Return whether a CSV row belongs to the requested timestep.
PetscErrorCode ComputeParticleMSD(UserCtx *user, const char *stats_prefix, PetscInt ti)
Internal helper implementation: ComputeParticleMSD().
Global statistics kernels for the Statistics Pipeline.