Xyce  6.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
N_DEV_Xygra.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_Xygra.C,v $
27 //
28 // Purpose : Provide a linking device for coupling to Alegra
29 // simulations.
30 //
31 // Special Notes : Alegra uses API calls to set conductance and source
32 // information used to construct F (or RHS), and to retrieve
33 // nodal voltages.
34 //
35 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
36 //
37 // Creation Date : 08/18/08
38 //
39 // Revision Information:
40 // ---------------------
41 //
42 // Revision Number: $Revision: 1.42.2.2 $
43 //
44 // Revision Date : $Date: 2014/03/06 23:33:44 $
45 //
46 // Current Owner : $Author: tvrusso $
47 //-------------------------------------------------------------------------
48 #include <Xyce_config.h>
49 
50 // ---------- Standard Includes ----------
51 #include <N_UTL_Misc.h>
52 
53 #include <sstream>
54 
55 // ---------- Xyce Includes ----------
56 #include <N_DEV_DeviceOptions.h>
57 #include <N_DEV_DeviceMaster.h>
58 #include <N_DEV_ExternData.h>
59 #include <N_DEV_MatrixLoadData.h>
60 #include <N_DEV_SolverState.h>
61 #include <N_DEV_Xygra.h>
62 #include <N_DEV_Message.h>
63 #include <N_ERH_ErrorMgr.h>
64 
65 #include <N_LAS_Vector.h>
66 #include <N_LAS_Matrix.h>
67 
68 namespace Xyce {
69 namespace Device {
70 
71 template<>
73 {
74  addPar ("NAME", "COIL0", &XygraCoilData::name);
75  addPar ("NUMWINDINGS", 1, &XygraCoilData::numWindings);
76 }
77 
79  static ParametricData<XygraCoilData> parMap;
80 
81  return parMap;
82 }
83 
84 
85 //-----------------------------------------------------------------------------
86 // Function : XygraCoilData::XygraCoilData
87 // Purpose : constructor
88 // Special Notes :
89 // Scope : public
90 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
91 // Creation Date : 9/11/2008
92 //-----------------------------------------------------------------------------
94  : CompositeParam(getParametricData()),
95  name("coil"),
96  numWindings(1)
97 {}
98 
99 //-----------------------------------------------------------------------------
100 // Function : XygraCoilData::processParams
101 // Purpose :
102 // Special Notes :
103 // Scope : public
104 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
105 // Creation Date : 9/11/2008
106 //-----------------------------------------------------------------------------
108 {
109 }
110 
111 #ifdef Xyce_DEBUG_DEVICE
112 //-----------------------------------------------------------------------------
113 // Function : operator<<
114 // Purpose : "<<" operator for XygraCoilData
115 // Special Notes :
116 // Scope : public
117 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
118 // Creation Date : 9/11/2008
119 //-----------------------------------------------------------------------------
120 std::ostream & operator<<(std::ostream & os, const XygraCoilData & xcd)
121 {
122  os << " XygraCoilData for: name = " << xcd.getName() <<
123  " numWindings=" << xcd.getNumWindings() <<
124  std::endl;
125 
126  return os;
127 }
128 #endif
129 
130 
131 
132 
133 namespace Xygra {
134 
135 
137 {
138  // Set up configuration constants:
140 }
141 
143 {}
144 
145 
146 // Class Instance
147 //-----------------------------------------------------------------------------
148 // Function : Instance::processParams
149 // Purpose :
150 // Special Notes :
151 // Scope : public
152 // Creator : Tom Russo
153 // Creation Date : 8/18/2008
154 //-----------------------------------------------------------------------------
156 {
157  // If there are any time dependent parameters, set their values at for
158  // the current time.
159 
160  // now set the temperature related stuff.
161  //updateTemperature(temp);
162 
163  return true;
164 }
165 
166 //-----------------------------------------------------------------------------
167 // Function : Instance::updateTemperature
168 // Purpose :
169 // Special Notes :
170 // Scope : public
171 // Creator : Tom Russo, Component Information and Models
172 // Creation Date : 8/18/2008
173 //-----------------------------------------------------------------------------
174 bool Instance::updateTemperature ( const double & temp)
175 {
176  bool bsuccess = true;
177  return bsuccess;
178 }
179 
180 //-----------------------------------------------------------------------------
181 // Function : Instance::Instance
182 // Purpose : constructor
183 // Special Notes :
184 // Scope : public
185 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
186 // Creation Date : 8/18/2008
187 //-----------------------------------------------------------------------------
189  const Configuration & configuration,
190  const InstanceBlock & IB,
191  Model & Miter,
192  const FactoryBlock & factory_block)
193 
194  : DeviceInstance(IB, configuration.getInstanceParameters(), factory_block),
195  model_(Miter),
196  t0_(0),
197  t1_(0)
198 {
199  numExtVars = IB.numExtVars; // we have as many as were specified on the
200  // instance line
201  numIntVars = 0;
202  numStateVars = 0;
203 
204  // Start with both vectors empty. We'll only make them the right size
205  // when they get set from setConductances or setSources, so we can tell
206  // whether to bother using them. If not the right size, no contribution
207  // from those terms.
208  theSourceVector_.clear();
209  theConductanceMatrix_.clear();
210  theKMatrix_.clear();
211  k0_.clear();
212  k1_.clear();
213  s0_.clear();
214  s1_.clear();
215 
216 
217  // Set params to constant default values:
218  setDefaultParams ();
219 
220  // Set params according to instance line and constant defaults from metadata:
221  setParams (IB.params);
222 
223  // Set any non-constant parameter defaults:
224 
225  //if (!given("TEMP"))
226  // temp = getDeviceOptions().temp.dVal();
227 
228  // Calculate any parameters specified as expressions:
230 
231  // calculate dependent (ie computed) params and check for errors:
232  processParams ();
233 
234 
235  int totalNumIntVars=0;
236  totalNumWindings = 0;
237  if (!coilDataVec.empty())
238  {
239 #ifdef Xyce_DEBUG_DEVICE
240  Xyce::dout() << " We were given a coil spec." << std::endl;
241  Xyce::dout() << " There were " << coilDataVec.size() << " coils." << std::endl;
242 #endif
243 
244  // numExtVars is apparently 0 for the default device (which is never
245  // used except for parameter output) and its associated coilDataVec always
246  // has one. So skip this test if numExtVars==0.
247 
248  if ( numExtVars != 0 && numExtVars != coilDataVec.size()*2)
249  {
250  UserError0(*this) << "Xygra Device " << getName() << "has "<< coilDataVec.size() << " coils and " << numExtVars << " external nodes. Number of external nodes should be twice number of coils.";
251  }
252 
253  nCoils=coilDataVec.size();
254  nWindings.resize(nCoils);
255  coilNames.resize(nCoils);
256  for ( int i = 0; i < nCoils; ++i)
257  {
258 #ifdef Xyce_DEBUG_DEVICE
259  Xyce::dout() << " Coil["<< i << "] name is " << coilDataVec[i]->getName() << ", has " << coilDataVec[i]->getNumWindings() << " windings. " << std::endl;
260 #endif // Xyce_DEBUG_DEVICE
261  totalNumIntVars += coilDataVec[i]->getNumWindings() - 1;
262  nWindings[i] = coilDataVec[i]->getNumWindings();
263  coilNames[i] = coilDataVec[i]->getName();
265  }
266 
267 #ifdef Xyce_DEBUG_DEVICE
268  Xyce::dout() << " We would have " << totalNumIntVars << " internal vars " << std::endl;
269 #endif
270  }
271  else
272  {
273  // if no coil spec given, each pair of external nodes corresponds to a
274  // one-winding coil.
275  nCoils=numExtVars/2;
276  nWindings.resize(nCoils,1);
278  if (nCoils*2 != numExtVars)
279  {
280  std::ostringstream ost;
281 
282  ost << "Instance::Instance:";
283  ost << "Number of nodes given to device " << getName() << "is not even."
284  << std::endl;
285  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, ost.str() );
286  }
287 
288  }
289  // we'll use these in mapping between winding currents/dV's and nodal
290  // contributions
291  coilExtStart.resize(nCoils);
292  coilIntStart.resize(nCoils);
294 
295  // set up numIntVars:
296  numIntVars = totalNumIntVars;
297 
298  // Now that we have computed our numIntVars, it's OK to
299  // set up jacStamp. For now, we'll have to assume a full (dense) Jacobian
300  // maybe there's a way to get this done later, but I think not, because
301  // topology needs to know it too soon, and we won't know the real structure
302  // until it's too late to use it.
303  //
304  setupJacStamp_();
305 
306  // set up numStateVars:
307  numStateVars = 0;
308 }
309 
310 //-----------------------------------------------------------------------------
311 // Function : Instance::~Instance
312 // Purpose : destructor
313 // Special Notes :
314 // Scope : public
315 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
316 // Creation Date : 8/18/2008
317 //-----------------------------------------------------------------------------
319 {
320 }
321 
322 //-----------------------------------------------------------------------------
323 // Function : Instance::setupJacStamp_
324 // Purpose : Utility function to set up the jacobian stamp
325 // Special Notes : Just makes a dense jacobian stamp for now
326 // Scope : private
327 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
328 // Creation Date : 8/18/08
329 //-----------------------------------------------------------------------------
331 {
332  int numNodes = numExtVars+numIntVars;
333  jacStamp_.resize(numNodes);
334  for (int i=0; i<numNodes; ++i)
335  {
336  jacStamp_[i].resize(numNodes);
337  for (int j=0; j<numNodes; ++j)
338  {
339  jacStamp_[i][j]=j;
340  }
341  }
342 }
343 
344 //-----------------------------------------------------------------------------
345 // Function : Instance::setConductances
346 // Purpose : take matrix of conductances, copy into our local array
347 // Special Notes : The matrix is dense
348 // Scope : public
349 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
350 // Creation Date : 8/18/08
351 //-----------------------------------------------------------------------------
352 bool Instance::setConductances(const std::vector< std::vector<double> > &cM )
353 {
354  int numNodes = numExtVars+numIntVars;
355  // Sanity check:
356  if (cM.size() != numNodes)
357  {
358  std::ostringstream ost;
359 
360  ost << "Instance::setConductances:";
361  ost << " Input matrix passed to device " << getName()
362  << " (" << cM.size()
363  << ") does not have number of rows required by netlist specification ("
364  << numNodes << ")." << std::endl;
365  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, ost.str());
366  }
367  theConductanceMatrix_.resize(numNodes);
368  for (int i=0; i<numNodes; ++i)
369  {
370  if (cM[i].size() != numNodes)
371  {
372  std::ostringstream ost;
373 
374  ost << "Instance::setConductances:";
375  ost << " row " << i << "of matrix passed to device " << getName()
376  << " has " << cM[i].size()
377  << " columns instead of number required by netlist specification ("
378  << numNodes << ")." << std::endl;
379  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, ost.str());
380  }
381 
382 #ifdef Xyce_DEBUG_DEVICE
384  {
385  Xyce::dout() << std::endl << subsection_divider << std::endl;
386  Xyce::dout() << " Device " << getName() << " setConductances called for time " << getSolverState().currTime << std::endl;
387  Xyce::dout() << std::endl << subsection_divider << std::endl;
388  }
389 #endif
390 
391  theConductanceMatrix_[i].resize(numNodes);
392  theConductanceMatrix_[i]=cM[i];
393  }
394  return true;
395 }
396 
397 //-----------------------------------------------------------------------------
398 // Function : Instance::setK
399 // Purpose : take matrix of K values, copy into our local array
400 // Special Notes : The matrix is dense
401 // Scope : public
402 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
403 // Creation Date : 8/18/08
404 //-----------------------------------------------------------------------------
405 bool Instance::setK(const std::vector< std::vector<double> > &kM ,
406  const double t)
407 {
408  std::vector<std::vector<double> > * kPtr;
409 
410  // Decide which of our two K's we're setting
411  if (t==0 || t == getSolverState().currTime)
412  {
413  kPtr = &k0_;
414  t0_=t;
415  }
416  else
417  {
418  kPtr = &k1_;
419  k0_=k1_; // copy old "future" K to "current" K, because we're resetting
420  // future
421  t0_=t1_; // save old "future" time
422  t1_=t; // set new one
423  }
424 
425  // Sanity check:
426  if (kM.size() != totalNumWindings)
427  {
428  std::ostringstream ost;
429 
430  ost << "Instance::setK:";
431  ost << " Input matrix passed to device " << getName()
432  << " (" << kM.size()
433  << ") does not have number of rows required by netlist specification ("
434  << totalNumWindings << ")." << std::endl;
435  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, ost.str());
436  }
437 
438  for (int i=0; i<totalNumWindings; ++i)
439  {
440  if (kM[i].size() != totalNumWindings)
441  {
442  std::ostringstream ost;
443 
444  ost << "Instance::setK:";
445  ost << " row " << i << "of matrix passed to device " << getName()
446  << " has " << kM[i].size()
447  << " columns instead of number required by netlist specification ("
448  << totalNumWindings << ")." << std::endl;
449  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, ost.str());
450  }
451  }
452 
453  // Sanity check is done, the matrix we're given fits our needs. Now just
454  // copy it.
455  (*kPtr) = kM;
456 
457  // If this is the first call, we have to set K1 properly.
458  if (t==0)
459  {
460  k1_ = k0_; // initialize k1
461  t1_=t0_;
462  }
463 
464 #ifdef Xyce_DEBUG_DEVICE
466  {
467  Xyce::dout() << std::endl << subsection_divider << std::endl;
468  Xyce::dout() << " Device " << getName() << " setK called for time " << getSolverState().currTime << std::endl;
469  Xyce::dout() << std::endl << subsection_divider << std::endl;
470  }
471 #endif
472  return true;
473 }
474 
475 //-----------------------------------------------------------------------------
476 // Function : Instance::getVoltages
477 // Purpose : Copy the values of this devices' nodal voltages
478 // into the provided array.
479 // Special Notes :
480 // Scope : public
481 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
482 // Creation Date : 8/18/08
483 //-----------------------------------------------------------------------------
484 bool Instance::getVoltages( std::vector< double > &nV )
485 {
486  bool bsuccess=true;
487  N_LAS_Vector * solVectorPtr = extData.nextSolVectorPtr;
488  int numNodes = numExtVars + numIntVars;
489  nV.resize(numNodes);
490 
491  // We will output these in a different order than we have them stored,
492  // so that all of a coil's nodes are in a predictable order:
493  // Coil1 positive
494  // [coil1 internal nodes]
495  // coil1 negative
496  // coil2 positive [...etc....]
497  int offset = 0;
498  for (int coil=0; coil < nCoils; ++coil)
499  {
500  nV[offset++] = (*solVectorPtr)[li_Nodes_[coilExtStart[coil]]];
501  for (int winding=0; winding< nWindings[coil]-1; ++winding)
502  {
503  nV[offset++] = (*solVectorPtr)[li_Nodes_[coilIntStart[coil]+winding]];
504  }
505  nV[offset++] = (*solVectorPtr)[li_Nodes_[coilExtStart[coil]+1]];
506  }
507 
508  return bsuccess;
509 }
510 
511 //-----------------------------------------------------------------------------
512 // Function : Instance::getCoilWindings
513 // Purpose : return individual winding counts for each coil in provided
514 // array
515 // Special Notes :
516 // Scope : public
517 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
518 // Creation Date : 9/15/08
519 //-----------------------------------------------------------------------------
520 void Instance::getCoilWindings( std::vector< int > &cW )
521 {
522  cW = nWindings;
523  return;
524 }
525 
526 //-----------------------------------------------------------------------------
527 // Function : Instance::getCoilNames
528 // Purpose : return individual names for each coil in provided
529 // array
530 // Special Notes :
531 // Scope : public
532 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
533 // Creation Date : 9/29/08
534 //-----------------------------------------------------------------------------
535 void Instance::getCoilNames( std::vector< std::string > &cN )
536 {
537  cN = coilNames;
538  return;
539 }
540 
541 //-----------------------------------------------------------------------------
542 // Function : Instance::setSources
543 // Purpose : take vector of source terms, copy into our local array
544 // Special Notes :
545 // Scope : public
546 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
547 // Creation Date : 8/18/08
548 //-----------------------------------------------------------------------------
549 bool Instance::setSources(const std::vector< double > &sV,
550  const double t)
551 {
552  int numNodes = numExtVars + numIntVars;
553 
554  std::vector<double> * sPtr;
555 
556 #ifdef Xyce_DEBUG_DEVICE
557  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
558  {
559  Xyce::dout() << std::endl << subsection_divider << std::endl;
560  Xyce::dout() << " Device " << getName() << " setSources called for time " << getSolverState().currTime << " with value t= " << t << std::endl;
561  Xyce::dout() << std::endl << subsection_divider << std::endl;
562  }
563 #endif
564 
565  // Decide which of our two S's we're setting
566  if (t==0 || t == getSolverState().currTime)
567  {
568  sPtr = &s0_;
569  t0_=t;
570 #ifdef Xyce_DEBUG_DEVICE
571  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
572  {
573  Xyce::dout() << " setting source s0 for t=" << t << std::endl;
574  }
575 #endif
576  }
577  else
578  {
579  sPtr = &s1_;
580  s0_=s1_; // copy old "future" S to "current" S, because we're resetting
581  // future
582  t0_=t1_; // save old "future" time
583  t1_=t; // set new one
584 #ifdef Xyce_DEBUG_DEVICE
585  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
586  {
587  Xyce::dout() << " setting source s1 for t=" << t << std::endl;
588  }
589 #endif
590  }
591 
592  // Sanity check:
593  if (sV.size() != totalNumWindings)
594  {
595  std::ostringstream ost;
596 
597  ost << "Instance::setSources:";
598  ost << " Input vector passed to device " << getName()
599  << " (" << sV.size()
600  << ") does not have number of rows required by netlist specification ("
601  << totalNumWindings << ")." << std::endl;
602  N_ERH_ErrorMgr::report(N_ERH_ErrorMgr::DEV_FATAL, ost.str());
603  }
604 
605  // Sanity check passes, copy the vector
606  (*sPtr) = sV;
607  // If this is the first call, we have to set S1 properly.
608  if (t==0)
609  {
610  s1_ = s0_; // initialize k1
611  t1_=t0_;
612  }
613 
614 
615  return true;
616 }
617 
618 //-----------------------------------------------------------------------------
619 // Function : Instance::interpolateSandK_
620 // Purpose : use s0,k0,t0, s1, k1, t1 to interpolate S and K to current
621 // time
622 // Special Notes :
623 // Scope : private
624 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
625 // Creation Date : 9/15/2008
626 //-----------------------------------------------------------------------------
628 {
629  // if S has been set at all:
630  if (!s0_.empty());
631  {
633  if (getSolverState().currTime > t0_ && !s1_.empty() && t0_ != t1_)
634  {
635  double fac=(getSolverState().currTime - t0_)/(t1_-t0_);
636  for (int i=0;i<totalNumWindings; ++i)
637  {
638  theSourceVector_[i] += (s1_[i]-s0_[i])*fac;
639 #ifdef Xyce_DEBUG_DEVICE
641  {
642  Xyce::dout() << " interpolated source for t=" << getSolverState().currTime << std::endl;
643  Xyce::dout() << " source vector is: " << std::endl;
644  Xyce::dout() << " s0["<<i<<"] = " << s0_[i] << std::endl;
645  Xyce::dout() << " s1["<<i<<"] = " << s1_[i] << std::endl;
646  Xyce::dout() << " fac = " << fac << std::endl;
647  Xyce::dout() << " s["<<i<<"] = " << theSourceVector_[i] << std::endl;
648  }
649 #endif
650  }
651  }
652  }
653 
654  // if K has been set at all:
655  if (!k0_.empty());
656  {
658  if (getSolverState().currTime > t0_ && !k1_.empty() && t0_ != t1_)
659  {
660  double fac=(getSolverState().currTime - t0_)/(t1_-t0_);
661  for (int i=0;i<totalNumWindings; ++i)
662  {
663  for (int j=0;i<totalNumWindings; ++i)
664  {
665  theKMatrix_[i][j] += (k1_[i][j]-k0_[i][j])*fac;
666  }
667  }
668  }
669  }
670 }
671 
672 //-----------------------------------------------------------------------------
673 // Function : Instance::registerLIDs
674 // Purpose :
675 // Special Notes :
676 // Scope : public
677 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
678 // Creation Date : 8/18/2008
679 //-----------------------------------------------------------------------------
680 void Instance::registerLIDs(const std::vector<int> & intLIDVecRef,
681  const std::vector<int> & extLIDVecRef)
682 {
683  AssertLIDs(intLIDVecRef.size() == numIntVars);
684  AssertLIDs(extLIDVecRef.size() == numExtVars);
685 
686 #ifdef Xyce_DEBUG_DEVICE
687  if (getDeviceOptions().debugLevel > 0)
688  {
689  Xyce::dout() << std::endl << section_divider << std::endl;
690  Xyce::dout() << " Instance::registerLIDs" << std::endl;
691  Xyce::dout() << " name = " << getName() << std::endl;
692  }
693 #endif
694 
695  int numNodes = numExtVars + numIntVars;
696 
697  // copy over the global ID lists.
698  intLIDVec = intLIDVecRef;
699  extLIDVec = extLIDVecRef;
700 
701  li_Nodes_.resize(numNodes);
702 
703  int extVar=0;
704  int intVar=0;
705  int theVar=0;
706 
707  // First comes the external nodes
708  for (int coil=0; coil<nCoils; ++coil)
709  {
710  coilExtStart[coil] = theVar;
711  li_Nodes_[theVar++] = extLIDVec[extVar++];
712  li_Nodes_[theVar++] = extLIDVec[extVar++];
713  }
714  // now the internals
715 
716  for (int coil=0; coil<nCoils; ++coil)
717  {
718  coilIntStart[coil] = theVar;
719  for( int i=0; i< nWindings[coil]-1; ++i)
720  {
721  li_Nodes_[theVar++] = intLIDVec[intVar++];
722  }
723  }
724 
725  // This doesn't really belong in registerLIDs, but since it's only here that
726  // we've calculated everything we need to know, and we need this stuff
727  // later, this is a good place to do it.
728 
729  int globalWinding=0;
730  for (int coil=0; coil<nCoils; ++coil)
731  {
732  for ( int coilWinding=0; coilWinding<nWindings[coil]; coilWinding++)
733  {
734  int posNode;
735  int negNode;
736  if (coilWinding==0)
737  {
738  posNode=coilExtStart[coil];
739  }
740  else
741  {
742  posNode=coilIntStart[coil]+(coilWinding-1);
743  }
744  if (coilWinding==nWindings[coil]-1)
745  {
746  negNode=coilExtStart[coil]+1;
747  }
748  else
749  {
750  negNode=coilIntStart[coil]+coilWinding;
751  }
752  windingNodes[globalWinding++] = std::pair<int,int>(posNode,negNode);
753  }
754  }
755 
756 #ifdef Xyce_DEBUG_DEVICE
757  if (getDeviceOptions().debugLevel > 0 )
758  {
759  for (int i=0; i<numNodes; ++i)
760  {
761  Xyce::dout() << " li_Nodes_[" <<i << "] = " << li_Nodes_[i] << std::endl;
762  }
763 
764  for (int winding=0; winding<totalNumWindings; winding++)
765  {
766  Xyce::dout() << "Winding " << winding << " between node "
767  << windingNodes[winding].first << " and "
768  << windingNodes[winding].second << std::endl;
769  }
770  }
771 #endif
772 
773 #ifdef Xyce_DEBUG_DEVICE
774  if (getDeviceOptions().debugLevel > 0 )
775  {
776  Xyce::dout() << section_divider << std::endl;
777  }
778 #endif
779 }
780 
781 //-----------------------------------------------------------------------------
782 // Function : Instance::getIntNameMap
783 // Purpose :
784 // Special Notes :
785 // Scope : public
786 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
787 // Creation Date : 8/18/2008
788 //-----------------------------------------------------------------------------
789 std::map<int,std::string> & Instance::getIntNameMap ()
790 {
791  // set up the internal name map, if it hasn't been already.
792 
793  if (intNameMap.empty() && numIntVars != 0)
794  {
795  std::string tmpstr;
796  // set up the internal name map
797  std::ostringstream ost;
798 
799  for (int coil = 0; coil< nCoils; ++coil)
800  {
801  if (nWindings[coil]>1)
802  {
803  // this coil has internal nodes
804  for (int nodeOffset=1; nodeOffset<nWindings[coil]; ++nodeOffset)
805  {
806  int localIndex=li_Nodes_[coilIntStart[coil]+nodeOffset-1];
807  ost.str("");
808  ost << getName() << "_coil" << coil << "_Internal"<<nodeOffset;
809  tmpstr = ost.str();
810  spiceInternalName(tmpstr);
811  intNameMap[localIndex] = tmpstr;
812  }
813  }
814  }
815  }
816 
817 
818  return intNameMap;
819 }
820 
821 //-----------------------------------------------------------------------------
822 // Function : Instance::registerStateLIDs
823 // Purpose :
824 // Special Notes :
825 // Scope : public
826 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
827 // Creation Date : 8/18/2008
828 //-----------------------------------------------------------------------------
829 void Instance::registerStateLIDs( const std::vector<int> & staLIDVecRef )
830 {
831  AssertLIDs(staLIDVecRef.size() == numStateVars);
832 
833  // copy over the global ID lists.
834  staLIDVec = staLIDVecRef;
835 }
836 
837 //-----------------------------------------------------------------------------
838 // Function : Instance::jacobianStamp
839 // Purpose :
840 // Special Notes :
841 // Scope : public
842 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
843 // Creation Date : 8/18/2008
844 //-----------------------------------------------------------------------------
845 const std::vector< std::vector<int> > & Instance::jacobianStamp() const
846 {
847  return jacStamp_;
848 }
849 
850 //-----------------------------------------------------------------------------
851 // Function : Instance::registerJacLIDs
852 // Purpose :
853 // Special Notes :
854 // Scope : public
855 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
856 // Creation Date : 8/18/2008
857 //-----------------------------------------------------------------------------
858 void Instance::registerJacLIDs( const std::vector< std::vector<int> > & jacLIDVec )
859 {
860  DeviceInstance::registerJacLIDs( jacLIDVec );
861  int numNodes=numExtVars+numIntVars;
862  A_Equ_NodeOffsets_.resize(numNodes);
863  for (int equ=0; equ < numNodes; ++equ)
864  {
865  A_Equ_NodeOffsets_[equ].resize(numNodes);
866  for (int node=0; node < numNodes; ++node)
867  {
868  A_Equ_NodeOffsets_[equ][node] = jacLIDVec[equ][node];
869  }
870  }
871 
872 }
873 
874 //-----------------------------------------------------------------------------
875 // Function : Instance::updatePrimaryState
876 // Purpose :
877 // Special Notes :
878 // Scope : public
879 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
880 // Creation Date : 8/18/2008
881 //-----------------------------------------------------------------------------
883 {
884  bool bsuccess = true;
885 
886 #ifdef Xyce_DEBUG_DEVICE
887  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
888  {
889  Xyce::dout() << "Instance::updatePrimaryState" <<std::endl;
890  }
891 #endif
892  bsuccess = updateIntermediateVars();
893  return bsuccess;
894 }
895 
896 //-----------------------------------------------------------------------------
897 // Function : Instance::updateSecondaryState
898 // Purpose :
899 // Special Notes :
900 // Scope : public
901 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
902 // Creation Date : 8/18/2008
903 //-----------------------------------------------------------------------------
905 {
906  bool bsuccess = true;
907 
908  return bsuccess;
909 }
910 
911 
912 //-----------------------------------------------------------------------------
913 // Function : Instance::updateIntermediateVars
914 // Purpose :
915 // Special Notes :
916 // Scope : public
917 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
918 // Creation Date : 9/12/2008
919 //-----------------------------------------------------------------------------
921 {
922 
923  bool bsuccess=true;
924  int numNodes=numIntVars+numExtVars;
925  N_LAS_Vector * solVectorPtr = extData.nextSolVectorPtr;
926 
927 
928 #ifdef Xyce_DEBUG_DEVICE
929 
930  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
931  {
932  Xyce::dout() << subsection_divider << std::endl;
933  Xyce::dout() << "Instance::updateIntermediateVars "<<std::endl;
934  Xyce::dout() << " name = " << getName() << std::endl;
935  }
936 #endif
937 
938  if (getSolverState().newtonIter == 0) // start of new timestep
939  {
941  }
942 
943  if (solutionVars.size() != numNodes)
944  {
945  // set up all the Fad vectors with the right sizes
946  // We only need to do this the first time around
947  solutionVars.resize(numNodes);
948  fContributions.resize(numNodes);
949  dV.resize(totalNumWindings);
951 
952  // Now, we have to loop over all those vectors and do "resize" operations
953  // on every element in them --- this sets the number of derivatives
954  // on the "DFad" type
955  for (int node=0 ; node<numNodes ; ++node)
956  {
957  solutionVars[node].resize(numNodes);
958  fContributions[node].resize(numNodes);
959  }
960  for (int winding=0 ; winding<totalNumWindings ; ++winding)
961  {
962  dV[winding].resize(numNodes);
963  windingCurrents[winding].resize(numNodes);
964  }
965  }
966  // initialize all the contributions to zero (automatically zeros derivs)
967  for (int node=0; node<numNodes; ++node)
968  {
969  fContributions[node] = 0.0;
970  }
971 
972  // same for the windings:
973  for (int winding=0; winding<totalNumWindings; ++winding)
974  {
975  windingCurrents[winding] = 0.0;
976  }
977 
978 
979  // Extract solution variables and set as DFad independent variables:
980  // (diff sets the row of the derivatives matrix to the identity row
981  for (int node=0; node<numNodes; ++node)
982  {
983  solutionVars[node] = (*solVectorPtr)[li_Nodes_[node]];
984  solutionVars[node].diff(node,numNodes);
985 #ifdef Xyce_DEBUG_DEVICE
986  Xyce::dout() << "solutionVar[" << node << "] = " << solutionVars[node] << std::endl;
987 #endif
988  }
989 
990  // Now compute winding dV's:
991  for (int winding=0; winding<totalNumWindings; ++winding)
992  {
993  int posNode=windingNodes[winding].first;
994  int negNode=windingNodes[winding].second;
995  dV[winding] = solutionVars[posNode]-solutionVars[negNode];
996  }
997 
998  // Now compute winding currents:
999  // I_i=S_i + sum(j=0,totalNumWindings,K_ij*dV_j)
1000  for (int windingI=0; windingI<totalNumWindings; ++windingI)
1001  {
1002  // we might nave no source vector
1003  if (!theSourceVector_.empty())
1004  windingCurrents[windingI] = theSourceVector_[windingI];
1005 
1006  // we might nave no K matrix
1007  if (!theKMatrix_.empty())
1008  {
1009  for (int windingJ=0; windingJ<totalNumWindings; ++windingJ)
1010  {
1011  windingCurrents[windingI] += theKMatrix_[windingI][windingJ]*dV[windingJ];
1012  }
1013  }
1014  }
1015 
1016  // Now assemble the contributions for the nodes:
1017  for (int winding=0; winding<totalNumWindings; ++winding)
1018  {
1019  int posNode=windingNodes[winding].first;
1020  int negNode=windingNodes[winding].second;
1021 #ifdef Xyce_DEBUG_DEVICE
1022  Xyce::dout() << "Winding " << winding << " adding " << windingCurrents[winding]
1023  << "to contribution to node " << posNode << " and subtracting same "
1024  << " from node " << negNode << std::endl;
1025 #endif
1026  fContributions[posNode] += windingCurrents[winding];
1027  fContributions[negNode] -= windingCurrents[winding];
1028  }
1029 
1030 #ifdef Xyce_DEBUG_DEVICE
1031  Xyce::dout() << " Contributions for device " << getName() << std::endl;
1032  for (int node=0; node<numNodes; ++node)
1033  {
1034  Xyce::dout() << " F[" << node << "] = " << fContributions[node] << std::endl;
1035  }
1036  Xyce::dout() << "Winding potential drops and currents: " << std::endl;
1037  for (int winding=0; winding<totalNumWindings; ++winding)
1038  {
1039  Xyce::dout() << " dV[" << winding << "] = " << dV[winding] << std::endl;
1040  Xyce::dout() << " I[" << winding << "] = " << windingCurrents[winding] << std::endl;
1041  }
1042 #endif
1043 
1044  return bsuccess;
1045 }
1046 
1047 //-----------------------------------------------------------------------------
1048 // Function : Instance::loadDAEQVector
1049 //
1050 // Purpose : Loads the Q-vector contributions for a single
1051 // instance.
1052 //
1053 // Special Notes : The "Q" vector is part of a standard DAE formalism in
1054 // which the system of equations is represented as:
1055 //
1056 // f(x) = dQ(x)/dt + F(x) - B(t) = 0
1057 //
1058 // Scope : public
1059 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
1060 // Creation Date : 8/18/2008
1061 //-----------------------------------------------------------------------------
1063 {
1064  bool bsuccess = true;
1065 
1066  N_LAS_Vector * solVectorPtr = extData.nextSolVectorPtr;
1067  N_LAS_Vector * staVectorPtr = extData.nextStaVectorPtr;
1068 
1069  return bsuccess;
1070 }
1071 
1072 
1073 
1074 //-----------------------------------------------------------------------------
1075 // Function : Instance::loadDAEFVector
1076 //
1077 // Purpose : Loads the F-vector contributions for a single
1078 // instance.
1079 //
1080 // Special Notes :
1081 //
1082 // Scope : public
1083 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
1084 // Creation Date : 8/18/2008
1085 //-----------------------------------------------------------------------------
1087 {
1088  bool bsuccess=true;
1089 
1090  N_LAS_Vector * daeFVecPtr = extData.daeFVectorPtr;
1091  int numNodes = numExtVars+numIntVars;
1092 
1093 #ifdef Xyce_DEBUG_DEVICE
1094 
1095  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1096  {
1097  Xyce::dout() << subsection_divider << std::endl;
1098  Xyce::dout() << "Instance::loadDAEFVector "<<std::endl;
1099  Xyce::dout() << " name = " << getName() << std::endl;
1100  }
1101 #endif
1102 
1103  for (int node=0; node<numNodes; ++node)
1104  {
1105  (*daeFVecPtr)[li_Nodes_[node]] += fContributions[node].val();
1106 #ifdef Xyce_DEBUG_DEVICE
1107  Xyce::dout() << " fVec[" << node << "] += "
1108  << fContributions[node].val() << std::endl;
1109  Xyce::dout() << " loaded into local ID " << li_Nodes_[node] << std::endl;
1110 #endif
1111  }
1112 
1113  return bsuccess;
1114 }
1115 
1116 //-----------------------------------------------------------------------------
1117 // Function : Instance::loadDAEdQdx
1118 //
1119 // Purpose : Loads the Q-vector contributions for a single
1120 // instance.
1121 // Scope : public
1122 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
1123 // Creation Date : 8/18/2008
1124 //-----------------------------------------------------------------------------
1126 {
1127  bool bsuccess = true;
1128 
1129  // N_LAS_Matrix * dQdxMatPtr = extData.dQdxMatrixPtr;
1130 
1131  return bsuccess;
1132 }
1133 
1134 
1135 
1136 //-----------------------------------------------------------------------------
1137 // Function : Instance::loadDAEdFdx ()
1138 //
1139 // Purpose : Loads the F-vector contributions for a single
1140 // instance.
1141 //
1142 // Special Notes :
1143 //
1144 // Scope : public
1145 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
1146 // Creation Date : 8/18/2008
1147 //-----------------------------------------------------------------------------
1149 {
1150  bool bsuccess = true;
1151 
1152  N_LAS_Matrix * dFdxMatPtr = extData.dFdxMatrixPtr;
1153  N_LAS_Vector * staVectorPtr = extData.nextStaVectorPtr;
1154  N_LAS_Vector * solVectorPtr = extData.nextSolVectorPtr;
1155 
1156  int numNodes = numExtVars+numIntVars;
1157 #ifdef Xyce_DEBUG_DEVICE
1158 
1159  if (getDeviceOptions().debugLevel > 0 && getSolverState().debugTimeFlag)
1160  {
1161  Xyce::dout() << subsection_divider << std::endl;
1162  Xyce::dout() << "Instance::loadDAEdFdx "<<std::endl;
1163  Xyce::dout() << " name = " << getName() << std::endl;
1164  }
1165 #endif
1166 
1167 #if 0
1168  // Problem: In orginal Xygra testing, I tried to have multi-winding,
1169  // source-only coils. This lead to the problem where internal nodes were
1170  // completely floating, and lead to singular jacobians. My initial reaction
1171  // was to populate the diagonals of the jacobian for source-only
1172  // coils. This is The Wrong Thing To Do.
1173  // It breaks the completely acceptable case of source-only single-winding
1174  // "coils" such as those needed by Emphasis, leading to nonlinear convergence
1175  // failures on what should be a completely linear problem.
1176  //
1177  // There should be something sane to do for multi-winding source-only
1178  // coils, but these are probably not going to be an important issue for now.
1179  // So punt it.
1180  if (theKMatrix_.empty())
1181  {
1182  for (int equ=0; equ < numNodes; ++equ)
1183  {
1184  (*dFdxMatPtr)[li_Nodes_[equ]][A_Equ_NodeOffsets_[equ][equ]] += 1.0;
1185  }
1186  }
1187  else
1188 #endif
1189  {
1190  // Using Sacado differentiation, set the dFdX terms
1191  for (int equ=0; equ < numNodes; ++equ)
1192  {
1193  for (int node=0; node < numNodes; ++node)
1194  {
1195  (*dFdxMatPtr)[li_Nodes_[equ]][A_Equ_NodeOffsets_[equ][node]]
1196  += fContributions[equ].dx(node);
1197 #ifdef Xyce_DEBUG_DEVICE
1198  Xyce::dout() << " dFdX[" << equ << "]["<<node<<"] += "
1199  << fContributions[equ].dx(node) << std::endl;
1200 #endif
1201  }
1202  }
1203  }
1204 
1205  return bsuccess;
1206 }
1207 
1208 //-----------------------------------------------------------------------------
1209 // Function : Instance::setIC
1210 // Purpose :
1211 // Special Notes :
1212 // Scope : public
1213 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
1214 // Creation Date : 8/18/2008
1215 //-----------------------------------------------------------------------------
1217 {
1218  bool bsuccess = true;
1219 
1220  return bsuccess;
1221 }
1222 
1223 //-----------------------------------------------------------------------------
1224 // Function : Instance::varTypes
1225 // Purpose :
1226 // Special Notes :
1227 // Scope : public
1228 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
1229 // Creation Date : 8/18/2008
1230 //-----------------------------------------------------------------------------
1231 void Instance::varTypes( std::vector<char> & varTypeVec )
1232 {
1233  //varTypeVec.resize(1);
1234  //varTypeVec[0] = 'I';
1235 }
1236 
1237 //-----------------------------------------------------------------------------
1238 // Function : Instance::constructComposite
1239 // Purpose :
1240 // Special Notes :
1241 // Scope : public
1242 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
1243 // Creation Date : 9/11/2008
1244 //-----------------------------------------------------------------------------
1245 CompositeParam *Instance::constructComposite(const std::string & cName, const std::string & pName)
1246 {
1247  if (cName == "COIL")
1248  {
1249  XygraCoilData *xcd = new XygraCoilData ();
1250  coilDataVec.push_back(xcd);
1251  return (static_cast<CompositeParam *> (xcd));
1252  }
1253  else
1254  {
1255  std::string msg =
1256  "Instance::constructComposite: unrecognized composite name: ";
1257  msg += cName;
1258  N_ERH_ErrorMgr::report ( N_ERH_ErrorMgr::DEV_FATAL,msg);
1259  }
1260  // never reached
1261  return NULL;
1262 }
1263 
1264 //-----------------------------------------------------------------------------
1265 // Function : Model::processParams
1266 // Purpose :
1267 // Special Notes :
1268 // Scope : public
1269 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
1270 // Creation Date : 8/18/2008
1271 //-----------------------------------------------------------------------------
1273 {
1274  return true;
1275 }
1276 
1277 //----------------------------------------------------------------------------
1278 // Function : Model::processInstanceParams
1279 // Purpose :
1280 // Special Notes :
1281 // Scope : public
1282 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
1283 // Creation Date : 8/18/2008
1284 //----------------------------------------------------------------------------
1286 {
1287 
1288  std::vector<Instance*>::iterator iter;
1289  std::vector<Instance*>::iterator first = instanceContainer.begin();
1290  std::vector<Instance*>::iterator last = instanceContainer.end();
1291 
1292  for (iter=first; iter!=last; ++iter)
1293  {
1294  (*iter)->processParams();
1295  }
1296 
1297  return true;
1298 }
1299 
1300 //-----------------------------------------------------------------------------
1301 // Function : Model::Model
1302 // Purpose : block constructor
1303 // Special Notes :
1304 // Scope : public
1305 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
1306 // Creation Date : 8/18/2008
1307 //-----------------------------------------------------------------------------
1309  const Configuration & configuration,
1310  const ModelBlock & MB,
1311  const FactoryBlock & factory_block)
1312  : DeviceModel(MB, configuration.getModelParameters(), factory_block)
1313 {
1314 
1315  // // Set up mapping from param names to class variables:
1316  // if (parMap.empty())
1317  // {
1318  // // Set up double precision variables:
1319 
1320  // }
1321 
1322  // Set params to constant default values:
1323  setDefaultParams ();
1324 
1325  // Set params according to .model line and constant defaults from metadata:
1326  setModParams (MB.params);
1327 
1328  // Set any non-constant parameter defaults:
1329  //if (!given("TNOM"))
1330  // tnom = getDeviceOptions().tnom;
1331 
1332  // Calculate any parameters specified as expressions:
1334 
1335  // calculate dependent (ie computed) params and check for errors:
1336 
1337  processParams ();
1338 }
1339 
1340 //-----------------------------------------------------------------------------
1341 // Function : Model::~Model
1342 // Purpose : destructor
1343 // Special Notes :
1344 // Scope : public
1345 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
1346 // Creation Date : 8/18/2008
1347 //-----------------------------------------------------------------------------
1349 {
1350  std::vector<Instance*>::iterator iter;
1351  std::vector<Instance*>::iterator first = instanceContainer.begin();
1352  std::vector<Instance*>::iterator last = instanceContainer.end();
1353 
1354  for (iter=first; iter!=last; ++iter)
1355  {
1356  delete (*iter);
1357  }
1358 
1359 }
1360 
1361 // additional Declarations
1362 
1363 //-----------------------------------------------------------------------------
1364 // Function : Model::printOutInstances
1365 // Purpose : debugging tool.
1366 // Special Notes :
1367 // Scope : public
1368 // Creator : Tom Russo, SNL, Electrical and Microsystems Modeling
1369 // Creation Date : 8/18/2008
1370 //-----------------------------------------------------------------------------
1371 std::ostream &Model::printOutInstances(std::ostream &os) const
1372 {
1373  std::vector<Instance*>::const_iterator iter;
1374  std::vector<Instance*>::const_iterator first = instanceContainer.begin();
1375  std::vector<Instance*>::const_iterator last = instanceContainer.end();
1376 
1377  int i, isize;
1378  isize = instanceContainer.size();
1379 
1380  os << std::endl;
1381  os << "Number of Xygra instances: " << isize << std::endl;
1382  os << " name=\t\tmodelName\tParameters" << std::endl;
1383  for (i=0, iter=first; iter!=last; ++iter, ++i)
1384  {
1385  os << " " << i << ": " << (*iter)->getName() << "\t";
1386  os << getName();
1387  os << std::endl;
1388  }
1389 
1390  os << std::endl;
1391 
1392  return os;
1393 }
1394 
1395 //-----------------------------------------------------------------------------
1396 // Function : Model::forEachInstance
1397 // Purpose :
1398 // Special Notes :
1399 // Scope : public
1400 // Creator : David Baur
1401 // Creation Date : 2/4/2014
1402 //-----------------------------------------------------------------------------
1403 /// Apply a device instance "op" to all instances associated with this
1404 /// model
1405 ///
1406 /// @param[in] op Operator to apply to all instances.
1407 ///
1408 ///
1409 void Model::forEachInstance(DeviceInstanceOp &op) const /* override */
1410 {
1411  for (std::vector<Instance *>::const_iterator it = instanceContainer.begin(); it != instanceContainer.end(); ++it)
1412  op(*it);
1413 }
1414 
1415 
1416 Device *Traits::factory(const Configuration &configuration, const FactoryBlock &factory_block)
1417 {
1418 
1419  return new DeviceMaster<Traits>(configuration, factory_block, factory_block.solverState_, factory_block.deviceOptions_);
1420 }
1421 
1423 {
1425  .registerDevice("xygra", 1)
1426  .registerModelType("xygra", 1);
1427 }
1428 
1429 } // namespace Xygra
1430 } // namespace Device
1431 } // namespace Xyce