File Coverage

blib/lib/Physics/Particles.pm
Criterion Covered Total %
statement 99 113 87.6
branch 6 14 42.8
condition 3 9 33.3
subroutine 14 17 82.3
pod 8 8 100.0
total 130 161 80.7


line stmt bran cond sub pod time code
1              
2             # See the POD documentation at the end of this
3             # document for detailed copyright information.
4             # (c) 2002-2003 Steffen Mueller, all rights reserved.
5              
6             package Physics::Particles;
7              
8 2     2   42079 use 5.006;
  2         9  
  2         108  
9 2     2   13 use strict;
  2         5  
  2         77  
10 2     2   12 use warnings;
  2         4  
  2         105  
11              
12 2     2   12 use constant C_VACUUM => 299792458;
  2         3  
  2         172  
13 2     2   17 use constant C_VACUUM_SQUARED => 299792458 * 299792458;
  2         5  
  2         84  
14              
15 2     2   11 use Carp;
  2         18  
  2         183  
16              
17 2     2   2429 use Data::Dumper;
  2         21890  
  2         193  
18              
19 2     2   21 use vars qw/$VERSION/;
  2         4  
  2         2448  
20             $VERSION = '1.02';
21              
22              
23             # constructor new
24             #
25             # Does not require any arguments. All arguments
26             # directly modify the object as key/value pairs.
27             # returns freshly created simulator object.
28              
29             sub new {
30 1     1 1 414 my $proto = shift;
31 1   33     9 my $class = ref $proto || $proto;
32              
33             # Make a new object with default values.
34 1         13 my $self = {
35             forces => [], # All forces (callbacks) will be stored here.
36             p => [], # All particles (hashrefs) will be stored here.
37             p_attr => {
38             x => 0,
39             y => 0,
40             z => 0,
41             vx => 0,
42             vy => 0,
43             vz => 0,
44             m => 1,
45             n => '',
46             },
47             @_
48             };
49              
50 1         5 bless $self => $class;
51             }
52              
53              
54             # method set_particle_default
55             #
56             # Takes a hash reference whichis then used as is for
57             # particle default attributes such as position, velocity, mass,
58             # unique ID, or whatever properties you fancy.
59              
60             sub set_particle_default {
61 0     0 1 0 my $self = shift;
62 0         0 my $hashref = shift;
63            
64 0 0       0 ref $hashref eq 'HASH'
65             or croak "Must pass hash reference to set_particle_default().";
66            
67 0         0 $self->{p_attr} = $hashref;
68 0         0 return 1;
69             }
70              
71             # method add_particle
72             #
73             # Takes key/value pairs of particle attributes.
74             # Attributes default to whatever has been set using
75             # set_particle_default.
76             # A new particle represented by the attributes is then
77             # created in the simulation and its particle number is
78             # returned. Attributes starting with an underscore are
79             # reserved to internal attributes (so don't use any of them).
80              
81             sub add_particle {
82 6     6 1 295 my $self = shift;
83            
84 6         69 my $particle = {
85 6         6 %{$self->{p_attr}},
86             @_,
87             _fx => 0,
88             _fy => 0,
89             _fz => 0,
90             };
91            
92 6         19 my $particle_no = $self->_make_particle();
93            
94 6         8 $self->{p}[$particle_no] = $particle;
95            
96 6         9 return $particle_no;
97             }
98              
99              
100             # private method _make_particle
101             #
102             # Returns a currently unused particle number or
103             # appends an empty particle to the particle list.
104              
105             sub _make_particle {
106 6     6   7 my $self = shift;
107 6         5 my $count = 0;
108 6         6 foreach (@{$self->{p}}) {
  6         12  
109 15 50       24 return $count unless ref $_;
110 15         18 $count++;
111             }
112            
113 6         7 push @{ $self->{p} }, undef;
  6         17  
114 6         12 return $count;
115             }
116              
117              
118             # method clear_particles
119             #
120             # Removes all particles from the simulation.
121              
122             sub clear_particles {
123 0     0 1 0 my $self = shift;
124 0         0 $self->{p} = [];
125 0         0 return 1;
126             }
127              
128              
129             # method remove_particle
130             #
131             # Removes a specific particle from the simulation.
132             # Takes the particle number as argument.
133             # Returns 1 on success, 0 otherwise.
134              
135             sub remove_particle {
136 0     0 1 0 my $self = shift;
137 0         0 my $particle_no = shift;
138            
139 0 0 0     0 return 0 if $particle_no < 0 || $particle_no > $#{$self->{p}};
  0         0  
140            
141 0         0 $self->{p}[$particle_no] = undef;
142            
143 0         0 return 1;
144             }
145              
146              
147             # method add_force
148             #
149             # Adds a new force to the simulation.
150             # Takes a subroutine reference (the force) as argument,
151             # appends it to the list of forces and returns the force it
152             # just appended. Second argument is optional and defaults to false.
153             # It is a boolean indicating whether or not the force is symmetric.
154             # Symmetric means that the force particle1 excerts on particle2
155             # is exactly zero minus the force particle2 excerts on particle1.
156              
157             sub add_force {
158 1     1 1 227 my $self = shift;
159 1         3 my $force = shift;
160 1         1 my $symmetric = shift;
161 1 50       4 $symmetric = 0 unless defined $symmetric;
162            
163 1         2 push @{$self->{forces}}, [$force, $symmetric];
  1         13  
164 1         3 return $force;
165             }
166              
167              
168             # method iterate_step
169             #
170             # Applies all forces (excerted by every particle) to all particles.
171             # That means this is of complexity no_particles*no_particles*forces.
172             # Takes a list of additional parameters as argument that will be
173             # passed to every force subroutine.
174              
175             sub iterate_step {
176 10     10 1 482 my $self = shift;
177 10         17 my @params = @_;
178              
179 10         12 my $forces = [];
180 10         13 foreach (@{ $self->{p} }) {
  10         17  
181 60         128 push @$forces, [$_->{_fx}, $_->{_fy}, $_->{_fz}];
182 60         126 $_->{_fx} = $_->{_fy} = $_->{_fz} = 0;
183             }
184              
185 10         15 my $new_state = [];
186              
187 10         11 my $p_no = 0;
188 10         11 foreach my $p (@{ $self->{p} }) {
  10         31  
189 60         430 my $new_p = { %$p };
190 60         119 push @$new_state, $new_p;
191 60         72 my $f = $forces->[$p_no];
192 60         64 foreach my $force_def (@{$self->{forces}}) {
  60         103  
193 60         69 my $symm = $force_def->[1];
194 60         61 my $force = $force_def->[0];
195 60         52 my $exc_no = 0;
196 60         58 foreach my $excerter (@{$self->{p}}) {
  60         81  
197 210 100 66     836 last if $symm and $exc_no >= $p_no;
198 150 50       269 $exc_no++, next if $exc_no == $p_no;
199 150         311 my @this_f = $force->(
200             $p,
201             $excerter,
202             \@params
203             );
204 150         1886 $f->[0] += $this_f[0];
205 150         180 $f->[1] += $this_f[1];
206 150         163 $f->[2] += $this_f[2];
207 150 50       249 if ($symm) {
208 150         188 $forces->[$exc_no][0] -= $this_f[0];
209 150         152 $forces->[$exc_no][1] -= $this_f[1];
210 150         176 $forces->[$exc_no][2] -= $this_f[2];
211             }
212 150         203 $exc_no++;
213             }
214             }
215 60         101 $p_no++;
216             }
217              
218 10         14 $p_no = 0;
219 10         14 foreach my $new_p (@$new_state) {
220 60         75 my $f = $forces->[$p_no++];
221             # accel_i = force_i / mass (i-th component)
222 60         164 my $m = $new_p->{m} /
223             sqrt(1 -
224             ($new_p->{vx}**2 +
225             $new_p->{vy}**2 +
226             $new_p->{vz}**2) /
227             C_VACUUM_SQUARED
228             );
229 60         257 my @a = ($f->[0] / $m, $f->[1] / $m, $f->[2] / $m);
230              
231 60         218 @a = map $_ * $params[0], @a;
232            
233 60         127 $new_p->{x} += $new_p->{vx} * $params[0] +
234             0.5 * $a[0] * $params[0];
235 60         103 $new_p->{y} += $new_p->{vy} * $params[0] +
236             0.5 * $a[1] * $params[0];
237 60         98 $new_p->{z} += $new_p->{vz} * $params[0] +
238             0.5 * $a[2] * $params[0];
239              
240 60         66 $new_p->{vx} += $a[0];
241 60         59 $new_p->{vy} += $a[1];
242 60         108 $new_p->{vz} += $a[2];
243             }
244            
245 10         19 $self->{p} = $new_state;
246              
247 10         105 return 1;
248             }
249              
250              
251             # method dump_state
252             #
253             # Returns a Data::Dumper dump of the state of all particles.
254              
255             sub dump_state {
256 1     1 1 353 my $self = shift;
257 1         8 return Dumper($self->{p});
258             }
259              
260             1;
261              
262              
263             __END__