1 //**************************************************************************************************
2 //                                         PrcNgSpice.cpp                                          *
3 //                                        ----------------                                         *
4 // Started     : 2004-05-07                                                                        *
5 // Last Update : 2015-03-20                                                                        *
6 // Copyright   : (C) 2004 by MSWaters                                                              *
7 //**************************************************************************************************
8 
9 //**************************************************************************************************
10 //                                                                                                 *
11 //      This program is free software; you can redistribute it and/or modify it under the          *
12 //      terms of the GNU General Public License as published by the Free Software Foundation;      *
13 //      either version 3 of the License, or (at your option) any later version.                    *
14 //                                                                                                 *
15 //**************************************************************************************************
16 
17 #include "PrcNgSpice.hpp"
18 
19 //**************************************************************************************************
20 // Constructor.
21 
PrcNgSpice(void)22 PrcNgSpice::PrcNgSpice( void ) : PrcSimEngBase( )
23 {
24   // Set the simulation engine type
25   m_eSimEng = eSIMR_NGSPICE;
26 
27   // Set the simulator binary file name if it can be found
28   bSetBinary( BIN_NGSPICE );
29 }
30 
31 //**************************************************************************************************
32 // Destructor.
33 
~PrcNgSpice()34 PrcNgSpice::~PrcNgSpice( )
35 {
36 }
37 
38 //**************************************************************************************************
39 // Match a component to a set of node labels.
40 //
41 // Component voltages cannot be specified using the component label in the NG-Spice print statement.
42 // The two node labels connected to the component must be specified. When parsing the print
43 // statement the component label must be derived from the node labels. This may not work where
44 // components are connected in parallel. The nodes are specified in the form "<Node1>,<Node2>"
45 // eg. "1,2".
46 //
47 // Argument List :
48 //   roSimn     - The simulation object
49 //   rosToNodes - The nodes to match, also used to return the component label
50 //
51 // Return Values :
52 //   true  - Success (a component with the specified nodes was found)
53 //   false - Failure
54 
bMatchCpnt(SimnNgSpice & roSimn,wxString & rosToNodes)55 bool  PrcNgSpice::bMatchCpnt( SimnNgSpice & roSimn, wxString & rosToNodes )
56 {
57   wxArrayString  osaNodes;
58   Component      tCpnt;
59   wxString       os1;
60   size_t         sz1;
61 
62   // Argument validity checks
63   if( rosToNodes.IsEmpty( ) )             return( false );
64   if( roSimn.m_oaCpnts.GetCount( ) <= 0 ) return( false );
65   if( rosToNodes.Freq( wxT(',') ) != 1 )  return( false );
66 
67   // Extract the node labels
68   osaNodes.Add( rosToNodes.BeforeFirst( wxT(',') ) );
69   osaNodes.Add( rosToNodes.AfterLast( wxT(',') ) );
70 
71   // Attempt to match the nodes with a component
72   for( sz1=0; sz1<roSimn.m_oaCpnts.GetCount( ); sz1++ )
73   {
74     tCpnt = roSimn.m_oaCpnts.Item( sz1 );
75     if( ! tCpnt.bParse( ) )            continue;
76     if( tCpnt.rosaGetNodes( ) == osaNodes ) break;
77   }
78 
79   // Was a match found?
80   if( ! tCpnt.bIsValid( ) )               return( false );
81 
82   rosToNodes = tCpnt.rosGetName( );
83 
84   return( true );
85 }
86 
87 //**************************************************************************************************
88 // Convert a list of component labels to the corresponding list of node pairs.
89 //
90 // Argument List :
91 //   rosaCpnts - A list of component labels
92 //   roSimn    - A simulation object
93 
Convert2Nodes(wxArrayString & rosaCpnts,SimnNgSpice & roSimn)94 void  PrcNgSpice::Convert2Nodes( wxArrayString & rosaCpnts, SimnNgSpice & roSimn )
95 {
96   Component  tCpnt;
97   wxString   os1;
98   size_t     sz1;
99 
100   for( sz1=0; sz1<rosaCpnts.GetCount( ); sz1++ )
101   {
102     os1 = rosaCpnts.Item( sz1 );
103     tCpnt = roSimn.roGetCpnt( os1 );
104     if( ! tCpnt.bParse( ) )                      continue;
105     if( tCpnt.rosaGetNodes( ).GetCount( ) != 2 ) continue;
106     os1.Empty( );
107     os1 << tCpnt.rosaGetNodes( ).Item( 0 ) << wxT(',') << tCpnt.rosaGetNodes( ).Item( 1 );
108     rosaCpnts.Item( sz1 ) = os1;
109   }
110 }
111 
112 //**************************************************************************************************
113 // Create a WIDTH command based on a PRINT command.
114 //
115 // By default raw output from NG-Spice uses an 80 column output width which allows about two
116 // dependant variables to be displayed. To display more information a .WIDTH statement must be added
117 // to increase the number columns in the output.
118 //
119 // Argument List :
120 //   rosCmdPR - The Print command
121 //
122 // Return Values :
123 //   Success - A .WIDTH command
124 //   Failure - An empty string
125 
rosMakeCmdWIDTH(wxString & rosCmdPR)126 wxString & PrcNgSpice::rosMakeCmdWIDTH( wxString & rosCmdPR )
127 {
128   static  wxString  osCmdWIDTH;
129   wxStringTokenizer  ostk1;
130   wxString  os1;
131   int  i1=0;
132 
133   osCmdWIDTH.Empty( );
134 
135   ostk1 = rosCmdPR;
136   if( ostk1.GetNextToken( ).StartsWith( wxT(".PR") ) )
137   {
138     os1 = ostk1.GetNextToken( );
139 
140     if(      os1.StartsWith( wxT("DC") ) )
141       i1 = 8 + 16 + ostk1.CountTokens( ) * 16;
142     else if( os1.StartsWith( wxT("AC") ) )
143       i1 = 8 + 2 * 16 + ostk1.CountTokens( ) * 16;
144     else if( os1.StartsWith( wxT("TR") ) )
145       i1 = 8 + 16 + ostk1.CountTokens( ) * 16;
146 
147     if( i1 > 80 ) osCmdWIDTH << wxT(".WIDTH OUT=") << i1;
148   }
149 
150   return( osCmdWIDTH );
151 }
152 
153 //**************************************************************************************************
154 // This function is a generic results file formatter which works in most circumstance.
155 //
156 // Return Values :
157 //   true  - Success
158 //   false - Failure
159 
bFmtCommon(void)160 bool  PrcNgSpice::bFmtCommon( void )
161 {
162   wxString  os1;
163   size_t    sz1, sz2;
164 
165   // This function requires the results file to be open
166   if( ! m_oFileResults.IsOpened( ) ) return( false );
167 
168   // Find the beginning of the data area (ie. the 1st line starting with "Index")
169   for( sz1=0; sz1<m_oFileResults.GetLineCount( ); sz1++ )
170   {
171     os1 = m_oFileResults.GetLine( sz1 );
172     if( os1.StartsWith( wxT("Index") ) ) break;
173   }
174   if( sz1 >= (m_oFileResults.GetLineCount( )-1) )
175   {
176     SetErrMsg( wxT("Couldn't find the data section in the results file.") );
177     return( false );
178   }
179 
180   // Delete everything before the data area
181   sz2 = sz1;
182   for( sz1=0; sz1<sz2; sz1++ )             m_oFileResults.RemoveLine( 0 );
183   if( m_oFileResults.GetLineCount( ) > 0 ) m_oFileResults.RemoveLine( 1 );
184 
185   // Format the column header line
186   bFmtColLabels( );
187 
188   // Delete lines other than data lines
189   for( sz1=1; sz1<m_oFileResults.GetLineCount( ); sz1++ )
190   {
191     os1 = m_oFileResults.GetLine( sz1 );
192     if( ! wxIsdigit( os1.GetChar( 0 ) ) )
193     {
194       m_oFileResults.RemoveLine( sz1 );
195       sz1--;
196     }
197   }
198 
199   // Format the data lines
200   bFmtDataLines( );
201 
202   return( true );
203 }
204 
205 //**************************************************************************************************
206 // Format the column header in the results file.
207 //
208 // Return Values :
209 //   true  - Success
210 //   false - Failure
211 
bFmtColLabels(void)212 bool  PrcNgSpice::bFmtColLabels( void )
213 {
214   wxStringTokenizer  ostk1;
215   wxString  osLine, os1;
216   int  i1;
217 
218   // This function requires the results file to be open
219   if( ! m_oFileResults.IsOpened( ) ) return( false );
220 
221   // Get the column header line
222   ostk1 = m_oFileResults.GetFirstLine( );
223   if( ostk1.CountTokens( ) < 3 )     return( false );
224 
225   // Dispose of the first field (ie. "Index")
226   ostk1.GetNextToken( );
227 
228   // Extract the operator field and replace the NG-Spice column labels
229   os1 << ostk1.GetNextToken( ) << wxT(' ') << m_osColLbls;
230   ostk1.SetString( os1 );
231 
232   // Format each field
233 // (06/08/2005) gWave breaks if there's a space after initial '#' ???
234 // osLine = wxT("# ");
235   osLine = wxT('#');
236   while( ostk1.HasMoreTokens( ) )
237   {
238     if( osLine.Length( ) > 2 )
239     { // Pad the column with spaces to the required width
240       i1 = NGSPICE_COL_WD - (osLine.Length( ) % NGSPICE_COL_WD) + 1;
241       if( i1 >= NGSPICE_COL_WD ) i1 = 1;
242       osLine.Append( wxT(' '), i1 );
243     }
244 
245     // Add the next label to the line
246     osLine << ostk1.GetNextToken( );
247   }
248 
249   osLine.Trim( ); // Remove trailing space characters
250 
251   m_oFileResults.GetFirstLine( ) = osLine;
252 
253   return( true );
254 }
255 
256 //**************************************************************************************************
257 // Format a data lines in the results file.
258 //
259 // Return Values :
260 //   true  - Success
261 //   false - Failure
262 
bFmtDataLines(void)263 bool  PrcNgSpice::bFmtDataLines( void )
264 {
265   wxStringTokenizer  ostk1;
266   wxString  osLine, os1;
267   size_t    sz1;
268   double    df1;
269   int       i1;
270 
271   // This function requires the results file to be open
272   if( ! m_oFileResults.IsOpened( ) ) return( false );
273 
274   for( sz1=1; sz1<m_oFileResults.GetLineCount( ); sz1++ )
275   {
276     // Get a line of data
277     osLine = m_oFileResults.GetLine( sz1 );
278 
279     // Tokenize the string
280     ostk1.SetString( osLine );
281     if( ostk1.CountTokens( ) < 3 )   return( false );
282 
283     // Dispose of the first field
284     ostk1.GetNextToken( );
285 
286     // Format each field
287     osLine.Empty( );
288     while( ostk1.HasMoreTokens( ) )
289     {
290       // Add the next parameter to the line
291       os1 = ostk1.GetNextToken( );
292       if( os1.Last( ) == wxT(',') )
293       {
294         os1.Last( ) = wxT(' ');
295         ostk1.GetNextToken( );
296       }
297       if( ! CnvtType::bStrToFlt( os1, &df1 ) ) df1 =      -9.999e+99;
298       if( ! CnvtType::bFltToStr( df1, os1 ) )  os1 = wxT("-9.999e+99");
299       osLine << os1;
300 
301       // Pad the column with spaces to the required width
302       i1 = NGSPICE_COL_WD - (osLine.Length( ) % NGSPICE_COL_WD);
303       osLine.Append( wxT(' '), i1 );
304     }
305 
306     m_oFileResults.GetLine( sz1 ) = osLine;
307 
308     osLine.Trim( ); // Remove trailing space characters
309   }
310 
311   return( true );
312 }
313 
314 //**************************************************************************************************
315 // Format any phase values contained in the results file.
316 //
317 // This is only pertinent for an AC analysis. It involves removing the discontinuity at 360 degree
318 // in the data.
319 //
320 // Return Values :
321 //   true  - Success
322 //   false - Failure
323 
bFmtPhaseData(void)324 bool  PrcNgSpice::bFmtPhaseData( void )
325 {
326   // ??? (02/02/2006) Not yet implemented
327 
328   return( true );
329 }
330 
331 //**************************************************************************************************
332 // Make the process argument list based on the contents of a simulation object.
333 //
334 // Argument List :
335 //   roSimn - The simulation object
336 //
337 // Return Values :
338 //   true  - Success
339 //   false - Failure
340 
bMakeArgLst(SimnBase & roSimn)341 bool  PrcNgSpice::bMakeArgLst( SimnBase & roSimn )
342 {
343   wxString  os1;
344 
345   // Envoke the base class operations
346   if( ! PrcSimEngBase::bMakeArgLst( roSimn ) ) return( false );
347 
348   // Construct the command line to execute the simulation
349   os1 = wxT("-n -b ") + roSimn.roGetSaveFile( ).GetFullPath( );
350   if( ! PrcBase::bSetArgLst( os1 ) )
351   {
352     SetErrMsg( wxT("Couldn't set argument list") );
353     return( false );
354   }
355 
356   // Get results file column labels (replace node pairs with component names)
357   if( roSimn.eGetSimEng( ) == eSIMR_NGSPICE )
358     m_osColLbls = ((SimnNgSpice &) roSimn).m_oCmdPR.rosGetParamLst( );
359 
360   return( true );
361 }
362 
363 //**************************************************************************************************
364 // Format the contents of the results file so that gWave can read it.
365 //
366 // Ie. add a header line at the top containing parameter names followed by the columns of data.
367 //
368 // Return Values :
369 //   true  - Success
370 //   false - Failure
371 
bFmtResults(void)372 bool  PrcNgSpice::bFmtResults( void )
373 {
374   bool      bRtn;
375   wxString  os1;
376 
377   m_osErrMsg.Empty( );
378 
379   // Check that the results file exists
380   if( ! roGetResultsFile( ).FileExists( ) )
381   {
382     os1.Empty( );
383     os1 << wxT("Results file doesn't exist : \n\n")
384         << roGetResultsFile( ).GetFullPath( );
385     SetErrMsg( os1 );
386     return( false );
387   }
388 
389   // Attempt to open the results file
390   if( ! m_oFileResults.Open( roGetResultsFile( ).GetFullPath( ) ) )
391   {
392     os1.Empty( );
393     os1 << wxT("Results file couldn't be opened : \n\n")
394         << roGetResultsFile( ).GetFullPath( );
395     SetErrMsg( os1 );
396     return( false );
397   }
398 
399   // Format the simulation results
400   bRtn = bFmtCommon( );
401 
402   // Need last line to end with a '\n' or text controls will not load it
403   os1 = m_oFileResults.GetLastLine( );
404   if( ! os1.IsEmpty( ) )
405     if( os1.Last( ) != wxT('\n') )
406       m_oFileResults.GetLastLine( ) << wxT('\n');
407 
408   m_oFileResults.Write( );  // Save the changes to disk
409   m_oFileResults.Close( );  // Close the file
410 
411   return( bRtn );
412 }
413 
414 //**************************************************************************************************
415