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