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 #if defined(__WINDOWS__) || (__WXMAC__)
291 #ifdef __WINDOWS__
292  wxFileName dllFile( "", NGSPICE_DLL_FILE );
293  const vector<string> dllPaths = { "", "/mingw64/bin", "/mingw32/bin" };
294 #endif /* __WINDOWS__ */
295 #ifdef __WXMAC__
296  wxFileName dllFile( "", NGSPICE_DLL_FILE );
297  const vector<string> dllPaths = {
298  GetOSXKicadUserDataDir() + "/PlugIns/ngspice",
299  GetOSXKicadMachineDataDir() + "/PlugIns/ngspice",
300  // when running kicad.app
301  stdPaths.GetPluginsDir() + "/sim",
302  // when running eeschema.app
303  wxFileName( stdPaths.GetExecutablePath() ).GetPath() + "/../../../../../Contents/PlugIns/sim"
304  };
305 #endif /* __WXMAC__ */
306 
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 #else
323  m_dll.Load( NGSPICE_DLL_FILE );
324 #endif /* __WINDOWS || __WXMAC__ */
325 
326  if( !m_dll.IsLoaded() )
327  throw std::runtime_error( "Missing ngspice shared library" );
328 
329  m_error = false;
330 
331  // Obtain function pointers
332  m_ngSpice_Init = (ngSpice_Init) m_dll.GetSymbol( "ngSpice_Init" );
333  m_ngSpice_Circ = (ngSpice_Circ) m_dll.GetSymbol( "ngSpice_Circ" );
334  m_ngSpice_Command = (ngSpice_Command) m_dll.GetSymbol( "ngSpice_Command" );
335  m_ngGet_Vec_Info = (ngGet_Vec_Info) m_dll.GetSymbol( "ngGet_Vec_Info" );
336  m_ngSpice_AllPlots = (ngSpice_AllPlots) m_dll.GetSymbol( "ngSpice_AllPlots" );
337  m_ngSpice_AllVecs = (ngSpice_AllVecs) m_dll.GetSymbol( "ngSpice_AllVecs" );
338  m_ngSpice_Running = (ngSpice_Running) m_dll.GetSymbol( "ngSpice_running" ); // it is not a typo
339 
341 
342  // Load a custom spinit file, to fix the problem with loading .cm files
343  // Switch to the executable directory, so the relative paths are correct
344  wxString cwd( wxGetCwd() );
345  wxFileName exeDir( stdPaths.GetExecutablePath() );
346  wxSetWorkingDirectory( exeDir.GetPath() );
347 
348  // Find *.cm files
349  string cmPath = findCmPath();
350 
351  // __CMPATH is used in custom spinit file to point to the codemodels directory
352  if( !cmPath.empty() )
353  Command( "set __CMPATH=\"" + cmPath + "\"" );
354 
355  // Possible relative locations for spinit file
356  const vector<string> spiceinitPaths =
357  {
358  ".",
359 #ifdef __WXMAC__
360  stdPaths.GetPluginsDir() + "/sim/ngspice/scripts",
361  wxFileName( stdPaths.GetExecutablePath() ).GetPath() + "/../../../../../Contents/PlugIns/sim/ngspice/scripts"
362 #endif /* __WXMAC__ */
363  "../share/kicad",
364  "../share",
365  "../../share/kicad",
366  "../../share"
367  };
368 
369  bool foundSpiceinit = false;
370 
371  for( const auto& path : spiceinitPaths )
372  {
373  wxLogTrace( traceNgspice, "ngspice init script search path: %s", path );
374 
375  if( loadSpinit( path + "/spiceinit" ) )
376  {
377  wxLogTrace( traceNgspice, "ngspice path found in: %s", path );
378  foundSpiceinit = true;
379  break;
380  }
381  }
382 
383  // Last chance to load codemodel files, we have not found
384  // spiceinit file, but we know the path to *.cm files
385  if( !foundSpiceinit && !cmPath.empty() )
386  loadCodemodels( cmPath );
387 
388  // Restore the working directory
389  wxSetWorkingDirectory( cwd );
390 
391  // Workarounds to avoid hang ups on certain errors
392  // These commands have to be called, no matter what is in the spinit file
393  Command( "unset interactive" );
394  Command( "set noaskquit" );
395  Command( "set nomoremode" );
396 
397  m_initialized = true;
398 }
399 
400 
401 bool NGSPICE::loadSpinit( const string& aFileName )
402 {
403  if( !wxFileName::FileExists( aFileName ) )
404  return false;
405 
406  wxTextFile file;
407 
408  if( !file.Open( aFileName ) )
409  return false;
410 
411  for( auto cmd = file.GetFirstLine(); !file.Eof(); cmd = file.GetNextLine() )
412  Command( cmd.ToStdString() );
413 
414  return true;
415 }
416 
417 
418 string NGSPICE::findCmPath() const
419 {
420  const vector<string> cmPaths =
421  {
422 #ifdef __WXMAC__
423  "/Applications/ngspice/lib/ngspice",
424  "Contents/Frameworks",
425  wxStandardPaths::Get().GetPluginsDir() + "/sim/ngspice",
426  wxFileName( wxStandardPaths::Get().GetExecutablePath() ).GetPath() + "/../../../../../Contents/PlugIns/sim/ngspice"
427 #endif /* __WXMAC__ */
428  "../lib/ngspice",
429  "../../lib/ngspice"
430  "lib/ngspice",
431  "ngspice"
432  };
433 
434  for( const auto& path : cmPaths )
435  {
436  wxLogTrace( traceNgspice, "ngspice code models search path: %s", path );
437 
438  if( wxFileName::FileExists( path + "/spice2poly.cm" ) )
439  {
440  wxLogTrace( traceNgspice, "ngspice code models found in: %s", path );
441  return path;
442  }
443  }
444 
445  return string();
446 }
447 
448 
449 bool NGSPICE::loadCodemodels( const string& aPath )
450 {
451  wxArrayString cmFiles;
452  size_t count = wxDir::GetAllFiles( aPath, &cmFiles );
453 
454  for( const auto& cm : cmFiles )
455  Command( "codemodel " + cm.ToStdString() );
456 
457  return count != 0;
458 }
459 
460 
461 int NGSPICE::cbSendChar( char* what, int id, void* user )
462 {
463  NGSPICE* sim = reinterpret_cast<NGSPICE*>( user );
464 
465  if( sim->m_reporter )
466  {
467  // strip stdout/stderr from the line
468  if( ( strncasecmp( what, "stdout ", 7 ) == 0 )
469  || ( strncasecmp( what, "stderr ", 7 ) == 0 ) )
470  what += 7;
471 
472  sim->m_reporter->Report( what );
473  }
474 
475  return 0;
476 }
477 
478 
479 int NGSPICE::cbSendStat( char* what, int id, void* user )
480 {
481 /* NGSPICE* sim = reinterpret_cast<NGSPICE*>( user );
482  if( sim->m_consoleReporter )
483  sim->m_consoleReporter->Report( what );*/
484 
485  return 0;
486 }
487 
488 
489 int NGSPICE::cbBGThreadRunning( bool is_running, int id, void* user )
490 {
491  NGSPICE* sim = reinterpret_cast<NGSPICE*>( user );
492 
493  if( sim->m_reporter )
494  // I know the test below seems like an error, but well, it works somehow..
495  sim->m_reporter->OnSimStateChange( sim, is_running ? SIM_IDLE : SIM_RUNNING );
496 
497  return 0;
498 }
499 
500 
501 int NGSPICE::cbControlledExit( int status, bool immediate, bool exit_upon_quit, int id, void* user )
502 {
503  // Something went wrong, reload the dll
504  NGSPICE* sim = reinterpret_cast<NGSPICE*>( user );
505  sim->m_error = true;
506 
507  return 0;
508 }
509 
510 
512 {
513  if( m_error )
514  {
515  m_initialized = false;
516  init_dll();
517  }
518 }
519 
520 
521 const std::string NGSPICE::GetNetlist() const
522 {
523  return m_netlist;
524 }
525 
526 
527 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:179
static int cbBGThreadRunning(bool is_running, int id, void *user)
Definition: ngspice.cpp:489
void validate()
Definition: ngspice.cpp:511
int(* ngSpice_Command)(char *command)
Definition: ngspice.h:90
std::complex< double > COMPLEX
char **(* ngSpice_AllPlots)(void)
Definition: ngspice.h:92
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:55
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:501
static int cbSendChar(char *what, int id, void *user)
Definition: ngspice.cpp:461
wxDynamicLibrary m_dll
Definition: ngspice.h:105
bool loadCodemodels(const std::string &aPath)
Loads codemodel files from a directory
Definition: ngspice.cpp:449
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:521
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 findCmPath() const
Checks a few different locations for codemodel files and returns one if it exists ...
Definition: ngspice.cpp:418
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:401
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:479
#define min(a, b)
Definition: auxiliary.h:85