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.267.2.2 $
40 //
41 // Revision Date : $Date: 2014/08/28 18:08:28 $
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  .setAnalyticSensitivityAvailable(true)
95  .setSensitivityFunctor(&capSens);
96  p.addPar("IC", 0.0, &Capacitor::Instance::IC)
97  .setGivenMember(&Capacitor::Instance::ICGiven)
98  .setUnit(STANDARD);
100  .setUnit(U_METER)
101  .setDescription("Semiconductor capacitor width");
102  p.addPar("W", 1.e-6, &Capacitor::Instance::width)
103  .setUnit(U_METER)
104  .setDescription("Semiconductor capacitor length");
105  p.addPar("AGE", 0.0, &Capacitor::Instance::age)
106  .setUnit(U_HOUR)
107  .setDescription("Age of capacitor");
108  p.addPar("D", 0.0233, &Capacitor::Instance::ageCoef)
109  .setDescription("Age degradation coefficient");
110  p.addPar("TEMP", 0.0, &Capacitor::Instance::temp)
111  .setExpressionAccess(ParameterType::TIME_DEP)
112  .setUnit(STANDARD)
113  .setDescription("Device temperature");
114 
116  .setGivenMember(&Capacitor::Instance::tempCoeff1Given)
117  .setUnit(U_DEGCM1)
118  .setDescription("Linear Temperature Coefficient");
120  .setGivenMember(&Capacitor::Instance::tempCoeff2Given)
121  .setUnit(U_DEGCM2)
122  .setDescription("Quadratic Temperature Coefficient");
123  p.makeVector("TC", 2);
124 }
125 
126 //-----------------------------------------------------------------------------
127 // Function : Xyce::Device::Capacitor::Traits::loadModelParameters
128 // Purpose :
129 // Special Notes : The addPar calls here were refactored and moved here
130 // from the model constructor. Those addPars had been
131 // in place from 2/4/2005.
132 // Scope : private
133 // Creator : David Baur
134 // Creation Date : 1/27/2014
135 //-----------------------------------------------------------------------------
136 ///
137 /// Loads the parameter definition into the model parameter map.
138 ///
139 /// @param p model parameter map
140 ///
141 /// @see Xyce::Device::Resistor::Traits::loadInstanceParameters
142 ///
144 {
146  .setUnit(U_NONE)
147  .setDescription("Capacitance multiplier");
148  p.addPar("CJ", 0.0, &Capacitor::Model::cj)
149  .setUnit(U_FARADMM2)
150  .setDescription("Junction bottom capacitance");
151  p.addPar("CJSW", 0.0, &Capacitor::Model::cjsw)
152  .setUnit(U_FARADMM1)
153  .setDescription("Junction sidewall capacitance");
154  p.addPar("DEFW", 1.e-6, &Capacitor::Model::defWidth)
155  .setUnit(U_METER)
156  .setDescription("Default device width");
157  p.addPar("NARROW", 0.0, &Capacitor::Model::narrow)
158  .setUnit(U_METER)
159  .setDescription("Narrowing due to side etching");
160  p.addPar("TC1", 0.0, &Capacitor::Model::tempCoeff1)
161  .setUnit(STANDARD);
162  p.addPar("TC2", 0.0, &Capacitor::Model::tempCoeff2)
163  .setUnit(STANDARD);
164  p.addPar("TNOM", 0.0, &Capacitor::Model::tnom)
165  .setUnit(STANDARD);
166 }
167 
168 // Class Instance
169 //-----------------------------------------------------------------------------
170 // Function : Instance::processParams
171 // Purpose :
172 // Special Notes :
173 // Scope : public
174 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
175 // Creation Date : 6/03/02
176 //-----------------------------------------------------------------------------
177 /// Process parameters.
178 ///
179 /// @return true on success
180 ///
181 /// In general, the processParams method is intended as a place for complex
182 /// manipulation of parameters that must happen if temperature or other
183 /// external changes happen.
184 ///
185 /// The Capacitor device supports an "AGE" parameter and a degradation
186 /// rate parameter that together determine how to modify the
187 /// capacitance given on the instance line. Further, Xyce supports a
188 /// "semiconductor capacitor" model which allows the user to specify
189 /// the capacitance through a combination of model parameters (junction
190 /// capacitance and junction sidewall capacitance) and instance parameters
191 /// (length and width).
192 ///
193 /// Both of these methods of capacitance value determination need to
194 /// be done after the normal processing of netlist parameters. That
195 /// processing is done here.
196 ///
197 /// @author Eric Keiter, SNL, Parallel Computational Sciences
198 /// @date 6/03/02
200 {
201 
202  baseCap = C;
203  if (!given("C") && given("AGE"))
204  {
205  UserError0(*this) << "Age defined, but no base capacitance given. Can't use age-aware with semiconductor capacitor options";
206  }
207 
208  // the age aware capacitor simply modifies the base capacitance.
209  if (given("AGE") && age >= 1)
210  {
211  baseCap = baseCap*(1-ageCoef*log10(age));
212  }
213 
214  if (!given("C") && !given("L"))
215  {
216  UserError0(*this) << "Could find neither C parameter or L in instance.";
217  }
218 
219  // Now we know we have either cap or length specified.
220  if (!given("C"))
221  {
222  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
223  {
224  dout() << "Semiconductor capacitor " << getName()
225  //<< Util::push << std::endl
226  << std::endl
227  << "cj = " << model_.cj << std::endl
228  << "cjsw = " << model_.cjsw << std::endl
229  << "width = " << width << std::endl
230  << "length = " << length << std::endl
231  << "narrow = " << model_.narrow <<
232  //Util::pop << std::endl;
233  std::endl;
234  }
235 
236  baseCap = C =
239  }
240 
241  // If there are any time dependent parameters, set their values for
242  // the current time.
243 
245 
246  return true;
247 }
248 
249 //-----------------------------------------------------------------------------
250 // Function : Instance::updateTemperature
251 // Purpose :
252 // Special Notes :
253 // Scope : public
254 // Creator : Tom Russo, Component Information and Models
255 // Creation Date : 02/27/01
256 //-----------------------------------------------------------------------------
257 ///
258 /// Update the parameters that depend on the temperature of the device
259 ///
260 /// @param temp_tmp temperature
261 ///
262 /// Xyce has a number of mechanisms that allow temperature to be changed
263 /// after a device has been instantiated. These include .STEP loops over
264 /// temperature. When temperature is changed, any device that has parameters
265 /// that depend on temperature must be updated. That updating happens here.
266 ///
267 /// The capacitor device supports temperature-dependent resistance through its
268 /// TC1 (linear dependence) and TC2 (quadratic dependence) parameters.
269 /// If these parameters are specified, the capacitance must be updated.
270 ///
271 bool Instance::updateTemperature ( const double & temp_tmp)
272 {
273  bool bsuccess = true;
274  double difference, factor;
275 
276  difference = temp - model_.tnom;
277  factor = model_.capacitanceMultiplier*(1.0 + tempCoeff1*difference +
278  tempCoeff2*difference*difference);
279  C = baseCap*factor;
280 
281  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
282  {
283  dout() << "Capacitor " << getName() << " updateTemperature()"
284  //<< Util::push << std::endl
285  << std::endl
286  << "C = " << C << std::endl
287  << "temp = " << temp << std::endl
288  << "temp_tmp = " << temp_tmp << std::endl
289  << "tnom = " << model_.tnom << std::endl
290  << "difference = " << difference << std::endl
291  << "tempCoeff1 = " << tempCoeff1 << std::endl
292  << "tempCoeff2 = " << tempCoeff2 << std::endl
293  << "baseCap = " << baseCap << std::endl
294  //<< "factor = " << factor << Util::pop << std::endl;
295  << "factor = " << factor << std::endl;
296  }
297 
298  return bsuccess;
299 }
300 
301 //-----------------------------------------------------------------------------
302 // Function : Instance::Instance
303 // Purpose : instance block constructor
304 // Special Notes :
305 // Scope : public
306 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
307 // Creation Date : 3/16/00
308 //-----------------------------------------------------------------------------
309 ///
310 /// Construct a Capacitor model from a "model block" that was created
311 /// by the netlist parser.
312 ///
313 /// @param configuration
314 /// @param model_block
315 /// @param factory_block
316 ///
317 /// @author Eric Keiter, SNL, Parallel Computational Sciences
318 /// @date 3/16/00
320  const Configuration & configuration,
321  const InstanceBlock & instance_block,
322  Model & model,
323  const FactoryBlock & factory_block)
324  : DeviceInstance(instance_block, configuration.getInstanceParameters(), factory_block),
325  model_(model),
326  expNumVars(0),
327  expPtr(0),
328  baseCap(0.0),
329  temp(getDeviceOptions().temp.getImmutableValue<double>()),
330  tempGiven(0),
331  tempCoeff1(0.0),
332  tempCoeff2(0.0),
333  tempCoeff1Given(false),
334  tempCoeff2Given(false),
335  li_Pos(-1),
336  li_Neg(-1),
337  li_QState(-1),
338  li_vcapState(-1),
339  li_capState(-1),
340  li_store_dev_i(-1),
341  APosEquPosNodeOffset(-1),
342  ANegEquPosNodeOffset(-1),
343  APosEquNegNodeOffset(-1),
344  ANegEquNegNodeOffset(-1),
345  APosEquBraNodeOffset(-1),
346  ANegEquBraNodeOffset(-1),
347  ABraEquBraNodeOffset(-1),
348  ABraEquPosNodeOffset(-1),
349  ABraEquNegNodeOffset(-1),
350 
352  qPosEquPosNodePtr(0),
353  qNegEquPosNodePtr(0),
354  qPosEquNegNodePtr(0),
355  qNegEquNegNodePtr(0),
356  fPosEquBraNodePtr(0),
357  fNegEquBraNodePtr(0),
358  fBraEquBraNodePtr(0),
359  fBraEquPosNodePtr(0),
360  fBraEquNegNodePtr(0),
361 #endif
362 
363  ICGiven(false),
364  solVarDepC(false),
365  IC(0)
366 
367 {
368  numIntVars = 0;
369  numExtVars = 2;
370  numStateVars = 1;
371  setNumStoreVars(0);
372  numLeadCurrentStoreVars = 1; // lead current DEV_I
373 
374  devConMap[0] = 1;
375  devConMap[1] = 2;
376 
377  /// Unlike the resistor, the capacitor's jacobian stamp is set up directly
378  /// in the constructor, and is not static. This is because the capacitor
379  /// supports some options that the resistor does not:
380  ///
381  /// - The capacitor instance line may be given an IC=value that
382  /// will be used as the initial voltage drop across the
383  /// capacitance at the operating point.
384  ///
385  /// - The capacitance on the instance line may be an expression
386  /// that is permitted to be a function of other solution variables (such
387  /// as voltages elsewhere in the circuit).
388  ///
389  /// Both of these require that the Jacobian stamp for the device be modified.
390  /// Use of a static Jacobian stamp would prevent this flexibility, because
391  /// the static stamp would be used by all capacitor devices, even those
392  /// that do not make use of the options.
393  ///
394  /// Since the setting of the Jacobian stamp in this otherwise simple device
395  /// is so complex, we will document how it is set here.
396  ///
397  /// In its simplest form, the charge \f$q_0\f$ on the capacitor is
398  /// \f$C(V_+-V_-)\f$. Thus, the current out of the positive node is
399  /// \f$d(q_0)/dt\f$, and the current "out" of the negative node is
400  /// \f$-d(q0)/dt\f$. In the Xyce formulation, we load \f$q_0\f$ into
401  /// the Q vector for the positive node, and \f$-q_0\f$ into the Q
402  /// vector for the negative node; the time integator will later
403  /// differentiate this to obtain the currents. Thus, the contribution
404  /// to the Jacobian from the capacitor (with constant capacitance and
405  /// no initial condition) will require loading the dQdx matrix with the
406  /// derivatives of \f$q_0\f$ with respect to the voltage nodes:
407  /// \f[
408  /// \left[\begin{array}{rr}
409  /// C & -C \\
410  /// -C & C
411  /// \end{array} \right] \f]
412  ///
413  /// The jacobian stamp in this case is the same as the one defined by
414  /// the resistor: it has two rows, one for the positive node equation
415  /// and one for negative node equation, and two columns, one for the
416  /// positive node and one for the negative. Column 0's value in each
417  /// row is 0 to reflect that the first nonzero value of the jacobian
418  /// row is the one corresponding to the positive node, and column 1's value
419  /// is 1 to reflect that the second nonzero corresponds to the dependence on
420  /// the negative node.
421  ///
422  /// If an initial condition is present, however, the circuit at DC is
423  /// the same as if there were only a voltage source across the
424  /// positive and negative nodes, and in transient it is the same as
425  /// the capacitor without the voltage source present. Thus, at DC the
426  /// current out of the positive node would be equal to the voltage
427  /// source branch current, and the current out of the negative node
428  /// would be the negative of that. Since these quantities are not
429  /// differentiated they would be placed in the F vector, no the Q
430  /// vector. An extra equation, called the "branch equation"
431  /// stipulates that the voltage drop between the positive and negative
432  /// node be equal to the initial condition, so this element of
433  /// the F vector would be loaded with \f$(V_+-V_-)-V_{ic}\f$.
434  /// Therefore in DC the dFdx matrix would be loaded with:
435  /// \f[
436  /// \left[ \begin{array}{rrr}
437  /// 0 & 0 & 1 \\
438  /// 0 & 0 & -1 \\
439  /// 1 & -1 & 0
440  /// \end{array}\right] \f]
441  /// The the first two rows are for the positive and negative node
442  /// equations, and the third row is for the branch equation. The
443  /// third column is for the branch current variable. At DC nothing
444  /// would be loaded into either the Q vector or its derivative.
445  ///
446  /// In transient, the loads into the Q vector are the same as
447  /// without the initial condition. Xyce requires that a single jacobian
448  /// stamp be used for both the dFdx and dQdx matrices, and does not allow
449  /// this matrix to vary between DC and transient. Thus the dQdx matrix would
450  /// be loaded with:
451  /// \f[
452  /// \left[ \begin{array}{rrr}
453  /// C & -C & 0 \\
454  /// -C & C & 0 \\
455  /// 0 & 0 & 0
456  /// \end{array}\right] \f]
457  /// The dFdx matrix must be loaded with a single value to turn off the
458  /// branch equation and prevent a singular Jacobian:
459  /// \f[
460  /// \left[ \begin{array}{rrr}
461  /// 0 & 0 & 0 \\
462  /// 0 & 0 & 0 \\
463  /// 0 & 0 & 1
464  /// \end{array}\right] \f]
465  /// The net result of this modification is that now, irrespective of whether
466  /// we are at DC or in transient, every element of the 3x3 matrix is potentially
467  /// nonzero in one or the other of dFdx or dQdx, and therefore our jacobian
468  /// stamp is also a dense 3x3 matrix, with each column's value equal to that
469  /// column's number.
470  ///
471  /// Finally, if the capacitance is solution-variable dependent, each of the
472  /// rows for positive and negative nodes must be augmented with an additional
473  /// column for each variable that the capacitance depends on. These rows
474  /// are similarly dense, and each value of the jacobian stamp for each column
475  /// is equal to its column number.
476  if( jacStamp.empty() )
477  {
478  jacStamp_IC.resize(3);
479  jacStamp_IC[0].resize(3);
480  jacStamp_IC[1].resize(3);
481  jacStamp_IC[2].resize(3);
482  jacStamp_IC[0][0] = 0;
483  jacStamp_IC[0][1] = 1;
484  jacStamp_IC[0][2] = 2;
485  jacStamp_IC[1][0] = 0;
486  jacStamp_IC[1][1] = 1;
487  jacStamp_IC[1][2] = 2;
488  jacStamp_IC[2][0] = 0;
489  jacStamp_IC[2][1] = 1;
490  jacStamp_IC[2][2] = 2;
491 
492  jacStamp.resize(2);
493  jacStamp[0].resize(2);
494  jacStamp[1].resize(2);
495  jacStamp[0][0] = 0;
496  jacStamp[0][1] = 1;
497  jacStamp[1][0] = 0;
498  jacStamp[1][1] = 1;
499  }
500 
501 
502  // Set params to constant default values:
503  setDefaultParams ();
504 
505  // Set params according to instance line and constant defaults from metadata:
506  setParams (instance_block.params);
507 
508  // Set any non-constant parameter defaults:
509 
510  if (!given("W"))
512  if (!given("TEMP"))
513  temp = getDeviceOptions().temp.getImmutableValue<double>();
514 
515  if (!tempCoeff1Given)
517  if (!tempCoeff2Given)
519 
520 
521 
522  // Handle case where capacitance is solution-variable dependent:
523  if (getDependentParams().size()>0)
524  {
525  std::vector<Depend>::const_iterator d;
526  std::vector<Depend>::const_iterator begin=getDependentParams().begin();
527  std::vector<Depend>::const_iterator end=getDependentParams().end();
528 
529  for (d=begin; d!=end; ++d)
530  {
531  if (d->name != "C")
532  {
533  UserError0(*this) << "Solution-variable-dependent parameter other than C detected";
534  }
535  else
536  {
537  expNumVars = d->n_vars;
538  expPtr = d->expr;
539 
540  if (expNumVars > 0 || expPtr->isTimeDependent())
541  {
542  solVarDepC = true;
543  // To do the proper integration of the charge, we need to save the
544  // voltage drop, the old capacitance and
545  // the derivatives of Q and C from the last step.
546  numStateVars += 2+2*expNumVars;
547 
548  if (expPtr->getNumDdt() != 0)
549  {
550  UserError0(*this) << "Solution-variable-dependent expression contains time derivatives";
551  }
552 
553  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0)
554  {
555  dout() << "Capacitor " << getName()
556  << ": Found solution-dependent parameter C depending on " << expNumVars << " variables" << std::endl;
557  if (expPtr->isTimeDependent())
558  {
559  dout() << " " << "Expression is time-dependent." << std::endl;
560  }
561  else
562  {
563  dout() << " " << "Expression is not time-dependent." << std::endl;
564  }
565  dout() << " " << "Expression depends on " << expPtr->num_vars() << " quantity, of which " << expPtr->num_vars()-expNumVars << " are not solution vars. " << std::endl;
566 
567  }
568 
569  // We now need to extend the pos and neg rows of the jacstamps
570  // to account for the additional dependencies:
571 
572  jacStamp[0].resize(2+expNumVars);
573  jacStamp[1].resize(2+expNumVars);
574  jacStamp_IC[0].resize(3+expNumVars);
575  jacStamp_IC[1].resize(3+expNumVars);
576  for (int i=0; i<expNumVars; ++i)
577  {
578  jacStamp[0][2+i]=2+i;
579  jacStamp[1][2+i]=2+i;
580 
581  jacStamp_IC[0][3+i]=3+i;
582  jacStamp_IC[1][3+i]=3+i;
583  }
584 
585  // finally, allocate space to hold the derivatives of C w.r.t.
586  // the variables it depends on:
587  expVarDerivs.resize(expNumVars);
588  // and LIDs for state vector
589  li_dQdXState.resize(expNumVars);
590  li_dCdXState.resize(expNumVars);
591 
592  }
593  }
594  }
595  }
596 
597  // Calculate any parameters specified as expressions:
598 
600 
601  // calculate dependent (ie computed) params:
602 
603  processParams ();
604 
605  // we're gonna have to fake a voltage source at the operating point
606  if (ICGiven ) numIntVars = 1;
607 }
608 
609 //-----------------------------------------------------------------------------
610 // Function : Instance::~Instance
611 // Purpose : destructor
612 // Special Notes :
613 // Scope : public
614 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
615 // Creation Date : 3/16/00
616 //-----------------------------------------------------------------------------
618 {
619 }
620 
621 // Additional Declarations
622 
623 //-----------------------------------------------------------------------------
624 // Function : Instance::registerLIDs
625 // Purpose :
626 // Special Notes :
627 // Scope : public
628 // Creator : Robert Hoekstra, SNL, Parallel Computational Sciences
629 // Creation Date : 6/20/02
630 //-----------------------------------------------------------------------------
631 ///
632 /// Register local IDs
633 ///
634 /// Register the local internal and external node IDs.
635 ///
636 /// @param intLIDVecRef internal local IDs from topology package
637 /// @param extLIDVecRef external local IDs from topology package
638 ///
639 /// Instantiation (calling the device constructor) of the device
640 /// sets up variables numIntVars and numExtVars, the numbers of internal and
641 /// external variables associated with the device. This information is then
642 /// used by the Topology package to assign locations in the solution vector
643 /// (and all other vectors of the same shape) for those variables.
644 /// The "Local IDs" (LIDs) of these locations are provided by Topology
645 /// so the device can know where to load its data.
646 ///
647 /// This method saves the LIDs from Topology and associates each one with
648 /// a particular local name for the internal or external variable. They
649 /// are then used when we load the F and Q vectors.
650 ///
651 /// The Capacitor device has no internal variables, so this method makes no use
652 /// of the intLIDVecRef array.
653 ///
654 /// @author Robert Hoekstra, SNL, Parallel Computational Sciences
655 /// @date 6/20/02
656 void Instance::registerLIDs( const std::vector<int> & intLIDVecRef,
657  const std::vector<int> & extLIDVecRef)
658 
659 {
660  AssertLIDs(intLIDVecRef.size() == numIntVars);
661  AssertLIDs(extLIDVecRef.size() == numExtVars);
662 
663  // Copy over the local ID lists:
664  intLIDVec = intLIDVecRef;
665  extLIDVec = extLIDVecRef;
666 
667  // Now use these lists to obtain the indices into the linear algebra
668  // entities. This assumes an order. For the matrix indices, first do the
669  // rows.
670 
671  li_Pos = extLIDVec[0];
672  li_Neg = extLIDVec[1];
673 
674  // For fake voltage source at operating point
675  if (ICGiven)
676  {
677  li_Bra = intLIDVec[0];
678  }
679 
680  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0)
681  {
682  dout() << "Capacitor " << getName() << " Instance::registerLIDs"
683  // << Util::push << std::endl
684  << std::endl
685  << "li_Pos_ = " << li_Pos << std::endl
686  << "li_Neg_ = " << li_Neg << std::endl;
687 
688  if (ICGiven)
689  dout() << "li_Bra = "<< li_Bra<< std::endl;
690 
691  //dout() << Util::pop << std::endl;
692  dout() << std::endl;
693  }
694 }
695 
696 //-----------------------------------------------------------------------------
697 // Function : Instance::registerStateLIDs
698 // Purpose :
699 // Special Notes :
700 // Scope : public
701 // Creator : Robert Hoekstra, SNL, Parallel Computational Sciences
702 // Creation Date : 6/20/02
703 //-----------------------------------------------------------------------------
704 ///
705 /// Register the local state IDs
706 ///
707 /// @param staLIDVecRef State variable local IDs
708 ///
709 /// In general, devices may declare at construction that they require storage
710 /// locations in the "state vector." Topology assigns locations in the
711 /// state vector and returns "Local IDs" (LIDs) for devices to use for their
712 /// state vector entries. If a device uses state vector entries, it
713 /// uses the registerStateLIDs method to save the local IDs for later use.
714 ///
715 /// The capacitor has at least one state variable (the charge) and as many
716 /// as three (the charge plus state variables used to support the "voltage
717 /// dependent capacitance" feature) plus the number of variables on whihc
718 /// the capacitance depends.
719 ///
720 /// @note The storage of the charge as a state variable when the capacitance
721 /// is a constant is a holdover from older implementations, and in the future
722 /// may be saved only as part of the voltage-dependent capacitance feature.
723 ///
724 /// @author Robert Hoekstra, SNL, Parallel Computational Sciences
725 /// @date 06/20/02
726 void Instance::registerStateLIDs( const std::vector<int> & staLIDVecRef)
727 {
728  AssertLIDs(staLIDVecRef.size() == numStateVars);
729 
730  int i=0;
731 
732  // Copy over the global ID lists:
733  staLIDVec = staLIDVecRef;
734 
735  li_QState = staLIDVec[i++];
736 
737  // If the capacitance is voltage dependent, we have additional state vars
738  if (solVarDepC)
739  {
740  li_vcapState = staLIDVec[i++];
741  li_capState = staLIDVec[i++];
742 
743  for (int j = 0; j<expNumVars; ++j)
744  {
745  li_dQdXState[j] = staLIDVec[i++];
746  }
747 
748  for (int j = 0; j<expNumVars; ++j)
749  {
750  li_dCdXState[j] = staLIDVec[i++];
751  }
752  }
753 
754 }
755 
756 
757 //-----------------------------------------------------------------------------
758 // Function : Instance::registerStoreLIDs
759 // Purpose : One store var for device current.
760 // Special Notes :
761 // Scope : public
762 // Creator : Richard Schiek, Electrical Systems Modeling
763 // Creation Date : 01/23/2013
764 //-----------------------------------------------------------------------------
765 /// Register the local store IDs
766 ///
767 /// In addition to state vector, Xyce maintains a separate datastructure
768 /// called a "store" vector. As with other such vectors, the device
769 /// declares at construction time how many store vector entries it needs,
770 /// and later Topology assigns locations for devices, returning LIDs.
771 ///
772 /// These LIDs are stored in this method for later use.
773 ///
774 /// The Capacitor device uses exactly one "store vector" element, where
775 /// it keeps the "lead current" that may be used on .PRINT lines as
776 /// "I(C1)" for the current through resistor R1.
777 ///
778 /// @param stoLIDVecRef Store variable local IDs
779 ///
780 /// @author Richard Schiek, Electrical Systems Modeling
781 /// @date 1/23/2013
782 void Instance::registerStoreLIDs(const std::vector<int> & stoLIDVecRef )
783 {
784  AssertLIDs(stoLIDVecRef.size() == getNumStoreVars());
785 
786  if( loadLeadCurrent )
787  {
788  li_store_dev_i = stoLIDVecRef[0];
789  }
790 }
791 
792 
793 //-----------------------------------------------------------------------------
794 // Function : Instance::getIntNameMap
795 // Purpose :
796 // Special Notes :
797 // Scope : public
798 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
799 // Creation Date : 05/13/05
800 //-----------------------------------------------------------------------------
801 /// Set up names for purely internal solution variables
802 ///
803 /// The capacitor device's initial condition code is implemented as
804 /// putting a DC voltage source of the given value across the capacitor,
805 /// to assure that the circuit is completely consistent at the operating
806 /// point with having that voltage across the capacitor.
807 ///
808 /// In the event that an initial condition is given, there is an extra
809 /// solution variable, the branch current of the voltage source. We give it
810 /// name that is the name of this capacitor with "_branch" appended.
811 ///
812 std::map<int,std::string> & Instance::getIntNameMap ()
813 {
814  // set up the internal name map, if it hasn't been already.
815  if (ICGiven)
816  {
817  if (intNameMap.empty ())
818  {
819  intNameMap[li_Bra] = spiceInternalName(getName(), "branch");
820  }
821  }
822 
823  return intNameMap;
824 }
825 
826 //-----------------------------------------------------------------------------
827 // Function : Instance::getStoreNameMap
828 // Purpose :
829 // Special Notes :
830 // Scope : public
831 // Creator : Rich Schiek, SNL, Electrical Systems Modeling
832 // Creation Date : 01/23/2013
833 //-----------------------------------------------------------------------------
834 /// Populates and returns the store name map.
835 ///
836 /// If the DeviceInstance::storeNameMap is empty, populate it.
837 ///
838 /// @return reference to the DeviceInstance::storeNameMap
839 ///
840 /// For the purpose of lead currents, store vector elements must be given
841 /// names that can be used to locate lead currents at print time.
842 /// When a netlist attempts to print, say, "I(C1)" the output code looks for
843 /// an entry in the store vector named C1:DEV_I.
844 ///
845 /// This method does the assignment of names to store vector elements.
846 ///
847 /// @author Richard Schiek, Electrical Systems Modeling
848 /// @date 1/23/2013
849 std::map<int,std::string> & Instance::getStoreNameMap ()
850 {
851  if( loadLeadCurrent && storeNameMap.empty () )
852  {
854  }
855 
856  return storeNameMap;
857 }
858 
859 
860 //-----------------------------------------------------------------------------
861 // Function : Instance::jacobianStamp
862 // Purpose :
863 // Special Notes :
864 // Scope : public
865 // Creator : Robert Hoekstra, SNL, Parallel Computational Sciences
866 // Creation Date : 08/27/02
867 //-----------------------------------------------------------------------------
868 ///
869 /// Return Jacobian stamp that informs topology of the layout of the
870 /// resistor jacobian.
871 ///
872 /// @return const reference to a std::vector of std::vector of
873 /// integers describing Jacobian stamp shape
874 //
875 /// The Jacobian stamp describes the shape of the Jacobian to the
876 /// Topology subsystem. The Topology subsystem, in turn, returns
877 /// the offsets into the matrix and solution vectors where this
878 /// instance data is located.
879 ///
880 /// The Jacobian stamp of the capacitor depends on whether an initial
881 /// condition is given or not.
882 ///
883 /// @author Robert Hoekstra
884 /// @date 8/20/2001
885 const std::vector< std::vector<int> > & Instance::jacobianStamp() const
886 {
887  if (ICGiven)
888  return jacStamp_IC;
889  else
890  return jacStamp;
891 }
892 
893 //-----------------------------------------------------------------------------
894 // Function : Instance::registerJacLIDs
895 // Purpose :
896 // Special Notes :
897 // Scope : public
898 // Creator : Robert Hoekstra, SNL, Parallel Computational Sciences
899 // Creation Date : 08/27/02
900 //-----------------------------------------------------------------------------
901 ///
902 /// Register the Jacobian local IDs
903 ///
904 /// @param jacLIDVec Jacobian local Ids
905 ///
906 /// @see Xyce::Device::Capacitor::Instance::Capacitor
907 ///
908 /// Having established local IDs for the solution variables, Topology must
909 /// also assign local IDs for the elements of the Jacobian matrix.
910 ///
911 /// For each non-zero element that was identified in the jacobianStamp,
912 /// Topology will assign a Jacobian LID. The jacLIDVec will have the
913 /// same structure as the JacStamp, but the values in the jacLIDVec will
914 /// be offsets into the row of the sparse Jacobian matrix corresponding
915 /// to the non-zero identified in the stamp.
916 ///
917 /// These offsets are stored in this method for use later when we load
918 /// the Jacobian.
919 ///
920 /// @note Because the capacitor's Jacobian stamp depends on whether an
921 /// initial condition was given or not, this method is slightly more
922 /// complex than the corresponding method in the Resistor. The method
923 /// is further complicated by the possibilty that the capacitance may
924 /// be given as an expression depending on solution variables; in this case,
925 /// the capacitor jacstamp has extra columns for the device's dependence on
926 /// those other variables.
927 ///
928 /// @see Xyce::Device::Resistor::Instance::registerJacLIDs
929 ///
930 /// @author Robert Hoekstra, SNL, Parallel Computational Sciences
931 /// @date 08/27/02
932 void Instance::registerJacLIDs( const std::vector< std::vector<int> > & jacLIDVec )
933 {
934  DeviceInstance::registerJacLIDs( jacLIDVec );
935 
936  APosEquPosNodeOffset = jacLIDVec[0][0];
937  APosEquNegNodeOffset = jacLIDVec[0][1];
938  ANegEquPosNodeOffset = jacLIDVec[1][0];
939  ANegEquNegNodeOffset = jacLIDVec[1][1];
940 
941  if (ICGiven)
942  {
943  APosEquBraNodeOffset = jacLIDVec[0][2];
944  ANegEquBraNodeOffset = jacLIDVec[1][2];
945  ABraEquPosNodeOffset = jacLIDVec[2][0];
946  ABraEquNegNodeOffset = jacLIDVec[2][1];
947  ABraEquBraNodeOffset = jacLIDVec[2][2];
948  }
949  // set offsets if we have a solution-variable dependent C
950  if (solVarDepC)
951  {
952  int depVarsBaseIndex = 2;
953  if (ICGiven)
954  {
955  depVarsBaseIndex=3;
956  }
957 
960 
961  for ( int i=0; i<expNumVars; ++i)
962  {
963  APosEquDepVarOffsets[i] = jacLIDVec[0][depVarsBaseIndex+i];
964  ANegEquDepVarOffsets[i] = jacLIDVec[1][depVarsBaseIndex+i];
965  }
966  }
967 
968  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
969  {
970  dout() << "Capacitor " << getName() << " Instance::registerJacLIDs"
971  //<< Util::push << std::endl
972  << std::endl
973  << "APosEquPosNodeOffset: " << APosEquPosNodeOffset << std::endl
974  << "APosEquNegNodeOffset: " << APosEquNegNodeOffset << std::endl
975  << "ANegEquPosNodeOffset: " << ANegEquPosNodeOffset << std::endl
976  << "ANegEquNegNodeOffset: " << ANegEquNegNodeOffset << std::endl
977  << "APosEquBraNodeOffset: " << APosEquBraNodeOffset << std::endl
978  << "ANegEquBraNodeOffset: " << ANegEquBraNodeOffset << std::endl
979  << "ABraEquPosNodeOffset: " << ABraEquPosNodeOffset << std::endl
980  << "ABraEquNegNodeOffset: " << ABraEquNegNodeOffset << std::endl
981  << "ABraEquBraNodeOffset: " << ABraEquBraNodeOffset << std::endl;
982  if (solVarDepC)
983  {
984  for ( int i=0; i<expNumVars; ++i)
985  {
986  dout() << "APosEquDepVarOffsets["<<i<<"]: " << APosEquDepVarOffsets[i] << std::endl
987  << "ANegEquDepVarOffsets["<<i<<"]: " << ANegEquDepVarOffsets[i] << std::endl;
988  }
989  }
990  //dout() << Util::pop << std::endl;
991  dout() << std::endl;
992  }
993 }
994 
995 //-----------------------------------------------------------------------------
996 // Function : Instance::setupPointers
997 // Purpose :
998 // Special Notes :
999 // Scope : public
1000 // Creator : Eric Keiter, SNL
1001 // Creation Date : 11/30/08
1002 //-----------------------------------------------------------------------------
1003 ///
1004 /// Setup direct access pointer to solution matrix and vectors.
1005 ///
1006 /// @see Xyce::Device::Capacitor::Instance::registerJacLIDs
1007 ///
1008 /// As an alternative to the row offsets defined in registerJacLIDs, it
1009 /// is also possible to obtain direct pointers of the Jacobian elements.
1010 ///
1011 /// This method uses the offsets obtained in registerJacLIDs to retrieve
1012 /// the pointers.
1013 ///
1014 /// In the resistor device the pointers to the matrix are only saved
1015 /// (and are only used for matrix access) if
1016 /// Xyce_NONPOINTER_MATRIX_LOAD is NOT defined at compile time. For
1017 /// some devices the use of pointers instead of array indexing can be
1018 /// a performance enhancement.
1019 ///
1020 /// Use of pointers in this device is disabled by defining
1021 /// Xyce_NONPOINTER_MATRIX_LOAD at compile time. When disabled, array
1022 /// indexing with the offsets from registerJacLIDs is used in
1023 /// the matrix load methods.
1024 ///
1025 /// @author Eric Keiter, SNL
1026 /// @date 11/30/08
1028 {
1029 #ifndef Xyce_NONPOINTER_MATRIX_LOAD
1030  N_LAS_Matrix & dFdx = *(extData.dFdxMatrixPtr);
1031  N_LAS_Matrix & dQdx = *(extData.dQdxMatrixPtr);
1032 
1037 
1038  if (solVarDepC)
1039  {
1042 
1043  for (int i=0; i<expNumVars; ++i)
1044  {
1047  }
1048  }
1049 
1050  // there are no contributions to the dFdx matrix from dependent C's, so
1051  // we don't bother with those pointers.
1052 
1053  if (ICGiven)
1054  {
1060  }
1061 #endif
1062 }
1063 
1064 //-----------------------------------------------------------------------------
1065 // Function : Instance::updatePrimaryState
1066 // Purpose :
1067 // Special Notes :
1068 // Scope : public
1069 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1070 // Creation Date : 01/29/01
1071 //-----------------------------------------------------------------------------
1072 ///
1073 /// Update the state variables.
1074 ///
1075 /// @return true on success
1076 ///
1077 /// The capacitor's state variables are used to store the charge on
1078 /// the capacitor. In the case of a constant capacitance the charge
1079 /// is \f$q_0=CV\f$, but the computation is much more complex if the
1080 /// capacitance is variable.
1081 ///
1082 /// @note This method is called by the default implementation of the
1083 /// loadState master function. While the Capacitor class reimplements
1084 /// the "Master" "loadState" function that loads the contributions
1085 /// from all capacitor devices in a single loop, because this method
1086 /// is so complicated for the case of solution-dependent capacitances,
1087 /// the overloaded method falls back on this function in that case.
1088 ///
1089 /// @note Even though this method IS called in some cases, note that it does
1090 /// NOT call updateIntermediateVars as other device updatePrimaryState methods
1091 /// do. The capacitor device doesn't actually *HAVE* an updateIntermediateVars
1092 /// method, and all the hard work is done either in the Master class
1093 /// updateState function directly for the simple, constant-capacitance
1094 /// case, or here, for the solution-variable-dependent-capacitance case.
1095 ///
1096 /// @see Xyce::Device::Capacitor::Master::updateState
1097 ///
1098 /// @author Eric Keiter, SNL, Parallel Computational Sciences
1099 /// @date 01/29/01
1100 ///
1102 {
1103  double * solVec = extData.nextSolVectorRawPtr;
1104  double * staVec = extData.nextStaVectorRawPtr;
1105  double v_pos = solVec[li_Pos];
1106  double v_neg = solVec[li_Neg];
1107 
1108  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1109  {
1110  dout() << " ----------------------------------" << std::endl;
1111  dout() << "Instance::updatePrimaryState:" << std::endl;
1112  }
1113 
1114  vcap = v_pos-v_neg;
1115 
1116  if( getSolverState().dcopFlag && ICGiven ) vcap = IC;
1117 
1118  if (!solVarDepC)
1119  {
1120  // Obtain the "current" value for the charge stored in the capacitor.
1121  q0 = C*vcap;
1122  staVec[li_QState] = q0;
1123  }
1124  else
1125  {
1126  // The capacitance depends on solution variables, the work load just
1127  // went up.
1128 
1129  // here, we're just calling evaluate
1130  // to get the derivatives of C, and discarding the actual value
1131  // of C (which has already been stored)
1132  double junk;
1133  expPtr->evaluate(junk,expVarDerivs);
1134 
1135 
1136  // At DC, the charge really still is V*C.
1137  if (getSolverState().dcopFlag)
1138  {
1139  q0 = vcap*C;
1140  // dQ/dX is just dC/dX*vcap
1141  for (int i=0;i<expNumVars; ++i)
1142  {
1143  expVarDerivs[i] *= vcap;
1144  }
1145  }
1146  else
1147  {
1148  ///
1149  /// For solution-variable dependent cap in transient, we can't
1150  /// use the expression \f$q_0=CV\f$ because the capacitance is
1151  /// actually dQ/dV, not Q/V. We must integrate CdV to get the
1152  /// charge. We approximate this by incrementally adding
1153  /// C'*deltaV as V changes. C' is the average capacitance
1154  /// between this and the previous step. Using the average
1155  /// assures charge conservation, at least when C is a function
1156  /// of vcap alone.
1157  ///
1158  double * oldstaVector = extData.currStaVectorRawPtr;
1159  double oldC;
1160  double oldVcap;
1161  q0=oldstaVector[li_QState];
1162  oldC=oldstaVector[li_capState];
1163  oldVcap=oldstaVector[li_vcapState];
1164 
1165  q0 += 0.5*(oldC+C)*(vcap-oldVcap);
1166 
1167  if (DEBUG_DEVICE && getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1168  {
1169  dout() << " Derivatives of C w.r.t variables: " << std::endl;
1170  for (int i=0;i<expNumVars;++i)
1171  {
1172  dout() << " expVarDerivs[ "<< i << " ] = " << expVarDerivs[i] << std::endl;
1173  }
1174  }
1175  ///
1176  /// When C is not a function of vcap alone, we have additional derivative
1177  /// terms that must also be integrated for proper computation of
1178  /// all dQdx entries.
1179  ///
1180  // If expressions contain the "ddt" (time differentiation)
1181  // function, everything becomes harder. So for now, we're going
1182  // to disallow use of ddt in C, so we don't have to deal with
1183  // that added complexity. This restriction is enforced in the
1184  // constructor, where we throw a fatal error if the user gives
1185  // us a ddt-dependent C. This code is NOT sufficient if ddt is allowed
1186  // in capacitance expressions.
1187  //
1188  // Now we have some trickiness because we are integrating CdV to get Q.
1189  // In order to get dQ/dX we have two cases:
1190  // X is one of our capacitor's voltage nodes: dQ/dX = C or -C depending
1191  // on whether X is the pos or negative node
1192  // X is NOT one of our nodes: dQ/dX = integral( dC/dX *dV)
1193 
1194  // For now, because we don't have an easy way to tell which of our
1195  // expression nodes is which, we'll just calculate all the dC/dX and dQ/dX
1196  // the same way. When it comes time to assemble the jacobian, we'll use
1197  // the node/equation offsets to know when to skip adding in this component.
1198 
1199  // The logic here is similar to the computation of the charge itself.
1200  // We'll use "expVarDerivs" to hold the final dQ/dX values.
1201 
1202 
1203  // Need to save the dC/dX value for next step.
1204  for (int i=0;i<expNumVars; ++i)
1205  {
1206  staVec[li_dCdXState[i]] = expVarDerivs[i];
1207  }
1208 
1209  // we have to integrate
1210 
1211  // dQ/dx = olddQdX + .5*(olddCdX+newdCdX)*(vcap-oldvcap)
1212 
1213  for (int i=0; i< expNumVars; ++i)
1214  {
1215  expVarDerivs[i] = oldstaVector[li_dQdXState[i]]
1216  + 0.5*(oldstaVector[li_dCdXState[i]]+expVarDerivs[i])*
1217  (vcap-oldstaVector[li_vcapState]);
1218 
1219  }
1220  }
1221  // Regardless of whether it's dcop or not, expVarDerivs now contains
1222  // dQ/dX for all the X's. It's WRONG if X is one of our voltage nodes,
1223  // so we have to be careful not to use it in that case. This logic
1224  // is handled down in loadDAEdQdx
1225  // Save to state:
1226  for (int i=0;i<expNumVars; ++i)
1227  {
1228  staVec[li_dQdXState[i]] = expVarDerivs[i];
1229  }
1230 
1231 
1232 
1233  staVec[li_QState] = q0;
1234  staVec[li_vcapState]=vcap;
1235  staVec[li_capState]=C;
1236  }
1237  return true;
1238 }
1239 
1240 
1241 //-----------------------------------------------------------------------------
1242 // Function : Instance::loadDAEQVector
1243 //
1244 // Purpose : Loads the Q-vector contributions for a single
1245 // capacitor instance.
1246 //
1247 // Special Notes : The "Q" vector is part of a standard DAE formalism in
1248 // which the system of equations is represented as:
1249 //
1250 // f(x) = dQ(x)/dt + F(x) - B(t) = 0
1251 //
1252 // Scope : public
1253 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1254 // Creation Date : 01/24/03
1255 //-----------------------------------------------------------------------------
1256 ///
1257 /// Load the DAE Q vector.
1258 ///
1259 /// @return true on success
1260 ///
1261 /// The Xyce DAE formulation solves the differential-algebraic
1262 /// equations \f$F(x)+dQ(x)/dt=0\f$ These are vector equations
1263 /// resulting from the modified nodal analysis of the circuit.
1264 ///
1265 /// This method loads the Q-vector contributions for a single capacitor
1266 /// instance.
1267 ///
1268 /// In this method, the offsets defined in registerLIDs are used to
1269 /// store the device's Q contributions into the Q vector.
1270 ///
1271 /// The Q vector is used for devices that store charge or magnetic
1272 /// energy. The capacitor is such a device
1273 ///
1274 /// @note This method is called by the default implementation of the
1275 /// loadDAEVectors master function. Since the capacitor class
1276 /// reimplements the "Master" "loadDAEVectors" function that loads the
1277 /// contributions from all capacitor devices in a single loop, THIS
1278 /// FUNCTION IS NOT ACTUALLY USED. The loadDAEQVector method is only
1279 /// called when a device class does not re-implement the master class.
1280 /// This can be a source of confusion when attempting to modify the Capacitor
1281 /// device, or any other device that reimplements the Master classes.
1282 ///
1283 /// @see Xyce::Device::Capacitor::Master::loadDAEVectors
1284 ///
1285 /// @author Eric Keiter, SNL, Parallel Computational Sciences
1286 /// @date 01/24/03
1288 {
1289  double * qVec = extData.daeQVectorRawPtr;
1290  qVec[li_Pos] += q0;
1291  qVec[li_Neg] += -q0;
1292 
1293  return true;
1294 }
1295 
1296 //-----------------------------------------------------------------------------
1297 // Function : Instance::loadDAEFVector
1298 //
1299 // Purpose : Loads the F-vector contributions for a single
1300 // capacitor instance.
1301 //
1302 // Special Notes : See the special notes for loadDAEFVector.
1303 //
1304 // For the capacitor this doesn't do anything, except in
1305 // the case of IC= being specified. In that case, then
1306 // some extra stuff is contributed that doesn't have time
1307 // derivatives.
1308 //
1309 // Scope : public
1310 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1311 // Creation Date : 01/24/03
1312 //-----------------------------------------------------------------------------
1313 ///
1314 /// Load the DAE F vector.
1315 ///
1316 /// @return true on success
1317 ///
1318 /// The Xyce DAE formulation solves the differential-algebraic
1319 /// equations \f$F(x)+dQ(x)/dt=0\f$ These are vector equations
1320 /// resulting from the modified nodal analysis of the circuit.
1321 ///
1322 /// This method loads the F-vector contributions for a single capacitor
1323 /// instance.
1324 ///
1325 /// In this method, the offsets defined in registerLIDs are used to
1326 /// store the device's F contributions into the F vector.
1327 ///
1328 /// The only time a capacitor adds anything to the F vector is in the
1329 /// DC phase of a computation if and only if an initial condition is given
1330 /// on the capacitor instance line.
1331 ///
1332 /// @note This method is called by the default implementation of the
1333 /// loadDAEVectors master function. Since the Capacitor class
1334 /// reimplements the "Master" "loadDAEVectors" function that loads the
1335 /// contributions from all capacitor devices in a single loop, THIS
1336 /// FUNCTION IS NOT ACTUALLY USED. The loadDAEFVector method is only
1337 /// called when a device class does not re-implement the master class.
1338 /// This can be a source of confusion when attempting to modify the Capacitor
1339 /// device, or any other device that reimplements the Master classes.
1340 ///
1341 /// @see Xyce::Device::Capacitor::Master::loadDAEVectors
1342 ///
1343 /// @author Eric Keiter, SNL, Parallel Computational Sciences
1344 /// @date 01/24/03
1346 {
1347  bool bsuccess = true;
1348  double Vpos = 0.0;
1349  double Vneg = 0.0;
1350  double v_tmp = 0.0;
1351  double * fVec = extData.daeFVectorRawPtr;
1352 
1353  if (ICGiven && getSolverState().dcopFlag)
1354  {
1355  // If we're doing the operating point and we have an initial condition,
1356  // get the current from the branch equation
1357  Vpos = (*extData.nextSolVectorPtr)[li_Pos];
1358  Vneg = (*extData.nextSolVectorPtr)[li_Neg];
1359 
1360  // load current into the F vector
1361  fVec[li_Pos] += (*extData.nextSolVectorPtr)[li_Bra];
1362  fVec[li_Neg] += -(*extData.nextSolVectorPtr)[li_Bra];
1363 
1364  if( loadLeadCurrent )
1365  {
1366  double * stoVec = extData.nextStoVectorRawPtr;
1368  }
1369  }
1370 
1371  // Initial condition stuff.
1372  v_tmp=0;
1373  if (ICGiven && getSolverState().dcopFlag)
1374  {
1375  v_tmp= (Vpos-Vneg-IC);
1376  }
1377 
1378  // Do this whenever there's a Branch equation, but only if there is one.
1379  // We'll be using 0 if we're not the OP.
1380  if (ICGiven)
1381  {
1382  fVec[li_Bra] += v_tmp;
1383  }
1384 
1385  return bsuccess;
1386 }
1387 
1388 //-----------------------------------------------------------------------------
1389 // Function : Instance::loadDAEdQdx
1390 //
1391 // Purpose : Loads the dQdx-matrix contributions for a single
1392 // capacitor instance.
1393 //
1394 // Special Notes : The "Q" vector is part of a standard DAE formalism in
1395 // which the system of equations is represented as:
1396 //
1397 // f(x) = dQ(x)/dt + F(x) - B(t) = 0
1398 //
1399 // Scope : public
1400 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1401 // Creation Date : 03/05/04
1402 //-----------------------------------------------------------------------------
1403 ///
1404 /// Load the DAE the derivative of the Q vector with respect to the
1405 /// solution vector x, dFdx
1406 ///
1407 /// Loads the contributions for a single resistor instance to the
1408 /// dFdx matrix (the Q contribution to the Jacobian).
1409 ///
1410 /// This method uses the Jacobian LIDs (row offsets) that were stored by
1411 /// registerJacLIDs.
1412 ///
1413 /// @see Xyce::Device::Capacitor::Instance::registerJacLIDs
1414 ///
1415 /// @note This method is called by the default implementation of the
1416 /// loadDAEMatrices master function. Since the Capacitor class
1417 /// reimplements the "Master" "loadDAEMatrices" function that loads the
1418 /// contributions from all capacitor devices in a single loop, THIS
1419 /// FUNCTION IS NOT ACTUALLY USED. The loadDAEdFdx method is only
1420 /// called when a device class does not re-implement the master class.
1421 /// This can be a source of confusion when attempting to modify the capacitor
1422 /// device, or any other device that reimplements the Master classes.
1423 ///
1424 /// @see Xyce::Device::Capacitor::Master::loadDAEMatrices
1425 ///
1426 /// @return true on success
1427 ///
1428 /// @author Eric Keiter, SNL, Parallel Computational Sciences
1429 /// @date 03/05/04
1431 {
1432  if (!(ICGiven&& getSolverState().dcopFlag))
1433  {
1434  N_LAS_Matrix & dQdx = *(extData.dQdxMatrixPtr);
1435  dQdx[li_Pos][APosEquPosNodeOffset] += C;
1436  dQdx[li_Pos][APosEquNegNodeOffset] -= C;
1437  dQdx[li_Neg][ANegEquPosNodeOffset] -= C;
1438  dQdx[li_Neg][ANegEquNegNodeOffset] += C;
1439 
1440 
1441  // Remember the comments in updatePrimaryState: expVarDerivs
1442  // contains dQ/dX, but they are only correct when X is not one of
1443  // our nodal voltages. If X *IS* one of our nodal voltages, dQ/dX
1444  // is either C or -C and is already handled above. We need only
1445  // do the stuff below for the dependencies on voltages that are
1446  // NOT our nodal voltages.
1447  if (solVarDepC)
1448  {
1449  for (int i=0; i< expNumVars; ++i)
1450  {
1453  {
1454  dQdx[li_Pos][APosEquDepVarOffsets[i]] += expVarDerivs[i];
1455  }
1458  {
1459  dQdx[li_Neg][ANegEquDepVarOffsets[i]] -= expVarDerivs[i];
1460  }
1461  }
1462  }
1463 
1464  }
1465  return true;
1466 }
1467 
1468 //-----------------------------------------------------------------------------
1469 // Function : Instance::loadDAEdFdx ()
1470 //
1471 // Purpose : Loads the F-vector contributions for a single
1472 // capacitor instance.
1473 //
1474 // Special Notes : See the special notes for loadDAEFVector.
1475 //
1476 // For the capacitor this doesn't do anything, unless IC=
1477 // has been specified for an initial condition. Then,
1478 // there are extra equations that do not contain time
1479 // derivatives.
1480 //
1481 // Scope : public
1482 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1483 // Creation Date : 03/05/04
1484 //-----------------------------------------------------------------------------
1485 ///
1486 /// Load the DAE the derivative of the F vector with respect to the
1487 /// solution vector x, dFdx
1488 ///
1489 /// Loads the contributions for a single capacitor instance to the
1490 /// dFdx matrix (the F contribution to the Jacobian).
1491 ///
1492 /// This method uses the Jacobian LIDs (row offsets) that were stored by
1493 /// registerJacLIDs.
1494 ///
1495 /// @see Xyce::Device::Capacitor::Instance::registerJacLIDs
1496 ///
1497 /// The capacitor only loads the dFdx matrix when an initial condition is
1498 /// given on the instance line for the device.
1499 ///
1500 /// @note This method is called by the default implementation of the
1501 /// loadDAEMatrices master function. Since the Capacitor class
1502 /// reimplements the "Master" "loadDAEMatrices" function that loads the
1503 /// contributions from all capacitor devices in a single loop, THIS
1504 /// FUNCTION IS NOT ACTUALLY USED. The loadDAEdFdx method is only
1505 /// called when a device class does not re-implement the master class.
1506 /// This can be a source of confusion when attempting to modify the Capacitor
1507 /// device, or any other device that reimplements the Master classes.
1508 ///
1509 /// @see Xyce::Device::Capacitor::Master::loadDAEMatrices
1510 ///
1511 /// @return true on success
1512 ///
1513 /// @author Eric Keiter, SNL, Parallel Computational Sciences
1514 /// @date 03/05/04
1516 {
1517  N_LAS_Matrix & dFdx = *(extData.dFdxMatrixPtr);
1518 
1519  if (ICGiven && getSolverState().dcopFlag)
1520  {
1521  // Special Jacobian if we're doing operating point when IC given
1522  dFdx[li_Pos][APosEquBraNodeOffset] += 1.0;
1523  dFdx[li_Neg][ANegEquBraNodeOffset] += -1.0;
1524  dFdx[li_Bra][ABraEquPosNodeOffset] += 1.0;
1525  dFdx[li_Bra][ABraEquNegNodeOffset] += -1.0;
1526  }
1527  else
1528  {
1529  if (ICGiven)
1530  dFdx[li_Bra][ABraEquBraNodeOffset] += 1.0;
1531  }
1532 
1533  return true;
1534 }
1535 
1536 //-----------------------------------------------------------------------------
1537 // Function : Instance::setIC
1538 // Purpose :
1539 // Special Notes :
1540 // Scope : public
1541 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
1542 // Creation Date : 01/10/02
1543 //-----------------------------------------------------------------------------
1545 {
1546  double Vic;
1547  double Vpos, Vneg;
1548  double * nextStaVector = extData.nextStaVectorRawPtr;
1549  double * currStaVector = extData.currStaVectorRawPtr;
1550 
1551  double * nextSolVector = extData.nextSolVectorRawPtr;
1552  double * currSolVector = extData.currSolVectorRawPtr;
1553 
1554  if (ICGiven)
1555  {
1556  Vic = IC;
1557  q0 = C*Vic;
1558 
1559  currStaVector[li_QState] = q0;
1560  nextStaVector[li_QState] = q0;
1561 
1562  Vneg = currSolVector[li_Neg];
1563  Vpos = Vneg + Vic;
1564 
1565  currSolVector[li_Pos] = Vpos;
1566  nextSolVector[li_Pos] = Vpos;
1567  currSolVector[li_Neg] = -Vpos;
1568  nextSolVector[li_Neg] = -Vpos;
1569  }
1570 
1571  return true;
1572 }
1573 
1574 //-----------------------------------------------------------------------------
1575 // Function : Instance::varTypes
1576 // Purpose :
1577 // Special Notes :
1578 // Scope : public
1579 // Creator : Robert Hoekstra, SNL, Parallel Computational Sciences
1580 // Creation Date : 02/17/04
1581 //-----------------------------------------------------------------------------
1582 void Instance::varTypes( std::vector<char> & varTypeVec )
1583 {
1584  if (ICGiven)
1585  {
1586  varTypeVec.resize(1);
1587  varTypeVec[0] = 'I';
1588  }
1589 }
1590 
1591 // Class Model
1592 
1593 //-----------------------------------------------------------------------------
1594 // Function : Model::processParams
1595 // Purpose :
1596 // Special Notes :
1597 // Scope : public
1598 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1599 // Creation Date : 6/03/02
1600 //-----------------------------------------------------------------------------
1601 ///
1602 /// Process model parameters
1603 ///
1604 /// @return true on success
1605 ///
1606 /// @author Eric Keiter, SNL, Parallel Computational Sciences
1607 /// @date 6/03/02
1609 {
1610 
1611  if (!tnomGiven)
1613  else
1614  tnom += CONSTCtoK; // if user-specified, assume it was in deg. C.
1615 
1616  // If there are any time dependent parameters, set their values for
1617  // the current time.
1618 
1619  return true;
1620 }
1621 
1622 //----------------------------------------------------------------------------
1623 // Function : Model::processInstanceParams
1624 // Purpose :
1625 // Special Notes :
1626 // Scope : public
1627 // Creator : Dave Shirely, PSSI
1628 // Creation Date : 03/23/06
1629 //----------------------------------------------------------------------------
1630 ///
1631 /// Process the instance parameters of instance owned by this model
1632 ///
1633 /// This method simply loops over all instances associated with this
1634 /// model and calls their processParams method.
1635 ///
1636 /// @return true
1637 ///
1638 /// @author Dave Shirely, PSSI
1639 /// @date 03/23/06
1641 {
1642 
1643  std::vector<Instance*>::iterator iter;
1644  std::vector<Instance*>::iterator first = instanceContainer.begin();
1645  std::vector<Instance*>::iterator last = instanceContainer.end();
1646 
1647  for (iter=first; iter!=last; ++iter)
1648  {
1649  (*iter)->processParams();
1650  }
1651 
1652  return true;
1653 }
1654 
1655 //-----------------------------------------------------------------------------
1656 // Function : Model::Model
1657 // Purpose : model block constructor
1658 // Special Notes :
1659 // Scope : public
1660 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
1661 // Creation Date : 5/17/00
1662 //-----------------------------------------------------------------------------
1663 ///
1664 /// Construct a capacitor model from a "model block" that was created
1665 /// by the netlist parser.
1666 ///
1667 /// @param configuration
1668 /// @param model_block
1669 /// @param factory_block
1670 ///
1671 /// @author Eric Keiter, SNL, Parallel Computational Sciences
1672 /// @date 5/17/00
1674  const Configuration & configuration,
1675  const ModelBlock & model_block,
1676  const FactoryBlock & factory_block)
1677  : DeviceModel(model_block, configuration.getModelParameters(), factory_block),
1678  capacitanceMultiplier(1.0),
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 * bVec, 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  .registerModelType("cap", 1);
2180 }
2181 
2182 //-----------------------------------------------------------------------------
2183 // Function : capSensitivity::operator
2184 // Purpose : produces df/dp and dq/dp, where p=C.
2185 // Special Notes :
2186 // Scope : public
2187 // Creator : Eric Keiter, SNL
2188 // Creation Date : 7/31/2014
2189 //-----------------------------------------------------------------------------
2191  const ParameterBase &entity,
2192  const std::string & name,
2193  std::vector<double> & dfdp,
2194  std::vector<double> & dqdp,
2195  std::vector<double> & dbdp,
2196  std::vector<int> & Findices,
2197  std::vector<int> & Qindices,
2198  std::vector<int> & Bindices
2199  ) const
2200 {
2201  const ParameterBase * e1 = &entity;
2202  const Instance * in = dynamic_cast<const Instance *> (e1);
2203 
2204  double * solVec = in->extData.nextSolVectorRawPtr;
2205  double v_pos = solVec[in->li_Pos];
2206  double v_neg = solVec[in->li_Neg];
2207  double vcap = v_pos-v_neg;
2208 
2209  double dqdpLoc = vcap;
2210 
2211  dqdp.resize(2);
2212  dqdp[0] = +dqdpLoc;
2213  dqdp[1] = -dqdpLoc;
2214 
2215  Qindices.resize(2);
2216  Qindices[0] = in->li_Pos;
2217  Qindices[1] = in->li_Neg;
2218 }
2219 
2220 } // namespace Capacitor
2221 } // namespace Device
2222 } // namespace Xyce