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,59 @@
|
|
|
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
|
+
class Mass( object ):
|
|
20
|
+
"""
|
|
21
|
+
'Meaure' for computing the total mass of the particle system
|
|
22
|
+
"""
|
|
23
|
+
def __init__( self , pset=None, force=None ):
|
|
24
|
+
super( Mass , self ).__init__( pset=pset , force=force )
|
|
25
|
+
self.__M = 0.0
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def update_measure( self ):
|
|
29
|
+
"""
|
|
30
|
+
Compute and return the totale mass of the system
|
|
31
|
+
"""
|
|
32
|
+
self.__M = np.sum( self.pset.M[:] )
|
|
33
|
+
return self.__M
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def value(self):
|
|
37
|
+
"""
|
|
38
|
+
Return the current measured total mass
|
|
39
|
+
"""
|
|
40
|
+
return self.__M
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def shape(self ):
|
|
44
|
+
"""
|
|
45
|
+
return a tuple containing the shape of the measures dataset
|
|
46
|
+
"""
|
|
47
|
+
return 1,
|
|
48
|
+
|
|
49
|
+
def dim( self ):
|
|
50
|
+
"""
|
|
51
|
+
return the dimension of the measure: 1 for the mass
|
|
52
|
+
"""
|
|
53
|
+
return 1
|
|
54
|
+
|
|
55
|
+
def name(self):
|
|
56
|
+
"""
|
|
57
|
+
Return the string: "mass"
|
|
58
|
+
"""
|
|
59
|
+
return "mass"
|
|
@@ -0,0 +1,156 @@
|
|
|
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
|
+
class Measure( object ):
|
|
20
|
+
"""
|
|
21
|
+
Main abstract class for defining the measurement procedures of the system, for example the total kinetic energy.
|
|
22
|
+
"""
|
|
23
|
+
def __init__( self , pset=None , force=None ):
|
|
24
|
+
"""
|
|
25
|
+
Constructor
|
|
26
|
+
|
|
27
|
+
:param pset: The particles set
|
|
28
|
+
:param force: The model of the used force
|
|
29
|
+
"""
|
|
30
|
+
self.__pset = pset
|
|
31
|
+
self.__force = force
|
|
32
|
+
self.__par = dict()
|
|
33
|
+
|
|
34
|
+
self.__str_f = "%f"
|
|
35
|
+
|
|
36
|
+
def get_pset( self ):
|
|
37
|
+
return self.__pset
|
|
38
|
+
|
|
39
|
+
def set_pset( self , pset ):
|
|
40
|
+
self.__pset = pset
|
|
41
|
+
|
|
42
|
+
pset = property( get_pset , set_pset , doc="set and get the current measured particle set" )
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_force( self ):
|
|
46
|
+
return self.__force
|
|
47
|
+
|
|
48
|
+
def set_force( self , force ):
|
|
49
|
+
self.__force = force
|
|
50
|
+
|
|
51
|
+
force = property( get_force , set_force , doc="set and get the current force model" )
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_parameter( self , name , val ):
|
|
55
|
+
"""
|
|
56
|
+
return the reference to the dict of the used parameter
|
|
57
|
+
A parameter should be the volume, some constant ....
|
|
58
|
+
"""
|
|
59
|
+
return self.__par
|
|
60
|
+
|
|
61
|
+
parameter = property( get_parameter , doc="return the reference to the dict of the used paramenter" )
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def update_measure( self ):
|
|
65
|
+
"""
|
|
66
|
+
compute and return the value of the measured quantity
|
|
67
|
+
"""
|
|
68
|
+
NotImplementedError(" %s : is virtual and must be overridden." % sys._getframe().f_code.co_name )
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def value( self ):
|
|
72
|
+
"""
|
|
73
|
+
Return the value of the current measure
|
|
74
|
+
"""
|
|
75
|
+
NotImplementedError(" %s : is virtual and must be overridden." % sys._getframe().f_code.co_name )
|
|
76
|
+
|
|
77
|
+
def set_str_format( self , f="%5.3f" ):
|
|
78
|
+
self.__str_f = f
|
|
79
|
+
|
|
80
|
+
def get_str_format( self ):
|
|
81
|
+
return self.__str_f
|
|
82
|
+
|
|
83
|
+
str_format = property( get_str_format , set_str_format , doc="get ad set the string format for representing the value" )
|
|
84
|
+
|
|
85
|
+
def value_str( self ):
|
|
86
|
+
"""
|
|
87
|
+
return a string containig the value of the current neasure formmatted according to the format defined with the str_property format. By default if uses the simple floaf format
|
|
88
|
+
"""
|
|
89
|
+
return self.str_format % ( self.value() )
|
|
90
|
+
|
|
91
|
+
def shape(self ):
|
|
92
|
+
"""
|
|
93
|
+
return a tuple containing the shape of the measures dataset
|
|
94
|
+
"""
|
|
95
|
+
NotImplementedError(" %s : is virtual and must be overridden." % sys._getframe().f_code.co_name )
|
|
96
|
+
|
|
97
|
+
def dim( self ):
|
|
98
|
+
"""
|
|
99
|
+
return the dimension of the measure, for the dimensionless measure it must return 1 (like kinetic energy or mass)
|
|
100
|
+
"""
|
|
101
|
+
NotImplementedError(" %s : is virtual and must be overridden." % sys._getframe().f_code.co_name )
|
|
102
|
+
|
|
103
|
+
def name(self):
|
|
104
|
+
"""
|
|
105
|
+
return a string containig the name of the measure
|
|
106
|
+
"""
|
|
107
|
+
NotImplementedError(" %s : is virtual and must be overridden." % sys._getframe().f_code.co_name )
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class MeasureParticles( Measure ):
|
|
114
|
+
"""
|
|
115
|
+
Abstract class used fopr measuring a subset of partiles or a set of singles particles.
|
|
116
|
+
"""
|
|
117
|
+
def __init__( self , pset=None , force=None , subset=None , model="part_by_part" ):
|
|
118
|
+
"""
|
|
119
|
+
Constructor:
|
|
120
|
+
|
|
121
|
+
:param pset: The particles set
|
|
122
|
+
:param force: The model of the used force
|
|
123
|
+
:param subset: a numpy 1D array containing the indicies of the measured particles
|
|
124
|
+
:param model: a strung describing the model for the measure: "part_by_part" or "subsystem"
|
|
125
|
+
|
|
126
|
+
"""
|
|
127
|
+
self.__subset = np.copy(subset)
|
|
128
|
+
|
|
129
|
+
self.__model = None
|
|
130
|
+
|
|
131
|
+
self.model = model
|
|
132
|
+
|
|
133
|
+
super( MeasureParticles , self ).__init__( pset=pset , force=force )
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def set_subset( self , subset ):
|
|
137
|
+
self.__subset = np.copy(subset)
|
|
138
|
+
|
|
139
|
+
def get_subset( self ):
|
|
140
|
+
return self.__subset
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
subset = property( get_subset , set_subset , doc="get and set the subset of particles to be measured" )
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def get_model ( self ):
|
|
147
|
+
return self.__model
|
|
148
|
+
|
|
149
|
+
def set_model ( self , model ):
|
|
150
|
+
if model not in [ "part_by_part" , "subsystem" ] :
|
|
151
|
+
ValueError
|
|
152
|
+
|
|
153
|
+
self.__model = model
|
|
154
|
+
|
|
155
|
+
model = property( get_model , set_model , doc="set and get the measurement model: \"part_by_part\" or \"subsystem\" ")
|
|
156
|
+
|
|
@@ -0,0 +1,144 @@
|
|
|
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.measures.measure as me
|
|
21
|
+
|
|
22
|
+
class MomentumSystem( me.Measure ):
|
|
23
|
+
"""
|
|
24
|
+
'Measure' for computing the total momentum of the particle system
|
|
25
|
+
"""
|
|
26
|
+
def __init__( self , pset=None ):
|
|
27
|
+
|
|
28
|
+
if pset != None :
|
|
29
|
+
self.__P = np.zeros(( pset.dim ))
|
|
30
|
+
else :
|
|
31
|
+
self.__P = None
|
|
32
|
+
|
|
33
|
+
super( MomentumSystem , self ).__init__( pset=pset , force=None )
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def value(self):
|
|
37
|
+
"""
|
|
38
|
+
return the current value of the total momentum
|
|
39
|
+
"""
|
|
40
|
+
return self.__P
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def update_measure( self ):
|
|
44
|
+
"""
|
|
45
|
+
Compute and return the total momentum of the system
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
self.__P = np.sum( self.pset.V * self.pset.M , 0 )
|
|
49
|
+
|
|
50
|
+
return self.__P
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def shape( self ):
|
|
54
|
+
"""
|
|
55
|
+
return a tuple containing the shape of the measures dataset
|
|
56
|
+
"""
|
|
57
|
+
return ( self.pset.dim )
|
|
58
|
+
|
|
59
|
+
def dim( self ):
|
|
60
|
+
"""
|
|
61
|
+
return the dimension of the measure: Dim for the momentum
|
|
62
|
+
"""
|
|
63
|
+
return self.pset.dim
|
|
64
|
+
|
|
65
|
+
def name(self):
|
|
66
|
+
"""
|
|
67
|
+
Return the string: "momentum"
|
|
68
|
+
"""
|
|
69
|
+
return "momentum"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class MomentumParticles( me.MeasureParticles ):
|
|
74
|
+
"""
|
|
75
|
+
'Measure' for computing the momentum particle by particle or of a subsystem
|
|
76
|
+
Example: ::
|
|
77
|
+
|
|
78
|
+
P = MomentumParticles( pset , subset=np.array([1,4,5]) , model="part_by_part")
|
|
79
|
+
P.update_measure()
|
|
80
|
+
print( P.value )
|
|
81
|
+
> [[ 1.1 , 2.3 , 3.2 ],
|
|
82
|
+
> [ 1.7 , 5.2 , 6.9 ],
|
|
83
|
+
> [ 1.8 , 2.3 , 1.7 ]
|
|
84
|
+
> ]
|
|
85
|
+
|
|
86
|
+
Constructor:
|
|
87
|
+
|
|
88
|
+
:param pset The particles set
|
|
89
|
+
:param force The model of the used force
|
|
90
|
+
:param subset a numpy 1D array containing the indicies of the measured particles
|
|
91
|
+
:param model a strung describing the model for the measure: "part_by_part" or "subsystem"
|
|
92
|
+
"""
|
|
93
|
+
def __init__( self , pset=None , force=None , subset=None , model="part_by_part" ):
|
|
94
|
+
|
|
95
|
+
if pset != None and model == "subsystem" :
|
|
96
|
+
self.__P = np.zeros(( pset.dim ))
|
|
97
|
+
elif pset != None and model == "part_by_part" and subset != None :
|
|
98
|
+
self.__P = np.zeros(( len(subset) , pset.dim ))
|
|
99
|
+
else :
|
|
100
|
+
self.__P = None
|
|
101
|
+
|
|
102
|
+
super( MomentumParticles , self ).__init__( pset , force , subset , model )
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def value(self):
|
|
106
|
+
"""
|
|
107
|
+
return the current value of the momentum
|
|
108
|
+
"""
|
|
109
|
+
return self.__P
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def update_measure( self ):
|
|
113
|
+
"""
|
|
114
|
+
Compute and return the total momentum of the specified particles
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
if self.model == "part_by_part" :
|
|
118
|
+
self.__P = self.pset.V[self.subset] * self.pset.M[self.subset]
|
|
119
|
+
else :
|
|
120
|
+
self.__P = np.sum( self.pset.V[self.subset] * self.pset.M[self.subset] , 0 )
|
|
121
|
+
|
|
122
|
+
return self.__P
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def shape( self ):
|
|
126
|
+
"""
|
|
127
|
+
return a tuple containing the shape of the measures dataset
|
|
128
|
+
"""
|
|
129
|
+
if self.model == "part_by_part" :
|
|
130
|
+
return ( len( self.subset ) , pset.dim )
|
|
131
|
+
else :
|
|
132
|
+
return ( 1 , pset.dim )
|
|
133
|
+
|
|
134
|
+
def dim( self ):
|
|
135
|
+
"""
|
|
136
|
+
return the dimension of the measure: Dim: (2D or 3D) for the momentum
|
|
137
|
+
"""
|
|
138
|
+
return self.pset.dim
|
|
139
|
+
|
|
140
|
+
def name(self):
|
|
141
|
+
"""
|
|
142
|
+
Return the string: "momentum"
|
|
143
|
+
"""
|
|
144
|
+
return "momentum"
|
|
@@ -0,0 +1,68 @@
|
|
|
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.measures.measure as me
|
|
21
|
+
|
|
22
|
+
class TotalEnergy( me.Measure ):
|
|
23
|
+
"""
|
|
24
|
+
Class derived from Measure used for computing the total energy of the particle system
|
|
25
|
+
given a potential and a kinetic energy it simply sum the two value
|
|
26
|
+
"""
|
|
27
|
+
def __init__( self , kinetic , potential ):
|
|
28
|
+
|
|
29
|
+
self.__kinetic = kinetic
|
|
30
|
+
self.__potential = potential
|
|
31
|
+
|
|
32
|
+
self.__tot = 0.0
|
|
33
|
+
|
|
34
|
+
super( TotalEnergy , self ).__init__( pset=None , force=None )
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def value(self):
|
|
38
|
+
"""
|
|
39
|
+
return the current value of the potential energy
|
|
40
|
+
"""
|
|
41
|
+
self.__tot = self.__kinetic.value() + self.__potential.value()
|
|
42
|
+
return self.__tot
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def update_measure( self ):
|
|
46
|
+
"""
|
|
47
|
+
Compute and return the total energy on the current state of pset
|
|
48
|
+
"""
|
|
49
|
+
return self.__tot
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def shape( self ):
|
|
53
|
+
"""
|
|
54
|
+
return a tuple containing the shape of the measures dataset
|
|
55
|
+
"""
|
|
56
|
+
return 1,
|
|
57
|
+
|
|
58
|
+
def dim( self ):
|
|
59
|
+
"""
|
|
60
|
+
return the dimension of the measure: 1 for the energy
|
|
61
|
+
"""
|
|
62
|
+
return 1
|
|
63
|
+
|
|
64
|
+
def name(self):
|
|
65
|
+
"""
|
|
66
|
+
Return the string: "total energy"
|
|
67
|
+
"""
|
|
68
|
+
return "total energy"
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
|
|
18
|
+
import os
|
|
19
|
+
import glob
|
|
20
|
+
|
|
21
|
+
__all__ = [ os.path.splitext( os.path.basename(f) )[0] for f in glob.glob(os.path.dirname(os.path.abspath(__file__))+"/*.py")]
|
|
@@ -0,0 +1,214 @@
|
|
|
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.ode.ode_solver as os
|
|
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 EulerSolver(os.OdeSolver):
|
|
21
|
+
def __init__(self, force, p_set, dt):
|
|
22
|
+
super(EulerSolver, self).__init__(force, p_set, dt)
|
|
23
|
+
|
|
24
|
+
def __step__(self, dt):
|
|
25
|
+
self.force.update_force(self.pset)
|
|
26
|
+
self.pset.V[:] = self.pset.V + self.force.A * dt
|
|
27
|
+
self.pset.X[:] = self.pset.X + self.pset.V * dt
|
|
28
|
+
self.pset.update_boundary()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class EulerSolverOCL(os.OdeSolver):
|
|
32
|
+
"""Euler integrator with persistent OpenCL X/V/A buffers.
|
|
33
|
+
|
|
34
|
+
``sync_velocity`` defaults to True to preserve the historical API where
|
|
35
|
+
``pset.V`` is current immediately after every step. Rendering-only demos
|
|
36
|
+
may disable it. Boundaries implementing ``needs_update`` then trigger a V
|
|
37
|
+
download only on frames where a particle actually crosses the domain.
|
|
38
|
+
|
|
39
|
+
``sync_positions`` defaults to True because the legacy renderer consumes
|
|
40
|
+
host ``pset.X`` every frame. Compute-only users, or a renderer backed by an
|
|
41
|
+
OpenCL/OpenGL shared buffer, may disable it so X never crosses PCIe during
|
|
42
|
+
the integration hot path. A host-side boundary still forces X current.
|
|
43
|
+
|
|
44
|
+
Device forces may optionally expose ``euler_step_device``. Fused calls
|
|
45
|
+
receive the current simulation time and step count so device-side boundary
|
|
46
|
+
or source models can evolve without synchronizing through the host.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
force,
|
|
52
|
+
p_set,
|
|
53
|
+
dt,
|
|
54
|
+
ocl_context=None,
|
|
55
|
+
sync_velocity=True,
|
|
56
|
+
sync_positions=True,
|
|
57
|
+
):
|
|
58
|
+
if cl is None:
|
|
59
|
+
raise RuntimeError("PyOpenCL is required for EulerSolverOCL")
|
|
60
|
+
if p_set.dim != 3:
|
|
61
|
+
raise ValueError("EulerSolverOCL currently supports only 3 dimensions")
|
|
62
|
+
|
|
63
|
+
super(EulerSolverOCL, self).__init__(force, p_set, dt)
|
|
64
|
+
|
|
65
|
+
if ocl_context is None:
|
|
66
|
+
self.__occ = occ.OpenCLcontext(
|
|
67
|
+
self.pset.size,
|
|
68
|
+
self.pset.dim,
|
|
69
|
+
occ.OCLC_X | occ.OCLC_V | occ.OCLC_A,
|
|
70
|
+
)
|
|
71
|
+
else:
|
|
72
|
+
self.__occ = ocl_context
|
|
73
|
+
|
|
74
|
+
self.__sync_velocity = bool(sync_velocity)
|
|
75
|
+
self.__sync_positions = bool(sync_positions)
|
|
76
|
+
self.__init_prog_cl()
|
|
77
|
+
|
|
78
|
+
def __init_prog_cl(self):
|
|
79
|
+
source = r"""
|
|
80
|
+
__kernel void euler(
|
|
81
|
+
__global float *V,
|
|
82
|
+
__global const float *A,
|
|
83
|
+
__global float *X,
|
|
84
|
+
float dt)
|
|
85
|
+
{
|
|
86
|
+
int i = get_global_id(0);
|
|
87
|
+
int i0 = 3*i;
|
|
88
|
+
int i1 = i0 + 1;
|
|
89
|
+
int i2 = i0 + 2;
|
|
90
|
+
|
|
91
|
+
V[i0] = V[i0] + A[i0]*dt;
|
|
92
|
+
V[i1] = V[i1] + A[i1]*dt;
|
|
93
|
+
V[i2] = V[i2] + A[i2]*dt;
|
|
94
|
+
|
|
95
|
+
X[i0] = X[i0] + V[i0]*dt;
|
|
96
|
+
X[i1] = X[i1] + V[i1]*dt;
|
|
97
|
+
X[i2] = X[i2] + V[i2]*dt;
|
|
98
|
+
}
|
|
99
|
+
"""
|
|
100
|
+
self.__cl_program = cl.Program(self.__occ.CL_context, source).build()
|
|
101
|
+
self.__euler_kernel = cl.Kernel(self.__cl_program, "euler")
|
|
102
|
+
|
|
103
|
+
def _has_device_force(self):
|
|
104
|
+
return (
|
|
105
|
+
hasattr(self.force, "update_force_device")
|
|
106
|
+
and getattr(self.force, "ocl_context", None) is self.__occ
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def _has_fused_euler_force(self):
|
|
110
|
+
return (
|
|
111
|
+
hasattr(self.force, "euler_step_device")
|
|
112
|
+
and getattr(self.force, "ocl_context", None) is self.__occ
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def __step__(self, dt):
|
|
116
|
+
if self._has_fused_euler_force():
|
|
117
|
+
self.__occ.sync_to_device("X", self.pset.X)
|
|
118
|
+
self.__occ.sync_to_device("V", self.pset.V)
|
|
119
|
+
self.force.euler_step_device(
|
|
120
|
+
self.pset,
|
|
121
|
+
dt,
|
|
122
|
+
sim_time=self.time,
|
|
123
|
+
step=self.steps_cnt,
|
|
124
|
+
)
|
|
125
|
+
else:
|
|
126
|
+
if self._has_device_force():
|
|
127
|
+
self.force.update_force_device(self.pset)
|
|
128
|
+
else:
|
|
129
|
+
self.__occ.sync_to_host("X", self.pset.X)
|
|
130
|
+
self.__occ.sync_to_host("V", self.pset.V)
|
|
131
|
+
self.force.update_force(self.pset)
|
|
132
|
+
self.__occ.set_from_host("A", self.force.A)
|
|
133
|
+
|
|
134
|
+
self.__occ.sync_to_device("X", self.pset.X)
|
|
135
|
+
self.__occ.sync_to_device("V", self.pset.V)
|
|
136
|
+
|
|
137
|
+
self.__euler_kernel(
|
|
138
|
+
self.__occ.CL_queue,
|
|
139
|
+
(self.pset.size,),
|
|
140
|
+
None,
|
|
141
|
+
self.__occ.V_cla.data,
|
|
142
|
+
self.__occ.A_cla.data,
|
|
143
|
+
self.__occ.X_cla.data,
|
|
144
|
+
np.float32(dt),
|
|
145
|
+
)
|
|
146
|
+
self.__occ.mark_device_modified("X")
|
|
147
|
+
self.__occ.mark_device_modified("V")
|
|
148
|
+
|
|
149
|
+
boundary = self.pset.boundary
|
|
150
|
+
need_host_positions = (
|
|
151
|
+
self.__sync_positions
|
|
152
|
+
or self.pset.log_X_enabled
|
|
153
|
+
or boundary is not None
|
|
154
|
+
)
|
|
155
|
+
if need_host_positions:
|
|
156
|
+
self.__occ.sync_to_host("X", self.pset.X)
|
|
157
|
+
|
|
158
|
+
boundary_active = False
|
|
159
|
+
if boundary is not None:
|
|
160
|
+
needs_update = getattr(boundary, "needs_update", None)
|
|
161
|
+
if needs_update is None:
|
|
162
|
+
boundary_active = True
|
|
163
|
+
else:
|
|
164
|
+
boundary_active = bool(needs_update(self.pset))
|
|
165
|
+
|
|
166
|
+
need_host_velocity = (
|
|
167
|
+
self.__sync_velocity
|
|
168
|
+
or self.pset.log_V_enabled
|
|
169
|
+
or boundary_active
|
|
170
|
+
)
|
|
171
|
+
if need_host_velocity:
|
|
172
|
+
self.__occ.sync_to_host("V", self.pset.V)
|
|
173
|
+
|
|
174
|
+
if boundary is not None and boundary_active:
|
|
175
|
+
changed = boundary.boundary(self.pset)
|
|
176
|
+
changed = True if changed is None else bool(changed)
|
|
177
|
+
if changed:
|
|
178
|
+
self.__occ.mark_host_modified("X")
|
|
179
|
+
self.__occ.mark_host_modified("V")
|
|
180
|
+
|
|
181
|
+
def sync_to_host(self, velocity=True):
|
|
182
|
+
"""Explicitly synchronize resident state for external host consumers."""
|
|
183
|
+
self.__occ.sync_to_host("X", self.pset.X)
|
|
184
|
+
if velocity:
|
|
185
|
+
self.__occ.sync_to_host("V", self.pset.V)
|
|
186
|
+
return self.pset
|
|
187
|
+
|
|
188
|
+
def notify_host_modified(self, positions=True, velocities=True):
|
|
189
|
+
"""Tell the solver that external code changed host X and/or V."""
|
|
190
|
+
if positions:
|
|
191
|
+
self.__occ.mark_host_modified("X")
|
|
192
|
+
if velocities:
|
|
193
|
+
self.__occ.mark_host_modified("V")
|
|
194
|
+
|
|
195
|
+
def get_ocl_context(self):
|
|
196
|
+
return self.__occ
|
|
197
|
+
|
|
198
|
+
ocl_context = property(get_ocl_context)
|
|
199
|
+
|
|
200
|
+
def get_sync_velocity(self):
|
|
201
|
+
return self.__sync_velocity
|
|
202
|
+
|
|
203
|
+
def set_sync_velocity(self, value):
|
|
204
|
+
self.__sync_velocity = bool(value)
|
|
205
|
+
|
|
206
|
+
sync_velocity = property(get_sync_velocity, set_sync_velocity)
|
|
207
|
+
|
|
208
|
+
def get_sync_positions(self):
|
|
209
|
+
return self.__sync_positions
|
|
210
|
+
|
|
211
|
+
def set_sync_positions(self, value):
|
|
212
|
+
self.__sync_positions = bool(value)
|
|
213
|
+
|
|
214
|
+
sync_positions = property(get_sync_positions, set_sync_positions)
|