Xyce  6.1
N_DEV_MembraneUserDefined.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_MembraneUserDefined.C,v $
27 //
28 // Purpose : This class defines a user-defined membrane mechanism.
29 //
30 // Special Notes :
31 //
32 // Creator : Christy Warrender, Cognitive Modeling
33 //
34 // Creation Date : 12/14/2010
35 //
36 // Revision Information:
37 // ---------------------
38 //
39 // Revision Number: $Revision: 1.20.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 #include<iostream>
49 #include<algorithm>
50 
51 // ---------- Standard Includes ----------
52 
53 
54 // ---------- Xyce Includes ----------
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 : MembraneUserDefined::MembraneUserDefined
65 // Purpose :
66 // Special Notes :
67 // Scope : public
68 // Creator : Christy Warrender, Cognitive Modeling & Richard Schiek Elec. Sys. Modeling
69 // Creation Date : 12/14/2010
70 //-----------------------------------------------------------------------------
71 MembraneUserDefined::MembraneUserDefined(const SolverState & ss1, double cMem, double gMem, double vRest,
72  std::vector<std::string> & currentEqus, std::vector<std::string> & indepVars, std::vector<std::string> & fEqs,
73  std::vector<std::string> & qEqs, std::vector<std::string> & extraFunctions, std::vector<std::string> & extraParameters) :
74  MembraneModel(ss1),
75  gMem_(gMem),
76  cMem_(cMem),
77  vRest_(vRest),
78  currentEqus_(currentEqus),
79  indepVars_(indepVars),
80  fEqs_(fEqs),
81  qEqs_(qEqs),
82  extraFunctions_(extraFunctions),
83  extraParameters_(extraParameters)
84 {
85  // make expressions from the passed in strings
92 
93  // try to reduce equations to just the unknowns (indepVars and V)
95 
96  numIndependentVars_ = indepVarsExpRCP_.size() + 1; // the +1 is for the voltage at each node which
97  // is always there even if this is a passive cable
98  systemJacOffset_.resize(numIndependentVars_); // this will hold a map to for the local jacobian
99  // offsets on each row.
100 }
101 
102 //-----------------------------------------------------------------------------
103 // Function : MembraneUserDefined::setJacStamp
104 // Purpose :
105 // Special Notes :
106 // Scope : public
107 // Creator : Christy Warrender, Cognitive Modeling & Richard Schiek Elec. Sys. Modeling
108 // Creation Date : 12/14/2010
109 //-----------------------------------------------------------------------------
110 void MembraneUserDefined::setJacStamp( int numExtVars, int segmentNumber, int vOffset,
111  std::vector< std::vector< int > > & segmentJacStamp )
112 {
113  // caller sets up size of row of jac stamp to numIndependentVars_ + extra's needed for
114  // its modeling. So for a cable based device this is Vpre, Vseg, (other membrane vars), Vnext.
115  // in general this is numIndependentVars_ + 2 (Vpre and Vnext). In this routine we fill in
116  // just what is needed for the membrane model
117 
118  int offset = numExtVars + numIndependentVars_*segmentNumber;
119 
120  // note, any external vars come before the internal vars. Dependance on Vprev and Vnext (if
121  // they are really there) are handled by the caller as in a cable equation model.
122 
123  // general Jacobian strcuture:
124  // Vpre V user var 1 user var 2 user var 3 Vnext
125  // kcl yes yes ? ? ? yes
126  // user var 1 ? yes ? ?
127  // user var 2 ? ? yes ?
128  // user var 3 ? ? ? yes
129  //
130  // need to parse user-defined equations to determine dependence of each internal variable on the
131  // others
132  std::map<std::string,int>::iterator currVarOffset = indepVarOffset_.begin();
133  std::map<std::string,int>::iterator endVarOffset = indepVarOffset_.end();
134 
135  while( currVarOffset != endVarOffset )
136  {
137  std::set<std::string> depVarNames;
138  depVarNames.clear();
139  if( currVarOffset->first == "V" )
140  {
141  // voltage is different as it depends on a set of current equations
142  for( int i=0; i<currentEqusVarNames_.size(); i++ )
143  {
144  depVarNames.insert( currentEqusVarNames_[i].begin(), currentEqusVarNames_[i].end() );
145  }
146  }
147  else
148  {
149  // for other variables we look at the F & Q equation
150  depVarNames.insert( fEqsEqusVarNames_[ currVarOffset->second - 1 ].begin(), fEqsEqusVarNames_[ currVarOffset->second - 1 ].end() );
151  depVarNames.insert( qEqsEqusVarNames_[ currVarOffset->second - 1 ].begin(), qEqsEqusVarNames_[ currVarOffset->second - 1 ].end() );
152  }
153 
154  // now we have a set of the variable names, but the order is unknown. Use the offsetToIndepVar_ map to make
155  // a new map in the right order - meaning the order of nonzero terms in the jacobian - and in sequence.
156  // cew - taking next bit out. systemJacOffsets_ is shared by all segments, so we can't use it to
157  // keep track of the differences in which variables V depends on - instead, add vOffset to the values
158  // stored here when they're used, both in setting up jacStamp below, and in loadDAEdFdx (it's
159  // not used in loadDAEdQdx - we assume no contributions to V from user-defined equations).
160  // Also, if the currVarOffset->first (the name of the variable we're processing now) =="V" then
161  // we have to add vOffset as this allows for "Vprevious" on the same row of the jacobian
162  // In other words, we're assuming that the internal segment variables don't depend on anything
163  // outside the membrane model, but we know that V does, and that may affect internal variables'
164  // indices in the segmentJacStamp
165  //int voltageOffset=0;
166  //if( currVarOffset->first == "V" )
167  //{
168  //voltageOffset=vOffset;
169  //}
170  std::set<std::string>::iterator depVarNamesEnd = depVarNames.end();
171  int numEnteries = offsetToIndepVar_.size();
172  std::map< std::string, int > tmpIndVarMap;
173  int numFound = 0;
174  for( int i=0; i<numEnteries; i++)
175  {
176  if( depVarNames.find( offsetToIndepVar_[i] ) != depVarNamesEnd )
177  {
178  //tmpIndVarMap[ offsetToIndepVar_[i] ] = voltageOffset + numFound++;
179  tmpIndVarMap[ offsetToIndepVar_[i] ] = numFound++;
180  Xyce::dout() << "Assigned tmpIndVarMap[ " << offsetToIndepVar_[i] << " ]= " << (numFound-1) << std::endl;
181  }
182  }
183 
184  systemJacOffset_[currVarOffset->second ]= tmpIndVarMap;
185  Xyce::dout() << "tmpIndVarMap assigned to variable " << currVarOffset->first << std::endl;
186 
187  // get the number of things on this row
188  int numvars = systemJacOffset_[currVarOffset->second].size();
189  // Xyce::dout() << "systemJacOffset_[ " << currVarOffset->second << "].size() = " << numvars << std::endl;
190 
191  // the owning device allocates this row for us in the case of V
192  // so don't resize it in that case
193  if( currVarOffset->first != "V" )
194  {
195  segmentJacStamp[offset + currVarOffset->second ].resize( numvars );
196  }
197 
198  // now loop over the set of independent vars to set jacStamp dependance
199  std::set<std::string>::iterator depVarNamesCurr = depVarNames.begin();
200  while( depVarNamesCurr != depVarNamesEnd )
201  {
202  if( currVarOffset->first == "V" )
203  {
204  segmentJacStamp[offset + currVarOffset->second ][ vOffset+systemJacOffset_[currVarOffset->second ][*depVarNamesCurr ]] = offset + indepVarOffset_[*depVarNamesCurr];
205  }
206  else
207  {
208  segmentJacStamp[offset + currVarOffset->second ][ systemJacOffset_[currVarOffset->second ][*depVarNamesCurr ]] = offset + indepVarOffset_[*depVarNamesCurr];
209  }
210  depVarNamesCurr++;
211  }
212  currVarOffset++;
213  }
214 
215  // print out jacstamp for debugging
216 
217  Xyce::dout() << "MembraneUserDefined::setJacStamp() jacStamp = " << std::endl;
218  for( int i = 0; i<segmentJacStamp.size(); ++i )
219  {
220  Xyce::dout() << "jacStamp[ " << i << " ] = { ";
221  for( int j=0; j<segmentJacStamp[i].size(); ++j)
222  {
223  Xyce::dout() << segmentJacStamp[i][j];
224  if( j != ( segmentJacStamp[i].size() -1 ) )
225  {
226  Xyce::dout() << ", ";
227  }
228  }
229  Xyce::dout() << " }" << std::endl;
230  }
231  Xyce::dout() << Xyce::section_divider << std::endl;
232 
233  Xyce::dout() << "MembraneUserDefined::setJacStamp() systemJacOffset_ = " << std::endl;
234  for( int i = 0; i<systemJacOffset_.size(); ++i )
235  {
236  Xyce::dout() << "systemJacOffset[ " << i << " ] = { ";
237  std::map<std::string,int>::iterator currVarOffset = systemJacOffset_[i].begin();
238  std::map<std::string,int>::iterator endVarOffset = systemJacOffset_[i].end();
239  while( currVarOffset != endVarOffset )
240  {
241  Xyce::dout() << currVarOffset->first << ", " << currVarOffset->second << std::endl;
242  currVarOffset++;
243  }
244  Xyce::dout() << " }" << std::endl;
245  }
246  Xyce::dout() << Xyce::section_divider << std::endl;
247 
248 
249 
250 }
251 
252 //-----------------------------------------------------------------------------
253 // Function : MembraneUserDefined::loadDAEQVector
254 // Purpose :
255 // Special Notes :
256 // Scope : public
257 // Creator : Christy Warrender, Cognitive Modeling & Richard Schiek Elec. Sys. Modeling
258 // Creation Date : 12/14/2010
259 //-----------------------------------------------------------------------------
260 void MembraneUserDefined::loadDAEQVector( int segmentNumber,
261  std::vector< int > & lidIndexVector,
262  Linear::Vector * solnVecPtr,
263  Linear::Vector * daeQVecPtr,
264  double segArea)
265 {
266  int index = segmentNumber * numIndependentVars_;
267 
268  // contribution of membrane capacitance to segment voltage
269  (*daeQVecPtr)[lidIndexVector[index]] += cMem_ * segArea * (*solnVecPtr)[lidIndexVector[index]];
270  int numCurrentExp = currentEqusExpRCP_.size();
271  Xyce::dout() << "loadDAEQVector: entry for index " << index << " : " << cMem_ * segArea * (*solnVecPtr)[lidIndexVector[index]] << std::endl;
272 
273  // add Q terms for user-defined vars
274  int numExp = qEqsExpRCP_.size();
275  for( int i=0; i<numExp; i++)
276  {
277  // get the number of vars in this expression and update their values
278  int numvars = qEqsEqusVarNames_[i].size();
279  for( int j=0; j<numvars; j++)
280  {
281  qEqsEqusVarValues_[i][j]=(*solnVecPtr)[ lidIndexVector[index + indepVarOffset_[ qEqsEqusVarNames_[i][j] ] ] ];
282  }
283  // evaluate the expression
284  double resultValue=0.0;
285  qEqsExpRCP_[i]->evaluateFunction( resultValue, qEqsEqusVarValues_[i] );
286 
287  // add it to the Q vector
288  // cew not sure about my change here; do we know that vars are in order?
289  // instead of adding i to index, should use indepVarOffset_ map
290  // but how do I get ver name at this level?
291  int lidIndexVectorIndex = index + i + 1; // +1 for V
292  (*daeQVecPtr)[lidIndexVector[lidIndexVectorIndex]] += resultValue;
293  Xyce::dout() << "loadDAEQVector: entry for LID index " << lidIndexVectorIndex
294  << ", varname " << offsetToIndepVar_[lidIndexVectorIndex-index] << " : " << resultValue << std::endl;
295  }
296 }
297 
298 
299 
300 //-----------------------------------------------------------------------------
301 // Function : MembraneUserDefined::loadDAEFVector
302 // Purpose :
303 // Special Notes :
304 // Scope : public
305 // Creator : Christy Warrender, Cognitive Modeling & Richard Schiek Elec. Sys. Modeling
306 // Creation Date : 12/14/2010
307 //-----------------------------------------------------------------------------
308 void MembraneUserDefined::loadDAEFVector( int segmentNumber,
309  std::vector< int > & lidIndexVector,
310  Linear::Vector * solnVecPtr,
311  Linear::Vector * daeFVecPtr,
312  double segArea)
313 {
314  Xyce::dout() << "loadDAEFVector" << std::endl;
315 
316  int index = segmentNumber * numIndependentVars_;
317 
318  // leak contribution
319  (*daeFVecPtr)[lidIndexVector[index]] += gMem_ * segArea * ((*solnVecPtr)[lidIndexVector[index]] - vRest_ );
320 
321  // to get at the independent vars of this segment we'll need to use the passed in lidIndexVector
322  // as in (*solnVecPtr)[ lidIndexVector[index + some_offset ] ]
323 
324  // add contribution from current equation
325  int numCurrentExp = currentEqusExpRCP_.size();
326  for( int i=0; i<numCurrentExp; i++)
327  {
328  Xyce::dout() << "membrane V equation contribution from current equation # " << i << std::endl;
329  // get the number of vars in this expression and update their values
330  int numvars = currentEqusVarNames_[i].size();
331  for( int j=0; j<numvars; j++)
332  {
333  currentEqusVarValues_[i][j]=(*solnVecPtr)[ lidIndexVector[index + indepVarOffset_[ currentEqusVarNames_[i][j] ] ] ];
334  Xyce::dout() << "Segment " << segmentNumber << " current load variable = " << currentEqusVarNames_[i][j] << " value = " << currentEqusVarValues_[i][j] << std::endl;
335  }
336  // evaluate the expression
337  double resultValue=0.0;
338  currentEqusExpRCP_[i]->evaluateFunction( resultValue, currentEqusVarValues_[i] );
339  Xyce::dout() << "Segment " << segmentNumber << " current equ F = " << resultValue << std::endl;
340 
341  // add it to the F vector
342  // cew - need to multiply by segArea, assuming current specified as current density
343  (*daeFVecPtr)[lidIndexVector[index]] += segArea*resultValue;
344  }
345 
346  // add contribution from extra vars
347  int numExp = fEqsExpRCP_.size();
348  for( int i=0; i<numExp; i++)
349  {
350  Xyce::dout() << "F terms from f equation # " << i << std::endl;
351  // get the number of vars in this expression and update their values
352  int numvars = fEqsEqusVarNames_[i].size();
353  for( int j=0; j<numvars; j++)
354  {
355  fEqsEqusVarValues_[i][j]=(*solnVecPtr)[ lidIndexVector[index + indepVarOffset_[ fEqsEqusVarNames_[i][j] ] ] ];
356  Xyce::dout() << "Segment " << segmentNumber << " extra var load variable = " << fEqsEqusVarNames_[i][j] << " value = " << fEqsEqusVarValues_[i][j] << std::endl;
357  }
358  // evaluate the expression
359  double resultValue=0.0;
360  fEqsExpRCP_[i]->evaluateFunction( resultValue, fEqsEqusVarValues_[i] );
361 
362  // add it to the F vector
363  //int lidIndexVectorIndex = index + numCurrentExp + i;
364  int lidIndexVectorIndex = index + i + 1; // +1 for V
365  (*daeFVecPtr)[lidIndexVector[lidIndexVectorIndex]] += resultValue;
366  Xyce::dout() << "Segment " << segmentNumber << " LID index " << lidIndexVectorIndex
367  << ", extra vars equ F = " << resultValue << std::endl;
368  }
369 
370 }
371 
372 //-----------------------------------------------------------------------------
373 // Function : MembraneUserDefined::loadDAEdQdx
374 // Purpose :
375 // Special Notes :
376 // Scope : public
377 // Creator : Christy Warrender, Cognitive Modeling & Richard Schiek Elec. Sys. Modeling
378 // Creation Date : 12/14/2010
379 //-----------------------------------------------------------------------------
380 void MembraneUserDefined::loadDAEdQdx( int segmentNumber, int vOffset,
381  std::vector< int > & lidIndexVector,
382  std::vector< std::vector< int > > & jacobianOffsets,
383  Linear::Vector * solnVecPtr,
384  Linear::Matrix * dQdxMatPtr,
385  double segArea)
386 {
387  Xyce::dout() << "loadDAEdQdx" << std::endl;
388 
389  // while lidIndexVector lists LID's for just the segment variables (just V in the case
390  // of a passive cable). The jacobianOffsets includes the Vin and Vout as the first
391  // two variables. Thus, there is a constant offset of 2 for everything in jacobianOffsets
392 
393  // And, as in the Q and F load functions, Each segment will have numIndependentVars_ with segment voltage being the first
394  // so, the cMem dV/dt term will be at segmentNumber * numIndependentVars_.
395  // in the case of the passive cable numIndependentVars_=1.
396 
397  int index = segmentNumber * numIndependentVars_;
398  int row = numExternalVars_ + index; // numExternalVars_ a contant of 2 assumed in MembraneModel base class
399 
400  // Vseg equation
401  (*dQdxMatPtr)[lidIndexVector[index]][jacobianOffsets[row][vOffset]] += cMem_ * segArea;
402  int numCurrentExp = currentEqusExpRCP_.size();
403 
404  // cew - in theory, currents might contribute to dQdx, although they don't in our test case
405 
406  // add contribution for extra vars
407  int numExp = qEqsExpRCP_.size();
408  for( int i=0; i<numExp; i++)
409  {
410  std::string targetVarName = offsetToIndepVar_[i+1]; // +1 because offsetToIndepVar includes V, but numExp doesn't
411  Xyce::dout() << "Q terms for q equation " << i << "; assumed to be for variable " << targetVarName << std::endl;
412  // get the number of vars in this expression and update their values
413  int numvars = qEqsEqusVarNames_[i].size();
414  for( int j=0; j<numvars; j++)
415  {
416  qEqsEqusVarValues_[i][j]=(*solnVecPtr)[ lidIndexVector[index + indepVarOffset_[ qEqsEqusVarNames_[i][j] ] ]];
417  Xyce::dout() << "contribution from " << qEqsEqusVarNames_[i][j] << ": " << qEqsEqusVarValues_[i][j] << std::endl;
418  }
419  // evaluate the expression
420  double resultValue=0.0;
421  std::vector<double> derivValues;
422  derivValues.resize( numvars );
423  qEqsExpRCP_[i]->evaluate( resultValue, derivValues, qEqsEqusVarValues_[i] );
424  Xyce::dout() << "expression result: " << resultValue << std::endl;
425 
426  // add it to the dQdx matrix
427  for( int j=0; j<numvars; j++)
428  {
429  std::string varName = qEqsEqusVarNames_[i][j];
430  Xyce::dout() << "contribution of variable " << j << "(" << varName << ")" << std::endl;
431  int targetVarOffset = indepVarOffset_[targetVarName];
432  int targetVarIndex = index + targetVarOffset;
433  int targetVarRow = row + targetVarOffset;
434  int jacOffsetsCol = systemJacOffset_[targetVarOffset][varName];
435  Xyce::dout() << "targerVarOffset: " << targetVarOffset << ", targetVarIndex: " << targetVarIndex
436  << ", jacobianRow: " << targetVarRow << ", jacobianCol: " << jacOffsetsCol << std::endl;
437  (*dQdxMatPtr)[lidIndexVector[targetVarIndex]][jacobianOffsets[targetVarRow][jacOffsetsCol]] += derivValues[j];
438  Xyce::dout() << "adding (*dQdxMatPtr)[ " << lidIndexVector[targetVarIndex] << " ][ " << jacobianOffsets[targetVarRow][jacOffsetsCol] << " ] = " << derivValues[j] << std::endl;
439  }
440  }
441 }
442 
443 //-----------------------------------------------------------------------------
444 // Function : MembraneUserDefined::loadDAEdFdx
445 // Purpose :
446 // Special Notes :
447 // Scope : public
448 // Creator : Christy Warrender, Cognitive Modeling & Richard Schiek Elec. Sys. Modeling
449 // Creation Date : 12/14/2010
450 //-----------------------------------------------------------------------------
451 void MembraneUserDefined::loadDAEdFdx( int segmentNumber, int vOffset,
452  std::vector< int > & lidIndexVector,
453  std::vector< std::vector< int > > & jacobianOffsets,
454  Linear::Vector * solnVecPtr,
455  Linear::Matrix * dFdxMatPtr,
456  double segArea)
457 {
458  Xyce::dout() << "loadDAEdFdx" << std::endl;
459 
460  // while lidIndexVector lists LID's for just the segment variables (just V in the case
461  // of a passive cable). The jacobianOffsets includes the Vin and Vout as the first
462  // two variables. Thus, there is a constant offset of 2 for everything in jacobianOffsets
463 
464  // And, as in the Q and F load functions, Each segment will have numIndependentVars_ with segment voltage being the first
465  // so, the cMem dV/dt term will be at segmentNumber * numIndependentVars_.
466  // in the case of the passive cable numIndependentVars_=1.
467 
468  int index = segmentNumber * numIndependentVars_;
469  int row = numExternalVars_ + index; // numExternalVars_ a contant of 2 assumed in MembraneModel base class
470 
471  // leak contribution
472  (*dFdxMatPtr)[lidIndexVector[index]][jacobianOffsets[row][vOffset]] += gMem_ * segArea;
473  Xyce::dout() << "leak current dFdx: " << "vOffset = " << vOffset << " row = " << row << std::endl;
474  Xyce::dout() << "adding (*dFdxMatPtr)[ " << lidIndexVector[index] << " ][ " << jacobianOffsets[row][vOffset ] << " ] = " << gMem_ * segArea << std::endl;
475 
476  // add contribution from current equation
477  int numCurrentEq = currentEqusExpRCP_.size();
478  for( int i=0; i<numCurrentEq; i++)
479  {
480  // get the number of vars in this expression and update their values
481  int numvars = currentEqusVarNames_[i].size();
482  for( int j=0; j<numvars; j++)
483  {
484  currentEqusVarValues_[i][j]=(*solnVecPtr)[ lidIndexVector[index + indepVarOffset_[ currentEqusVarNames_[i][j] ] ] ];
485  }
486  // evaluate the expression
487  double resultValue=0.0;
488  std::vector<double> derivValues;
489  derivValues.resize( numvars );
490  currentEqusExpRCP_[i]->evaluate( resultValue, derivValues, currentEqusVarValues_[i] );
491 
492  // now we have the derivValues[] but the order is determined by currentEqusVarNames_. We
493  // need to convert variable names to the appropriate indices into jacobianOffsets
494  for( int j=0; j<numvars; j++) // this loops through the variables AS THEY APPEAR in the current equation
495  {
496  std::string varName = currentEqusVarNames_[i][j];
497  Xyce::dout() << "contribution of variable " << j << "(" << varName << ") to V" << std::endl;
498  // cew jacOffsetsCol comes from the first row of systemJacOffset_ (for V) and the name of this variable
499  // here, we have to allow for the fact that nonzero entries may be shifted by vOffset
500  int jacOffsetsCol = systemJacOffset_[0][varName] + vOffset;
501  Xyce::dout() << " jacOffsetsRow: " << row << " jacOffsetsCol: " << jacOffsetsCol << std::endl;
502  // cew need to multiply these terms by segArea
503  (*dFdxMatPtr)[lidIndexVector[index]][ jacobianOffsets[row][jacOffsetsCol] ] += segArea*derivValues[j];
504  Xyce::dout() << "adding (*dFdxMatPtr)[ " << lidIndexVector[index] << " ][ " << jacobianOffsets[row][jacOffsetsCol] << " ] = " << segArea*derivValues[j] << std::endl;
505  }
506  }
507 
508  // add contribution from extra vars
509  int numExp = fEqsExpRCP_.size();
510  for( int i=0; i<numExp; i++)
511  {
512  std::string targetVarName = offsetToIndepVar_[i+1];
513  Xyce::dout() << "F terms for f equation " << i << "; assumed to be for variable " << targetVarName << std::endl;
514  // get the number of vars in this expression and update their values
515  int numvars = fEqsEqusVarNames_[i].size();
516  for( int j=0; j<numvars; j++)
517  {
518  fEqsEqusVarValues_[i][j]=(*solnVecPtr)[ lidIndexVector[index + indepVarOffset_[ fEqsEqusVarNames_[i][j] ] ] ];
519  }
520  // evaluate the expression
521  double resultValue=0.0;
522  std::vector<double> derivValues;
523  derivValues.resize( numvars );
524  fEqsExpRCP_[i]->evaluate( resultValue, derivValues, fEqsEqusVarValues_[i] );
525 
526  // add it to the F vector
527  for( int j=0; j<numvars; j++) // this loops through variables AS THEY APPEAR in F eqn i
528  {
529  std::string varName = fEqsEqusVarNames_[i][j];
530  Xyce::dout() << "contribution of variable " << j << "(" << varName << ")" << std::endl;
531  int targetVarOffset = indepVarOffset_[targetVarName];
532  int targetVarIndex = index + targetVarOffset;
533  int targetVarRow = row + targetVarOffset;
534  int jacOffsetsCol = systemJacOffset_[targetVarOffset][varName];
535  Xyce::dout() << "targetVarOffset: " << targetVarOffset << " targetVarIndex: " << targetVarIndex
536  << ", jacOffsetsRow: " << targetVarRow << " jacOffsetsCol: " << jacOffsetsCol << std::endl;
537  (*dFdxMatPtr)[lidIndexVector[targetVarIndex]][ jacobianOffsets[targetVarRow][jacOffsetsCol] ] += derivValues[j];
538  Xyce::dout() << "Adding (*dFdxMatPtr)[ " << lidIndexVector[targetVarIndex] << " ][ " << jacobianOffsets[targetVarRow][jacOffsetsCol] << " ] = " << derivValues[j] << std::endl;
539  }
540  }
541 }
542 
543 //-----------------------------------------------------------------------------
544 // Function : MembraneUserDefined::convertStringsToExpression
545 // Purpose : convert vectors of strings to expressions
546 // Special Notes :
547 // Scope : public
548 // Creator : Richard Schiek, Electrical Systems Modeling
549 // Creation Date : 09/08/2011
550 //-----------------------------------------------------------------------------
551 void MembraneUserDefined::convertStringsToExpression( std::vector< std::string > & stringInput, std::vector<RCP<Util::Expression> > & expRCPOut )
552 {
553  int numStrings = stringInput.size();
554  for( int i=0; i<numStrings; i++ )
555  {
556  //Xyce::dout() << "Making expression from :" << stringInput.at(i) << std::endl;
557  expRCPOut.push_back( rcp( new Util::Expression( stringInput.at(i) ) ) );
558  /*
559  int type=0;
560  std::vector<string> names;
561  expRCPOut.at(i)->get_names(type, names);
562 
563  Xyce::dout() << " type = " << type << std::endl;
564  for( int j=0; j<names.size(); j++)
565  {
566  Xyce::dout() << "name[ " << j << " ] = " << names.at(j) << std::endl;
567  }
568  */
569 
570  }
571  return;
572 }
573 
574 
575 //-----------------------------------------------------------------------------
576 // Function : MembraneUserDefined::consolidateExpressions
577 // Purpose : simplify parameters and functions in membrane equations
578 // Special Notes :
579 // Scope : public
580 // Creator : Richard Schiek, Electrical Systems Modeling
581 // Creation Date : 09/08/2011
582 //-----------------------------------------------------------------------------
584 {
585  // loop through parameters and try to set their value in the other expressions
586  int numParams = extraParametersExpRCP_.size();
587  for( int i=0; i<numParams; i+=2)
588  {
589  std::vector<std::string> names;
590  int type=0;
591  extraParametersExpRCP_.at(i)->get_names(type, names);
592  if( names.size() > 1 )
593  {
594  Xyce::dout() << "Warning MembraneUserDefined::consolidateExpressions() parameter name vec longer than expected." << names.size() << std::endl;
595  }
596  paramNames_.push_back( names[0] );
597  double value=0;
598  extraParametersExpRCP_.at(i+1)->evaluateFunction( value );
599  paramValues_.push_back( value );
600  }
601 
602  // loop over functions to get the function name to function value association
603  int numFuncs = extraFunctionsExpRCP_.size();
604  for( int i=0; i<numFuncs; i+=2 )
605  {
606  std::vector<std::string> names;
607  int type=0;
608  extraFunctionsExpRCP_.at(i)->get_names(type, names);
609  funcNames_.push_back(names[0]);
610  funcExpRCP_.push_back(extraFunctionsExpRCP_.at(i+1));
611  funcNumArgs_.push_back( extraFunctionsExpRCP_.at(i+1)->num_vars() );
612  }
613 
614  // substitute parameters into current expression, extra equations and functions
619 
620  // now substitute functions
624 
625  // convert remaining symbols into variables
626  makeSymbolSet();
630 
631 
632  // debugging output
633  for( int i=0; i<paramNames_.size(); i++ )
634  {
635  Xyce::dout() << "Param \"" << paramNames_.at(i) << "\" = " << paramValues_.at(i) << std::endl;
636  }
637 
638  for( int i=0; i<funcNames_.size(); i++ )
639  {
640  Xyce::dout() << "Func \"" << funcNames_.at(i) << "\" = " << funcExpRCP_.at(i)->get_expression() << " num_args = " << funcNumArgs_.at(i) << std::endl;
641  }
642 
643  for( int i=0; i<currentEqusExpRCP_.size(); i++ )
644  {
645  Xyce::dout() << "Currentequ = " << currentEqusExpRCP_.at(i)->get_expression() << std::endl;
646  }
647 
648  for( int i=0; i<fEqsExpRCP_.size(); i++ )
649  {
650  Xyce::dout() << "F: " << fEqsExpRCP_.at(i)->get_expression() << " Q: " << qEqsExpRCP_.at(i)->get_expression() << std::endl;
651  double fval=0, qval=0;
652  std::vector<double> fargs, qargs;
653  fargs.push_back(0.2); fargs.push_back(-0.015); qargs.push_back(0.2);
654  fEqsExpRCP_.at(i)->evaluateFunction(fval, fargs);
655  qEqsExpRCP_.at(i)->evaluateFunction(qval, qargs);
656  Xyce::dout() << "F(V=-0.015,n/m/h=0.2) = " << fval << " Q(V=-0.015,n/m/h=0.2) = " << qval << std::endl;
657  }
658 
659 
660 
661  return;
662 }
663 
664 
665 //-----------------------------------------------------------------------------
666 // Function : MembraneUserDefined::substituteParameters
667 // Purpose : substitute parameter values in a vector of expressions
668 // Special Notes :
669 // Scope : public
670 // Creator : Richard Schiek, Electrical Systems Modeling
671 // Creation Date : 09/08/2011
672 //-----------------------------------------------------------------------------
673 void MembraneUserDefined::substituteParameters( std::vector<RCP<Util::Expression> > & expRCP_ )
674 {
675  int numParams = paramNames_.size();
676  for( int i=0; i<numParams; i++)
677  {
678  int numExp = expRCP_.size();
679  for( int j=0; j<numExp; j++ )
680  {
681  expRCP_.at(j)->make_constant(paramNames_[i], paramValues_[i]);
682  }
683  }
684 }
685 
686 
687 //-----------------------------------------------------------------------------
688 // Function : MembraneUserDefined::substituteFunctions
689 // Purpose : substitute user supplied functions in a vector of expressions
690 // Special Notes :
691 // Scope : public
692 // Creator : Richard Schiek, Electrical Systems Modeling
693 // Creation Date : 09/08/2011
694 //-----------------------------------------------------------------------------
695 void MembraneUserDefined::substituteFunctions( std::vector<RCP<Util::Expression> > & expRCP_ )
696 {
697  int numFuncs = funcNames_.size();
698  for( int i=0; i<numFuncs; i++)
699  {
700  int numExp = expRCP_.size();
701  for( int j=0; j<numExp; j++ )
702  {
703  expRCP_.at(j)->replace_func(funcNames_[i], *funcExpRCP_[i], funcNumArgs_[i]);
704  }
705  }
706 }
707 
708 
709 //-----------------------------------------------------------------------------
710 // Function : MembraneUserDefined::makeSymbolSet
711 // Purpose : Scan the user supplied equations to get the variables
712 // and make a map of variable name to offset that will
713 // be used in loads
714 // Special Notes :
715 // Scope : public
716 // Creator : Richard Schiek, Electrical Systems Modeling
717 // Creation Date : 09/08/2011
718 //-----------------------------------------------------------------------------
720 {
721  // get the user defined names
722  // this assumes that the user hasn't repeated a name. I should trap for this.
723  // I used a set<string> for this initially, but the ordering isn't unique so the
724  // vars would end up in a strange order.
725  int type=0;
726  int numIndependentVars = indepVarsExpRCP_.size(); // cew 1 less than numIndependentVars_, which includes V
727  for( int i=0; i<numIndependentVars; i++ )
728  {
729  std::vector<std::string> expNames;
730  indepVarsExpRCP_.at(i)->get_names( type, expNames );
731  int numExpNames=expNames.size();
732  for( int j=0; j<numExpNames; j++ ) // cew ? isn't there just one name per independent var?
733  {
734  Xyce::dout() << "makeSymbolSet, i=" <<i << ", j="<<j << ": adding " << expNames.at(j) << " to userDefinedNames" << std::endl;
735  userDefinedNames_.push_back( expNames.at(j) );
736  }
737  }
738 
739  // TODO: check through the F and Q and current equations and make sure there aren't any variables that aren't
740  // in userDefinedNames_
741  // actually, methods called from consolidateExpressions kind of does this;
742  // currently just prints a warning, sets value to 0, and continues
743 
744  // now userDefinedNames_ contains all the vars the user supplied
745  // assign them unique offsets so that we can consistently index them in
746  // the solution, F, Q and jacobian.
747 
748  std::vector<std::string>::iterator currName = userDefinedNames_.begin();
749  std::vector<std::string>::iterator endName = userDefinedNames_.end();
750  int offsetCount=0;
751  while( currName != endName )
752  {
753  indepVarOffset_[ *currName ] = ++offsetCount; // set up map
754  offsetToIndepVar_[offsetCount] = *currName; // and inverse map
755  currName++;
756  }
757 
758  // now add "V" to userDefinedNames_ and give it an offset of zero in
759  // the indepVarOffset_ map: "V" must be offset zero -- it's first.
760  userDefinedNames_.push_back("V"); // V is automatically part of the system of equations.
761  indepVarOffset_["V"] = 0;
762  offsetToIndepVar_[0]="V";
763 
764  // debugging output
765  Xyce::dout() << "MembraneUserDefined::makeSymbolSet() Independent var offset map : " << std::endl;
766  std::map<std::string,int>::iterator currVarOffset = indepVarOffset_.begin();
767  std::map<std::string,int>::iterator endVarOffset = indepVarOffset_.end();
768  while( currVarOffset != endVarOffset )
769  {
770  Xyce::dout() << "map[ " << currVarOffset->first << " ] = " << currVarOffset->second << std::endl;
771  currVarOffset++;
772  }
773  std::map<int,std::string>::iterator currOffset = offsetToIndepVar_.begin();
774  std::map<int,std::string>::iterator endOffset = offsetToIndepVar_.end();
775  while( currOffset != endOffset )
776  {
777  Xyce::dout() << "inv-map[ " << currOffset->first << " ] = " << currOffset->second << std::endl;
778  currOffset++;
779  }
780 
781 }
782 
783 
784 //-----------------------------------------------------------------------------
785 // Function : MembraneUserDefined::convertSymbolsToVars
786 // Purpose : Using user supplied list and what's left in an expression,
787 // convert remaining symbols to vars so that we can set them
788 // and evaluate the expression
789 // Special Notes :
790 // Scope : public
791 // Creator : Richard Schiek, Electrical Systems Modeling
792 // Creation Date : 09/08/2011
793 //-----------------------------------------------------------------------------
794 void MembraneUserDefined::convertSymbolsToVars( std::vector<RCP<Util::Expression> > & expRCP, std::vector< std::vector<std::string> > & expNamesVec, std::vector< std::vector<double> > & expValsVec )
795 {
796  int type=0;
797  std::vector<std::string>::iterator endName = userDefinedNames_.end();
798  int numExp = expRCP.size();
799  if( numExp > 0 )
800  {
801  expNamesVec.resize( numExp );
802  expValsVec.resize( numExp );
803  }
804  for( int i=0; i<numExp; i++ )
805  {
806  // get the remaining symbol names in the expression
807  std::vector<std::string> expNamesTmp;
808  expRCP.at(i)->get_names( type, expNamesTmp );
809  int numExpNames=expNamesTmp.size();
810  for( int j=0; j<numExpNames; j++ )
811  {
812  // if expNames[j] is in our set of known symbols that
813  // are variables, then set its type to variable. Otherwise
814  // issue a warning.
815  std::vector<std::string>::iterator findResult = find( userDefinedNames_.begin(), userDefinedNames_.end(), expNamesTmp.at(j) );
816  if( findResult != endName )
817  {
818  // found this one in the set so make it a variable
819  expRCP.at(i)->make_var( expNamesTmp.at(j) );
820  // save it in a std::vector<std::string> so we can quickly load values into a std::vector<double> to evaluate the expression.
821  expNamesVec[i].push_back( expNamesTmp.at(j) );
822  }
823  else
824  {
825  Xyce::dout() << "Warning symbol \"" << expNamesTmp.at(j) << "\" not listed in independent variables. Ignoring for now." << std::endl;
826  }
827  }
828  // make the values vec the same size as the names vec.
829  expValsVec[i].resize( expNamesVec[i].size() );
830 
831  Xyce::dout() << "MembraneUserDefined::convertSymbolsToVars: expression " << expRCP.at(i)->get_expression() << " Has vars: ";
832  std::vector<std::string>::iterator cni = expNamesVec[i].begin();
833  std::vector<std::string>::iterator eni = expNamesVec[i].end();
834  while( cni != eni )
835  {
836  Xyce::dout() << *cni << ", ";
837  cni++;
838  }
839  Xyce::dout() << std::endl;
840 
841  }
842 }
843 
844 } // namespace Device
845 } // namespace Xyce
std::vector< std::vector< std::string > > currentEqusVarNames_
std::vector< std::vector< double > > currentEqusVarValues_
std::vector< std::string > extraParameters_
std::map< int, std::string > offsetToIndepVar_
std::vector< std::vector< std::string > > qEqsEqusVarNames_
Pure virtual class to augment a linear system.
void substituteParameters(std::vector< RCP< Util::Expression > > &expRCP_)
void convertSymbolsToVars(std::vector< RCP< Util::Expression > > &expRCP_, std::vector< std::vector< std::string > > &expNames, std::vector< std::vector< double > > &expValsVec)
std::vector< std::vector< double > > fEqsEqusVarValues_
std::vector< RCP< Util::Expression > > extraFunctionsExpRCP_
std::vector< RCP< Util::Expression > > currentEqusExpRCP_
std::vector< RCP< Util::Expression > > qEqsExpRCP_
std::vector< std::vector< double > > qEqsEqusVarValues_
void loadDAEdQdx(int segmentNumber, int vOffset, std::vector< int > &lidIndexVector, std::vector< std::vector< int > > &jacobianOffsets, Linear::Vector *solnVecPtr, Linear::Matrix *dQdxMatPtr, double segArea)
const T & value(const ParameterBase &entity, const Descriptor &descriptor)
Returns the value of the parameter for the entity.
Definition: N_DEV_Pars.h:1224
std::vector< RCP< Util::Expression > > fEqsExpRCP_
void loadDAEFVector(int segmentNumber, std::vector< int > &lidIndexVector, Linear::Vector *solnVecPtr, Linear::Vector *daeFVecPtr, double segArea)
std::vector< RCP< Util::Expression > > indepVarsExpRCP_
MembraneUserDefined(const SolverState &ss1, double cMem, double gMem, double vRest, std::vector< std::string > &currentEqus, std::vector< std::string > &indepVars, std::vector< std::string > &fEqs, std::vector< std::string > &qEqs, std::vector< std::string > &extraFunctions, std::vector< std::string > &extraParameters)
std::vector< RCP< Util::Expression > > funcExpRCP_
std::vector< std::string > extraFunctions_
void convertStringsToExpression(std::vector< std::string > &stringInput, std::vector< RCP< Util::Expression > > &expRCPOut)
void loadDAEQVector(int segmentNumber, std::vector< int > &lidIndexVector, Linear::Vector *solnVecPtr, Linear::Vector *daeQVecPtr, double segArea)
std::vector< std::vector< std::string > > fEqsEqusVarNames_
void loadDAEdFdx(int segmentNumber, int vOffset, std::vector< int > &lidIndexVector, std::vector< std::vector< int > > &jacobianOffsets, Linear::Vector *solnVecPtr, Linear::Matrix *dFdxMatPtr, double segArea)
std::vector< std::map< std::string, int > > systemJacOffset_
std::vector< std::string > userDefinedNames_
std::vector< RCP< Util::Expression > > extraParametersExpRCP_
void substituteFunctions(std::vector< RCP< Util::Expression > > &expRCP_)
void setJacStamp(int numExtVars, int segmentNumber, int vOffset, std::vector< std::vector< int > > &segmentJacStamp)
std::map< std::string, int > indepVarOffset_