PyParticles3 0.4.0rc1__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 +335 -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 +87 -0
  94. pyparticles/utils/time_formatter.py +50 -0
  95. pyparticles3-0.4.0rc1.dist-info/METADATA +196 -0
  96. pyparticles3-0.4.0rc1.dist-info/RECORD +100 -0
  97. pyparticles3-0.4.0rc1.dist-info/WHEEL +5 -0
  98. pyparticles3-0.4.0rc1.dist-info/entry_points.txt +3 -0
  99. pyparticles3-0.4.0rc1.dist-info/licenses/LICENSE-gpl-3.0.txt +674 -0
  100. pyparticles3-0.4.0rc1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,335 @@
1
+ # PyParticles : Particles simulation in python
2
+ # Copyright (C) 2012 Simone Riva simone.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
+
12
+ try:
13
+ import pyopencl as cl
14
+ import pyopencl.array as cla
15
+ except ImportError as exc:
16
+ cl = None
17
+ cla = None
18
+ _PYOPENCL_IMPORT_ERROR = exc
19
+ else:
20
+ _PYOPENCL_IMPORT_ERROR = None
21
+
22
+
23
+ def get_gl_sharing_context_properties():
24
+ """Return properties for sharing the currently-active OpenGL context.
25
+
26
+ PyOpenCL historically documented this helper at the top-level namespace,
27
+ while 2026.1.x keeps the implementation in ``pyopencl.tools``. Support
28
+ both layouts so the PyParticles compatibility layer works across them.
29
+ """
30
+ if cl is None:
31
+ raise RuntimeError("PyOpenCL is not available") from _PYOPENCL_IMPORT_ERROR
32
+
33
+ helper = getattr(cl, "get_gl_sharing_context_properties", None)
34
+ if helper is None:
35
+ from pyopencl.tools import get_gl_sharing_context_properties as helper
36
+ return list(helper())
37
+
38
+
39
+ OCLC_X = np.uint8(0b10000000)
40
+ OCLC_V = np.uint8(0b01000000)
41
+ OCLC_A = np.uint8(0b00100000)
42
+ OCLC_M = np.uint8(0b00010000)
43
+
44
+
45
+ class OpenCLcontext(object):
46
+ """Shared OpenCL context and particle buffers.
47
+
48
+ The modern implementation tracks which side contains the newest value for
49
+ X/V/A/M. It can also create a context sharing the currently-active OpenGL
50
+ context. In GL-sharing mode normal particle arrays remain ordinary OpenCL
51
+ buffers; a renderer may create :class:`pyopencl.GLBuffer` objects in the
52
+ same context and copy positions VRAM-to-VRAM without crossing PCIe.
53
+ """
54
+
55
+ _VALID_NAMES = ("X", "V", "A", "M")
56
+
57
+ def __init__(
58
+ self,
59
+ size,
60
+ dim,
61
+ mask=(OCLC_X | OCLC_V | OCLC_A | OCLC_M),
62
+ dtype=np.float32,
63
+ gl_sharing=False,
64
+ ):
65
+ if cl is None:
66
+ raise RuntimeError("PyOpenCL is not available") from _PYOPENCL_IMPORT_ERROR
67
+
68
+ np_dtype = np.dtype(dtype)
69
+ if np_dtype != np.dtype(np.float32):
70
+ raise TypeError("PyParticles OpenCL kernels currently support only float32")
71
+
72
+ self.__dtype = np_dtype.type
73
+ self.__size = int(size)
74
+ self.__dim = int(dim)
75
+ self.__opt_arrays = {}
76
+ self.__gl_sharing = bool(gl_sharing)
77
+ self.__platform = None
78
+ self.__device = None
79
+
80
+ try:
81
+ if self.__gl_sharing:
82
+ self.__cl_context = self._create_gl_sharing_context()
83
+ else:
84
+ self.__cl_context = cl.create_some_context(interactive=False)
85
+ if self.__cl_context.devices:
86
+ self.__device = self.__cl_context.devices[0]
87
+ self.__platform = self.__device.platform
88
+ except Exception as exc:
89
+ if self.__gl_sharing:
90
+ raise RuntimeError(
91
+ "No OpenCL context could share the active OpenGL context"
92
+ ) from exc
93
+ raise RuntimeError("No usable OpenCL context could be created") from exc
94
+
95
+ self.__cl_queue = cl.CommandQueue(
96
+ self.__cl_context,
97
+ properties=cl.command_queue_properties.PROFILING_ENABLE,
98
+ )
99
+
100
+ self.__V_cla = self._new_array((self.__size, self.__dim)) if mask & OCLC_V else None
101
+ self.__X_cla = self._new_array((self.__size, self.__dim)) if mask & OCLC_X else None
102
+ self.__A_cla = self._new_array((self.__size, self.__dim)) if mask & OCLC_A else None
103
+ self.__M_cla = self._new_array((self.__size, 1)) if mask & OCLC_M else None
104
+
105
+ self.__buffer_state = {
106
+ "X": "host" if self.__X_cla is not None else None,
107
+ "V": "host" if self.__V_cla is not None else None,
108
+ "A": "host" if self.__A_cla is not None else None,
109
+ "M": "host" if self.__M_cla is not None else None,
110
+ }
111
+ self.reset_transfer_stats()
112
+
113
+ def _create_gl_sharing_context(self):
114
+ if not hasattr(cl, "have_gl") or not cl.have_gl():
115
+ raise RuntimeError("PyOpenCL was built without OpenGL interoperability")
116
+
117
+ sharing = get_gl_sharing_context_properties()
118
+ last_error = None
119
+ for platform in cl.get_platforms():
120
+ for device in platform.get_devices():
121
+ if "cl_khr_gl_sharing" not in device.extensions.split():
122
+ continue
123
+
124
+ properties = [
125
+ (cl.context_properties.PLATFORM, platform),
126
+ *sharing,
127
+ ]
128
+ try:
129
+ context = cl.Context(devices=[device], properties=properties)
130
+ except Exception as exc:
131
+ last_error = exc
132
+ continue
133
+
134
+ self.__platform = platform
135
+ self.__device = device
136
+ return context
137
+
138
+ if last_error is not None:
139
+ raise RuntimeError("No CL/GL sharing device accepted the active GL context") from last_error
140
+ raise RuntimeError("No OpenCL device advertises cl_khr_gl_sharing")
141
+
142
+ def _new_array(self, shape, dtype=None):
143
+ if dtype is None:
144
+ dtype = self.__dtype
145
+ return cla.Array(self.__cl_queue, shape, np.dtype(dtype).type)
146
+
147
+ def _array_for_name(self, name):
148
+ name = str(name).upper()
149
+ arrays = {
150
+ "X": self.__X_cla,
151
+ "V": self.__V_cla,
152
+ "A": self.__A_cla,
153
+ "M": self.__M_cla,
154
+ }
155
+ if name not in arrays:
156
+ raise KeyError("Unknown OpenCL particle buffer %r" % name)
157
+ array = arrays[name]
158
+ if array is None:
159
+ raise ValueError("OpenCL particle buffer %s was not allocated" % name)
160
+ return name, array
161
+
162
+ def _record_transfer(self, direction, name, nbytes):
163
+ self.__transfer_stats[direction + "_calls"] += 1
164
+ self.__transfer_stats[direction + "_bytes"] += int(nbytes)
165
+ self.__transfer_stats["by_buffer"][name][direction + "_calls"] += 1
166
+ self.__transfer_stats["by_buffer"][name][direction + "_bytes"] += int(nbytes)
167
+
168
+ def reset_transfer_stats(self):
169
+ self.__transfer_stats = {
170
+ "h2d_calls": 0,
171
+ "d2h_calls": 0,
172
+ "h2d_bytes": 0,
173
+ "d2h_bytes": 0,
174
+ "by_buffer": {
175
+ name: {
176
+ "h2d_calls": 0,
177
+ "d2h_calls": 0,
178
+ "h2d_bytes": 0,
179
+ "d2h_bytes": 0,
180
+ }
181
+ for name in self._VALID_NAMES
182
+ },
183
+ }
184
+
185
+ def get_transfer_stats(self):
186
+ return {
187
+ "h2d_calls": self.__transfer_stats["h2d_calls"],
188
+ "d2h_calls": self.__transfer_stats["d2h_calls"],
189
+ "h2d_bytes": self.__transfer_stats["h2d_bytes"],
190
+ "d2h_bytes": self.__transfer_stats["d2h_bytes"],
191
+ "by_buffer": {
192
+ name: dict(values)
193
+ for name, values in self.__transfer_stats["by_buffer"].items()
194
+ },
195
+ }
196
+
197
+ transfer_stats = property(get_transfer_stats)
198
+
199
+ def get_buffer_state(self, name):
200
+ name, _ = self._array_for_name(name)
201
+ return self.__buffer_state[name]
202
+
203
+ def mark_host_modified(self, name):
204
+ name, _ = self._array_for_name(name)
205
+ self.__buffer_state[name] = "host"
206
+
207
+ def mark_device_modified(self, name):
208
+ name, _ = self._array_for_name(name)
209
+ self.__buffer_state[name] = "device"
210
+
211
+ def mark_synced(self, name):
212
+ name, _ = self._array_for_name(name)
213
+ self.__buffer_state[name] = "sync"
214
+
215
+ def sync_to_device(self, name, host_array):
216
+ """Upload *host_array* only when the host contains the newest value."""
217
+ name, device_array = self._array_for_name(name)
218
+ if self.__buffer_state[name] == "host":
219
+ host = np.ascontiguousarray(host_array, dtype=device_array.dtype)
220
+ device_array.set(host, queue=self.__cl_queue)
221
+ self._record_transfer("h2d", name, host.nbytes)
222
+ self.__buffer_state[name] = "sync"
223
+ return device_array
224
+
225
+ def sync_to_host(self, name, host_array):
226
+ """Download a device buffer only when the device contains newer data."""
227
+ name, device_array = self._array_for_name(name)
228
+ if self.__buffer_state[name] == "device":
229
+ device_array.get(queue=self.__cl_queue, ary=host_array)
230
+ self._record_transfer("d2h", name, np.asarray(host_array).nbytes)
231
+ self.__buffer_state[name] = "sync"
232
+ return host_array
233
+
234
+ def set_from_host(self, name, host_array):
235
+ """Declare the host authoritative and synchronize it to the device."""
236
+ self.mark_host_modified(name)
237
+ return self.sync_to_device(name, host_array)
238
+
239
+ def create_gl_buffer(self, gl_buffer_id, flags=None):
240
+ """Wrap an OpenGL buffer object in this GL-sharing OpenCL context."""
241
+ if not self.__gl_sharing:
242
+ raise RuntimeError("This OpenCL context was not created for GL sharing")
243
+ if flags is None:
244
+ flags = cl.mem_flags.READ_WRITE
245
+ return cl.GLBuffer(self.__cl_context, flags, int(gl_buffer_id))
246
+
247
+ def acquire_gl_objects(self, objects, wait_for=None):
248
+ if not self.__gl_sharing:
249
+ raise RuntimeError("This OpenCL context was not created for GL sharing")
250
+ return cl.enqueue_acquire_gl_objects(
251
+ self.__cl_queue, list(objects), wait_for=wait_for
252
+ )
253
+
254
+ def release_gl_objects(self, objects, wait_for=None):
255
+ if not self.__gl_sharing:
256
+ raise RuntimeError("This OpenCL context was not created for GL sharing")
257
+ return cl.enqueue_release_gl_objects(
258
+ self.__cl_queue, list(objects), wait_for=wait_for
259
+ )
260
+
261
+ def add_array_by_name(self, key, size=None, dim=None, dtype=None):
262
+ if dim is None:
263
+ dim = self.__dim
264
+ if size is None:
265
+ size = self.__size
266
+ if dtype is None:
267
+ dtype = self.dtype
268
+
269
+ self.__opt_arrays[key] = self._new_array(
270
+ (int(size), int(dim)),
271
+ dtype=np.dtype(dtype).type,
272
+ )
273
+
274
+ def get_by_name(self, key):
275
+ return self.__opt_arrays[key]
276
+
277
+ def get_dtype(self):
278
+ return self.__dtype
279
+
280
+ dtype = property(get_dtype, doc="return the dtype of the context")
281
+
282
+ def get_size(self):
283
+ return self.__size
284
+
285
+ size = property(get_size)
286
+
287
+ def get_dim(self):
288
+ return self.__dim
289
+
290
+ dim = property(get_dim)
291
+
292
+ def get_CL_context(self):
293
+ return self.__cl_context
294
+
295
+ CL_context = property(get_CL_context, doc="return the opencl context")
296
+
297
+ def get_CL_queue(self):
298
+ return self.__cl_queue
299
+
300
+ CL_queue = property(get_CL_queue, doc="return the command queue")
301
+
302
+ def get_X_cla(self):
303
+ return self.__X_cla
304
+
305
+ X_cla = property(get_X_cla, doc="return the positions array")
306
+
307
+ def get_A_cla(self):
308
+ return self.__A_cla
309
+
310
+ A_cla = property(get_A_cla, doc="return the acceleration array")
311
+
312
+ def get_V_cla(self):
313
+ return self.__V_cla
314
+
315
+ V_cla = property(get_V_cla, doc="return the velocity array")
316
+
317
+ def get_M_cla(self):
318
+ return self.__M_cla
319
+
320
+ M_cla = property(get_M_cla, doc="return the masses array")
321
+
322
+ def get_gl_sharing(self):
323
+ return self.__gl_sharing
324
+
325
+ gl_sharing = property(get_gl_sharing)
326
+
327
+ def get_device(self):
328
+ return self.__device
329
+
330
+ device = property(get_device)
331
+
332
+ def get_platform(self):
333
+ return self.__platform
334
+
335
+ platform = property(get_platform)