fpex0.fokker_planck

  1import numpy as np
  2from scipy import sparse
  3
  4class FokkerPlanck:
  5    def __init__(self):
  6        self._NN         = None # variables for FokkerPlanckODE_dfdu()
  7        self._A          = None
  8        self._B          = None
  9
 10    @staticmethod
 11    def FokkerPlanckODE(t, u, h, driftFcn, driftParams, diffusionFcn, diffusionParams):
 12        """
 13        ODE RHS of Fokker-Planck PDE by using the method of lines (MOL) approach.
 14
 15        FP-PDE
 16        >       u_t = - a(t,p) * u_x(t,x)  +  D(t,p) * u_xx(t,x)
 17
 18        FD-Approx
 19        >       u_x  = ( u(t,x+h) - u(t,x-h ) / (2h)
 20        >       u_xx = ( u(t,x+h) - 2u(t,x) + u(t,x-h) ) / h
 21
 22        ## Takes           
 23        **t**: Time
 24
 25        **x**: State vector
 26        
 27        **h**: MOL interval size
 28        
 29        **driftFcn**
 30        <br> Drift function, evaluated at t, driftP.
 31        
 32        **driftParams**
 33        <br> Parameter vector for drift function.
 34        
 35        **diffusionFcn**
 36        <br> Diffusion function, evaluated at t, driftP.
 37        
 38        **diffusionParams**
 39        <br> Parameter vector for diffusion function.
 40
 41        ## Returns
 42        **dx**
 43        <br> rhs vector.
 44        """
 45    
 46        # number of grid points and number of simultaneously requested vectors
 47        shape = u.shape
 48        # shape is (N,) if u is a one-dim array
 49        if len(shape) > 1:
 50            (N, vectors) = shape
 51        else:
 52            N = shape[0]
 53            vectors = 1
 54
 55        # preallocate vectors for A*u (approx. of u_x) and B*u (approx. of u_xx)
 56        Bu = np.zeros_like(u)
 57        Au = np.zeros_like(u)
 58
 59        # numpy indexing is works differently for 1d and Nd arrays
 60        if vectors > 1:
 61            # first node
 62            Bu[0,:] = ( -2*u[0,:] + 2*u[1,:] ) / h**2
 63            # Au(1) is zero
 64
 65            # inner nodes (remember: python slicing is exclusive right)
 66            i = np.arange(1,N-1)
 67            Au[i,:] = ( u[i-1,:] - u[i+1,:] ) / (2*h)            # 1st derivative stencil and scale
 68            Bu[i,:] = ( u[i-1,:] - 2*u[i,:] + u[i+1,:] ) / h**2  # 2nd derivative stencil and scale
 69
 70            # last node
 71            Bu[N-1,:] = ( -2*u[N-1,:] + 2*u[N-2,:] ) / h**2
 72            # Au(N) is zero
 73        elif vectors == 1:
 74            # first node
 75            Bu[0] = ( -2*u[0] + 2*u[1] ) / h**2
 76            # Au(1) is zero
 77
 78            # inner nodes (remember: python slicing is exclusive right)
 79            i = np.arange(1,N-1)
 80            Au[i] = ( u[i-1] - u[i+1] ) / (2*h)           # 1st derivative stencil and scale
 81            Bu[i] = ( u[i-1] - 2*u[i] + u[i+1] ) / h**2   # 2nd derivative stencil and scale
 82
 83            # last node
 84            Bu[N-1] = ( -2*u[N-1] + 2*u[N-2] ) / h**2
 85            # Au[N-1] is zero      
 86
 87        # evaluate drift and diffusion
 88        alpha = driftFcn(t,driftParams)
 89        D = diffusionFcn(t,diffusionParams)
 90
 91        # assemble rhs
 92        dx = -alpha*Au + D*Bu
 93        return dx
 94
 95
 96    def FokkerPlanckODE_dfdu(self, t, u, h, driftFcn, driftParams, diffusionFcn, diffusionParams):
 97        """
 98        Jacobian of FokkerPlanckODE w.r.t. state u, i.e. df/du. 
 99        
100        FP-PDE
101        >       u_t = - a(t,p) * u_x(t,x)  +  D(t,p) * u_xx(t,x)
102        >
103        >       where a is the driftFcn, and b is the diffusionFcn
104         
105        FD-Approx  
106        >       u_x  = ( u(t,x+h) - u(t,x-h ) / (2h)
107        >       u_xx = ( u(t,x+h) - 2u(t,x) + u(t,x-h) ) / h
108        
109        Jacobian
110        >       df/du = -a(t,p) * A + D(t,p) * B
111        >
112        >       where A is the first-derivative stencil [-1 0 1]
113        >       and B is the second-derivative stencil [1 -2 1] plus Robin boundary
114         
115        ## Takes           
116        **t**: Time
117        
118        **x**: State vector
119        
120        **h**: MOL interval size
121        
122        **driftFcn**
123        <br> Drift function, evaluated at t, driftP.
124        
125        **driftParams**
126        <br> Parameter vector for drift function.
127        
128        **diffusionFcn**
129        <br> Diffusion function, evaluated at t, driftP.
130        
131        **diffusionParams**
132        <br> Parameter vector for diffusion function.
133        
134
135        ## Returns
136        **dfdu**
137        <br> Sparse jacobian of FokkerPlanckODE (scipy.sparse).
138        """
139        # dimension
140        N = len(u)
141
142        # if dimension has changed, we have to reassemble the stencil
143        if N != self._NN:
144            e1 = np.ones(N)
145            e0 = np.zeros(N)
146            # 1st order stencil
147            self._A =  sparse.dia_matrix(( np.array([e1, e0, -e1]), [-1, 0, 1] ), shape=(N, N))
148            self._A = sparse.csr_matrix(self._A)   # change format to edit single entries
149            self._A[0  ,1  ] = 0
150            self._A[N-1,N-2] = 0
151            # 2nd order stencil + robin boundary
152            self._B = sparse.dia_matrix(( np.array([e1 , -2*e1 ,  e1]), [-1,0,1] ), shape=(N, N))
153            self._B = sparse.csr_matrix(self._B)
154            self._B[0  ,1  ] = 2
155            self._B[N-1,N-2] = 2
156            self._NN = N
157        
158        # evaluate drift and diffusion
159        alpha = driftFcn(t, driftParams)
160        D     = diffusionFcn(t, diffusionParams)
161
162        # asseble the jacobian
163        dfdu = -alpha * self._A / (2*h)  +  D * self._B / h**2
164        return dfdu
165
166    @staticmethod
167    def defaultDriftFcn(t,p):
168        """
169        Default drift function used in FPEX0.
170
171        ## Takes
172        **t**
173        <br> Current time / heating rate.
174
175        **p**
176        <br> Drift parameter vector (drift-parameters only!).
177
178
179        ## Returns
180        **fval**
181        <br> Function value for drift.
182        """
183
184        # linear drift parametrization
185        fval = p[0] + p[1] * t
186        return fval
187 
188    @staticmethod
189    def defaultDiffusionFcn(t,p,betamax):
190        """
191        Default diffusion function used in FPEX0.
192
193        ## Takes
194        **t**
195        <br> Current time / heating rate.
196
197        **p**
198        <br> Diffusion parameter vector (diffusion-parameters only!).
199
200        **betamax**
201        <br> Maximum time / heat rate (used for ensuring non-negativity).
202
203
204        ## Returns
205        **fval**
206        <br> Function value for diffusion.
207        """
208
209        # linear parametrization ensuring non-negativity for non-negative p1 and p2
210        fval = p[0] + t * (p[1] - p[0]) / betamax
211
212        return fval
class FokkerPlanck:
  5class FokkerPlanck:
  6    def __init__(self):
  7        self._NN         = None # variables for FokkerPlanckODE_dfdu()
  8        self._A          = None
  9        self._B          = None
 10
 11    @staticmethod
 12    def FokkerPlanckODE(t, u, h, driftFcn, driftParams, diffusionFcn, diffusionParams):
 13        """
 14        ODE RHS of Fokker-Planck PDE by using the method of lines (MOL) approach.
 15
 16        FP-PDE
 17        >       u_t = - a(t,p) * u_x(t,x)  +  D(t,p) * u_xx(t,x)
 18
 19        FD-Approx
 20        >       u_x  = ( u(t,x+h) - u(t,x-h ) / (2h)
 21        >       u_xx = ( u(t,x+h) - 2u(t,x) + u(t,x-h) ) / h
 22
 23        ## Takes           
 24        **t**: Time
 25
 26        **x**: State vector
 27        
 28        **h**: MOL interval size
 29        
 30        **driftFcn**
 31        <br> Drift function, evaluated at t, driftP.
 32        
 33        **driftParams**
 34        <br> Parameter vector for drift function.
 35        
 36        **diffusionFcn**
 37        <br> Diffusion function, evaluated at t, driftP.
 38        
 39        **diffusionParams**
 40        <br> Parameter vector for diffusion function.
 41
 42        ## Returns
 43        **dx**
 44        <br> rhs vector.
 45        """
 46    
 47        # number of grid points and number of simultaneously requested vectors
 48        shape = u.shape
 49        # shape is (N,) if u is a one-dim array
 50        if len(shape) > 1:
 51            (N, vectors) = shape
 52        else:
 53            N = shape[0]
 54            vectors = 1
 55
 56        # preallocate vectors for A*u (approx. of u_x) and B*u (approx. of u_xx)
 57        Bu = np.zeros_like(u)
 58        Au = np.zeros_like(u)
 59
 60        # numpy indexing is works differently for 1d and Nd arrays
 61        if vectors > 1:
 62            # first node
 63            Bu[0,:] = ( -2*u[0,:] + 2*u[1,:] ) / h**2
 64            # Au(1) is zero
 65
 66            # inner nodes (remember: python slicing is exclusive right)
 67            i = np.arange(1,N-1)
 68            Au[i,:] = ( u[i-1,:] - u[i+1,:] ) / (2*h)            # 1st derivative stencil and scale
 69            Bu[i,:] = ( u[i-1,:] - 2*u[i,:] + u[i+1,:] ) / h**2  # 2nd derivative stencil and scale
 70
 71            # last node
 72            Bu[N-1,:] = ( -2*u[N-1,:] + 2*u[N-2,:] ) / h**2
 73            # Au(N) is zero
 74        elif vectors == 1:
 75            # first node
 76            Bu[0] = ( -2*u[0] + 2*u[1] ) / h**2
 77            # Au(1) is zero
 78
 79            # inner nodes (remember: python slicing is exclusive right)
 80            i = np.arange(1,N-1)
 81            Au[i] = ( u[i-1] - u[i+1] ) / (2*h)           # 1st derivative stencil and scale
 82            Bu[i] = ( u[i-1] - 2*u[i] + u[i+1] ) / h**2   # 2nd derivative stencil and scale
 83
 84            # last node
 85            Bu[N-1] = ( -2*u[N-1] + 2*u[N-2] ) / h**2
 86            # Au[N-1] is zero      
 87
 88        # evaluate drift and diffusion
 89        alpha = driftFcn(t,driftParams)
 90        D = diffusionFcn(t,diffusionParams)
 91
 92        # assemble rhs
 93        dx = -alpha*Au + D*Bu
 94        return dx
 95
 96
 97    def FokkerPlanckODE_dfdu(self, t, u, h, driftFcn, driftParams, diffusionFcn, diffusionParams):
 98        """
 99        Jacobian of FokkerPlanckODE w.r.t. state u, i.e. df/du. 
100        
101        FP-PDE
102        >       u_t = - a(t,p) * u_x(t,x)  +  D(t,p) * u_xx(t,x)
103        >
104        >       where a is the driftFcn, and b is the diffusionFcn
105         
106        FD-Approx  
107        >       u_x  = ( u(t,x+h) - u(t,x-h ) / (2h)
108        >       u_xx = ( u(t,x+h) - 2u(t,x) + u(t,x-h) ) / h
109        
110        Jacobian
111        >       df/du = -a(t,p) * A + D(t,p) * B
112        >
113        >       where A is the first-derivative stencil [-1 0 1]
114        >       and B is the second-derivative stencil [1 -2 1] plus Robin boundary
115         
116        ## Takes           
117        **t**: Time
118        
119        **x**: State vector
120        
121        **h**: MOL interval size
122        
123        **driftFcn**
124        <br> Drift function, evaluated at t, driftP.
125        
126        **driftParams**
127        <br> Parameter vector for drift function.
128        
129        **diffusionFcn**
130        <br> Diffusion function, evaluated at t, driftP.
131        
132        **diffusionParams**
133        <br> Parameter vector for diffusion function.
134        
135
136        ## Returns
137        **dfdu**
138        <br> Sparse jacobian of FokkerPlanckODE (scipy.sparse).
139        """
140        # dimension
141        N = len(u)
142
143        # if dimension has changed, we have to reassemble the stencil
144        if N != self._NN:
145            e1 = np.ones(N)
146            e0 = np.zeros(N)
147            # 1st order stencil
148            self._A =  sparse.dia_matrix(( np.array([e1, e0, -e1]), [-1, 0, 1] ), shape=(N, N))
149            self._A = sparse.csr_matrix(self._A)   # change format to edit single entries
150            self._A[0  ,1  ] = 0
151            self._A[N-1,N-2] = 0
152            # 2nd order stencil + robin boundary
153            self._B = sparse.dia_matrix(( np.array([e1 , -2*e1 ,  e1]), [-1,0,1] ), shape=(N, N))
154            self._B = sparse.csr_matrix(self._B)
155            self._B[0  ,1  ] = 2
156            self._B[N-1,N-2] = 2
157            self._NN = N
158        
159        # evaluate drift and diffusion
160        alpha = driftFcn(t, driftParams)
161        D     = diffusionFcn(t, diffusionParams)
162
163        # asseble the jacobian
164        dfdu = -alpha * self._A / (2*h)  +  D * self._B / h**2
165        return dfdu
166
167    @staticmethod
168    def defaultDriftFcn(t,p):
169        """
170        Default drift function used in FPEX0.
171
172        ## Takes
173        **t**
174        <br> Current time / heating rate.
175
176        **p**
177        <br> Drift parameter vector (drift-parameters only!).
178
179
180        ## Returns
181        **fval**
182        <br> Function value for drift.
183        """
184
185        # linear drift parametrization
186        fval = p[0] + p[1] * t
187        return fval
188 
189    @staticmethod
190    def defaultDiffusionFcn(t,p,betamax):
191        """
192        Default diffusion function used in FPEX0.
193
194        ## Takes
195        **t**
196        <br> Current time / heating rate.
197
198        **p**
199        <br> Diffusion parameter vector (diffusion-parameters only!).
200
201        **betamax**
202        <br> Maximum time / heat rate (used for ensuring non-negativity).
203
204
205        ## Returns
206        **fval**
207        <br> Function value for diffusion.
208        """
209
210        # linear parametrization ensuring non-negativity for non-negative p1 and p2
211        fval = p[0] + t * (p[1] - p[0]) / betamax
212
213        return fval
FokkerPlanck()
6    def __init__(self):
7        self._NN         = None # variables for FokkerPlanckODE_dfdu()
8        self._A          = None
9        self._B          = None
@staticmethod
def FokkerPlanckODE(t, u, h, driftFcn, driftParams, diffusionFcn, diffusionParams):
11    @staticmethod
12    def FokkerPlanckODE(t, u, h, driftFcn, driftParams, diffusionFcn, diffusionParams):
13        """
14        ODE RHS of Fokker-Planck PDE by using the method of lines (MOL) approach.
15
16        FP-PDE
17        >       u_t = - a(t,p) * u_x(t,x)  +  D(t,p) * u_xx(t,x)
18
19        FD-Approx
20        >       u_x  = ( u(t,x+h) - u(t,x-h ) / (2h)
21        >       u_xx = ( u(t,x+h) - 2u(t,x) + u(t,x-h) ) / h
22
23        ## Takes           
24        **t**: Time
25
26        **x**: State vector
27        
28        **h**: MOL interval size
29        
30        **driftFcn**
31        <br> Drift function, evaluated at t, driftP.
32        
33        **driftParams**
34        <br> Parameter vector for drift function.
35        
36        **diffusionFcn**
37        <br> Diffusion function, evaluated at t, driftP.
38        
39        **diffusionParams**
40        <br> Parameter vector for diffusion function.
41
42        ## Returns
43        **dx**
44        <br> rhs vector.
45        """
46    
47        # number of grid points and number of simultaneously requested vectors
48        shape = u.shape
49        # shape is (N,) if u is a one-dim array
50        if len(shape) > 1:
51            (N, vectors) = shape
52        else:
53            N = shape[0]
54            vectors = 1
55
56        # preallocate vectors for A*u (approx. of u_x) and B*u (approx. of u_xx)
57        Bu = np.zeros_like(u)
58        Au = np.zeros_like(u)
59
60        # numpy indexing is works differently for 1d and Nd arrays
61        if vectors > 1:
62            # first node
63            Bu[0,:] = ( -2*u[0,:] + 2*u[1,:] ) / h**2
64            # Au(1) is zero
65
66            # inner nodes (remember: python slicing is exclusive right)
67            i = np.arange(1,N-1)
68            Au[i,:] = ( u[i-1,:] - u[i+1,:] ) / (2*h)            # 1st derivative stencil and scale
69            Bu[i,:] = ( u[i-1,:] - 2*u[i,:] + u[i+1,:] ) / h**2  # 2nd derivative stencil and scale
70
71            # last node
72            Bu[N-1,:] = ( -2*u[N-1,:] + 2*u[N-2,:] ) / h**2
73            # Au(N) is zero
74        elif vectors == 1:
75            # first node
76            Bu[0] = ( -2*u[0] + 2*u[1] ) / h**2
77            # Au(1) is zero
78
79            # inner nodes (remember: python slicing is exclusive right)
80            i = np.arange(1,N-1)
81            Au[i] = ( u[i-1] - u[i+1] ) / (2*h)           # 1st derivative stencil and scale
82            Bu[i] = ( u[i-1] - 2*u[i] + u[i+1] ) / h**2   # 2nd derivative stencil and scale
83
84            # last node
85            Bu[N-1] = ( -2*u[N-1] + 2*u[N-2] ) / h**2
86            # Au[N-1] is zero      
87
88        # evaluate drift and diffusion
89        alpha = driftFcn(t,driftParams)
90        D = diffusionFcn(t,diffusionParams)
91
92        # assemble rhs
93        dx = -alpha*Au + D*Bu
94        return dx

