Module: YPetri::Core::Timed::RungeKutta

Defined in:
lib/y_petri/core/timed/runge_kutta.rb

Overview

Runge-Kutta method. Like vanilla Euler method, assumes that only T transitions are in the net.

Instance Method Summary collapse

Instance Method Details

#alert_user!(object) ⇒ Object



108
109
110
111
112
# File 'lib/y_petri/core/timed/runge_kutta.rb', line 108

def alert_user! object
  # TODO: As soon as more core's method begin relying on core's own state,
  # this method will be moved to Core module.
  @user_alert_closure.call( object )
end

#delta(Δt) ⇒ Object Also known as: Δ

Computes delta by Runge-Kutta 4th order method.



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/y_petri/core/timed/runge_kutta.rb', line 8

def delta Δt
  # The f below is from the equation state' = f( state )
  f = lambda do |mv| # mv is the marking vector of the free places
    # Delta from s transitions.
    # TODO: This is only array now. Make it something else. One possibility
    # would be to use simulation's MarkingVector class, but core should
    # actually have its own marking vector class, probably parametrized by
    # the net. It does not matter because alone I won't be able to exhaust
    # all the possibilities.
    delta_s = simulation.MarkingVector.zero( simulation.free_pp )
    # Here, we get the nonstoichiometric transitions of the simulation.
    nonstoichio_tt = simulation.s_tt
    # Now, let's get the delta contribution of the nonstoichio. tt.
    nonstoichio_tt.each { |t|
      domain, codomain = t.domain, t.codomain # transition's domain
      function = t.rate_closure # transition's function
      output = Array function.call( *domain.map { |p| mv.fetch p } )
      codomain.each_with_index do |p, i|
        delta_s.set( p, delta_s.fetch( p ) + output[i] )
      end
      # TODO: The above code is suboptimal, needlessly computing
      # MarkingVector#index and #fetch( place ) each time.
      # The array incrementing might not be the best choice either,
      # and most of all, the whole thing would need to be compiled
      # into assembly language or at least FORTRAN.
    }

    # Delta from S transitions.
    # TODO: (Same remark as for s transitions, see above.)
    delta_S = simulation.MarkingVector.zero( simulation.free_pp )
    # Here, we get the stoichiometric transitions of the simulation
    stoichio_tt = simulation.S_tt
    # Now, let's get the delta contribution of the stoichio. tt.
    stoichio_tt.each { |t|
      domain, codomain = t.domain, t.codomain # transition's domain
      function = t.rate_closure # transition's function
      s = t.stoichiometry
      flux = function.call( *domain.map { |place| mv.fetch place } )
      codomain.each_with_index do |p, i|
        delta_S.set( p, delta_S.fetch( p ) + flux * s[i] )
      end
      # TODO: Again, the above code is suboptimal.
    }

    return delta_s + delta_S
  end

  y = marking_of_free_places

  k1 = f.( y ) # puts "k1 ( = f( y ) ) is #{k1}"
  k2 = f.( y + Δt / 2 * k1 ) # puts "k2 is #{k2}"
  k3 = f.( y + Δt / 2 * k2 ) # puts "k3 is #{k3}"
  k4 = f.( y + Δt * k3 ) # puts "k4 is #{k4}"

  rslt = Δt / 6 * ( k1 + 2 * k2 + 2 * k3 + k4 ) # puts "rslt is #{rslt}"

  return rslt                 # Marking vector of free places
end

#increment_marking_of_free_places(by) ⇒ Object



79
80
81
82
# File 'lib/y_petri/core/timed/runge_kutta.rb', line 79

def increment_marking_of_free_places by
  # TODO: Same remark as above.
  @marking_of_free_places += by
end

#increment_time!(by) ⇒ Object



84
85
86
87
88
# File 'lib/y_petri/core/timed/runge_kutta.rb', line 84

def increment_time! by
  # TODO: Once other timed methods than runge_kutta are reasonable, this
  # should be moved to core/timed.rb
  @time += by
end

#reset_time!(to = 0.0) ⇒ Object



90
91
92
93
94
# File 'lib/y_petri/core/timed/runge_kutta.rb', line 90

def reset_time! to=0.0
  # TODO: Once other timed methods than runge_kutta are reasonable, this
  # should be moved to core/timed.rb
  @time = to
end

#set_user_alert_closure(&block) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
# File 'lib/y_petri/core/timed/runge_kutta.rb', line 96

def set_user_alert_closure &block
  # TODO: Core's runge_kutta method is special for now, and even
  # simulation recognizes that. With runge_kutta method, core uses
  # single @user_alert_closure which it calls whenever the state
  # of the core progresses. It is the business of the user to supply,
  # before using the core, that does what the user wants. It is also
  # imaginable that different core's modes of operation would have
  # different sensitivity with regard to alerting the user, but for now,
  # the user is alerted whenever anything happens at all.
  @user_alert_closure = block
end

#step!(Δt = simulation.step) ⇒ Object



68
69
70
71
72
73
74
75
76
77
# File 'lib/y_petri/core/timed/runge_kutta.rb', line 68

def step! Δt=simulation.step
  # TODO: Thus far, runge_kutta method is an exception in the core in
  # that it works with core's own state. (Core used to work with
  # simulation's state before and rely on the simulation to provide
  # state increment and assign closures.) This is how whole core should
  # work.
  increment_marking_of_free_places Δ( Δt )
  increment_time! Δt
  alert_user! marking_of_free_places
end