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.
- 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 +335 -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 +87 -0
- pyparticles/utils/time_formatter.py +50 -0
- pyparticles3-0.4.0rc1.dist-info/METADATA +196 -0
- pyparticles3-0.4.0rc1.dist-info/RECORD +100 -0
- pyparticles3-0.4.0rc1.dist-info/WHEEL +5 -0
- pyparticles3-0.4.0rc1.dist-info/entry_points.txt +3 -0
- pyparticles3-0.4.0rc1.dist-info/licenses/LICENSE-gpl-3.0.txt +674 -0
- pyparticles3-0.4.0rc1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,174 @@
|
|
|
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.forces.force as fr
|
|
20
|
+
|
|
21
|
+
import pyparticles.pset.opencl_context as occ
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
import pyopencl as cl
|
|
25
|
+
import pyopencl.array as cla
|
|
26
|
+
except:
|
|
27
|
+
___foo = 0
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Damping( fr.Force ) :
|
|
31
|
+
r"""
|
|
32
|
+
Compute the damping forces, the damping is a force that react proportionally to the velocity
|
|
33
|
+
|
|
34
|
+
The force is given the equation:
|
|
35
|
+
|
|
36
|
+
.. math::
|
|
37
|
+
|
|
38
|
+
F_i = -C\dot{X}
|
|
39
|
+
|
|
40
|
+
Constructor
|
|
41
|
+
|
|
42
|
+
:param size: the number of particles in the system
|
|
43
|
+
:param dim: the dimension of the system
|
|
44
|
+
:param m: a vector containig the masses
|
|
45
|
+
:param Const: the damping factor
|
|
46
|
+
"""
|
|
47
|
+
def __init__(self , size , dim=3 , m=None , Consts=1.0 ):
|
|
48
|
+
|
|
49
|
+
self.__dim = dim
|
|
50
|
+
self.__size = size
|
|
51
|
+
|
|
52
|
+
self.__C = np.zeros( ( size , 1 ) )
|
|
53
|
+
self.__C[:] = Consts
|
|
54
|
+
|
|
55
|
+
self.__A = np.zeros( ( size , dim ) )
|
|
56
|
+
self.__F = np.zeros( ( size , dim ) )
|
|
57
|
+
|
|
58
|
+
self.__M = np.zeros( ( size , 1 ) )
|
|
59
|
+
if m != None :
|
|
60
|
+
self.set_masses( m )
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def set_masses( self , m ):
|
|
65
|
+
self.__M[:] = m
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def update_force( self , pset ):
|
|
69
|
+
|
|
70
|
+
self.__F[:] = -pset.V[:] * self.__C[:]
|
|
71
|
+
self.__A = self.__F[:] / self.__M
|
|
72
|
+
|
|
73
|
+
return self.__A
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def getA(self):
|
|
77
|
+
return self.__A
|
|
78
|
+
|
|
79
|
+
A = property( getA )
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def getF(self):
|
|
83
|
+
return self.__A * self.__M[:,0]
|
|
84
|
+
|
|
85
|
+
F = property( getF )
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class DampingOCL( fr.Force ) :
|
|
89
|
+
r"""
|
|
90
|
+
Compute the damping forces, the damping is a force that react proportionally to the velocity
|
|
91
|
+
|
|
92
|
+
The force is given the equation:
|
|
93
|
+
|
|
94
|
+
.. math::
|
|
95
|
+
|
|
96
|
+
F_i = -C\dot{X}
|
|
97
|
+
|
|
98
|
+
Constructor
|
|
99
|
+
|
|
100
|
+
:param size: the number of particles in the system
|
|
101
|
+
:param dim: the dimension of the system
|
|
102
|
+
:param m: a vector containig the masses
|
|
103
|
+
:param Const: the damping factor
|
|
104
|
+
"""
|
|
105
|
+
def __init__(self , size , dim=3 , m=None , Consts=1.0 , ocl_context=None ):
|
|
106
|
+
|
|
107
|
+
self.__dim = np.int( dim )
|
|
108
|
+
self.__size = np.int( size )
|
|
109
|
+
|
|
110
|
+
if ocl_context == None :
|
|
111
|
+
self.__occ = occ.OpenCLcontext( size , dim , ( occ.OCLC_V | occ.OCLC_A | occ.OCLC_M ) )
|
|
112
|
+
else :
|
|
113
|
+
self.__occ = ocl_context
|
|
114
|
+
|
|
115
|
+
self.__dtype = self.__occ.dtype
|
|
116
|
+
|
|
117
|
+
self.__K = self.__occ.dtype( Consts )
|
|
118
|
+
|
|
119
|
+
self.__A = np.zeros( ( size , dim ) , dtype=self.__occ.dtype )
|
|
120
|
+
self.__F = np.zeros( ( size , dim ) , dtype=self.__occ.dtype )
|
|
121
|
+
|
|
122
|
+
if m != None :
|
|
123
|
+
self.set_masses( m )
|
|
124
|
+
|
|
125
|
+
self.__init_prog_cl()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def __init_prog_cl(self):
|
|
129
|
+
self.__damping_prg = """
|
|
130
|
+
__kernel void damping(__global const float *V ,
|
|
131
|
+
__global const float *M ,
|
|
132
|
+
float K ,
|
|
133
|
+
__global float *A )
|
|
134
|
+
{
|
|
135
|
+
int i = get_global_id(0) ;
|
|
136
|
+
|
|
137
|
+
A[3*i] = ( K * V[3*i] ) / M[i] ;
|
|
138
|
+
A[3*i+1] = ( K * V[3*i+1] ) / M[i] ;
|
|
139
|
+
A[3*i+2] = ( K * V[3*i+2] ) / M[i] ;
|
|
140
|
+
}
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
self.__cl_program = cl.Program( self.__occ.CL_context , self.__damping_prg ).build()
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def set_masses( self , m ):
|
|
147
|
+
self.__occ.M_cla.set( self.__dtype( m ) , queue=self.__occ.CL_queue )
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def update_force( self , pset ):
|
|
151
|
+
|
|
152
|
+
self.__occ.V_cla.set( self.__dtype( pset.V ) , queue=self.__occ.CL_queue )
|
|
153
|
+
|
|
154
|
+
self.__cl_program.damping( self.__occ.CL_queue , ( self.__size , ) , None ,
|
|
155
|
+
self.__occ.V_cla.data ,
|
|
156
|
+
self.__occ.M_cla.data ,
|
|
157
|
+
self.__K ,
|
|
158
|
+
self.__occ.A_cla.data )
|
|
159
|
+
|
|
160
|
+
self.__occ.A_cla.get( self.__occ.CL_queue , self.__A )
|
|
161
|
+
|
|
162
|
+
return self.__A
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def getA(self):
|
|
166
|
+
return self.__A
|
|
167
|
+
|
|
168
|
+
A = property( getA )
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def getF(self):
|
|
172
|
+
return self.__A * self.__M[:,0]
|
|
173
|
+
|
|
174
|
+
F = property( getF )
|
|
@@ -0,0 +1,167 @@
|
|
|
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 numpy as np
|
|
10
|
+
|
|
11
|
+
import pyparticles.forces.force as fr
|
|
12
|
+
import pyparticles.pset.opencl_context as occ
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
import pyopencl as cl
|
|
16
|
+
except ImportError:
|
|
17
|
+
cl = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Drag(fr.Force):
|
|
21
|
+
"""Quadratic drag force."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, size, dim=3, m=None, Consts=1.0):
|
|
24
|
+
self.__dim = int(dim)
|
|
25
|
+
self.__size = int(size)
|
|
26
|
+
self.__G = np.zeros((size, 1))
|
|
27
|
+
self.__G[:] = Consts
|
|
28
|
+
self.__A = np.zeros((size, dim))
|
|
29
|
+
self.__F = np.zeros((size, dim))
|
|
30
|
+
self.__V = np.zeros((size, 1))
|
|
31
|
+
self.__M = np.zeros((size, 1))
|
|
32
|
+
|
|
33
|
+
if m is not None:
|
|
34
|
+
self.set_masses(m)
|
|
35
|
+
|
|
36
|
+
def set_masses(self, m):
|
|
37
|
+
self.__M[:] = m
|
|
38
|
+
|
|
39
|
+
def update_force(self, pset):
|
|
40
|
+
self.__V[:, 0] = np.sqrt(np.sum(pset.V**2, axis=1))
|
|
41
|
+
self.__F[:] = -0.5 * self.__V * pset.V * self.__G
|
|
42
|
+
self.__A[:] = self.__F / self.__M
|
|
43
|
+
return self.__A
|
|
44
|
+
|
|
45
|
+
def getA(self):
|
|
46
|
+
return self.__A
|
|
47
|
+
|
|
48
|
+
A = property(getA)
|
|
49
|
+
|
|
50
|
+
def getF(self):
|
|
51
|
+
return self.__A * self.__M
|
|
52
|
+
|
|
53
|
+
F = property(getF)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class DragOCL(fr.Force):
|
|
57
|
+
"""OpenCL implementation of 3-D quadratic drag."""
|
|
58
|
+
|
|
59
|
+
def __init__(self, size, dim=3, m=None, Consts=1.0, ocl_context=None):
|
|
60
|
+
if cl is None:
|
|
61
|
+
raise RuntimeError("PyOpenCL is required for DragOCL")
|
|
62
|
+
if int(dim) != 3:
|
|
63
|
+
raise ValueError("DragOCL currently supports only 3 dimensions")
|
|
64
|
+
|
|
65
|
+
self.__dim = int(dim)
|
|
66
|
+
self.__size = int(size)
|
|
67
|
+
|
|
68
|
+
if ocl_context is None:
|
|
69
|
+
self.__occ = occ.OpenCLcontext(
|
|
70
|
+
size,
|
|
71
|
+
dim,
|
|
72
|
+
occ.OCLC_V | occ.OCLC_A | occ.OCLC_M,
|
|
73
|
+
)
|
|
74
|
+
else:
|
|
75
|
+
self.__occ = ocl_context
|
|
76
|
+
|
|
77
|
+
self.__dtype = self.__occ.dtype
|
|
78
|
+
self.__K = self.__dtype(Consts)
|
|
79
|
+
self.__A = np.zeros((size, dim), dtype=self.__dtype)
|
|
80
|
+
self.__M = np.zeros((size, 1), dtype=self.__dtype)
|
|
81
|
+
|
|
82
|
+
self.__init_prog_cl()
|
|
83
|
+
if m is not None:
|
|
84
|
+
self.set_masses(m)
|
|
85
|
+
|
|
86
|
+
def __init_prog_cl(self):
|
|
87
|
+
source = r"""
|
|
88
|
+
__kernel void drag(
|
|
89
|
+
__global const float *V,
|
|
90
|
+
__global const float *M,
|
|
91
|
+
float K,
|
|
92
|
+
int accumulate,
|
|
93
|
+
__global float *A)
|
|
94
|
+
{
|
|
95
|
+
int i = get_global_id(0);
|
|
96
|
+
int i0 = 3*i;
|
|
97
|
+
int i1 = i0 + 1;
|
|
98
|
+
int i2 = i0 + 2;
|
|
99
|
+
|
|
100
|
+
float speed = sqrt(
|
|
101
|
+
V[i0]*V[i0] + V[i1]*V[i1] + V[i2]*V[i2]
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
float ax = (-0.5f * K * speed * V[i0]) / M[i];
|
|
105
|
+
float ay = (-0.5f * K * speed * V[i1]) / M[i];
|
|
106
|
+
float az = (-0.5f * K * speed * V[i2]) / M[i];
|
|
107
|
+
|
|
108
|
+
if (accumulate)
|
|
109
|
+
{
|
|
110
|
+
A[i0] += ax;
|
|
111
|
+
A[i1] += ay;
|
|
112
|
+
A[i2] += az;
|
|
113
|
+
}
|
|
114
|
+
else
|
|
115
|
+
{
|
|
116
|
+
A[i0] = ax;
|
|
117
|
+
A[i1] = ay;
|
|
118
|
+
A[i2] = az;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
"""
|
|
122
|
+
self.__cl_program = cl.Program(self.__occ.CL_context, source).build()
|
|
123
|
+
self.__drag_kernel = cl.Kernel(self.__cl_program, "drag")
|
|
124
|
+
|
|
125
|
+
def set_masses(self, m):
|
|
126
|
+
self.__M[:] = np.asarray(m, dtype=self.__dtype)
|
|
127
|
+
self.__occ.set_from_host("M", self.__M)
|
|
128
|
+
|
|
129
|
+
def update_force_device(self, pset, accumulate=False, host_authoritative=False):
|
|
130
|
+
if host_authoritative:
|
|
131
|
+
self.__occ.mark_host_modified("V")
|
|
132
|
+
self.__occ.sync_to_device("V", pset.V)
|
|
133
|
+
|
|
134
|
+
self.__drag_kernel(
|
|
135
|
+
self.__occ.CL_queue,
|
|
136
|
+
(self.__size,),
|
|
137
|
+
None,
|
|
138
|
+
self.__occ.V_cla.data,
|
|
139
|
+
self.__occ.M_cla.data,
|
|
140
|
+
self.__K,
|
|
141
|
+
np.int32(bool(accumulate)),
|
|
142
|
+
self.__occ.A_cla.data,
|
|
143
|
+
)
|
|
144
|
+
self.__occ.mark_device_modified("A")
|
|
145
|
+
return self.__occ.A_cla
|
|
146
|
+
|
|
147
|
+
def update_force(self, pset):
|
|
148
|
+
self.update_force_device(pset, accumulate=False, host_authoritative=True)
|
|
149
|
+
self.__occ.sync_to_host("A", self.__A)
|
|
150
|
+
return self.__A
|
|
151
|
+
|
|
152
|
+
def getA(self):
|
|
153
|
+
return self.__A
|
|
154
|
+
|
|
155
|
+
A = property(getA)
|
|
156
|
+
|
|
157
|
+
def getF(self):
|
|
158
|
+
# Keep the legacy host property useful even after a device-only update.
|
|
159
|
+
self.__occ.sync_to_host("A", self.__A)
|
|
160
|
+
return self.__A * self.__M
|
|
161
|
+
|
|
162
|
+
F = property(getF)
|
|
163
|
+
|
|
164
|
+
def get_ocl_context(self):
|
|
165
|
+
return self.__occ
|
|
166
|
+
|
|
167
|
+
ocl_context = property(get_ocl_context)
|
|
@@ -0,0 +1,105 @@
|
|
|
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
|
+
import scipy.spatial.distance as dist
|
|
19
|
+
|
|
20
|
+
import pyparticles.forces.force as fr
|
|
21
|
+
|
|
22
|
+
class Electromagnetic( fr.Force ) :
|
|
23
|
+
r"""
|
|
24
|
+
Compute the electromagnetic force of a self-interacting particles system according to the Lorenz formulation.
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
Not yet tested.
|
|
29
|
+
"""
|
|
30
|
+
def __init__( self , size , dim=3 , m=None , Consts=1.0 ):
|
|
31
|
+
|
|
32
|
+
self.__dim = dim
|
|
33
|
+
self.__size = size
|
|
34
|
+
|
|
35
|
+
self.__Ke = Consts[0]
|
|
36
|
+
self.__Km = Consts[1]
|
|
37
|
+
|
|
38
|
+
self.__Am = np.zeros( ( size , dim ) )
|
|
39
|
+
self.__Ae = np.zeros( ( size , dim ) )
|
|
40
|
+
|
|
41
|
+
self.__Fe = np.zeros( ( size , size ) )
|
|
42
|
+
self.__Fm = np.zeros( ( size , size ) )
|
|
43
|
+
|
|
44
|
+
self.__V = np.zeros( ( size , size ) )
|
|
45
|
+
self.__D = np.zeros( ( size , size ) )
|
|
46
|
+
self.__Q = np.zeros( ( size , size ) )
|
|
47
|
+
self.__M = np.zeros( ( size , 1 ) )
|
|
48
|
+
|
|
49
|
+
self.__r = np.zeros( ( size , dim ) )
|
|
50
|
+
|
|
51
|
+
self.__Cr = np.zeros( ( size , dim ) )
|
|
52
|
+
|
|
53
|
+
if m != None :
|
|
54
|
+
self.set_masses( m )
|
|
55
|
+
|
|
56
|
+
def set_masses( self , m ):
|
|
57
|
+
self.__M[:] = m
|
|
58
|
+
|
|
59
|
+
def set_charges( self , q ):
|
|
60
|
+
self.__Q[:,:] = q
|
|
61
|
+
self.__Q[:,:] = self.__Q * self.__Q.T
|
|
62
|
+
|
|
63
|
+
def update_force( self , p_set ):
|
|
64
|
+
self.__D[:] = dist.squareform( dist.pdist( p_set.X , 'euclidean' ) )
|
|
65
|
+
|
|
66
|
+
self.__Fe[:] = self.__Ke * self.__Q[:] / ( ( self.__D[:] ) ** 3.0 )
|
|
67
|
+
self.__Fm[:] = self.__Km * self.__Q[:] / ( ( self.__D[:] ) ** 2.0 )
|
|
68
|
+
|
|
69
|
+
np.fill_diagonal( self.__Fe , 0.0 )
|
|
70
|
+
np.fill_diagonal( self.__Fm , 0.0 )
|
|
71
|
+
|
|
72
|
+
for i in range( self.__dim ):
|
|
73
|
+
self.__V[:,:] = p_set.X[:,i]
|
|
74
|
+
self.__V[:,:] = ( self.__V[:,:].T - p_set.X[:,i] ).T
|
|
75
|
+
|
|
76
|
+
self.__Ae[:,i] = np.sum( self.__Fe * self.__V[:,:] , 0 )
|
|
77
|
+
|
|
78
|
+
r = self.__r
|
|
79
|
+
for j in range( self.__size ) :
|
|
80
|
+
r[:] = p_set.X[j,:] - p_set.X[:]
|
|
81
|
+
|
|
82
|
+
r[:] = (r.T / np.sqrt( np.sum(r**2,1))).T
|
|
83
|
+
r[j,:] = 0.0
|
|
84
|
+
|
|
85
|
+
r[:] = np.cross( p_set.V[:] , r[:] )
|
|
86
|
+
r[:] = np.cross( p_set.V[j,:] , r[:] )
|
|
87
|
+
|
|
88
|
+
self.__Am[j,:] = np.sum( self.__Fm[:,j].T * r[:] , 0 ).T / self.__M[j]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
#print( self.__X )
|
|
92
|
+
self.__A[:] = self.__Ae + self.__Am
|
|
93
|
+
|
|
94
|
+
return self.__A
|
|
95
|
+
|
|
96
|
+
def getA(self):
|
|
97
|
+
return self.__A
|
|
98
|
+
|
|
99
|
+
A = property( getA )
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def getF(self):
|
|
103
|
+
return self.__A * self.__M
|
|
104
|
+
|
|
105
|
+
F = property( getF )
|
|
@@ -0,0 +1,95 @@
|
|
|
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 random
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
|
|
21
|
+
import pyparticles.forces.force as fr
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ElectromagneticField(fr.Force):
|
|
25
|
+
r"""
|
|
26
|
+
Electromagnetic force for non-self-interacting charged particles.
|
|
27
|
+
|
|
28
|
+
.. math::
|
|
29
|
+
|
|
30
|
+
\mathbf{F} = q(\mathbf{E} + \mathbf{v} \times \mathbf{B})
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, size, dim=3, m=None, q=None, Consts=1.0):
|
|
34
|
+
self.__dim = dim
|
|
35
|
+
self.__size = size
|
|
36
|
+
|
|
37
|
+
self.__A = np.zeros((size, dim))
|
|
38
|
+
self.__E = np.zeros((size, dim))
|
|
39
|
+
self.__B = np.zeros((size, dim))
|
|
40
|
+
self.__Fe = np.zeros((size, dim))
|
|
41
|
+
self.__Fm = np.zeros((size, dim))
|
|
42
|
+
self.__M = np.zeros((size, 1))
|
|
43
|
+
self.__Q = np.zeros((size, 1))
|
|
44
|
+
|
|
45
|
+
if m is not None:
|
|
46
|
+
self.set_masses(m)
|
|
47
|
+
if q is not None:
|
|
48
|
+
self.set_charges(q)
|
|
49
|
+
|
|
50
|
+
self.__el_fields = {}
|
|
51
|
+
self.__ma_fields = {}
|
|
52
|
+
|
|
53
|
+
def set_masses(self, m):
|
|
54
|
+
self.__M[:] = m
|
|
55
|
+
|
|
56
|
+
def set_charges(self, q):
|
|
57
|
+
self.__Q[:] = q
|
|
58
|
+
|
|
59
|
+
def append_electric_field(self, ef, key=None):
|
|
60
|
+
if key is None:
|
|
61
|
+
key = str(random.randint(0, 2**64))
|
|
62
|
+
self.__el_fields[key] = ef
|
|
63
|
+
return key
|
|
64
|
+
|
|
65
|
+
def append_magnetic_field(self, bf, key=None):
|
|
66
|
+
if key is None:
|
|
67
|
+
key = str(random.randint(0, 2**64))
|
|
68
|
+
self.__ma_fields[key] = bf
|
|
69
|
+
return key
|
|
70
|
+
|
|
71
|
+
def update_force(self, pset):
|
|
72
|
+
self.__Fe[:] = 0.0
|
|
73
|
+
self.__Fm[:] = 0.0
|
|
74
|
+
|
|
75
|
+
for field in self.__el_fields.values():
|
|
76
|
+
field(self.__E, pset.X)
|
|
77
|
+
self.__Fe += self.__Q * self.__E
|
|
78
|
+
|
|
79
|
+
for field in self.__ma_fields.values():
|
|
80
|
+
field(self.__B, pset.X)
|
|
81
|
+
self.__Fm += self.__Q * np.cross(pset.V, self.__B)
|
|
82
|
+
|
|
83
|
+
self.__Fe += self.__Fm
|
|
84
|
+
self.__A[:] = self.__Fe / self.__M
|
|
85
|
+
return self.__A
|
|
86
|
+
|
|
87
|
+
def getA(self):
|
|
88
|
+
return self.__A
|
|
89
|
+
|
|
90
|
+
A = property(getA)
|
|
91
|
+
|
|
92
|
+
def getF(self):
|
|
93
|
+
return self.__Fe
|
|
94
|
+
|
|
95
|
+
F = property(getF)
|
|
@@ -0,0 +1,72 @@
|
|
|
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
|
+
import scipy.spatial.distance as dist
|
|
19
|
+
|
|
20
|
+
import pyparticles.forces.force as fr
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Electrostatic(fr.Force):
|
|
24
|
+
r"""Compute electrostatic force using Coulomb's law."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, size, dim=3, m=None, q=None, Consts=1.0):
|
|
27
|
+
self.__dim = dim
|
|
28
|
+
self.__size = size
|
|
29
|
+
self.__K = Consts
|
|
30
|
+
self.__A = np.zeros((size, dim))
|
|
31
|
+
self.__Fm = np.zeros((size, size))
|
|
32
|
+
self.__V = np.zeros((size, size))
|
|
33
|
+
self.__D = np.zeros((size, size))
|
|
34
|
+
self.__Q = np.zeros((size, size))
|
|
35
|
+
self.__M = np.zeros((size, 1))
|
|
36
|
+
|
|
37
|
+
if m is not None:
|
|
38
|
+
self.set_masses(m)
|
|
39
|
+
if q is not None:
|
|
40
|
+
self.set_charges(q)
|
|
41
|
+
|
|
42
|
+
def set_masses(self, m):
|
|
43
|
+
self.__M[:] = m
|
|
44
|
+
|
|
45
|
+
def set_charges(self, q):
|
|
46
|
+
q = np.asarray(q).reshape(self.__size, 1)
|
|
47
|
+
self.__Q[:] = q * q.T
|
|
48
|
+
|
|
49
|
+
def update_force(self, p_set):
|
|
50
|
+
self.__D[:] = dist.squareform(dist.pdist(p_set.X, "euclidean"))
|
|
51
|
+
|
|
52
|
+
with np.errstate(divide="ignore", invalid="ignore"):
|
|
53
|
+
self.__Fm[:] = self.__K * self.__Q / self.__D**3.0
|
|
54
|
+
np.fill_diagonal(self.__Fm, 0.0)
|
|
55
|
+
|
|
56
|
+
for i in range(self.__dim):
|
|
57
|
+
self.__V[:, :] = p_set.X[:, i]
|
|
58
|
+
self.__V[:, :] = (self.__V.T - p_set.X[:, i]).T
|
|
59
|
+
force_component = np.sum(self.__Fm * self.__V, axis=0)
|
|
60
|
+
self.__A[:, i] = force_component / self.__M[:, 0]
|
|
61
|
+
|
|
62
|
+
return self.__A
|
|
63
|
+
|
|
64
|
+
def getA(self):
|
|
65
|
+
return self.__A
|
|
66
|
+
|
|
67
|
+
A = property(getA)
|
|
68
|
+
|
|
69
|
+
def getF(self):
|
|
70
|
+
return self.__A * self.__M
|
|
71
|
+
|
|
72
|
+
F = property(getF)
|
|
@@ -0,0 +1,75 @@
|
|
|
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 sys
|
|
18
|
+
|
|
19
|
+
class Force(object):
|
|
20
|
+
"""
|
|
21
|
+
The main abstract class used as interface for the forces classes
|
|
22
|
+
|
|
23
|
+
Constructor
|
|
24
|
+
|
|
25
|
+
:param size: the number of particles in the system
|
|
26
|
+
:param dim: the dimension of the system (3D, 2D..)
|
|
27
|
+
:param m: a vector containig the masses
|
|
28
|
+
:param Const: the force constants (Like G, K ...)
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self , size , dim , m=None , Conts=1.0 ):
|
|
32
|
+
NotImplementedError(" %s : is virtual and must be overridden." % sys._getframe().f_code.co_name )
|
|
33
|
+
|
|
34
|
+
def set_masses( self , m ):
|
|
35
|
+
"""
|
|
36
|
+
Set the masses used for computing the forces.
|
|
37
|
+
|
|
38
|
+
:param m: An array containig the masses
|
|
39
|
+
"""
|
|
40
|
+
NotImplementedError(" %s : is virtual and must be overridden." % sys._getframe().f_code.co_name )
|
|
41
|
+
|
|
42
|
+
def update_force( self , p_set ):
|
|
43
|
+
"""
|
|
44
|
+
Computes the forces of the current status ad return the accelerations of the particles
|
|
45
|
+
|
|
46
|
+
:param p_set: Particles set obj.
|
|
47
|
+
"""
|
|
48
|
+
NotImplementedError(" %s : is virtual and must be overridden." % sys._getframe().f_code.co_name )
|
|
49
|
+
|
|
50
|
+
def getA(self):
|
|
51
|
+
"""
|
|
52
|
+
return the array of the acclerations
|
|
53
|
+
"""
|
|
54
|
+
NotImplementedError(" %s : is virtual and must be overridden." % sys._getframe().f_code.co_name )
|
|
55
|
+
|
|
56
|
+
A = property( getA , doc="(property) return the array of the acclerations")
|
|
57
|
+
|
|
58
|
+
def getF(self):
|
|
59
|
+
"""
|
|
60
|
+
returns the array of the forces
|
|
61
|
+
"""
|
|
62
|
+
NotImplementedError(" %s : is virtual and must be overridden." % sys._getframe().f_code.co_name )
|
|
63
|
+
|
|
64
|
+
F = property( getF , doc="(property) returns the array of the forces" )
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_const( self ):
|
|
68
|
+
"""
|
|
69
|
+
returns the force contants
|
|
70
|
+
"""
|
|
71
|
+
NotImplementedError(" %s : is virtual and must be overridden." % sys._getframe().f_code.co_name )
|
|
72
|
+
|
|
73
|
+
const = property( get_const , doc="(property) returns the force contants")
|
|
74
|
+
|
|
75
|
+
|