Xyce  6.1
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-2015 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.18.2.1 $
40 //
41 // Revision Date : $Date: 2015/04/02 18:29:37 $
42 //
43 // Current Owner : $Author: tvrusso $
44 //-------------------------------------------------------------------------
45 
46 #include <Xyce_config.h>
47 
48 
49 // ---------- Standard Includes ----------
50 
51 
52 // ---------- Xyce Includes ----------
54 #include <N_DEV_MembraneHH.h>
55 #include <N_LAS_Vector.h>
56 #include <N_LAS_Matrix.h>
57 #include <N_DEV_SolverState.h>
58 
59 namespace Xyce {
60 namespace Device {
61 
62 //-----------------------------------------------------------------------------
63 // Function : MembraneHH::MembraneHH
64 // Purpose :
65 // Special Notes :
66 // Scope : public
67 // Creator : Richard Schiek, Electrical and Microsytem Modeling
68 // Creation Date : 08/11/2010
69 //-----------------------------------------------------------------------------
70 MembraneHH::MembraneHH (const SolverState & ss1, double cMem, double gMem, double vRest, double eK, double gK, double eNa, double gNa):
71  MembraneModel(ss1), gMem_(gMem), cMem_(cMem), vRest_(vRest), eK_(eK), gK_(gK), eNa_(eNa), gNa_(gNa)
72 {
73  // Hodgkin-Huxley has the following unknows for the membrane
74  // 1. voltage
75  // 2. n
76  // 3. m
77  // 4. h
79 }
80 
81 
82 //-----------------------------------------------------------------------------
83 // Function : MembraneHH::setJacStamp
84 // Purpose :
85 // Special Notes :
86 // Scope : public
87 // Creator : Richard Schiek, Electrical and Microsytem Modeling
88 // Creation Date : 08/11/2010
89 //-----------------------------------------------------------------------------
90 void MembraneHH::setJacStamp( int numExtVars, int segmentNumber, int vOffset, std::vector< std::vector< int > > & segmentJacStamp )
91 {
92  // caller sets up size of row of jac stamp to numIndependentVars_ + extra's needed for
93  // its modeling. So for a cable based device this is Vpre, Vseg, (other membrane vars), Vnext.
94  // in general this is numIndependentVars_ + 2 (Vpre and Vnext). In this routine we fill in
95  // just what is needed for the membrane model
96 
97  int offset = numExtVars + numIndependentVars_*segmentNumber;
98  int jacobianRowSize = segmentJacStamp[offset].size();
99 
100  // note, any external vars come before the internal vars. Dependance on Vprev and Vnext (if
101  // they are really there are handled by the caller as in a cable equation model.
102 
103  // Jacobian strcuture:
104  // Vpre V n m h Vnext
105  // kcl yes yes yes yes yes yes
106  // n-equ yes yes
107  // m-equ yes yes
108  // h-equ yes yes
109 
110  // membrane voltage equation.
111  segmentJacStamp[offset][vOffset ] = offset; // V this should already have been set
112  segmentJacStamp[offset][vOffset + 1] = offset+1; // n
113  segmentJacStamp[offset][vOffset + 2] = offset+2; // m
114  segmentJacStamp[offset][vOffset + 3] = offset+3; // h
115 
116  // n equation
117  segmentJacStamp[offset+1].resize(2);
118  segmentJacStamp[offset+1][0] = offset;
119  segmentJacStamp[offset+1][1] = offset+1;
120 
121  // m equation
122  segmentJacStamp[offset+2].resize(2);
123  segmentJacStamp[offset+2][0] = offset;
124  segmentJacStamp[offset+2][1] = offset+2;
125 
126  // h equation
127  segmentJacStamp[offset+3].resize(2);
128  segmentJacStamp[offset+3][0] = offset;
129  segmentJacStamp[offset+3][1] = offset+3;
130 
131 }
132 
133 //-----------------------------------------------------------------------------
134 // Function : MembraneHH::loadDAEQVector
135 // Purpose :
136 // Special Notes :
137 // Scope : public
138 // Creator : Richard Schiek, Electrical and Microsytem Modeling
139 // Creation Date : 08/11/2010
140 //-----------------------------------------------------------------------------
141 void MembraneHH::loadDAEQVector( int segmentNumber, std::vector< int > & lidIndexVector, Linear::Vector * solnVecPtr, Linear::Vector * daeQVecPtr, double segArea)
142 {
143  // Each segment will have numIndependentVars_ with segment voltage being the first
144  // so, the cMem dV/dt term will be at segmentNumber * numIndependentVars_.
145  int index = segmentNumber * numIndependentVars_;
146  (*daeQVecPtr)[lidIndexVector[index]] += cMem_ * segArea * (*solnVecPtr)[lidIndexVector[index]];
147  // n equation
148  (*daeQVecPtr)[lidIndexVector[index + 1]] += (*solnVecPtr)[lidIndexVector[index + 1]];
149  // m equation
150  (*daeQVecPtr)[lidIndexVector[index + 2]] += (*solnVecPtr)[lidIndexVector[index + 2]];
151  // h equation
152  (*daeQVecPtr)[lidIndexVector[index + 3]] += (*solnVecPtr)[lidIndexVector[index + 3]];
153 }
154 
155 //-----------------------------------------------------------------------------
156 // Function : MembraneHH::loadDAEFVector
157 // Purpose :
158 // Special Notes :
159 // Scope : public
160 // Creator : Richard Schiek, Electrical and Microsytem Modeling
161 // Creation Date : 08/11/2010
162 //-----------------------------------------------------------------------------
163 void MembraneHH::loadDAEFVector( int segmentNumber, std::vector< int > & lidIndexVector, Linear::Vector * solnVecPtr, Linear::Vector * daeFVecPtr, double segArea)
164 {
165 // Each segment will have numIndependentVars_ with segment voltage being the first
166  // so, the cMem dV/dt term will be at segmentNumber * numIndependentVars_.
167 
168  int index = segmentNumber * numIndependentVars_;
169  double vSeg = (*solnVecPtr)[lidIndexVector[index]];
170  double n = (*solnVecPtr)[lidIndexVector[index + 1]];
171  double m = (*solnVecPtr)[lidIndexVector[index + 2]];
172  double h = (*solnVecPtr)[lidIndexVector[index + 3]];
173 
174  // membrane current equation
175  (*daeFVecPtr)[lidIndexVector[index]] += Neuron::HH_Vseg_F<double>(
176  vSeg, n, m, h, (gMem_ * segArea), vRest_, (gK_ * segArea), eK_,
177  (gNa_ * segArea), eNa_ );
178 
179  // cew 2/18/11 - changed the functions used below to use voltage shifted by resting potential
180  // n equation
181  (*daeFVecPtr)[lidIndexVector[index + 1]] += Neuron::nEquF<double>( vSeg-vRest_, n );
182 
183  // m equation
184  (*daeFVecPtr)[lidIndexVector[index + 2]] += Neuron::mEquF<double>( vSeg-vRest_, m );
185 
186  // h equation
187  (*daeFVecPtr)[lidIndexVector[index + 3]] += Neuron::hEquF<double>( vSeg-vRest_, h );
188 
189 }
190 
191 //-----------------------------------------------------------------------------
192 // Function : MembraneHH::loadDAEdQdx
193 // Purpose :
194 // Special Notes :
195 // Scope : public
196 // Creator : Richard Schiek, Electrical and Microsytem Modeling
197 // Creation Date : 08/11/2010
198 //-----------------------------------------------------------------------------
199 void MembraneHH::loadDAEdQdx( int segmentNumber, int vOffset, std::vector< int > & lidIndexVector,
200  std::vector< std::vector< int > > & jacobianOffsets,
201  Linear::Vector * solnVecPtr,
202  Linear::Matrix * dQdxMatPtr,
203  double segArea)
204 {
205  // while lidIndexVector lists LID's for just the segment variables (just V in the case
206  // of a passive cable). The jacobianOffsets includes the Vin and Vout as the first
207  // two variables. Thus, there is a constant offset of 2 for everything in jacobianOffsets
208 
209  // And, as in the Q and F load functions, Each segment will have numIndependentVars_ with segment voltage being the first
210  // so, the cMem dV/dt term will be at segmentNumber * numIndependentVars_.
211  // in the case of the passive cable numIndependentVars_=1.
212 
213  int index = segmentNumber * numIndependentVars_;
214  int row = numExternalVars_ + index; // numExternalVars_ a contant of 2 assumed in MembraneModel base class
215 
216  // Vseg equation
217  (*dQdxMatPtr)[lidIndexVector[index]][jacobianOffsets[row][vOffset]] += cMem_ * segArea;
218 
219  // for internal variables remember that order if Vseg, n, m, h
220 
221  // n equation
222  (*dQdxMatPtr)[lidIndexVector[index + 1]][jacobianOffsets[row + 1][1]] += 1;
223 
224  // m equation
225  (*dQdxMatPtr)[lidIndexVector[index + 2]][jacobianOffsets[row + 2][1]] += 1;
226 
227  // h equation
228  (*dQdxMatPtr)[lidIndexVector[index + 3]][jacobianOffsets[row + 3][1]] += 1;
229 }
230 
231 //-----------------------------------------------------------------------------
232 // Function : MembraneHH::loadDAEdFdx
233 // Purpose :
234 // Special Notes :
235 // Scope : public
236 // Creator : Richard Schiek, Electrical and Microsytem Modeling
237 // Creation Date : 08/11/2010
238 //-----------------------------------------------------------------------------
239 void MembraneHH::loadDAEdFdx( int segmentNumber, int vOffset, std::vector< int > & lidIndexVector,
240  std::vector< std::vector< int > > & jacobianOffsets,
241  Linear::Vector * solnVecPtr,
242  Linear::Matrix * dFdxMatPtr,
243  double segArea)
244 {
245  // while lidIndexVector lists LID's for just the segment variables (just V in the case
246  // of a passive cable). The jacobianOffsets includes the Vin and Vout as the first
247  // two variables. Thus, there is a constant offset of 2 for everything in jacobianOffsets
248 
249  // And, as in the Q and F load functions, Each segment will have numIndependentVars_ with segment voltage being the first
250  // so, the cMem dV/dt term will be at segmentNumber * numIndependentVars_.
251  // in the case of the passive cable numIndependentVars_=1.
252 
253  int index = segmentNumber * numIndependentVars_;
254  int row = numExternalVars_ + index; // numExternalVars_ a contant of 2 assumed in MembraneModel base class
255 
256  double vSeg = (*solnVecPtr)[lidIndexVector[index]];
257  double n = (*solnVecPtr)[lidIndexVector[index + 1]];
258  double m = (*solnVecPtr)[lidIndexVector[index + 2]];
259  double h = (*solnVecPtr)[lidIndexVector[index + 3]];
260 
261  // Since the Sacado types for auto differentiation are templated on the number of
262  // derivatives needed, use scoping brackets here so I can safely reuse some variable names
263 
264  // Vseg equation
265  {
266  Sacado::Fad::SFad<double,4> vVar( 4, 0, vSeg );
267  Sacado::Fad::SFad<double,4> nVar( 4, 1, n );
268  Sacado::Fad::SFad<double,4> mVar( 4, 2, m );
269  Sacado::Fad::SFad<double,4> hVar( 4, 3, h );
270  // parameters
271  Sacado::Fad::SFad<double,4> gMemVar( gMem_ * segArea );
272  Sacado::Fad::SFad<double,4> vRestVar( vRest_ );
273  Sacado::Fad::SFad<double,4> gKVar( gK_ * segArea );
274  Sacado::Fad::SFad<double,4> eKVar( eK_ );
275  Sacado::Fad::SFad<double,4> gNaVar( gNa_ * segArea );
276  Sacado::Fad::SFad<double,4> eNaVar( eNa_ );
277 
278  Sacado::Fad::SFad<double,4> resultFad;
279  resultFad = Neuron::HH_Vseg_F( vVar, nVar, mVar, hVar, gMemVar, vRestVar, gKVar, eKVar, gNaVar, eNaVar );
280 
281  (*dFdxMatPtr)[lidIndexVector[index]][jacobianOffsets[row][vOffset]] += resultFad.dx(0); // /dVseg
282  (*dFdxMatPtr)[lidIndexVector[index]][jacobianOffsets[row][vOffset+1]] += resultFad.dx(1); // /dn
283  (*dFdxMatPtr)[lidIndexVector[index]][jacobianOffsets[row][vOffset+2]] += resultFad.dx(2); // /dm
284  (*dFdxMatPtr)[lidIndexVector[index]][jacobianOffsets[row][vOffset+3]] += resultFad.dx(3); // /dh
285  }
286 
287  // cew 2/18/11 - changed the functions used below to use voltage shifted by resting potential
288  // n equation
289  {
290  Sacado::Fad::SFad<double,2> vVar( 2, 0, vSeg-vRest_ );
291  Sacado::Fad::SFad<double,2> nVar( 2, 1, n );
292  Sacado::Fad::SFad<double,2> nEquResultFad = Neuron::nEquF( vVar, nVar );
293 
294  (*dFdxMatPtr)[lidIndexVector[index + 1]][jacobianOffsets[row + 1][0]] += nEquResultFad.dx(0); // dVseg
295  (*dFdxMatPtr)[lidIndexVector[index + 1]][jacobianOffsets[row + 1][1]] += nEquResultFad.dx(1); // dn
296  }
297 
298  // m equation
299  {
300  Sacado::Fad::SFad<double,2> vVar( 2, 0, vSeg-vRest_ );
301  Sacado::Fad::SFad<double,2> mVar( 2, 1, m );
302  Sacado::Fad::SFad<double,2> mEquResultFad = Neuron::mEquF( vVar, mVar );
303 
304  (*dFdxMatPtr)[lidIndexVector[index + 2]][jacobianOffsets[row + 2][0]] += mEquResultFad.dx(0); // dVseg
305  (*dFdxMatPtr)[lidIndexVector[index + 2]][jacobianOffsets[row + 2][1]] += mEquResultFad.dx(1); // dm
306  }
307 
308  // h equation
309  {
310  Sacado::Fad::SFad<double,2> vVar( 2, 0, vSeg-vRest_ );
311  Sacado::Fad::SFad<double,2> hVar( 2, 1, h );
312  Sacado::Fad::SFad<double,2> hEquResultFad = Neuron::hEquF( vVar, hVar );
313 
314  (*dFdxMatPtr)[lidIndexVector[index + 3]][jacobianOffsets[row + 3][0]] += hEquResultFad.dx(0); // dVseg
315  (*dFdxMatPtr)[lidIndexVector[index + 3]][jacobianOffsets[row + 3][1]] += hEquResultFad.dx(1); // dh
316  }
317 
318 }
319 
320 } // namespace Device
321 } // namespace Xyce
static ScalarT hEquF(const ScalarT &Vn, const ScalarT &h)
void loadDAEQVector(int segmentNumber, std::vector< int > &lidIndexVector, Linear::Vector *solnVecPtr, Linear::Vector *daeQVecPtr, double segArea)
static ScalarT HH_Vseg_F(const ScalarT &Vseg, const ScalarT &n, const ScalarT &m, const ScalarT &h, const ScalarT &memG, const ScalarT &restV, const ScalarT &Kg, const ScalarT &Ke, const ScalarT &NaG, const ScalarT &NaE)
Pure virtual class to augment a linear system.
void loadDAEdFdx(int segmentNumber, int vOffset, std::vector< int > &lidIndexVector, std::vector< std::vector< int > > &jacobianOffsets, Linear::Vector *solnVecPtr, Linear::Matrix *dFdxMatPtr, double segArea)
void setJacStamp(int numExtVars, int segmentNumber, int vOffset, std::vector< std::vector< int > > &segmentJacStamp)
void loadDAEdQdx(int segmentNumber, int vOffset, std::vector< int > &lidIndexVector, std::vector< std::vector< int > > &jacobianOffsets, Linear::Vector *solnVecPtr, Linear::Matrix *dQdxMatPtr, double segArea)
MembraneHH(const SolverState &ss1, double cMem, double gMem, double vRest, double eK, double gK, double eNa, double gNa)
static ScalarT nEquF(const ScalarT &Vn, const ScalarT &n)
static ScalarT mEquF(const ScalarT &Vn, const ScalarT &m)
void loadDAEFVector(int segmentNumber, std::vector< int > &lidIndexVector, Linear::Vector *solnVecPtr, Linear::Vector *daeFVecPtr, double segArea)