PyParticles3 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. pyparticles/__init__.py +26 -0
  2. pyparticles/__main__.py +7 -0
  3. pyparticles/animation/__init__.py +21 -0
  4. pyparticles/animation/animated_cli.py +53 -0
  5. pyparticles/animation/animated_ogl.py +691 -0
  6. pyparticles/animation/animated_ogl_compat.py +243 -0
  7. pyparticles/animation/animated_scatter.py +94 -0
  8. pyparticles/animation/animation.py +251 -0
  9. pyparticles/animation/test_animation.py +310 -0
  10. pyparticles/demo/__init__.py +33 -0
  11. pyparticles/demo/bubble.py +106 -0
  12. pyparticles/demo/electromagnetic_demo.py +128 -0
  13. pyparticles/demo/electrostatic_demo.py +126 -0
  14. pyparticles/demo/fountain.py +478 -0
  15. pyparticles/demo/gas_lennard_jones.py +87 -0
  16. pyparticles/demo/gravity_clusters.py +73 -0
  17. pyparticles/demo/solar_system.py +222 -0
  18. pyparticles/demo/springs.py +141 -0
  19. pyparticles/demo/springs_constr.py +135 -0
  20. pyparticles/demo/test.py +34 -0
  21. pyparticles/forces/__init__.py +21 -0
  22. pyparticles/forces/const_force.py +140 -0
  23. pyparticles/forces/damping.py +174 -0
  24. pyparticles/forces/drag.py +167 -0
  25. pyparticles/forces/electromagnetic.py +105 -0
  26. pyparticles/forces/electromagnetic_field.py +95 -0
  27. pyparticles/forces/electrostatic.py +72 -0
  28. pyparticles/forces/force.py +75 -0
  29. pyparticles/forces/force_constrained.py +32 -0
  30. pyparticles/forces/fused_const_drag.py +445 -0
  31. pyparticles/forces/gravity.py +345 -0
  32. pyparticles/forces/lennard_jones.py +74 -0
  33. pyparticles/forces/linear_spring.py +86 -0
  34. pyparticles/forces/linear_spring_constrained.py +72 -0
  35. pyparticles/forces/multiple_force.py +134 -0
  36. pyparticles/forces/pseudo_bubble.py +226 -0
  37. pyparticles/forces/van_der_waals_force.py +60 -0
  38. pyparticles/forces/vector_field_force.py +52 -0
  39. pyparticles/geometry/__init__.py +21 -0
  40. pyparticles/geometry/dist.py +24 -0
  41. pyparticles/geometry/intersection.py +62 -0
  42. pyparticles/geometry/transformations.py +387 -0
  43. pyparticles/main/__init__.py +21 -0
  44. pyparticles/main/main.py +466 -0
  45. pyparticles/measures/__init__.py +21 -0
  46. pyparticles/measures/elastic_potential_energy.py +67 -0
  47. pyparticles/measures/gravitational_potential_energy.py +69 -0
  48. pyparticles/measures/kinetic_energy.py +68 -0
  49. pyparticles/measures/mass.py +59 -0
  50. pyparticles/measures/measure.py +156 -0
  51. pyparticles/measures/momentum.py +144 -0
  52. pyparticles/measures/total_energy.py +68 -0
  53. pyparticles/ode/__init__.py +21 -0
  54. pyparticles/ode/euler_solver.py +214 -0
  55. pyparticles/ode/euler_solver_constrained.py +49 -0
  56. pyparticles/ode/leapfrog_solver.py +37 -0
  57. pyparticles/ode/leapfrog_solver_constrained.py +53 -0
  58. pyparticles/ode/midpoint_solver.py +43 -0
  59. pyparticles/ode/midpoint_solver_constrained.py +60 -0
  60. pyparticles/ode/ode_solver.py +134 -0
  61. pyparticles/ode/ode_solver_constrained.py +43 -0
  62. pyparticles/ode/runge_kutta_solver.py +79 -0
  63. pyparticles/ode/runge_kutta_solver_constrained.py +98 -0
  64. pyparticles/ode/sim_time.py +56 -0
  65. pyparticles/ode/stormer_verlet_solver.py +50 -0
  66. pyparticles/ode/stormer_verlet_solver_constrained.py +73 -0
  67. pyparticles/ogl/__init__.py +21 -0
  68. pyparticles/ogl/axis_ogl.py +210 -0
  69. pyparticles/ogl/draw_particles_ogl.py +313 -0
  70. pyparticles/ogl/draw_particles_ogl_compat.py +509 -0
  71. pyparticles/ogl/draw_vector_field.py +221 -0
  72. pyparticles/ogl/opencl_gl_vbo.py +485 -0
  73. pyparticles/ogl/trackball.py +131 -0
  74. pyparticles/ogl/translate_scene.py +87 -0
  75. pyparticles/pset/__init__.py +21 -0
  76. pyparticles/pset/boundary.py +69 -0
  77. pyparticles/pset/cluster.py +28 -0
  78. pyparticles/pset/constrained_force_interactions.py +63 -0
  79. pyparticles/pset/constrained_x.py +158 -0
  80. pyparticles/pset/constraint.py +42 -0
  81. pyparticles/pset/default_boundary.py +43 -0
  82. pyparticles/pset/file_cluster.py +131 -0
  83. pyparticles/pset/logger.py +152 -0
  84. pyparticles/pset/octree.py +451 -0
  85. pyparticles/pset/opencl_context.py +374 -0
  86. pyparticles/pset/particles_set.py +499 -0
  87. pyparticles/pset/periodic_boundary.py +36 -0
  88. pyparticles/pset/rand_cluster.py +188 -0
  89. pyparticles/pset/rebound_boundary.py +68 -0
  90. pyparticles/utils/__init__.py +21 -0
  91. pyparticles/utils/parse_args.py +72 -0
  92. pyparticles/utils/problem_config.py +608 -0
  93. pyparticles/utils/pypart_global.py +122 -0
  94. pyparticles/utils/time_formatter.py +50 -0
  95. pyparticles3-0.4.0.dist-info/METADATA +395 -0
  96. pyparticles3-0.4.0.dist-info/RECORD +100 -0
  97. pyparticles3-0.4.0.dist-info/WHEEL +5 -0
  98. pyparticles3-0.4.0.dist-info/entry_points.txt +3 -0
  99. pyparticles3-0.4.0.dist-info/licenses/LICENSE-gpl-3.0.txt +674 -0
  100. pyparticles3-0.4.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,478 @@
