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
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
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.
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).
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.
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.