Xyce  6.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
N_DEV_Capacitor.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-2014 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_Capacitor.C,v $
27 //
28 // Purpose :
29 //
30 // Special Notes :
31 //
32 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
33 //
34 // Creation Date : 02/28/00
35 //
36 // Revision Information:
37 // ---------------------
38 //
39 // Revision Number: $Revision: 1.261.2.4 $
40 //
41 // Revision Date : $Date: 2014/03/06 23:33:43 $
42 //
43 // Current Owner : $Author: tvrusso $
44 //-------------------------------------------------------------------------
45 
46 #include <Xyce_config.h>
47 
48 #include <N_DEV_Capacitor.h>
49 
50 #include <N_UTL_Expression.h>
51 #include <N_UTL_Misc.h>
52 #include <N_UTL_Math.h>
53 #include <N_UTL_LogStream.h>
54 
55 #include <N_DEV_Const.h>
56 #include <N_DEV_DeviceOptions.h>
57 #include <N_DEV_ExternData.h>
58 #include <N_DEV_MatrixLoadData.h>
59 #include <N_DEV_Message.h>
60 #include <N_DEV_SolverState.h>
61 
62 #include <N_LAS_Vector.h>
63 #include <N_LAS_Matrix.h>
64 
65 
66 namespace Xyce {
67 namespace Device {
68 namespace Capacitor {
69 
70 
71 //-----------------------------------------------------------------------------
72 // Function : Xyce::Device::Capacitor::Traits::loadInstanceParameters
73 // Purpose :
74 // Special Notes : The addPar calls here were refactored and moved here
75 // from the instance constructor. Those addPars had been
76 // in place from 2/4/2005.
77 // Scope : private
78 // Creator : David Baur
79 // Creation Date : 1/27/2014
80 //-----------------------------------------------------------------------------
81 ///
82 /// Loads the parameter definition into the instance parameter map.
83 ///
84 /// @param p instance parameter map
85 ///
86 /// @see Xyce::Device::Resistor::Traits::loadInstanceParameters
87 ///
89 {
90  p.addPar("C", 1.e-6, &Capacitor::Instance::C)
91  .setExpressionAccess(ParameterType::SOLN_DEP)
92  .setUnit(U_FARAD)
93  .setDescription("Capacitance");
94  p.addPar("IC", 0.0, &Capacitor::Instance::IC)
95  .setGivenMember(&Capacitor::Instance::ICGiven)
96  .setUnit(STANDARD);
98  .setUnit(U_METER)
99  .setDescription("Semiconductor capacitor width");
100  p.addPar("W", 1.e-6, &Capacitor::Instance::width)
101  .setUnit(U_METER)
102  .setDescription("Semiconductor capacitor length");
103  p.addPar("AGE", 0.0, &Capacitor::Instance::age)
104  .setUnit(U_HOUR)
105  .setDescription("Age of capacitor");
106  p.addPar("D", 0.0233, &Capacitor::Instance::ageCoef)
107  .setDescription("Age degradation coefficient");
108  p.addPar("TEMP", 0.0, &Capacitor::Instance::temp)
109  .setExpressionAccess(ParameterType::TIME_DEP)
110  .setUnit(STANDARD)
111  .setDescription("Device temperature");
112 
114  .setGivenMember(&Capacitor::Instance::tempCoeff1Given)
115  .setUnit(U_DEGCM1)
116  .setDescription("Linear Temperature Coefficient");
118  .setGivenMember(&Capacitor::Instance::tempCoeff2Given)
119  .setUnit(U_DEGCM2)
120  .setDescription("Quadratic Temperature Coefficient");
121  p.makeVector("TC", 2);
122 }
123 
124 //-----------------------------------------------------------------------------
125 // Function : Xyce::Device::Capacitor::Traits::loadModelParameters
126 // Purpose :
127 // Special Notes : The addPar calls here were refactored and moved here
128 // from the model constructor. Those addPars had been
129 // in place from 2/4/2005.
130 // Scope : private
131 // Creator : David Baur
132 // Creation Date : 1/27/2014
133 //-----------------------------------------------------------------------------
134 ///
135 /// Loads the parameter definition into the model parameter map.
136 ///
137 /// @param p model parameter map
138 ///
139 /// @see Xyce::Device::Resistor::Traits::loadInstanceParameters
140 ///
142 {
143  p.addPar("CJ", 0.0, &Capacitor::Model::cj)
144  .setUnit(U_FARADMM2)
145  .setDescription("Junction bottom capacitance");
146  p.addPar("CJSW", 0.0, &Capacitor::Model::cjsw)
147  .setUnit(U_FARADMM1)
148  .setDescription("Junction sidewall capacitance");
149  p.addPar("DEFW", 1.e-6, &Capacitor::Model::defWidth)
150  .setUnit(U_METER)
151  .setDescription("Default device width");
152  p.addPar("NARROW", 0.0, &Capacitor::Model::narrow)
153  .setUnit(U_METER)
154  .setDescription("Narrowing due to side etching");
155  p.addPar("TC1", 0.0, &Capacitor::Model::tempCoeff1)
156  .setUnit(STANDARD);
157  p.addPar("TC2", 0.0, &Capacitor::Model::tempCoeff2)
158  .setUnit(STANDARD);
159  p.addPar("TNOM", 0.0, &Capacitor::Model::tnom)
160  .setUnit(STANDARD);
161 }
162 
163 // Class Instance
164 //-----------------------------------------------------------------------------
165 // Function : Instance::processParams
166 // Purpose :
167 // Special Notes :
168 // Scope : public
169 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
170 // Creation Date : 6/03/02
171 //-----------------------------------------------------------------------------
172 /// Process parameters.
173 ///
174 /// @return true on success
175 ///
176 /// In general, the processParams method is intended as a place for complex
177 /// manipulation of parameters that must happen if temperature or other
178 /// external changes happen.
179 ///
180 /// The Capacitor device supports an "AGE" parameter and a degradation
181 /// rate parameter that together determine how to modify the
182 /// capacitance given on the instance line. Further, Xyce supports a
183 /// "semiconductor capacitor" model which allows the user to specify
184 /// the capacitance through a combination of model parameters (junction
185 /// capacitance and junction sidewall capacitance) and instance parameters
186 /// (length and width).
187 ///
188 /// Both of these methods of capacitance value determination need to
189 /// be done after the normal processing of netlist parameters. That
190 /// processing is done here.
191 ///
192 /// @author Eric Keiter, SNL, Parallel Computational Sciences
193 /// @date 6/03/02
195 {
196 
197  baseCap = C;
198  if (!given("C") && given("AGE"))
199  {
200  UserError0(*this) << "Age defined, but no base capacitance given. Can't use age-aware with semiconductor capacitor options";
201  }
202 
203  // the age aware capacitor simply modifies the base capacitance.
204  if (given("AGE") && age >= 1)
205  {
206  baseCap = baseCap*(1-ageCoef*log10(age));
207  }
208 
209  if (!given("C") && !given("L"))
210  {
211  UserError0(*this) << "Could find neither C parameter or L in instance.";
212  }
213 
214  // Now we know we have either cap or length specified.
215  if (!given("C"))
216  {
217  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
218  {
219  dout() << "Semiconductor capacitor " << getName()
220  //<< Util::push << std::endl
221  << std::endl
222  << "cj = " << model_.cj << std::endl
223  << "cjsw = " << model_.cjsw << std::endl
224  << "width = " << width << std::endl
225  << "length = " << length << std::endl
226  << "narrow = " << model_.narrow <<
227  //Util::pop << std::endl;
228  std::endl;
229  }
230 
231  baseCap = C =
234  }
235 
236  // If there are any time dependent parameters, set their values for
237  // the current time.
238 
240 
241  return true;
242 }
243 
244 //-----------------------------------------------------------------------------
245 // Function : Instance::updateTemperature
246 // Purpose :
247 // Special Notes :
248 // Scope : public
249 // Creator : Tom Russo, Component Information and Models
250 // Creation Date : 02/27/01
251 //-----------------------------------------------------------------------------
252 ///
253 /// Update the parameters that depend on the temperature of the device
254 ///
255 /// @param temp_tmp temperature
256 ///
257 /// Xyce has a number of mechanisms that allow temperature to be changed
258 /// after a device has been instantiated. These include .STEP loops over
259 /// temperature. When temperature is changed, any device that has parameters
260 /// that depend on temperature must be updated. That updating happens here.
261 ///
262 /// The capacitor device supports temperature-dependent resistance through its
263 /// TC1 (linear dependence) and TC2 (quadratic dependence) parameters.
264 /// If these parameters are specified, the capacitance must be updated.
265 ///
266 bool Instance::updateTemperature ( const double & temp_tmp)
267 {
268  bool bsuccess = true;
269  double difference, factor;
270 
271  difference = temp - model_.tnom;
272  factor = 1.0 + tempCoeff1*difference +
273  tempCoeff2*difference*difference;
274  C = baseCap*factor;
275 
276  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
277  {
278  dout() << "Capacitor " << getName() << " updateTemperature()"
279  //<< Util::push << std::endl
280  << std::endl
281  << "C = " << C << std::endl
282  << "temp = " << temp << std::endl
283  << "temp_tmp = " << temp_tmp << std::endl
284  << "tnom = " << model_.tnom << std::endl
285  << "difference = " << difference << std::endl
286  << "tempCoeff1 = " << tempCoeff1 << std::endl
287  << "tempCoeff2 = " << tempCoeff2 << std::endl
288  << "baseCap = " << baseCap << std::endl
289  //<< "factor = " << factor << Util::pop << std::endl;
290  << "factor = " << factor << std::endl;
291  }
292 
293  return bsuccess;
294 }
295 
296 //-----------------------------------------------------------------------------
297 // Function : Instance::Instance
298 // Purpose : instance block constructor
299 // Special Notes :
300 // Scope : public
301 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
302 // Creation Date : 3/16/00
303 //-----------------------------------------------------------------------------
304 ///
305 /// Construct a Capacitor model from a "model block" that was created
306 /// by the netlist parser.
307 ///
308 /// @param configuration
309 /// @param model_block
310 /// @param factory_block
311 ///
312 /// @author Eric Keiter, SNL, Parallel Computational Sciences
313 /// @date 3/16/00
315  const Configuration & configuration,
316  const InstanceBlock & instance_block,
317  Model & model,
318  const FactoryBlock & factory_block)
319  : DeviceInstance(instance_block, configuration.getInstanceParameters(), factory_block),
320  model_(model),
321  expNumVars(0),
322  expPtr(0),
323  baseCap(0.0),
324  temp(getDeviceOptions().temp.getImmutableValue<double>()),
325  tempGiven(0),
326  tempCoeff1(0.0),
327  tempCoeff2(0.0),
328  tempCoeff1Given(false),
329  tempCoeff2Given(false),
330  li_Pos(-1),
331  li_Neg(-1),
332  li_QState(-1),
333  li_vcapState(-1),
334  li_capState(-1),
335  li_store_dev_i(-1),
336  APosEquPosNodeOffset(-1),
337  ANegEquPosNodeOffset(-1),
338  APosEquNegNodeOffset(-1),
339  ANegEquNegNodeOffset(-1),
340  APosEquBraNodeOffset(-1),
341  ANegEquBraNodeOffset(-1),
342  ABraEquBraNodeOffset(-1),
343  ABraEquPosNodeOffset(-1),
344  ABraEquNegNodeOffset(-1),
345 
347  qPosEquPosNodePtr(0),
348  qNegEquPosNodePtr(0),
349  qPosEquNegNodePtr(0),
350  qNegEquNegNodePtr(0),
351  fPosEquBraNodePtr(0),
352  fNegEquBraNodePtr(0),
353  fBraEquBraNodePtr(0),
354  fBraEquPosNodePtr(0),
355  fBraEquNegNodePtr(0),
356 #endif
357 
358  ICGiven(false),
359  solVarDepC(false),
360  IC(0)
361 
362 {
363  numIntVars = 0;
364  numExtVars = 2;
365  numStateVars = 1;
366  setNumStoreVars(0);
367  numLeadCurrentStoreVars = 1; // lead current DEV_I
368 
369  devConMap[0] = 1;
370  devConMap[1] = 2;
371 
372  /// Unlike the resistor, the capacitor's jacobian stamp is set up directly
373  /// in the constructor, and is not static. This is because the capacitor
374  /// supports some options that the resistor does not:
375  ///
376  /// - The capacitor instance line may be given an IC=value that
377  /// will be used as the initial voltage drop across the
378  /// capacitance at the operating point.
379  ///
380  /// - The capacitance on the instance line may be an expression
381  /// that is permitted to be a function of other solution variables (such
382  /// as voltages elsewhere in the circuit).
383  ///
384  /// Both of these require that the Jacobian stamp for the device be modified.
385  /// Use of a static Jacobian stamp would prevent this flexibility, because
386  /// the static stamp would be used by all capacitor devices, even those
387  /// that do not make use of the options.
388  ///
389  /// Since the setting of the Jacobian stamp in this otherwise simple device
390  /// is so complex, we will document how it is set here.
391  ///
392  /// In its simplest form, the charge \f$q_0\f$ on the capacitor is
393  /// \f$C(V_+-V_-)\f$. Thus, the current out of the positive node is
394  /// \f$d(q_0)/dt\f$, and the current "out" of the negative node is
395  /// \f$-d(q0)/dt\f$. In the Xyce formulation, we load \f$q_0\f$ into
396  /// the Q vector for the positive node, and \f$-q_0\f$ into the Q
397  /// vector for the negative node; the time integator will later
398  /// differentiate this to obtain the currents. Thus, the contribution
399  /// to the Jacobian from the capacitor (with constant capacitance and
400  /// no initial condition) will require loading the dQdx matrix with the
401  /// derivatives of \f$q_0\f$ with respect to the voltage nodes:
402  /// \f[
403  /// \left[\begin{array}{cc}
404  /// C & -C \\
405  /// -C & C
406  /// \end{array} \right] \f]
407  ///
408  /// The jacobian stamp in this case is the same as the one defined by
409  /// the resistor: it has two rows, one for the positive node equation
410  /// and one for negative node equation, and two columns, one for the
411  /// positive node and one for the negative. Column 0's value in each
412  /// row is 0 to reflect that the first nonzero value of the jacobian
413  /// row is the one corresponding to the positive node, and column 1's value
414  /// is 1 to reflect that the second nonzero corresponds to the dependence on
415  /// the negative node.
416  ///
417  /// If an initial condition is present, however, the circuit at DC is
418  /// the same as if there were only a voltage source across the
419  /// positive and negative nodes, and in transient it is the same as
420  /// the capacitor without the voltage source present. Thus, at DC the
421  /// current out of the positive node would be equal to the voltage
422  /// source branch current, and the current out of the negative node
423  /// would be the negative of that. Since these quantities are not
424  /// differentiated they would be placed in the F vector, no the Q
425  /// vector. An extra equation, called the "branch equation"
426  /// stipulates that the voltage drop between the positive and negative
427  /// node be equal to the initial condition, so this element of
428  /// the F vector would be loaded with \f$(V_+-V_-)-V_{ic}\f$.
429  /// Therefore in DC the dFdx matrix would be loaded with:
430  /// \f[
431  /// \left[ \begin{array}{ccc}
432  /// 0 & 0 & 1 \\
433  /// 0 & 0 & -1 \\
434  /// 1 & -1 & 0
435  /// \end{array}\right] \f]
436  /// The the first two rows are for the positive and negative node
437  /// equations, and the third row is for the branch equation. The
438  /// third column is for the branch current variable. At DC nothing
439  /// would be loaded into either the Q vector or its derivative.
440  ///
441  /// In transient, the loads into the Q vector are the same as
442  /// without the initial condition. Xyce requires that a single jacobian
443  /// stamp be used for both the dFdx and dQdx matrices, and does not allow
444  /// this matrix to vary between DC and transient. Thus the dQdx matrix would
445  /// be loaded with:
446  /// \f[
447  /// \left[ \begin{array}{ccc}
448  /// C & -C & 0 \\
449  /// -C & C & 0 \\
450  /// 0 & 0 & 0
451  /// \end{array}\right] \f]
452  /// The dFdx matrix must be loaded with a single value to turn off the
453  /// branch equation and prevent a singular Jacobian:
454  /// \f[
455  /// \left[ \begin{array}{ccc}
456  /// 0 & 0 & 0 \\
457  /// 0 & 0 & 0 \\
458  /// 0 & 0 & 1
459  /// \end{array}\right] \f]
460  /// The net result of this modification is that now, irrespective of whether
461  /// we are at DC or in transient, every element of the 3x3 matrix is potentially
462  /// nonzero in one or the other of dFdx or dQdx, and therefore our jacobian
463  /// stamp is also a dense 3x3 matrix, with each column's value equal to that
464  /// column's number.
465  ///
466  /// Finally, if the capacitance is solution-variable dependent, each of the
467  /// rows for positive and negative nodes must be augmented with an additional
468  /// column for each variable that the capacitance depends on. These rows
469  /// are similarly dense, and each value of the jacobian stamp for each column
470  /// is equal to its column number.
471  if( jacStamp.empty() )
472  {
473  jacStamp_IC.resize(3);
474  jacStamp_IC[0].resize(3);
475  jacStamp_IC[1].resize(3);
476  jacStamp_IC[2].resize(3);
477  jacStamp_IC[0][0] = 0;
478  jacStamp_IC[0][1] = 1;
479  jacStamp_IC[0][2] = 2;
480  jacStamp_IC[1][0] = 0;
481  jacStamp_IC[1][1] = 1;
482  jacStamp_IC[1][2] = 2;
483  jacStamp_IC[2][0] = 0;
484  jacStamp_IC[2][1] = 1;
485  jacStamp_IC[2][2] = 2;
486 
487  jacStamp.resize(2);
488  jacStamp[0].resize(2);
489  jacStamp[1].resize(2);
490  jacStamp[0][0] = 0;
491  jacStamp[0][1] = 1;
492  jacStamp[1][0] = 0;
493  jacStamp[1][1] = 1;
494  }
495 
496 
497  // Set params to constant default values:
498  setDefaultParams ();
499 
500  // Set params according to instance line and constant defaults from metadata:
501  setParams (instance_block.params);
502 
503  // Set any non-constant parameter defaults:
504 
505  if (!given("W"))
507  if (!given("TEMP"))
508  temp = getDeviceOptions().temp.getImmutableValue<double>();
509 
510  if (!tempCoeff1Given)
512  if (!tempCoeff2Given)
514 
515 
516 
517  // Handle case where capacitance is solution-variable dependent:
518  if (dependentParams.size()>0)
519  {
520  std::vector<sDepend>::iterator d;
521  std::vector<sDepend>::iterator begin=dependentParams.begin();
522  std::vector<sDepend>::iterator end=dependentParams.end();
523 
524  for (d=begin; d!=end; ++d)
525  {
526  if (d->name != "C")
527  {
528  UserError0(*this) << "Solution-variable-dependent parameter other than C detected";
529  }
530  else
531  {
532  expNumVars = d->n_vars;
533  expPtr = d->expr;
534 
535  if (expNumVars > 0 || expPtr->isTimeDependent())
536  {
537  solVarDepC = true;
538  // To do the proper integration of the charge, we need to save the
539  // voltage drop, the old capacitance and
540  // the derivatives of Q and C from the last step.
541  numStateVars += 2+2*expNumVars;
542 
543  if (expPtr->getNumDdt() != 0)
544  {
545  UserError0(*this) << "Solution-variable-dependent expression contains time derivatives";
546  }
547 
548  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0)
549  {
550  dout() << "Capacitor " << getName()
551  << ": Found solution-dependent parameter C depending on " << expNumVars << " variables" << std::endl;
552  if (expPtr->isTimeDependent())
553  {
554  dout() << " " << "Expression is time-dependent." << std::endl;
555  }
556  else
557  {
558  dout() << " " << "Expression is not time-dependent." << std::endl;
559  }
560  dout() << " " << "Expression depends on " << expPtr->num_vars() << " quantity, of which " << expPtr->num_vars()-expNumVars << " are not solution vars. " << std::endl;
561 
562  }
563 
564  // We now need to extend the pos and neg rows of the jacstamps
565  // to account for the additional dependencies:
566 
567  jacStamp[0].resize(2+expNumVars);
568  jacStamp[1].resize(2+expNumVars);
569  jacStamp_IC[0].resize(3+expNumVars);
570  jacStamp_IC[1].resize(3+expNumVars);
571  for (int i=0; i<expNumVars; ++i)
572  {
573  jacStamp[0][2+i]=2+i;
574  jacStamp[1][2+i]=2+i;
575 
576  jacStamp_IC[0][3+i]=3+i;
577  jacStamp_IC[1][3+i]=3+i;
578  }
579 
580  // finally, allocate space to hold the derivatives of C w.r.t.
581  // the variables it depends on:
582  expVarDerivs.resize(expNumVars);
583  // and LIDs for state vector
584  li_dQdXState.resize(expNumVars);
585  li_dCdXState.resize(expNumVars);
586 
587  }
588  }
589  }
590  }
591 
592  // Calculate any parameters specified as expressions:
593 
595 
596  // calculate dependent (ie computed) params:
597 
598  processParams ();
599 
600  // we're gonna have to fake a voltage source at the operating point
601  if (ICGiven ) numIntVars = 1;
602 }
603 
604 //-----------------------------------------------------------------------------
605 // Function : Instance::~Instance
606 // Purpose : destructor
607 // Special Notes :
608 // Scope : public
609 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
610 // Creation Date : 3/16/00
611 //-----------------------------------------------------------------------------
613 {
614 }
615 
616 // Additional Declarations
617 
618 //-----------------------------------------------------------------------------
619 // Function : Instance::registerLIDs
620 // Purpose :
621 // Special Notes :
622 // Scope : public
623 // Creator : Robert Hoekstra, SNL, Parallel Computational Sciences
624 // Creation Date : 6/20/02
625 //-----------------------------------------------------------------------------
626 ///
627 /// Register local IDs
628 ///
629 /// Register the local internal and external node IDs.
630 ///
631 /// @param intLIDVecRef internal local IDs from topology package
632 /// @param extLIDVecRef external local IDs from topology package
633 ///
634 /// Instantiation (calling the device constructor) of the device
635 /// sets up variables numIntVars and numExtVars, the numbers of internal and
636 /// external variables associated with the device. This information is then
637 /// used by the Topology package to assign locations in the solution vector
638 /// (and all other vectors of the same shape) for those variables.
639 /// The "Local IDs" (LIDs) of these locations are provided by Topology
640 /// so the device can know where to load its data.
641 ///
642 /// This method saves the LIDs from Topology and associates each one with
643 /// a particular local name for the internal or external variable. They
644 /// are then used when we load the F and Q vectors.
645 ///
646 /// The Capacitor device has no internal variables, so this method makes no use
647 /// of the intLIDVecRef array.
648 ///
649 /// @author Robert Hoekstra, SNL, Parallel Computational Sciences
650 /// @date 6/20/02
651 void Instance::registerLIDs( const std::vector<int> & intLIDVecRef,
652  const std::vector<int> & extLIDVecRef)
653 
654 {
655  AssertLIDs(intLIDVecRef.size() == numIntVars);
656  AssertLIDs(extLIDVecRef.size() == numExtVars);
657 
658  // Copy over the local ID lists:
659  intLIDVec = intLIDVecRef;
660  extLIDVec = extLIDVecRef;
661 
662  // Now use these lists to obtain the indices into the linear algebra
663  // entities. This assumes an order. For the matrix indices, first do the
664  // rows.
665 
666  li_Pos = extLIDVec[0];
667  li_Neg = extLIDVec[1];
668 
669  // For fake voltage source at operating point
670  if (ICGiven)
671  {
672  li_Bra = intLIDVec[0];
673  }
674 
675  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0)
676  {
677  dout() << "Capacitor " << getName() << " Instance::registerLIDs"
678  // << Util::push << std::endl
679  << std::endl
680  << "li_Pos_ = " << li_Pos << std::endl
681  << "li_Neg_ = " << li_Neg << std::endl;
682 
683  if (ICGiven)
684  dout() << "li_Bra = "<< li_Bra<< std::endl;
685 
686  //dout() << Util::pop << std::endl;
687  dout() << std::endl;
688  }
689 }
690 
691 //-----------------------------------------------------------------------------
692 // Function : Instance::registerStateLIDs
693 // Purpose :
694 // Special Notes :
695 // Scope : public
696 // Creator : Robert Hoekstra, SNL, Parallel Computational Sciences
697 // Creation Date : 6/20/02
698 //-----------------------------------------------------------------------------
699 ///
700 /// Register the local state IDs
701 ///
702 /// @param staLIDVecRef State variable local IDs
703 ///
704 /// In general, devices may declare at construction that they require storage
705 /// locations in the "state vector." Topology assigns locations in the
706 /// state vector and returns "Local IDs" (LIDs) for devices to use for their
707 /// state vector entries. If a device uses state vector entries, it
708 /// uses the registerStateLIDs method to save the local IDs for later use.
709 ///
710 /// The capacitor has at least one state variable (the charge) and as many
711 /// as three (the charge plus state variables used to support the "voltage
712 /// dependent capacitance" feature) plus the number of variables on whihc
713 /// the capacitance depends.
714 ///
715 /// @note The storage of the charge as a state variable when the capacitance
716 /// is a constant is a holdover from older implementations, and in the future
717 /// may be saved only as part of the voltage-dependent capacitance feature.
718 ///
719 /// @author Robert Hoekstra, SNL, Parallel Computational Sciences
720 /// @date 06/20/02
721 void Instance::registerStateLIDs( const std::vector<int> & staLIDVecRef)
722 {
723  AssertLIDs(staLIDVecRef.size() == numStateVars);
724 
725  int i=0;
726 
727  // Copy over the global ID lists:
728  staLIDVec = staLIDVecRef;
729 
730  li_QState = staLIDVec[i++];
731 
732  // If the capacitance is voltage dependent, we have additional state vars
733  if (solVarDepC)
734  {
735  li_vcapState = staLIDVec[i++];
736  li_capState = staLIDVec[i++];
737 
738  for (int j = 0; j<expNumVars; ++j)
739  {
740  li_dQdXState[j] = staLIDVec[i++];
741  }
742 
743  for (int j = 0; j<expNumVars; ++j)
744  {
745  li_dCdXState[j] = staLIDVec[i++];
746  }
747  }
748 
749 }
750 
751 
752 //-----------------------------------------------------------------------------
753 // Function : Instance::registerStoreLIDs
754 // Purpose : One store var for device current.
755 // Special Notes :
756 // Scope : public
757 // Creator : Richard Schiek, Electrical Systems Modeling
758 // Creation Date : 01/23/2013
759 //-----------------------------------------------------------------------------
760 /// Register the local store IDs
761 ///
762 /// In addition to state vector, Xyce maintains a separate datastructure
763 /// called a "store" vector. As with other such vectors, the device
764 /// declares at construction time how many store vector entries it needs,
765 /// and later Topology assigns locations for devices, returning LIDs.
766 ///
767 /// These LIDs are stored in this method for later use.
768 ///
769 /// The Capacitor device uses exactly one "store vector" element, where
770 /// it keeps the "lead current" that may be used on .PRINT lines as
771 /// "I(C1)" for the current through resistor R1.
772 ///
773 /// @param stoLIDVecRef Store variable local IDs
774 ///
775 /// @author Richard Schiek, Electrical Systems Modeling
776 /// @date 1/23/2013
777 void Instance::registerStoreLIDs(const std::vector<int> & stoLIDVecRef )
778 {
779  AssertLIDs(stoLIDVecRef.size() == getNumStoreVars());
780 
781  if( loadLeadCurrent )
782  {
783  li_store_dev_i = stoLIDVecRef[0];
784  }
785 }
786 
787 
788 //-----------------------------------------------------------------------------
789 // Function : Instance::getIntNameMap
790 // Purpose :
791 // Special Notes :
792 // Scope : public
793 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
794 // Creation Date : 05/13/05
795 //-----------------------------------------------------------------------------
796 /// Set up names for purely internal solution variables
797 ///
798 /// The capacitor device's initial condition code is implemented as
799 /// putting a DC voltage source of the given value across the capacitor,
800 /// to assure that the circuit is completely consistent at the operating
801 /// point with having that voltage across the capacitor.
802 ///
803 /// In the event that an initial condition is given, there is an extra
804 /// solution variable, the branch current of the voltage source. We give it
805 /// name that is the name of this capacitor with "_branch" appended.
806 ///
807 std::map<int,std::string> & Instance::getIntNameMap ()
808 {
809  // set up the internal name map, if it hasn't been already.
810  if (ICGiven)
811  {
812  if (intNameMap.empty ())
813  {
814  std::string tmpstr;
815  tmpstr = getName()+"_branch";
816  spiceInternalName (tmpstr);
817  intNameMap[li_Bra] = tmpstr;
818  }
819  }
820 
821  return intNameMap;
822 }
823 
824 //-----------------------------------------------------------------------------
825 // Function : Instance::getStoreNameMap
826 // Purpose :
827 // Special Notes :
828 // Scope : public
829 // Creator : Rich Schiek, SNL, Electrical Systems Modeling
830 // Creation Date : 01/23/2013
831 //-----------------------------------------------------------------------------
832 /// Populates and returns the store name map.
833 ///
834 /// If the DeviceInstance::storeNameMap is empty, populate it.
835 ///
836 /// @return reference to the DeviceInstance::storeNameMap
837 ///
838 /// For the purpose of lead currents, store vector elements must be given
839 /// names that can be used to locate lead currents at print time.
840 /// When a netlist attempts to print, say, "I(C1)" the output code looks for
841 /// an entry in the store vector named C1:DEV_I.
842 ///
843 /// This method does the assignment of names to store vector elements.
844 ///
845 /// @author Richard Schiek, Electrical Systems Modeling
846 /// @date 1/23/2013
847 std::map<int,std::string> & Instance::getStoreNameMap ()
848 {
849  if( loadLeadCurrent && storeNameMap.empty () )
850  {
851  std::string tmpstr(getName());
852  spiceInternalName (tmpstr);
853  tmpstr = getName()+":DEV_I";
854  storeNameMap[li_store_dev_i] = tmpstr;
855  }
856 
857  return storeNameMap;
858 }
859 
860 
861 //-----------------------------------------------------------------------------
862 // Function : Instance::jacobianStamp
863 // Purpose :
864 // Special Notes :
865 // Scope : public
866 // Creator : Robert Hoekstra, SNL, Parallel Computational Sciences
867 // Creation Date : 08/27/02
868 //-----------------------------------------------------------------------------
869 ///
870 /// Return Jacobian stamp that informs topology of the layout of the
871 /// resistor jacobian.
872 ///
873 /// @return const reference to a std::vector of std::vector of
874 /// integers describing Jacobian stamp shape
875 //
876 /// The Jacobian stamp describes the shape of the Jacobian to the
877 /// Topology subsystem. The Topology subsystem, in turn, returns
878 /// the offsets into the matrix and solution vectors where this
879 /// instance data is located.
880 ///
881 /// The Jacobian stamp of the capacitor depends on whether an initial
882 /// condition is given or not.
883 ///
884 /// @author Robert Hoekstra
885 /// @date 8/20/2001
886 const std::vector< std::vector<int> > & Instance::jacobianStamp() const
887 {
888  if (ICGiven)
889  return jacStamp_IC;
890  else
891  return jacStamp;
892 }
893 
894 //-----------------------------------------------------------------------------
895 // Function : Instance::registerJacLIDs
896 // Purpose :
897 // Special Notes :
898 // Scope : public
899 // Creator : Robert Hoekstra, SNL, Parallel Computational Sciences
900 // Creation Date : 08/27/02
901 //-----------------------------------------------------------------------------
902 ///
903 /// Register the Jacobian local IDs
904 ///
905 /// @param jacLIDVec Jacobian local Ids
906 ///
907 /// @see Xyce::Device::Capacitor::Instance::Capacitor
908 ///
909 /// Having established local IDs for the solution variables, Topology must
910 /// also assign local IDs for the elements of the Jacobian matrix.
911 ///
912 /// For each non-zero element that was identified in the jacobianStamp,
913 /// Topology will assign a Jacobian LID. The jacLIDVec will have the
914 /// same structure as the JacStamp, but the values in the jacLIDVec will
915 /// be offsets into the row of the sparse Jacobian matrix corresponding
916 /// to the non-zero identified in the stamp.
917 ///
918 /// These offsets are stored in this method for use later when we load
919 /// the Jacobian.
920 ///
921 /// @note Because the capacitor's Jacobian stamp depends on whether an
922 /// initial condition was given or not, this method is slightly more
923 /// complex than the corresponding method in the Resistor. The method
924 /// is further complicated by the possibilty that the capacitance may
925 /// be given as an expression depending on solution variables; in this case,
926 /// the capacitor jacstamp has extra columns for the device's dependence on
927 /// those other variables.
928 ///
929 /// @see Xyce::Device::Resistor::Instance::registerJacLIDs
930 ///
931 /// @author Robert Hoekstra, SNL, Parallel Computational Sciences
932 /// @date 08/27/02
933 void Instance::registerJacLIDs( const std::vector< std::vector<int> > & jacLIDVec )
934 {
935  DeviceInstance::registerJacLIDs( jacLIDVec );
936 
937  APosEquPosNodeOffset = jacLIDVec[0][0];
938  APosEquNegNodeOffset = jacLIDVec[0][1];
939  ANegEquPosNodeOffset = jacLIDVec[1][0];
940  ANegEquNegNodeOffset = jacLIDVec[1][1];
941 
942  if (ICGiven)
943  {
944  APosEquBraNodeOffset = jacLIDVec[0][2];
945  ANegEquBraNodeOffset = jacLIDVec[1][2];
946  ABraEquPosNodeOffset = jacLIDVec[2][0];
947  ABraEquNegNodeOffset = jacLIDVec[2][1];
948  ABraEquBraNodeOffset = jacLIDVec[2][2];
949  }
950  // set offsets if we have a solution-variable dependent C
951  if (solVarDepC)
952  {
953  int depVarsBaseIndex = 2;
954  if (ICGiven)
955  {
956  depVarsBaseIndex=3;
957  }
958 
961 
962  for ( int i=0; i<expNumVars; ++i)
963  {
964  APosEquDepVarOffsets[i] = jacLIDVec[0][depVarsBaseIndex+i];
965  ANegEquDepVarOffsets[i] = jacLIDVec[1][depVarsBaseIndex+i];
966  }
967  }
968 
969  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
970  {
971  dout() << "Capacitor " << getName() << " Instance::registerJacLIDs"
972  //<< Util::push << std::endl
973  << std::endl
974  << "APosEquPosNodeOffset: " << APosEquPosNodeOffset << std::endl
975  << "APosEquNegNodeOffset: " << APosEquNegNodeOffset << std::endl
976  << "ANegEquPosNodeOffset: " << ANegEquPosNodeOffset << std::endl
977  << "ANegEquNegNodeOffset: " << ANegEquNegNodeOffset << std::endl
978  << "APosEquBraNodeOffset: " << APosEquBraNodeOffset << std::endl
979  << "ANegEquBraNodeOffset: " << ANegEquBraNodeOffset << std::endl
980  << "ABraEquPosNodeOffset: " << ABraEquPosNodeOffset << std::endl
981  << "ABraEquNegNodeOffset: " << ABraEquNegNodeOffset << std::endl
982  << "ABraEquBraNodeOffset: " << ABraEquBraNodeOffset << std::endl;
983  if (solVarDepC)
984  {
985  for ( int i=0; i<expNumVars; ++i)
986  {
987  dout() << "APosEquDepVarOffsets["<<i<<"]: " << APosEquDepVarOffsets[i] << std::endl
988  << "ANegEquDepVarOffsets["<<i<<"]: " << ANegEquDepVarOffsets[i] << std::endl;
989  }
990  }
991  //dout() << Util::pop << std::endl;
992  dout() << std::endl;
993  }
994 }
995 
996 //-----------------------------------------------------------------------------
997 // Function : Instance::setupPointers
998 // Purpose :
999 // Special Notes :
1000 // Scope : public
1001 // Creator : Eric Keiter, SNL
1002 // Creation Date : 11/30/08
1003 //-----------------------------------------------------------------------------
1004 ///
1005 /// Setup direct access pointer to solution matrix and vectors.
1006 ///
1007 /// @see Xyce::Device::Capacitor::Instance::registerJacLIDs
1008 ///
1009 /// As an alternative to the row offsets defined in registerJacLIDs, it
1010 /// is also possible to obtain direct pointers of the Jacobian elements.
1011 ///
1012 /// This method uses the offsets obtained in registerJacLIDs to retrieve
1013 /// the pointers.
1014 ///
1015 /// In the resistor device the pointers to the matrix are only saved
1016 /// (and are only used for matrix access) if
1017 /// Xyce_NONPOINTER_MATRIX_LOAD is NOT defined at compile time. For
1018 /// some devices the use of pointers instead of array indexing can be
1019 /// a performance enhancement.
1020 ///
1021 /// Use of pointers in this device is disabled by defining
1022 /// Xyce_NONPOINTER_MATRIX_LOAD at compile time. When disabled, array
1023 /// indexing with the offsets from registerJacLIDs is used in
1024 /// the matrix load methods.
1025 ///
1026 /// @author Eric Keiter, SNL
1027 /// @date 11/30/08
1029 {
1030 #ifndef Xyce_NONPOINTER_MATRIX_LOAD
1031  N_LAS_Matrix & dFdx = *(extData.dFdxMatrixPtr);
1032  N_LAS_Matrix & dQdx = *(extData.dQdxMatrixPtr);
1033 
1038 
1039  if (solVarDepC)
1040  {
1043 
1044  for (int i=0; i<expNumVars; ++i)
1045  {
1048  }
1049  }
1050 
1051  // there are no contributions to the dFdx matrix from dependent C's, so
1052  // we don't bother with those pointers.
1053 
1054  if (ICGiven)
1055  {
1061  }
1062 #endif
1063 }
1064 
1065 //-----------------------------------------------------------------------------
1066 // Function : Instance::updatePrimaryState
1067 // Purpose :
1068 // Special Notes :
1069 // Scope : public
1070 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1071 // Creation Date : 01/29/01
1072 //-----------------------------------------------------------------------------
1073 ///
1074 /// Update the state variables.
1075 ///
1076 /// @return true on success
1077 ///
1078 /// The capacitor's state variables are used to store the charge on
1079 /// the capacitor. In the case of a constant capacitance the charge
1080 /// is \f$q_0=CV\f$, but the computation is much more complex if the
1081 /// capacitance is variable.
1082 ///
1083 /// @note This method is called by the default implementation of the
1084 /// loadState master function. While the Capacitor class reimplements
1085 /// the "Master" "loadState" function that loads the contributions
1086 /// from all capacitor devices in a single loop, because this method
1087 /// is so complicated for the case of solution-dependent capacitances,
1088 /// the overloaded method falls back on this function in that case.
1089 ///
1090 /// @note Even though this method IS called in some cases, note that it does
1091 /// NOT call updateIntermediateVars as other device updatePrimaryState methods
1092 /// do. The capacitor device doesn't actually *HAVE* an updateIntermediateVars
1093 /// method, and all the hard work is done either in the Master class
1094 /// updateState function directly for the simple, constant-capacitance
1095 /// case, or here, for the solution-variable-dependent-capacitance case.
1096 ///
1097 /// @see Xyce::Device::Capacitor::Master::updateState
1098 ///
1099 /// @author Eric Keiter, SNL, Parallel Computational Sciences
1100 /// @date 01/29/01
1101 ///
1103 {
1104  double * solVec = extData.nextSolVectorRawPtr;
1105  double * staVec = extData.nextStaVectorRawPtr;
1106  double v_pos = solVec[li_Pos];
1107  double v_neg = solVec[li_Neg];
1108 
1109  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1110  {
1111  dout() << " ----------------------------------" << std::endl;
1112  dout() << "Instance::updatePrimaryState:" << std::endl;
1113  }
1114 
1115  vcap = v_pos-v_neg;
1116 
1117  if( getSolverState().dcopFlag && ICGiven ) vcap = IC;
1118 
1119  if (!solVarDepC)
1120  {
1121  // Obtain the "current" value for the charge stored in the capacitor.
1122  q0 = C*vcap;
1123  staVec[li_QState] = q0;
1124  }
1125  else
1126  {
1127  // The capacitance depends on solution variables, the work load just
1128  // went up.
1129 
1130  // here, we're just calling evaluate
1131  // to get the derivatives of C, and discarding the actual value
1132  // of C (which has already been stored)
1133  double junk;
1134  expPtr->evaluate(junk,expVarDerivs);
1135 
1136 
1137  // At DC, the charge really still is V*C.
1138  if (getSolverState().dcopFlag)
1139  {
1140  q0 = vcap*C;
1141  // dQ/dX is just dC/dX*vcap
1142  for (int i=0;i<expNumVars; ++i)
1143  {
1144  expVarDerivs[i] *= vcap;
1145  }
1146  }
1147  else
1148  {
1149  ///
1150  /// For solution-variable dependent cap in transient, we can't
1151  /// use the expression \f$q_0=CV\f$ because the capacitance is
1152  /// actually dQ/dV, not Q/V. We must integrate CdV to get the
1153  /// charge. We approximate this by incrementally adding
1154  /// C'*deltaV as V changes. C' is the average capacitance
1155  /// between this and the previous step. Using the average
1156  /// assures charge conservation, at least when C is a function
1157  /// of vcap alone.
1158  ///
1159  double * oldstaVector = extData.currStaVectorRawPtr;
1160  double oldC;
1161  double oldVcap;
1162  q0=oldstaVector[li_QState];
1163  oldC=oldstaVector[li_capState];
1164  oldVcap=oldstaVector[li_vcapState];
1165 
1166  q0 += 0.5*(oldC+C)*(vcap-oldVcap);
1167 
1168  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1169  {
1170  dout() << " Derivatives of C w.r.t variables: " << std::endl;
1171  for (int i=0;i<expNumVars;++i)
1172  {
1173  dout() << " expVarDerivs[ "<< i << " ] = " << expVarDerivs[i] << std::endl;
1174  }
1175  }
1176  ///
1177  /// When C is not a function of vcap alone, we have additional derivative
1178  /// terms that must also be integrated for proper computation of
1179  /// all dQdx entries.
1180  ///
1181  // If expressions contain the "ddt" (time differentiation)
1182  // function, everything becomes harder. So for now, we're going
1183  // to disallow use of ddt in C, so we don't have to deal with
1184  // that added complexity. This restriction is enforced in the
1185  // constructor, where we throw a fatal error if the user gives
1186  // us a ddt-dependent C. This code is NOT sufficient if ddt is allowed
1187  // in capacitance expressions.
1188  //
1189  // Now we have some trickiness because we are integrating CdV to get Q.
1190  // In order to get dQ/dX we have two cases:
1191  // X is one of our capacitor's voltage nodes: dQ/dX = C or -C depending
1192  // on whether X is the pos or negative node
1193  // X is NOT one of our nodes: dQ/dX = integral( dC/dX *dV)
1194 
1195  // For now, because we don't have an easy way to tell which of our
1196  // expression nodes is which, we'll just calculate all the dC/dX and dQ/dX
1197  // the same way. When it comes time to assemble the jacobian, we'll use
1198  // the node/equation offsets to know when to skip adding in this component.
1199 
1200  // The logic here is similar to the computation of the charge itself.
1201  // We'll use "expVarDerivs" to hold the final dQ/dX values.
1202 
1203 
1204  // Need to save the dC/dX value for next step.
1205  for (int i=0;i<expNumVars; ++i)
1206  {
1207  staVec[li_dCdXState[i]] = expVarDerivs[i];
1208  }
1209 
1210  // we have to integrate
1211 
1212  // dQ/dx = olddQdX + .5*(olddCdX+newdCdX)*(vcap-oldvcap)
1213 
1214  for (int i=0; i< expNumVars; ++i)
1215  {
1216  expVarDerivs[i] = oldstaVector[li_dQdXState[i]]
1217  + 0.5*(oldstaVector[li_dCdXState[i]]+expVarDerivs[i])*
1218  (vcap-oldstaVector[li_vcapState]);
1219 
1220  }
1221  }
1222  // Regardless of whether it's dcop or not, expVarDerivs now contains
1223  // dQ/dX for all the X's. It's WRONG if X is one of our voltage nodes,
1224  // so we have to be careful not to use it in that case. This logic
1225  // is handled down in loadDAEdQdx
1226  // Save to state:
1227  for (int i=0;i<expNumVars; ++i)
1228  {
1229  staVec[li_dQdXState[i]] = expVarDerivs[i];
1230  }
1231 
1232 
1233 
1234  staVec[li_QState] = q0;
1235  staVec[li_vcapState]=vcap;
1236  staVec[li_capState]=C;
1237  }
1238  return true;
1239 }
1240 
1241 
1242 //-----------------------------------------------------------------------------
1243 // Function : Instance::loadDAEQVector
1244 //
1245 // Purpose : Loads the Q-vector contributions for a single
1246 // capacitor instance.
1247 //
1248 // Special Notes : The "Q" vector is part of a standard DAE formalism in
1249 // which the system of equations is represented as:
1250 //
1251 // f(x) = dQ(x)/dt + F(x) - B(t) = 0
1252 //
1253 // Scope : public
1254 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1255 // Creation Date : 01/24/03
1256 //-----------------------------------------------------------------------------
1257 ///
1258 /// Load the DAE Q vector.
1259 ///
1260 /// @return true on success
1261 ///
1262 /// The Xyce DAE formulation solves the differential-algebraic
1263 /// equations \f$F(x)+dQ(x)/dt=0\f$ These are vector equations
1264 /// resulting from the modified nodal analysis of the circuit.
1265 ///
1266 /// This method loads the Q-vector contributions for a single capacitor
1267 /// instance.
1268 ///
1269 /// In this method, the offsets defined in registerLIDs are used to
1270 /// store the device's Q contributions into the Q vector.
1271 ///
1272 /// The Q vector is used for devices that store charge or magnetic
1273 /// energy. The capacitor is such a device
1274 ///
1275 /// @note This method is called by the default implementation of the
1276 /// loadDAEVectors master function. Since the capacitor class
1277 /// reimplements the "Master" "loadDAEVectors" function that loads the
1278 /// contributions from all capacitor devices in a single loop, THIS
1279 /// FUNCTION IS NOT ACTUALLY USED. The loadDAEQVector method is only
1280 /// called when a device class does not re-implement the master class.
1281 /// This can be a source of confusion when attempting to modify the Capacitor
1282 /// device, or any other device that reimplements the Master classes.
1283 ///
1284 /// @see Xyce::Device::Capacitor::Master::loadDAEVectors
1285 ///
1286 /// @author Eric Keiter, SNL, Parallel Computational Sciences
1287 /// @date 01/24/03
1289 {
1290  double * qVec = extData.daeQVectorRawPtr;
1291  qVec[li_Pos] += q0;
1292  qVec[li_Neg] += -q0;
1293 
1294  return true;
1295 }
1296 
1297 //-----------------------------------------------------------------------------
1298 // Function : Instance::loadDAEFVector
1299 //
1300 // Purpose : Loads the F-vector contributions for a single
1301 // capacitor instance.
1302 //
1303 // Special Notes : See the special notes for loadDAEFVector.
1304 //
1305 // For the capacitor this doesn't do anything, except in
1306 // the case of IC= being specified. In that case, then
1307 // some extra stuff is contributed that doesn't have time
1308 // derivatives.
1309 //
1310 // Scope : public
1311 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1312 // Creation Date : 01/24/03
1313 //-----------------------------------------------------------------------------
1314 ///
1315 /// Load the DAE F vector.
1316 ///
1317 /// @return true on success
1318 ///
1319 /// The Xyce DAE formulation solves the differential-algebraic
1320 /// equations \f$F(x)+dQ(x)/dt=0\f$ These are vector equations
1321 /// resulting from the modified nodal analysis of the circuit.
1322 ///
1323 /// This method loads the F-vector contributions for a single capacitor
1324 /// instance.
1325 ///
1326 /// In this method, the offsets defined in registerLIDs are used to
1327 /// store the device's F contributions into the F vector.
1328 ///
1329 /// The only time a capacitor adds anything to the F vector is in the
1330 /// DC phase of a computation if and only if an initial condition is given
1331 /// on the capacitor instance line.
1332 ///
1333 /// @note This method is called by the default implementation of the
1334 /// loadDAEVectors master function. Since the Capacitor class
1335 /// reimplements the "Master" "loadDAEVectors" function that loads the
1336 /// contributions from all capacitor devices in a single loop, THIS
1337 /// FUNCTION IS NOT ACTUALLY USED. The loadDAEFVector method is only
1338 /// called when a device class does not re-implement the master class.
1339 /// This can be a source of confusion when attempting to modify the Capacitor
1340 /// device, or any other device that reimplements the Master classes.
1341 ///
1342 /// @see Xyce::Device::Capacitor::Master::loadDAEVectors
1343 ///
1344 /// @author Eric Keiter, SNL, Parallel Computational Sciences
1345 /// @date 01/24/03
1347 {
1348  bool bsuccess = true;
1349  double Vpos = 0.0;
1350  double Vneg = 0.0;
1351  double v_tmp = 0.0;
1352  double * fVec = extData.daeFVectorRawPtr;
1353 
1354  if (ICGiven && getSolverState().dcopFlag)
1355  {
1356  // If we're doing the operating point and we have an initial condition,
1357  // get the current from the branch equation
1358  Vpos = (*extData.nextSolVectorPtr)[li_Pos];
1359  Vneg = (*extData.nextSolVectorPtr)[li_Neg];
1360 
1361  // load current into the F vector
1362  fVec[li_Pos] += (*extData.nextSolVectorPtr)[li_Bra];
1363  fVec[li_Neg] += -(*extData.nextSolVectorPtr)[li_Bra];
1364 
1365  if( loadLeadCurrent )
1366  {
1367  double * stoVec = extData.nextStoVectorRawPtr;
1369  }
1370  }
1371 
1372  // Initial condition stuff.
1373  v_tmp=0;
1374  if (ICGiven && getSolverState().dcopFlag)
1375  {
1376  v_tmp= (Vpos-Vneg-IC);
1377  }
1378 
1379  // Do this whenever there's a Branch equation, but only if there is one.
1380  // We'll be using 0 if we're not the OP.
1381  if (ICGiven)
1382  {
1383  fVec[li_Bra] += v_tmp;
1384  }
1385 
1386  return bsuccess;
1387 }
1388 
1389 //-----------------------------------------------------------------------------
1390 // Function : Instance::loadDAEdQdx
1391 //
1392 // Purpose : Loads the dQdx-matrix contributions for a single
1393 // capacitor instance.
1394 //
1395 // Special Notes : The "Q" vector is part of a standard DAE formalism in
1396 // which the system of equations is represented as:
1397 //
1398 // f(x) = dQ(x)/dt + F(x) - B(t) = 0
1399 //
1400 // Scope : public
1401 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1402 // Creation Date : 03/05/04
1403 //-----------------------------------------------------------------------------
1404 ///
1405 /// Load the DAE the derivative of the Q vector with respect to the
1406 /// solution vector x, dFdx
1407 ///
1408 /// Loads the contributions for a single resistor instance to the
1409 /// dFdx matrix (the Q contribution to the Jacobian).
1410 ///
1411 /// This method uses the Jacobian LIDs (row offsets) that were stored by
1412 /// registerJacLIDs.
1413 ///
1414 /// @see Xyce::Device::Capacitor::Instance::registerJacLIDs
1415 ///
1416 /// @note This method is called by the default implementation of the
1417 /// loadDAEMatrices master function. Since the Capacitor class
1418 /// reimplements the "Master" "loadDAEMatrices" function that loads the
1419 /// contributions from all capacitor devices in a single loop, THIS
1420 /// FUNCTION IS NOT ACTUALLY USED. The loadDAEdFdx method is only
1421 /// called when a device class does not re-implement the master class.
1422 /// This can be a source of confusion when attempting to modify the capacitor
1423 /// device, or any other device that reimplements the Master classes.
1424 ///
1425 /// @see Xyce::Device::Capacitor::Master::loadDAEMatrices
1426 ///
1427 /// @return true on success
1428 ///
1429 /// @author Eric Keiter, SNL, Parallel Computational Sciences
1430 /// @date 03/05/04
1432 {
1433  if (!(ICGiven&& getSolverState().dcopFlag))
1434  {
1435  N_LAS_Matrix & dQdx = *(extData.dQdxMatrixPtr);
1436  dQdx[li_Pos][APosEquPosNodeOffset] += C;
1437  dQdx[li_Pos][APosEquNegNodeOffset] -= C;
1438  dQdx[li_Neg][ANegEquPosNodeOffset] -= C;
1439  dQdx[li_Neg][ANegEquNegNodeOffset] += C;
1440 
1441 
1442  // Remember the comments in updatePrimaryState: expVarDerivs
1443  // contains dQ/dX, but they are only correct when X is not one of
1444  // our nodal voltages. If X *IS* one of our nodal voltages, dQ/dX
1445  // is either C or -C and is already handled above. We need only
1446  // do the stuff below for the dependencies on voltages that are
1447  // NOT our nodal voltages.
1448  if (solVarDepC)
1449  {
1450  for (int i=0; i< expNumVars; ++i)
1451  {
1454  {
1455  dQdx[li_Pos][APosEquDepVarOffsets[i]] += expVarDerivs[i];
1456  }
1459  {
1460  dQdx[li_Neg][ANegEquDepVarOffsets[i]] -= expVarDerivs[i];
1461  }
1462  }
1463  }
1464 
1465  }
1466  return true;
1467 }
1468 
1469 //-----------------------------------------------------------------------------
1470 // Function : Instance::loadDAEdFdx ()
1471 //
1472 // Purpose : Loads the F-vector contributions for a single
1473 // capacitor instance.
1474 //
1475 // Special Notes : See the special notes for loadDAEFVector.
1476 //
1477 // For the capacitor this doesn't do anything, unless IC=
1478 // has been specified for an initial condition. Then,
1479 // there are extra equations that do not contain time
1480 // derivatives.
1481 //
1482 // Scope : public
1483 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1484 // Creation Date : 03/05/04
1485 //-----------------------------------------------------------------------------
1486 ///
1487 /// Load the DAE the derivative of the F vector with respect to the
1488 /// solution vector x, dFdx
1489 ///
1490 /// Loads the contributions for a single capacitor instance to the
1491 /// dFdx matrix (the F contribution to the Jacobian).
1492 ///
1493 /// This method uses the Jacobian LIDs (row offsets) that were stored by
1494 /// registerJacLIDs.
1495 ///
1496 /// @see Xyce::Device::Capacitor::Instance::registerJacLIDs
1497 ///
1498 /// The capacitor only loads the dFdx matrix when an initial condition is
1499 /// given on the instance line for the device.
1500 ///
1501 /// @note This method is called by the default implementation of the
1502 /// loadDAEMatrices master function. Since the Capacitor class
1503 /// reimplements the "Master" "loadDAEMatrices" function that loads the
1504 /// contributions from all capacitor devices in a single loop, THIS
1505 /// FUNCTION IS NOT ACTUALLY USED. The loadDAEdFdx method is only
1506 /// called when a device class does not re-implement the master class.
1507 /// This can be a source of confusion when attempting to modify the Capacitor
1508 /// device, or any other device that reimplements the Master classes.
1509 ///
1510 /// @see Xyce::Device::Capacitor::Master::loadDAEMatrices
1511 ///
1512 /// @return true on success
1513 ///
1514 /// @author Eric Keiter, SNL, Parallel Computational Sciences
1515 /// @date 03/05/04
1517 {
1518  N_LAS_Matrix & dFdx = *(extData.dFdxMatrixPtr);
1519 
1520  if (ICGiven && getSolverState().dcopFlag)
1521  {
1522  // Special Jacobian if we're doing operating point when IC given
1523  dFdx[li_Pos][APosEquBraNodeOffset] += 1.0;
1524  dFdx[li_Neg][ANegEquBraNodeOffset] += -1.0;
1525  dFdx[li_Bra][ABraEquPosNodeOffset] += 1.0;
1526  dFdx[li_Bra][ABraEquNegNodeOffset] += -1.0;
1527  }
1528  else
1529  {
1530  if (ICGiven)
1531  dFdx[li_Bra][ABraEquBraNodeOffset] += 1.0;
1532  }
1533 
1534  return true;
1535 }
1536 
1537 //-----------------------------------------------------------------------------
1538 // Function : Instance::setIC
1539 // Purpose :
1540 // Special Notes :
1541 // Scope : public
1542 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
1543 // Creation Date : 01/10/02
1544 //-----------------------------------------------------------------------------
1546 {
1547  double Vic;
1548  double Vpos, Vneg;
1549  double * nextStaVector = extData.nextStaVectorRawPtr;
1550  double * currStaVector = extData.currStaVectorRawPtr;
1551 
1552  double * nextSolVector = extData.nextSolVectorRawPtr;
1553  double * currSolVector = extData.currSolVectorRawPtr;
1554 
1555  if (ICGiven)
1556  {
1557  Vic = IC;
1558  q0 = C*Vic;
1559 
1560  currStaVector[li_QState] = q0;
1561  nextStaVector[li_QState] = q0;
1562 
1563  Vneg = currSolVector[li_Neg];
1564  Vpos = Vneg + Vic;
1565 
1566  currSolVector[li_Pos] = Vpos;
1567  nextSolVector[li_Pos] = Vpos;
1568  currSolVector[li_Neg] = -Vpos;
1569  nextSolVector[li_Neg] = -Vpos;
1570  }
1571 
1572  return true;
1573 }
1574 
1575 //-----------------------------------------------------------------------------
1576 // Function : Instance::varTypes
1577 // Purpose :
1578 // Special Notes :
1579 // Scope : public
1580 // Creator : Robert Hoekstra, SNL, Parallel Computational Sciences
1581 // Creation Date : 02/17/04
1582 //-----------------------------------------------------------------------------
1583 void Instance::varTypes( std::vector<char> & varTypeVec )
1584 {
1585  if (ICGiven)
1586  {
1587  varTypeVec.resize(1);
1588  varTypeVec[0] = 'I';
1589  }
1590 }
1591 
1592 // Class Model
1593 
1594 //-----------------------------------------------------------------------------
1595 // Function : Model::processParams
1596 // Purpose :
1597 // Special Notes :
1598 // Scope : public
1599 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1600 // Creation Date : 6/03/02
1601 //-----------------------------------------------------------------------------
1602 ///
1603 /// Process model parameters
1604 ///
1605 /// @return true on success
1606 ///
1607 /// @author Eric Keiter, SNL, Parallel Computational Sciences
1608 /// @date 6/03/02
1610 {
1611 
1612  if (!tnomGiven)
1614  else
1615  tnom += CONSTCtoK; // if user-specified, assume it was in deg. C.
1616 
1617  // If there are any time dependent parameters, set their values for
1618  // the current time.
1619 
1620  return true;
1621 }
1622 
1623 //----------------------------------------------------------------------------
1624 // Function : Model::processInstanceParams
1625 // Purpose :
1626 // Special Notes :
1627 // Scope : public
1628 // Creator : Dave Shirely, PSSI
1629 // Creation Date : 03/23/06
1630 //----------------------------------------------------------------------------
1631 ///
1632 /// Process the instance parameters of instance owned by this model
1633 ///
1634 /// This method simply loops over all instances associated with this
1635 /// model and calls their processParams method.
1636 ///
1637 /// @return true
1638 ///
1639 /// @author Dave Shirely, PSSI
1640 /// @date 03/23/06
1642 {
1643 
1644  std::vector<Instance*>::iterator iter;
1645  std::vector<Instance*>::iterator first = instanceContainer.begin();
1646  std::vector<Instance*>::iterator last = instanceContainer.end();
1647 
1648  for (iter=first; iter!=last; ++iter)
1649  {
1650  (*iter)->processParams();
1651  }
1652 
1653  return true;
1654 }
1655 
1656 //-----------------------------------------------------------------------------
1657 // Function : Model::Model
1658 // Purpose : model block constructor
1659 // Special Notes :
1660 // Scope : public
1661 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1662 // Creation Date : 5/17/00
1663 //-----------------------------------------------------------------------------
1664 ///
1665 /// Construct a capacitor model from a "model block" that was created
1666 /// by the netlist parser.
1667 ///
1668 /// @param configuration
1669 /// @param model_block
1670 /// @param factory_block
1671 ///
1672 /// @author Eric Keiter, SNL, Parallel Computational Sciences
1673 /// @date 5/17/00
1675  const Configuration & configuration,
1676  const ModelBlock & model_block,
1677  const FactoryBlock & factory_block)
1678  : DeviceModel(model_block, configuration.getModelParameters(), factory_block),
1679  cj(0.0),
1680  cjsw(0.0),
1681  defWidth(10e-6),
1682  narrow(0),
1683  tempCoeff1(0.0),
1684  tempCoeff2(0.0),
1685  tnom(getDeviceOptions().tnom),
1686  tnomGiven(0)
1687 {
1688 
1689  // Set params to constant default values :
1690  setDefaultParams ();
1691 
1692  // Set params according to .model line and constant defaults from metadata:
1693  setModParams(model_block.params);
1694 
1695  // Set any non-constant parameter defaults:
1696  if (!given("TNOM"))
1698 
1699  // Calculate any parameters specified as expressions:
1700 
1702 
1703  // calculate dependent (ie computed) params and check for errors:
1704 
1705  processParams ();
1706 }
1707 
1708 //-----------------------------------------------------------------------------
1709 // Function : Model::Model
1710 // Purpose : destructor
1711 // Special Notes :
1712 // Scope : public
1713 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1714 // Creation Date : 3/16/00
1715 //-----------------------------------------------------------------------------
1716 
1718 {
1719  std::vector<Instance*>::iterator iter;
1720  std::vector<Instance*>::iterator first = instanceContainer.begin();
1721  std::vector<Instance*>::iterator last = instanceContainer.end();
1722 
1723  for (iter=first; iter!=last; ++iter)
1724  {
1725  delete (*iter);
1726  }
1727 
1728 }
1729 
1730 //-----------------------------------------------------------------------------
1731 // Function : Model::printOutInstances
1732 // Purpose : debugging tool.
1733 // Special Notes :
1734 // Scope : public
1735 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1736 // Creation Date : 4/03/00
1737 //-----------------------------------------------------------------------------
1738 
1739 std::ostream &Model::printOutInstances(std::ostream &os) const
1740 {
1741  std::vector<Instance*>::const_iterator iter;
1742  std::vector<Instance*>::const_iterator first = instanceContainer.begin();
1743  std::vector<Instance*>::const_iterator last = instanceContainer.end();
1744 
1745  int i,isize;
1746 
1747  isize = instanceContainer.size();
1748  os << std::endl;
1749  os << "Number of capacitor instances: " << isize << std::endl;
1750  os << " name\t\tmodelName\tParameters" << std::endl;
1751 
1752  for (i = 0, iter = first; iter != last; ++iter, ++i)
1753  {
1754  os << " " << i << ": " << (*iter)->getName() << "\t";
1755  os << getName();
1756  os << "\t\tC = " << (*iter)->C;
1757  os << "\tIC = " << (*iter)->IC;
1758  os << std::endl;
1759  }
1760 
1761  os << std::endl;
1762 
1763  return os;
1764 }
1765 
1766 //-----------------------------------------------------------------------------
1767 // Function : Model::forEachInstance
1768 // Purpose :
1769 // Special Notes :
1770 // Scope : public
1771 // Creator : David Baur
1772 // Creation Date : 2/4/2014
1773 //-----------------------------------------------------------------------------
1774 /// Apply a device instance "op" to all instances associated with this
1775 /// model
1776 ///
1777 /// @param[in] op Operator to apply to all instances.
1778 ///
1779 ///
1780 void Model::forEachInstance(DeviceInstanceOp &op) const /* override */
1781 {
1782  for (std::vector<Instance *>::const_iterator it = instanceContainer.begin(); it != instanceContainer.end(); ++it)
1783  op(*it);
1784 }
1785 
1786 
1787 // Capacitor Master functions:
1788 
1789 //-----------------------------------------------------------------------------
1790 // Function : Master::updateState
1791 // Purpose :
1792 // Special Notes :
1793 // Scope : public
1794 // Creator : Eric Keiter, SNL
1795 // Creation Date : 11/26/08
1796 //-----------------------------------------------------------------------------
1797 ///
1798 /// Update state for all capacitor instances, regardless of model.
1799 ///
1800 /// @param solVec solution vector
1801 /// @param staVec state vector
1802 /// @param stoVec store vector
1803 ///
1804 /// @return true on success
1805 ///
1806 /// @note Because the capacitor device re-implements the base-class
1807 /// Master::updateState, the Instance::updatePrimaryState method is never
1808 /// called, nor is the Instance::updateIntermediateVars method. This method
1809 /// replaces those, and does the same work but inside a loop over all
1810 /// capacitor instances.
1811 ///
1812 /// Because the computation of state variables is so complex in the
1813 /// event that the capacitance is given by an expression that depends
1814 /// on solution variables, this method falls back on calling the
1815 /// instance's updatePrimaryState method instead of reimplementing the
1816 /// computation here.
1817 ///
1818 /// @see Xyce::Device::Capacitor::Instance::updatePrimaryState
1819 /// @author Eric Keiter, SNL
1820 /// @date 11/26/08
1821 bool Master::updateState (double * solVec, double * staVec, double * stoVec)
1822 {
1823  for (InstanceVector::const_iterator it = getInstanceBegin(); it != getInstanceEnd(); ++it)
1824  {
1825  Instance & ci = *(*it);
1826 
1827  double v_pos = solVec[ci.li_Pos];
1828  double v_neg = solVec[ci.li_Neg];
1829  ci.vcap = v_pos-v_neg;
1830 
1831  if( getSolverState().dcopFlag && ci.ICGiven )
1832  {
1833  ci.vcap = ci.IC;
1834  }
1835 
1836  if (!ci.solVarDepC)
1837  {
1838  // Obtain the "current" value for the charge stored in the capacitor.
1839  ci.q0 = ci.C * ci.vcap;
1840  staVec[ci.li_QState] = ci.q0;
1841  }
1842  else
1843  {
1844  // fall back on old pre-turbo scheme
1845  bool tmpBool=true;
1846  tmpBool = ci.updatePrimaryState ();
1847  }
1848  }
1849 
1850  return true;
1851 }
1852 
1853 //-----------------------------------------------------------------------------
1854 // Function : Master::loadDAEVectors
1855 // Purpose :
1856 // Special Notes :
1857 // Scope : public
1858 // Creator : Eric Keiter, SNL
1859 // Creation Date : 11/26/08
1860 //-----------------------------------------------------------------------------
1861 ///
1862 /// Load DAE vectors of all capacitor instances, regardless of model
1863 ///
1864 /// @param solVec solution vector
1865 /// @param fVec f vector
1866 /// @param qVec q vector
1867 /// @param storeLeadF store lead current f vector
1868 /// @param storeLeadQ store lead current q vector
1869 ///
1870 /// @return true on success
1871 ///
1872 /// @note Because the capacitor device re-implements the base-class
1873 /// Master::loadDAEVectors, the Instance::loadDAEFVector method is
1874 /// never called. This method replaces those, and does the same work
1875 /// but inside a loop over all capacitor instances.
1876 ///
1877 /// @see Xyce::Device::Capacitor::Instance::loadDAEFVector
1878 ///
1879 /// @author Eric Keiter, SNL
1880 /// @date 11/26/08
1881 bool Master::loadDAEVectors (double * solVec, double * fVec, double *qVec, double * storeLeadF, double * storeLeadQ)
1882 {
1883 
1884  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1885  {
1886  dout() << " ----------------------------------" << std::endl;
1887  dout() << " Master::loadDAEVectors: " << std::endl;
1888  }
1889 
1890  for (InstanceVector::const_iterator it = getInstanceBegin(); it != getInstanceEnd(); ++it)
1891  {
1892  Instance & ci = *(*it);
1893  if (ci.ICGiven)
1894  {
1895  double Vpos (0.0), Vneg (0.0), v_tmp (0.0);
1896 
1897  // Initial condition
1898  if (getSolverState().dcopFlag)
1899  {
1900  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1901  {
1902  dout() << " loading dcop F vector for cap " << ci.getName() << ":" << std::endl;
1903  }
1904  // If doing the DCOP and have IC=, get current from branch equation
1905  // ci.i0 = solVec[ci.li_Bra]; moved to CapacitorMaster::updateState() where stovec is passed in.
1906  Vpos = solVec[ci.li_Pos];
1907  Vneg = solVec[ci.li_Neg];
1908  fVec [ci.li_Pos] += solVec[ci.li_Bra];
1909  fVec [ci.li_Neg] += -solVec[ci.li_Bra];
1910 
1911  if( ci.loadLeadCurrent )
1912  {
1913  storeLeadF[ci.li_store_dev_i] = solVec[ci.li_Bra];
1914  }
1915 
1916  if( ci.loadLeadCurrent )
1917  {
1918  storeLeadF[ci.li_store_dev_i] = solVec[ci.li_Bra];
1919  }
1920 
1921  if( ci.loadLeadCurrent )
1922  {
1923  storeLeadF[ci.li_store_dev_i] = solVec[ci.li_Bra];
1924  }
1925 
1926  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1927  {
1928  dout() << " f[ " << ci.li_Pos << " ] += " << solVec[ci.li_Bra]<< std::endl;
1929  dout() << " f[ " << ci.li_Neg << " ] += " << -solVec[ci.li_Bra] << std::endl;
1930  }
1931 
1932  v_tmp= (Vpos-Vneg-ci.IC);
1933  }
1934 
1935  // Do this only if there's a Branch equation
1936  fVec[ci.li_Bra] += v_tmp;
1937 
1938  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1939  {
1940  dout() << " f[ " << ci.li_Bra << " ] += " << v_tmp << std::endl;
1941  }
1942  }
1943 
1944  qVec[ci.li_Pos] += ci.q0;
1945  qVec[ci.li_Neg] += -ci.q0;
1946 
1947  if( ci.loadLeadCurrent )
1948  {
1949  storeLeadQ[ci.li_store_dev_i] = ci.q0;
1950  storeLeadF[ci.li_store_dev_i] = 0;
1951  }
1952 
1953  if( ci.loadLeadCurrent )
1954  {
1955  storeLeadQ[ci.li_store_dev_i] = ci.q0;
1956  storeLeadF[ci.li_store_dev_i] = 0;
1957  }
1958 
1959  if( ci.loadLeadCurrent )
1960  {
1961  storeLeadQ[ci.li_store_dev_i] = ci.q0;
1962  storeLeadF[ci.li_store_dev_i] = 0;
1963  }
1964 
1965  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1966  {
1967  dout() << " loading Q vector for cap " << ci.getName() << ":" << std::endl;
1968  dout() << " q[ " << ci.li_Pos << " ] += " << ci.q0 << std::endl;
1969  dout() << " q[ " << ci.li_Neg << " ] += " << -ci.q0 << std::endl;
1970 
1971  }
1972 
1973  }
1974  return true;
1975 }
1976 
1977 //-----------------------------------------------------------------------------
1978 // Function : Master::loadDAEMatrices
1979 // Purpose :
1980 // Special Notes :
1981 // Scope : public
1982 // Creator : Eric Keiter, SNL
1983 // Creation Date : 11/26/08
1984 //-----------------------------------------------------------------------------
1985 ///
1986 /// Load DAE matrices for all capacitor instances, regardless of model
1987 ///
1988 /// @param dFdx matrix of derivatives of F vector with respect to solution
1989 /// @param dQdx matrix of derivatives of Q vector with respect to solution
1990 ///
1991 /// @return true on success
1992 ///
1993 /// @note Because the capacitor device re-implements the base-class
1994 /// Master::loadDAEMatrices, the Instance::loadDAEdFdx method is
1995 /// never called. This method replaces those, and does the same work
1996 /// but inside a loop over all capacitor instances.
1997 ///
1998 /// @see Xyce::Device::Capacitor::Instance::loadDAEdFdx
1999 ///
2000 /// @author Eric Keiter, SNL
2001 /// @date 11/26/08
2002 bool Master::loadDAEMatrices (N_LAS_Matrix & dFdx, N_LAS_Matrix & dQdx)
2003 {
2004 
2005  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2006  {
2007  dout() << " ----------------------------------" << std::endl;
2008  dout() << " Master::loadDAEMatrices: " << std::endl;
2009  }
2010 
2011  for (InstanceVector::const_iterator it = getInstanceBegin(); it != getInstanceEnd(); ++it)
2012  {
2013  Instance & ci = *(*it);
2014 
2015  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2016  {
2017  dout() << " loads for capacitor " << ci.getName() << std::endl;
2018  }
2019 
2020  if (ci.ICGiven && getSolverState().dcopFlag)
2021  {
2022  // Special Jacobian if we're doing operating point when IC given
2023 #ifndef Xyce_NONPOINTER_MATRIX_LOAD
2024  *(ci.fPosEquBraNodePtr) += 1.0;
2025  *(ci.fNegEquBraNodePtr) += -1.0;
2026  *(ci.fBraEquPosNodePtr) += 1.0;
2027  *(ci.fBraEquNegNodePtr) += -1.0;
2028 #else
2029  dFdx[ci.li_Pos][ci.APosEquBraNodeOffset] += 1.0;
2030  dFdx[ci.li_Neg][ci.ANegEquBraNodeOffset] += -1.0;
2031  dFdx[ci.li_Bra][ci.ABraEquPosNodeOffset] += 1.0;
2032  dFdx[ci.li_Bra][ci.ABraEquNegNodeOffset] += -1.0;
2033 #endif
2034  }
2035  else
2036  {
2037  if (ci.ICGiven)
2038  {
2039 #ifndef Xyce_NONPOINTER_MATRIX_LOAD
2040  *(ci.fBraEquBraNodePtr) += 1.0;
2041 #else
2042  dFdx[ci.li_Bra][ci.ABraEquBraNodeOffset] += 1.0;
2043 #endif
2044  }
2045  }
2046 
2047  if (!(ci.ICGiven&& getSolverState().dcopFlag))
2048  {
2049 #ifndef Xyce_NONPOINTER_MATRIX_LOAD
2050  *(ci.qPosEquPosNodePtr) += ci.C;
2051  *(ci.qPosEquNegNodePtr) -= ci.C;
2052  *(ci.qNegEquPosNodePtr) -= ci.C;
2053  *(ci.qNegEquNegNodePtr) += ci.C;
2054 
2055  if (ci.solVarDepC)
2056  {
2057  for (int i=0; i< ci.expNumVars; ++i)
2058  {
2059  // Similar to logic in loadDAEdQdX:
2060  if ((ci.qPosEquDepVarsPtrs[i] != ci.qPosEquPosNodePtr)
2061  && (ci.qPosEquDepVarsPtrs[i] != ci.qPosEquNegNodePtr))
2062  {
2063  *(ci.qPosEquDepVarsPtrs[i]) += ci.expVarDerivs[i];
2064  }
2065 
2066  if ((ci.qNegEquDepVarsPtrs[i] != ci.qNegEquPosNodePtr)
2067  && (ci.qNegEquDepVarsPtrs[i] != ci.qNegEquNegNodePtr))
2068  {
2069  *(ci.qNegEquDepVarsPtrs[i]) -= ci.expVarDerivs[i];
2070  }
2071  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2072  {
2073  if ((ci.qPosEquDepVarsPtrs[i] != ci.qPosEquPosNodePtr)
2074  && (ci.qPosEquDepVarsPtrs[i] != ci.qPosEquNegNodePtr))
2075  {
2076  dout() << " q[pos][ " << ci.APosEquDepVarOffsets[i] << " ] += "
2077  << ci.expVarDerivs[i] << std::endl;
2078  }
2079  if ((ci.qNegEquDepVarsPtrs[i] != ci.qNegEquPosNodePtr)
2080  && (ci.qNegEquDepVarsPtrs[i] != ci.qNegEquNegNodePtr))
2081  {
2082  dout() << " q[neg][ " << ci.ANegEquDepVarOffsets[i] << " ] += "
2083  << ci.expVarDerivs[i] << std::endl;
2084  }
2085  }
2086 
2087  }
2088  }
2089 #else
2090  dQdx[ci.li_Pos][ci.APosEquPosNodeOffset] += ci.C;
2091  dQdx[ci.li_Pos][ci.APosEquNegNodeOffset] -= ci.C;
2092  dQdx[ci.li_Neg][ci.ANegEquPosNodeOffset] -= ci.C;
2093  dQdx[ci.li_Neg][ci.ANegEquNegNodeOffset] += ci.C;
2094 
2095  if (ci.solVarDepC)
2096  {
2097  for (int i=0; i< ci.expNumVars; ++i)
2098  {
2099  if ( (ci.APosEquDepVarOffsets[i] != ci.APosEquPosNodeOffset)
2100  && (ci.APosEquDepVarOffsets[i] != ci.APosEquNegNodeOffset))
2101  {
2102  dQdx[ci.li_Pos][ci.APosEquDepVarOffsets[i]] +=
2103  ci.expVarDerivs[i];
2104  }
2105  if ( (ci.ANegEquDepVarOffsets[i] != ci.ANegEquPosNodeOffset)
2106  && (ci.ANegEquDepVarOffsets[i] != ci.ANegEquNegNodeOffset))
2107  {
2108  dQdx[ci.li_Neg][ci.ANegEquDepVarOffsets[i]] -=
2109  ci.expVarDerivs[i];
2110  }
2111 
2112  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
2113  {
2114  if ( (ci.APosEquDepVarOffsets[i] != ci.APosEquPosNodeOffset)
2115  && (ci.APosEquDepVarOffsets[i] != ci.APosEquNegNodeOffset))
2116  {
2117  dout() << " q[pos][ " << ci.APosEquDepVarOffsets[i] << " ] += "
2118  << expVarDerivs[i] << std::endl;
2119  }
2120  if ( (ci.ANegEquDepVarOffsets[i] != ci.ANegEquPosNodeOffset)
2121  && (ci.ANegEquDepVarOffsets[i] != ci.ANegEquNegNodeOffset))
2122  {
2123  dout() << " q[neg][ " << ci.ANegEquDepVarOffsets[i] << " ] += "
2124  << ci.expVarDerivs[i] << std::endl;
2125  }
2126  }
2127  }
2128  }
2129 
2130 #endif
2131  }
2132  }
2133 
2134  return true;
2135 }
2136 
2137 //-----------------------------------------------------------------------------
2138 // Function : Xyce::Device::Capacitor::Traits::factory
2139 // Purpose :
2140 // Special Notes :
2141 // Scope : public
2142 // Creator : David Baur
2143 // Creation Date :
2144 //-----------------------------------------------------------------------------
2145 ///
2146 /// Create a new instance of the Capacitor device.
2147 ///
2148 /// @param configuration
2149 /// @param factory_block
2150 ///
2151 Device *
2152 Traits::factory(const Configuration &configuration, const FactoryBlock &factory_block)
2153 {
2154  return new Master(configuration, factory_block, factory_block.solverState_, factory_block.deviceOptions_);
2155 }
2156 
2157 //-----------------------------------------------------------------------------
2158 // Function : Xyce::Device::Capacitor::registerDevice
2159 // Purpose :
2160 // Special Notes :
2161 // Scope : public
2162 // Creator : David Baur
2163 // Creation Date :
2164 //-----------------------------------------------------------------------------
2165 ///
2166 /// Define how to use the device in a netlist.
2167 ///
2168 /// This method is called from the Xyce::Device::registerOpenDevices
2169 /// function, which in turn is called by the device manager.
2170 ///
2171 /// The device is declared here to be an "R" device, which may optionally
2172 /// take a model card of type "R". This device will correspond to model
2173 /// level 1 of capacitor models.
2175 {
2177  .registerDevice("c", 1)
2178  .registerModelType("c", 1);
2179 }
2180 
2181 } // namespace Capacitor
2182 } // namespace Device
2183 } // namespace Xyce