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