rb-daspk 0.0.2-x86-darwin-10

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 (4) hide show
  1. data/lib/libdaspk.dylib +0 -0
  2. data/lib/rb-daspk.rb +294 -0
  3. data/lib/test.rb +131 -0
  4. metadata +64 -0
Binary file
data/lib/rb-daspk.rb ADDED
@@ -0,0 +1,294 @@
1
+ # Copyright (c) 2009 Eric Todd Meyers
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person
4
+ # obtaining a copy of this software and associated documentation
5
+ # files (the "Software"), to deal in the Software without
6
+ # restriction, including without limitation the rights to use,
7
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the
9
+ # Software is furnished to do so, subject to the following
10
+ # conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ # OTHER DEALINGS IN THE SOFTWARE.
23
+ #
24
+ #
25
+ # More information, source code and gems @ http://rubyforge.org/projects/rb-daspk/
26
+ #
27
+ #
28
+
29
+ require 'rubygems'
30
+ require 'ffi'
31
+
32
+ module FFI
33
+ class Pointer
34
+ def read_double
35
+ get_float64(0)
36
+ end
37
+
38
+ def write_double(obj)
39
+ put_float64(0, obj)
40
+ end
41
+
42
+ def read_array_of_double(length)
43
+ get_array_of_double(0, length)
44
+ end
45
+
46
+ def write_array_of_double(ary)
47
+ put_array_of_double(0, ary)
48
+ end
49
+ end
50
+ end
51
+
52
+
53
+ module DASPK
54
+ extend FFI::Library
55
+ ffi_lib File.dirname(__FILE__) + '/libdaspk.dylib'
56
+ callback :jac, [:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer], :void
57
+ callback :res, [:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer], :void
58
+ attach_function 'ddaspk_', [:res, :pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,
59
+ :pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:jac, :pointer], :void
60
+ #
61
+ #
62
+ # interface to DASPK
63
+ #
64
+ # typical usage
65
+ #
66
+ # 1. create an instance of the Solver class. pass in the initial states/derivatives and the residual function as a block
67
+ # 2. if the initial conditions are not consistent call solveForInitialValues
68
+ # 3. call solve with the start and end times desired
69
+ # 4. repeated calls to solve will solve to each end time. the start time is ingored. this allows
70
+ # for continued integration
71
+ #
72
+ class Solver
73
+
74
+ include DASPK
75
+
76
+ attr_accessor :rtol,:atol,:init,:jac,:maxstep
77
+ #
78
+ #
79
+ # * neq = the number of equations. this is always the sum of the number of states and algebraics
80
+ # * y = array containing the initial states and algebraics
81
+ # * yprime = array containing the initial state derivatives
82
+ # * inter = Proc instance. defaults to nil. if non-nil used as a callback to report the integration results at every time step
83
+ # * jac = Proc instance. defauls to nil. if non-nil used to get the modified Jacbian matrix. otherwise DASPK will determine by iteration
84
+ # * res = residual (delta) Proc. must be present. this is responsible for filling of the delta (residual) array.
85
+ #
86
+ def initialize (neq, y, yprime, inter=nil, jac=nil, &res)
87
+ @lwr = 15000
88
+ @lwi = 15000
89
+ @plwr = FFI::MemoryPointer.new(:int)
90
+ @plwr.write_int(@lwr)
91
+ @plwi = FFI::MemoryPointer.new(:int)
92
+ @plwi.write_int(@lwi)
93
+ @iwork = [0]*@lwi
94
+ @work = [0]*@lwr
95
+ @pwork = FFI::MemoryPointer.new(:double,@lwr)
96
+ @piwork = FFI::MemoryPointer.new(:int,@lwi)
97
+ @info = [0]*30
98
+ @pinfo = FFI::MemoryPointer.new(:int,30)
99
+ @ialgs = [1]*neq
100
+ @res = res
101
+ @inter = inter
102
+ @rtol = 0.000001
103
+ @atol = 0.000001
104
+ @jac = jac
105
+ @maxstep =0
106
+ @init = 0
107
+
108
+ if (inter) then
109
+ @info[2]=1
110
+ end
111
+
112
+ @neq = neq
113
+ @pneq = FFI::MemoryPointer.new(:int)
114
+ @pneq.write_int(neq)
115
+ @y=y
116
+ @yprime=yprime
117
+
118
+ @py = FFI::MemoryPointer.new(:double,neq)
119
+ @py.write_array_of_double(y)
120
+ @pyprime = FFI::MemoryPointer.new(:double,neq)
121
+ @pyprime.write_array_of_double(yprime)
122
+
123
+ @prtol = FFI::MemoryPointer.new(:double)
124
+ @patol = FFI::MemoryPointer.new(:double)
125
+
126
+ #
127
+ # residual proc called from the Fortran. Let's pass it onto the user's residual procedure
128
+ # in a easier to use form
129
+ #
130
+ @resproc = Proc.new do |p1,p2,p3,p4,p5,p6,p7,p8|
131
+ delta = p5.read_array_of_double(@neq)
132
+ @res.call(p1.read_double,p2.read_array_of_double(@neq),p3.read_array_of_double(@neq),delta)
133
+ p5.write_array_of_double(delta)
134
+ end
135
+
136
+ #
137
+ # jacobian proc called from the Fortran. Let's pass it onto the user's jacboian procedure
138
+ # in a easier to use form
139
+ #
140
+ @jacproc = Proc.new do |p1,p2,p3,p4,p5,p6,p7|
141
+ all = p4.read_array_of_double(@neq*@neq)
142
+ jacm = Array.new
143
+ for i in 0..(@neq-1) do
144
+ arow = Array.new
145
+ for j in 0..(@neq-1) do
146
+ arow << all[j*@neq+i] # Fortran stores in column major, though references in row major
147
+ end
148
+ jacm << arow
149
+ end
150
+
151
+ @jac.call(p1.read_double,p2.read_array_of_double(@neq),p3.read_array_of_double(@neq),jacm,p5.read_double)
152
+
153
+ p4.write_array_of_double(jacm.transpose.flatten) # Fortran stores in column major, though references in row major
154
+ end
155
+
156
+
157
+
158
+
159
+ end
160
+ #
161
+ # solve the initial value problem
162
+ # this is only required if the Y's and Y'primes are not
163
+ # already consistent
164
+ #
165
+ # y or yprime will be modified
166
+ #
167
+ # init=
168
+ # * 1 compute derivatives and algebraics from states
169
+ # * 2 compute algebraics and states from derivatives
170
+ #
171
+ # ialgs = an array indicating which Y's are algebraics
172
+ # 0 means it is a state (aka has derivative in the equations)
173
+ # 1 means it is an algebraic
174
+ #
175
+ def solveForInitialValues(time,y,yprime,ialgs=[0]*@neqs,init=1)
176
+
177
+ ptime = FFI::MemoryPointer.new(:double)
178
+ ptime.write_double(time)
179
+ ptout = FFI::MemoryPointer.new(:double)
180
+ ptout.write_double(time+1)
181
+ pidid = FFI::MemoryPointer.new(:int)
182
+ @prtol.write_double(@rtol)
183
+ @patol.write_double(@atol)
184
+ @y=y
185
+ @yprime=yprime
186
+ @py.write_array_of_double(@y)
187
+ @pyprime.write_array_of_double(@yprime)
188
+
189
+ if (init==1) then
190
+ @info[10]=1
191
+ for i in 0..(@neq-1) do
192
+ @iwork[40+i] = (ialgs[i]==1 ? -1 : 1)
193
+ end
194
+ @info[13]=1
195
+ @piwork.write_array_of_int(@iwork)
196
+ @pinfo.write_array_of_int(@info)
197
+ ddaspk_(@resproc,@pneq,ptime,@py,@pyprime,ptout,@pinfo,@prtol,@patol,pidid,@pwork,@plwr,@piwork,@plwi,nil,nil,@jacproc,nil)
198
+ @y.replace @py.read_array_of_double(@neq)
199
+ @yprime.replace @pyprime.read_array_of_double(@neq)
200
+ idid = pidid.read_int
201
+ elsif (init==2) then
202
+ @info[10]=2
203
+ for i in 0..(@neq-1) do
204
+ @iwork[40+i] = (ialgs[i]==1 ? -1 : 1)
205
+ end
206
+ @info[13]=1
207
+ @piwork.write_array_of_int(@iwork)
208
+ @pinfo.write_array_of_int(@info)
209
+ ddaspk_(@resproc,@pneq,ptime,@py,@pyprime,ptout,@pinfo,@prtol,@patol,pidid,@pwork,@plwr,@piwork,@plwi,nil,nil,@jacproc,nil)
210
+ @y.replace @py.read_array_of_double(@neq)
211
+ @yprime.replace @pyprime.read_array_of_double(@neq)
212
+ idid = pidid.read_int
213
+ end
214
+ @info[13]=0
215
+ @info[10]=0
216
+ @info[0]=0
217
+ idid
218
+
219
+ end
220
+
221
+ def solve (time, tout)
222
+
223
+ @prtol.write_double(@rtol)
224
+ @patol.write_double(@atol)
225
+
226
+ #
227
+ # user supply an explicit Jacobian?
228
+ #
229
+ if (@jac) then
230
+ @info[4]=1
231
+ else
232
+ @info[4]=0
233
+ end
234
+ #
235
+ # maximum time step option
236
+ #
237
+ if (@maxstep!=0) then
238
+ @info[6]=1
239
+ @work[1]=@maxstep
240
+ else
241
+ @info[6]=0
242
+ @work[1]=@maxstep
243
+ end
244
+
245
+ #
246
+ # starting time
247
+ #
248
+ ptime = FFI::MemoryPointer.new(:double)
249
+ ptime.write_double(time)
250
+
251
+ #
252
+ # ending time
253
+ #
254
+ ptout = FFI::MemoryPointer.new(:double)
255
+ ptout.write_double(tout)
256
+
257
+ idid=1
258
+ pidid = FFI::MemoryPointer.new(:int)
259
+ pidid.write_int(idid)
260
+
261
+ @pwork.write_array_of_double(@work)
262
+ @piwork.write_array_of_int(@iwork)
263
+ @pinfo.write_array_of_int(@info)
264
+
265
+
266
+ while (idid==1) do
267
+ ddaspk_(@resproc,@pneq,ptime,@py,@pyprime,ptout,@pinfo,@prtol,@patol,pidid,@pwork,@plwr,@piwork,@plwi,nil,nil,@jacproc,nil)
268
+ idid = pidid.read_int
269
+ #
270
+ # report intermediate values ?
271
+ #
272
+ if ((idid==1)&&(@inter)) then
273
+ @inter.call(ptime.read_double,@py.read_array_of_double(@neq),@pyprime.read_array_of_double(@neq))
274
+ end
275
+
276
+ end
277
+
278
+ @work =@pwork.read_array_of_double(@lwr) # save the working ram
279
+ @iwork=@piwork.read_array_of_int(@lwi)
280
+ @info[0]=1
281
+ [ptime.read_double,@py.read_array_of_double(@neq),@pyprime.read_array_of_double(@neq),idid]
282
+
283
+ end
284
+
285
+ def reset
286
+ @info[0]=0
287
+ @py.write_array_of_double(@y)
288
+ @pyprime.write_array_of_double(@yprime)
289
+ end
290
+
291
+
292
+ end
293
+
294
+ end
data/lib/test.rb ADDED
@@ -0,0 +1,131 @@
1
+ # Copyright (c) 2009 Eric Todd Meyers
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person
4
+ # obtaining a copy of this software and associated documentation
5
+ # files (the "Software"), to deal in the Software without
6
+ # restriction, including without limitation the rights to use,
7
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the
9
+ # Software is furnished to do so, subject to the following
10
+ # conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ # OTHER DEALINGS IN THE SOFTWARE.
23
+ #
24
+ #
25
+ # More information, source code and gems @ http://rubyforge.org/projects/rb-daspk/
26
+ #
27
+ #
28
+
29
+ require 'rubygems'
30
+ require 'rb-daspk'
31
+ include DASPK
32
+
33
+ y = [1.0,0.5]
34
+ yprime = [0.0,0.0]
35
+
36
+ #
37
+ # in case the user wants to see details during the integration. in this case were just dumping values
38
+ # not required
39
+ #
40
+ inter = Proc.new do |time,y,yprime|
41
+ print "time = #{time} y0=#{y[0]} y1=#{y[1]}\n"
42
+ end
43
+
44
+
45
+ #
46
+ # let's solve and easy one
47
+ #
48
+ # dy1/dy = y1
49
+ # dy2/dt = 2*y2
50
+ #
51
+ # we all know the answer is (for the starting y's above)
52
+ # y1 = e**t
53
+ # y2 = e**(2t+ln(0.5))
54
+ #
55
+ # at 2 seconds we should get y1 = 7.389056098930649, y2 = 27.29907501657209
56
+ #
57
+ #
58
+
59
+ #
60
+ # explicit modified jacobian
61
+ # not required, but used if passed to solver
62
+ #
63
+ jac = Proc.new do |time,y,yprime,pd,cj|
64
+ pd[0][0] = 1.0 - cj # first equation with respect to first var
65
+ pd[0][1] = 0.0 # first equation with respect to second var
66
+ pd[1][0] = 0.0 # second equation with respect to first var
67
+ pd[1][1] = 2.0 - cj # second equation with respect to second var
68
+ end
69
+
70
+ s=Solver.new(2,y,yprime,inter, jac) do |time,yy,yyprime,delta|
71
+ #s=Solver.new(2,y,yprime,inter) do |time,yy,yyprime,delta|
72
+ delta[0] = yy[0] - yyprime[0]
73
+ delta[1] = 2.0*yy[1] - yyprime[1]
74
+ end
75
+
76
+
77
+ #
78
+ # solve the initial value problem
79
+ # this is only required if the Y's and Y'primes are not
80
+ # already consistent
81
+ #
82
+ # y and yprime will be modified
83
+ #
84
+ #
85
+ #
86
+ #
87
+ #
88
+ #
89
+ ans = s.solveForInitialValues(0,y,yprime,[0,0],1)
90
+ print " y0=#{y[0]} y1=#{y[1]}\n"
91
+ print " yp0=#{yprime[0]} yp1=#{yprime[1]}\n"
92
+ print "idid = #{ans}\n"
93
+
94
+ #
95
+ # integrate to 1 second
96
+ #
97
+ ans = s.solve(0.0,1.0)
98
+
99
+
100
+ #print final values
101
+ time = ans[0]
102
+ ya = ans[1]
103
+ ypa = ans[2]
104
+ print "time = #{time} y0=#{ya[0]} y1=#{ya[1]}\n"
105
+ print "time = #{time} yp0=#{ypa[0]} yp1=#{ypa[1]}\n"
106
+
107
+ #
108
+ # lets keep going. should start at the last time we left off regardless of the sart time
109
+ # integrate to 2 seconds
110
+ #
111
+ ans = s.solve(0.0,2.0)
112
+ #print final values
113
+ time = ans[0]
114
+ ya = ans[1]
115
+ ypa = ans[2]
116
+ print "time = #{time} y0=#{ya[0]} y1=#{ya[1]}\n"
117
+ print "time = #{time} yp0=#{ypa[0]} yp1=#{ypa[1]}\n"
118
+
119
+ #
120
+ # reset integration. should go back to tzero
121
+ # integrate to 2 seconds
122
+ #
123
+ s.reset
124
+
125
+ ans = s.solve(0.0,2.0)
126
+ #print final values
127
+ time = ans[0]
128
+ ypa = ans[2]
129
+ ya = ans[1]
130
+ print "time = #{time} y0=#{ya[0]} y1=#{ya[1]}\n"
131
+ print "time = #{time} yp0=#{ypa[0]} yp1=#{ypa[1]}\n"
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rb-daspk
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: x86-darwin-10
6
+ authors:
7
+ - Eric Meyers
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-28 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: ffi
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.5.3
24
+ version:
25
+ description: Allows users to solve differential algebraic equations in Ruby, using Ruby constructs. Interfaces with the DASPK Fortran library. DASPK solves DAE's of the form G(t,y,y',p) = 0
26
+ email: etm@ericmeyers.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - lib/libdaspk.dylib
35
+ - lib/rb-daspk.rb
36
+ - lib/test.rb
37
+ has_rdoc: false
38
+ homepage:
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --inline-source
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project: rb-daspk
59
+ rubygems_version: 1.3.1
60
+ signing_key:
61
+ specification_version: 2
62
+ summary: DASPK Ruby Interface. Allows users to solve differential algebraic equations in Ruby, using Ruby constructs. Interfaces with the DASPK Fortran library. DASPK solves DAE's of the form G(t,y,y',p) = 0
63
+ test_files: []
64
+