Xyce  6.1
N_DEV_DeviceModel.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_DeviceModel.C,v $
27 //
28 // Purpose :
29 //
30 // Special Notes :
31 //
32 // Creator : Eric R. Keiter, SNL, Parallel Computational Sciences
33 //
34 // Creation Date : 04/03/00
35 //
36 // Revision Information:
37 // ---------------------
38 //
39 // Revision Number: $Revision: 1.72.2.1 $
40 //
41 // Revision Date : $Date: 2015/04/02 18:20:09 $
42 //
43 // Current Owner : $Author: tvrusso $
44 //-------------------------------------------------------------------------
45 
46 #include <Xyce_config.h>
47 
48 #include <iostream>
49 #include <set>
50 #if defined(HAVE_UNORDERED_MAP)
51 #include <unordered_map>
52 using std::unordered_map;
53 #elif defined(HAVE_TR1_UNORDERED_MAP)
54 #include <tr1/unordered_map>
55 using std::tr1::unordered_map;
56 #else
57 #error neither unordered_map or tr1/unordered_map found
58 #endif
59 
60 #include <N_DEV_Const.h>
61 #include <N_DEV_DeviceModel.h>
62 #include <N_DEV_DeviceBlock.h>
63 #include <N_DEV_DeviceOptions.h>
64 #include <N_DEV_SolverState.h>
65 #include <N_DEV_Param.h>
66 #include <N_DEV_Message.h>
67 #include <N_DEV_Configuration.h>
68 
69 #include <N_ERH_ErrorMgr.h>
70 
71 namespace Xyce {
72 namespace Device {
73 
74 //-----------------------------------------------------------------------------
75 // Function : DeviceModel::DeviceModel
76 // Purpose : model block constructor
77 // Special Notes :
78 // Scope : public
79 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
80 // Creation Date : 3/30/00
81 //-----------------------------------------------------------------------------
83  const ModelBlock & model_block,
84  ParametricData<void> & parametric_data,
85  const FactoryBlock & factory_block)
86  : DeviceEntity(parametric_data, factory_block.solverState_, factory_block.deviceOptions_, model_block.getNetlistLocation().getFilename(), model_block.getNetlistLocation().getLineNumber()),
87  name_(model_block.getName()),
88  type_(model_block.getType()),
89  level_(model_block.getLevel()),
90  temperatureModel(""),
91  doseModel(""),
92  iModel(TEMP),
93  iMethod(QUAD),
94  base_temp(CONSTREFTEMP)
95 {}
96 
97 std::ostream &
98 DeviceModel::printName(std::ostream &os) const
99 {
100  return os << "model " << name_;
101 }
102 
103 //-----------------------------------------------------------------------------
104 // Function : DeviceModel::setModParams
105 // Purpose : Set up parameter fits from model line
106 // Special Notes :
107 //
108 // Scope : public
109 // Creator : Dave Shirley, PSSI
110 // Creation Date : 8/26/05
111 //-----------------------------------------------------------------------------
112 void DeviceModel::setModParams(const std::vector<Param> &params)
113 {
114  std::vector<int> m_start;
115  std::set<std::string> pname;
116  unordered_map<std::string, int> ptype;
117 
118  int param_index = 0;
119  m_start.push_back(param_index);
120  for (std::vector<Param>::const_iterator mp = params.begin(); mp != params.end(); ++mp)
121  {
122  ++param_index;
123  if ((*mp).tag() == "INDEPENDENT;PARAM")
124  {
125  m_start.push_back(param_index);
126  pname.clear();
127  }
128  else
129  {
130  if (pname.find((*mp).tag()) != pname.end())
131  {
132  UserError0(*this) << "Duplicate specification of parameter " << (*mp).tag();
133  return;
134  }
135  pname.insert((*mp).tag());
136  if (m_start.size() == 1)
137  {
138  ptype[(*mp).tag()] = (*mp).getType();
139  }
140  }
141  }
142  if (m_start.size() == 1)
143  {
144  setParams(params);
145  }
146  else
147  {
148  m_start.push_back(param_index + 1);
149 
150  // An interpolation method is present, first figure out what it
151  // is and make sure that all models agree on the method.
152  std::string tmod("");
153  std::string dmod("");
154  std::string modName("");
155 
156  for (int i = 0; i < m_start.size()-1; ++i)
157  {
158  for (int j = m_start[i]; j < m_start[i+1]-1; ++j)
159  {
160  if (params[j].tag() == "TEMPMODEL" && params[j].stringValue() != "NONE")
161  {
162  if (i == 0)
163  {
164  tmod = params[j].stringValue();
165  }
166  else
167  {
168  if (tmod != params[j].stringValue())
169  {
170  UserError0(*this) << "Inconsistent or missing TEMPMODEL parameter, " << params[j].stringValue() << " specified here, " << tmod << " specified perviously";
171  return;
172  }
173  }
174  }
175  if (params[j].tag() == "DOSEMODEL" && params[j].stringValue() != "NONE")
176  {
177  if (i == 0)
178  {
179  dmod = params[j].stringValue();
180  }
181  else
182  {
183  if (dmod != params[j].stringValue())
184  {
185  UserError0(*this) << "Inconsistent or missing DOSEMODEL parameter, " << params[j].stringValue() << " specified here, " << dmod << " specified perviously";
186  return;
187  }
188  }
189  }
190  }
191  }
192 
193  if (tmod == "" && dmod == "")
194  {
195  UserError0(*this) << "Duplicate model specification implies parameter interpolation, TEMPMODEL or DOSEMODEL parameters must specified";
196  return;
197  }
198  else if (tmod != "" && dmod != "")
199  {
200  UserError0(*this) << "Only one of TEMPMODEL or DOSEMODEL parameters may be specified";
201  return;
202  }
203  else if (tmod != "")
204  {
205  modName = tmod;
206  iModel = TEMP;
207  }
208  else if (dmod != "")
209  {
210  modName = dmod;
211  iModel = DOSE;
212  }
213 
214  if (modName == "QUADRATIC")
215  {
216  iMethod = QUAD;
217  if (m_start.size() != 4)
218  {
219  UserError0(*this) << "Three model specifications required for QUADRATIC fit";
220  return;
221  }
222  fit.resize(3);
223  }
224  else if (modName == "PWL")
225  {
226  iMethod = PWL;
227  fit.resize(m_start.size() - 1);
228  }
229  else
230  {
231  UserError0(*this) << "Only QUADRATIC or PWL interpolation method is supported";
232  return;
233  }
234 
235  // First find the params that vary between the specified models:
236  unordered_map<std::string,double> basePars;
237  std::vector<double> t;
238  std::string par;
239 
240  for (int i = 0 ;i < m_start.size()-1; ++i)
241  {
242  for (int j = m_start[i]; j < m_start[i+1]-1; ++j)
243  {
244  par = params[j].tag();
245  ParameterMap::const_iterator parIt = getParameterMap().find(par);
246  if (parIt == getParameterMap().end())
247  DevelFatal0(*this).in("DeviceModel::setModParams") << "Parameter " << par << " not found";
248 
249  const Descriptor &nPar = *(*parIt).second;
250  if (params[j].given() && nPar.isType<double>())
251  {
252  if (iModel == TEMP)
253  {
254  if (par == "TNOM")
255  {
256  t.push_back(params[j].getImmutableValue<double>());
257  }
258  }
259  else if (iModel == DOSE)
260  {
261  if (par == "DOSE")
262  {
263  t.push_back(params[j].getImmutableValue<double>());
264  }
265  }
266  if (i == 0)
267  {
268  if (params[j].getType() != Util::EXPR)
269  {
270  basePars[par] = params[j].getImmutableValue<double>();
271  }
272  }
273  else
274  {
275  if (ptype[par] == Util::EXPR)
276  {
277  UserWarning0(*this) << "Non-constant expression for parameter " << par << ", it will not interpolated";
278  }
279  else
280  {
281  if (basePars.find(par) == basePars.end())
282  {
283  UserError0(*this) << "Unknown parameter " << params[j].tag() << " in temperature compensation .MODEL statement";
284  return;
285  }
286  if (basePars[par] != params[j].getImmutableValue<double>() && params[j].given())
287  {
288  if (fitMap.find(par) == fitMap.end())
289  {
290  fitMap[par] = fit[0].size();
291  fitParams.push_back(nPar.getMemberPtr<double>());
292  fit[0].push_back(basePars[par]);
293  }
294  }
295  }
296  }
297  }
298  }
299  }
300 
301  if (t.size() != m_start.size()-1)
302  {
303  UserError0(*this) << (iModel == TEMP ? "TNOM" : "DOSE") << " not specified in all .MODEL statements";
304  return;
305  }
306 
307  for (int i = 1; i < t.size(); ++i)
308  {
309  for (int j = 0; j < i; ++j)
310  {
311  if (t[i] == t[j])
312  {
313  UserError0(*this) << "Identical " << (iModel == TEMP ? "TNOM" : "DOSE") << " values in .MODEL statements";
314  return;
315  }
316  }
317  }
318 
319  // Now, collect the values to use for the fits:
320  int nFit = fitMap.size();
321  //int nSet = t.size();
322  std::vector<std::vector<double> > temp(nFit);
323  std::vector<std::vector<double> > val(nFit);
324  oldParams.resize(nFit);
325 
326  for (int i = 1; i < fit.size(); ++i)
327  {
328  fit[i].resize(nFit);
329  }
330  min_par.resize(nFit);
331  max_par.resize(nFit);
332  parType.resize(nFit);
333  std::map<std::string, int>::iterator fm = fitMap.begin();
334  std::map<std::string, int>::iterator fm_end = fitMap.end();
335 
336  for ( ; fm != fm_end ; ++fm)
337  {
338  par = (*fm).first;
339  ParameterMap::const_iterator parIt = getParameterMap().find(par);
340  if (parIt == getParameterMap().end())
341  throw std::runtime_error(std::string("Parameter ") + par + " not found");
342  {
343  const Descriptor &nPar = *(*parIt).second;
344 
346  {
347  parType[fitMap[par]] = LOG_FIT;
348  }
349  else
350  {
351  parType[fitMap[par]] = LINEAR_FIT;
352  }
353  }
354  }
355 
356  base_temp = t[0];
357  if (iModel == TEMP)
358  {
359  base_temp += CONSTCtoK;
360  }
361 
362  for (int i = 0; i < nFit; ++i)
363  {
364  temp[i].push_back(t[0]);
365  val[i].push_back(fit[0][i]);
366  if (parType[i] == LOG_FIT)
367  {
368  if (val[i][0] <= 0)
369  {
370  UserError0 message(*this);
371  message << "Negative parameter value for log interpolation based parameter ";
372  fm = fitMap.begin();
373  for ( ; fm != fm_end ; ++fm)
374  {
375  if ((*fm).second == i)
376  {
377  par = (*fm).first;
378  break;
379  }
380  }
381  message << par;
382  return;
383  }
384  val[i][0] = log(val[i][0]);
385  }
386  min_par[i] = val[i][0];
387  max_par[i] = val[i][0];
388  }
389 
390  for (int i = 1; i < m_start.size()-1; ++i)
391  {
392  for (int j = m_start[i]; j < m_start[i+1]-1; ++j)
393  {
394  if (params[j].getType() == Util::DBLE && params[j].given())
395  {
396  par = params[j].tag();
397  std::map<std::string, int>::iterator fm1 = fitMap.find(par);
398  if (fm1 != fitMap.end())
399  {
400  int k = fm1->second;
401  temp[k].push_back(t[i]);
402  double p = params[j].getImmutableValue<double>();
403  if (parType[k] == LOG_FIT)
404  {
405  if (p <= 0)
406  {
407  UserError0 message(*this);
408  message << "Negative parameter value for log interpolation based parameter ";
409  fm = fitMap.begin();
410  for ( ; fm != fm_end ; ++fm)
411  {
412  if ((*fm).second == k)
413  {
414  par = (*fm).first;
415  break;
416  }
417  }
418  message << par;
419  return;
420  }
421  p = log(p);
422  }
423  val[k].push_back(p);
424  if (p > max_par[k])
425  max_par[k] = p;
426  if (p < min_par[k])
427  min_par[k] = p;
428  }
429  }
430  }
431  }
432 
433  // Finally, do the actual fits:
434  if (fitMap.size() > 0)
435  {
436  if (iMethod == QUAD)
437  {
438  std::map<std::string, int>::iterator f;
439  for (f=fitMap.begin() ; f!=fitMap.end() ; ++f)
440  {
441  int i = (*f).second;
442  if (temp[i].size() == 2)
443  {
444  fit[0][i] = val[i][0];
445  fit[1][i] = (val[i][1] - val[i][0])/(temp[i][1] - temp[i][0]);
446  fit[2][i] = 0;
447  }
448  else if (temp[i].size() == 3)
449  {
450  fit[0][i] = val[i][0];
451  double x1,x2,y1,y2;
452  x1 = temp[i][1] - temp[i][0];
453  y1 = val[i][1];
454  x2 = temp[i][2] - temp[i][0];
455  y2 = val[i][2];
456  fit[2][i] = (x2*y1-x1*y2-fit[0][i]*(x2-x1))/(x2*x1*x1-x1*x2*x2);
457  fit[1][i] = (y1-fit[2][i]*x1*x1-fit[0][i])/x1;
458  }
459  else
460  {
461  DevelFatal0(*this).in("setModParams") << "Internal error in DeviceModel, illegal number of fit points for parameter " << (*f).first;
462  }
463  if ((*f).first == "TNOM")
464  {
465  fit[0][i] += CONSTCtoK;
466  }
467  }
468  }
469  else if (iMethod == PWL)
470  {
471  int nT = fit.size();
472  std::map<double,int> tOrd;
473  for (int i = 0; i < nT; ++i)
474  {
475  tOrd[t[i]] = 0;
476  }
477  int i = 0;
478  std::map<double,int>::iterator tOrd_i = tOrd.begin();
479  std::map<double,int>::iterator tOrd_end = tOrd.end();
480  for ( ; tOrd_i != tOrd_end; ++tOrd_i)
481  {
482  if (iModel == TEMP)
483  {
484  base.push_back((*tOrd_i).first+CONSTCtoK);
485  }
486  else
487  {
488  base.push_back((*tOrd_i).first);
489  }
490  (*tOrd_i).second = i++;
491  }
492  std::map<std::string, int>::iterator f;
493  std::map<std::string, int>::iterator f_end;
494  std::vector<bool> p(nT,false);
495  f=fitMap.begin();
496  f_end=fitMap.end();
497  for ( ; f!=f_end; ++f)
498  {
499  i = (*f).second;
500  for (int j = 0 ;j < nT; ++j)
501  {
502  p[j] = false;
503  }
504  for (int j = 0; j < temp[i].size() ; ++j)
505  {
506  int ind = tOrd[temp[i][j]];
507  p[ind] = true;
508  fit[ind][i] = val[i][j];
509  }
510  for (int j = 0; j < nT ; ++j)
511  {
512  if (!p[j])
513  {
514  int k_lo = j;
515  int k_hi = j;
516  while (k_lo >= 0 && !p[k_lo])
517  {
518  k_lo--;
519  }
520  while (k_hi <nT && !p[k_hi])
521  {
522  ++k_hi;
523  }
524  if (k_lo == -1)
525  {
526  if (k_hi < nT)
527  {
528  fit[j][i] = fit[k_hi][i];
529  }
530  else
531  {
532  DevelFatal0(*this).in("DeviceModel::setModParams") <<"DeviceModel::setModParams: Internal error forming PWL interpolation";
533  }
534  }
535  else
536  {
537  if (k_hi < nT)
538  {
539  double frac = (base[j]-base[k_lo])/(base[k_hi]-base[k_lo]);
540  fit[j][i] = fit[k_hi][i]*frac+fit[k_lo][i]*(1-frac);
541  }
542  else
543  {
544  fit[j][i] = fit[k_lo][i];
545  }
546  }
547  }
548  if ((*f).first == "TNOM")
549  {
550  fit[j][i] += CONSTCtoK;
551  }
552  }
553  }
554  }
555  }
556 
557  // params.resize(m_start[1]-1);
558  // setParams(params);
559  std::vector<Param> remaining_params(&params[0], &params[m_start[1] - 1]);
560  setParams(remaining_params);
561  }
562 }
563 
564 //-----------------------------------------------------------------------------
565 // Function : DeviceModel::~DeviceModel
566 // Purpose : destructor
567 // Special Notes :
568 // Scope : public
569 // Creator : Eric Keiter, SNL, Parallel Computational Sciences
570 // Creation Date : 3/30/00
571 //-----------------------------------------------------------------------------
573 {
574 }
575 
576 //-----------------------------------------------------------------------------
577 // Function : DeviceModel::saveParams
578 // Purpose : save existing param values before fitting params to temperature
579 // Special Notes :
580 // Scope : public
581 // Creator : Dave Shirley, PSSI
582 // Creation Date : 8/29/05
583 //-----------------------------------------------------------------------------
585 {
586  int nFit = fitMap.size();
587  int i;
588 
589  if (nFit == 0)
590  {
591  return;
592  }
593 
594  for (i=0 ; i<nFit ; ++i)
595  {
596  oldParams[i] = this->*(fitParams[i]);
597  }
598 }
599 
600 //-----------------------------------------------------------------------------
601 // Function : DeviceModel::interpolateTNOM
602 // Purpose : interpolate param values to a specified temperature
603 // Special Notes :
604 // Scope : public
605 // Creator : Dave Shirley, PSSI
606 // Creation Date : 8/29/05
607 //-----------------------------------------------------------------------------
609 {
610  if (iModel != TEMP)
611  {
612  return false;
613  }
614 
615  return interpolate(t);
616 }
617 
618 //-----------------------------------------------------------------------------
619 // Function : DeviceModel::interpolateDOSE
620 // Purpose : interpolate param values to a specified temperature
621 // Special Notes :
622 // Scope : public
623 // Creator : Dave Shirley, PSSI
624 // Creation Date : 11/11/05
625 //-----------------------------------------------------------------------------
627 {
628  if (iModel != DOSE)
629  {
630  return false;
631  }
632 
633  return interpolate(d);
634 }
635 
636 //-----------------------------------------------------------------------------
637 // Function : DeviceModel::interpolated
638 // Purpose : returns true if an interpolation is in effect
639 // Special Notes :
640 // Scope : private
641 // Creator : Dave Shirley, PSSI
642 // Creation Date : 1/31/06
643 //-----------------------------------------------------------------------------
645 {
646  return (fitMap.size() > 0);
647 }
648 
649 //-----------------------------------------------------------------------------
650 // Function : DeviceModel::interpolate
651 // Purpose : interpolate param values to a specified temperature
652 // Special Notes :
653 // Scope : private
654 // Creator : Dave Shirley, PSSI
655 // Creation Date : 11/11/05
656 //-----------------------------------------------------------------------------
658 {
659  int nFit = fitMap.size();
660  int i, j, k_hi, k_lo;
661  double del;
662  double frac, p;
663 
664  if (nFit == 0)
665  {
666  return false;
667  }
668 
669  if (iMethod == QUAD)
670  {
671  del = t - base_temp;
672  // for (i=0 ; i<nFit ; ++i)
673  std::map<std::string,int>::iterator fp;
674  std::map<std::string,int>::iterator fm_begin=fitMap.begin();
675  std::map<std::string,int>::iterator fm_end=fitMap.end();
676  for (fp=fm_begin; fp != fm_end; fp++)
677  {
678  i=fp->second;
679  p = (fit[2][i]*del + fit[1][i])*del + fit[0][i];
680 
681  if (p > max_par[i] && i>0)
682  {
683  this->*(fitParams[i]) = max_par[i];
684  }
685  else if (p < min_par[i] && i>0)
686  {
687  this->*(fitParams[i]) = min_par[i];
688  }
689  else
690  {
691  this->*(fitParams[i]) = p;
692  }
693  }
694  }
695  else if (iMethod == PWL)
696  {
697  del = t;
698  k_hi = 0;
699  for (j=0 ; j<fit.size() ; ++j)
700  {
701  if (base[j] >= del)
702  {
703  break;
704  }
705  k_hi = j+1;
706  }
707  if (k_hi == 0)
708  {
709  frac = 0;
710  }
711  else if (k_hi == fit.size())
712  {
713  k_hi = fit.size()-1;
714  frac = 1;
715  }
716  else
717  {
718  k_lo = k_hi-1;
719  frac = (del-base[k_lo])/(base[k_hi]-base[k_lo]);
720  }
721  if (frac == 1)
722  {
723  for (i=0 ; i<nFit ; ++i)
724  {
725  this->*(fitParams[i]) = fit[k_hi][i];
726  }
727  }
728  else
729  {
730  for (i=0 ; i<nFit ; ++i)
731  this->*(fitParams[i]) = fit[k_hi][i]*frac + fit[k_lo][i]*(1-frac);
732  }
733  }
734  for (i=0 ; i<nFit ; ++i)
735  {
736  if (parType[i] == LOG_FIT)
737  {
738  this->*(fitParams[i]) = exp(this->*(fitParams[i]));
739  }
740  }
741 
742  return true;
743 }
744 
745 //-----------------------------------------------------------------------------
746 // Function : DeviceModel::restoreParams
747 // Purpose : restore previously saved param values
748 // Special Notes :
749 // Scope : public
750 // Creator : Dave Shirley, PSSI
751 // Creation Date : 8/29/05
752 //-----------------------------------------------------------------------------
754 {
755  int nFit = fitMap.size();
756  int i;
757 
758  if (nFit == 0)
759  {
760  return;
761  }
762 
763  for (i=0 ; i<nFit ; ++i)
764  {
765  this->*(fitParams[i]) = oldParams[i];
766  }
767 }
768 
769 } // namespace Device
770 } // namespace Xyce
std::vector< double > oldParams
std::vector< double > max_par
#define CONSTCtoK
Definition: N_DEV_Const.h:52
#define CONSTREFTEMP
Definition: N_DEV_Const.h:56
bool given(const std::string &parameter_name) const
Pure virtual class to augment a linear system.
std::vector< std::vector< double > > fit
const std::string & getName(const C *c)
Returns the name of the specified object.
void setParams(const std::vector< Param > &params)
const ParameterMap & getParameterMap() const
The FactoryBlock contains parameters needed by the device, instance and model creation functions...
std::vector< double > base
Class ParametricData manages the configuration information and the parameter binding map...
Definition: N_DEV_Pars.h:1303
ExprAccess getExpressionAccess() const
Gets the expression access which describes the usage of the paramter.
Definition: N_DEV_Pars.h:659
Parameter uses temperature interpolation based on log of value.
Definition: N_DEV_Pars.h:69
Class Descriptor describes the parameters stored in the ParametricData parameter map.
Definition: N_DEV_Pars.h:546
std::vector< fitType > parType
virtual std::ostream & printName(std::ostream &os) const
const std::string & getType() const
bool isType() const
Tests entry data type.
Definition: N_DEV_Pars.h:597
std::vector< double > min_par
ModelBlock represents a .MODEL line from the netlist.
T ParameterBase::* getMemberPtr() const
Returns the parameter member variable pointer of the enrtry.
Definition: N_DEV_Pars.h:905
std::map< std::string, int > fitMap
std::vector< double DeviceEntity::* > fitParams
void setModParams(const std::vector< Param > &params)