1
+ # PyParticles : Particles simulation in python
2
+ # Copyright (C) 2012 Simone Riva
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ import gc
10
+ import os
11
+ import time
12
+
13
+ import numpy as np
14
+
15
+ import pyparticles.pset.particles_set as ps
16
+ import pyparticles.pset.opencl_context as occ
17
+ import pyparticles.pset.default_boundary as db
18
+
19
+ import pyparticles.ode.euler_solver as els
20
+
21
+ import pyparticles.forces.const_force as cf
22
+ import pyparticles.forces.drag as dr
23
+ import pyparticles.forces.multiple_force as mf
24
+ from pyparticles.forces.fused_const_drag import FusedConstDragOCL
25
+
26
+ import pyparticles.animation.animated_ogl_compat as aogl
27
+ from pyparticles.ogl.opencl_gl_vbo import OpenCLGLPositionBuffer
28
+
29
+ from pyparticles.utils.pypart_global import test_pyopencl
30
+
31
+ try:
32
+ import pyopencl as cl
33
+ except ImportError:
34
+ cl = None
35
+
36
+
37
+ def _env_true(name):
38
+ return os.environ.get(name, "").strip().lower() in (
39
+ "1", "true", "yes", "on",
40
+ )
41
+
42
+
43
+ def _event_profile_seconds(event):
44
+ nan = float("nan")
45
+ result = {
46
+ "queue_s": nan,
47
+ "exec_s": nan,
48
+ "total_s": nan,
49
+ }
50
+ if event is None:
51
+ return result
52
+ try:
53
+ queued = int(event.profile.queued)
54
+ start = int(event.profile.start)
55
+ end = int(event.profile.end)
56
+ except Exception:
57
+ return result
58
+
59
+ scale = 1.0e-9
60
+ if queued > 0 and start >= queued:
61
+ result["queue_s"] = (start - queued) * scale
62
+ if start > 0 and end >= start:
63
+ result["exec_s"] = (end - start) * scale
64
+ if queued > 0 and end >= queued:
65
+ result["total_s"] = (end - queued) * scale
66
+ return result
67
+
68
+
69
+ def _valid_mean(values):
70
+ if not values:
71
+ return float("nan")
72
+ array = np.asarray(values, dtype=float)
73
+ array = array[np.isfinite(array)]
74
+ if array.size == 0:
75
+ return float("nan")
76
+ return float(np.mean(array))
77
+
78
+
79
+ def _format_ms(value):
80
+ if not np.isfinite(value):
81
+ return " n/a"
82
+ return "%8.3f ms" % (value * 1000.0)
83
+
84
+
85
+ def default_pos(pset, indx):
86
+ """Historical host fallback for systems without CL/GL sharing."""
87
+ t = default_pos.sim_time.time
88
+
89
+ pset.X[indx, :] = 0.01 * np.random.rand(len(indx), pset.dim).astype(pset.dtype)
90
+
91
+ fs = 1.0 / (1.0 + np.exp(-(t * 4.0 - 2.0)))
92
+ alpha = 2.0 * np.pi * np.random.rand(len(indx)).astype(pset.dtype)
93
+
94
+ pset.V[indx, 0] = 2.0 * fs * np.cos(alpha)
95
+ pset.V[indx, 1] = 2.0 * fs * np.sin(alpha)
96
+ pset.V[indx, 2] = 10.0 * fs + fs * np.random.rand(len(indx))
97
+
98
+
99
+ def fountain():
100
+ """Fountain demo with resident/fused OpenCL and optional CL/GL interop."""
101
+ steps = 10000000
102
+ dt = 0.005
103
+ pcnt = 100000
104
+ ocl_ok = test_pyopencl()
105
+
106
+ profile_clgl = _env_true("PYPARTICLES_PROFILE_CLGL")
107
+ fused_mirror = _env_true("PYPARTICLES_CLGL_FUSED_MIRROR")
108
+ try:
109
+ profile_frames = max(
110
+ 50, int(os.environ.get("PYPARTICLES_PROFILE_FRAMES", "1000"))
111
+ )
112
+ except ValueError:
113
+ profile_frames = 1000
114
+ try:
115
+ profile_warmup = max(
116
+ 0, int(os.environ.get("PYPARTICLES_PROFILE_WARMUP", "200"))
117
+ )
118
+ except ValueError:
119
+ profile_warmup = 200
120
+
121
+ if ocl_ok:
122
+ print("OpenCL is installed and enabled")
123
+ print("Try, at least, 200000 particles")
124
+
125
+ while True:
126
+ try:
127
+ print("")
128
+ pcnt = int(input("How many particles: "))
129
+ except ValueError:
130
+ print("Please insert a number!")
131
+ else:
132
+ break
133
+
134
+ pset = ps.ParticlesSet(pcnt, dtype=np.float32)
135
+ pset.M[:] = 0.1
136
+ pset.X[:, 2] = 0.7 * np.random.rand(pset.size)
137
+
138
+ bd = (-100.0, 100.0, -100.0, 100.0, 0.0, 100.0)
139
+
140
+ if ocl_ok:
141
+ occx = occ.OpenCLcontext(
142
+ pset.size,
143
+ pset.dim,
144
+ occ.OCLC_X | occ.OCLC_V | occ.OCLC_A | occ.OCLC_M,
145
+ )
146
+ force = FusedConstDragOCL(
147
+ pset.size,
148
+ dim=pset.dim,
149
+ m=pset.M,
150
+ u_force=(0.0, 0.0, -10.0),
151
+ drag_const=0.01,
152
+ ocl_context=occx,
153
+ )
154
+ solver = els.EulerSolverOCL(
155
+ force,
156
+ pset,
157
+ dt,
158
+ ocl_context=occx,
159
+ sync_velocity=False,
160
+ sync_positions=True,
161
+ )
162
+ else:
163
+ grav = cf.ConstForce(
164
+ pset.size,
165
+ dim=pset.dim,
166
+ u_force=(0.0, 0.0, -10.0),
167
+ )
168
+ drag = dr.Drag(pset.size, dim=pset.dim, Consts=0.01)
169
+ force = mf.MultipleForce(pset.size, dim=pset.dim)
170
+ force.append_force(grav)
171
+ force.append_force(drag)
172
+ force.set_masses(pset.M)
173
+ solver = els.EulerSolver(force, pset, dt)
174
+
175
+ default_pos.sim_time = solver.get_sim_time()
176
+ pset.set_boundary(db.DefaultBoundary(bd, dim=3, defualt_pos=default_pos))
177
+
178
+ a = aogl.AnimatedGl()
179
+ if "PYPARTICLES_GL_BENCH_MODE" not in os.environ:
180
+ # Profiling on the GTX 1060 shows that per-particle multisampling adds
181
+ # about 0.9 ms at 2M points while the remaining legacy states are nearly
182
+ # free. Preserve the rest of the historical renderer and disable MSAA
183
+ # only for the particle draw. Set PYPARTICLES_GL_BENCH_MODE=legacy to
184
+ # reproduce the original rendering path exactly.
185
+ a.draw_particles.render_benchmark_mode = "no_msaa"
186
+ print("OpenGL particle draw mode: no_msaa (fountain optimized default)")
187
+ a.ode_solver = solver
188
+ a.pset = pset
189
+ a.steps = steps
190
+ a.draw_particles.set_draw_model(a.draw_particles.DRAW_MODEL_VECTOR)
191
+ a.init_rotation(-80, [0.7, 0.05, 0])
192
+
193
+ gl_interop_possible = (
194
+ ocl_ok
195
+ and cl is not None
196
+ and hasattr(cl, "have_gl")
197
+ and cl.have_gl()
198
+ )
199
+
200
+ if gl_interop_possible:
201
+ def enable_cl_gl_interop(animation):
202
+ bridge = None
203
+ try:
204
+ shared_ctx = occ.OpenCLcontext(
205
+ pset.size,
206
+ pset.dim,
207
+ occ.OCLC_X | occ.OCLC_V | occ.OCLC_A | occ.OCLC_M,
208
+ gl_sharing=True,
209
+ )
210
+ bridge = OpenCLGLPositionBuffer(
211
+ shared_ctx,
212
+ pset,
213
+ animation.draw_particles,
214
+ )
215
+
216
+ shared_force = FusedConstDragOCL(
217
+ pset.size,
218
+ dim=pset.dim,
219
+ m=pset.M,
220
+ u_force=(0.0, 0.0, -10.0),
221
+ drag_const=0.01,
222
+ ocl_context=shared_ctx,
223
+ fountain_bounds=bd,
224
+ )
225
+ shared_solver = els.EulerSolverOCL(
226
+ shared_force,
227
+ pset,
228
+ dt,
229
+ ocl_context=shared_ctx,
230
+ sync_velocity=False,
231
+ sync_positions=False,
232
+ )
233
+
234
+ shared_ctx.set_from_host("X", pset.X)
235
+ shared_ctx.set_from_host("V", pset.V)
236
+
237
+ animation.ode_solver = shared_solver
238
+ pset.set_boundary(None)
239
+
240
+ if fused_mirror:
241
+ def prepare_fused_render(_animation):
242
+ render_buffer, acquire = bridge.prepare_fused_render()
243
+ shared_force.set_render_target(
244
+ render_buffer, wait_for=[acquire]
245
+ )
246
+
247
+ animation.set_pre_step_callback(prepare_fused_render)
248
+ else:
249
+ animation.set_pre_step_callback(None)
250
+
251
+ def complete_bridge_update():
252
+ if fused_mirror:
253
+ bridge.finish_fused_render(shared_force.last_step_event)
254
+ else:
255
+ bridge.update_from_device()
256
+
257
+ if profile_clgl:
258
+ animation.draw_particles.set_gpu_timing_enabled(True)
259
+
260
+ metric_names = (
261
+ "frame_wall_s",
262
+ "physics_gpu_s",
263
+ "physics_queue_s",
264
+ "physics_total_s",
265
+ "gl_draw_gpu_s",
266
+ "gl_fence_wait_wall_s",
267
+ "gl_finish_fallback_wall_s",
268
+ "fence_immediate",
269
+ "acquire_gpu_s",
270
+ "acquire_queue_s",
271
+ "acquire_total_s",
272
+ "copy_gpu_s",
273
+ "copy_queue_s",
274
+ "copy_total_s",
275
+ "release_gpu_s",
276
+ "release_queue_s",
277
+ "release_total_s",
278
+ "release_wait_wall_s",
279
+ "bridge_wall_s",
280
+ "draw_submit_cpu_s",
281
+ )
282
+ profile_state = {
283
+ "seen": 0,
284
+ "last_bridge_end": None,
285
+ "samples": {name: [] for name in metric_names},
286
+ }
287
+
288
+ def emit_profile(force=False):
289
+ samples = profile_state["samples"]
290
+ count = len(samples["frame_wall_s"])
291
+ if count == 0:
292
+ return
293
+ if not force and count < profile_frames:
294
+ return
295
+
296
+ avg = {
297
+ name: _valid_mean(values)
298
+ for name, values in samples.items()
299
+ }
300
+ frame_ms = avg["frame_wall_s"] * 1000.0
301
+ fps = 1.0 / avg["frame_wall_s"]
302
+ p95_ms = float(
303
+ np.percentile(samples["frame_wall_s"], 95)
304
+ ) * 1000.0
305
+ copy_bw = float("nan")
306
+ if np.isfinite(avg["copy_gpu_s"]) and avg["copy_gpu_s"] > 0.0:
307
+ copy_bw = (
308
+ pset.size * pset.dim * np.dtype(np.float32).itemsize
309
+ / avg["copy_gpu_s"]
310
+ / (1024.0 ** 3)
311
+ )
312
+
313
+ gl_samples = len(samples["gl_draw_gpu_s"])
314
+ timing_stats = animation.draw_particles.gpu_timing_stats
315
+
316
+ print("")
317
+ print(
318
+ "=== CL/GL profile: %d frames, %d particles ==="
319
+ % (count, pset.size)
320
+ )
321
+ print("frame wall avg : %8.3f ms (%7.1f FPS)" % (frame_ms, fps))
322
+ print("frame wall p95 : %8.3f ms" % p95_ms)
323
+ print("physics queue->start : %s" % _format_ms(avg["physics_queue_s"]))
324
+ print("physics fused GPU : %s" % _format_ms(avg["physics_gpu_s"]))
325
+ print("physics queued->end : %s" % _format_ms(avg["physics_total_s"]))
326
+ if gl_samples and np.isfinite(avg["gl_draw_gpu_s"]):
327
+ print("glDrawArrays GPU : %8.3f ms (%d async samples)" % (
328
+ avg["gl_draw_gpu_s"] * 1000.0, gl_samples
329
+ ))
330
+ else:
331
+ print("glDrawArrays GPU : n/a (no async samples ready)")
332
+ print("GL timer queries : pending=%d skipped=%d available=%s" % (
333
+ timing_stats["pending"],
334
+ timing_stats["skipped"],
335
+ timing_stats["available"],
336
+ ))
337
+ if timing_stats.get("error"):
338
+ print("GL timer error : %s" % timing_stats["error"])
339
+ print("GL fence wait wall : %s" % _format_ms(avg["gl_fence_wait_wall_s"]))
340
+ print("fence immediate : %8.1f %%" % (
341
+ avg["fence_immediate"] * 100.0
342
+ ))
343
+ print("glFinish fallback : %s" % _format_ms(avg["gl_finish_fallback_wall_s"]))
344
+ print("CL acquire queue : %s" % _format_ms(avg["acquire_queue_s"]))
345
+ print("CL acquire exec : %s" % _format_ms(avg["acquire_gpu_s"]))
346
+ print("CL acquire total : %s" % _format_ms(avg["acquire_total_s"]))
347
+ print("CL copy queue : %s" % _format_ms(avg["copy_queue_s"]))
348
+ if np.isfinite(copy_bw):
349
+ print("X -> VBO copy GPU : %8.3f ms (%6.2f GiB/s)" % (
350
+ avg["copy_gpu_s"] * 1000.0, copy_bw
351
+ ))
352
+ else:
353
+ print("X -> VBO copy GPU : %s" % _format_ms(avg["copy_gpu_s"]))
354
+ print("CL copy total : %s" % _format_ms(avg["copy_total_s"]))
355
+ print("CL release queue : %s" % _format_ms(avg["release_queue_s"]))
356
+ print("CL release exec : %s" % _format_ms(avg["release_gpu_s"]))
357
+ print("CL release total : %s" % _format_ms(avg["release_total_s"]))
358
+ print("release.wait wall : %s" % _format_ms(avg["release_wait_wall_s"]))
359
+ print("CL/GL bridge wall : %s" % _format_ms(avg["bridge_wall_s"]))
360
+ print("glDrawArrays submit : %8.3f ms CPU" % (
361
+ avg["draw_submit_cpu_s"] * 1000.0
362
+ ))
363
+ print(
364
+ "note: GL/CL GPU timings and wall waits overlap; "
365
+ "do not add these rows."
366
+ )
367
+ print("")
368
+
369
+ for values in samples.values():
370
+ values[:] = []
371
+
372
+ def profiled_bridge_update(_animation):
373
+ # Poll timer-query results from earlier GL draws. This
374
+ # checks QUERY_RESULT_AVAILABLE first and never waits.
375
+ ready_gl_draws = animation.draw_particles.drain_gpu_draw_times()
376
+
377
+ complete_bridge_update()
378
+ bridge_end = time.perf_counter()
379
+
380
+ last_end = profile_state["last_bridge_end"]
381
+ profile_state["last_bridge_end"] = bridge_end
382
+ profile_state["seen"] += 1
383
+ if last_end is None:
384
+ return
385
+ if profile_state["seen"] <= profile_warmup:
386
+ return
387
+
388
+ bp = bridge.last_profile
389
+ physics_profile = _event_profile_seconds(
390
+ shared_force.last_step_event
391
+ )
392
+ samples = profile_state["samples"]
393
+ samples["frame_wall_s"].append(bridge_end - last_end)
394
+ samples["physics_gpu_s"].append(physics_profile["exec_s"])
395
+ samples["physics_queue_s"].append(physics_profile["queue_s"])
396
+ samples["physics_total_s"].append(physics_profile["total_s"])
397
+ samples["gl_draw_gpu_s"].extend(ready_gl_draws)
398
+
399
+ for name in (
400
+ "gl_fence_wait_wall_s",
401
+ "gl_finish_fallback_wall_s",
402
+ "fence_immediate",
403
+ "acquire_gpu_s",
404
+ "acquire_queue_s",
405
+ "acquire_total_s",
406
+ "copy_gpu_s",
407
+ "copy_queue_s",
408
+ "copy_total_s",
409
+ "release_gpu_s",
410
+ "release_queue_s",
411
+ "release_total_s",
412
+ "release_wait_wall_s",
413
+ "bridge_wall_s",
414
+ ):
415
+ samples[name].append(
416
+ float(bp.get(name, float("nan")))
417
+ )
418
+ samples["draw_submit_cpu_s"].append(
419
+ float(animation.draw_particles.last_draw_submit_seconds)
420
+ )
421
+ emit_profile(force=False)
422
+
423
+ animation.set_post_step_callback(profiled_bridge_update)
424
+ print(
425
+ "CL/GL profiling enabled: warmup=%d, report every %d frames"
426
+ % (profile_warmup, profile_frames)
427
+ )
428
+ print("OpenGL GPU timing: asynchronous GL_TIME_ELAPSED queries")
429
+ else:
430
+ profile_state = None
431
+ emit_profile = None
432
+ animation.set_post_step_callback(
433
+ lambda _animation: complete_bridge_update()
434
+ )
435
+
436
+ def cleanup_interop(_animation, _bridge=bridge, _fallback=solver):
437
+ _animation.set_pre_step_callback(None)
438
+ _animation.set_post_step_callback(None)
439
+ shared_force.clear_render_target()
440
+ if profile_clgl and emit_profile is not None:
441
+ # Collect only already-available timer results. Cleanup
442
+ # must not block merely to improve profiling statistics.
443
+ profile_state["samples"]["gl_draw_gpu_s"].extend(
444
+ _animation.draw_particles.drain_gpu_draw_times()
445
+ )
446
+ emit_profile(force=True)
447
+ _bridge.close()
448
+ _animation.ode_solver = _fallback
449
+ gc.collect()
450
+ print("OpenCL/OpenGL interop resources released before GL shutdown")
451
+
452
+ animation.add_cleanup_callback(cleanup_interop)
453
+
454
+ print(
455
+ "OpenCL/OpenGL interop enabled: "
456
+ "positions render without host copies"
457
+ )
458
+ print("CL/GL sync: double-buffered VBOs with per-buffer GL fences")
459
+ if fused_mirror:
460
+ print("CL/GL position path: fused kernel mirror (experimental)")
461
+ else:
462
+ print("CL/GL position path: X -> VBO device copy (stable)")
463
+ print("Interop device:", shared_ctx.device.name)
464
+ except Exception as exc:
465
+ if bridge is not None:
466
+ try:
467
+ bridge.close()
468
+ except Exception:
469
+ pass
470
+ print(
471
+ "OpenCL/OpenGL interop unavailable; "
472
+ "using host-sync renderer: %s" % exc
473
+ )
474
+
475
+ a.set_gl_context_ready_callback(enable_cl_gl_interop)
476
+
477
+ a.build_animation()
478
+ a.start()
@@ -0,0 +1,87 @@
1
+ # PyParticles : Particles simulation in python
2
+ # Copyright (C) 2012 Simone Riva
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ import numpy as np
18
+
19
+ import pyparticles.pset.particles_set as ps
20
+
21
+ import pyparticles.forces.lennard_jones as lj
22
+
23
+ import pyparticles.ode.euler_solver as els
24
+ import pyparticles.ode.leapfrog_solver as lps
25
+ import pyparticles.ode.runge_kutta_solver as rks
26
+ import pyparticles.ode.stormer_verlet_solver as svs
27
+ import pyparticles.ode.midpoint_solver as mds
28
+
29
+ import pyparticles.measures.kinetic_energy as ke
30
+ import pyparticles.measures.total_energy as te
31
+
32
+ import pyparticles.pset.rand_cluster as rc
33
+ import pyparticles.pset.periodic_boundary as pb
34
+
35
+ import pyparticles.animation.animated_ogl as aogl
36
+
37
+ import sys
38
+
39
+ def gas_lj():
40
+ """
41
+ Gas simulation based on the Lennard Jones force
42
+ """
43
+
44
+ steps = 1000000
45
+ dt = 0.001
46
+
47
+ omicron = 0.05
48
+ epsilon = 1.0
49
+
50
+ rand_c = rc.RandCluster()
51
+
52
+ pset = ps.ParticlesSet( 1000 )
53
+
54
+ r_min = 2.0**(1./6.) * omicron
55
+
56
+ rand_c.insert3( X=pset.X ,
57
+ M=pset.M ,
58
+ start_indx=0 ,
59
+ n=pset.size ,
60
+ radius=5.0 ,
61
+ mass_rng=(0.1,0.3) ,
62
+ r_min=r_min )
63
+
64
+ lennard_jones = lj.LenardJones( pset.size , pset.dim , pset.M , Consts=( epsilon , omicron ) )
65
+
66
+ solver = els.EulerSolver( lennard_jones , pset , dt )
67
+ #solver = lps.LeapfrogSolver( lennard_jones , pset , dt )
68
+ #solver = svs.StormerVerletSolver( lennard_jones , pset , dt )
69
+ #solver = rks.RungeKuttaSolver( lennard_jones , pset , dt )
70
+ #solver = mds.MidpointSolver( lennard_jones , pset , dt )
71
+
72
+ bound = pb.PeriodicBoundary( bound=(-6.0,6.0) )
73
+
74
+ pset.set_boundary( bound )
75
+
76
+ a = aogl.AnimatedGl()
77
+
78
+ a.ode_solver = solver
79
+ a.pset = pset
80
+ a.steps = steps
81
+
82
+ a.build_animation()
83
+
84
+ a.start()
85
+
86
+ return
87
+
@@ -0,0 +1,73 @@
1
+ # PyParticles : Particles simulation in python
2
+ # Copyright (C) 2012 Simone Riva mail: simone {dot} rva {at} gmail {dot} com
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ import numpy as np
10
+
11
+ import pyparticles.pset.rand_cluster as clu
12
+ import pyparticles.pset.particles_set as ps
13
+ import pyparticles.pset.opencl_context as occ
14
+
15
+ import pyparticles.forces.gravity as gr
16
+ import pyparticles.ode.euler_solver as els
17
+ import pyparticles.animation.animated_ogl as aogl
18
+
19
+ from pyparticles.utils.pypart_global import test_pyopencl
20
+
21
+
22
+ def gravity_cluster():
23
+ if not test_pyopencl():
24
+ print("")
25
+ print("Attention !!!")
26
+ print("This demo requires a usable PyOpenCL device.")
27
+ print("")
28
+ return
29
+
30
+ G = 0.000001
31
+ steps = 100000000
32
+ n = 2000
33
+ dt = 0.04
34
+
35
+ pset = ps.ParticlesSet(n, dtype=np.float32)
36
+ cs = clu.RandGalaxyCluster()
37
+
38
+ print("Building initial galaxy ....")
39
+ cs.insert3(pset.X, M=pset.M, V=pset.V, G=G)
40
+
41
+ try:
42
+ occx = occ.OpenCLcontext(
43
+ pset.size,
44
+ pset.dim,
45
+ occ.OCLC_X | occ.OCLC_V | occ.OCLC_A | occ.OCLC_M,
46
+ )
47
+ except Exception:
48
+ print("")
49
+ print("ERROR !!!")
50
+ print("Please verify your OpenCL installation and GPU OpenCL driver.")
51
+ print("")
52
+ return
53
+
54
+ grav = gr.GravityOCL(pset.size, Consts=G, ocl_context=occx)
55
+ grav.set_masses(pset.M)
56
+
57
+ # The OpenGL renderer consumes positions only. Keep V resident in VRAM and
58
+ # avoid a device-to-host velocity copy on every integration step.
59
+ solver = els.EulerSolverOCL(
60
+ grav,
61
+ pset,
62
+ dt,
63
+ ocl_context=occx,
64
+ sync_velocity=False,
65
+ )
66
+
67
+ a = aogl.AnimatedGl()
68
+ a.draw_particles.set_draw_model(a.draw_particles.DRAW_MODEL_VECTOR)
69
+ a.ode_solver = solver
70
+ a.pset = pset
71
+ a.steps = steps
72
+ a.build_animation()
73
+ a.start()