KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sim_lib_mgr.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 <filename_resolver.h>
22#include <pgm_base.h>
23#include <string>
24#include <string_utils.h>
25#include <common.h>
26#include <functional>
27#include <sch_symbol.h>
28#include <schematic.h>
29
30// Include simulator headers after wxWidgets headers to avoid conflicts with Windows headers
31// (especially on msys2 + wxWidgets 3.0.x)
32#include <sim/sim_lib_mgr.h>
33#include <sim/sim_library.h>
34#include <sim/sim_model.h>
35#include <sim/sim_model_ideal.h>
36
37using namespace std::placeholders;
38
39
41 m_project( aPrj ),
42 m_forceFullParse( false )
43{
44}
45
46
48{
49 m_libraries.clear();
50 m_models.clear();
51}
52
53
54wxString SIM_LIB_MGR::ResolveLibraryPath( const wxString& aLibraryPath, REPORTER& aReporter )
55{
57
58 resolver.SetProject( m_project );
59
60 std::vector<const EMBEDDED_FILES*> embeddedFilesStack;
61
62 for( const EMBEDDED_FILES* embeddedFiles : m_embeddedFilesStack )
63 embeddedFilesStack.push_back( embeddedFiles );
64
65 wxString resolvedPath = resolver.ResolvePath( aLibraryPath, wxEmptyString,
66 std::move( embeddedFilesStack ) );
67
68 // If FILENAME_RESOLVER found the file, use that result
69 if( !resolvedPath.IsEmpty() )
70 {
71 wxFileName fn( resolvedPath );
72
73 if( fn.IsAbsolute() )
74 return fn.GetFullPath();
75 }
76
77 // Fall back to the original behavior for paths not found by FILENAME_RESOLVER.
78 // First expand any environment variables in the path.
79 wxString expandedPath = ExpandEnvVarSubstitutions( aLibraryPath, m_project );
80
81 // Convert to UNIX format
82 expandedPath.Replace( '\\', '/' );
83
84 wxFileName fn( expandedPath );
85
86 if( fn.IsAbsolute() && fn.Exists() )
87 return fn.GetFullPath();
88
89 wxFileName projectFn( m_project ? m_project->AbsolutePath( expandedPath ) : expandedPath );
90
91 if( projectFn.Exists() )
92 return projectFn.GetFullPath();
93
94 // Check relative to SPICE_LIB_DIR environment variable
95 wxFileName spiceLibFn( expandedPath );
96 wxString spiceLibDir;
97
98 wxGetEnv( wxT( "SPICE_LIB_DIR" ), &spiceLibDir );
99
100 if( !spiceLibDir.IsEmpty() && spiceLibFn.MakeAbsolute( spiceLibDir ) && spiceLibFn.Exists() )
101 return spiceLibFn.GetFullPath();
102
103 if( spiceLibDir.IsEmpty() || spiceLibFn.GetFullPath() == projectFn.GetFullPath() )
104 {
105 aReporter.Report( wxString::Format( _( "Simulation model library not found at '%s'" ),
106 projectFn.GetFullPath() ) );
107 }
108 else
109 {
110 aReporter.Report( wxString::Format( _( "Simulation model library not found at '%s' or '%s'" ),
111 projectFn.GetFullPath(),
112 spiceLibFn.GetFullPath() ) );
113 }
114
115 return aLibraryPath;
116}
117
118
119wxString SIM_LIB_MGR::ResolveEmbeddedLibraryPath( const wxString& aLibPath,
120 const wxString& aRelativeLib,
121 REPORTER& aReporter )
122{
123 wxFileName testPath( aLibPath );
124 wxString fullPath( aLibPath );
125
126 if( !testPath.IsAbsolute() && !aRelativeLib.empty() )
127 {
128 wxString relLib( aRelativeLib );
129
130 relLib = ResolveLibraryPath( relLib, aReporter );
131
132 wxFileName fn( relLib );
133
134 testPath.MakeAbsolute( fn.GetPath( true ) );
135 fullPath = testPath.GetFullPath();
136 }
137
138 wxFileName fn( fullPath );
139
140 if( !fn.Exists() )
141 fullPath = aLibPath;
142
143 fullPath = ResolveLibraryPath( fullPath, aReporter );
144
145 return fullPath;
146}
147
148
149void SIM_LIB_MGR::SetLibrary( const wxString& aLibraryPath, REPORTER& aReporter )
150{
151 wxString path = ResolveLibraryPath( aLibraryPath, aReporter );
152
154 return;
155
156 if( !wxFileName::Exists( path ) )
157 {
158 aReporter.Report( wxString::Format( _( "Simulation model library not found at '%s'" ),
159 path ) );
160 return;
161 }
162
163 std::unique_ptr<SIM_LIBRARY> library = SIM_LIBRARY::Create( path, m_forceFullParse, aReporter,
164 [&]( const wxString& libPath, const wxString& relativeLib ) -> wxString
165 {
166 return ResolveEmbeddedLibraryPath( libPath, relativeLib, aReporter );
167 } );
168
169 Clear();
170 m_libraries[path] = std::move( library );
171}
172
173
174SIM_MODEL& SIM_LIB_MGR::CreateModel( SIM_MODEL::TYPE aType, std::span<const wxString> aPins,
175 REPORTER& aReporter )
176{
177 m_models.push_back( SIM_MODEL::Create( aType, aPins, aReporter ) );
178 return *m_models.back();
179}
180
181
183 std::span<const wxString> aPins, REPORTER& aReporter )
184{
185 m_models.push_back( SIM_MODEL::Create( aBaseModel, aPins, aReporter ) );
186 return *m_models.back();
187}
188
189
190SIM_MODEL& SIM_LIB_MGR::CreateModel( const SIM_MODEL* aBaseModel, std::span<const wxString> aPins,
191 const std::vector<SCH_FIELD>& aFields, bool aResolve, int aDepth,
192 REPORTER& aReporter )
193{
194 m_models.push_back( SIM_MODEL::Create( aBaseModel, aPins, aFields, aResolve, aDepth, aReporter ) );
195 return *m_models.back();
196}
197
198
200 int aDepth, const wxString& aVariantName,
201 const wxString& aMergedSimPins )
202{
203 SIM_MODEL_INPUT input;
204 input.prefix = aSymbol.GetPrefix();
205
206 for( const SCH_FIELD& field : aSymbol.GetFields() )
207 {
208 if( field.GetId() == FIELD_T::REFERENCE )
209 {
210 input.fields.emplace_back( field.GetName(), aSymbol.GetRef( aSheetPath ) );
211 }
212 else if( field.GetId() == FIELD_T::VALUE || field.GetName().StartsWith( wxS( "Sim." ) ) )
213 {
214 const wxString value = !aMergedSimPins.IsEmpty() && field.GetName() == SIM_PINS_FIELD
215 ? aMergedSimPins
216 : field.GetShownText( aSheetPath, FOR_NETNAME, aVariantName, aDepth );
217 input.fields.emplace_back( field.GetName(), value );
218 }
219 }
220
221 input.inferencePins = SIM_MODEL::PinNumbers( aSymbol.GetPins( aSheetPath ) );
222 std::sort( input.inferencePins.begin(), input.inferencePins.end() );
223 input.modelPins = SIM_MODEL::PinNumbers( aSymbol.GetAllLibPins() );
224 std::sort( input.modelPins.begin(), input.modelPins.end(),
225 []( const wxString& lhs, const wxString& rhs ) { return StrNumCmp( lhs, rhs, true ) < 0; } );
226 return input;
227}
228
229
231 bool aResolve, int aDepth, const wxString& aVariantName,
232 REPORTER& aReporter, const wxString& aMergedSimPins )
233{
234 return CreateModel( CaptureModelInput( aSheetPath, aSymbol, aDepth, aVariantName, aMergedSimPins ),
235 aResolve, aReporter );
236}
237
238
239SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const SIM_MODEL_INPUT& aInput, bool aAllowRawFallback,
240 REPORTER& aReporter )
241{
242 std::vector<SCH_FIELD> fields;
243
244 for( const auto& [name, value] : aInput.fields )
245 {
246 fields.emplace_back( nullptr, FIELD_T::USER, name );
247 fields.back().SetText( value );
248 }
249
250 auto getOrCreateField =
251 [&fields]( const wxString& name ) -> SCH_FIELD*
252 {
253 if( SCH_FIELD* field = FindField( fields, name ) )
254 return field;
255
256 fields.emplace_back( nullptr, FIELD_T::USER, name );
257 return &fields.back();
258 };
259
260 wxString deviceType;
261 wxString modelType;
262 wxString modelParams;
263 wxString pinMap;
264 bool storeInValue = false;
265
266 // Infer RLC and VI models if they aren't specified
267 if( SIM_MODEL::InferSimModel( aInput.prefix, aInput.inferencePins, &fields, false, 0,
268 SIM_VALUE_GRAMMAR::NOTATION::SI, &deviceType, &modelType, &modelParams, &pinMap ) )
269 {
270 getOrCreateField( SIM_DEVICE_FIELD )->SetText( deviceType );
271
272 if( !modelType.IsEmpty() )
273 getOrCreateField( SIM_DEVICE_SUBTYPE_FIELD )->SetText( modelType );
274
275 getOrCreateField( SIM_PARAMS_FIELD )->SetText( modelParams );
276 getOrCreateField( SIM_PINS_FIELD )->SetText( pinMap );
277
278 storeInValue = true;
279 }
280
281 SIM_LIBRARY::MODEL model = createModel( fields, false, 0, aInput.modelPins, aReporter, aAllowRawFallback );
282
283 model.model.SetIsStoredInValue( storeInValue );
284
285 return model;
286}
287
288
289SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const std::vector<SCH_FIELD>& aFields,
290 bool aResolve, int aDepth,
291 std::span<const wxString> aPins,
292 REPORTER& aReporter )
293{
294 return createModel( aFields, aResolve, aDepth, aPins, aReporter, aResolve );
295}
296
297
298SIM_LIBRARY::MODEL SIM_LIB_MGR::createModel( const std::vector<SCH_FIELD>& aFields, bool aResolve, int aDepth,
299 std::span<const wxString> aPins, REPORTER& aReporter,
300 bool aAllowRawFallback )
301{
302 std::string libraryPath = GetFieldValue( &aFields, SIM_LIBRARY::LIBRARY_FIELD, aResolve, aDepth );
303 std::string baseModelName = GetFieldValue( &aFields, SIM_LIBRARY::NAME_FIELD, aResolve, aDepth );
304
305 if( libraryPath != "" )
306 {
307 return CreateModel( libraryPath, baseModelName, aFields, aResolve, aDepth, aPins, aReporter );
308 }
309 else
310 {
311 m_models.push_back( SIM_MODEL::Create( aFields, aResolve, aDepth, aPins, aReporter, aAllowRawFallback ) );
312 return { baseModelName, *m_models.back() };
313 }
314}
315
316
317SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const wxString& aLibraryPath,
318 const std::string& aBaseModelName,
319 const std::vector<SCH_FIELD>& aFields,
320 bool aResolve, int aDepth,
321 std::span<const wxString> aPins,
322 REPORTER& aReporter )
323{
324 wxString msg;
325 SIM_LIBRARY* library = nullptr;
326 SIM_MODEL* baseModel = nullptr;
327 std::string modelName;
328 wxString path = ResolveLibraryPath( aLibraryPath, aReporter );
329
330 auto it = m_libraries.find( path );
331
332 if( it == m_libraries.end() )
333 {
334 it = m_libraries.emplace( path, SIM_LIBRARY::Create( path, m_forceFullParse, aReporter,
335 [&]( const wxString& libPath, const wxString& relativeLib ) -> wxString
336 {
337 return ResolveEmbeddedLibraryPath( libPath, relativeLib, aReporter );
338 } ) ).first;
339 }
340
341 library = &*it->second;
342
343 if( aBaseModelName == "" )
344 {
345 msg.Printf( _( "Error loading simulation model: no '%s' field" ),
347
348 aReporter.Report( msg, RPT_SEVERITY_ERROR );
349
350 modelName = _( "unknown" ).ToStdString();
351 }
352 else if( library )
353 {
354 baseModel = library->FindModel( aBaseModelName );
355 modelName = aBaseModelName;
356
357 if( !baseModel )
358 {
359 msg.Printf( _( "Error loading simulation model: could not find base model '%s' "
360 "in library '%s'" ),
361 aBaseModelName,
362 path );
363
364 aReporter.Report( msg, RPT_SEVERITY_ERROR );
365 }
366 }
367
368 m_models.push_back( SIM_MODEL::Create( baseModel, aPins, aFields, aResolve, aDepth, aReporter ) );
369
370 return { modelName, *m_models.back() };
371}
372
373
374void SIM_LIB_MGR::SetModel( int aIndex, std::unique_ptr<SIM_MODEL> aModel )
375{
376 m_models.at( aIndex ) = std::move( aModel );
377}
378
379
380std::map<wxString, std::reference_wrapper<const SIM_LIBRARY>> SIM_LIB_MGR::GetLibraries() const
381{
382 std::map<wxString, std::reference_wrapper<const SIM_LIBRARY>> libraries;
383
384 for( auto& [path, library] : m_libraries )
385 libraries.try_emplace( path, *library );
386
387 return libraries;
388}
389
390
391std::vector<std::reference_wrapper<SIM_MODEL>> SIM_LIB_MGR::GetModels() const
392{
393 std::vector<std::reference_wrapper<SIM_MODEL>> models;
394
395 for( const std::unique_ptr<SIM_MODEL>& model : m_models )
396 models.emplace_back( *model );
397
398 return models;
399}
const char * name
Provide an extensible class to resolve 3D model paths.
Container for project specific data.
Definition project.h:63
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
virtual bool HasMessageOfSeverity(int aSeverityMask) const
Returns true if the reporter has one or more messages matching the specified severity mask.
Definition reporter.h:152
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Schematic symbol object.
Definition sch_symbol.h:75
std::vector< const SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
std::vector< SCH_PIN * > GetAllLibPins() const
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
wxString GetPrefix() const
Definition sch_symbol.h:239
static std::unique_ptr< SIM_LIBRARY > Create(const wxString &aFilePath, bool aForceFullParse, REPORTER &aReporter, const std::function< wxString(const wxString &, const wxString &)> &aResolver)
Read library from a source file (e.g.
static constexpr auto LIBRARY_FIELD
Definition sim_library.h:31
static constexpr auto NAME_FIELD
Definition sim_library.h:32
void SetModel(int aIndex, std::unique_ptr< SIM_MODEL > aModel)
SIM_LIBRARY::MODEL createModel(const std::vector< SCH_FIELD > &aFields, bool aResolve, int aDepth, std::span< const wxString > aPins, REPORTER &aReporter, bool aAllowRawFallback)
wxString ResolveLibraryPath(const wxString &aLibraryPath, REPORTER &aReporter)
const PROJECT * m_project
Definition sim_lib_mgr.h:99
std::vector< EMBEDDED_FILES * > m_embeddedFilesStack
Definition sim_lib_mgr.h:98
bool m_forceFullParse
SIM_LIB_MGR(const PROJECT *aPrj)
std::vector< std::unique_ptr< SIM_MODEL > > m_models
SIM_MODEL & CreateModel(SIM_MODEL::TYPE aType, std::span< const wxString > aPins, REPORTER &aReporter)
std::map< wxString, std::reference_wrapper< const SIM_LIBRARY > > GetLibraries() const
wxString ResolveEmbeddedLibraryPath(const wxString &aLibPath, const wxString &aRelativeLib, REPORTER &aReporter)
std::vector< std::reference_wrapper< SIM_MODEL > > GetModels() const
static SIM_MODEL_INPUT CaptureModelInput(const SCH_SHEET_PATH *aSheetPath, const SCH_SYMBOL &aSymbol, int aDepth, const wxString &aVariantName, const wxString &aMergedSimPins=wxEmptyString)
Resolve instance fields once; editors use the field-based overloads for unresolved input.
void SetLibrary(const wxString &aLibraryPath, REPORTER &aReporter)
std::map< wxString, std::unique_ptr< SIM_LIBRARY > > m_libraries
static std::unique_ptr< SIM_MODEL > Create(TYPE aType, std::span< const wxString > aPins, REPORTER &aReporter)
static bool InferSimModel(const wxString &aPrefix, std::span< const wxString > aPins, std::vector< SCH_FIELD > *aFields, bool aResolve, int aDepth, SIM_VALUE_GRAMMAR::NOTATION aNotation, wxString *aDeviceType, wxString *aModelType, wxString *aModelParams, wxString *aPinMap)
aPins follows lexical symbol-pin number order, including duplicate numbers.
static std::vector< wxString > PinNumbers(std::span< const SCH_PIN *const > aPins)
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:776
@ FOR_NETNAME
Definition common.h:90
#define _(s)
static FILENAME_RESOLVER * resolver
see class PGM_BASE
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_UNDEFINED
wxString GetFieldValue(const std::vector< SCH_FIELD > *aFields, FIELD_T aFieldType)
Definition sch_field.h:431
const SCH_FIELD * FindField(const std::vector< SCH_FIELD > &aFields, FIELD_T aFieldId)
Definition sch_field.h:393
#define SIM_PINS_FIELD
Definition sim_model.h:51
#define SIM_DEVICE_FIELD
Definition sim_model.h:49
#define SIM_PARAMS_FIELD
Definition sim_model.h:53
#define SIM_DEVICE_SUBTYPE_FIELD
Definition sim_model.h:50
std::vector< wxString > inferencePins
Selected-unit pin numbers in lexical order, including duplicates.
std::vector< std::pair< wxString, wxString > > fields
Resolved shown values in symbol order, adapted to USER fields without further expansion.
std::vector< wxString > modelPins
All library pin numbers in natural (StrNumCmp) order, including duplicates.
@ USER
The field ID hasn't been set yet; field is invalid.
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
std::string path
KIBIS_MODEL * model