KiCad PCB EDA Suite
ngspice.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2016-2018 CERN
5  * Copyright (C) 2018 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
8  * @author Maciej Suminski <maciej.suminski@cern.ch>
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 3
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, you may find one here:
22  * https://www.gnu.org/licenses/gpl-3.0.html
23  * or you may search the http://www.gnu.org website for the version 3 license,
24  * or you may write to the Free Software Foundation, Inc.,
25  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
26  */
27 
28 #include <config.h> // Needed for MSW compilation
29 
30 #include "ngspice.h"
31 #include "spice_reporter.h"
32 
33 #include <common.h>
34 
35 #include <wx/stdpaths.h>
36 #include <wx/dir.h>
37 
38 #include <stdexcept>
39 
40 #include <algorithm>
41 
42 using namespace std;
43 
44 static const wxChar* const traceNgspice = wxT( "KICAD_NGSPICE" );
45 
47  : m_ngSpice_Init( nullptr ),
48  m_ngSpice_Circ( nullptr ),
49  m_ngSpice_Command( nullptr ),
50  m_ngGet_Vec_Info( nullptr ),
51  m_ngSpice_CurPlot( nullptr ),
52  m_ngSpice_AllPlots( nullptr ),
53  m_ngSpice_AllVecs( nullptr ),
54  m_ngSpice_Running( nullptr ),
55  m_error( false )
56 {
57  init_dll();
58 }
59 
60 
62 {
63 }
64 
65 
67 {
68  Command( "reset" );
69 }
70 
71 
72 vector<string> NGSPICE::AllPlots()
73 {
74  LOCALE_IO c_locale; // ngspice works correctly only with C locale
75  char* currentPlot = m_ngSpice_CurPlot();
76  char** allPlots = m_ngSpice_AllVecs( currentPlot );
77  int noOfPlots = 0;
78 
79  if( allPlots != nullptr )
80  for( char** plot = allPlots; *plot != nullptr; plot++ )
81  noOfPlots++;
82 
83  vector<string> retVal( noOfPlots );
84  for( int i = 0; i < noOfPlots; i++, allPlots++ )
85  {
86  string vec = *allPlots;
87  retVal.at( i ) = vec;
88  }
89 
90  return retVal;
91 }
92 
93 
94 vector<COMPLEX> NGSPICE::GetPlot( const string& aName, int aMaxLen )
95 {
96  LOCALE_IO c_locale; // ngspice works correctly only with C locale
97  vector<COMPLEX> data;
98  vector_info* vi = m_ngGet_Vec_Info( (char*) aName.c_str() );
99 
100  if( vi )
101  {
102  int length = aMaxLen < 0 ? vi->v_length : std::min( aMaxLen, vi->v_length );
103  data.reserve( length );
104 
105  if( vi->v_realdata )
106  {
107  for( int i = 0; i < length; i++ )
108  data.emplace_back( vi->v_realdata[i], 0.0 );
109  }
110  else if( vi->v_compdata )
111  {
112  for( int i = 0; i < length; i++ )
113  data.emplace_back( vi->v_compdata[i].cx_real, vi->v_compdata[i].cx_imag );
114  }
115  }
116 
117  return data;
118 }
119 
120 
121 vector<double> NGSPICE::GetRealPlot( const string& aName, int aMaxLen )
122 {
123  LOCALE_IO c_locale; // ngspice works correctly only with C locale
124  vector<double> data;
125  vector_info* vi = m_ngGet_Vec_Info( (char*) aName.c_str() );
126 
127  if( vi )
128  {
129  int length = aMaxLen < 0 ? vi->v_length : std::min( aMaxLen, vi->v_length );
130  data.reserve( length );
131 
132  if( vi->v_realdata )
133  {
134  for( int i = 0; i < length; i++ )
135  {
136  data.push_back( vi->v_realdata[i] );
137  }
138  }
139  else if( vi->v_compdata )
140  {
141  for( int i = 0; i < length; i++ )
142  {
143  assert( vi->v_compdata[i].cx_imag == 0.0 );
144  data.push_back( vi->v_compdata[i].cx_real );
145  }
146  }
147  }
148 
149  return data;
150 }
151 
152 
153 vector<double> NGSPICE::GetImagPlot( const string& aName, int aMaxLen )
154 {
155  LOCALE_IO c_locale; // ngspice works correctly only with C locale
156  vector<double> data;
157  vector_info* vi = m_ngGet_Vec_Info( (char*) aName.c_str() );
158 
159  if( vi )
160  {
161  int length = aMaxLen < 0 ? vi->v_length : std::min( aMaxLen, vi->v_length );
162  data.reserve( length );
163 
164  if( vi->v_compdata )
165  {
166  for( int i = 0; i < length; i++ )
167  {
168  data.push_back( vi->v_compdata[i].cx_imag );
169  }
170  }
171  }
172 
173  return data;
174 }
175 
176 
177 vector<double> NGSPICE::GetMagPlot( const string& aName, int aMaxLen )
178 {
179  LOCALE_IO c_locale; // ngspice works correctly only with C locale
180  vector<double> data;
181  vector_info* vi = m_ngGet_Vec_Info( (char*) aName.c_str() );
182 
183  if( vi )
184  {
185  int length = aMaxLen < 0 ? vi->v_length : std::min( aMaxLen, vi->v_length );
186  data.reserve( length );
187 
188  if( vi->v_realdata )
189  {
190  for( int i = 0; i < length; i++ )
191  data.push_back( vi->v_realdata[i] );
192  }
193  else if( vi->v_compdata )
194  {
195  for( int i = 0; i < length; i++ )
196  data.push_back( hypot( vi->v_compdata[i].cx_real, vi->v_compdata[i].cx_imag ) );
197  }
198  }
199 
200  return data;
201 }
202 
203 
204 vector<double> NGSPICE::GetPhasePlot( const string& aName, int aMaxLen )
205 {
206  LOCALE_IO c_locale; // ngspice works correctly only with C locale
207  vector<double> data;
208  vector_info* vi = m_ngGet_Vec_Info( (char*) aName.c_str() );
209 
210  if( vi )
211  {
212  int length = aMaxLen < 0 ? vi->v_length : std::min( aMaxLen, vi->v_length );
213  data.reserve( length );
214 
215  if( vi->v_realdata )
216  {
217  for( int i = 0; i < length; i++ )
218  data.push_back( 0.0 ); // well, that's life
219  }
220  else if( vi->v_compdata )
221  {
222  for( int i = 0; i < length; i++ )
223  data.push_back( atan2( vi->v_compdata[i].cx_imag, vi->v_compdata[i].cx_real ) );
224  }
225  }
226 
227  return data;
228 }
229 
230 
231 bool NGSPICE::LoadNetlist( const string& aNetlist )
232 {
233  LOCALE_IO c_locale; // ngspice works correctly only with C locale
234  vector<char*> lines;
235  stringstream ss( aNetlist );
236 
237  m_netlist = "";
238 
239  while( !ss.eof() )
240  {
241  char line[1024];
242  ss.getline( line, sizeof( line ) );
243  lines.push_back( strdup( line ) );
244  m_netlist += std::string( line ) + std::string( "\n" );
245  }
246 
247  lines.push_back( nullptr ); // sentinel, as requested in ngSpice_Circ description
248  m_ngSpice_Circ( lines.data() );
249 
250  for( auto line : lines )
251  free( line );
252 
253  return true;
254 }
255 
256 
258 {
259  LOCALE_IO c_locale; // ngspice works correctly only with C locale
260  return Command( "bg_run" ); // bg_* commands execute in a separate thread
261 }
262 
263 
265 {
266  LOCALE_IO c_locale; // ngspice works correctly only with C locale
267  return Command( "bg_halt" ); // bg_* commands execute in a separate thread
268 }
269 
270 
272 {
273  LOCALE_IO c_locale; // ngspice works correctly only with C locale
274  return m_ngSpice_Running();
275 }
276 
277 
278 bool NGSPICE::Command( const string& aCmd )
279 {
280  LOCALE_IO c_locale; // ngspice works correctly only with C locale
281  validate();
282  m_ngSpice_Command( (char*) aCmd.c_str() );
283  return true;
284 }
285 
286 
287 string NGSPICE::GetXAxis( SIM_TYPE aType ) const
288 {
289  switch( aType )
290  {
291  case ST_AC:
292  case ST_NOISE:
293  return string( "frequency" );
294  break;
295 
296  case ST_DC:
297  return string( "v-sweep" );
298  break;
299 
300  case ST_TRANSIENT:
301  return string( "time" );
302  break;
303 
304  default:
305  break;
306  }
307 
308  return string( "" );
309 }
310 
311 
313 {
314  if( m_initialized )
315  return;
316 
317  LOCALE_IO c_locale; // ngspice works correctly only with C locale
318  const wxStandardPaths& stdPaths = wxStandardPaths::Get();
319 
320  if( m_dll.IsLoaded() ) // enable force reload
321  m_dll.Unload();
322 
323 // Extra effort to find libngspice
324  wxFileName dllFile( "", NGSPICE_DLL_FILE );
325 #if defined(__WINDOWS__)
326  const vector<string> dllPaths = { "", "/mingw64/bin", "/mingw32/bin" };
327 #elif defined(__WXMAC__)
328  const vector<string> dllPaths = {
329  GetOSXKicadUserDataDir() + "/PlugIns/ngspice",
330  GetOSXKicadMachineDataDir() + "/PlugIns/ngspice",
331  // when running kicad.app
332  stdPaths.GetPluginsDir() + "/sim",
333  // when running eeschema.app
334  wxFileName( stdPaths.GetExecutablePath() ).GetPath() + "/../../../../../Contents/PlugIns/sim"
335  };
336 #else // Unix systems
337  const vector<string> dllPaths = { "/usr/local/lib" };
338 #endif
339 
340 #if defined(__WINDOWS__) || (__WXMAC__)
341  for( const auto& path : dllPaths )
342  {
343  dllFile.SetPath( path );
344  wxLogTrace( traceNgspice, "libngspice search path: %s", dllFile.GetFullPath() );
345  m_dll.Load( dllFile.GetFullPath(), wxDL_VERBATIM | wxDL_QUIET | wxDL_NOW );
346 
347  if( m_dll.IsLoaded() )
348  {
349  wxLogTrace( traceNgspice, "libngspice path found in: %s", dllFile.GetFullPath() );
350  break;
351  }
352  }
353 
354  if( !m_dll.IsLoaded() ) // try also the system libraries
355  m_dll.Load( wxDynamicLibrary::CanonicalizeName( "ngspice" ) );
356 
357 #else // here: __LINUX__
358  // First, try the system libraries
359  m_dll.Load( NGSPICE_DLL_FILE, wxDL_VERBATIM | wxDL_QUIET | wxDL_NOW );
360 
361  // If failed, try some other paths:
362  if( !m_dll.IsLoaded() )
363  {
364  for( const auto& path : dllPaths )
365  {
366  dllFile.SetPath( path );
367  wxLogTrace( traceNgspice, "libngspice search path: %s", dllFile.GetFullPath() );
368  m_dll.Load( dllFile.GetFullPath(), wxDL_VERBATIM | wxDL_QUIET | wxDL_NOW );
369 
370  if( m_dll.IsLoaded() )
371  {
372  wxLogTrace( traceNgspice, "libngspice path found in: %s", dllFile.GetFullPath() );
373  break;
374  }
375  }
376  }
377 #endif
378 
379  if( !m_dll.IsLoaded() )
380  throw std::runtime_error( "Missing ngspice shared library" );
381 
382  m_error = false;
383 
384  // Obtain function pointers
385  m_ngSpice_Init = (ngSpice_Init) m_dll.GetSymbol( "ngSpice_Init" );
386  m_ngSpice_Circ = (ngSpice_Circ) m_dll.GetSymbol( "ngSpice_Circ" );
387  m_ngSpice_Command = (ngSpice_Command) m_dll.GetSymbol( "ngSpice_Command" );
388  m_ngGet_Vec_Info = (ngGet_Vec_Info) m_dll.GetSymbol( "ngGet_Vec_Info" );
389  m_ngSpice_CurPlot = (ngSpice_CurPlot) m_dll.GetSymbol( "ngSpice_CurPlot" );
390  m_ngSpice_AllPlots = (ngSpice_AllPlots) m_dll.GetSymbol( "ngSpice_AllPlots" );
391  m_ngSpice_AllVecs = (ngSpice_AllVecs) m_dll.GetSymbol( "ngSpice_AllVecs" );
392  m_ngSpice_Running = (ngSpice_Running) m_dll.GetSymbol( "ngSpice_running" ); // it is not a typo
393 
395 
396  // Load a custom spinit file, to fix the problem with loading .cm files
397  // Switch to the executable directory, so the relative paths are correct
398  wxString cwd( wxGetCwd() );
399  wxFileName exeDir( stdPaths.GetExecutablePath() );
400  wxSetWorkingDirectory( exeDir.GetPath() );
401 
402  // Find *.cm files
403  string cmPath = findCmPath();
404 
405  // __CMPATH is used in custom spinit file to point to the codemodels directory
406  if( !cmPath.empty() )
407  Command( "set __CMPATH=\"" + cmPath + "\"" );
408 
409  // Possible relative locations for spinit file
410  const vector<string> spiceinitPaths =
411  {
412  ".",
413 #ifdef __WXMAC__
414  stdPaths.GetPluginsDir() + "/sim/ngspice/scripts",
415  wxFileName( stdPaths.GetExecutablePath() ).GetPath() + "/../../../../../Contents/PlugIns/sim/ngspice/scripts"
416 #endif /* __WXMAC__ */
417  "../share/kicad",
418  "../share",
419  "../../share/kicad",
420  "../../share"
421  };
422 
423  bool foundSpiceinit = false;
424 
425  for( const auto& path : spiceinitPaths )
426  {
427  wxLogTrace( traceNgspice, "ngspice init script search path: %s", path );
428 
429  if( loadSpinit( path + "/spiceinit" ) )
430  {
431  wxLogTrace( traceNgspice, "ngspice path found in: %s", path );
432  foundSpiceinit = true;
433  break;
434  }
435  }
436 
437  // Last chance to load codemodel files, we have not found
438  // spiceinit file, but we know the path to *.cm files
439  if( !foundSpiceinit && !cmPath.empty() )
440  loadCodemodels( cmPath );
441 
442  // Restore the working directory
443  wxSetWorkingDirectory( cwd );
444 
445  // Workarounds to avoid hang ups on certain errors
446  // These commands have to be called, no matter what is in the spinit file
447  Command( "unset interactive" );
448  Command( "set noaskquit" );
449  Command( "set nomoremode" );
450 
451  m_initialized = true;
452 }
453 
454 
455 bool NGSPICE::loadSpinit( const string& aFileName )
456 {
457  if( !wxFileName::FileExists( aFileName ) )
458  return false;
459 
460  wxTextFile file;
461 
462  if( !file.Open( aFileName ) )
463  return false;
464 
465  for( auto cmd = file.GetFirstLine(); !file.Eof(); cmd = file.GetNextLine() )
466  Command( cmd.ToStdString() );
467 
468  return true;
469 }
470 
471 
472 string NGSPICE::findCmPath() const
473 {
474  const vector<string> cmPaths =
475  {
476 #ifdef __WXMAC__
477  "/Applications/ngspice/lib/ngspice",
478  "Contents/Frameworks",
479  wxStandardPaths::Get().GetPluginsDir() + "/sim/ngspice",
480  wxFileName( wxStandardPaths::Get().GetExecutablePath() ).GetPath() + "/../../../../../Contents/PlugIns/sim/ngspice",
481  "../Plugins/sim/ngspice",
482 #endif /* __WXMAC__ */
483  "../lib/ngspice",
484  "../../lib/ngspice",
485  "lib/ngspice",
486  "ngspice"
487  };
488 
489  for( const auto& path : cmPaths )
490  {
491  wxLogTrace( traceNgspice, "ngspice code models search path: %s", path );
492 
493  if( wxFileName::FileExists( path + "/spice2poly.cm" ) )
494  {
495  wxLogTrace( traceNgspice, "ngspice code models found in: %s", path );
496  return path;
497  }
498  }
499 
500  return string();
501 }
502 
503 
504 bool NGSPICE::loadCodemodels( const string& aPath )
505 {
506  wxArrayString cmFiles;
507  size_t count = wxDir::GetAllFiles( aPath, &cmFiles );
508 
509  for( const auto& cm : cmFiles )
510  Command( "codemodel " + cm.ToStdString() );
511 
512  return count != 0;
513 }
514 
515 
516 int NGSPICE::cbSendChar( char* what, int id, void* user )
517 {
518  NGSPICE* sim = reinterpret_cast<NGSPICE*>( user );
519 
520  if( sim->m_reporter )
521  {
522  // strip stdout/stderr from the line
523  if( ( strncasecmp( what, "stdout ", 7 ) == 0 )
524  || ( strncasecmp( what, "stderr ", 7 ) == 0 ) )
525  what += 7;
526 
527  sim->m_reporter->Report( what );
528  }
529 
530  return 0;
531 }
532 
533 
534 int NGSPICE::cbSendStat( char* what, int id, void* user )
535 {
536 /* NGSPICE* sim = reinterpret_cast<NGSPICE*>( user );
537  if( sim->m_consoleReporter )
538  sim->m_consoleReporter->Report( what );*/
539 
540  return 0;
541 }
542 
543 
544 int NGSPICE::cbBGThreadRunning( bool is_running, int id, void* user )
545 {
546  NGSPICE* sim = reinterpret_cast<NGSPICE*>( user );
547 
548  if( sim->m_reporter )
549  // I know the test below seems like an error, but well, it works somehow..
550  sim->m_reporter->OnSimStateChange( sim, is_running ? SIM_IDLE : SIM_RUNNING );
551 
552  return 0;
553 }
554 
555 
556 int NGSPICE::cbControlledExit( int status, bool immediate, bool exit_upon_quit, int id, void* user )
557 {
558  // Something went wrong, reload the dll
559  NGSPICE* sim = reinterpret_cast<NGSPICE*>( user );
560  sim->m_error = true;
561 
562  return 0;
563 }
564 
565 
567 {
568  if( m_error )
569  {
570  m_initialized = false;
571  init_dll();
572  }
573 }
574 
575 
576 const std::string NGSPICE::GetNetlist() const
577 {
578  return m_netlist;
579 }
580 
581 
582 bool NGSPICE::m_initialized = false;
virtual ~NGSPICE()
Definition: ngspice.cpp:61
bool Run() override
Definition: ngspice.cpp:257
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: common.h:214
static int cbBGThreadRunning(bool is_running, int id, void *user)
Definition: ngspice.cpp:544
void validate()
Definition: ngspice.cpp:566
int(* ngSpice_Command)(char *command)
Definition: ngspice.h:93
char **(* ngSpice_AllPlots)(void)
Definition: ngspice.h:96
std::string findCmPath() const
Checks a few different locations for codemodel files and returns one if it exists
Definition: ngspice.cpp:472
SPICE_REPORTER * m_reporter
Reporter object to receive simulation log
Template specialization to enable wxStrings for certain containers (e.g. unordered_map)
Definition: bitmap.cpp:56
std::vector< double > GetRealPlot(const std::string &aName, int aMaxLen=-1) override
Definition: ngspice.cpp:121
static int cbControlledExit(int status, bool immediate, bool exit_upon_quit, int id, void *user)
Definition: ngspice.cpp:556
static int cbSendChar(char *what, int id, void *user)
Definition: ngspice.cpp:516
wxDynamicLibrary m_dll
Definition: ngspice.h:110
bool loadCodemodels(const std::string &aPath)
Loads codemodel files from a directory
Definition: ngspice.cpp:504
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Function Report is a pure virtual function to override in the derived object.
std::vector< double > GetImagPlot(const std::string &aName, int aMaxLen=-1) override
Definition: ngspice.cpp:153
ngSpice_Running m_ngSpice_Running
Definition: ngspice.h:108
std::vector< double > GetMagPlot(const std::string &aName, int aMaxLen=-1) override
Definition: ngspice.cpp:177
static bool m_initialized
NGspice should be initialized only once
Definition: ngspice.h:135
bool(* ngSpice_Running)(void)
Definition: ngspice.h:98
void Init() override
Definition: ngspice.cpp:66
virtual const std::string GetNetlist() const override
Definition: ngspice.cpp:576
#define NULL
NGSPICE()
Definition: ngspice.cpp:46
ngGet_Vec_Info m_ngGet_Vec_Info
Definition: ngspice.h:104
bool m_error
Error flag indicating that ngspice needs to be reloaded
Definition: ngspice.h:132
void init_dll()
Definition: ngspice.cpp:312
static const wxChar *const traceNgspice
Definition: ngspice.cpp:44
SIM_TYPE
Possible simulation types
Definition: sim_types.h:29
pvector_info(* ngGet_Vec_Info)(char *vecname)
Definition: ngspice.h:94
std::vector< std::string > AllPlots() override
Definition: ngspice.cpp:72
char *(* ngSpice_CurPlot)(void)
Definition: ngspice.h:95
std::vector< COMPLEX > GetPlot(const std::string &aName, int aMaxLen=-1) override
Definition: ngspice.cpp:94
bool Stop() override
Definition: ngspice.cpp:264
ngSpice_Init m_ngSpice_Init
Handles to DLL functions
Definition: ngspice.h:101
std::string GetXAxis(SIM_TYPE aType) const override
Definition: ngspice.cpp:287
std::vector< double > GetPhasePlot(const std::string &aName, int aMaxLen=-1) override
Definition: ngspice.cpp:204
bool LoadNetlist(const std::string &aNetlist) override
Definition: ngspice.cpp:231
void(* ngSpice_Init)(SendChar *, SendStat *, ControlledExit *, SendData *, SendInitData *, BGThreadRunning *, void *)
Definition: ngspice.h:90
char **(* ngSpice_AllVecs)(char *plotname)
Definition: ngspice.h:97
bool Command(const std::string &aCmd) override
Definition: ngspice.cpp:278
bool IsRunning() override
Definition: ngspice.cpp:271
ngSpice_AllVecs m_ngSpice_AllVecs
Definition: ngspice.h:107
The common library.
virtual void OnSimStateChange(SPICE_SIMULATOR *aObject, SIM_STATE aNewState)=0
ngSpice_Command m_ngSpice_Command
Definition: ngspice.h:103
std::string m_netlist
current netlist
Definition: ngspice.h:138
bool loadSpinit(const std::string &aFileName)
Executes commands from a file
Definition: ngspice.cpp:455
int(* ngSpice_Circ)(char **circarray)
Definition: ngspice.h:92
ngSpice_Circ m_ngSpice_Circ
Definition: ngspice.h:102
ngSpice_CurPlot m_ngSpice_CurPlot
Definition: ngspice.h:105
ngSpice_AllPlots m_ngSpice_AllPlots
Definition: ngspice.h:106
static int cbSendStat(char *what, int id, void *user)
Definition: ngspice.cpp:534