Peano
Loading...
Searching...
No Matches
fv-tracers-one-body-problem.py
Go to the documentation of this file.
1# This file is part of the ExaHyPE2 project. For conditions of distribution and
2# use, please see the copyright notice at www.peano-framework.org
3import peano4
4import exahype2
5
6"""
7This is a convergence test for time integration methods in particle tracing using orbital mechanics.
8
9This simulation implements the classical one-body problem (particle orbiting a fixed central mass)
10to systematically test the convergence properties and accuracy of different time integration schemes.
11The orbital motion provides an ideal test case because:
12
13- It has a known analytical solution for circular orbits
14- It tests integration stability over long time periods
15- Orbital energy and angular momentum should be conserved
16- Integration errors accumulate and are easily measurable
17
18The particle experiences central gravitational force F = GMm/r² and is initialised with exact
19circular orbital conditions. By comparing numerical trajectories against the analytical solution,
20this test quantifies:
21
22- Order of accuracy for different integration schemes (Euler, RK, Verlet)
23- Long-term stability and energy conservation
24- Phase error accumulation in periodic motion
25
26This convergence study validates the temporal discretisation accuracy of the particle
27tracing framework for gravitational dynamics.
28"""
29
30parser = exahype2.ArgumentParser(
31 "ExaHyPE 2 - Finite Volumes Particle Tracing Testing Script"
32)
33
34interpolation_methods = {
35 "None": 0, # No interpolation (use raw values)
36 "Constant": 1, # Piecewise constant interpolation (nearest neighbor)
37 "Linear": 2, # Piecewise linear interpolation (more accurate)
38}
39parser.add_argument(
40 "-interp", "--interpolation", default="None", help="|".join(interpolation_methods)
41)
42
43integration_schemes = {
44 "Static": 0, # No integration (static particles)
45 "Euler": 1, # Explicit Euler method (simple but less accurate)
46 "SemiEuler": 2, # Semi-implicit Euler method (better stability)
47 "Verlet": 3, # Velocity Verlet algorithm (good for orbital mechanics)
48 "Midpoint": 4, # Midpoint method (2nd order Runge-Kutta)
49 "RK3": 5, # 3rd order Runge-Kutta method (high accuracy)
50 "RK4": 6, # 4th order Runge-Kutta method (higher accuracy)
51}
52parser.add_argument(
53 "-integ", "--integration", default="Euler", help="|".join(integration_schemes)
54)
55
56parser.set_defaults(
57 min_depth=3,
58 degrees_of_freedom=64,
59 end_time=2.0,
60)
61
62args = parser.parse_args()
63
64size = [3.0, 3.0]
65offset = [0.0, 0.0]
66max_h = 1.1 * min(size) / (3.0**args.min_depth)
67min_h = max_h * 3.0 ** (-args.amr_levels)
68
69project = exahype2.Project(
70 namespace=["tests", "exahype2", "fv"],
71 project_name=".",
72 directory=".",
73 executable="ExaHyPE",
74)
75
76if args.number_of_snapshots <= 0:
77 time_in_between_plots = 0.0
78else:
79 time_in_between_plots = args.end_time / args.number_of_snapshots
80 project.set_output_path(args.output)
81
82project.set_global_simulation_parameters(
83 dimensions=2,
84 size=size,
85 offset=offset,
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,
90 periodic_BC=[
91 args.periodic_boundary_conditions_x,
92 args.periodic_boundary_conditions_y,
93 ],
94)
95
96initial_conditions = """
97 for (int i = 0; i < NumberOfUnknowns + NumberOfAuxiliaryVariables; i++) {
98 Q[i] = 0.0;
99 }
100"""
101
102boundary_conditions = """
103 for (int i = 0; i < NumberOfUnknowns + NumberOfAuxiliaryVariables; i++) {
104 Qoutside[i] = Qinside[i];
105 }
106"""
107
108flux = """
109 for (int i = 0; i < NumberOfUnknowns; i++) {
110 F[i] = 0.0;
111 }
112"""
113
114max_eigenvalue = """
115 return 1.0;
116"""
117
118analytical_solution = """
119 // Analytical solution for one-body problem with fixed central mass (circular orbit)
120 // System parameters
121 constexpr double mu = 10.0; // Gravitational parameter (G*M)
122
123 // Fixed central body position (Domain center)
124 constexpr double x_center = 1.5;
125 constexpr double y_center = 1.5;
126
127 // Initial position of orbiting body
128 constexpr double x0 = 2.0;
129 constexpr double y0 = 2.0;
130
131 // Initial velocity of orbiting body (circular orbit)
132 const double r = sqrt(pow(x0 - x_center, 2) + pow(y0 - y_center, 2));
133 const double omega = sqrt(mu / (r * r * r));
134
135 // Compute velocity perpendicular to radius for circular orbit
136 const double vx0 = -omega * (y0 - y_center); // Perpendicular direction (tangential)
137 const double vy0 = omega * (x0 - x_center);
138
139 // Mass of orbiting body
140 const double mass = 1.0;
141
142 // Time-dependent angle
143 const double angle = atan2(y0 - y_center, x0 - x_center) + omega * t;
144
145 // Position
146 solution[0] = x_center + r * cos(angle); // x
147 solution[1] = y_center + r * sin(angle); // y
148
149 // Velocity (perpendicular to radius)
150 solution[5] = -r * omega * sin(angle); // vx
151 solution[6] = r * omega * cos(angle); // vy
152
153 // Acceleration (central)
154 solution[7] = -mu / (r * r * r) * (solution[0] - x_center); // ax
155 solution[8] = -mu / (r * r * r) * (solution[1] - y_center); // ay
156
157 // Other fields
158 solution[2] = 0.0; // rho (density)
159 solution[3] = 0.0; // fluid_u
160 solution[4] = 0.0; // fluid_v
161 solution[9] = mass; // m (mass)
162"""
163
164fv_solver = exahype2.solvers.fv.godunov.GlobalFixedTimeStep(
165 name="FVSolver",
166 patch_size=args.degrees_of_freedom,
167 unknowns=2,
168 auxiliary_variables=0,
169 min_volume_h=min_h,
170 max_volume_h=max_h,
171 normalised_time_step_size=0.0025,
172)
173
174fv_solver.set_implementation(
175 initial_conditions=initial_conditions,
176 boundary_conditions=boundary_conditions,
177 max_eigenvalue=max_eigenvalue,
178 flux=flux,
179)
180
181project.add_solver(fv_solver)
182
183tracer_attributes = {
184 "unknowns": {
185 "rho": 1,
186 "fluid_u": 1,
187 "fluid_v": 1,
188 "u": 1,
189 "v": 1,
190 "a_x": 1,
191 "a_y": 1,
192 "m": 1,
193 }
194}
195
196tracer_particles = fv_solver.add_tracer(
197 name="Tracers", particle_attributes=tracer_attributes
198)
199
200init_tracers = exahype2.tracer.InsertParticlesByCoordinatesAndData(
201 particle_set=tracer_particles,
202 unknowns_per_particle=len(tracer_attributes["unknowns"]),
203 initial_data="""
204 {2.0, 2.0, 0.0, 0.0, 0.0, -2.65914794847249380538e+00, 2.65914794847249424947e+00, -1.41421356237309474579e+01, -1.41421356237309474579e+01, 1.0}
205""",
206)
207
208init_tracers.descend_invocation_order = 0
209project.add_action_set_to_initialisation(init_tracers)
210
211tracing_action_set = exahype2.tracer.FiniteVolumesTracingWithForce(
212 tracer_particles,
213 fv_solver,
214 project,
215 integration_scheme=integration_schemes[args.integration],
216 force_method="1", # Central gravitational force towards the center of the domain
217 interpolation_scheme=interpolation_methods[args.interpolation],
218)
219
220tracing_action_set.descend_invocation_order = (
221 fv_solver._action_set_update_patch.descend_invocation_order + 1
222)
223
224project.add_action_set_to_timestepping(tracing_action_set)
225project.add_action_set_to_initialisation(tracing_action_set)
226
227from pathlib import Path
228
229error_measurement = exahype2.solvers.fv.ErrorMeasurementParticles(
230 fv_solver,
231 error_measurement_implementation=analytical_solution,
232 output_file_name=args.output + "/Error_" + Path(__file__).name,
233 particle_set=tracer_particles,
234 descend_invocation_order=65536, # After all other action sets
235)
236
237project.set_load_balancer("new ::exahype2::LoadBalancingConfiguration")
238project.set_build_mode(mode=peano4.output.string_to_mode(args.build_mode))
239project = project.generate_Peano4_project(verbose=False)
240project.build(make=True, make_clean_first=True, throw_away_data_after_build=True)