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