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,509 @@
|
|
|
1
|
+
# PyParticles : Particles simulation in python
|
|
2
|
+
# Copyright (C) 2012 Simone Riva
|
|
3
|
+
#
|
|
4
|
+
# Modern compatibility helpers for the legacy OpenGL particle renderer.
|
|
5
|
+
|
|
6
|
+
"""Modern PyOpenGL/NumPy compatibility for :mod:`draw_particles_ogl`.
|
|
7
|
+
|
|
8
|
+
The original renderer assumes Python-2-era scalar coercions and frequently
|
|
9
|
+
passes float64 NumPy storage to OpenGL pointers declared as GL_FLOAT. This
|
|
10
|
+
module preserves its public interface while normalizing scalar values and
|
|
11
|
+
client arrays for current PyOpenGL and NumPy releases.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import ctypes
|
|
15
|
+
import os
|
|
16
|
+
import time
|
|
17
|
+
|
|
18
|
+
import numpy as np
|
|
19
|
+
|
|
20
|
+
from OpenGL.GL import (
|
|
21
|
+
GL_ALPHA_TEST,
|
|
22
|
+
GL_ARRAY_BUFFER,
|
|
23
|
+
GL_BLEND,
|
|
24
|
+
GL_COLOR_ARRAY,
|
|
25
|
+
GL_DEPTH_TEST,
|
|
26
|
+
GL_FLOAT,
|
|
27
|
+
GL_FOG,
|
|
28
|
+
GL_LINES,
|
|
29
|
+
GL_MULTISAMPLE,
|
|
30
|
+
GL_POINTS,
|
|
31
|
+
GL_QUERY_RESULT,
|
|
32
|
+
GL_QUERY_RESULT_AVAILABLE,
|
|
33
|
+
GL_TEXTURE_2D,
|
|
34
|
+
GL_TIME_ELAPSED,
|
|
35
|
+
GL_UNSIGNED_INT,
|
|
36
|
+
GL_VERTEX_ARRAY,
|
|
37
|
+
glBegin,
|
|
38
|
+
glBeginQuery,
|
|
39
|
+
glBindBuffer,
|
|
40
|
+
glCallList,
|
|
41
|
+
glColor4f,
|
|
42
|
+
glColorPointer,
|
|
43
|
+
glDeleteQueries,
|
|
44
|
+
glDisable,
|
|
45
|
+
glDisableClientState,
|
|
46
|
+
glDrawArrays,
|
|
47
|
+
glDrawElements,
|
|
48
|
+
glEnable,
|
|
49
|
+
glEnableClientState,
|
|
50
|
+
glEnd,
|
|
51
|
+
glEndQuery,
|
|
52
|
+
glGenQueries,
|
|
53
|
+
glIsEnabled,
|
|
54
|
+
glPointSize,
|
|
55
|
+
glPopMatrix,
|
|
56
|
+
glPushMatrix,
|
|
57
|
+
glScalef,
|
|
58
|
+
glTranslatef,
|
|
59
|
+
glVertex3f,
|
|
60
|
+
glVertexPointer,
|
|
61
|
+
)
|
|
62
|
+
from OpenGL.raw.GL.VERSION.GL_1_5 import (
|
|
63
|
+
glGetQueryObjectiv as _raw_glGetQueryObjectiv,
|
|
64
|
+
)
|
|
65
|
+
from OpenGL.raw.GL.VERSION.GL_3_3 import (
|
|
66
|
+
glGetQueryObjectui64v as _raw_glGetQueryObjectui64v,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
import pyparticles.ogl.draw_particles_ogl as legacy
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _scalar(value):
|
|
73
|
+
"""Return a Python float from a scalar or one-element NumPy value."""
|
|
74
|
+
return float(np.asarray(value).reshape(-1)[0])
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def charged_particles_color(pset, i):
|
|
78
|
+
a = 0.4
|
|
79
|
+
charge = _scalar(pset.Q[i])
|
|
80
|
+
if charge > 0.0:
|
|
81
|
+
return (1.0, a, a, 1.0)
|
|
82
|
+
if charge < 0.0:
|
|
83
|
+
return (a, a, 1.0, 1.0)
|
|
84
|
+
return (a, a, a, 1.0)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def charged_particles_vect_color(RGBA, pset):
|
|
88
|
+
a = 0.4
|
|
89
|
+
charges = np.asarray(pset.Q).reshape(-1)
|
|
90
|
+
RGBA[charges > 0.0, :] = (1.0, a, a, 1.0)
|
|
91
|
+
RGBA[charges < 0.0, :] = (a, a, 1.0, 1.0)
|
|
92
|
+
RGBA[charges == 0.0, :] = (a, a, a, 1.0)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class DrawParticlesGL(legacy.DrawParticlesGL):
|
|
96
|
+
"""Legacy particle renderer with modern scalar and buffer handling."""
|
|
97
|
+
|
|
98
|
+
_GPU_QUERY_LIMIT = 64
|
|
99
|
+
_RENDER_BENCH_MODES = {
|
|
100
|
+
"legacy": (),
|
|
101
|
+
"no_msaa": (GL_MULTISAMPLE,),
|
|
102
|
+
"no_fog": (GL_FOG,),
|
|
103
|
+
"no_blend": (GL_BLEND,),
|
|
104
|
+
"no_alpha": (GL_ALPHA_TEST,),
|
|
105
|
+
"no_depth": (GL_DEPTH_TEST,),
|
|
106
|
+
"fast_points": (
|
|
107
|
+
GL_MULTISAMPLE,
|
|
108
|
+
GL_FOG,
|
|
109
|
+
GL_BLEND,
|
|
110
|
+
GL_ALPHA_TEST,
|
|
111
|
+
GL_DEPTH_TEST,
|
|
112
|
+
GL_TEXTURE_2D,
|
|
113
|
+
),
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
def __init__(self, *args, **kwargs):
|
|
117
|
+
super(DrawParticlesGL, self).__init__(*args, **kwargs)
|
|
118
|
+
self.__shared_position_vbo = None
|
|
119
|
+
self.__shared_position_draw_complete_callback = None
|
|
120
|
+
self.__last_draw_submit_seconds = 0.0
|
|
121
|
+
|
|
122
|
+
requested_mode = os.environ.get(
|
|
123
|
+
"PYPARTICLES_GL_BENCH_MODE", "legacy"
|
|
124
|
+
).strip().lower()
|
|
125
|
+
if requested_mode not in self._RENDER_BENCH_MODES:
|
|
126
|
+
requested_mode = "legacy"
|
|
127
|
+
self.__render_benchmark_mode = requested_mode
|
|
128
|
+
|
|
129
|
+
if "PYPARTICLES_GL_BENCH_MODE" in os.environ:
|
|
130
|
+
print("OpenGL particle draw mode:", self.__render_benchmark_mode)
|
|
131
|
+
|
|
132
|
+
# GL timer queries are opt-in and are polled asynchronously. Never ask
|
|
133
|
+
# for GL_QUERY_RESULT until GL_QUERY_RESULT_AVAILABLE says the result is
|
|
134
|
+
# ready, otherwise the profiler itself would insert a GPU/CPU stall.
|
|
135
|
+
self.__gpu_timing_enabled = False
|
|
136
|
+
self.__gpu_timing_available = True
|
|
137
|
+
self.__gpu_timing_error = None
|
|
138
|
+
self.__gpu_queries_pending = []
|
|
139
|
+
self.__gpu_draw_seconds_ready = []
|
|
140
|
+
self.__gpu_query_skipped = 0
|
|
141
|
+
|
|
142
|
+
def __del__(self):
|
|
143
|
+
# OpenGL contexts are frequently already gone during interpreter
|
|
144
|
+
# teardown. The driver releases display-list/query resources with
|
|
145
|
+
# context destruction, so avoid unsafe GL calls from __del__.
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
def _get_color_fun(self):
|
|
149
|
+
return self._DrawParticlesGL__color_fun
|
|
150
|
+
|
|
151
|
+
def _set_color_fun(self, fun):
|
|
152
|
+
self._DrawParticlesGL__color_fun = fun
|
|
153
|
+
|
|
154
|
+
color_fun = property(_get_color_fun, _set_color_fun)
|
|
155
|
+
|
|
156
|
+
def _get_vect_color_fun(self):
|
|
157
|
+
return self._DrawParticlesGL__vect_color_fun
|
|
158
|
+
|
|
159
|
+
def _set_vect_color_fun(self, fun):
|
|
160
|
+
self._DrawParticlesGL__vect_color_fun = fun
|
|
161
|
+
self._DrawParticlesGL__vect_color_fun_fl = False
|
|
162
|
+
|
|
163
|
+
vect_color_fun = property(_get_vect_color_fun, _set_vect_color_fun)
|
|
164
|
+
|
|
165
|
+
def set_shared_position_vbo(self, vbo):
|
|
166
|
+
"""Use *vbo* as the vectorized particle position source.
|
|
167
|
+
|
|
168
|
+
``None`` restores the normal host NumPy client-array path. The VBO is
|
|
169
|
+
expected to contain tightly-packed float32 XYZ triples in particle
|
|
170
|
+
order and remains owned by the CL/GL bridge that created it.
|
|
171
|
+
"""
|
|
172
|
+
self.__shared_position_vbo = None if vbo is None else int(vbo)
|
|
173
|
+
|
|
174
|
+
def get_shared_position_vbo(self):
|
|
175
|
+
return self.__shared_position_vbo
|
|
176
|
+
|
|
177
|
+
shared_position_vbo = property(
|
|
178
|
+
get_shared_position_vbo, set_shared_position_vbo
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
def set_shared_position_draw_complete_callback(self, callback):
|
|
182
|
+
"""Set a callback invoked just after a shared-VBO draw is submitted."""
|
|
183
|
+
self.__shared_position_draw_complete_callback = callback
|
|
184
|
+
|
|
185
|
+
def get_shared_position_draw_complete_callback(self):
|
|
186
|
+
return self.__shared_position_draw_complete_callback
|
|
187
|
+
|
|
188
|
+
shared_position_draw_complete_callback = property(
|
|
189
|
+
get_shared_position_draw_complete_callback,
|
|
190
|
+
set_shared_position_draw_complete_callback,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def set_render_benchmark_mode(self, mode):
|
|
194
|
+
"""Select particle-only GL state controls for renderer benchmarking."""
|
|
195
|
+
mode = str(mode).strip().lower()
|
|
196
|
+
if mode not in self._RENDER_BENCH_MODES:
|
|
197
|
+
raise ValueError(
|
|
198
|
+
"Unknown OpenGL benchmark mode %r; expected one of: %s"
|
|
199
|
+
% (mode, ", ".join(sorted(self._RENDER_BENCH_MODES)))
|
|
200
|
+
)
|
|
201
|
+
self.__render_benchmark_mode = mode
|
|
202
|
+
|
|
203
|
+
def get_render_benchmark_mode(self):
|
|
204
|
+
return self.__render_benchmark_mode
|
|
205
|
+
|
|
206
|
+
render_benchmark_mode = property(
|
|
207
|
+
get_render_benchmark_mode, set_render_benchmark_mode
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
def get_render_benchmark_modes(self):
|
|
211
|
+
return tuple(sorted(self._RENDER_BENCH_MODES))
|
|
212
|
+
|
|
213
|
+
render_benchmark_modes = property(get_render_benchmark_modes)
|
|
214
|
+
|
|
215
|
+
def _disable_benchmark_states(self):
|
|
216
|
+
"""Disable selected GL states and return those that must be restored."""
|
|
217
|
+
disabled = []
|
|
218
|
+
for state in self._RENDER_BENCH_MODES[self.__render_benchmark_mode]:
|
|
219
|
+
try:
|
|
220
|
+
if bool(glIsEnabled(state)):
|
|
221
|
+
glDisable(state)
|
|
222
|
+
disabled.append(state)
|
|
223
|
+
except Exception:
|
|
224
|
+
# Benchmark controls must never make the renderer unusable.
|
|
225
|
+
pass
|
|
226
|
+
return disabled
|
|
227
|
+
|
|
228
|
+
@staticmethod
|
|
229
|
+
def _restore_benchmark_states(states):
|
|
230
|
+
for state in states:
|
|
231
|
+
try:
|
|
232
|
+
glEnable(state)
|
|
233
|
+
except Exception:
|
|
234
|
+
pass
|
|
235
|
+
|
|
236
|
+
def get_last_draw_submit_seconds(self):
|
|
237
|
+
"""CPU time spent submitting the most recent glDrawArrays call."""
|
|
238
|
+
return self.__last_draw_submit_seconds
|
|
239
|
+
|
|
240
|
+
last_draw_submit_seconds = property(get_last_draw_submit_seconds)
|
|
241
|
+
|
|
242
|
+
def set_gpu_timing_enabled(self, enabled):
|
|
243
|
+
"""Enable non-blocking GL_TIME_ELAPSED measurements for glDrawArrays."""
|
|
244
|
+
enabled = bool(enabled)
|
|
245
|
+
if enabled:
|
|
246
|
+
self.__gpu_timing_enabled = True
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
self.__gpu_timing_enabled = False
|
|
250
|
+
self.cleanup_gpu_timing()
|
|
251
|
+
|
|
252
|
+
def get_gpu_timing_enabled(self):
|
|
253
|
+
return self.__gpu_timing_enabled
|
|
254
|
+
|
|
255
|
+
gpu_timing_enabled = property(
|
|
256
|
+
get_gpu_timing_enabled, set_gpu_timing_enabled
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
def _disable_gpu_timing(self, exc):
|
|
260
|
+
self.__gpu_timing_available = False
|
|
261
|
+
self.__gpu_timing_error = "%s: %s" % (
|
|
262
|
+
exc.__class__.__name__, exc
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
def _poll_gpu_timing(self):
|
|
266
|
+
"""Collect completed timer queries without waiting for the GPU.
|
|
267
|
+
|
|
268
|
+
Use raw OpenGL entry points with explicit ctypes output storage. The
|
|
269
|
+
high-level PyOpenGL wrappers for glGetQueryObject* are not consistently
|
|
270
|
+
auto-wrapped on all installations and may require the output pointer.
|
|
271
|
+
"""
|
|
272
|
+
if not self.__gpu_queries_pending:
|
|
273
|
+
return
|
|
274
|
+
|
|
275
|
+
while self.__gpu_queries_pending:
|
|
276
|
+
query = self.__gpu_queries_pending[0]
|
|
277
|
+
try:
|
|
278
|
+
available_value = ctypes.c_int(0)
|
|
279
|
+
_raw_glGetQueryObjectiv(
|
|
280
|
+
query,
|
|
281
|
+
GL_QUERY_RESULT_AVAILABLE,
|
|
282
|
+
ctypes.byref(available_value),
|
|
283
|
+
)
|
|
284
|
+
available = bool(available_value.value)
|
|
285
|
+
except Exception as exc:
|
|
286
|
+
self._disable_gpu_timing(exc)
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
if not available:
|
|
290
|
+
break
|
|
291
|
+
|
|
292
|
+
self.__gpu_queries_pending.pop(0)
|
|
293
|
+
try:
|
|
294
|
+
elapsed_value = ctypes.c_uint64(0)
|
|
295
|
+
_raw_glGetQueryObjectui64v(
|
|
296
|
+
query,
|
|
297
|
+
GL_QUERY_RESULT,
|
|
298
|
+
ctypes.byref(elapsed_value),
|
|
299
|
+
)
|
|
300
|
+
self.__gpu_draw_seconds_ready.append(
|
|
301
|
+
int(elapsed_value.value) * 1.0e-9
|
|
302
|
+
)
|
|
303
|
+
except Exception as exc:
|
|
304
|
+
self._disable_gpu_timing(exc)
|
|
305
|
+
finally:
|
|
306
|
+
try:
|
|
307
|
+
glDeleteQueries(1, [query])
|
|
308
|
+
except Exception:
|
|
309
|
+
pass
|
|
310
|
+
|
|
311
|
+
def poll_gpu_timing(self):
|
|
312
|
+
self._poll_gpu_timing()
|
|
313
|
+
|
|
314
|
+
def drain_gpu_draw_times(self):
|
|
315
|
+
"""Return completed draw timings accumulated since the previous drain."""
|
|
316
|
+
self._poll_gpu_timing()
|
|
317
|
+
values = list(self.__gpu_draw_seconds_ready)
|
|
318
|
+
self.__gpu_draw_seconds_ready[:] = []
|
|
319
|
+
return values
|
|
320
|
+
|
|
321
|
+
def get_gpu_timing_stats(self):
|
|
322
|
+
return {
|
|
323
|
+
"available": bool(self.__gpu_timing_available),
|
|
324
|
+
"pending": len(self.__gpu_queries_pending),
|
|
325
|
+
"ready": len(self.__gpu_draw_seconds_ready),
|
|
326
|
+
"skipped": int(self.__gpu_query_skipped),
|
|
327
|
+
"error": self.__gpu_timing_error,
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
gpu_timing_stats = property(get_gpu_timing_stats)
|
|
331
|
+
|
|
332
|
+
def cleanup_gpu_timing(self):
|
|
333
|
+
"""Delete pending query objects while the GL context is still current."""
|
|
334
|
+
for query in self.__gpu_queries_pending:
|
|
335
|
+
try:
|
|
336
|
+
glDeleteQueries(1, [query])
|
|
337
|
+
except Exception:
|
|
338
|
+
pass
|
|
339
|
+
self.__gpu_queries_pending[:] = []
|
|
340
|
+
self.__gpu_draw_seconds_ready[:] = []
|
|
341
|
+
|
|
342
|
+
def _draw_arrays_profiled(self):
|
|
343
|
+
"""Submit the point draw, optionally wrapped in an asynchronous timer."""
|
|
344
|
+
self._poll_gpu_timing()
|
|
345
|
+
query = None
|
|
346
|
+
disabled_states = self._disable_benchmark_states()
|
|
347
|
+
|
|
348
|
+
if (
|
|
349
|
+
self.__gpu_timing_enabled
|
|
350
|
+
and self.__gpu_timing_available
|
|
351
|
+
and len(self.__gpu_queries_pending) < self._GPU_QUERY_LIMIT
|
|
352
|
+
):
|
|
353
|
+
try:
|
|
354
|
+
query = int(np.asarray(glGenQueries(1)).reshape(-1)[0])
|
|
355
|
+
glBeginQuery(GL_TIME_ELAPSED, query)
|
|
356
|
+
except Exception as exc:
|
|
357
|
+
self._disable_gpu_timing(exc)
|
|
358
|
+
query = None
|
|
359
|
+
elif self.__gpu_timing_enabled:
|
|
360
|
+
self.__gpu_query_skipped += 1
|
|
361
|
+
|
|
362
|
+
draw_start = time.perf_counter()
|
|
363
|
+
try:
|
|
364
|
+
glDrawArrays(GL_POINTS, 0, self.pset.size)
|
|
365
|
+
finally:
|
|
366
|
+
self.__last_draw_submit_seconds = time.perf_counter() - draw_start
|
|
367
|
+
if query is not None:
|
|
368
|
+
try:
|
|
369
|
+
glEndQuery(GL_TIME_ELAPSED)
|
|
370
|
+
self.__gpu_queries_pending.append(query)
|
|
371
|
+
except Exception as exc:
|
|
372
|
+
self._disable_gpu_timing(exc)
|
|
373
|
+
try:
|
|
374
|
+
glDeleteQueries(1, [query])
|
|
375
|
+
except Exception:
|
|
376
|
+
pass
|
|
377
|
+
self._restore_benchmark_states(disabled_states)
|
|
378
|
+
|
|
379
|
+
def draw_particle(self, pset, i):
|
|
380
|
+
mass = _scalar(pset.M[i])
|
|
381
|
+
glPointSize(float(0.01 + mass / pset.mass_unit))
|
|
382
|
+
glColor4f(*self._DrawParticlesGL__color_fun(pset, i))
|
|
383
|
+
|
|
384
|
+
glBegin(GL_POINTS)
|
|
385
|
+
glVertex3f(
|
|
386
|
+
float(pset.X[i, 0] / pset.unit),
|
|
387
|
+
float(pset.X[i, 1] / pset.unit),
|
|
388
|
+
float(pset.X[i, 2] / pset.unit),
|
|
389
|
+
)
|
|
390
|
+
glEnd()
|
|
391
|
+
|
|
392
|
+
def draw_particle_sphere(self, pset, i):
|
|
393
|
+
mass = _scalar(pset.M[i])
|
|
394
|
+
radius = 0.5 * (0.05 + 0.1 / (1.0 + np.exp(-mass / pset.mass_unit)))
|
|
395
|
+
|
|
396
|
+
glColor4f(*self._DrawParticlesGL__color_fun(pset, i))
|
|
397
|
+
glPushMatrix()
|
|
398
|
+
glTranslatef(
|
|
399
|
+
float(pset.X[i, 0] / pset.unit),
|
|
400
|
+
float(pset.X[i, 1] / pset.unit),
|
|
401
|
+
float(pset.X[i, 2] / pset.unit),
|
|
402
|
+
)
|
|
403
|
+
glScalef(float(radius), float(radius), float(radius))
|
|
404
|
+
glCallList(self._DrawParticlesGL__sph_dl)
|
|
405
|
+
glPopMatrix()
|
|
406
|
+
|
|
407
|
+
def draw_particle_teapot(self, pset, i):
|
|
408
|
+
mass = _scalar(pset.M[i])
|
|
409
|
+
radius = 0.5 * (0.05 + 0.1 / (1.0 + np.exp(-mass / pset.mass_unit)))
|
|
410
|
+
|
|
411
|
+
glColor4f(*self._DrawParticlesGL__color_fun(pset, i))
|
|
412
|
+
glPushMatrix()
|
|
413
|
+
glTranslatef(
|
|
414
|
+
float(pset.X[i, 0] / pset.unit),
|
|
415
|
+
float(pset.X[i, 1] / pset.unit),
|
|
416
|
+
float(pset.X[i, 2] / pset.unit),
|
|
417
|
+
)
|
|
418
|
+
glScalef(float(radius), float(radius), float(radius))
|
|
419
|
+
glCallList(self._DrawParticlesGL__tea_dl)
|
|
420
|
+
glPopMatrix()
|
|
421
|
+
|
|
422
|
+
def _draw_vectorized(self):
|
|
423
|
+
vect_color_fun = self._DrawParticlesGL__vect_color_fun
|
|
424
|
+
if vect_color_fun is not None:
|
|
425
|
+
colors = np.empty((self.pset.size, 4), dtype=np.float32)
|
|
426
|
+
vect_color_fun(colors, self.pset)
|
|
427
|
+
colors = np.ascontiguousarray(colors, dtype=np.float32)
|
|
428
|
+
# Client color memory must be captured while no array buffer is
|
|
429
|
+
# bound; otherwise the pointer is interpreted as a VBO offset.
|
|
430
|
+
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
|
431
|
+
glEnableClientState(GL_COLOR_ARRAY)
|
|
432
|
+
glColorPointer(4, GL_FLOAT, 0, colors)
|
|
433
|
+
else:
|
|
434
|
+
colors = None
|
|
435
|
+
|
|
436
|
+
glEnableClientState(GL_VERTEX_ARRAY)
|
|
437
|
+
if self.__shared_position_vbo is None:
|
|
438
|
+
vertices = np.ascontiguousarray(
|
|
439
|
+
np.asarray(self.pset.X) / self.pset.unit,
|
|
440
|
+
dtype=np.float32,
|
|
441
|
+
)
|
|
442
|
+
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
|
443
|
+
glVertexPointer(3, GL_FLOAT, 0, vertices)
|
|
444
|
+
self._draw_arrays_profiled()
|
|
445
|
+
else:
|
|
446
|
+
# Positions are already in GPU memory. Apply the unit conversion
|
|
447
|
+
# as a model-view scale instead of materializing a host array.
|
|
448
|
+
vbo = self.__shared_position_vbo
|
|
449
|
+
glBindBuffer(GL_ARRAY_BUFFER, vbo)
|
|
450
|
+
glVertexPointer(3, GL_FLOAT, 0, ctypes.c_void_p(0))
|
|
451
|
+
glPushMatrix()
|
|
452
|
+
unit_scale = 1.0 / float(self.pset.unit)
|
|
453
|
+
glScalef(unit_scale, unit_scale, unit_scale)
|
|
454
|
+
self._draw_arrays_profiled()
|
|
455
|
+
|
|
456
|
+
# The fence belongs immediately after the command that consumes
|
|
457
|
+
# this VBO. The CL/GL bridge uses it before reacquiring this same
|
|
458
|
+
# buffer on a later frame, avoiding a global glFinish().
|
|
459
|
+
callback = self.__shared_position_draw_complete_callback
|
|
460
|
+
if callback is not None:
|
|
461
|
+
callback(vbo)
|
|
462
|
+
|
|
463
|
+
glPopMatrix()
|
|
464
|
+
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
|
465
|
+
|
|
466
|
+
glDisableClientState(GL_VERTEX_ARRAY)
|
|
467
|
+
|
|
468
|
+
if colors is not None:
|
|
469
|
+
glDisableClientState(GL_COLOR_ARRAY)
|
|
470
|
+
|
|
471
|
+
def draw_trajectory(self):
|
|
472
|
+
if self.pset.log_size < self.trajectory_step + 1:
|
|
473
|
+
return
|
|
474
|
+
|
|
475
|
+
indices = self._DrawParticlesGL__log_indices
|
|
476
|
+
if indices is None or len(indices) != max(0, 2 * self.pset.log_max_size - 2):
|
|
477
|
+
indices = self.pset.get_log_indices_segments(True)
|
|
478
|
+
self._DrawParticlesGL__log_indices = indices
|
|
479
|
+
self._DrawParticlesGL__log_array = np.zeros(
|
|
480
|
+
(self.pset.log_max_size, self.pset.dim),
|
|
481
|
+
dtype=np.float32,
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
log_array = self._DrawParticlesGL__log_array
|
|
485
|
+
for i in range(self.pset.size):
|
|
486
|
+
glColor4f(*self._DrawParticlesGL__color_fun(self.pset, i))
|
|
487
|
+
glEnableClientState(GL_VERTEX_ARRAY)
|
|
488
|
+
|
|
489
|
+
_, count = self.pset.read_log_array(i, (log_array,))
|
|
490
|
+
if count > 0:
|
|
491
|
+
vertices = np.ascontiguousarray(
|
|
492
|
+
log_array / self.pset.unit,
|
|
493
|
+
dtype=np.float32,
|
|
494
|
+
)
|
|
495
|
+
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
|
496
|
+
glVertexPointer(3, GL_FLOAT, 0, vertices)
|
|
497
|
+
glDrawElements(
|
|
498
|
+
GL_LINES,
|
|
499
|
+
count,
|
|
500
|
+
GL_UNSIGNED_INT,
|
|
501
|
+
np.ascontiguousarray(indices[:count], dtype=np.uint32),
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
glDisableClientState(GL_VERTEX_ARRAY)
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
# Keep color helpers obtained from the historical module working as well.
|
|
508
|
+
legacy.charged_particles_color = charged_particles_color
|
|
509
|
+
legacy.charged_particles_vect_color = charged_particles_vect_color
|