7This is a particle settling simulation with buoyancy and drag forces.
9This simulation models the dynamics of spherical particles settling under the combined
10influence of gravity, buoyancy, and Stokes drag forces. The particles experience:
12- Gravitational force: F_g = mg (downward)
13- Buoyancy force: F_b = ρ_fluid * V * g (upward)
14- Stokes drag: F_d = 6πμRv (opposing motion)
16The analytical solution describes the exponential approach to terminal velocity:
17v(t) = -v_s * (1 - e^(-t/τ)),
18where v_s is terminal velocity and τ is the relaxation time.
20This provides a validation test for:
22- Multi-physics force integration (gravity + buoyancy + drag)
23- Particle-fluid interaction through interpolated density fields
24- Transient dynamics approaching steady-state terminal velocity
26Particles with different masses and radii are initialised to test the scaling
27of settling behavior with particle properties.
30parser = exahype2.ArgumentParser(
31 "ExaHyPE 2 - Finite Volumes Particle Tracing Testing Script"
34interpolation_methods = {
40 "-interp",
"--interpolation", default=
"Linear", help=
"|".join(interpolation_methods)
43integration_schemes = {
53 "-integ",
"--integration", default=
"SemiEuler", help=
"|".join(integration_schemes)
58 degrees_of_freedom=64,
62args = parser.parse_args()
66max_h = 1.1 * min(size) / (3.0**args.min_depth)
67min_h = max_h * 3.0 ** (-args.amr_levels)
69project = exahype2.Project(
70 namespace=[
"tests",
"exahype2",
"fv"],
76if args.number_of_snapshots <= 0:
77 time_in_between_plots = 0.0
79 time_in_between_plots = args.end_time / args.number_of_snapshots
80 project.set_output_path(args.output)
82project.set_global_simulation_parameters(
86 min_end_time=args.end_time,
87 max_end_time=args.end_time,
88 first_plot_time_stamp=0.0,
89 time_in_between_plots=time_in_between_plots,
91 args.periodic_boundary_conditions_x,
92 args.periodic_boundary_conditions_y,
96initial_conditions =
"""
97 for (int i = 0; i < NumberOfUnknowns + NumberOfAuxiliaryVariables; i++) {
103boundary_conditions =
"""
104 for (int i = 0; i < NumberOfUnknowns + NumberOfAuxiliaryVariables; i++) {
105 Qoutside[i] = Qinside[i];
110 for (int i = 0; i < NumberOfUnknowns; i++) {
119analytical_solution =
"""
120 constexpr double g = 1.0; // Gravitational acceleration
121 constexpr double mu = 1.0; // Dynamic viscosity
123 // Pull particle properties
124 const double mass = particle->getData(7);
125 const double radius = particle->getData(8);
127 // Compute volume & densities
128 double volume = (4.0/3.0) * M_PI * radius*radius*radius;
129 double rho_particle = mass / volume;
130 double rho_fluid = 1.0;
131 double density_difference = rho_particle - rho_fluid;
133 // Buoyant acceleration (effective g)
134 const double a_eff = density_difference * g / rho_particle;
135 // Stokes drag relaxation time
136 const double tau = mass / (6.0 * M_PI * mu * radius);
137 // Terminal speed (buoyancy + drag)
138 const double vs = a_eff * tau;
140 // Transient solution
141 const double expTerm = std::exp(- t / tau);
142 const double vy = -vs * (1.0 - expTerm);
143 const double y = 2.5 + (
144 - vs * t // Long-term drift
145 + vs * tau // Offset so y(0)=2.5
149 solution[0] = 1.5; // x(t)=const
150 solution[1] = y; // y(t)
152 solution[2] = rho_fluid;
153 solution[3] = 0.0; // Fluid u
154 solution[4] = 0.0; // Fluid v
156 solution[5] = 0.0; // Particle u
157 solution[6] = vy; // Particle v(t)
159 solution[7] = mass; // Keep mass at index 7
160 solution[8] = radius; // Keep radius at index 8
163fv_solver = exahype2.solvers.fv.godunov.GlobalFixedTimeStep(
165 patch_size=args.degrees_of_freedom,
167 auxiliary_variables=0,
170 normalised_time_step_size=0.001,
173fv_solver.set_implementation(
174 initial_conditions=initial_conditions,
175 boundary_conditions=boundary_conditions,
176 max_eigenvalue=max_eigenvalue,
180project.add_solver(fv_solver)
196tracer_particles = fv_solver.add_tracer(
197 name=
"Tracers", particle_attributes=tracer_attributes
200init_tracers = exahype2.tracer.InsertParticlesByCoordinatesAndData(
201 particle_set=tracer_particles,
202 unknowns_per_particle=len(tracer_attributes[
"unknowns"]),
205 2.0, 2.5, 0.0, 0.0, 0.0, 0, 0, 0, 0.0, 0.01, 0.1,
206 1.5, 2.5, 0.0, 0.0, 0.0, 0, 0, 0, 0.0, 0.1, 0.1,
207 1.0, 2.5, 0.0, 0.0, 0.0, 0, 0, 0, 0.0, 0.5, 0.1,
208 0.5, 2.5, 0.0, 0.0, 0.0, 0, 0, 0, 0.0, 1.0, 0.1
213init_tracers.descend_invocation_order = 0
214project.add_action_set_to_initialisation(init_tracers)
216tracing_action_set = exahype2.tracer.FiniteVolumesTracingWithForce(
220 integration_scheme=integration_schemes[args.integration],
227 interpolation_scheme=interpolation_methods[args.interpolation],
238 fields_to_interpolate=
"0,1,2",
241tracing_action_set.descend_invocation_order = (
242 fv_solver._action_set_update_patch.descend_invocation_order + 1
245project.add_action_set_to_timestepping(tracing_action_set)
246project.add_action_set_to_initialisation(tracing_action_set)
248from pathlib
import Path
250error_measurement = exahype2.solvers.fv.ErrorMeasurementParticles(
252 error_measurement_implementation=analytical_solution,
253 output_file_name=args.output +
"/Error_" + Path(__file__).name,
254 particle_set=tracer_particles,
255 descend_invocation_order=65536,
258project.set_load_balancer(
"new ::exahype2::LoadBalancingConfiguration")
259project.set_build_mode(mode=peano4.output.string_to_mode(args.build_mode))
260project = project.generate_Peano4_project(verbose=
False)
261project.build(make=
True, make_clean_first=
True, throw_away_data_after_build=
True)