KiCad PCB EDA Suite
netlist_exporter_pspice_sim.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 Maciej Suminski <maciej.suminski@cern.ch>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * https://www.gnu.org/licenses/gpl-3.0.html
20  * or you may search the http://www.gnu.org website for the version 3 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
26 #include <wx/regex.h>
27 #include <wx/tokenzr.h>
28 
30  const wxString& aName, SIM_PLOT_TYPE aType, const wxString& aParam ) const
31 {
32  wxString res;
33 
34  // Some of the flags should exclude mutually
35  assert( ( ( aType & SPT_VOLTAGE ) == 0 ) != ( ( aType & SPT_CURRENT ) == 0 ) );
36  assert( ( ( aType & SPT_AC_PHASE ) == 0 ) || ( ( aType & SPT_AC_MAG ) == 0 ) );
37 
38  if( aType & SPT_VOLTAGE )
39  {
40  // netnames are escaped (can contain "{slash}" for '/') Unscape them:
41  wxString spicenet = UnescapeString( aName );
42 
43  // Spice netlist netnames does not accept some chars, which are replaced
44  // by eeschema netlist generator.
45  // Replace these forbidden chars to find the actual spice net name
47 
48  return wxString::Format( "V(%s)", spicenet );
49  }
50 
51  else if( aType & SPT_CURRENT )
52  {
53  wxString device = GetSpiceDevice( aName ).Lower();
54  wxString param = aParam.Lower();
55 
56  if( device[0] == 'x' )
57  {
58  return "current probe of .subckt not yet implemented";
59  }
60  else
61  {
62  return wxString::Format( "@%s[%s]",
63  device,
64  param.IsEmpty() ? "i" : param );
65  }
66  }
67 
68  return res;
69 }
70 
71 
73  const std::string& aVector, wxString& aSignal ) const
74 {
75  using namespace std;
76 
77  // See ngspice manual chapt. 31.1 "Accessing internal device parameters"
78  wxRegEx internalDevParameter( "^@(\\w*[\\.\\w+]*)\\[(\\w*)\\]$", wxRE_ADVANCED );
79  wxString vector( aVector );
80 
81  if( !internalDevParameter.Matches( vector ) )
82  {
83  // any text is a node name, which returns voltage
84  aSignal = "V(" + aVector + ")";
85  return SPT_VOLTAGE;
86  }
87  else
88  {
89  wxString paramType = internalDevParameter.GetMatch( vector, 2 );
90 
91  if( paramType.Lower()[0] == 'i' )
92  {
93  // this is a branch current
94  paramType[0] = 'I';
95  aSignal = paramType + "(";
96  aSignal += internalDevParameter.GetMatch( vector, 1 ).Upper() + ")";
97  return SPT_CURRENT;
98  }
99  else
100  {
101  return SPT_UNKNOWN;
102  }
103  }
104 }
105 
106 
107 const std::vector<wxString>& NETLIST_EXPORTER_PSPICE_SIM::GetCurrents( SPICE_PRIMITIVE aPrimitive )
108 {
109  static const std::vector<wxString> passive = { "I" };
110  static const std::vector<wxString> diode = { "Id" };
111  static const std::vector<wxString> bjt = { "Ib", "Ic", "Ie" };
112  static const std::vector<wxString> mos = { "Ig", "Id", "Is" };
113  static const std::vector<wxString> empty;
114 
115  switch( aPrimitive )
116  {
117  case SP_RESISTOR:
118  case SP_CAPACITOR:
119  case SP_INDUCTOR:
120  case SP_VSOURCE:
121  return passive;
122 
123  case SP_DIODE:
124  return diode;
125 
126  case SP_BJT:
127  return bjt;
128 
129  case SP_MOSFET:
130  return mos;
131 
132  default:
133  return empty;
134  }
135 }
136 
137 
139 {
140  wxString simCmd;
141 
143 
144  for( const auto& dir : GetDirectives() )
145  {
146  if( IsSimCommand( dir ) )
147  simCmd += wxString::Format( "%s\r\n", dir );
148  }
149 
150  return simCmd;
151 }
152 
153 
155 {
156  return m_simCommand.IsEmpty() ? GetSheetSimCommand() : m_simCommand;
157 }
158 
159 
161 {
163 }
164 
165 
167 {
168  const std::vector<std::pair<wxString, SIM_TYPE>> simCmds = {
169  { "^.ac\\M.*", ST_AC },
170  { "^.dc\\M.*", ST_DC },
171  { "^.tran\\M.*", ST_TRANSIENT },
172  { "^.op\\M.*", ST_OP },
173  { "^.disto\\M.*", ST_DISTORTION },
174  { "^.noise\\M.*", ST_NOISE },
175  { "^.pz\\M.*", ST_POLE_ZERO },
176  { "^.sens\\M.*", ST_SENSITIVITY },
177  { "^.tf\\M.*", ST_TRANS_FUNC } };
178  wxRegEx simCmd;
179 
180  for( const auto& c : simCmds )
181  {
182  simCmd.Compile( c.first, wxRE_ADVANCED | wxRE_NOSUB | wxRE_ICASE );
183 
184  if( simCmd.Matches( aCmd ) )
185  return c.second;
186  }
187 
188  return ST_UNKNOWN;
189 }
190 
191 
192 bool NETLIST_EXPORTER_PSPICE_SIM::ParseDCCommand( const wxString& aCmd, SPICE_DC_PARAMS* aSource1,
193  SPICE_DC_PARAMS* aSource2 )
194 {
195  if( !aCmd.Lower().StartsWith( ".dc" ) )
196  return false;
197 
198  wxString cmd = aCmd.Mid( 3 ).Trim().Trim( false );
199 
200  wxStringTokenizer tokens( cmd );
201 
202  size_t num = tokens.CountTokens();
203 
204  if( num != 4 && num != 8 )
205  return false;
206 
207  aSource1->m_source = tokens.GetNextToken();
208  aSource1->m_vstart = SPICE_VALUE( tokens.GetNextToken() );
209  aSource1->m_vend = SPICE_VALUE( tokens.GetNextToken() );
210  aSource1->m_vincrement = SPICE_VALUE( tokens.GetNextToken() );
211 
212  if( num == 8 )
213  {
214  aSource2->m_source = tokens.GetNextToken();
215  aSource2->m_vstart = SPICE_VALUE( tokens.GetNextToken() );
216  aSource2->m_vend = SPICE_VALUE( tokens.GetNextToken() );
217  aSource2->m_vincrement = SPICE_VALUE( tokens.GetNextToken() );
218  }
219 
220  return true;
221 }
222 
223 
224 void NETLIST_EXPORTER_PSPICE_SIM::writeDirectives( OUTPUTFORMATTER* aFormatter, unsigned aCtl ) const
225 {
226  // Add a directive to obtain currents
227  //aFormatter->Print( 0, ".options savecurrents\n" ); // does not work :(
228 
229  for( const auto& item : GetSpiceItems() )
230  {
231  for( const auto& current :
233  {
234  if( !item.m_enabled )
235  continue;
236 
238  aFormatter->Print( 0, ".save %s\n",
239  (const char*) ComponentToVector( item.m_refName, SPT_CURRENT, current )
240  .c_str() );
241  }
242  }
243 
244  // If we print out .save directives for currents, then it needs to be done for voltages as well
245  for( const auto& netMap : GetNetIndexMap() )
246  {
247  // the "0" and the "GND" nets are automaticallly saved internally by ngspice.
248  // Skip them
249  wxString netname = ComponentToVector( netMap.first, SPT_VOLTAGE );
250 
251  if( netname == "V(0)" || netname == "V(GND)" )
252  continue;
253 
254  aFormatter->Print( 0, ".save %s\n", (const char*) netname.c_str() );
255  }
256 
257  if( m_simCommand.IsEmpty() )
258  {
259  // Fallback to the default behavior and just write all directives
260  NETLIST_EXPORTER_PSPICE::writeDirectives( aFormatter, aCtl );
261  }
262  else
263  {
264  // Dump all directives but simulation commands
265  for( const auto& dir : GetDirectives() )
266  {
267  if( !IsSimCommand( dir ) )
268  aFormatter->Print( 0, "%s\n", (const char*) dir.c_str() );
269  }
270 
271  // Finish with our custom simulation command
272  aFormatter->Print( 0, "%s\n", (const char*) m_simCommand.c_str() );
273  }
274 }
wxString m_simCommand
Custom simulation command (has priority over the schematic sheet simulation commands)
wxString ComponentToVector(const wxString &aName, SIM_PLOT_TYPE aType, const wxString &aParam=wxEmptyString) const
Returns name of Spice dataset for a specific plot.
const SPICE_ITEM_LIST & GetSpiceItems() const
Returns list of items representing schematic components in the Spice world.
wxString GetUsedSimCommand()
Returns the command directive that is in use (either from the sheet or from m_simCommand.
const std::vector< wxString > GetDirectives() const
Returnss a vector of Spice directives found in the schematics.
Template specialization to enable wxStrings for certain containers (e.g. unordered_map)
Definition: bitmap.cpp:56
OUTPUTFORMATTER is an important interface (abstract class) used to output 8 bit text in a convenient ...
Definition: richio.h:327
SIM_PLOT_TYPE VectorToSignal(const std::string &aVector, wxString &aSignal) const
Returns name of Spice dataset for a specific plot.
static bool IsSimCommand(const wxString &aCmd)
Determines if a directive is a simulation command.
static SIM_TYPE CommandToSimType(const wxString &aCmd)
Returns simulation type basing on a simulation command directive.
void writeDirectives(OUTPUTFORMATTER *aFormatter, unsigned aCtl) const override
Saves the Spice directives.
static const std::vector< wxString > & GetCurrents(SPICE_PRIMITIVE aPrimitive)
Returns a list of currents that can be probed in a Spice primitive.
wxString GetSheetSimCommand()
Returns simulation command directives placed in schematic sheets (if any).
Helper class to handle Spice way of expressing values (e.g. 10.5 Meg)
Definition: spice_value.h:32
SIM_TYPE
Possible simulation types
Definition: sim_types.h:29
virtual void writeDirectives(OUTPUTFORMATTER *aFormatter, unsigned aCtl) const
Saves the Spice directives.
void UpdateDirectives(unsigned aCtl)
Updates the vector of Spice directives placed in the schematics.
const NET_INDEX_MAP & GetNetIndexMap() const
Returns a map of circuit nodes to net names.
static void ReplaceForbiddenChars(wxString &aNetName)
some chars are not accepted in netnames in spice netlists.
bool ParseDCCommand(const wxString &aCmd, SPICE_DC_PARAMS *aSource1, SPICE_DC_PARAMS *aSource2)
Parses a two-source .dc command directive into its components.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:201
SPICE_PRIMITIVE
Basic Spice component primitives
wxString UnescapeString(const wxString &aSource)
Definition: string.cpp:152
static bool empty(const wxTextEntryBase *aCtrl)
wxString GetSpiceDevice(const wxString &aComponent) const
Returns name of Spice device corresponding to a schematic component.
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Function Print formats and writes text to the output stream.
Definition: richio.cpp:404
SIM_PLOT_TYPE
Possible plot types
Definition: sim_types.h:44
SIM_TYPE GetSimType()
Returns simulation type basing on the simulation command directives.