File Coverage

blib/lib/Math/Volume/Rotational.pm
Criterion Covered Total %
statement 24 82 29.2
branch 0 34 0.0
condition 0 3 0.0
subroutine 8 12 66.6
pod 3 4 75.0
total 35 135 25.9


line stmt bran cond sub pod time code
1             =head1 NAME
2            
3             Math::Volume::Rotational - Volume of rotational bodies
4            
5             =head1 SYNOPSIS
6            
7             use Math::Volume::Rotational qw/volume_x volume_y/;
8            
9             my $volume = volume_rot_x( '(2-x^2)^0.5', -2, 2 );
10            
11             # equivalent:
12             use Math::Symbolic qw/parse_from_string/;
13             my $formula = parse_from_string('(2-x^2)^0.5');
14             $volume = volume_rot_x($formula, -2, 2);
15            
16             # The above calculates the volume of a sphere of radius 2 by rotating
17             # the half-circle of radius 2 around the x-axis.
18             # This is equivalent to the well-known formula "4/3*pi*radius^3".
19            
20             # volume_rot_y works similar by rotating around the y-axis.
21            
22             =head1 DESCRIPTION
23            
24             This module calculates the volume of rotational bodies. These are bodies
25             resulting from the rotation of a portion of a 2D function around an axis.
26            
27             Please note that rotations around an axis other than either x- or y-axis
28             are considered highly experimental at this point.
29            
30             =head2 EXPORT
31            
32             None by default, but you may choose to have any of the following subroutines
33             exported to the calling namespace via standard Exporter semantics:
34            
35             volume_rot_x
36             volume_rot_y
37             volume_rot_arb
38            
39             Additionally, you may use the export tag ':all' to export all of the above.
40            
41             =head1 SUBROUTINES
42            
43             =cut
44            
45             package Math::Volume::Rotational;
46            
47 1     1   748 use 5.006;
  1         3  
  1         33  
48 1     1   4 use strict;
  1         2  
  1         27  
49 1     1   14 use warnings;
  1         2  
  1         32  
50            
51 1     1   4 use Carp;
  1         2  
  1         101  
52            
53 1     1   5 use constant PI => 3.141592653589793238462643;
  1         2  
  1         55  
54 1     1   761 use Math::Integral::Romberg 'integral';
  1         612  
  1         64  
55 1     1   814 use Math::Symbolic qw/parse_from_string/;
  1         165246  
  1         144  
56 1     1   15 use Math::Symbolic::Compiler;
  1         2  
  1         1139  
