Xyce  6.1
N_DEV_PowerGridGenBus.C
Go to the documentation of this file.
1 //-----------------------------------------------------------------------------
2 // Copyright Notice
3 //
4 // Copyright 2002 Sandia Corporation. Under the terms
5 // of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S.
6 // Government retains certain rights in this software.
7 //
8 // Xyce(TM) Parallel Electrical Simulator
9 // Copyright (C) 2002-2015 Sandia Corporation
10 //
11 // This program is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 //-----------------------------------------------------------------------------
24 
25 //----------------------------------------------------------------------------
26 // Filename : $RCSfile: N_DEV_PowerGridGenBus.C,v $
27 //
28 // Purpose : PowerGrid classes: provides a device that calculates the
29 // steady state power flow from an Ideal Generator Bus.
30 // This device has a fixed output power (P) and a fixed
31 // voltage magnitude (VM).
32 //
33 // Special Notes : Experimental new device for an LDRD.
34 //
35 // Creator : Pete Sholander, SNL, Electrical and Microsystems Modeling
36 //
37 // Creation Date : 12/9/14
38 //
39 // Revision Information:
40 // ---------------------
41 //
42 // Revision Number: $Revision: 1.1.2.4 $
43 //
44 // Revision Date : $Date: 2015/04/03 16:42:57 $
45 //
46 // Current Owner : $Author: peshola $
47 //----------------------------------------------------------------------------
48 
49 #include <Xyce_config.h>
50 
51 
52 // ---------- Standard Includes ----------
53 
54 // ---------- Xyce Includes ----------
55 #include <N_DEV_PowerGridGenBus.h>
56 #include <N_DEV_Const.h>
57 #include <N_DEV_DeviceOptions.h>
58 #include <N_DEV_DeviceMaster.h>
59 #include <N_DEV_ExternData.h>
60 #include <N_DEV_MatrixLoadData.h>
61 #include <N_DEV_Message.h>
62 #include <N_DEV_SolverState.h>
63 
64 #include <N_LAS_Matrix.h>
65 #include <N_LAS_Vector.h>
66 
67 #include <N_UTL_ExtendedString.h>
68 #include <N_UTL_FeatureTest.h>
69 #include <N_UTL_IndentStreamBuf.h>
70 
71 namespace Xyce {
72 namespace Device {
73 namespace PowerGridGenBus {
74 
75 // enables debug output for just this device
76 //static const int DEBUG_DEVICE = 1;
77 
79 {
80  p.addPar("AT", std::string("PQP"), &PowerGridGenBus::Instance::analysisTypeStr_)
81  .setUnit(U_NONE)
82  .setDescription("Analysis Type");
83 
85  .setExpressionAccess(ParameterType::TIME_DEP)
86  .setUnit(U_PERUNIT)
87  .setDescription("Generator Output Power");
88 
90  .setExpressionAccess(ParameterType::TIME_DEP)
91  .setUnit(U_PERUNIT)
92  .setDescription("Voltage Magnitude");
93 
95  .setExpressionAccess(ParameterType::TIME_DEP)
96  .setUnit(U_PERUNIT)
97  .setDescription("Reactive Power Max Limit");
98 
100  .setExpressionAccess(ParameterType::TIME_DEP)
101  .setUnit(U_PERUNIT)
102  .setDescription("Reactive Power Min Limit");
103 }
104 
106 {}
107 
108 
109 
110 std::vector< std::vector<int> > Instance::jacStamp;
111 
112 // Class Instance
113 //-----------------------------------------------------------------------------
114 // Function : Instance::Instance
115 // Purpose : "instance block" constructor
116 // Special Notes :
117 // Scope : public
118 // Creator : Pete Sholander, SNL, Electrical and Microsystems Modeling
119 // Creation Date : 12/9/14
120 //-----------------------------------------------------------------------------
122  const Configuration & configuration,
123  const InstanceBlock & IB,
124  Model & Miter,
125  const FactoryBlock & factory_block)
126  : DeviceInstance(IB, configuration.getInstanceParameters(), factory_block),
127  model_(Miter),
128  analysisTypeStr_("PQP"),
129  analysisType_(PQP),
130  power_(1.0),
131  VMag_(1.0),
132  QMax_(1.0),
133  QMin_(0.0)
134 {
135  if (DEBUG_DEVICE && isActive(Diag::DEVICE_PARAMETERS))
136  {
137  Xyce::dout() << std::endl << "In device constructor for " << getName() << std::endl;
138  }
139 
140  std::vector<Param>::const_iterator iter = IB.params.begin();
141  std::vector<Param>::const_iterator last = IB.params.end();
142 
143  for ( ; iter != last; ++iter)
144  {
145  const std::string & tmpname = iter->tag();
146  const bool & tmpgiven = iter->given();
147 
148  // copy instance parameter values into private variables
149  if (tmpname == "AT" && tmpgiven == true)
150  {
151  ExtendedString atStr(iter->stringValue());
152  atStr.toUpper();
153  if ( atStr == "IV" )
154  {
155  // temporary error message since only PQP format works in Xyce 6.3
156  UserError0(*this) << "Only PQP Analysis Type is supported for PowerGridGenBus device: " << getName();
157  analysisType_ = IV;
158  analysisTypeStr_ = "IV";
159  }
160  else if ( atStr == "PQR" )
161  {
162  // temporary error message since only PQP format works in Xyce 6.3
163  UserError0(*this) << "Only PQP Analysis Type is supported for PowerGridGenBus device: " << getName();
164  analysisType_ = PQR;
165  analysisTypeStr_ = "PQR";
166  }
167  else if ( atStr == "PQP" )
168  {
169  analysisType_ = PQP;
170  analysisTypeStr_ = "PQP";
171  }
172  else
173  {
174  UserError0(*this) << "Analysis Type must be IV, PQR or PQP in power grid device: " << getName();
175  }
176  }
177  if (tmpname == "VM" && tmpgiven == true)
178  {
179  VMag_ = iter->getImmutableValue<double>();
180  }
181  if (tmpname == "P" && tmpgiven == true)
182  {
183  power_ = iter->getImmutableValue<double>();
184  }
185  if (tmpname == "QMAX" && tmpgiven == true)
186  {
187  QMax_ = iter->getImmutableValue<double>();
188  }
189  if (tmpname == "QMIN" && tmpgiven == true)
190  {
191  QMin_ = iter->getImmutableValue<double>();
192  }
193  }
194 
195  if ( analysisType_ == DVS || analysisType_ == IV || analysisType_ == PQR)
196  {
197  numIntVars = 2;
198  numExtVars = 4;
199  numStateVars = 0;
200  }
201  else if (analysisType_ == PQP )
202  {
203  numIntVars = 1;
204  numExtVars = 4;
205  numStateVars = 0;
206  }
207  else
208  {
209  UserError0(*this) << "Analysis Type must be IV, PQR or PQP in power grid device: " << getName();
210  return;
211  }
212 
213  if( jacStamp.empty() )
214  {
215  if ( analysisType_ == DVS || analysisType_ == IV )
216  {
217  jacStamp.resize(6);
218  jacStamp[0].resize(1);
219  jacStamp[1].resize(1);
220  jacStamp[2].resize(1);
221  jacStamp[3].resize(1);
222  jacStamp[4].resize(2);
223  jacStamp[5].resize(2);
224 
225  jacStamp[0][0] = 4;
226  jacStamp[1][0] = 4;
227  jacStamp[2][0] = 5;
228  jacStamp[3][0] = 5;
229  jacStamp[4][0] = 0;
230  jacStamp[4][1] = 1;
231  jacStamp[5][0] = 2;
232  jacStamp[5][1] = 3;
233  }
234  else if (analysisType_ == PQR)
235  {
236  jacStamp.resize(6);
237  jacStamp[0].resize(1);
238  jacStamp[1].resize(1);
239  jacStamp[2].resize(3);
240  jacStamp[3].resize(3);
241  jacStamp[4].resize(2);
242  jacStamp[5].resize(2);
243 
244  jacStamp[0][0] = 4;
245  jacStamp[1][0] = 4;
246  jacStamp[2][0] = 0;
247  jacStamp[2][1] = 1;
248  jacStamp[2][2] = 5;
249  jacStamp[3][0] = 0;
250  jacStamp[3][1] = 1;
251  jacStamp[3][2] = 5;
252  jacStamp[4][0] = 0;
253  jacStamp[4][1] = 1;
254  jacStamp[5][0] = 2;
255  jacStamp[5][1] = 3;
256  }
257  else if (analysisType_ == PQP )
258  {
259  jacStamp.resize(5);
260  jacStamp[2].resize(1);
261  jacStamp[3].resize(1);
262  jacStamp[4].resize(2);
263 
264  jacStamp[2][0] = 4;
265  jacStamp[3][0] = 4;
266  jacStamp[4][0] = 2;
267  jacStamp[4][1] = 3;
268  }
269  }
270 
271  // Set params to constant default values:
272  setDefaultParams ();
273 
274  // Set params according to instance line and constant defaults from metadata:
275  setParams (IB.params);
276 
277  // Set any non-constant parameter defaults:
278 
279 }
280 
281 //-----------------------------------------------------------------------------
282 // Function : Instance::~Instance
283 // Purpose : destructor
284 // Special Notes :
285 // Scope : public
286 // Creator : Pete Sholander, SNL, Electrical and Microsystems Modeling
287 // Creation Date : 12/9/14
288 //-----------------------------------------------------------------------------
290 {
291 }
292 
293 //-----------------------------------------------------------------------------
294 // Function : Instance::registerLIDs
295 // Purpose :
296 // Special Notes :
297 // Scope : public
298 // Creator : Pete Sholander, SNL, Electrical and Microsystems Modeling
299 // Creation Date : 9/12/14
300 //-----------------------------------------------------------------------------
301 void Instance::registerLIDs( const std::vector<int> & intLIDVecRef,
302  const std::vector<int> & extLIDVecRef )
303 {
304  AssertLIDs(intLIDVecRef.size() == numIntVars);
305  AssertLIDs(extLIDVecRef.size() == numExtVars);
306 
307  // copy over the global ID lists.
308  intLIDVec = intLIDVecRef;
309  extLIDVec = extLIDVecRef;
310 
311  // in general, there will be 6 I/0 nodes for each Gen Bus
312  // these if-else blocks will be made into separate classes,
313  // once they work
314  if ( analysisType_ == DVS || analysisType_ == IV)
315  {
316  li_VR1 = extLIDVec[0];
317  li_VR2 = extLIDVec[1];
318  li_VI1 = extLIDVec[2];
319  li_VI2 = extLIDVec[3];
320  li_BranCurrR = intLIDVec[0];
321  li_BranCurrI = intLIDVec[1];
322 
323  if (DEBUG_DEVICE && isActive(Diag::DEVICE_PARAMETERS))
324  {
325  Xyce::dout() << getName() << " LIDs"
326  << Util::push << std::endl
327  << "li_VR1 = " << li_VR1 << std::endl
328  << "li_VI1 = " << li_VI1 << std::endl
329  << "li_VR2 = " << li_VR2 << std::endl
330  << "li_VI2 = " << li_VI2 << std::endl
331  << "li_BranCurrR = " << li_BranCurrR << std::endl
332  << "li_BranCurrI = " << li_BranCurrI << std::endl
333  << std::endl;
334  }
335  }
336  else if ( analysisType_ == PQR)
337  {
338  li_VR1 = extLIDVec[0];
339  li_VR2 = extLIDVec[1];
340  li_VI1 = extLIDVec[2];
341  li_VI2 = extLIDVec[3];
342  li_BranCurrP = intLIDVec[0];
343  li_BranCurrQ = intLIDVec[1];
344 
345  if (DEBUG_DEVICE && isActive(Diag::DEVICE_PARAMETERS))
346  {
347  Xyce::dout() << getName() << " LIDs"
348  << Util::push << std::endl
349  << "li_VR1 = " << li_VR1 << std::endl
350  << "li_VI1 = " << li_VI1 << std::endl
351  << "li_VR2 = " << li_VR2 << std::endl
352  << "li_VI2 = " << li_VI2 << std::endl
353  << "li_BranCurrP = " << li_BranCurrP << std::endl
354  << "li_BranCurrQ = " << li_BranCurrQ << std::endl
355  << std::endl;
356  }
357  }
358  else if (analysisType_ == PQP )
359  {
360  li_Theta1 = extLIDVec[0];
361  li_Theta2 = extLIDVec[1];
362  li_VM1 = extLIDVec[2];
363  li_VM2 = extLIDVec[3];
364  li_BranCurrQ = intLIDVec[0];
365 
366  if (DEBUG_DEVICE && isActive(Diag::DEVICE_PARAMETERS))
367  {
368  Xyce::dout() << getName() << " LIDs"
369  << Util::push << std::endl
370  << "li_Theta1 = " << li_Theta1 << std::endl
371  << "li_VM1 = " << li_VM1 << std::endl
372  << "li_Theta2 = " << li_Theta2 << std::endl
373  << "li_VM2 = " << li_VM2 << std::endl
374  << "li_BranCurrQ = " << li_BranCurrQ << std::endl
375  << std::endl;
376  }
377  }
378  else
379  {
380  UserError0(*this) << "Analysis Type must be IV, PQR or PQP in power grid device: " << getName();
381  return;
382  }
383 }
384 
385 //-----------------------------------------------------------------------------
386 // Function : Instance::loadNodeSymbols
387 // Purpose :
388 // Special Notes :
389 // Scope : public
390 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
391 // Creation Date : 05/13/05
392 //-----------------------------------------------------------------------------
393 void Instance::loadNodeSymbols(Util::SymbolTable &symbol_table) const
394 {
395  if ( analysisType_ == DVS || analysisType_ == IV)
396  {
397  addInternalNode(symbol_table, li_BranCurrR, getName(), "BranchCurrR");
398  addInternalNode(symbol_table, li_BranCurrI, getName(), "BranchCurrI");
399  }
400  else if ( analysisType_ == PQR )
401  {
402  addInternalNode(symbol_table, li_BranCurrP, getName(), "BranchCurrP");
403  addInternalNode(symbol_table, li_BranCurrQ, getName(), "BranchCurrQ");
404  }
405  else if ( analysisType_ == PQP )
406  {
407  addInternalNode(symbol_table, li_BranCurrQ, getName(), "BranchCurrQ");
408  }
409  else
410  {
411  UserError0(*this) << "Analysis Type must be IV, PQR or PQP in power grid device: " << getName();
412  return;
413  }
414 }
415 
416 //-----------------------------------------------------------------------------
417 // Function : Instance::registerStateLIDs
418 // Purpose : Set up offsets so we can store quantities that need to be
419 // differentiated.
420 // Special Notes :
421 // Scope : public
422 // Creator : Pete Sholander, SNL, Electrical and Microsystems Modeling
423 // Creation Date : 9/12/14
424 //-----------------------------------------------------------------------------
425 void Instance::registerStateLIDs(const std::vector<int> & staLIDVecRef )
426 {
427  AssertLIDs(staLIDVecRef.size() == numStateVars);
428 }
429 
430 //-----------------------------------------------------------------------------
431 // Function : Instance::jacobianStamp
432 // Purpose :
433 // Special Notes :
434 // Scope : public
435 // Creator : Robert Hoekstra, SNL, Parallel Computational Sciences
436 // Creation Date : 08/20/01
437 //-----------------------------------------------------------------------------
438 const std::vector< std::vector<int> > & Instance::jacobianStamp() const
439 {
440  return jacStamp;
441 }
442 
443 //-----------------------------------------------------------------------------
444 // Function : Instance::registerJacLIDs
445 // Purpose :
446 // Special Notes :
447 // Scope : public
448 // Creator : Robert Hoekstra, SNL, Parallel Computational Sciences
449 // Creation Date : 08/27/01
450 //-----------------------------------------------------------------------------
451 void Instance::registerJacLIDs( const std::vector< std::vector<int> > & jacLIDVec )
452 {
453  DeviceInstance::registerJacLIDs( jacLIDVec );
454 
455  // These will be split out into separate classes once they both work
456  if (analysisType_ == DVS || analysisType_ == IV )
457  {
458  VR1_IR_Offset = jacLIDVec[0][0];
459  VR2_IR_Offset = jacLIDVec[1][0];
460  VI1_II_Offset = jacLIDVec[2][0];
461  VI2_II_Offset = jacLIDVec[3][0];
462 
463  IR_VR1_Offset = jacLIDVec[4][0];
464  IR_VR2_Offset = jacLIDVec[4][1];
465  II_VI1_Offset = jacLIDVec[5][0];
466  II_VI2_Offset = jacLIDVec[5][1];
467 
468  if (DEBUG_DEVICE && isActive(Diag::DEVICE_PARAMETERS))
469  {
470  Xyce::dout() << getName() << ": In registerJacLIDs, the offsets are:" << std::endl
471  << "VR offsets are (" << VR1_IR_Offset << " , " << VR2_IR_Offset << ")" << std::endl
472  << "VI offsets are (" << VI1_II_Offset << " , " << VI1_II_Offset << ")" << std::endl
473  << "IR offsets are (" << IR_VR1_Offset << " , " << IR_VR2_Offset << ")" << std::endl
474  << "II offsets are (" << II_VI1_Offset << " , " << II_VI2_Offset << ")" << std::endl
475  << std::endl;
476  }
477  }
478  else if (analysisType_ == PQR)
479  {
480  VR1_P_Offset = jacLIDVec[0][0];
481  VR2_P_Offset = jacLIDVec[1][0];
482  VI1_VR1_Offset = jacLIDVec[2][0];
483  VI1_VR2_Offset = jacLIDVec[2][1];
484  VI1_Q_Offset = jacLIDVec[2][2];
485  VI2_VR1_Offset = jacLIDVec[3][0];
486  VI2_VR2_Offset = jacLIDVec[3][1];
487  VI2_Q_Offset = jacLIDVec[3][2];
488 
489  P_VR1_Offset = jacLIDVec[4][0];
490  P_VR2_Offset = jacLIDVec[4][1];
491  Q_VI1_Offset = jacLIDVec[5][0];
492  Q_VI2_Offset = jacLIDVec[5][1];
493 
494  if (DEBUG_DEVICE && isActive(Diag::DEVICE_PARAMETERS))
495  {
496  Xyce::dout() << getName() << ": In registerJacLIDs, the offsets are:" << std::endl
497  << "VR offsets are (" << VR1_P_Offset << " , " << VR2_P_Offset << ")" << std::endl
498  << "VI offsets are (" << VI1_Q_Offset << " , " << VI1_Q_Offset << ")" << std::endl
499  << "IR offsets are (" << P_VR1_Offset << " , " << P_VR2_Offset << ")" << std::endl
500  << "II offsets are (" << Q_VI1_Offset << " , " << Q_VI2_Offset << ")" << std::endl
501  << std::endl;
502  }
503  }
504  else if (analysisType_ == PQP)
505  {
506  VM1_Q_Offset = jacLIDVec[2][0];
507  VM2_Q_Offset = jacLIDVec[3][0];
508 
509  Q_VM1_Offset = jacLIDVec[4][0];
510  Q_VM2_Offset = jacLIDVec[4][1];
511 
512  if (DEBUG_DEVICE && isActive(Diag::DEVICE_PARAMETERS))
513  {
514  Xyce::dout() << getName() << ": In registerJacLIDs, the offsets are:" << std::endl
515  << "VM offsets are (" << VM1_Q_Offset << " , " << VM2_Q_Offset << ")" << std::endl
516  << "Q offsets are (" << Q_VM1_Offset << " , " << Q_VM2_Offset << ")" << std::endl
517  << std::endl;
518  }
519  }
520 }
521 
522 //-----------------------------------------------------------------------------
523 // Function : Instance::updateIntermediateVars
524 // Purpose : update intermediate variables for one diode instance
525 // Special Notes :
526 // Scope : public
527 // Creator : Pete Sholander, SNL, Electrical and Microsystems Modeling
528 // Creation Date : 9/12/14
529 //-----------------------------------------------------------------------------
531 {
532  double * solVec = extData.nextSolVectorRawPtr;
533 
534  // These will be split out into separate classes once they work
535  if (analysisType_ == DVS || analysisType_ == IV)
536  {
537  VR1 = solVec[li_VR1];
538  VR2 = solVec[li_VR2];
539  VI1 = solVec[li_VI1];
540  VI2 = solVec[li_VI2];
541 
542  Bran_CurrR = solVec[li_BranCurrR];
543  Bran_CurrI = solVec[li_BranCurrI];
544 
545  srcDropR = VR1 - VR2;
546  srcBCR = VMag_;
548 
549  srcDropI = VI1 - VI2;
550  srcBCI = VMag_;
552  }
553  else if (analysisType_ == PQR)
554  {
555  // implement P as a current source, which means it has no state
556 
557  // VI will be implmented as voltage source that depends on VR
558  VR1 = solVec[li_VR1];
559  VR2 = solVec[li_VR2];
560  VI1 = solVec[li_VI1];
561  VI2 = solVec[li_VI2];
562 
563  Bran_CurrP = solVec[li_BranCurrR];
564  Bran_CurrQ = solVec[li_BranCurrI];
565 
566  srcDropR = VR1 - VR2;
567  srcDropI = VI1 - VI2;
568 
569  // enforce Voltage Magnitude constraint
570  if (srcDropR > VMag_)
571  {
572  srcBCR = VMag_;
573  srcBCI = 0;
574  }
575  else if (-srcDropR > VMag_)
576  {
577  srcBCR = -VMag_;
578  srcBCI = 0;
579  }
580  else
581  {
582  srcBCR = srcDropR;
583  double signVal = (srcBCI < 0) ? -1.0 : 1.0;
584  srcBCI = signVal * sqrt( VMag_*VMag_ - srcDropR * srcDropR);
585  }
586 
589 
590  /*if (Bran_CurrQ > QMax_)
591  {
592  reactPower_ = QMax_;
593  }
594  else if (Bran_CurrQ < QMin_)
595  {
596  reactPower_ = QMin_;
597  }
598  else
599  {
600  reactPower_ = Bran_CurrQ;
601  }*/
602 
603  /*Q1 = Bran_CurrQ;
604  if (Q1 < -QMax_)
605  {
606  Q1 = -QMax_;
607  }
608  else if (Q1 > -QMin_)
609  {
610  Q1 = -QMin_;
611  }
612  else
613  { // Q is within limits. So, enforce the voltage magnitude constraint
614 
615  }
616 
617  Q2 = -Q1;*/
618  }
619  else if (analysisType_ == PQP)
620  {
621  // implement P as a current source, which means it has no state
622 
623  // implement VM as a voltage source
624  VM1 = solVec[li_VM1];
625  VM2 = solVec[li_VM2];
626  Bran_CurrQ = solVec[li_BranCurrQ];
627 
628  srcDropVM = VM1 - VM2;
629  srcBCVM = VMag_;
631 
632  /*if (Bran_CurrQ < -QMax_)
633  {
634  Q1 = -QMax_;
635  }
636  else if (Bran_CurrQ > -QMin_)
637  {
638  Q1 = -QMin_;
639  }
640  else
641  { // Q is within limits. So, enforce the voltage magnitude constraint
642  Q1 = Bran_CurrQ;
643  srcBCMag = VMag_;
644  srcBCTh = 0;
645  }*/
646  }
647  else
648  {
649  UserError0(*this) << "Analysis Type must be IV, PQR or PQP in power grid device: " << getName();
650  return false;
651  }
652 
653  return true;
654 }
655 
656 //-----------------------------------------------------------------------------
657 // Function : Instance::updatePrimaryState
658 // Purpose :
659 // Special Notes :
660 // Scope : public
661 // Creator : Pete Sholander, SNL, Electrical and Microsystems Modeling
662 // Creation Date : 9/12/14
663 //-----------------------------------------------------------------------------
665 {
666  return updateIntermediateVars ();
667 }
668 
669 //-----------------------------------------------------------------------------
670 // Function : Instance::loadDAEFVector
671 //
672 // Purpose : Loads the F-vector contributions
673 //
674 // Special Notes :
675 //
676 //
677 // Scope : public
678 // Creator : Pete Sholander, SNL, Electrical and Microsystems Modeling
679 // Creation Date : 9/12/14
680 //-----------------------------------------------------------------------------
682 {
683  double * fVec = extData.daeFVectorRawPtr;
684 
685  // this needs to be vectorized in the general case.
686  // These will be split out into separate classes once they work
687  if (analysisType_ == DVS || analysisType_ == IV)
688  {
689  fVec[li_VR1] += Bran_CurrR;
690  fVec[li_VR2] -= Bran_CurrR;
691  fVec[li_VI1] += Bran_CurrI;
692  fVec[li_VI2] -= Bran_CurrI;
693 
694  fVec[li_BranCurrR] += srcDropR;
695  fVec[li_BranCurrI] += srcDropI;
696  }
697  else if (analysisType_ == PQR)
698  {
699  fVec[li_VR1] -= power_;
700  fVec[li_VR2] += power_;
701  fVec[li_VI1] -= Bran_CurrQ;
702  fVec[li_VI2] += Bran_CurrQ;
703 
704  fVec[li_BranCurrP] -= srcDropR;
705  fVec[li_BranCurrP] += srcBCR;
706 
707  fVec[li_BranCurrQ] -= srcDropI;
708  fVec[li_BranCurrQ] += srcBCI;
709 
710  Xyce::dout() << getName() << ": F Vector Load info is (P,Q): (" << power_ << ","
711  << Bran_CurrQ << ")" << std::endl
712  << " (srcDropR,srcBCR,srcDropI,srcBCI: ("
713  << srcDropR << "," << srcBCR << "," <<srcDropI << ","
714  << srcBCI << ")"<< std::endl << std::endl
715  << " Solution Vector Currents are (P,Q): (" << Bran_CurrP << ","
716  << Bran_CurrQ << ")" << std::endl
717  << " (VR1,VR2,VI1,VI2): ("
718  << VR1 << "," << VR2 << "," << VI1 << ","
719  << VI2 << ")"<< std::endl << std::endl;
720  //fVec[li_VR1] += P1;
721  //fVec[li_VR2] += P2;
722  //fVec[li_VI1] += Q1;
723  //fVec[li_VI2] += Q2;
724  }
725  else if (analysisType_ == PQP)
726  {
727  // initial version does not have reactive power limiting.
728 
729  // implement P as a current source, which means it has no F-Vector contribution.
730  // Note though that the P is a generator. So, the current polarity is reversed from
731  // a normal Xyce current source, as explained below. The polarity difference is
732  // accounted for in the loadDAEBVector() function.
733 
734 
735  // implement VM as a voltage source. This device is a generator. So, the current polarities
736  // on the branch current contributions, fVec[li_VM1] and fVec[li_VM2], are reversed from a
737  // normal Xyce voltage source. Also note that the dFdx[li_VM1][VM1_Q_Offset] and
738  // dFdx[li_VM2][VM2_Q_Offset] terms in the dFdX equations in loadDAEdFdx () are also reversed
739  // from a normal Xyce voltage source. For a Xyce voltage source, "positive current" flows
740  // from the positive node through the source to the negative node. For a Power Grid generator
741  // (like this device), the convention is that positive reactive power (Q) flows into the power
742  // grid from the positive VM terminal. A similar convention holds for the real power (P) flow
743  // from the positive Theta terminal.
744  fVec[li_VM1] -= Bran_CurrQ;
745  fVec[li_VM2] += Bran_CurrQ;
746  if (DEBUG_DEVICE && isActive(Diag::DEVICE_LOAD_VECTOR))
747  {
748  Xyce::dout() << "Bran_CurrQ = " << Bran_CurrQ << std::endl;
749  }
750 
751  fVec[li_BranCurrQ] += srcDropVM;
752 
753  if (DEBUG_DEVICE && isActive(Diag::DEVICE_PARAMETERS))
754  {
755  Xyce::dout() << getName() << ": F Vector Load info is (P,Q,srcDropVM,srcBCVM): (" << power_ << " , "
756  << Bran_CurrQ << " , " << srcDropVM << " , " << srcBCVM << ")" << std::endl << std::endl;
757  }
758  }
759  else
760  {
761  UserError0(*this) << "Analysis Type must be IV, PQR or PQP in power grid device: " << getName();
762  return false;
763  }
764 
765  if (getSolverState().dcopFlag)
766  {
767  // no op placeholder
768  }
769  else
770  {
771  // no op placeholder
772  }
773 
774  return true;
775 }
776 
777 //-----------------------------------------------------------------------------
778 // Function : Instance::loadDAEBVector
779 //
780 // Purpose : Loads the B-vector contributions for a single
781 // vsrc instance.
782 //
783 // Special Notes :
784 //
785 // Scope : public
786 // Creator : Eric Keiter, SNL
787 // Creation Date :
788 //-----------------------------------------------------------------------------
790 {
791  double * bVec = extData.daeBVectorRawPtr;
792 
793  if (analysisType_ == DVS || analysisType_ == IV )
794  {
795  bVec[li_BranCurrR] += srcBCR;
796  bVec[li_BranCurrI] += srcBCI;
797  }
798  else if (analysisType_ == PQR)
799  {
800  bVec[li_BranCurrR] -= srcBCR;
801  bVec[li_BranCurrI] -= srcBCI;
802  }
803  else if (analysisType_ == PQP)
804  {
805  // current source for P. Note the P is a generator. So, the current polarity for
806  // the bVec[li_Theta1] and bVec[li_Theta2] contributions are reversed from a normal Xyce
807  // current source. The comments for the loadDAEFVector() function provide more explanation.
808  bVec[li_Theta1] += power_;
809  bVec[li_Theta2] -= power_;
810 
811  // voltage source for VM
812  bVec[li_BranCurrQ] += srcBCVM;
813  }
814  else
815  {
816  UserError0(*this) << "Analysis Type must be IV, PQR or PQP in power grid device: " << getName();
817  return false;
818  }
819 
820  return true;
821 }
822 
823 //-----------------------------------------------------------------------------
824 // Function : Instance::loadDAEdFdx ()
825 //
826 // Purpose : Loads the F-vector contributions
827 //
828 // Special Notes :
829 //
830 // Scope : public
831 // Creator : Pete Sholander, SNL, Electrical and Microsystems Modeling
832 // Creation Date : 9/12/14
833 //-----------------------------------------------------------------------------
835 {
836  N_LAS_Matrix & dFdx = *(extData.dFdxMatrixPtr);
837 
838  if (analysisType_ == DVS || analysisType_ == IV)
839  {
840  dFdx[li_VR1][VR1_IR_Offset] += 1.0;
841  dFdx[li_VR2][VR2_IR_Offset] -= 1.0;
842  dFdx[li_VI1][VI1_II_Offset] += 1.0;
843  dFdx[li_VI2][VI2_II_Offset] -= 1.0;
844 
845  dFdx[li_BranCurrR][IR_VR1_Offset] += 1.0;
846  dFdx[li_BranCurrR][IR_VR2_Offset] -= 1.0;
847  dFdx[li_BranCurrI][II_VI1_Offset] += 1.0;
848  dFdx[li_BranCurrI][II_VI2_Offset] -= 1.0;
849  }
850  else if (analysisType_ == PQR)
851  {
852  dFdx[li_VR1][VR1_P_Offset] += 1.0;
853  dFdx[li_VR2][VR2_P_Offset] -= 1.0;
854  dFdx[li_VI1][VI1_Q_Offset] += 1.0;
855  dFdx[li_VI2][VI2_Q_Offset] -= 1.0;
856 
857  // jacobiam term that come from the VMag constraint
858  double jacTerm = (VMag_ - abs(VR1-VR2)) > 0
859  ? (VR2 - VR1) / sqrt(VMag_*VMag_ - (VR1-VR2)*(VR1-VR2)) : 1e10*(VR2 - VR1);
860  Xyce::dout() << "Jacobian term = " << jacTerm << std::endl;
861  dFdx[li_VI1][VI1_VR1_Offset] += jacTerm;
862  dFdx[li_VI1][VI1_VR2_Offset] -= jacTerm;
863  dFdx[li_VI2][VI2_VR1_Offset] -= jacTerm;
864  dFdx[li_VI2][VI2_VR2_Offset] += jacTerm;
865 
866  //Jacobian terms from the voltage drop constraints
867  dFdx[li_BranCurrP][P_VR1_Offset] -= 1.0;
868  dFdx[li_BranCurrP][P_VR2_Offset] += 1.0;
869  dFdx[li_BranCurrQ][Q_VI1_Offset] -= 1.0;
870  dFdx[li_BranCurrQ][Q_VI2_Offset] += 1.0;
871  }
872  else if (analysisType_ == PQP)
873  {
874  // P is implemented as a current source. So, there are no dFdX terms for it
875 
876  // VM is implemented as a voltage source. This device is a generator. So, the signs of the
877  // dFdx[li_VM1][VM1_Q_Offset] and dFdx[li_VM2][VM2_Q_Offset] terms are reversed from that of
878  // a normal Xyce voltage source. Also note that the signs of the branch current contributions,
879  // fVec[li_VM1] and fVec[li_VM2], in loadDAEFVector () are also reversed from a normal Xyce
880  // voltage source.
881  dFdx[li_VM1][VM1_Q_Offset] -= 1.0;
882  dFdx[li_VM2][VM2_Q_Offset] += 1.0;
883 
884  dFdx[li_BranCurrQ][Q_VM1_Offset] += 1.0;
885  dFdx[li_BranCurrQ][Q_VM2_Offset] -= 1.0;
886  }
887  else
888  {
889  UserError0(*this) << "Analysis Type must be IV, PQR or PQP in power grid device: " << getName();
890  return false;
891  }
892 
893  if (getSolverState().dcopFlag)
894  {
895  // no op placeholder code;
896  }
897  else
898  {
899  // no op placeholder code;
900  }
901 
902  return true;
903 }
904 
905 //-----------------------------------------------------------------------------
906 // Function : Model::Model
907 // Purpose : "Model block" constructor
908 // Special Notes :
909 // Scope : public
910 // Creator : Pete Sholander, SNL, Electrical and Microsystems Modeling
911 // Creation Date : 9/12/14
912 //-----------------------------------------------------------------------------
913 
914 
915 // These are all just placeholder functions, as there is nothing to the model
916 // class.
917 // Class Model
918 
919 //-----------------------------------------------------------------------------
920 // Function : Model::Model
921 // Purpose : "Model block" constructor
922 // Special Notes :
923 // Scope : public
924 // Creator : Pete Sholander, SNL, Electrical and Microsystems Modeling
925 // Creation Date : 9/12/14
926 //-----------------------------------------------------------------------------
928  const Configuration & configuration,
929  const ModelBlock & MB,
930  const FactoryBlock & factory_block)
931  : DeviceModel(MB, configuration.getModelParameters(), factory_block)
932 {
933 }
934 
935 //-----------------------------------------------------------------------------
936 // Function : Model::~Model
937 // Purpose : destructor
938 // Special Notes :
939 // Scope : public
940 // Creator : Pete Sholander, SNL, Electrical and Microsystems Modeling
941 // Creation Date : 9/12/14
942 //-----------------------------------------------------------------------------
944 {
945  std::vector<Instance*>::iterator iter;
946  std::vector<Instance*>::iterator first = instanceContainer.begin();
947  std::vector<Instance*>::iterator last = instanceContainer.end();
948 
949  for (iter=first; iter!=last; ++iter)
950  {
951  delete (*iter);
952  }
953 }
954 
955 //-----------------------------------------------------------------------------
956 // Function : Model::printOutInstances
957 // Purpose : debugging tool.
958 // Special Notes :
959 // Scope : public
960 // Creator : Pete Sholander, SNL, Electrical and Microsystems Modeling
961 // Creation Date : 9/12/14
962 //-----------------------------------------------------------------------------
963 std::ostream &Model::printOutInstances(std::ostream &os) const
964 {
965  std::vector<Instance*>::const_iterator iter;
966  std::vector<Instance*>::const_iterator first = instanceContainer.begin();
967  std::vector<Instance*>::const_iterator last = instanceContainer.end();
968 
969  int i;
970  os << std::endl;
971  os << " name model name Parameters" << std::endl;
972  for (i=0, iter=first; iter!=last; ++iter, ++i)
973  {
974  os << " " << i << ": " << (*iter)->getName() << " ";
975  os << getName();
976  os << std::endl;
977  }
978 
979  os << std::endl;
980 
981  return os;
982 }
983 
984 //-----------------------------------------------------------------------------
985 // Function : Model::forEachInstance
986 // Purpose :
987 // Special Notes :
988 // Scope : public
989 // Creator : David Baur
990 // Creation Date : 2/4/2014
991 //-----------------------------------------------------------------------------
992 /// Apply a device instance "op" to all instances associated with this
993 /// model
994 ///
995 /// @param[in] op Operator to apply to all instances.
996 ///
997 ///
998 void Model::forEachInstance(DeviceInstanceOp &op) const /* override */
999 {
1000  for (std::vector<Instance *>::const_iterator it = instanceContainer.begin(); it != instanceContainer.end(); ++it)
1001  op(*it);
1002 }
1003 
1004 
1005 Device *Traits::factory(const Configuration &configuration, const FactoryBlock &factory_block)
1006 {
1007 
1008  return new DeviceMaster<Traits>(configuration, factory_block, factory_block.solverState_, factory_block.deviceOptions_);
1009 }
1010 
1012 {
1014  .registerDevice("PowerGridGenBus", 1)
1015  .registerDevice("PGGB", 1)
1016  .registerModelType("PowerGridGenBus", 1);
1017 }
1018 
1019 } // namespace PowerGridGenBus
1020 } // namespace Device
1021 } // namespace Xyce
const InstanceName & getName() const
const SolverState & solverState_
Descriptor & addPar(const char *parName, T default_value, T U::*varPtr)
Adds the parameter description to the parameter map.
Definition: N_DEV_Pars.h:1429
Pure virtual class to augment a linear system.
Parameter may be specified as time dependent expression from netlist.
Definition: N_DEV_Pars.h:67
void addInternalNode(Util::SymbolTable &symbol_table, int index, const InstanceName &instance_name, const std::string &lead_name)
std::vector< Instance * > instanceContainer
static Device * factory(const Configuration &configuration, const FactoryBlock &factory_block)
void registerJacLIDs(const std::vector< std::vector< int > > &jacLIDVec)
#define AssertLIDs(cmp)
virtual void registerJacLIDs(const JacobianStamp &jacLIDVec)
static void loadInstanceParameters(ParametricData< Instance > &instance_parameters)
void setParams(const std::vector< Param > &params)
const std::string & getName() const
void registerStateLIDs(const std::vector< int > &staLIDVecRef)
The FactoryBlock contains parameters needed by the device, instance and model creation functions...
const DeviceOptions & deviceOptions_
static Config< T > & addConfiguration()
Adds the device to the Xyce device configuration.
static void loadModelParameters(ParametricData< Model > &model_parameters)
Linear::Matrix * dFdxMatrixPtr
The Device class is an interface for device implementations.
Definition: N_DEV_Device.h:101
static std::vector< std::vector< int > > jacStamp
Class Configuration contains device configuration data.
void loadNodeSymbols(Util::SymbolTable &symbol_table) const
Populates and returns the store name map.
virtual void forEachInstance(DeviceInstanceOp &op) const
Apply a device instance "op" to all instances associated with this model.
void registerLIDs(const std::vector< int > &intLIDVecRef, const std::vector< int > &extLIDVecRef)
const SolverState & getSolverState() const
Instance(const Configuration &configuration, const InstanceBlock &IB, Model &Riter, const FactoryBlock &factory_block)
const std::vector< std::vector< int > > & jacobianStamp() const
ModelBlock represents a .MODEL line from the netlist.
virtual std::ostream & printOutInstances(std::ostream &os) const
Manages parameter binding for class C.
Definition: N_DEV_Pars.h:214
InstanceBlock represent a device instance line from the netlist.
std::vector< Param > params