rb-daspk 0.0.5-x86-mswin32-60 → 0.0.6-x86-mswin32-60

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/examples/springmass.rb +130 -130
  2. data/lib/daspk.dll +0 -0
  3. data/lib/rb-daspk.rb +324 -317
  4. metadata +1 -1
@@ -1,130 +1,130 @@
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
- require 'rubygems'
29
- require 'rb-daspk'
30
- include DASPK
31
-
32
- #
33
- # classic spring mass problem
34
- #
35
- # d2x/dt + 2*l*w0*dxdt + w0*w0*x = 0
36
- #
37
- # let v = dx/dt, then
38
- #
39
- # 1. dxdt - v = 0
40
- # 2. dvdt + 2*l*w0*v + w0*w0*x = 0
41
- #
42
- #
43
- # let's start with x at 1, dxdt at 0 (v=0)
44
- # we'll ask DASPK for the state derivatives
45
- #
46
- y = [1.0,0.0]
47
- yprime= [0.0,-1.0] # these are gusses in this case. we will call solveForInitialValues to get them
48
-
49
-
50
- w0 = 1.0
51
- l = 0.0 # undamped
52
-
53
- #
54
- # in case we want to see details during the integration. in this case were just dumping values
55
- # not required (can pass nil to Solver.new if you want)
56
- #
57
- inter = Proc.new do |time,y,yprime|
58
- print " time = #{time} x = #{y[0]}\n"
59
- end
60
-
61
- jac = Proc.new do |time,y,yprime,jacm,cj|
62
-
63
- jacm[0][0] = cj
64
- jacm[0][1] = -1.0
65
- jacm[1][0] = w0**2.0
66
- jacm[1][1] = (2*l*w0 + cj)
67
-
68
- end
69
-
70
- s=Solver.new(2,y,yprime,inter,jac) do |time,y,yprime,delta|
71
- #s=Solver.new(2,y,yprime,inter) do |time,y,yprime,delta|
72
- delta[0] = yprime[0] - y[1]
73
- delta[1] = yprime[1] + 2.0*l*w0*y[1] + w0*w0*y[0]
74
- end
75
-
76
-
77
- #
78
- # solve the initial value problem
79
- # note, this isnt required if the y and yprimes are already consistent
80
- #
81
- # return code from solveFoeInitialValues
82
- # 4 success (see DDASPK for all possible values)
83
- #
84
- idid = s.solveForInitialValues(0,y,yprime,[0,0])
85
- print " ----------------------------------------\n"
86
- print " initial conditions\n"
87
- print " x = #{y[0]}\n"
88
- print " vel = #{y[1]}\n"
89
- print " accelaration = #{yprime[1]}\n"
90
- print " ----------------------------------------\n"
91
-
92
- #
93
- # integrate to PI seconds
94
- #
95
- #
96
- ans = s.solve(0.0,Math::PI)
97
- #
98
- # answer is an array of arrays
99
- # ans[0] = end time
100
- # ans[1] = end y array
101
- # ans[2] = end yprime array
102
- # ans[3] = DASPK return code (see DDASPK.F for meaning)
103
- #
104
- print " ----------------------------------------\n"
105
- print " final conditions\n"
106
- print " time = #{ans[0]}\n"
107
- print " x = #{ans[1][0]}\n"
108
- print " vel = #{ans[1][1]}\n"
109
- print " accelaration = #{ans[2][1]}\n"
110
- print " ----------------------------------------\n"
111
-
112
- #
113
- # continue to integrate to 2PI seconds
114
- #
115
- #
116
- ans = s.solve(0.0,2*Math::PI)
117
- #
118
- # answer is an array of arrays
119
- # ans[0] = end time
120
- # ans[1] = end y array
121
- # ans[2] = end yprime array
122
- # ans[3] = DASPK return code (see DDASPK.F for meaning)
123
- #
124
- print " ----------------------------------------\n"
125
- print " final conditions\n"
126
- print " time = #{ans[0]}\n"
127
- print " x = #{ans[1][0]}\n"
128
- print " vel = #{ans[1][1]}\n"
129
- print " accelaration = #{ans[2][1]}\n"
130
- print " ----------------------------------------\n"
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
+ require 'rubygems'
29
+ require 'rb-daspk'
30
+ include DASPK
31
+
32
+ #
33
+ # classic spring mass problem
34
+ #
35
+ # d2x/dt + 2*l*w0*dxdt + w0*w0*x = 0
36
+ #
37
+ # let v = dx/dt, then
38
+ #
39
+ # 1. dxdt - v = 0
40
+ # 2. dvdt + 2*l*w0*v + w0*w0*x = 0
41
+ #
42
+ #
43
+ # let's start with x at 1, dxdt at 0 (v=0)
44
+ # we'll ask DASPK for the state derivatives
45
+ #
46
+ y = [1.0,0.0]
47
+ yprime= [0.0,0.0] # these are gusses in this case. we will call solveForInitialValues to get them
48
+
49
+
50
+ w0 = 1.0
51
+ l = 0.0 # undamped
52
+
53
+ #
54
+ # in case we want to see details during the integration. in this case were just dumping values
55
+ # not required (can pass nil to Solver.new if you want)
56
+ #
57
+ inter = Proc.new do |time,y,yprime|
58
+ print " time = #{time} x = #{y[0]}\n"
59
+ end
60
+
61
+ jac = Proc.new do |time,y,yprime,jacm,cj|
62
+
63
+ jacm[0][0] = cj
64
+ jacm[0][1] = -1.0
65
+ jacm[1][0] = w0**2.0
66
+ jacm[1][1] = (2*l*w0 + cj)
67
+
68
+ end
69
+
70
+ s=Solver.new(2,y,yprime,inter,jac) do |time,y,yprime,delta|
71
+ #s=Solver.new(2,y,yprime) do |time,y,yprime,delta|
72
+ delta[0] = yprime[0] - y[1]
73
+ delta[1] = yprime[1] + 2.0*l*w0*y[1] + w0*w0*y[0]
74
+ end
75
+
76
+
77
+ #
78
+ # solve the initial value problem
79
+ # note, this isnt required if the y and yprimes are already consistent
80
+ #
81
+ # return code from solveFoeInitialValues
82
+ # 4 success (see DDASPK for all possible values)
83
+ #
84
+ idid = s.solveForInitialValues(0,y,yprime,[0,0])
85
+ print " ----------------------------------------\n"
86
+ print " initial conditions\n"
87
+ print " x = #{y[0]}\n"
88
+ print " vel = #{y[1]}\n"
89
+ print " accelaration = #{yprime[1]}\n"
90
+ print " ----------------------------------------\n"
91
+
92
+ #
93
+ # integrate to PI seconds
94
+ #
95
+ #
96
+ ans = s.solve(0.0,Math::PI)
97
+ #
98
+ # answer is an array of arrays
99
+ # ans[0] = end time
100
+ # ans[1] = end y array
101
+ # ans[2] = end yprime array
102
+ # ans[3] = DASPK return code (see DDASPK.F for meaning)
103
+ #
104
+ print " ----------------------------------------\n"
105
+ print " final conditions\n"
106
+ print " time = #{ans[0]}\n"
107
+ print " x = #{ans[1][0]}\n"
108
+ print " vel = #{ans[1][1]}\n"
109
+ print " accelaration = #{ans[2][1]}\n"
110
+ print " ----------------------------------------\n"
111
+
112
+ #
113
+ # continue to integrate to 2PI seconds
114
+ #
115
+ #
116
+ ans = s.solve(0.0,2*Math::PI)
117
+ #
118
+ # answer is an array of arrays
119
+ # ans[0] = end time
120
+ # ans[1] = end y array
121
+ # ans[2] = end yprime array
122
+ # ans[3] = DASPK return code (see DDASPK.F for meaning)
123
+ #
124
+ print " ----------------------------------------\n"
125
+ print " final conditions\n"
126
+ print " time = #{ans[0]}\n"
127
+ print " x = #{ans[1][0]}\n"
128
+ print " vel = #{ans[1][1]}\n"
129
+ print " accelaration = #{ans[2][1]}\n"
130
+ print " ----------------------------------------\n"
Binary file
@@ -1,317 +1,324 @@
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
- #:main:DASPK::Solver
28
- #
29
-
30
-
31
- require 'rubygems'
32
- require 'ffi'
33
-
34
- module FFI
35
- class Pointer
36
- def read_double
37
- get_float64(0)
38
- end
39
-
40
- def write_double(obj)
41
- put_float64(0, obj)
42
- end
43
-
44
- def read_array_of_double(length)
45
- get_array_of_double(0, length)
46
- end
47
-
48
- def write_array_of_double(ary)
49
- put_array_of_double(0, ary)
50
- end
51
- end
52
- end
53
-
54
-
55
- module DASPKLIB
56
- extend FFI::Library
57
- ffi_lib File.dirname(__FILE__) + '/daspk.dll'
58
- callback :jac, [:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer], :void
59
- callback :res, [:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer], :void
60
- attach_function 'DDASPK', [:res, :pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,
61
- :pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:jac, :pointer], :void
62
- attach_function 'D1MACH', [:pointer],:double
63
- end
64
-
65
- module DASPK
66
- #
67
- # interface to DASPK
68
- #
69
- # typical usage
70
- #
71
- # 1. create an instance of the Solver class. pass in the initial states/derivatives and the residual function as a block
72
- # 2. if the initial conditions are not consistent call solveForInitialValues
73
- # 3. call solve with the start and end times desired
74
- # 4. repeated calls to solve will solve to each end time. the start time is ingored. this allows
75
- # for continued integration
76
- #
77
- class Solver
78
-
79
- include DASPKLIB
80
- attr_accessor :rtol,:atol,:inter,:jac,:maxstep
81
- #
82
- #
83
- # * neq = the number of equations. this is always the sum of the number of states and algebraics
84
- # * y = array containing the initial states and algebraics
85
- # * yprime = array containing the initial state derivatives
86
- # * inter = Proc instance. defaults to nil. if non-nil used as a callback to report the integration results at every time step
87
- # * jac = Proc instance. defauls to nil. if non-nil used to get the modified Jacbian matrix. otherwise DASPK will determine by iteration
88
- # * res = residual (delta) Proc. must be present. this is responsible for filling of the delta (residual) array.
89
- #
90
- def initialize (neq, y, yprime, inter=nil, jac=nil, &res)
91
- @lwr = 500
92
- @lwi = 500
93
- @plwr = FFI::MemoryPointer.new(:int)
94
- @plwr.write_int(@lwr)
95
- @plwi = FFI::MemoryPointer.new(:int)
96
- @plwi.write_int(@lwi)
97
- @iwork = [0]*@lwi
98
- @work = [0]*@lwr
99
- @pwork = FFI::MemoryPointer.new(:double,@lwr)
100
- @piwork = FFI::MemoryPointer.new(:int,@lwi)
101
- @info = [0]*30
102
- @pinfo = FFI::MemoryPointer.new(:int,30)
103
- @ialgs = [0]*neq
104
- @res = res
105
- @inter = inter
106
- @rtol = 0.000001
107
- @atol = 0.000001
108
- @jac = jac
109
- @maxstep = 0
110
-
111
-
112
- if (inter) then
113
- @info[2]=1
114
- end
115
-
116
- @neq = neq
117
- @pneq = FFI::MemoryPointer.new(:int)
118
- @pneq.write_int(neq)
119
- @y = y
120
- @yprime = yprime
121
-
122
- @py = FFI::MemoryPointer.new(:double,neq)
123
- @py.write_array_of_double(y)
124
- @pyprime = FFI::MemoryPointer.new(:double,neq)
125
- @pyprime.write_array_of_double(yprime)
126
-
127
- @prtol = FFI::MemoryPointer.new(:double)
128
- @patol = FFI::MemoryPointer.new(:double)
129
-
130
- #
131
- # residual proc called from the Fortran. Let's pass it onto the user's residual procedure
132
- # in a easier to use form
133
- #
134
- @resproc = Proc.new do |p1,p2,p3,p4,p5,p6,p7,p8|
135
- delta = p5.read_array_of_double(@neq)
136
- @res.call(p1.read_double,p2.read_array_of_double(@neq),p3.read_array_of_double(@neq),delta)
137
- p5.write_array_of_double(delta)
138
- end
139
-
140
- #
141
- # jacobian proc called from the Fortran. Let's pass it onto the user's jacboian procedure
142
- # in a easier to use form
143
- #
144
- @jacproc = Proc.new do |p1,p2,p3,p4,p5,p6,p7|
145
- all = p4.read_array_of_double(@neq*@neq)
146
- jacm = Array.new
147
- for i in 0..(@neq-1) do
148
- arow = Array.new
149
- for j in 0..(@neq-1) do
150
- arow << all[j*@neq+i] # Fortran stores in column major, though references in row major
151
- end
152
- jacm << arow
153
- end
154
-
155
- @jac.call(p1.read_double,p2.read_array_of_double(@neq),p3.read_array_of_double(@neq),jacm,p5.read_double)
156
-
157
- p4.write_array_of_double(jacm.transpose.flatten) # Fortran stores in column major, though references in row major
158
- end
159
-
160
-
161
-
162
-
163
- end
164
- #
165
- # solve the initial value problem
166
- # this is only required if the Y's and Y'primes are not
167
- # already consistent
168
- #
169
- # the contents of y or yprime will be modified
170
- #
171
- # init=
172
- # * 1 compute derivatives and algebraics from states
173
- # * 2 compute algebraics and states from derivatives
174
- #
175
- # ialgs = an array indicating which Y's are algebraics
176
- # 0 means it is a state (aka has derivative in the equations)
177
- # 1 means it is an algebraic
178
- #
179
- def solveForInitialValues(time,y,yprime,ialgs,init=1,mxnit=5,mxnj=6,mxnh=5,lsoff=0,stptol=0,epinit=0.01)
180
-
181
- @mxnit = mxnit
182
- @mxnj = mxnj
183
- @mxnh = mxnh
184
- @lsoff = lsoff
185
- pidum = FFI::MemoryPointer.new(:int)
186
- @stptol = (stptol==0 ? D1MACH(pidum)**(2.0/3.0) : stptol)
187
- @epinit = epinit
188
- @info[16] = 1
189
- @iwork[31] = @mxnit
190
- @iwork[32] = @mxnj
191
- @iwork[33] = @mxnh
192
- @iwork[34] = @lsoff
193
- @work[13] = @stptol
194
- @work[14] = @epinit
195
-
196
- ptime = FFI::MemoryPointer.new(:double)
197
- ptout = FFI::MemoryPointer.new(:double)
198
- pidid = FFI::MemoryPointer.new(:int)
199
- @y = y
200
- @yprime = yprime
201
-
202
- ptime.write_double(time)
203
- ptout.write_double(time+1)
204
- @py.write_array_of_double(@y)
205
- @pyprime.write_array_of_double(@yprime)
206
- @prtol.write_double(@rtol)
207
- @patol.write_double(@atol)
208
-
209
- if (init==1) then
210
- @info[10]=1
211
- for i in 0..(@neq-1) do
212
- @iwork[40+i] = (ialgs[i]==1 ? -1 : 1)
213
- end
214
- @info[13]=1
215
- @piwork.write_array_of_int(@iwork)
216
- @pwork.write_array_of_double(@work)
217
- @pinfo.write_array_of_int(@info)
218
- DDASPK(@resproc,@pneq,ptime,@py,@pyprime,ptout,@pinfo,@prtol,@patol,pidid,@pwork,@plwr,@piwork,@plwi,nil,nil,@jacproc,nil)
219
- @y.replace @py.read_array_of_double(@neq)
220
- @yprime.replace @pyprime.read_array_of_double(@neq)
221
- idid = pidid.read_int
222
- elsif (init==2) then
223
- @info[10]=2
224
- for i in 0..(@neq-1) do
225
- @iwork[40+i] = (ialgs[i]==1 ? -1 : 1)
226
- end
227
- @info[13]=1
228
- @piwork.write_array_of_int(@iwork)
229
- @pwork.write_array_of_double(@work)
230
- @pinfo.write_array_of_int(@info)
231
- DDASPK(@resproc,@pneq,ptime,@py,@pyprime,ptout,@pinfo,@prtol,@patol,pidid,@pwork,@plwr,@piwork,@plwi,nil,nil,@jacproc,nil)
232
- @y.replace @py.read_array_of_double(@neq)
233
- @yprime.replace @pyprime.read_array_of_double(@neq)
234
- idid = pidid.read_int
235
- end
236
- @info[13] = 0
237
- @info[10] = 0
238
- @info[0] = 0
239
- @info[16] = 0
240
- idid
241
-
242
- end
243
-
244
- def solve (time, tout)
245
- #
246
- # solver options
247
- #
248
-
249
- @prtol.write_double(@rtol)
250
- @patol.write_double(@atol)
251
-
252
- #
253
- # user supply an explicit Jacobian?
254
- #
255
- if (@jac) then
256
- @info[4]=1
257
- else
258
- @info[4]=0
259
- end
260
- #
261
- # maximum time step option
262
- #
263
- if (@maxstep!=0) then
264
- @info[6]=1
265
- @work[1]=@maxstep
266
- else
267
- @info[6]=0
268
- @work[1]=@maxstep
269
- end
270
-
271
- #
272
- # starting time
273
- #
274
- ptime = FFI::MemoryPointer.new(:double)
275
- ptime.write_double(time)
276
-
277
- #
278
- # ending time
279
- #
280
- ptout = FFI::MemoryPointer.new(:double)
281
- ptout.write_double(tout)
282
-
283
- idid=1
284
- pidid = FFI::MemoryPointer.new(:int)
285
- pidid.write_int(idid)
286
- @pwork.write_array_of_double(@work)
287
- @piwork.write_array_of_int(@iwork)
288
- @pinfo.write_array_of_int(@info)
289
-
290
- while (idid==1) do
291
- DDASPK(@resproc,@pneq,ptime,@py,@pyprime,ptout,@pinfo,@prtol,@patol,pidid,@pwork,@plwr,@piwork,@plwi,nil,nil,@jacproc,nil)
292
- idid = pidid.read_int
293
- #
294
- # report intermediate values ?
295
- #
296
- if ((idid==1)&&(@inter)) then
297
- @inter.call(ptime.read_double,@py.read_array_of_double(@neq),@pyprime.read_array_of_double(@neq))
298
- end
299
-
300
- end
301
-
302
- @work = @pwork.read_array_of_double(@lwr) # save the working ram
303
- @iwork = @piwork.read_array_of_int(@lwi)
304
- @info[0] = 1
305
- [ptime.read_double,@py.read_array_of_double(@neq),@pyprime.read_array_of_double(@neq),idid]
306
-
307
- end
308
-
309
- def reset
310
- @info[0]=0
311
- @py.write_array_of_double(@y)
312
- @pyprime.write_array_of_double(@yprime)
313
- end
314
-
315
- end
316
-
317
- end
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
+ #:main:DASPK::Solver
28
+ #
29
+
30
+
31
+ require 'rubygems'
32
+ require 'ffi'
33
+
34
+ WINDOZE = Config::CONFIG['host_os'] =~ /mswin|mingw/
35
+
36
+ module FFI
37
+ class Pointer
38
+ def read_double
39
+ get_float64(0)
40
+ end
41
+
42
+ def write_double(obj)
43
+ put_float64(0, obj)
44
+ end
45
+
46
+ def read_array_of_double(length)
47
+ get_array_of_double(0, length)
48
+ end
49
+
50
+ def write_array_of_double(ary)
51
+ put_array_of_double(0, ary)
52
+ end
53
+ end
54
+ end
55
+
56
+
57
+ module DASPKLIB
58
+ extend FFI::Library
59
+ if (!WINDOZE) then
60
+ ffi_lib File.dirname(__FILE__) + '/libdaspk.dylib'
61
+ else
62
+ ffi_lib File.dirname(__FILE__) + '/daspk.dll'
63
+ end
64
+
65
+ callback :jac, [:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer], :void
66
+ callback :res, [:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer], :void
67
+ attach_function 'ddaspk_', [:res, :pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,
68
+ :pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:jac, :pointer], :void
69
+ attach_function 'd1mach_', [:pointer],:double
70
+ end
71
+
72
+ module DASPK
73
+ #
74
+ # interface to DASPK
75
+ #
76
+ # typical usage
77
+ #
78
+ # 1. create an instance of the Solver class. pass in the initial states/derivatives and the residual function as a block
79
+ # 2. if the initial conditions are not consistent call solveForInitialValues
80
+ # 3. call solve with the start and end times desired
81
+ # 4. repeated calls to solve will solve to each end time. the start time is ingored. this allows
82
+ # for continued integration
83
+ #
84
+ class Solver
85
+
86
+ include DASPKLIB
87
+ attr_accessor :rtol,:atol,:inter,:jac,:maxstep
88
+ #
89
+ #
90
+ # * neq = the number of equations. this is always the sum of the number of states and algebraics
91
+ # * y = array containing the initial states and algebraics
92
+ # * yprime = array containing the initial state derivatives
93
+ # * inter = Proc instance. defaults to nil. if non-nil used as a callback to report the integration results at every time step
94
+ # * jac = Proc instance. defauls to nil. if non-nil used to get the modified Jacbian matrix. otherwise DASPK will determine by iteration
95
+ # * res = residual (delta) Proc. must be present. this is responsible for filling of the delta (residual) array.
96
+ #
97
+ def initialize (neq, y, yprime, inter=nil, jac=nil, &res)
98
+ @lwr = 15000
99
+ @lwi = 15000
100
+ @plwr = FFI::MemoryPointer.new(:int)
101
+ @plwr.write_int(@lwr)
102
+ @plwi = FFI::MemoryPointer.new(:int)
103
+ @plwi.write_int(@lwi)
104
+ @iwork = [0]*@lwi
105
+ @work = [0]*@lwr
106
+ @pwork = FFI::MemoryPointer.new(:double,@lwr)
107
+ @piwork = FFI::MemoryPointer.new(:int,@lwi)
108
+ @info = [0]*30
109
+ @pinfo = FFI::MemoryPointer.new(:int,30)
110
+ @ialgs = [0]*neq
111
+ @res = res
112
+ @inter = inter
113
+ @rtol = 0.000001
114
+ @atol = 0.000001
115
+ @jac = jac
116
+ @maxstep = 0
117
+
118
+
119
+ if (inter) then
120
+ @info[2]=1
121
+ end
122
+
123
+ @neq = neq
124
+ @pneq = FFI::MemoryPointer.new(:int)
125
+ @pneq.write_int(neq)
126
+ @y = y
127
+ @yprime = yprime
128
+
129
+ @py = FFI::MemoryPointer.new(:double,neq)
130
+ @py.write_array_of_double(y)
131
+ @pyprime = FFI::MemoryPointer.new(:double,neq)
132
+ @pyprime.write_array_of_double(yprime)
133
+
134
+ @prtol = FFI::MemoryPointer.new(:double)
135
+ @patol = FFI::MemoryPointer.new(:double)
136
+
137
+ #
138
+ # residual proc called from the Fortran. Let's pass it onto the user's residual procedure
139
+ # in a easier to use form
140
+ #
141
+ @resproc = Proc.new do |p1,p2,p3,p4,p5,p6,p7,p8|
142
+ delta = p5.read_array_of_double(@neq)
143
+ @res.call(p1.read_double,p2.read_array_of_double(@neq),p3.read_array_of_double(@neq),delta)
144
+ p5.write_array_of_double(delta)
145
+ end
146
+
147
+ #
148
+ # jacobian proc called from the Fortran. Let's pass it onto the user's jacboian procedure
149
+ # in a easier to use form
150
+ #
151
+ @jacproc = Proc.new do |p1,p2,p3,p4,p5,p6,p7|
152
+ all = p4.read_array_of_double(@neq*@neq)
153
+ jacm = Array.new
154
+ for i in 0..(@neq-1) do
155
+ arow = Array.new
156
+ for j in 0..(@neq-1) do
157
+ arow << all[j*@neq+i] # Fortran stores in column major, though references in row major
158
+ end
159
+ jacm << arow
160
+ end
161
+
162
+ @jac.call(p1.read_double,p2.read_array_of_double(@neq),p3.read_array_of_double(@neq),jacm,p5.read_double)
163
+
164
+ p4.write_array_of_double(jacm.transpose.flatten) # Fortran stores in column major, though references in row major
165
+ end
166
+
167
+
168
+
169
+
170
+ end
171
+ #
172
+ # solve the initial value problem
173
+ # this is only required if the Y's and Y'primes are not
174
+ # already consistent
175
+ #
176
+ # the contents of y or yprime will be modified
177
+ #
178
+ # init=
179
+ # * 1 compute derivatives and algebraics from states
180
+ # * 2 compute algebraics and states from derivatives
181
+ #
182
+ # ialgs = an array indicating which Y's are algebraics
183
+ # 0 means it is a state (aka has derivative in the equations)
184
+ # 1 means it is an algebraic
185
+ #
186
+ def solveForInitialValues(time,y,yprime,ialgs,init=1,mxnit=5,mxnj=6,mxnh=5,lsoff=0,stptol=0,epinit=0.01)
187
+
188
+ @mxnit = mxnit
189
+ @mxnj = mxnj
190
+ @mxnh = mxnh
191
+ @lsoff = lsoff
192
+ pidum = FFI::MemoryPointer.new(:int)
193
+ @stptol = (stptol==0 ? d1mach_(pidum)**(2.0/3.0) : stptol)
194
+ @epinit = epinit
195
+ @info[16] = 1
196
+ @iwork[31] = @mxnit
197
+ @iwork[32] = @mxnj
198
+ @iwork[33] = @mxnh
199
+ @iwork[34] = @lsoff
200
+ @work[13] = @stptol
201
+ @work[14] = @epinit
202
+
203
+ ptime = FFI::MemoryPointer.new(:double)
204
+ ptout = FFI::MemoryPointer.new(:double)
205
+ pidid = FFI::MemoryPointer.new(:int)
206
+ @y = y
207
+ @yprime = yprime
208
+
209
+ ptime.write_double(time)
210
+ ptout.write_double(time+1)
211
+ @py.write_array_of_double(@y)
212
+ @pyprime.write_array_of_double(@yprime)
213
+ @prtol.write_double(@rtol)
214
+ @patol.write_double(@atol)
215
+
216
+ if (init==1) then
217
+ @info[10]=1
218
+ for i in 0..(@neq-1) do
219
+ @iwork[40+i] = (ialgs[i]==1 ? -1 : 1)
220
+ end
221
+ @info[13]=1
222
+ @piwork.write_array_of_int(@iwork)
223
+ @pwork.write_array_of_double(@work)
224
+ @pinfo.write_array_of_int(@info)
225
+ ddaspk_(@resproc,@pneq,ptime,@py,@pyprime,ptout,@pinfo,@prtol,@patol,pidid,@pwork,@plwr,@piwork,@plwi,nil,nil,@jacproc,nil)
226
+ @y.replace @py.read_array_of_double(@neq)
227
+ @yprime.replace @pyprime.read_array_of_double(@neq)
228
+ idid = pidid.read_int
229
+ elsif (init==2) then
230
+ @info[10]=2
231
+ for i in 0..(@neq-1) do
232
+ @iwork[40+i] = (ialgs[i]==1 ? -1 : 1)
233
+ end
234
+ @info[13]=1
235
+ @piwork.write_array_of_int(@iwork)
236
+ @pwork.write_array_of_double(@work)
237
+ @pinfo.write_array_of_int(@info)
238
+ ddaspk_(@resproc,@pneq,ptime,@py,@pyprime,ptout,@pinfo,@prtol,@patol,pidid,@pwork,@plwr,@piwork,@plwi,nil,nil,@jacproc,nil)
239
+ @y.replace @py.read_array_of_double(@neq)
240
+ @yprime.replace @pyprime.read_array_of_double(@neq)
241
+ idid = pidid.read_int
242
+ end
243
+ @info[13] = 0
244
+ @info[10] = 0
245
+ @info[0] = 0
246
+ @info[16] = 0
247
+ idid
248
+
249
+ end
250
+
251
+ def solve (time, tout)
252
+ #
253
+ # solver options
254
+ #
255
+
256
+ @prtol.write_double(@rtol)
257
+ @patol.write_double(@atol)
258
+
259
+ #
260
+ # user supply an explicit Jacobian?
261
+ #
262
+ if (@jac) then
263
+ @info[4]=1
264
+ else
265
+ @info[4]=0
266
+ end
267
+ #
268
+ # maximum time step option
269
+ #
270
+ if (@maxstep!=0) then
271
+ @info[6]=1
272
+ @work[1]=@maxstep
273
+ else
274
+ @info[6]=0
275
+ @work[1]=@maxstep
276
+ end
277
+
278
+ #
279
+ # starting time
280
+ #
281
+ ptime = FFI::MemoryPointer.new(:double)
282
+ ptime.write_double(time)
283
+
284
+ #
285
+ # ending time
286
+ #
287
+ ptout = FFI::MemoryPointer.new(:double)
288
+ ptout.write_double(tout)
289
+
290
+ idid=1
291
+ pidid = FFI::MemoryPointer.new(:int)
292
+ pidid.write_int(idid)
293
+ @pwork.write_array_of_double(@work)
294
+ @piwork.write_array_of_int(@iwork)
295
+ @pinfo.write_array_of_int(@info)
296
+
297
+ while (idid==1) do
298
+ ddaspk_(@resproc,@pneq,ptime,@py,@pyprime,ptout,@pinfo,@prtol,@patol,pidid,@pwork,@plwr,@piwork,@plwi,nil,nil,@jacproc,nil)
299
+ idid = pidid.read_int
300
+ #
301
+ # report intermediate values ?
302
+ #
303
+ if ((idid==1)&&(@inter)) then
304
+ @inter.call(ptime.read_double,@py.read_array_of_double(@neq),@pyprime.read_array_of_double(@neq))
305
+ end
306
+
307
+ end
308
+
309
+ @work = @pwork.read_array_of_double(@lwr) # save the working ram
310
+ @iwork = @piwork.read_array_of_int(@lwi)
311
+ @info[0] = 1
312
+ [ptime.read_double,@py.read_array_of_double(@neq),@pyprime.read_array_of_double(@neq),idid]
313
+
314
+ end
315
+
316
+ def reset
317
+ @info[0]=0
318
+ @py.write_array_of_double(@y)
319
+ @pyprime.write_array_of_double(@yprime)
320
+ end
321
+
322
+ end
323
+
324
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rb-daspk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: x86-mswin32-60
6
6
  authors:
7
7
  - Eric Meyers