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 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 
35 using namespace std;
36 
38 {
39  init();
40 }
41 
42 
44 {
45 }
46 
47 
49 {
50  Command( "reset" );
51 }
52 
53 
54 vector<COMPLEX> NGSPICE::GetPlot( const string& aName, int aMaxLen )
55 {
56  LOCALE_IO c_locale; // ngspice works correctly only with C locale
57  vector<COMPLEX> data;
58  vector_info* vi = ngGet_Vec_Info( (char*) aName.c_str() );
59 
60  if( vi )
61  {
62  int length = aMaxLen < 0 ? vi->v_length : std::min( aMaxLen, vi->v_length );
63  data.reserve( length );
64 
65  if( vi->v_realdata )
66  {
67  for( int i = 0; i < length; i++ )
68  data.push_back( COMPLEX( vi->v_realdata[i], 0.0 ) );
69  }
70  else if( vi->v_compdata )
71  {
72  for( int i = 0; i < length; i++ )
73  data.push_back( COMPLEX( vi->v_compdata[i].cx_real, vi->v_compdata[i].cx_imag ) );
74  }
75  }
76 
77  return data;
78 }
79 
80 
81 vector<double> NGSPICE::GetRealPlot( const string& aName, int aMaxLen )
82 {
83  LOCALE_IO c_locale; // ngspice works correctly only with C locale
84  vector<double> data;
85  vector_info* vi = ngGet_Vec_Info( (char*) aName.c_str() );
86 
87  if( vi )
88  {
89  int length = aMaxLen < 0 ? vi->v_length : std::min( aMaxLen, vi->v_length );
90  data.reserve( length );
91 
92  if( vi->v_realdata )
93  {
94  for( int i = 0; i < length; i++ )
95  {
96  data.push_back( vi->v_realdata[i] );
97  }
98  }
99  else if( vi->v_compdata )
100  {
101  for( int i = 0; i < length; i++ )
102  {
103  assert( vi->v_compdata[i].cx_imag == 0.0 );
104  data.push_back( vi->v_compdata[i].cx_real );
105  }
106  }
107  }
108 
109  return data;
110 }
111 
112 
113 vector<double> NGSPICE::GetImagPlot( const string& aName, int aMaxLen )
114 {
115  LOCALE_IO c_locale; // ngspice works correctly only with C locale
116  vector<double> data;
117  vector_info* vi = ngGet_Vec_Info( (char*) aName.c_str() );
118 
119  if( vi )
120  {
121  int length = aMaxLen < 0 ? vi->v_length : std::min( aMaxLen, vi->v_length );
122  data.reserve( length );
123 
124  if( vi->v_compdata )
125  {
126  for( int i = 0; i < length; i++ )
127  {
128  data.push_back( vi->v_compdata[i].cx_imag );
129  }
130  }
131  }
132 
133  return data;
134 }
135 
136 
137 vector<double> NGSPICE::GetMagPlot( const string& aName, int aMaxLen )
138 {
139  LOCALE_IO c_locale; // ngspice works correctly only with C locale
140  vector<double> data;
141  vector_info* vi = ngGet_Vec_Info( (char*) aName.c_str() );
142 
143  if( vi )
144  {
145  int length = aMaxLen < 0 ? vi->v_length : std::min( aMaxLen, vi->v_length );
146  data.reserve( length );
147 
148  if( vi->v_realdata )
149  {
150  for( int i = 0; i < length; i++ )
151  data.push_back( vi->v_realdata[i] );
152  }
153  else if( vi->v_compdata )
154  {
155  for( int i = 0; i < length; i++ )
156  data.push_back( hypot( vi->v_compdata[i].cx_real, vi->v_compdata[i].cx_imag ) );
157  }
158  }
159 
160  return data;
161 }
162 
163 
164 vector<double> NGSPICE::GetPhasePlot( const string& aName, int aMaxLen )
165 {
166  LOCALE_IO c_locale; // ngspice works correctly only with C locale
167  vector<double> data;
168  vector_info* vi = ngGet_Vec_Info( (char*) aName.c_str() );
169 
170  if( vi )
171  {
172  int length = aMaxLen < 0 ? vi->v_length : std::min( aMaxLen, vi->v_length );
173  data.reserve( length );
174 
175  if( vi->v_realdata )
176  {
177  for( int i = 0; i < length; i++ )
178  data.push_back( 0.0 ); // well, that's life
179  }
180  else if( vi->v_compdata )
181  {
182  for( int i = 0; i < length; i++ )
183  data.push_back( atan2( vi->v_compdata[i].cx_imag, vi->v_compdata[i].cx_real ) );
184  }
185  }
186 
187  return data;
188 }
189 
190 
191 bool NGSPICE::LoadNetlist( const string& aNetlist )
192 {
193  LOCALE_IO c_locale; // ngspice works correctly only with C locale
194  vector<char*> lines;
195  stringstream ss( aNetlist );
196 
197  while( !ss.eof() )
198  {
199  char line[1024];
200  ss.getline( line, sizeof( line ) );
201  lines.push_back( strdup( line ) );
202  }
203 
204  lines.push_back( nullptr );
205 
206  ngSpice_Circ( lines.data() );
207 
208  for( auto line : lines )
209  free( line );
210 
211  return true;
212 }
213 
214 
216 {
217  LOCALE_IO c_locale; // ngspice works correctly only with C locale
218  return Command( "bg_run" ); // bg_* commands execute in a separate thread
219 }
220 
221 
223 {
224  LOCALE_IO c_locale; // ngspice works correctly only with C locale
225  return Command( "bg_halt" ); // bg_* commands execute in a separate thread
226 }
227 
228 
230 {
231  LOCALE_IO c_locale; // ngspice works correctly only with C locale
232  return ngSpice_running();
233 }
234 
235 
236 bool NGSPICE::Command( const string& aCmd )
237 {
238  LOCALE_IO c_locale; // ngspice works correctly only with C locale
239  ngSpice_Command( (char*) aCmd.c_str() );
240 
241  return true;
242 }
243 
244 
245 string NGSPICE::GetXAxis( SIM_TYPE aType ) const
246 {
247  switch( aType )
248  {
249  case ST_AC:
250  case ST_NOISE:
251  return string( "frequency" );
252  break;
253 
254  case ST_DC:
255  return string( "v-sweep" );
256  break;
257 
258  case ST_TRANSIENT:
259  return string( "time" );
260  break;
261 
262  default:
263  break;
264  }
265 
266  return string( "" );
267 }
268 
269 
271 {
272  if( m_initialized )
273  return;
274 
275  LOCALE_IO c_locale; // ngspice works correctly only with C locale
276  ngSpice_Init( &cbSendChar, &cbSendStat, &cbControlledExit, NULL, NULL, &cbBGThreadRunning, this );
277 
278  // Load a custom spinit file, to fix the problem with loading .cm files
279  // Switch to the executable directory, so the relative paths are correct
280  const wxStandardPaths& paths = wxStandardPaths::Get();
281  wxString cwd( wxGetCwd() );
282  wxFileName exeDir( paths.GetExecutablePath() );
283  wxSetWorkingDirectory( exeDir.GetPath() );
284 
285  // Find *.cm files
286  string cmPath = findCmPath();
287 
288  // __CMPATH is used in custom spinit file to point to the codemodels directory
289  if( !cmPath.empty() )
290  Command( "set __CMPATH=\"" + cmPath + "\"" );
291 
292  // Possible relative locations for spinit file
293  const vector<string> spiceinitPaths =
294  {
295  ".",
296  "../share/kicad",
297  "../share",
298  "../../share/kicad",
299  "../../share"
300  };
301 
302  bool foundSpiceinit = false;
303 
304  for( const auto& path : spiceinitPaths )
305  {
306  if( loadSpinit( path + "/spiceinit" ) )
307  {
308  foundSpiceinit = true;
309  break;
310  }
311  }
312 
313  // Last chance to load codemodel files, we have not found
314  // spiceinit file, but we know the path to *.cm files
315  if( !foundSpiceinit && !cmPath.empty() )
316  loadCodemodels( cmPath );
317 
318  // Restore the working directory
319  wxSetWorkingDirectory( cwd );
320 
321  // Workarounds to avoid hang ups on certain errors,
322  // they have to be called, no matter what is in the spinit file
323  Command( "unset interactive" );
324  Command( "set noaskquit" );
325  Command( "set nomoremode" );
326 
327  m_initialized = true;
328 }
329 
330 
331 bool NGSPICE::loadSpinit( const string& aFileName )
332 {
333  if( !wxFileName::FileExists( aFileName ) )
334  return false;
335 
336  wxTextFile file;
337 
338  if( !file.Open( aFileName ) )
339  return false;
340 
341  for( auto cmd = file.GetFirstLine(); !file.Eof(); cmd = file.GetNextLine() )
342  Command( cmd.ToStdString() );
343 
344  return true;
345 }
346 
347 
348 string NGSPICE::findCmPath() const
349 {
350  const vector<string> cmPaths =
351  {
352 #ifdef __APPLE__
353  "/Applications/ngspice/lib/ngspice",
354 #endif /* __APPLE__ */
355  "../lib/ngspice",
356  "../../lib/ngspice"
357  "lib/ngspice",
358  "ngspice"
359  };
360 
361  for( const auto& path : cmPaths )
362  {
363  if( wxFileName::DirExists( path ) )
364  return path;
365  }
366 
367  return string();
368 }
369 
370 
371 bool NGSPICE::loadCodemodels( const string& aPath )
372 {
373  wxArrayString cmFiles;
374  size_t count = wxDir::GetAllFiles( aPath, &cmFiles );
375 
376  for( const auto& cm : cmFiles )
377  Command( "codemodel " + cm.ToStdString() );
378 
379  return count != 0;
380 }
381 
382 
383 int NGSPICE::cbSendChar( char* what, int id, void* user )
384 {
385  NGSPICE* sim = reinterpret_cast<NGSPICE*>( user );
386 
387  if( sim->m_reporter )
388  {
389  // strip stdout/stderr from the line
390  if( ( strncasecmp( what, "stdout ", 7 ) == 0 )
391  || ( strncasecmp( what, "stderr ", 7 ) == 0 ) )
392  what += 7;
393 
394  sim->m_reporter->Report( what );
395  }
396 
397  return 0;
398 }
399 
400 
401 int NGSPICE::cbSendStat( char* what, int id, void* user )
402 {
403 /* NGSPICE* sim = reinterpret_cast<NGSPICE*>( user );
404  if( sim->m_consoleReporter )
405  sim->m_consoleReporter->Report( what );*/
406 
407  return 0;
408 }
409 
410 
411 int NGSPICE::cbBGThreadRunning( bool is_running, int id, void* user )
412 {
413  NGSPICE* sim = reinterpret_cast<NGSPICE*>( user );
414 
415  if( sim->m_reporter )
416  // I know the test below seems like an error, but well, it works somehow..
417  sim->m_reporter->OnSimStateChange( sim, is_running ? SIM_IDLE : SIM_RUNNING );
418 
419  return 0;
420 }
421 
422 
423 int NGSPICE::cbControlledExit( int status, bool immediate, bool exit_upon_quit, int id, void* user )
424 {
425  // Something went wrong, reload the dll
426  //NGSPICE* sim = reinterpret_cast<NGSPICE*>( user );
427  //sim->m_initialized = false;
428  //printf("stat %d immed %d quit %d\n", status, !!immediate, !!exit_upon_quit);
429 
430  return 0;
431 }
432 
433 
434 bool NGSPICE::m_initialized = false;
virtual ~NGSPICE()
Definition: ngspice.cpp:43
bool Run() override
>
Definition: ngspice.cpp:215
Class LOCALE_IO is a class that can be instantiated within a scope in which you are expecting excepti...
Definition: common.h:166
static int cbBGThreadRunning(bool is_running, int id, void *user)
Definition: ngspice.cpp:411
std::complex< double > COMPLEX
SPICE_REPORTER * m_reporter
Reporter object to receive simulation log
std::vector< double > GetRealPlot(const std::string &aName, int aMaxLen=-1) override
>
Definition: ngspice.cpp:81
static int cbControlledExit(int status, bool immediate, bool exit_upon_quit, int id, void *user)
Definition: ngspice.cpp:423
static int cbSendChar(char *what, int id, void *user)
Definition: ngspice.cpp:383
bool loadCodemodels(const std::string &aPath)
Loads codemodel files from a directory
Definition: ngspice.cpp:371
std::vector< double > GetImagPlot(const std::string &aName, int aMaxLen=-1) override
>
Definition: ngspice.cpp:113
std::vector< double > GetMagPlot(const std::string &aName, int aMaxLen=-1) override
>
Definition: ngspice.cpp:137
static bool m_initialized
NGspice should be initialized only once
Definition: ngspice.h:98
void Init() override
>
Definition: ngspice.cpp:48
NGSPICE()
Definition: ngspice.cpp:37
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:54
bool Stop() override
>
Definition: ngspice.cpp:222
std::string findCmPath() const
Checks a few different locations for codemodel files and returns one if it exists ...
Definition: ngspice.cpp:348
std::string GetXAxis(SIM_TYPE aType) const override
>
Definition: ngspice.cpp:245
std::vector< double > GetPhasePlot(const std::string &aName, int aMaxLen=-1) override
>
Definition: ngspice.cpp:164
void init()
Definition: ngspice.cpp:270
bool LoadNetlist(const std::string &aNetlist) override
>
Definition: ngspice.cpp:191
bool Command(const std::string &aCmd) override
>
Definition: ngspice.cpp:236
bool IsRunning() override
>
Definition: ngspice.cpp:229
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:331
static int cbSendStat(char *what, int id, void *user)
Definition: ngspice.cpp:401
#define min(a, b)
Definition: auxiliary.h:85