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