Xyce  6.1
N_DEV_MemristorTEAM.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_MemristorTEAM.C,v $
27 //
28 // Purpose : Implementation of the TEAM memristor model. See.
29 // TEAM: Threshold Adaptive Memristor Model
30 // Shahar Kvatinsky, Eby G. Friedman, Uri Weiser,
31 // IEEE Transactions on Circuits and Systems-I Vol 60, No. 1, Jan. 2013
32 // model details.
33 //
34 // Special Notes :
35 //
36 // Creator : Richard Schiek, Electrical Models & Simulations
37 //
38 // Creation Date : 10/23/14
39 //
40 // Revision Information:
41 // ---------------------
42 //
43 // Revision Number: $Revision: 1.7.2.3 $
44 //
45 // Revision Date : $Date: 2015/04/03 19:21:55 $
46 //
47 // Current Owner : $Author: tvrusso $
48 //----------------------------------------------------------------------------
49 #include <Xyce_config.h>
50 
51 #include <N_DEV_MemristorTEAM.h>
52 
53 #include <N_DEV_DeviceOptions.h>
54 #include <N_DEV_ExternData.h>
55 #include <N_DEV_SolverState.h>
56 #include <N_DEV_Message.h>
57 #include <N_LAS_Matrix.h>
58 #include <N_UTL_FeatureTest.h>
59 #include <Sacado.hpp>
60 
61 namespace Xyce {
62 namespace Device {
63 namespace MemristorTEAM {
64 
65 //
66 // Window functions used to keep the internal variable
67 // "w" when it characterized the width of the conductive layer
68 // "x" when it characterizes the thickness of the conductive layer
69 //
70 //
71 template <typename ScalarT>
72 void JogelkarWindowFunction( const ScalarT & w, const ScalarT & D, const ScalarT & p, ScalarT & fval )
73 {
74  fval = 1.0 - pow( (2.0*(w/D) - 1.0), (2*p) );
75 }
76 
77 
78 template <typename ScalarT>
79 void BiolekWindowFunction( const ScalarT & w, const ScalarT & D, const ScalarT & p, const ScalarT & i, ScalarT & fval )
80 {
81  ScalarT stp_of_i = 1.0;
82  if( i < 0.0 )
83  stp_of_i = 0;
84  fval = 1.0 - pow( ((w/D) - stp_of_i), (2*p) );
85 }
86 
87 template <typename ScalarT>
88 void ProdromakisWindowFunction( const ScalarT & w, const ScalarT & D, const ScalarT & p, const ScalarT & j, ScalarT & fval )
89 {
90  fval = j * (1.0 - pow( (pow( (w - 0.5), 2) -0.75), p ) );
91 }
92 
93 
94 //
95 // Window function F for TEAM window function
96 // takes x, Aoff and wc as parameters.
97 // returns fval as result
98 template <typename ScalarT>
99 void TEAMWindowFunctionF(const ScalarT & x, const ScalarT & i, const ScalarT & aOff, ScalarT & aOn, const ScalarT & wc, ScalarT & fval )
100 {
101  if( i >= 0.0 )
102  {
103  fval = exp( -exp( (x-aOff)/wc ) );
104  }
105  else
106  {
107  fval = exp( -exp( -(x-aOn)/wc ) );
108  }
109 }
110 
111 
112 // Internal state variable equations needed by the TEAM model
113 //
114 // dx(t)/dt + F(X) = 0
115 //
116 // F(x) : defined as.
117 //
118 // F(x) = -k_off ( (i(t)/i_off) - 1)^a_off F_off(x) if 0 < i_off < i
119 //
120 // F(x) = 0 if i_on < i < i_off
121 //
122 // F(x) = -k_on ( (i(t)/i_on) - 1 )^a_on F_on(x) if i < i_on < 0
123 //
124 // Note F(x) has a "-" sign in the above relative to the paper it's
125 // based on because I've moved it to the left hand side of the equation.
126 
127 //
128 // State variable x. F vector term
129 //
130 // F(x) : defined as.
131 //
132 // F(x) = -k_off ( (i(t)/i_off) - 1)^a_off F_off(x) if 0 < i_off < i
133 //
134 // F(x) = 0 if i_on < i < i_off
135 //
136 // F(x) = -k_on ( (i(t)/i_on) - 1 )^a_on F_on(x) if i < i_on < 0
137 //
138 // Note F(x) has a "-" sign in the above relative to the paper it's
139 // based on because I've moved it to the left hand side of the equation.
140 
141 template <typename ScalarT>
142 void xVarFterm( const ScalarT & Vpos, const ScalarT & Vneg, const ScalarT & x,
143  const ScalarT & G, const ScalarT & iOff, const ScalarT & iOn,
144  const ScalarT & kOff, const ScalarT & kOn,
145  const ScalarT & alphaOff, const ScalarT & alphaOn, ScalarT & fval )
146 {
147 
148  // equation 14a, 14b and 14c
149  ScalarT i = G * (Vpos - Vneg);
150 
151  if( i>=iOff )
152  {
153  fval=-kOff*pow( ((i/iOff) - 1.0), alphaOff );
154  }
155  else if( i<=iOn )
156  {
157  fval=-kOn*pow( ((i/iOn) - 1.0), alphaOn);
158  }
159  if ( (i>iOn) && (i<iOff) )
160  {
161  fval=0.0;
162  }
163 }
164 
165 
166 // linear current voltage model, Reff
167 template <typename ScalarT>
168 void ReffLin( const ScalarT & X, const ScalarT & Ron, const ScalarT & Roff, const ScalarT & Xon, const ScalarT & Xoff, ScalarT & fval )
169 {
170  // equation 15
171  fval = Ron + (X - Xon) * (Roff-Ron)/(Xoff - Xon);
172 }
173 
174 // nonlinear current voltage model, Reff
175 template <typename ScalarT>
176 void ReffNonLin( const ScalarT & X, const ScalarT & Ron, const ScalarT & Roff, const ScalarT & Xon, const ScalarT & Xoff, ScalarT & fval )
177 {
178  // equation 17
179  ScalarT lambda = log(Roff/Ron);
180  // equation 16
181  fval = Ron * exp( (X - Xon) * lambda /(Xoff - Xon) );
182 }
183 
184 
185 // Common Jacobian Stamp for all MemristorTEAM devices.
186 // Because all memristors have identical Jacobian stamps, this data is
187 // declared static and is shared by all memristor instances.
188 //
189 std::vector<std::vector<int> > Instance::jacStamp;
190 
191 //-----------------------------------------------------------------------------
192 // Function : Xyce::Device::MemristorTEAM::Instance::initializeJacobianStamp
193 // Purpose :
194 // Special Notes :
195 // Scope : private
196 // Creator : Richard Schiek, Electrical Models & Simulations
197 // Creation Date : 2/11/2014
198 //-----------------------------------------------------------------------------
199 //
200 // @brief Common Jacobian stamp initializer for all MemristorTEAM devices.
201 //
202 // The Jacobian stamp is a sparse-matrix representation of the pattern
203 // of non-zero elements that a device will put into the Jacobian matrix.
204 //
205 // The Jacobian stamp is used by the Topology package to determine indices
206 // into the full Jacobian matrix for elements that correspond to this
207 // device.
208 //
209 // There is one row of the Jacobian stamp for each equation associated with
210 // a device. The number of elements in a row are the number of non-zero
211 // elements in that row of the device's contribution to the Jacobian.
212 // The values of the elements are numbers local to the device that
213 // represent the column in which the non-zero element appears.
214 //
215 // For this memristor, there are two external nodes (the positive and negative
216 // terminals of the device). The positive node is referred to as the 0th
217 // node of the device, and the negative node the 1st node.
218 //
220 {
221  if (jacStamp.empty())
222  {
223  jacStamp.resize(3);
224  jacStamp[0].resize(3);
225  jacStamp[0][0] = 0;
226  jacStamp[0][1] = 1;
227  jacStamp[0][2] = 2;
228  jacStamp[1].resize(3);
229  jacStamp[1][0] = 0;
230  jacStamp[1][1] = 1;
231  jacStamp[1][2] = 2;
232  jacStamp[2].resize(3);
233  jacStamp[2][0] = 0;
234  jacStamp[2][1] = 1;
235  jacStamp[2][2] = 2;
236  }
237 }
238 
239 //-----------------------------------------------------------------------------
240 // Function : Xyce::Device::MemristorTEAM::Traits::loadInstanceParameters
241 // Purpose :
242 // Special Notes :
243 // Scope : private
244 // Creator : Richard Schiek, Electrical Models & Simulations
245 // Creation Date : 10/23/2014
246 //-----------------------------------------------------------------------------
247 //
248 // Loads the parameter definition into the instance parameter map.
249 //
250 // @param p instance parameter map
251 //
252 // Each parameter supported by the memristor device instance is
253 // defined via the addPar call. The minimum information required is
254 // the name of the parameter, the default value, and a member pointer
255 // to a variable in the instance class to which the value will be
256 // assigned.
257 //
258 // Additional information such as documentation for the parameter, units
259 // (used only in output of tables for documentation), whether a special
260 // boolean should be set if the parameter is given, etc. may be specified
261 // using the various set* calls of the Xyce::Device::Descriptor class.
262 //
263 // It is important to note that since parameters defined by addPar are
264 // defined as metadata separate from any instance, it is not possible to
265 // establish interrelationships between parameter defaults here. Parameter
266 // defaults specified in addPar are always constants. If a device requires
267 // that parameter defaults depend on values of other parameters, this has to
268 // be done in the instance constructor. Examples of such parameters in this
269 // device arethe "DTEMP" and "W" parameters, which are set to special defaults
270 // at device instantiation time. Defaults for those parameters in the addPar
271 // calls (and hence in the LaTeX tables in the reference guide produced from
272 // this data) are misleading.
273 //
275 {
276  p.addPar("IVRELATION", 0, &MemristorTEAM::Instance::IVrelation)
277  .setUnit(U_NONE)
278  .setDescription("IV relationship to use, 0 is linear, 1 is nonlinear");
279 
280 }
281 
282 //-----------------------------------------------------------------------------
283 // Function : Xyce::Device::MemristorTEAM::Traits::loadModelParameters
284 // Purpose :
285 // Special Notes : The addPar calls here were refactored and moved here
286 // from the model constructor. Those addPars had been
287 // in place from 2/4/2005.
288 // Scope : private
289 // Creator : Richard Schiek, Electrical Models & Simulations
290 // Creation Date : 10/23/2014
291 //-----------------------------------------------------------------------------
292 //
293 // Loads the parameter definition into the model parameter map.
294 //
295 // @param p model parameter map
296 //
297 // @see Xyce::Device::MemristorTEAM::Traits::loadInstanceParameters
298 //
299 //
301 {
302 
303  // Create parameter definitions for parameter member variables
304  p.addPar("KON", -8.0e-13, &MemristorTEAM::Model::kOn_)
305  .setUnit(U_MSM1)
306  .setDescription("Modeling Coefficient");
307  p.addPar("KOFF", 8.0e-13, &MemristorTEAM::Model::kOff_)
308  .setUnit(U_MSM1)
309  .setDescription("Modeling Coefficient");
310  p.addPar("ALPHAON", 3.0, &MemristorTEAM::Model::alphaOn_)
311  .setUnit(U_NONE)
312  .setDescription("Modeling Coefficient");
313  p.addPar("ALPHAOFF", 3.0, &MemristorTEAM::Model::alphaOff_)
314  .setUnit(U_NONE)
315  .setDescription("Modeling Coefficient");
316  p.addPar("XON", 0.0, &MemristorTEAM::Model::xOn_)
317  .setUnit(U_METER)
318  .setDescription("Modeling Coefficient");
319  p.addPar("XOFF", 3.0e-9, &MemristorTEAM::Model::xOff_)
320  .setUnit(U_METER)
321  .setDescription("Modeling Coefficient");
322  p.addPar("RON", 50.0, &MemristorTEAM::Model::ROn_)
323  .setUnit(U_OHM)
324  .setDescription("Resistence in on state");
325  p.addPar("ROFF", 1.0e3, &MemristorTEAM::Model::ROff_)
326  .setUnit(U_OHM)
327  .setDescription("Resistence in off state");
328  p.addPar("ION", 8.9e-6, &MemristorTEAM::Model::iOn_)
329  .setUnit(U_AMP)
330  .setDescription("Current scale in On state");
331  p.addPar("IOFF", 1.15e-4, &MemristorTEAM::Model::iOff_)
332  .setUnit(U_OHM)
333  .setDescription("Current scale in off state");
334  p.addPar("XSCALING", 1.0, &MemristorTEAM::Model::xScaling_)
335  .setUnit(U_NONE)
336  .setDescription("Scaling for x variable. For example 1e9 if x will be in units of nanometers.");
337 
338  p.addPar("D", 1.15e-4, &MemristorTEAM::Model::D_)
339  .setUnit(U_NONE)
340  .setDescription("Window Function Parameter (windows 1, 2 and 3)");
341  p.addPar("P", 1.15e-4, &MemristorTEAM::Model::p_)
342  .setUnit(U_NONE)
343  .setDescription("Window Function Parameter (windows 1, 2 and 3)");
344  p.addPar("J", 1.15e-4, &MemristorTEAM::Model::j_)
345  .setUnit(U_NONE)
346  .setDescription("Window Function Parameter (window 3)");
347 
348  p.addPar("AON", 0.0, &MemristorTEAM::Model::aOn_)
349  .setUnit(U_METER)
350  .setDescription("Window Function Parameter (window 4)");
351  p.addPar("AOFF", 3.0e-9, &MemristorTEAM::Model::aOff_)
352  .setUnit(U_METER)
353  .setDescription("Window Function Parameter (window 4)");
354 
355  p.addPar("WC", 1.07e-12, &MemristorTEAM::Model::wc_)
356  .setUnit(U_METER)
357  .setDescription("Window Function Parameter (window 4)");
358 
360  .setUnit(U_NONE)
361  .setDescription("Type of windowing function: 0-None, 1-Jogelkar, 2-Biolek, 3-Prodromakis, 4-Kvatinsky");
362 
363 
364 }
365 
366 //-----------------------------------------------------------------------------
367 // Function : Xyce::Device::MemristorTEAM::Instance::Instance
368 // Purpose : Instance constructor
369 // Special Notes :
370 // Scope : public
371 // Creator : Richard Schiek, Electrical Models & Simulations
372 // Creation Date : 10/23/2014
373 //-----------------------------------------------------------------------------
374 //
375 // Construct a memristor instance.
376 //
377 
379  const Configuration & configuration,
380  const InstanceBlock & instance_block,
381  Model & model,
382  const FactoryBlock & factory_block)
383  : DeviceInstance(instance_block, configuration.getInstanceParameters(), factory_block),
384  model_(model),
385  IVrelation(0),
386  G(0.0),
387  i0(0.0),
388  li_Pos(-1),
389  li_Neg(-1),
390  li_x(-1),
391  li_store_dev_i(-1),
392  li_branch_data(0),
393  APosEquPosNodeOffset(-1),
394  APosEquNegNodeOffset(-1),
395  APosEquXNodeOffset(-1),
396  ANegEquPosNodeOffset(-1),
397  ANegEquNegNodeOffset(-1),
398  ANegEquXNodeOffset(-1),
399  XEquVPosOffset(-1),
400  XEquVNegOffset(-1),
401  XEquXOffset(-1)
403  ,
404  f_PosEquPosNodePtr(0),
405  f_PosEquNegNodePtr(0),
406  f_PosEquXNodePtr(0),
407  f_NegEquPosNodePtr(0),
408  f_NegEquNegNodePtr(0),
409  f_NegEquXNodePtr(0),
410  f_XEquPosNodePtr(0),
411  f_XEquNegNodePtr(0),
412  f_XEquXNodePtr(0),
413  q_XEquXNodePtr(0)
414  // ,
415 #endif
416  //resSens(*this)
417 {
418  // Initialize DeviceInstance values
419  numIntVars = 1; // Initialize number if internal nodes in DeviceInstance
420  numExtVars = 2; // Initialize number if external nodes in DeviceInstance
421  numStateVars = 0; // Initialize number if state variables in DeviceInstance
422  setNumStoreVars(1); // Initialize number if store variables in DeviceInstance
423  numLeadCurrentStoreVars = 1; // Initialize number if lead current variables
424  //in DeviceInstance
425 
426  setNumBranchDataVars(0); // by default don't allocate space in branch vectors
427  numBranchDataVarsIfAllocated = 1; // this is the space to allocate if lead current or power is needed.
428 
430 
431  // Set params to constant default values from parameter definition
433 
434  // Set params according to instance line and constant defaults from metadata
435  setParams(instance_block.params);
436 
437  // Calculate any parameters specified as expressions
439 
440  // Process the parameters to complete initialization
441  processParams();
442 
443 }
444 
445 //-----------------------------------------------------------------------------
446 // Function : Xyce::Device::MemristorTEAM::Instance::processParams
447 // Purpose :
448 // Special Notes :
449 // Scope : public
450 // Creator : Richard Schiek, Electrical Models & Simulations
451 // Creation Date : 10/23/2014
452 //-----------------------------------------------------------------------------
453 // Process parameters.
454 //
455 // @return true on success
456 //
457 // In general, the processParams method is intended as a place for complex
458 // manipulation of parameters that must happen if temperature or other
459 // external changes happen.
460 //
462 {
463  // now set the temperature related stuff.
464  double temp=0;
465  return updateTemperature(temp);
466 }
467 
468 //-----------------------------------------------------------------------------
469 // Function : Xyce::Device::MemristorTEAM::Instance::registerLIDs
470 // Purpose :
471 // Special Notes :
472 // Scope : public
473 // Creator : Richard Schiek, Electrical Models & Simulations
474 // Creation Date : 10/23/2014
475 //-----------------------------------------------------------------------------
476 //
477 // Register local IDs
478 //
479 // Register the local internal and external node IDs.
480 //
481 // @param intLIDVecRef internal local IDs from topology package
482 // @param extLIDVecRef external local IDs from topology package
483 //
485  const std::vector<int> & intLIDVecRef,
486  const std::vector<int> & extLIDVecRef)
487 {
488  AssertLIDs(intLIDVecRef.size() == numIntVars);
489  AssertLIDs(extLIDVecRef.size() == numExtVars);
490 
491  // Copy the local ID lists.
492  intLIDVec = intLIDVecRef; // Set the internal local IDs in DeviceInstance
493  extLIDVec = extLIDVecRef; // Set the external local IDs in DeviceInstance
494 
495  li_Pos = extLIDVec[0];
496  li_Neg = extLIDVec[1];
497 
498  // This lid is for the internal variable for layer thickess that determines resistence
499  li_x = intLIDVec[0];
500 
501 }
502 
503 //-----------------------------------------------------------------------------
504 // Function : Xyce::Device::MemristorTEAM::Instance::registerStateLIDs
505 // Purpose :
506 // Special Notes :
507 // Scope : public
508 // Creator : Richard Schiek, Electrical Models & Simulations
509 // Creation Date : 10/23/2014
510 //-----------------------------------------------------------------------------
511 //
512 // Register the local state IDs
513 //
514 // @note The memristor does not have any state vars, so this function
515 // does nothing.
516 //
517 // @param staLIDVecRef State variable local IDs
518 //
519 // In general, devices may declare at construction that they require storage
520 // locations in the "state vector." Topology assigns locations in the
521 // state vector and returns "Local IDs" (LIDs) for devices to use for their
522 // state vector entries. If a device uses state vector entries, it
523 // uses the registerStateLIDs method to save the local IDs for later use.
524 //
525 // @author Robert Hoekstra, SNL, Parallel Computational Sciences
526 // @date 06/12/02
527 
528 void Instance::registerStateLIDs(const std::vector<int> & staLIDVecRef)
529 {
530  AssertLIDs(staLIDVecRef.size() == numStateVars);
531 }
532 
533 //-----------------------------------------------------------------------------
534 // Function : Xyce::Device::MemristorTEAM::Instance::registerStoreLIDs
535 // Purpose :
536 // Special Notes :
537 // Scope : public
538 // Creator : Richard Schiek, Electrical Models & Simulations
539 // Creation Date : 12/18/2012
540 //-----------------------------------------------------------------------------
541 // Register the local store IDs
542 //
543 // In addition to state vector, Xyce maintains a separate datastructure
544 // called a "store" vector. As with other such vectors, the device
545 // declares at construction time how many store vector entries it needs,
546 // and later Topology assigns locations for devices, returning LIDs.
547 //
548 //
549 //
550 
551 void Instance::registerStoreLIDs(const std::vector<int> & stoLIDVecRef)
552 {
553  AssertLIDs(stoLIDVecRef.size() == getNumStoreVars());
554  li_store_R = stoLIDVecRef[0];
555  if (loadLeadCurrent)
556  {
557  li_store_dev_i = stoLIDVecRef[1];
558  }
559 
560 }
561 
562 //-----------------------------------------------------------------------------
563 // Function : Xyce::Device::MemristorTEAM::Instance::registerBranchDataLIDs
564 // Purpose :
565 // Special Notes :
566 // Scope : public
567 // Creator : Richard Schiek, Electrical Systems Modeling
568 // Creation Date : 12/18/2012
569 //-----------------------------------------------------------------------------
570 // Register the local store IDs
571 //
572 // In addition to state vector, Xyce maintains a separate datastructure
573 // called a "branch data" vector. As with other such vectors, the device
574 // declares at construction time how many branch vector entries it needs,
575 // and later Topology assigns locations for devices, returning LIDs.
576 //
577 // These LIDs are stored in this method for later use.
578 //
579 // The memristor device uses exactly one "branch data vector" element, where
580 // it keeps the "lead current" that may be used on .PRINT lines as
581 // "I(ymemristor)" for the current through ymemristor. and a junction voltage.
582 //
583 // @param stoLIDVecRef Store variable local IDs
584 //
585 // @author Richard Schiek, Electrical Systems Modeling
586 // @date 12/18/2012
587 
588 void Instance::registerBranchDataLIDs(const std::vector<int> & branchLIDVecRef)
589 {
590  AssertLIDs(branchLIDVecRef.size() == getNumBranchDataVars());
591 
592  if (loadLeadCurrent)
593  {
594  li_branch_data= branchLIDVecRef[0];
595  }
596 }
597 
598 
599 
600 //-----------------------------------------------------------------------------
601 // Function : Instance::loadNodeSymbols
602 // Purpose :
603 // Special Notes :
604 // Scope : public
605 // Creator : Richard Schiek, Electrical Sysetms Modeling
606 // Creation Date : 05/13/05
607 //-----------------------------------------------------------------------------
608 void Instance::loadNodeSymbols(Util::SymbolTable &symbol_table) const
609 {
610  addInternalNode(symbol_table, li_x, getName(), "x");
611 
612  addStoreNode(symbol_table, li_store_R, getName(), "R");
613 
614  if (loadLeadCurrent)
615  {
616  addBranchDataNode( symbol_table, li_branch_data, getName(), "BRANCH_D");
617  }
618 }
619 
620 
621 
622 
623 //-----------------------------------------------------------------------------
624 // Function : Xyce::Device::MemristorTEAM::Instance::registerJacLIDs
625 // Purpose :
626 // Special Notes :
627 // Scope : public
628 // Creator : Richard Schiek, Electrical Models & Simulations
629 // Creation Date : 10/23/2014
630 //-----------------------------------------------------------------------------
631 //
632 // Register the Jacobian local IDs
633 //
634 // @param jacLIDVec Jacobian local Ids
635 //
636 // @see Xyce::Device::MemristorTEAM::Instance::initializeJacobianStamp
637 //
638 // Having established local IDs for the solution variables, Topology must
639 // also assign local IDs for the elements of the Jacobian matrix.
640 //
641 // For each non-zero element that was identified in the jacobianStamp,
642 // Topology will assign a Jacobian LID. The jacLIDVec will have the
643 // same structure as the JacStamp, but the values in the jacLIDVec will
644 // be offsets into the row of the sparse Jacobian matrix corresponding
645 // to the non-zero identified in the stamp.
646 //
647 // These offsets are stored in this method for use later when we load
648 // the Jacobian.
649 //
650 // @author Robert Hoekstra, SNL, Parallel Computational Sciences
651 // @date 08/27/01
652 
653 void Instance::registerJacLIDs(const std::vector< std::vector<int> > & jacLIDVec)
654 {
655  // Let DeviceInstance do its work.
657 
658  // Store offsets of the components of the Jacobian of this instance
659  APosEquPosNodeOffset = jacLIDVec[0][0];
660  APosEquNegNodeOffset = jacLIDVec[0][1];
661  APosEquXNodeOffset = jacLIDVec[0][2];
662  ANegEquPosNodeOffset = jacLIDVec[1][0];
663  ANegEquNegNodeOffset = jacLIDVec[1][1];
664  ANegEquXNodeOffset = jacLIDVec[1][2];
665  XEquVPosOffset = jacLIDVec[2][0];
666  XEquVNegOffset = jacLIDVec[2][1];
667  XEquXOffset = jacLIDVec[2][2];
668 
669 }
670 
671 //-----------------------------------------------------------------------------
672 // Function : Xyce::Device::MemristorTEAM::Instance::setupPointers
673 // Purpose :
674 // Special Notes :
675 // Scope : public
676 // Creator : Richard Schiek, Electrical Models & Simulations
677 // Creation Date : 10/23/2014
678 //-----------------------------------------------------------------------------
679 //
680 // Setup direct access pointer to solution matrix and vectors.
681 //
682 // @see Xyce::Device::MemristorTEAM::Instance::registerJacLIDs
683 //
684 // As an alternative to the row offsets defined in registerJacLIDs, it
685 // is also possible to obtain direct pointers of the Jacobian elements.
686 //
687 // This method uses the offsets obtained in registerJacLIDs to retrieve
688 // the pointers.
689 //
690 // In this device the pointers to the matrix are only saved
691 // (and are only used for matrix access) if
692 // Xyce_NONPOINTER_MATRIX_LOAD is NOT defined at compile time. For
693 // some devices the use of pointers instead of array indexing can be
694 // a performance enhancement.
695 //
696 // Use of pointers in this device is disabled by defining
697 // Xyce_NONPOINTER_MATRIX_LOAD at compile time. When disabled, array
698 // indexing with the offsets from registerJacLIDs is used in
699 // the matrix load methods.
700 //
701 // @author Eric Keiter, SNL
702 // @date 11/30/08
703 
705 {
706 #ifndef Xyce_NONPOINTER_MATRIX_LOAD
707  Linear::Matrix & dFdx = *(extData.dFdxMatrixPtr);
708  Linear::Matrix & dQdx = *(extData.dQdxMatrixPtr);
717  f_XEquXNodePtr = &(dFdx[li_x][XEquXOffset]);
718  q_XEquXNodePtr = &(dQdx[li_x][XEquXOffset]);
719 #endif
720 }
721 
722 //-----------------------------------------------------------------------------
723 // Function : Xyce::Device::MemristorTEAM::Instance::updateIntermediateVars
724 // Purpose :
725 // Special Notes :
726 // Scope : public
727 // Creator : Richard Schiek, Electrical Models & Simulations
728 // Creation Date : 10/23/2014
729 //-----------------------------------------------------------------------------
730 //
731 // Update the intermediate variables
732 //
733 // @return true on success
734 //
735 // The bulk of any device's computation is handled in the instance class'
736 // updateIntermediateVars method. For the memristor, this is
737 // merely the computation of the current through the device given the
738 // voltage difference between its terminals.
739 //
740 // Intermediate variables computed here are used in the methods that
741 // load data into the F, Q, dFdX and dQdX data structures.
742 //
743 // @note This method is called by the updatePrimaryState
744 // method. Since the MemristorTEAM class reimplements the "Master"
745 // "loadState" function that loads the contributions from all
746 // such devices in a single loop, THIS FUNCTION IS NOT ACTUALLY
747 // USED!
748 //
749 //
751 {
752  double * solVec = extData.nextSolVectorRawPtr;
753  double v_pos = solVec[li_Pos];
754  double v_neg = solVec[li_Neg];
755  double x = solVec[li_x];
756  double Reff = model_.ROn_;
757  //
758  // Calclate Reff and deriative term dReff/dx
759  //
760  {
761  // extra scope to limit variable extent. Otherwise one will have lots of
762  // similar variables
763  Sacado::Fad::SFad<double,1> varX( 1, 0, x );
764  Sacado::Fad::SFad<double,1> paramRon( model_.ROn_ );
765  Sacado::Fad::SFad<double,1> paramRoff( model_.ROff_ );
766  Sacado::Fad::SFad<double,1> paramXon( model_.xOn_ );
767  Sacado::Fad::SFad<double,1> paramXoff( model_.xOff_ );
768  Sacado::Fad::SFad<double,1> resultFad;
769  if(IVrelation==0)
770  {
771  // liner current voltage relationship
772  ReffLin( varX, paramRon, paramRoff, paramXon, paramXoff, resultFad );
773 
774  }
775  else if( IVrelation==1 )
776  {
777  ReffNonLin( varX, paramRon, paramRoff, paramXon, paramXoff, resultFad );
778  }
779  Reff = resultFad.val();
780  dReffdx = resultFad.dx(0);
781  dReffdvpos = -v_pos * pow( Reff, 2 ) * dReffdx;
782  dReffdvneg = v_neg * pow( Reff, 2 ) * dReffdx;
783  }
784 
785 
786  G=1.0/Reff;
787  i0= (v_pos-v_neg)*G;
788 
789  {
790  // calculate the xVar F equation update and the
791  // derivatives d(F(x))/dVpos, d(F(x))/dVneg and d(F(x))/dx
792  Sacado::Fad::SFad<double,3> varVpos( 3, 0, v_pos);
793  Sacado::Fad::SFad<double,3> varVneg( 3, 1, v_neg);
794  Sacado::Fad::SFad<double,3> varX( 3, 2, x );
795  Sacado::Fad::SFad<double,3> parami( i0 );
796  Sacado::Fad::SFad<double,3> paramG( G );
797  Sacado::Fad::SFad<double,3> paramiOff( model_.iOff_ );
798  Sacado::Fad::SFad<double,3> paramiOn( model_.iOn_ );
799  Sacado::Fad::SFad<double,3> paramkOff( model_.kOff_ );
800  Sacado::Fad::SFad<double,3> paramkOn( model_.kOn_ );
801  Sacado::Fad::SFad<double,3> paramAlphaOff( model_.alphaOff_ );
802  Sacado::Fad::SFad<double,3> paramAlphaOn( model_.alphaOn_ );
803  Sacado::Fad::SFad<double,3> resultFad;
804 
805  xVarFterm(varVpos, varVneg, varX, paramG, paramiOff, paramiOn, paramkOff, paramkOn,
806  paramAlphaOff, paramAlphaOn, resultFad);
807 
808  // calculate the window function value
809  Sacado::Fad::SFad<double,3> windowFunctionFad;
810 
811  if( model_.windowType_ == 0)
812  {
813  // no window
814  windowFunctionFad = 1.0;
815  }
816  else if( model_.windowType_ == 1)
817  {
818  Sacado::Fad::SFad<double,3> paramD( model_.D_ );
819  Sacado::Fad::SFad<double,3> paramP( model_.p_ );
820  JogelkarWindowFunction( varX, paramD, paramP, windowFunctionFad );
821  }
822  else if( model_.windowType_ == 2)
823  {
824  Sacado::Fad::SFad<double,3> paramD( model_.D_ );
825  Sacado::Fad::SFad<double,3> paramP( model_.p_ );
826  BiolekWindowFunction( varX, paramD, paramP, parami, windowFunctionFad );
827  }
828  else if( model_.windowType_ == 3)
829  {
830  Sacado::Fad::SFad<double,3> paramD( model_.D_ );
831  Sacado::Fad::SFad<double,3> paramP( model_.p_ );
832  Sacado::Fad::SFad<double,3> paramJ( model_.j_ );
833  ProdromakisWindowFunction( varX, paramD, paramP, paramJ, windowFunctionFad );
834  }
835  else if( model_.windowType_ == 4)
836  {
837  Sacado::Fad::SFad<double,3> paramAOn( model_.aOn_ );
838  Sacado::Fad::SFad<double,3> paramAOff( model_.aOff_ );
839  Sacado::Fad::SFad<double,3> paramWc( model_.wc_ );
840  TEAMWindowFunctionF( varX, parami, paramAOff, paramAOn, paramWc, windowFunctionFad );
841  }
842 
843  xVarFContribution = resultFad.val();
844  dxFEqdVpos = resultFad.dx(0);
845  dxFEqdVneg = resultFad.dx(1);
846  dxFEqdx = resultFad.dx(2);
847  //xVarFContribution = 1.0;
848  //dxFEqdVpos = 0.0;
849  //dxFEqdVneg = 0.0;
850  //dxFEqdx = 0.0;
851 
852  }
853 
854  //i0 = (v_pos-v_neg)*G;
855 
856  // get derivative terms for jacobian load
857  //
858  // APosEquPosNodeOffset --> G
859  // APosEquNegNodeOffset --> -G
860  // APosEquXNodeOffset --> -Reff^2 dR/dx
861  // ANegEquPosNodeOffset --> -G
862  // ANegEquNegNodeOffset --> G
863  // ANegEquXNodeOffset --> Reff^2 dR/dx
864  // XEquVPosOffset --> d xVarFterm / dVpos
865  // XEquVNegOffset --> d xVarFterm / dVneg
866  // XEquXOffset --> d xVarFterm / dx
867 
868  return true;
869 }
870 
871 //-----------------------------------------------------------------------------
872 // Function : Xyce::Device::MemristorTEAM::Instance::updatePrimaryState
873 // Purpose :
874 // Special Notes :
875 // Scope : public
876 // Creator : Richard Schiek, Electrical Models & Simulations
877 // Creation Date : 10/23/2014
878 //-----------------------------------------------------------------------------
879 //
880 // Update the state variables.
881 //
882 // @return true on success
883 //
884 // This function is the function that is called from the device manager
885 // each time a load of vectors and Jacobian is required. Its first
886 // job is to call updateIntermediateVars.
887 //
888 // After calling updateIntermediateVars, the updatePrimaryState method
889 // may load state vector elements as needed.
890 //
891 // The memristor device has no state vector elements, so all this method does
892 // in the memristor instance class is call updateIntermediateVars.
893 //
894 // There is no longer a "secondary" state. The "primary" in
895 // this method's name is purely historical.
896 //
897 // @note This method is called by the default implementation of the
898 // loadState master function. Since the MemristorTEAM class reimplements
899 // the "Master" "loadState" function that loads the contributions
900 // from all memristor devices in a single loop, THIS FUNCTION IS NOT
901 // ACTUALLY USED, NOR is the updateIntermediateVars method it calls!
902 // The updatePrimaryState method is only called when a device class
903 // does not re-implement the master class. This can be a source of
904 // confusion when attempting to modify the MemristorTEAM device, or any
905 // other device That reimplements the Master classes.
906 //
907 // @see Xyce::Device::MemristorTEAM::Master::updateState
908 //
909 // @author Eric Keiter, SNL, Parallel Computational Sciences
910 // @date 01/29/01
911 //
913 {
914  return updateIntermediateVars();
915 }
916 
918 {
919  double * qVec = extData.daeQVectorRawPtr;
920  double * solVec = extData.nextSolVectorRawPtr;
921  double x = solVec[li_x];
922 
923  qVec[li_x] += x;
924 
925  return true;
926 }
927 
928 
929 //-----------------------------------------------------------------------------
930 // Function : Xyce::Device::MemristorTEAM::Instance::loadDAEFVector
931 // Purpose :
932 // Special Notes :
933 // Scope : public
934 // Creator : Richard Schiek, Electrical Models & Simulations
935 // Creation Date : 10/23/2014
936 //-----------------------------------------------------------------------------
937 //
938 // Load the DAE F vector.
939 //
940 // @return true on success
941 //
942 // The Xyce DAE formulation solves the differential-algebraic
943 // equations \f$F(x)+dQ(x)/dt=0\f$ These are vector equations
944 // resulting from the modified nodal analysis of the circuit.
945 //
946 // This method loads the F-vector contributions for a single memristor
947 // instance.
948 //
949 // In this method, the offsets defined in registerLIDs are used to
950 // store the device's F contributions into the F vector.
951 //
952 // The Q vector is used for devices that store charge or magnetic
953 // energy, and since the memristor does none of that, the F vector
954 // contributions are the whole of the memristor's contribution to the
955 // full set of DAEs.
956 //
957 // @note This method is called by the default implementation of the
958 // loadDAEVectors master function. Since the MemristorTEAM class
959 // reimplements the "Master" "loadDAEVectors" function that loads the
960 // contributions from all memristor devices in a single loop, THIS
961 // FUNCTION IS NOT ACTUALLY USED. The loadDAEFVector method is only
962 // called when a device class does not re-implement the master class.
963 // This can be a source of confusion when attempting to modify the MemristorTEAM
964 // device, or any other device that reimplements the Master classes.
965 //
966 // @see Xyce::Device::MemristorTEAM::Master::loadDAEVectors
967 //
968 // @author Eric Keiter, SNL, Parallel Computational Sciences
969 // @date 01/24/03
970 //
972 {
973  double * fVec = extData.daeFVectorRawPtr;
974  fVec[li_Pos] += i0;
975  fVec[li_Neg] += -i0;
976  fVec[li_x] += xVarFContribution;
977 
978  if( loadLeadCurrent )
979  {
980  double * leadF = extData.nextLeadCurrFCompRawPtr;
981  double * junctionV = extData.nextJunctionVCompRawPtr;
982  double * solVec = extData.nextSolVectorRawPtr;
983  leadF[li_branch_data] = i0;
984  junctionV[li_branch_data] = solVec[li_Pos] - solVec[li_Neg];
985  }
986 
987 
988  return true;
989 }
990 
992 {
993  Linear::Matrix & dQdx = *(extData.dQdxMatrixPtr);
994  dQdx[li_x][XEquXOffset] = 1.0;
995  return true;
996 }
997 
998 
999 //-----------------------------------------------------------------------------
1000 // Function : Xyce::Device::MemristorTEAM::Instance::loadDAEdFdx
1001 // Purpose : Loads the F-vector contributions for a single
1002 // memristor instance.
1003 // Special Notes :
1004 // Scope : public
1005 // Creator : Richard Schiek, Electrical Models & Simulations
1006 // Creation Date : 10/23/2014
1007 //-----------------------------------------------------------------------------
1008 //
1009 // Load the DAE the derivative of the F vector with respect to the
1010 // solution vector x, dFdx
1011 //
1012 // Loads the contributions for a single memristor instance to the
1013 // dFdx matrix (the F contribution to the Jacobian).
1014 //
1015 // This method uses the Jacobian LIDs (row offsets) that were stored by
1016 // registerJacLIDs.
1017 //
1018 // @see Xyce::Device::MemristorTEAM::Instance::registerJacLIDs
1019 //
1020 // @note This method is called by the default implementation of the
1021 // loadDAEMatrices master function. Since the MemristorTEAM class
1022 // reimplements the "Master" "loadDAEMatrices" function that loads the
1023 // contributions from all memristor devices in a single loop, THIS
1024 // FUNCTION IS NOT ACTUALLY USED. The loadDAEdFdx method is only
1025 // called when a device class does not re-implement the master class.
1026 // This can be a source of confusion when attempting to modify the MemristorTEAM
1027 // device, or any other device that reimplements the Master classes.
1028 //
1029 // @see Xyce::Device::MemristorTEAM::Master::loadDAEMatrices
1030 //
1031 // @return true on success
1032 //
1033 // @author Eric Keiter, SNL, Parallel Computational Sciences
1034 // @date 03/05/04
1036 {
1037  Linear::Matrix & dFdx = *(extData.dFdxMatrixPtr);
1038  dFdx[li_Pos][APosEquPosNodeOffset] += G;
1039  dFdx[li_Pos][APosEquNegNodeOffset] -= G;
1041  dFdx[li_Neg][ANegEquPosNodeOffset] -= G;
1042  dFdx[li_Neg][ANegEquNegNodeOffset] += G;
1044  dFdx[li_x][XEquVPosOffset] += dxFEqdVpos;
1045  dFdx[li_x][XEquVNegOffset] += dxFEqdVneg;
1046  dFdx[li_x][XEquXOffset] += dxFEqdx;
1047  return true;
1048 }
1049 
1050 //-----------------------------------------------------------------------------
1051 // Function : Xyce::Device::MemristorTEAM::Instance::updateTemperature
1052 // Purpose :
1053 // Special Notes :
1054 // Scope : public
1055 // Creator : Richard Schiek, Electrical Models & Simulations
1056 // Creation Date : 10/23/2014
1057 //-----------------------------------------------------------------------------
1058 //
1059 // Update the parameters that depend on the temperature of the device
1060 //
1061 // @param temp_tmp temperature
1062 //
1063 // Xyce has a number of mechanisms that allow temperature to be changed
1064 // after a device has been instantiated. These include .STEP loops over
1065 // temperature. When temperature is changed, any device that has parameters
1066 // that depend on temperature must be updated. That updating happens here.
1067 //
1068 // The MemristorTEAM device supports temperature-dependent resistance through its
1069 // TC1 (linear dependence) and TC2 (quadratic dependence) parameters.
1070 // If these parameters are specified, the resistance must be updated.
1071 //
1072 // @return true on success
1073 //
1074 // @author Tom Russo, Component Information and Models
1075 // @date 02/27/01
1076 bool Instance::updateTemperature(const double & temp_tmp)
1077 {
1078  bool bsuccess = true;
1079  /*
1080  double difference, factor;
1081 
1082  if (temp_tmp != -999.0)
1083  temp = temp_tmp;
1084  difference = temp - model_.tnom;
1085  factor = model_.resistanceMultiplier*(1.0 + tempCoeff1*difference + tempCoeff2*difference*difference);
1086 
1087  if (R*factor != 0.0)
1088  G = 1.0/(R * factor);
1089  else
1090  G = 0.0;
1091  */
1092  return bsuccess;
1093 }
1094 
1095 //-----------------------------------------------------------------------------
1096 // Function : Xyce::Device::MemristorTEAM::Model::processParams
1097 // Purpose :
1098 // Special Notes :
1099 // Scope : public
1100 // Creator : Richard Schiek, Electrical Models & Simulations
1101 // Creation Date : 10/23/2014
1102 //-----------------------------------------------------------------------------
1103 //
1104 // Process model parameters
1105 //
1106 // @return true on success
1107 //
1108 // @author Eric Keiter, SNL, Parallel Computational Sciences
1109 // @date 6/03/02
1111 {
1112  return true;
1113 }
1114 
1115 //----------------------------------------------------------------------------
1116 // Function : Xyce::Device::MemristorTEAM::Model::processInstanceParams
1117 // Purpose :
1118 // Special Notes :
1119 // Scope : public
1120 // Creator : Richard Schiek, Electrical Models & Simulations
1121 // Creation Date : 10/23/2014
1122 //----------------------------------------------------------------------------
1123 //
1124 // Process the instance parameters of instance owned by this model
1125 //
1126 // This method simply loops over all instances associated with this
1127 // model and calls their processParams method.
1128 //
1129 // @return true
1130 //
1131 // @author Dave Shirely, PSSI
1132 // @date 03/23/06
1133 
1135 {
1136  for (InstanceVector::const_iterator it = instanceContainer.begin(); it != instanceContainer.end(); ++it)
1137  {
1138  (*it)->processParams();
1139  }
1140 
1141  return true;
1142 }
1143 //-----------------------------------------------------------------------------
1144 // Function : Xyce::Device::MemristorTEAM::Model
1145 // Purpose : model block constructor
1146 // Special Notes :
1147 // Scope : public
1148 // Creator : Richard Schiek, Electrical Models & Simulations
1149 // Creation Date : 10/23/2014
1150 //-----------------------------------------------------------------------------
1151 //
1152 // Construct a memristor model from a "model block" that was created
1153 // by the netlist parser.
1154 //
1155 // @param configuration
1156 // @param model_block
1157 // @param factory_block
1158 //
1159 // @author Eric Keiter, SNL, Parallel Computational Sciences
1160 // @date 5/16/00
1162  const Configuration & configuration,
1163  const ModelBlock & model_block,
1164  const FactoryBlock & factory_block)
1165  : DeviceModel(model_block, configuration.getModelParameters(), factory_block),
1166  kOn_(0.0),
1167  kOff_(0.0),
1168  alphaOn_(0.0),
1169  alphaOff_(0.0),
1170  xOn_(0.0),
1171  xOff_(0.0)
1172 
1173 {
1174  // Set params to constant default values.
1175  setDefaultParams();
1176 
1177  // Set params according to .model line and constant defaults from metadata.
1178  setModParams(model_block.params);
1179 
1180  // Set any non-constant parameter defaults.
1181  //if (!given("TNOM"))
1182  // tnom = getDeviceOptions().tnom;
1183 
1184  // Calculate any parameters specified as expressions.
1186 
1187  // calculate dependent (ie computed) params and check for errors.
1188  processParams();
1189 
1190 }
1191 
1192 //-----------------------------------------------------------------------------
1193 // Function : Xyce::Device::MemristorTEAM::Model::~Model
1194 // Purpose : destructor
1195 // Special Notes :
1196 // Scope : public
1197 // Creator : Richard Schiek, Electrical Models & Simulations
1198 // Creation Date : 10/23/2014
1199 //-----------------------------------------------------------------------------
1200 //
1201 // Destroy this model.
1202 //
1203 // Also destroys all instances that use this model.
1204 //
1205 // @author Eric Keiter, SNL, Parallel Computational Sciences
1206 // @date 3/16/00
1208 {
1209  // Destory all owned instances
1210  for (InstanceVector::const_iterator it = instanceContainer.begin(); it != instanceContainer.end(); ++it)
1211  {
1212  delete (*it);
1213  }
1214 
1215 }
1216 
1217 //-----------------------------------------------------------------------------
1218 // Function : N_DEV_MemristorTEAMModel::printOutInstances
1219 // Purpose : debugging tool.
1220 // Special Notes :
1221 // Scope : public
1222 // Creator : Richard Schiek, Electrical Models & Simulations
1223 // Creation Date : 10/23/2014
1224 //-----------------------------------------------------------------------------
1225 //
1226 // Print instances associated with this model.
1227 //
1228 // Used only for debugging
1229 //
1230 // @param os output stream
1231 //
1232 // @return reference to output stream
1233 //
1234 // @author Eric Keiter, SNL, Parallel Computational Sciences
1235 // @date 4/03/00
1236 //
1237 std::ostream &Model::printOutInstances(std::ostream &os) const
1238 {
1239  os << std::endl;
1240  os << "Number of MemristorTEAM Instances: " << instanceContainer.size() << std::endl;
1241  os << " name model name Parameters" << std::endl;
1242 
1243  int i = 0;
1244  for (InstanceVector::const_iterator it = instanceContainer.begin(); it != instanceContainer.end(); ++it)
1245  {
1246  os << " " << i << ": " << (*it)->getName() << "\t";
1247  os << getName();
1248  //os << "\t\tR(Tnom) = " << (*it)->R;
1249  os << "\tG(T) = " << (*it)->G;
1250  os << std::endl;
1251  ++i;
1252  }
1253 
1254  os << std::endl;
1255 
1256  return os;
1257 }
1258 
1259 //-----------------------------------------------------------------------------
1260 // Function : Model::forEachInstance
1261 // Purpose :
1262 // Special Notes :
1263 // Scope : public
1264 // Creator : Richard Schiek, Electrical Models & Simulations
1265 // Creation Date : 10/23/2014
1266 //-----------------------------------------------------------------------------
1267 // Apply a device instance "op" to all instances associated with this
1268 // model
1269 //
1270 // @param[in] op Operator to apply to all instances.
1271 //
1272 //
1273 void Model::forEachInstance(DeviceInstanceOp &op) const /* override */
1274 {
1275  for (std::vector<Instance *>::const_iterator it = instanceContainer.begin(); it != instanceContainer.end(); ++it)
1276  op(*it);
1277 }
1278 
1279 //-----------------------------------------------------------------------------
1280 // Function : Xyce::Device::MemristorTEAM::Master::updateState
1281 // Purpose :
1282 // Special Notes :
1283 // Scope : public
1284 // Creator : Richard Schiek, Electrical Models & Simulations
1285 // Creation Date : 10/23/2014
1286 //-----------------------------------------------------------------------------
1287 //
1288 // Update state for all memristor instances, regardless of model.
1289 //
1290 // @param solVec solution vector
1291 // @param staVec state vector
1292 // @param stoVec store vector
1293 //
1294 // @return true on success
1295 //
1296 // @note Because the memristor device re-implements the base-class
1297 // Master::updateState, the Instance::updatePrimaryState method is never
1298 // called, nor is the Instance::updateIntermediateVars method. This method
1299 // replaces those, and does the same work but inside a loop over all
1300 // memristor instances.
1301 //
1302 // @see Xyce::Device::MemristorTEAM::Instance::updatePrimaryState
1303 // @author Eric Keiter, SNL
1304 // @date 11/26/08
1305 
1306 bool Master::updateState(double * solVec, double * staVec, double * stoVec)
1307 {
1308  for (InstanceVector::const_iterator it = getInstanceBegin(); it != getInstanceEnd(); ++it)
1309  {
1310  Instance & ri = *(*it);
1311 
1312  double v_pos = solVec[ri.li_Pos];
1313  double v_neg = solVec[ri.li_Neg];
1314  double x = solVec[ri.li_x];
1315  double Reff = ri.model_.ROn_;
1316  //
1317  // Calclate Reff and deriative term dReff/dx
1318  //
1319  {
1320  // extra scope to limit variable extent. Otherwise one will have lots of
1321  // similar variables
1322  Sacado::Fad::SFad<double,1> varX( 1, 0, x );
1323  Sacado::Fad::SFad<double,1> paramRon( ri.model_.ROn_ );
1324  Sacado::Fad::SFad<double,1> paramRoff( ri.model_.ROff_ );
1325  Sacado::Fad::SFad<double,1> paramXon( ri.model_.xOn_ * ri.model_.xScaling_ );
1326  Sacado::Fad::SFad<double,1> paramXoff( ri.model_.xOff_ * ri.model_.xScaling_ );
1327  Sacado::Fad::SFad<double,1> resultFad;
1328  if(ri.IVrelation==0)
1329  {
1330  // liner current voltage relationship
1331  //Xyce::dout() << ri.getName() << "About to call ReffLin..." << ri.model_.ROn_ << " , " << ri.model_.ROff_ << ", " << ri.model_.xOn_ << ", " << ri.model_.xOff_ << std::endl;
1332 
1333  ReffLin( varX, paramRon, paramRoff, paramXon, paramXoff, resultFad );
1334  //fval = Ron + (X - Xon) * (Roff-Ron)/(Xoff - Xon);
1335  //dReff/dV1 = 0
1336  //dReff/dV2 = 0;
1337  //dReff/dx = (Roff-Ron)/(Xoff - Xon);
1338  }
1339  else if( ri.IVrelation==1 )
1340  {
1341  //Xyce::dout() << ri.getName() << "About to call ReffNonLin..." << std::endl;
1342  ReffNonLin( varX, paramRon, paramRoff, paramXon, paramXoff, resultFad );
1343  }
1344  ri.Reff = resultFad.val();
1345  ri.dReffdx = resultFad.dx(0);
1346  ri.dReffdvpos = (v_pos-v_neg)*(-pow( ri.Reff, 2 ) * ri.dReffdx) / (ri.model_.xScaling_);
1347  ri.dReffdvneg = -(v_pos-v_neg)*(-pow( ri.Reff, 2 ) * ri.dReffdx) / (ri.model_.xScaling_);
1348  //ri.dReffdvpos = 0.0;
1349  //ri.dReffdvneg = 0.0;
1350 
1351  }
1352 
1353  ri.G = 1.0/(ri.Reff);
1354  ri.i0 = (v_pos-v_neg)*ri.G;
1355 
1356  {
1357  // calculate the xVar F equation update and the
1358  // derivatives d(F(x))/dVpos, d(F(x))/dVneg and d(F(x))/dx
1359  Sacado::Fad::SFad<double,3> varVpos( 3, 0, v_pos);
1360  Sacado::Fad::SFad<double,3> varVneg( 3, 1, v_neg);
1361  Sacado::Fad::SFad<double,3> varX( 3, 2, x );
1362  Sacado::Fad::SFad<double,3> parami( ri.i0 );
1363  Sacado::Fad::SFad<double,3> paramG( ri.G );
1364  Sacado::Fad::SFad<double,3> paramiOff( ri.model_.iOff_ );
1365  Sacado::Fad::SFad<double,3> paramiOn( ri.model_.iOn_ );
1366 
1367  //Sacado::Fad::SFad<double,3> paramkOff( ri.model_.kOff_ );
1368  //Sacado::Fad::SFad<double,3> paramkOn( ri.model_.kOn_ );
1369  Sacado::Fad::SFad<double,3> paramkOff( ri.model_.kOff_*ri.model_.xScaling_ );
1370  Sacado::Fad::SFad<double,3> paramkOn( ri.model_.kOn_ *ri.model_.xScaling_ );
1371  Sacado::Fad::SFad<double,3> paramAlphaOff( ri.model_.alphaOff_ );
1372  Sacado::Fad::SFad<double,3> paramAlphaOn( ri.model_.alphaOn_ );
1373  Sacado::Fad::SFad<double,3> resultFad;
1374 
1375  xVarFterm(varVpos, varVneg, varX, paramG, paramiOff, paramiOn, paramkOff, paramkOn,
1376  paramAlphaOff, paramAlphaOn, resultFad);
1377 
1378  // calculate the window function value
1379  Sacado::Fad::SFad<double,3> windowFunctionFad;
1380 
1381  if( ri.model_.windowType_ == 0)
1382  {
1383  // no window
1384  windowFunctionFad = 1.0;
1385  }
1386  else if( ri.model_.windowType_ == 1)
1387  {
1388  Sacado::Fad::SFad<double,3> paramD( ri.model_.D_ );
1389  Sacado::Fad::SFad<double,3> paramP( ri.model_.p_ );
1390  JogelkarWindowFunction( varX, paramD, paramP, windowFunctionFad );
1391  }
1392  else if( ri.model_.windowType_ == 2)
1393  {
1394  Sacado::Fad::SFad<double,3> paramD( ri.model_.D_ );
1395  Sacado::Fad::SFad<double,3> paramP( ri.model_.p_ );
1396  BiolekWindowFunction( varX, paramD, paramP, parami, windowFunctionFad );
1397  }
1398  else if( ri.model_.windowType_ == 3)
1399  {
1400  Sacado::Fad::SFad<double,3> paramD( ri.model_.D_ );
1401  Sacado::Fad::SFad<double,3> paramP( ri.model_.p_ );
1402  Sacado::Fad::SFad<double,3> paramJ( ri.model_.j_ );
1403  ProdromakisWindowFunction( varX, paramD, paramP, paramJ, windowFunctionFad );
1404  }
1405  else if( ri.model_.windowType_ == 4)
1406  {
1407  Sacado::Fad::SFad<double,3> paramAOn( ri.model_.aOn_ *ri.model_.xScaling_ );
1408  Sacado::Fad::SFad<double,3> paramAOff( ri.model_.aOff_ *ri.model_.xScaling_ );
1409  Sacado::Fad::SFad<double,3> paramWc( ri.model_.wc_ *ri.model_.xScaling_ );
1410  TEAMWindowFunctionF( varX, parami, paramAOff, paramAOn, paramWc, windowFunctionFad );
1411  }
1412 
1413  // xVarFterm * WindowFunction
1414  // dertivative terms then become: WindowFunction dxVarFterm/dv1 + xVarFterm dW/dv1
1415 
1416  ri.xVarFContribution = resultFad.val()*windowFunctionFad.val();
1417  ri.dxFEqdVpos = windowFunctionFad.val()*resultFad.dx(0);
1418  ri.dxFEqdVneg = windowFunctionFad.val()*resultFad.dx(1);
1419  ri.dxFEqdx = windowFunctionFad.val()*resultFad.dx(2) + resultFad.val()*windowFunctionFad.dx(2) / (ri.model_.xScaling_);
1420 
1421  }
1422 
1423  //ri.G = 1.0/(rfactor*ri.Reff);
1424  //ri.i0 = (v_pos-v_neg)*ri.G;
1425 
1426  }
1427 
1428  return true;
1429 }
1430 
1431 //-----------------------------------------------------------------------------
1432 // Function : Xyce::Device::MemristorTEAM::Master::loadDAEVectors
1433 // Purpose :
1434 // Special Notes :
1435 // Scope : public
1436 // Creator : Richard Schiek, Electrical Models & Simulations
1437 // Creation Date : 10/23/2014
1438 //-----------------------------------------------------------------------------
1439 //
1440 // Load DAE vectors of all memristor instances, regardless of model
1441 //
1442 // @param solVec solution vector
1443 // @param fVec f vector
1444 // @param qVec q vector
1445 // @param storeLeadF store lead current f vector
1446 // @param storeLeadQ store lead current q vector
1447 //
1448 // @return true on success
1449 //
1450 // @note Because the memristor device re-implements the base-class
1451 // Master::loadDAEVectors, the Instance::loadDAEFVector method is
1452 // never called. This method replaces those, and does the same work
1453 // but inside a loop over all memristor instances.
1454 //
1455 // @see Xyce::Device::MemristorTEAM::Instance::loadDAEFVector
1456 //
1457 // @author Eric Keiter, SNL
1458 // @date 11/26/08
1459 
1460 bool Master::loadDAEVectors (double * solVec, double * fVec, double *qVec, double * bVec, double * storeLeadF, double * storeLeadQ, double * leadF, double * leadQ, double * junctionV)
1461 {
1462  for (InstanceVector::const_iterator it = getInstanceBegin(); it != getInstanceEnd(); ++it)
1463  {
1464  Instance & ri = *(*it);
1465  fVec[ri.li_Pos] += ri.i0;
1466  fVec[ri.li_Neg] += -ri.i0;
1467  fVec[ri.li_x] += ri.xVarFContribution;
1468  qVec[ri.li_x] += solVec[ri.li_x];
1469  storeLeadF[ri.li_store_R] = 1.0/ri.G;
1470 
1471  if( ri.loadLeadCurrent )
1472  {
1473  leadF[ri.li_branch_data] = ri.i0;
1474  junctionV[ri.li_branch_data] = solVec[ri.li_Pos] - solVec[ri.li_Neg];
1475  }
1476 
1477  }
1478  return true;
1479 }
1480 
1481 //-----------------------------------------------------------------------------
1482 // Function : Xyce::Device::MemristorTEAM::Master::loadDAEMatrices
1483 // Purpose :
1484 // Special Notes :
1485 // Scope : public
1486 // Creator : Richard Schiek, Electrical Models & Simulations
1487 // Creation Date : 10/23/2014
1488 //-----------------------------------------------------------------------------
1489 //
1490 // Load DAE matrices for all memristor instances, regardless of model
1491 //
1492 // @param dFdx matrix of derivatives of F vector with respect to solution
1493 // @param dQdx matrix of derivatives of Q vector with respect to solution
1494 //
1495 // @return true on success
1496 //
1497 // @note Because the memristor device re-implements the base-class
1498 // Master::loadDAEMatrices, the Instance::loadDAEdFdx method is
1499 // never called. This method replaces those, and does the same work
1500 // but inside a loop over all memristor instances.
1501 //
1502 // @see Xyce::Device::MemristorTEAM::Instance::loadDAEdFdx
1503 //
1504 // @author Eric Keiter, SNL
1505 // @date 11/26/08
1506 
1507 bool Master::loadDAEMatrices(Linear::Matrix & dFdx, Linear::Matrix & dQdx)
1508 {
1509  for (InstanceVector::const_iterator it = getInstanceBegin(); it != getInstanceEnd(); ++it)
1510  {
1511  Instance & ri = *(*it);
1512 
1513 #ifndef Xyce_NONPOINTER_MATRIX_LOAD
1514  *(ri.f_PosEquPosNodePtr) += ri.G;
1515  *(ri.f_PosEquNegNodePtr) -= ri.G;
1516  *(ri.f_PosEquXNodePtr) += ri.dReffdvpos;
1517  *(ri.f_NegEquPosNodePtr) -= ri.G;
1518  *(ri.f_NegEquNegNodePtr) += ri.G;
1519  *(ri.f_NegEquXNodePtr ) += ri.dReffdvneg;
1520  *(ri.f_XEquPosNodePtr ) += ri.dxFEqdVpos;
1521  *(ri.f_XEquNegNodePtr ) += ri.dxFEqdVneg;
1522  *(ri.f_XEquXNodePtr ) += ri.dxFEqdx;
1523  *(ri.q_XEquXNodePtr ) = 1.0;
1524 #else
1525  dFdx[ri.li_Pos][ri.APosEquPosNodeOffset] += ri.G;
1526  dFdx[ri.li_Pos][ri.APosEquNegNodeOffset] -= ri.G;
1527  dFdx[ri.li_Pos][ri.APosEquXNodeOffset] += ri.dReffdvpos;
1528  dFdx[ri.li_Neg][ri.ANegEquPosNodeOffset] -= ri.G;
1529  dFdx[ri.li_Neg][ri.ANegEquNegNodeOffset] += ri.G;
1530  dFdx[ri.li_Neg][ri.ANegEquXNodeOffset] += ri.dReffdvneg;
1531  dFdx[ri.li_x][ri.XEquVPosOffset] += ri.dxFEqdVpos;
1532  dFdx[ri.li_x][ri.XEquVNegOffset] += ri.dxFEqdVneg;
1533  dFdx[ri.li_x][ri.XEquXOffset] += ri.dxFEqdx;
1534  dQdx[ri.li_x][ri.XEquXOffset] = 1.0;
1535 #endif
1536  }
1537 
1538  return true;
1539 }
1540 
1541 //-----------------------------------------------------------------------------
1542 // Function : Xyce::Device::MemristorTEAM::Traits::factory
1543 // Purpose :
1544 // Special Notes :
1545 // Scope : public
1546 // Creator : Richard Schiek, Electrical Models & Simulations
1547 // Creation Date : 10/23/2014
1548 //-----------------------------------------------------------------------------
1549 //
1550 // Create a new instance of the MemristorTEAM device.
1551 //
1552 // @param configuration
1553 // @param factory_block
1554 //
1555 Device *
1556 Traits::factory(const Configuration &configuration, const FactoryBlock &factory_block)
1557 {
1558  return new Master(configuration, factory_block, factory_block.solverState_, factory_block.deviceOptions_);
1559 }
1560 
1561 //-----------------------------------------------------------------------------
1562 // Function : Xyce::Device::MemristorTEAM::registerDevice
1563 // Purpose :
1564 // Special Notes :
1565 // Scope : public
1566 // Creator : Richard Schiek, Electrical Models & Simulations
1567 // Creation Date : 10/23/2014
1568 //-----------------------------------------------------------------------------
1569 //
1570 // Define how to use the device in a netlist.
1571 //
1572 // This method is called from the Xyce::Device::registerOpenDevices
1573 // function, which in turn is called by the device manager.
1574 //
1575 // The device is declared here to be an "memristor" device, which must
1576 // take a model card of type "memristor". This device will correspond to model
1577 // level 4 of memristor models.
1578 void
1580 {
1582  .registerDevice("memristor", 2)
1583  .registerModelType("memristor", 2);
1584 }
1585 
1586 //-----------------------------------------------------------------------------
1587 // Function : memristorTEAMSensitivity::operator
1588 // Purpose : produces df/dp and dq/dp, where p=R.
1589 // Special Notes :
1590 // Scope : public
1591 // Creator : Richard Schiek, Electrical Models & Simulations
1592 // Creation Date : 10/23/2014
1593 //-----------------------------------------------------------------------------
1594 void memristorTEAMSensitivity::operator()(
1595  const ParameterBase &entity,
1596  const std::string & name,
1597  std::vector<double> & dfdp,
1598  std::vector<double> & dqdp,
1599  std::vector<double> & dbdp,
1600  std::vector<int> & Findices,
1601  std::vector<int> & Qindices,
1602  std::vector<int> & Bindices
1603  ) const
1604 {
1605  const ParameterBase * e1 = &entity;
1606  const Instance * in = dynamic_cast<const Instance *> (e1);
1607 
1608  double * solVec = in->extData.nextSolVectorRawPtr;
1609  double v_pos = solVec[in->li_Pos];
1610  double v_neg = solVec[in->li_Neg];
1611 
1612  double dfdpLoc = -(v_pos-v_neg)*in->G*in->G;
1613 
1614  dfdp.resize(2);
1615  dfdp[0] = +dfdpLoc;
1616  dfdp[1] = -dfdpLoc;
1617 
1618  Findices.resize(2);
1619  Findices[0] = in->li_Pos;
1620  Findices[1] = in->li_Neg;
1621 }
1622 
1623 } // namespace MemristorTEAM
1624 } // namespace Device
1625 } // namespace Xyce
const InstanceName & getName() const
const DeviceOptions & deviceOptions_
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
virtual void registerStoreLIDs(const std::vector< int > &stoLIDVecRef)
Pure virtual class to augment a linear system.
void addInternalNode(Util::SymbolTable &symbol_table, int index, const InstanceName &instance_name, const std::string &lead_name)
virtual void registerLIDs(const std::vector< int > &intLIDVecRef, const std::vector< int > &extLIDVecRef)
void setNumStoreVars(int num_store_vars)
void addBranchDataNode(Util::SymbolTable &symbol_table, int index, const InstanceName &instance_name, const std::string &lead_name)
InstanceVector::const_iterator getInstanceEnd() const
Returns an iterator to the ending of the vector of all instances created for this device...
Base class for all parameters.
Definition: N_DEV_Pars.h:169
virtual std::ostream & printOutInstances(std::ostream &os) const
#define AssertLIDs(cmp)
virtual void registerJacLIDs(const JacobianStamp &jacLIDVec)
static void loadInstanceParameters(ParametricData< Instance > &p)
InstanceVector::const_iterator getInstanceBegin() const
Returns an iterator to the beginning of the vector of all instances created for this device...
std::vector< Param > params
Parameters from the line.
void JogelkarWindowFunction(const ScalarT &w, const ScalarT &D, const ScalarT &p, ScalarT &fval)
void setParams(const std::vector< Param > &params)
const std::string & getName() const
The FactoryBlock contains parameters needed by the device, instance and model creation functions...
virtual void registerJacLIDs(const std::vector< std::vector< int > > &jacLIDVec)
void xVarFterm(const ScalarT &Vpos, const ScalarT &Vneg, const ScalarT &x, const ScalarT &G, const ScalarT &iOff, const ScalarT &iOn, const ScalarT &kOff, const ScalarT &kOn, const ScalarT &alphaOff, const ScalarT &alphaOn, ScalarT &fval)
Instance(const Configuration &configuration, const InstanceBlock &instance_block, Model &model, const FactoryBlock &factory_block)
void ProdromakisWindowFunction(const ScalarT &w, const ScalarT &D, const ScalarT &p, const ScalarT &j, ScalarT &fval)
static Config< T > & addConfiguration()
Adds the device to the Xyce device configuration.
Linear::Matrix * dFdxMatrixPtr
void ReffLin(const ScalarT &X, const ScalarT &Ron, const ScalarT &Roff, const ScalarT &Xon, const ScalarT &Xoff, ScalarT &fval)
virtual bool processInstanceParams()
processInstanceParams
The Device class is an interface for device implementations.
Definition: N_DEV_Device.h:101
void addStoreNode(Util::SymbolTable &symbol_table, int index, const InstanceName &instance_name, const std::string &lead_name)
virtual bool updateTemperature(const double &temp_tmp)
const SolverState & solverState_
Class Configuration contains device configuration data.
virtual void registerBranchDataLIDs(const std::vector< int > &branchLIDVecRef)
virtual bool updateState(double *solVec, double *staVec, double *stoVec)
Updates the devices state information.
virtual void forEachInstance(DeviceInstanceOp &op) const
virtual void registerStateLIDs(const std::vector< int > &staLIDVecRef)
void setNumBranchDataVars(int num_branch_data_vars)
void ReffNonLin(const ScalarT &X, const ScalarT &Ron, const ScalarT &Roff, const ScalarT &Xon, const ScalarT &Xoff, ScalarT &fval)
static void loadModelParameters(ParametricData< Model > &p)
#define Xyce_NONPOINTER_MATRIX_LOAD
Definition: N_DEV_Bsrc.C:92
static std::vector< std::vector< int > > jacStamp
void loadNodeSymbols(Util::SymbolTable &symbol_table) const
Populates and returns the store name map.
ModelBlock represents a .MODEL line from the netlist.
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
Linear::Matrix * dQdxMatrixPtr
void BiolekWindowFunction(const ScalarT &w, const ScalarT &D, const ScalarT &p, const ScalarT &i, ScalarT &fval)
void TEAMWindowFunctionF(const ScalarT &x, const ScalarT &i, const ScalarT &aOff, ScalarT &aOn, const ScalarT &wc, ScalarT &fval)
virtual bool processParams()
processParams
void setModParams(const std::vector< Param > &params)