Xyce  6.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
N_DEV_MembraneHH.C
Go to the documentation of this file.
1 //-----------------------------------------------------------------------------
2 // Copyright Notice
3 //
4 // Copyright 2002 Sandia Corporation. Under the terms
5 // of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S.
6 // Government retains certain rights in this software.
7 //
8 // Xyce(TM) Parallel Electrical Simulator
9 // Copyright (C) 2002-2011 Sandia Corporation
10 //
11 // This program is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 //-----------------------------------------------------------------------------
24 
25 //-------------------------------------------------------------------------
26 // Filename : $RCSfile: N_DEV_MembraneHH.C,v $
27 //
28 // Purpose :
29 //
30 // Special Notes :
31 //
32 // Creator : Richard Schiek, Electrical and Microsytem Modeling
33 //
34 // Creation Date : 08/11/2010
35 //
36 // Revision Information:
37 // ---------------------
38 //
39 // Revision Number: $Revision: 1.16 $
40 //
41 // Revision Date : $Date: 2014/01/23 16:19:04 $
42 //
43 // Current Owner : $Author: dgbaur $
44 //-------------------------------------------------------------------------
45 
46 #include <Xyce_config.h>
47 
48 
49 // ---------- Standard Includes ----------
50 
51 #include <N_UTL_Misc.h>
52 
53 // ---------- Xyce Includes ----------
55 #include <N_DEV_MembraneHH.h>
56 #include <N_LAS_Vector.h>
57 #include <N_LAS_Matrix.h>
58 #include <N_DEV_SolverState.h>
59 
60 namespace Xyce {
61 namespace Device {
62 
63 //-----------------------------------------------------------------------------
64 // Function : MembraneHH::MembraneHH
65 // Purpose :
66 // Special Notes :
67 // Scope : public
68 // Creator : Richard Schiek, Electrical and Microsytem Modeling
69 // Creation Date : 08/11/2010
70 //-----------------------------------------------------------------------------
71 MembraneHH::MembraneHH (const SolverState & ss1, double cMem, double gMem, double vRest, double eK, double gK, double eNa, double gNa):
72  MembraneModel(ss1), gMem_(gMem), cMem_(cMem), vRest_(vRest), eK_(eK), gK_(gK), eNa_(eNa), gNa_(gNa)
73 {
74  // Hodgkin-Huxley has the following unknows for the membrane
75  // 1. voltage
76  // 2. n
77  // 3. m
78  // 4. h
80 }
81 
82 
83 //-----------------------------------------------------------------------------
84 // Function : MembraneHH::setJacStamp
85 // Purpose :
86 // Special Notes :
87 // Scope : public
88 // Creator : Richard Schiek, Electrical and Microsytem Modeling
89 // Creation Date : 08/11/2010
90 //-----------------------------------------------------------------------------
91 void MembraneHH::setJacStamp( int numExtVars, int segmentNumber, int vOffset, std::vector< std::vector< int > > & segmentJacStamp )
92 {
93  // caller sets up size of row of jac stamp to numIndependentVars_ + extra's needed for
94  // its modeling. So for a cable based device this is Vpre, Vseg, (other membrane vars), Vnext.
95  // in general this is numIndependentVars_ + 2 (Vpre and Vnext). In this routine we fill in
96  // just what is needed for the membrane model
97 
98  int offset = numExtVars + numIndependentVars_*segmentNumber;
99  int jacobianRowSize = segmentJacStamp[offset].size();
100 
101  // note, any external vars come before the internal vars. Dependance on Vprev and Vnext (if
102  // they are really there are handled by the caller as in a cable equation model.
103 
104  // Jacobian strcuture:
105  // Vpre V n m h Vnext
106  // kcl yes yes yes yes yes yes
107  // n-equ yes yes
108  // m-equ yes yes
109  // h-equ yes yes
110 
111  // membrane voltage equation.
112  segmentJacStamp[offset][vOffset ] = offset; // V this should already have been set
113  segmentJacStamp[offset][vOffset + 1] = offset+1; // n
114  segmentJacStamp[offset][vOffset + 2] = offset+2; // m
115  segmentJacStamp[offset][vOffset + 3] = offset+3; // h
116 
117  // n equation
118  segmentJacStamp[offset+1].resize(2);
119  segmentJacStamp[offset+1][0] = offset;
120  segmentJacStamp[offset+1][1] = offset+1;
121 
122  // m equation
123  segmentJacStamp[offset+2].resize(2);
124  segmentJacStamp[offset+2][0] = offset;
125  segmentJacStamp[offset+2][1] = offset+2;
126 
127  // h equation
128  segmentJacStamp[offset+3].resize(2);
129  segmentJacStamp[offset+3][0] = offset;
130  segmentJacStamp[offset+3][1] = offset+3;
131 
132 }
133 
134 //-----------------------------------------------------------------------------
135 // Function : MembraneHH::loadDAEQVector
136 // Purpose :
137 // Special Notes :
138 // Scope : public
139 // Creator : Richard Schiek, Electrical and Microsytem Modeling
140 // Creation Date : 08/11/2010
141 //-----------------------------------------------------------------------------
142 void MembraneHH::loadDAEQVector( int segmentNumber, std::vector< int > & lidIndexVector, N_LAS_Vector * solnVecPtr, N_LAS_Vector * daeQVecPtr, double segArea)
143 {
144  // Each segment will have numIndependentVars_ with segment voltage being the first
145  // so, the cMem dV/dt term will be at segmentNumber * numIndependentVars_.
146  int index = segmentNumber * numIndependentVars_;
147  (*daeQVecPtr)[lidIndexVector[index]] += cMem_ * segArea * (*solnVecPtr)[lidIndexVector[index]];
148  // n equation
149  (*daeQVecPtr)[lidIndexVector[index + 1]] += (*solnVecPtr)[lidIndexVector[index + 1]];
150  // m equation
151  (*daeQVecPtr)[lidIndexVector[index + 2]] += (*solnVecPtr)[lidIndexVector[index + 2]];
152  // h equation
153  (*daeQVecPtr)[lidIndexVector[index + 3]] += (*solnVecPtr)[lidIndexVector[index + 3]];
154 }
155 
156 //-----------------------------------------------------------------------------
157 // Function : MembraneHH::loadDAEFVector
158 // Purpose :
159 // Special Notes :
160 // Scope : public
161 // Creator : Richard Schiek, Electrical and Microsytem Modeling
162 // Creation Date : 08/11/2010
163 //-----------------------------------------------------------------------------
164 void MembraneHH::loadDAEFVector( int segmentNumber, std::vector< int > & lidIndexVector, N_LAS_Vector * solnVecPtr, N_LAS_Vector * daeFVecPtr, double segArea)
165 {
166 // Each segment will have numIndependentVars_ with segment voltage being the first
167  // so, the cMem dV/dt term will be at segmentNumber * numIndependentVars_.
168 
169  int index = segmentNumber * numIndependentVars_;
170  double vSeg = (*solnVecPtr)[lidIndexVector[index]];
171  double n = (*solnVecPtr)[lidIndexVector[index + 1]];
172  double m = (*solnVecPtr)[lidIndexVector[index + 2]];
173  double h = (*solnVecPtr)[lidIndexVector[index + 3]];
174 
175  // membrane current equation
176  (*daeFVecPtr)[lidIndexVector[index]] += Neuron::HH_Vseg_F<double>(
177  vSeg, n, m, h, (gMem_ * segArea), vRest_, (gK_ * segArea), eK_,
178  (gNa_ * segArea), eNa_ );
179 
180  // cew 2/18/11 - changed the functions used below to use voltage shifted by resting potential
181  // n equation
182  (*daeFVecPtr)[lidIndexVector[index + 1]] += Neuron::nEquF<double>( vSeg-vRest_, n );
183 
184  // m equation
185  (*daeFVecPtr)[lidIndexVector[index + 2]] += Neuron::mEquF<double>( vSeg-vRest_, m );
186 
187  // h equation
188  (*daeFVecPtr)[lidIndexVector[index + 3]] += Neuron::hEquF<double>( vSeg-vRest_, h );
189 
190 }
191 
192 //-----------------------------------------------------------------------------
193 // Function : MembraneHH::loadDAEdQdx
194 // Purpose :
195 // Special Notes :
196 // Scope : public
197 // Creator : Richard Schiek, Electrical and Microsytem Modeling
198 // Creation Date : 08/11/2010
199 //-----------------------------------------------------------------------------
200 void MembraneHH::loadDAEdQdx( int segmentNumber, int vOffset, std::vector< int > & lidIndexVector,
201  std::vector< std::vector< int > > & jacobianOffsets,
202  N_LAS_Vector * solnVecPtr,
203  N_LAS_Matrix * dQdxMatPtr,
204  double segArea)
205 {
206  // while lidIndexVector lists LID's for just the segment variables (just V in the case
207  // of a passive cable). The jacobianOffsets includes the Vin and Vout as the first
208  // two variables. Thus, there is a constant offset of 2 for everything in jacobianOffsets
209 
210  // And, as in the Q and F load functions, Each segment will have numIndependentVars_ with segment voltage being the first
211  // so, the cMem dV/dt term will be at segmentNumber * numIndependentVars_.
212  // in the case of the passive cable numIndependentVars_=1.
213 
214  int index = segmentNumber * numIndependentVars_;
215  int row = numExternalVars_ + index; // numExternalVars_ a contant of 2 assumed in MembraneModel base class
216 
217  // Vseg equation
218  (*dQdxMatPtr)[lidIndexVector[index]][jacobianOffsets[row][vOffset]] += cMem_ * segArea;
219 
220  // for internal variables remember that order if Vseg, n, m, h
221 
222  // n equation
223  (*dQdxMatPtr)[lidIndexVector[index + 1]][jacobianOffsets[row + 1][1]] += 1;
224 
225  // m equation
226  (*dQdxMatPtr)[lidIndexVector[index + 2]][jacobianOffsets[row + 2][1]] += 1;
227 
228  // h equation
229  (*dQdxMatPtr)[lidIndexVector[index + 3]][jacobianOffsets[row + 3][1]] += 1;
230 }
231 
232 //-----------------------------------------------------------------------------
233 // Function : MembraneHH::loadDAEdFdx
234 // Purpose :
235 // Special Notes :
236 // Scope : public
237 // Creator : Richard Schiek, Electrical and Microsytem Modeling
238 // Creation Date : 08/11/2010
239 //-----------------------------------------------------------------------------
240 void MembraneHH::loadDAEdFdx( int segmentNumber, int vOffset, std::vector< int > & lidIndexVector,
241  std::vector< std::vector< int > > & jacobianOffsets,
242  N_LAS_Vector * solnVecPtr,
243  N_LAS_Matrix * dFdxMatPtr,
244  double segArea)
245 {
246  // while lidIndexVector lists LID's for just the segment variables (just V in the case
247  // of a passive cable). The jacobianOffsets includes the Vin and Vout as the first
248  // two variables. Thus, there is a constant offset of 2 for everything in jacobianOffsets
249 
250  // And, as in the Q and F load functions, Each segment will have numIndependentVars_ with segment voltage being the first
251  // so, the cMem dV/dt term will be at segmentNumber * numIndependentVars_.
252  // in the case of the passive cable numIndependentVars_=1.
253 
254  int index = segmentNumber * numIndependentVars_;
255  int row = numExternalVars_ + index; // numExternalVars_ a contant of 2 assumed in MembraneModel base class
256 
257  double vSeg = (*solnVecPtr)[lidIndexVector[index]];
258  double n = (*solnVecPtr)[lidIndexVector[index + 1]];
259  double m = (*solnVecPtr)[lidIndexVector[index + 2]];
260  double h = (*solnVecPtr)[lidIndexVector[index + 3]];
261 
262  // Since the Sacado types for auto differentiation are templated on the number of
263  // derivatives needed, use scoping brackets here so I can safely reuse some variable names
264 
265  // Vseg equation
266  {
267  Sacado::Fad::SFad<double,4> vVar( 4, 0, vSeg );
268  Sacado::Fad::SFad<double,4> nVar( 4, 1, n );
269  Sacado::Fad::SFad<double,4> mVar( 4, 2, m );
270  Sacado::Fad::SFad<double,4> hVar( 4, 3, h );
271  // parameters
272  Sacado::Fad::SFad<double,4> gMemVar( gMem_ * segArea );
273  Sacado::Fad::SFad<double,4> vRestVar( vRest_ );
274  Sacado::Fad::SFad<double,4> gKVar( gK_ * segArea );
275  Sacado::Fad::SFad<double,4> eKVar( eK_ );
276  Sacado::Fad::SFad<double,4> gNaVar( gNa_ * segArea );
277  Sacado::Fad::SFad<double,4> eNaVar( eNa_ );
278 
279  Sacado::Fad::SFad<double,4> resultFad;
280  resultFad = Neuron::HH_Vseg_F( vVar, nVar, mVar, hVar, gMemVar, vRestVar, gKVar, eKVar, gNaVar, eNaVar );
281 
282  (*dFdxMatPtr)[lidIndexVector[index]][jacobianOffsets[row][vOffset]] += resultFad.dx(0); // /dVseg
283  (*dFdxMatPtr)[lidIndexVector[index]][jacobianOffsets[row][vOffset+1]] += resultFad.dx(1); // /dn
284  (*dFdxMatPtr)[lidIndexVector[index]][jacobianOffsets[row][vOffset+2]] += resultFad.dx(2); // /dm
285  (*dFdxMatPtr)[lidIndexVector[index]][jacobianOffsets[row][vOffset+3]] += resultFad.dx(3); // /dh
286  }
287 
288  // cew 2/18/11 - changed the functions used below to use voltage shifted by resting potential
289  // n equation
290  {
291  Sacado::Fad::SFad<double,2> vVar( 2, 0, vSeg-vRest_ );
292  Sacado::Fad::SFad<double,2> nVar( 2, 1, n );
293  Sacado::Fad::SFad<double,2> nEquResultFad = Neuron::nEquF( vVar, nVar );
294 
295  (*dFdxMatPtr)[lidIndexVector[index + 1]][jacobianOffsets[row + 1][0]] += nEquResultFad.dx(0); // dVseg
296  (*dFdxMatPtr)[lidIndexVector[index + 1]][jacobianOffsets[row + 1][1]] += nEquResultFad.dx(1); // dn
297  }
298 
299  // m equation
300  {
301  Sacado::Fad::SFad<double,2> vVar( 2, 0, vSeg-vRest_ );
302  Sacado::Fad::SFad<double,2> mVar( 2, 1, m );
303  Sacado::Fad::SFad<double,2> mEquResultFad = Neuron::mEquF( vVar, mVar );
304 
305  (*dFdxMatPtr)[lidIndexVector[index + 2]][jacobianOffsets[row + 2][0]] += mEquResultFad.dx(0); // dVseg
306  (*dFdxMatPtr)[lidIndexVector[index + 2]][jacobianOffsets[row + 2][1]] += mEquResultFad.dx(1); // dm
307  }
308 
309  // h equation
310  {
311  Sacado::Fad::SFad<double,2> vVar( 2, 0, vSeg-vRest_ );
312  Sacado::Fad::SFad<double,2> hVar( 2, 1, h );
313  Sacado::Fad::SFad<double,2> hEquResultFad = Neuron::hEquF( vVar, hVar );
314 
315  (*dFdxMatPtr)[lidIndexVector[index + 3]][jacobianOffsets[row + 3][0]] += hEquResultFad.dx(0); // dVseg
316  (*dFdxMatPtr)[lidIndexVector[index + 3]][jacobianOffsets[row + 3][1]] += hEquResultFad.dx(1); // dh
317  }
318 
319 }
320 
321 } // namespace Device
322 } // namespace Xyce