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.
- pyparticles/__init__.py +26 -0
- pyparticles/__main__.py +7 -0
- pyparticles/animation/__init__.py +21 -0
- pyparticles/animation/animated_cli.py +53 -0
- pyparticles/animation/animated_ogl.py +691 -0
- pyparticles/animation/animated_ogl_compat.py +243 -0
- pyparticles/animation/animated_scatter.py +94 -0
- pyparticles/animation/animation.py +251 -0
- pyparticles/animation/test_animation.py +310 -0
- pyparticles/demo/__init__.py +33 -0
- pyparticles/demo/bubble.py +106 -0
- pyparticles/demo/electromagnetic_demo.py +128 -0
- pyparticles/demo/electrostatic_demo.py +126 -0
- pyparticles/demo/fountain.py +478 -0
- pyparticles/demo/gas_lennard_jones.py +87 -0
- pyparticles/demo/gravity_clusters.py +73 -0
- pyparticles/demo/solar_system.py +222 -0
- pyparticles/demo/springs.py +141 -0
- pyparticles/demo/springs_constr.py +135 -0
- pyparticles/demo/test.py +34 -0
- pyparticles/forces/__init__.py +21 -0
- pyparticles/forces/const_force.py +140 -0
- pyparticles/forces/damping.py +174 -0
- pyparticles/forces/drag.py +167 -0
- pyparticles/forces/electromagnetic.py +105 -0
- pyparticles/forces/electromagnetic_field.py +95 -0
- pyparticles/forces/electrostatic.py +72 -0
- pyparticles/forces/force.py +75 -0
- pyparticles/forces/force_constrained.py +32 -0
- pyparticles/forces/fused_const_drag.py +445 -0
- pyparticles/forces/gravity.py +345 -0
- pyparticles/forces/lennard_jones.py +74 -0
- pyparticles/forces/linear_spring.py +86 -0
- pyparticles/forces/linear_spring_constrained.py +72 -0
- pyparticles/forces/multiple_force.py +134 -0
- pyparticles/forces/pseudo_bubble.py +226 -0
- pyparticles/forces/van_der_waals_force.py +60 -0
- pyparticles/forces/vector_field_force.py +52 -0
- pyparticles/geometry/__init__.py +21 -0
- pyparticles/geometry/dist.py +24 -0
- pyparticles/geometry/intersection.py +62 -0
- pyparticles/geometry/transformations.py +387 -0
- pyparticles/main/__init__.py +21 -0
- pyparticles/main/main.py +466 -0
- pyparticles/measures/__init__.py +21 -0
- pyparticles/measures/elastic_potential_energy.py +67 -0
- pyparticles/measures/gravitational_potential_energy.py +69 -0
- pyparticles/measures/kinetic_energy.py +68 -0
- pyparticles/measures/mass.py +59 -0
- pyparticles/measures/measure.py +156 -0
- pyparticles/measures/momentum.py +144 -0
- pyparticles/measures/total_energy.py +68 -0
- pyparticles/ode/__init__.py +21 -0
- pyparticles/ode/euler_solver.py +214 -0
- pyparticles/ode/euler_solver_constrained.py +49 -0
- pyparticles/ode/leapfrog_solver.py +37 -0
- pyparticles/ode/leapfrog_solver_constrained.py +53 -0
- pyparticles/ode/midpoint_solver.py +43 -0
- pyparticles/ode/midpoint_solver_constrained.py +60 -0
- pyparticles/ode/ode_solver.py +134 -0
- pyparticles/ode/ode_solver_constrained.py +43 -0
- pyparticles/ode/runge_kutta_solver.py +79 -0
- pyparticles/ode/runge_kutta_solver_constrained.py +98 -0
- pyparticles/ode/sim_time.py +56 -0
- pyparticles/ode/stormer_verlet_solver.py +50 -0
- pyparticles/ode/stormer_verlet_solver_constrained.py +73 -0
- pyparticles/ogl/__init__.py +21 -0
- pyparticles/ogl/axis_ogl.py +210 -0
- pyparticles/ogl/draw_particles_ogl.py +313 -0
- pyparticles/ogl/draw_particles_ogl_compat.py +509 -0
- pyparticles/ogl/draw_vector_field.py +221 -0
- pyparticles/ogl/opencl_gl_vbo.py +485 -0
- pyparticles/ogl/trackball.py +131 -0
- pyparticles/ogl/translate_scene.py +87 -0
- pyparticles/pset/__init__.py +21 -0
- pyparticles/pset/boundary.py +69 -0
- pyparticles/pset/cluster.py +28 -0
- pyparticles/pset/constrained_force_interactions.py +63 -0
- pyparticles/pset/constrained_x.py +158 -0
- pyparticles/pset/constraint.py +42 -0
- pyparticles/pset/default_boundary.py +43 -0
- pyparticles/pset/file_cluster.py +131 -0
- pyparticles/pset/logger.py +152 -0
- pyparticles/pset/octree.py +451 -0
- pyparticles/pset/opencl_context.py +374 -0
- pyparticles/pset/particles_set.py +499 -0
- pyparticles/pset/periodic_boundary.py +36 -0
- pyparticles/pset/rand_cluster.py +188 -0
- pyparticles/pset/rebound_boundary.py +68 -0
- pyparticles/utils/__init__.py +21 -0
- pyparticles/utils/parse_args.py +72 -0
- pyparticles/utils/problem_config.py +608 -0
- pyparticles/utils/pypart_global.py +122 -0
- pyparticles/utils/time_formatter.py +50 -0
- pyparticles3-0.4.0.dist-info/METADATA +395 -0
- pyparticles3-0.4.0.dist-info/RECORD +100 -0
- pyparticles3-0.4.0.dist-info/WHEEL +5 -0
- pyparticles3-0.4.0.dist-info/entry_points.txt +3 -0
- pyparticles3-0.4.0.dist-info/licenses/LICENSE-gpl-3.0.txt +674 -0
- 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()
|