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