KiCad PCB EDA Suite
Loading...
Searching...
No Matches
spice_generator.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) 2022 Mikolaj Wielgus
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
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, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <sim/spice_generator.h>
22
23#include <boost/algorithm/string/predicate.hpp>
24#include <boost/algorithm/string/trim.hpp>
25#include <fmt/core.h>
26
27
28std::string SPICE_GENERATOR::ModelName( const SPICE_ITEM& aItem ) const
29{
30 if( aItem.baseModelName == "" )
31 return fmt::format( "__{}", aItem.refName );
32
33 if( m_model.requiresSpiceModelLine( aItem ) )
34 return fmt::format( "{}.{}", aItem.refName, aItem.baseModelName );
35
36 return aItem.baseModelName;
37}
38
39
40std::string SPICE_GENERATOR::ModelLine( const SPICE_ITEM& aItem ) const
41{
42 if( !m_model.requiresSpiceModelLine( aItem ) )
43 return "";
44
45 std::string result;
46
47 result.append( fmt::format( ".model {} ", aItem.modelName ) );
48 size_t indentLength = result.length();
49
50 const SIM_MODEL::SPICE_INFO& spiceInfo = m_model.GetSpiceInfo();
51 result.append( spiceInfo.modelType );
52
53 if ( !spiceInfo.isDefaultLevel && !spiceInfo.level.empty() )
54 result.append( fmt::format( " level={}", spiceInfo.level ) );
55
56 if ( !spiceInfo.version.empty() )
57 result.append( fmt::format( " version={}", spiceInfo.version ) );
58
59 result.append( "\n" );
60
61 for( int ii = 0; ii < m_model.GetParamCount(); ++ii )
62 {
63 const SIM_MODEL::PARAM& param = m_model.GetParam( ii );
64
65 if( param.info.isSpiceInstanceParam )
66 continue;
67
68 std::string name;
69 std::string value;
70
71 if( !param.info.spiceModelName.empty() )
72 {
73 name = param.info.spiceModelName;
74 }
75 else
76 {
77 // Because of collisions with instance parameters, we append some model parameters
78 // with "_".
79 if( boost::ends_with( param.info.name, "_" ) )
80 name = param.info.name.substr( 0, param.info.name.length() - 1 );
81 else
82 name = param.info.name;
83 }
84
85 value = SIM_VALUE::ToSpice( param.value );
86
87 if( value == "" )
88 continue;
89
90 if( param.info.category == SIM_MODEL::PARAM::CATEGORY::FLAGS )
91 {
92 if( value == "1" )
93 result.append( fmt::format( "+{}\n", name ) );
94 }
95 else
96 {
97 result.append( fmt::format( "+{}{}={}\n",
98 std::string( indentLength - 1, ' ' ),
99 name,
100 value ) );
101 }
102 }
103
104 // Don't send SPICE empty models.
105 if( result.length() == indentLength + 1 /* line ending */ )
106 result.clear();
107
108 return result;
109}
110
111
112std::string SPICE_GENERATOR::ItemLine( const SPICE_ITEM& aItem ) const
113{
114 SPICE_ITEM item = aItem;
115
116 if( item.pinNumbers.empty() )
117 {
118 for( int i = 0; i < m_model.GetPinCount(); ++i )
119 item.pinNumbers.push_back( fmt::format( "{}", i + 1 ) );
120 }
121
122 if( item.pinNetNames.empty() )
123 {
124 for( const SIM_MODEL_PIN& pin : GetPins() )
125 item.pinNetNames.push_back( pin.modelPinName );
126 }
127
128 std::string result;
129 result.append( ItemName( aItem ) );
130 result.append( ItemPins( aItem ) );
131 result.append( ItemModelName( aItem ) );
132 result.append( ItemParams() );
133 result.append( "\n" );
134 return result;
135}
136
137
138std::string SPICE_GENERATOR::ItemName( const SPICE_ITEM& aItem ) const
139{
140 if( aItem.refName != "" && boost::starts_with( aItem.refName, m_model.GetSpiceInfo().itemType ) )
141 return aItem.refName;
142 else
143 return fmt::format( "{}{}", m_model.GetSpiceInfo().itemType, aItem.refName );
144}
145
146
147std::string SPICE_GENERATOR::ItemPins( const SPICE_ITEM& aItem ) const
148{
149 std::string result;
150 int ncCounter = 0;
151
152 for( const SIM_MODEL_PIN& pin : GetPins() )
153 {
154 auto it = std::find( aItem.pinNumbers.begin(), aItem.pinNumbers.end(),
155 pin.symbolPinNumber );
156
157 if( it == aItem.pinNumbers.end() )
158 {
159 result.append( fmt::format( " NC-{}-{}", aItem.refName, ncCounter++ ) );
160 }
161 else
162 {
163 long symbolPinIndex = std::distance( aItem.pinNumbers.begin(), it );
164 result.append( fmt::format( " {}", aItem.pinNetNames.at( symbolPinIndex ) ) );
165 }
166 }
167
168 return result;
169}
170
171
172std::string SPICE_GENERATOR::ItemModelName( const SPICE_ITEM& aItem ) const
173{
174 return fmt::format( " {}", aItem.modelName );
175}
176
177
179{
180 std::string result;
181
182 for( int ii = 0; ii < m_model.GetParamCount(); ++ii )
183 {
184 const SIM_MODEL::PARAM& param = m_model.GetParam( ii );
185
186 if( !param.info.isSpiceInstanceParam )
187 continue;
188
189 std::string name = param.info.spiceInstanceName.empty() ? param.info.name
190 : param.info.spiceInstanceName;
191 std::string value = SIM_VALUE::ToSpice( param.value );
192
193 if( param.info.category == SIM_MODEL::PARAM::CATEGORY::FLAGS )
194 {
195 if( value == "1" )
196 result.append( fmt::format( " {}", name ) );
197 }
198 else
199 {
200 if( value != "" )
201 result.append( fmt::format( " {}={}", name, value ) );
202 }
203 }
204
205 return result;
206}
207
208
209std::string SPICE_GENERATOR::TunerCommand( const SPICE_ITEM& aItem, double aValue ) const
210{
211 // No tuning available by default.
212 return "";
213}
214
215
216std::vector<std::string> SPICE_GENERATOR::CurrentNames( const SPICE_ITEM& aItem ) const
217{
218 return { fmt::format( "I({})", ItemName( aItem ) ) };
219}
220
221
222std::string SPICE_GENERATOR::Preview( const SPICE_ITEM& aItem ) const
223{
224 std::string spiceCode = ModelLine( aItem );
225
226 std::string itemLine = ItemLine( aItem );
227
228 if( spiceCode != "" )
229 spiceCode.append( "\n" );
230
231 spiceCode.append( itemLine );
232 return boost::trim_copy( spiceCode );
233}
234
235
const char * name
static std::string ToSpice(const std::string &aString)
virtual std::string ItemName(const SPICE_ITEM &aItem) const
virtual std::string ItemPins(const SPICE_ITEM &aItem) const
virtual std::string ItemLine(const SPICE_ITEM &aItem) const
virtual std::string ModelLine(const SPICE_ITEM &aItem) const
virtual std::vector< std::reference_wrapper< const SIM_MODEL_PIN > > GetPins() const
virtual std::string ItemParams() const
const SIM_MODEL & m_model
virtual std::string Preview(const SPICE_ITEM &aItem) const
virtual std::string ItemModelName(const SPICE_ITEM &aItem) const
virtual std::string TunerCommand(const SPICE_ITEM &aItem, double aValue) const
virtual std::string ModelName(const SPICE_ITEM &aItem) const
virtual std::vector< std::string > CurrentNames(const SPICE_ITEM &aItem) const
std::string spiceModelName
Definition sim_model.h:383
std::string spiceInstanceName
Definition sim_model.h:384
std::string value
Definition sim_model.h:397
const INFO & info
Definition sim_model.h:398
std::string modelType
Definition sim_model.h:302
std::string version
Definition sim_model.h:307
std::string refName
std::string modelName
std::vector< std::string > pinNetNames
std::string baseModelName
std::vector< std::string > pinNumbers
KIBIS_PIN * pin
wxString result
Test unit parsing edge cases and error handling.