ODE RHS of Fokker-Planck PDE by using the method of lines (MOL) approach.

FP-PDE

  u_t = - a(t,p) * u_x(t,x)  +  D(t,p) * u_xx(t,x)

FD-Approx

  u_x  = ( u(t,x+h) - u(t,x-h ) / (2h)
  u_xx = ( u(t,x+h) - 2u(t,x) + u(t,x-h) ) / h

Takes

t: Time

x: State vector

h: MOL interval size

driftFcn
Drift function, evaluated at t, driftP.

driftParams
Parameter vector for drift function.

diffusionFcn
Diffusion function, evaluated at t, driftP.

diffusionParams
Parameter vector for diffusion function.

Returns

dx
rhs vector.

def FokkerPlanckODE_dfdu(self, t, u, h, driftFcn, driftParams, diffusionFcn, diffusionParams):
 97    def FokkerPlanckODE_dfdu(self, t, u, h, driftFcn, driftParams, diffusionFcn, diffusionParams):
 98        """
 99        Jacobian of FokkerPlanckODE w.r.t. state u, i.e. df/du. 
100        
101        FP-PDE
102        >       u_t = - a(t,p) * u_x(t,x)  +  D(t,p) * u_xx(t,x)
103        >
104        >       where a is the driftFcn, and b is the diffusionFcn
105         
106        FD-Approx  
107        >       u_x  = ( u(t,x+h) - u(t,x-h ) / (2h)
108        >       u_xx = ( u(t,x+h) - 2u(t,x) + u(t,x-h) ) / h
109        
110        Jacobian
111        >       df/du = -a(t,p) * A + D(t,p) * B
112        >
113        >       where A is the first-derivative stencil [-1 0 1]
114        >       and B is the second-derivative stencil [1 -2 1] plus Robin boundary
115         
116        ## Takes           
117        **t**: Time
118        
119        **x**: State vector
120        
121        **h**: MOL interval size
122        
123        **driftFcn**
124        <br> Drift function, evaluated at t, driftP.
125        
126        **driftParams**
127        <br> Parameter vector for drift function.
128        
129        **diffusionFcn**
130        <br> Diffusion function, evaluated at t, driftP.
131        
132        **diffusionParams**
133        <br> Parameter vector for diffusion function.
134        
135
136        ## Returns
137        **dfdu**
138        <br> Sparse jacobian of FokkerPlanckODE (scipy.sparse).
139        """
140        # dimension
141        N = len(u)
142
143        # if dimension has changed, we have to reassemble the stencil
144        if N != self._NN:
145            e1 = np.ones(N)
146            e0 = np.zeros(N)
147            # 1st order stencil
148            self._A =  sparse.dia_matrix(( np.array([e1, e0, -e1]), [-1, 0, 1] ), shape=(N, N))
149            self._A = sparse.csr_matrix(self._A)   # change format to edit single entries
150            self._A[0  ,1  ] = 0
151            self._A[N-1,N-2] = 0
152            # 2nd order stencil + robin boundary
153            self._B = sparse.dia_matrix(( np.array([e1 , -2*e1 ,  e1]), [-1,0,1] ), shape=(N, N))
154            self._B = sparse.csr_matrix(self._B)
155            self._B[0  ,1  ] = 2
156            self._B[N-1,N-2] = 2
157            self._NN = N
158        
159        # evaluate drift and diffusion
160        alpha = driftFcn(t, driftParams)
161        D     = diffusionFcn(t, diffusionParams)
162
163        # asseble the jacobian
164        dfdu = -alpha * self._A / (2*h)  +  D * self._B / h**2
165        return dfdu

Jacobian of FokkerPlanckODE w.r.t. state u, i.e. df/du.

FP-PDE

  u_t = - a(t,p) * u_x(t,x)  +  D(t,p) * u_xx(t,x)

  where a is the driftFcn, and b is the diffusionFcn

FD-Approx

  u_x  = ( u(t,x+h) - u(t,x-h ) / (2h)
  u_xx = ( u(t,x+h) - 2u(t,x) + u(t,x-h) ) / h

Jacobian

  df/du = -a(t,p) * A + D(t,p) * B

  where A is the first-derivative stencil [-1 0 1]
  and B is the second-derivative stencil [1 -2 1] plus Robin boundary

Takes

t: Time

x: State vector

h: MOL interval size

driftFcn
Drift function, evaluated at t, driftP.

driftParams
Parameter vector for drift function.

diffusionFcn
Diffusion function, evaluated at t, driftP.

diffusionParams
Parameter vector for diffusion function.

Returns

dfdu
Sparse jacobian of FokkerPlanckODE (scipy.sparse).

@staticmethod
def defaultDriftFcn(t, p):
167    @staticmethod
168    def defaultDriftFcn(t,p):
169        """
170        Default drift function used in FPEX0.
171
172        ## Takes
173        **t**
174        <br> Current time / heating rate.
175
176        **p**
177        <br> Drift parameter vector (drift-parameters only!).
178
179
180        ## Returns
181        **fval**
182        <br> Function value for drift.
183        """
184
185        # linear drift parametrization
186        fval = p[0] + p[1] * t
187        return fval

Default drift function used in FPEX0.

Takes

t
Current time / heating rate.

p
Drift parameter vector (drift-parameters only!).

Returns

fval
Function value for drift.

@staticmethod
def defaultDiffusionFcn(t, p, betamax):
189    @staticmethod
190    def defaultDiffusionFcn(t,p,betamax):
191        """
192        Default diffusion function used in FPEX0.
193
194        ## Takes
195        **t**
196        <br> Current time / heating rate.
197
198        **p**
199        <br> Diffusion parameter vector (diffusion-parameters only!).
200
201        **betamax**
202        <br> Maximum time / heat rate (used for ensuring non-negativity).
203
204
205        ## Returns
206        **fval**
207        <br> Function value for diffusion.
208        """
209
210        # linear parametrization ensuring non-negativity for non-negative p1 and p2
211        fval = p[0] + t * (p[1] - p[0]) / betamax
212
213        return fval

Default diffusion function used in FPEX0.

Takes

t
Current time / heating rate.

p
Diffusion parameter vector (diffusion-parameters only!).

betamax
Maximum time / heat rate (used for ensuring non-negativity).

Returns

fval
Function value for diffusion.