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