57            
58             require Exporter;
59            
60             our @ISA = qw(Exporter);
61            
62             our %EXPORT_TAGS = (
63             'all' => [ qw(
64             volume_rot_x
65             volume_rot_y
66             volume_rot_arb
67             ) ],
68             );
69            
70             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
71             our @EXPORT;
72            
73             our $VERSION = '0.11';
74            
75            
76            
77             =head2 volume_rot_x
78            
79             Calculates the volume of a rotational body by rotatinf a the portion of
80             a function graph around the x-axis. The function graph is integrated from
81             a lower to an upper boundary.
82            
83             Expects a Math::Symbolic tree or a string to be parsed as such as first
84             argument. Second argument must be the lower boundary, third must be the
85             upper parameter boundary.
86            
87             =cut
88            
89             sub volume_rot_x {
90 0     0 1   my ($function, $lower, $upper) = @_;
91            
92 0 0         $function = parse_from_string($function)
93             if not ref($function) =~ /^Math::Symbolic/;
94            
95 0           my @sig = $function->signature();
96 0 0         croak "Function has to be a scalar function" if @sig > 1;
97            
98 0           $function = $function ** 2;
99 0           my($compiled) = $function->to_sub();
100            
101 0           return PI * integral($compiled, $lower, $upper);
102             }
103            
104            
105            
106             =head2 volume_rot_y
107            
108             Works the same as volume_rot_x. It calculates the volume of a rotational
109             body by rotating a the portion of
110             a function graph around the y-axis. The function graph is integrated from
111             a lower to an upper boundary.
112            
113             Expects a Math::Symbolic tree or a string to be parsed as such as first
114             argument. Second argument must be the lower boundary, third must be the
115             upper parameter boundary.
116            
117             =cut
118            
119             sub volume_rot_y {
120 0     0 1   my ($function, $lower, $upper) = @_;
121            
122 0 0         $function = Math::Symbolic::parse_from_string($function)
123             if not ref($function) =~ /^Math::Symbolic/;
124            
125 0           my @sig = $function->signature();
126 0 0         croak "Function has to be a scalar function" if @sig > 1;
127 0 0         my $var = (@sig ? $sig[0] : 'x');
128 0           $function = Math::Symbolic::Operator->new(
129             'partial_derivative',
130             $function,
131             Math::Symbolic::Variable->new($var)
132             ) * "$var^2";
133 0           $function = $function->apply_derivatives()->simplify();
134 0           my($compiled) = $function->to_sub();
135            
136 0           return PI * integral($compiled, $lower, $upper);
137             }
138            
139            
140            
141             =head2 volume_rot_arb
142            
143             Calculates the volume of a rotational
144             body by rotating a portion of
145             a function graph around an arbitrary axis in R^2.
146             The function graph is integrated from a lower to an upper boundary.
147             volume_rot_arb takes named arguments:
148            
149             =over 4
150            
151             =item function
152            
153             This is the function to integrate over. It must be a Math::Symbolic tree
154             or a string to be parsed as such. Needs to be a scalar function.
155             This is a mandatory argument.
156            
157             =item var
158            
159             Indicates the name of the variable to use for integration. This is an
160             optional argument if the variable can be inferred from the function.
161            
162             =item lower_boundary_function
163            
164             This optional argument indicates a function to subtract from the integration
165             function before integration. Thus, you can calculate the volume of a hollow
166             sphere of a given thickness.
167            
168             =item axis_y
169            
170             Mandatory argument indicating the y value of the axis to rotate around at
171             x=0.
172            
173             =item axis_slope
174            
175             Indicates the slope of the axis in R^2. Mandatory argument.
176            
177             =item lower
178            
179             The lower boundary for integration. Mandatory argument.
180            
181             =item upper
182            
183             The upper boundary for integration. Mandatory argument.
184            
185             =back
186            
187             =cut
188            
189             sub volume_rot_arb {
190 0     0 1   my %args = @_;
191 0           my $var = $args{var};
192 0           my $f1 = $args{function};
193 0           my $f2 = $args{lower_boundary_function};
194 0           my $y = $args{axis_y};
195 0           my $slope = $args{axis_slope};
196 0           my $lower = $args{lower};
197 0           my $upper = $args{upper};
198            
199 0 0         croak "'function' is a mandatory named argument to volume_rot_arb"
200             if not defined $f1;
201 0 0         croak "'lower' is a mandatory named argument to volume_rot_arb"
202             if not defined $lower;
203 0 0         croak "'upper' is a mandatory named argument to volume_rot_arb"
204             if not defined $upper;
205 0 0         croak "'axis_y' is a mandatory named argument to volume_rot_arb"
206             if not defined $y;
207 0 0         croak "'axis_slope' is a mandatory named argument to volume_rot_arb"
208             if not defined $slope;
209            
210 0 0         $f1 = parse_from_string($f1) if not ref($f1) =~ /^Math::Symbolic/;
211 0 0 0       $f2 = parse_from_string($f2)
212             if defined $f2 and not ref($f2) =~ /^Math::Symbolic/;
213            
214 0 0         if (not defined $var) {
215 0           my @sig = $f1->signature;
216 0 0         if (@sig == 1) {
217 0           $var = $sig[0];
218             }
219             else {
220 0           croak "Could not infer integration variable";
221             }
222             }
223 0           my $func = $f1;
224 0 0         $func -= $f2 if defined $f2;
225 0           my ($code) = $func->to_sub();
226 0           my $area = integral($code, $lower, $upper);
227            
228 0           my $com = calculate_center_of_mass($func, $lower, $upper, $var, $area);
229            
230 0           my $dist = abs($com->[0] * (1-$slope) - $y) / sqrt(1 + $slope**2);
231            
232 0           my $volume = PI*$dist*$area;
233             }
234            
235             sub calculate_center_of_mass {
236 0     0 0   my $f = shift;
237 0           my $l = shift;
238 0           my $u = shift;
239 0           my $v = shift;
240 0           my $integral = shift;
241            
242 0 0         $f = parse_from_string($f) if not ref($f) =~ /^Math::Symbolic/;
243 0           my ($c_f) = $f->to_sub();
244            
245 0           my $xf = $f * $v;
246 0           my ($c_xf) = $xf->to_sub();
247            
248 0           my $fsq = $f ** 2;
249 0           my ($c_fsq) = $fsq->to_sub();
250            
251 0 0         $integral = integral($c_f, $l, $u) if not defined $integral;
252            
253 0           my $s_x = integral($c_xf, $l, $u) / $integral;
254 0           my $s_y = 0.5 * integral($c_fsq, $l, $u) / $integral;
255 0           return [$s_x, $s_y];
256             }
257            
258            
259             1;
260             __END__