-
Notifications
You must be signed in to change notification settings - Fork 59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for parsing Fourier Analysis #55
Conversation
Hi Kyle, I'll only have time to make a proper revision later this week. Best Regards, |
Hi Nuno, Sounds good. Unfortunately, a data frame alone is insufficient as the DC value and THD don't fit in the table. Newer versions of pandas have an attrs attribute that can be used to store metadata, but according to the documentation it is still experimental, so there is some risk involved with that approach. It is also less obvious where to find that information with that approach. Here's an example of the output of a Fourier analysis to give you a better idea of what it looks like. There can be any number of rows in the table.
-Kyle |
Hello Kyle, Finally I have a moment to revise your code. Again sorry for the late reply. On the LTSteps.py I have a few remarks. On line 360, I think you're missing a backslash before the point if you want that the regular expression matches the point.
I like the pandas library, but, since it is adding a dependency, I was wondering whether it couldn't be supported by a dedicated class that could provide a better interface to the user. Able to also compute other things like relative differences of fourier components in regards to a reference signal. Simulation accuracy and data compression have an impact on the fourier analysis, so, maybe a relative distortion could make sense. It would also allow to get harmonics on a single trace, or the frequency component on a list of traces. Or to calculate SNR and SINAD when providing a noise level (different simulation). The library dependencies was so far an important point for me. You might note that even the usage of numpy as been so far an optional. Maybe this no longer makes sense, if considering that almost anyone making signal analysis, should have numpy and pandas installed. What are your thoughts one that? On line 391 and a few more time below, you are using this construct var_name: type_name = value. Thanks again for your initiative to share your code. I can already say welcome your code, irrespective of you agreeing with my suggestion regarding having dedicated class. Best Regards, |
Hi Nuno, Thank you for the feedback!
I'm going to be pretty busy the next few weeks, but I will try to find time to work on the class for storing harmonic information. Let me know if you have any more thoughts! -Kyle |
Hi Kyle, I'm happy that we are converging. I was afraid that you wouldn't. I agree with you on point 4. I really like type hinting, as it brings some formalism to a language that otherwise has none. Type hinting has very often prevented some bugs, so, I'm definitely in favor of it. On the second value of THD, I've spent some time searching for an explanation. The best I could was an answer directly from Mike Engelhardt which says: Generally, most people just ignore the second value. Before moving forward and start implementing, I think it makes sense to take a moment to define an interface. Having it defined, will help define an implementation. Here are some ideas to seed a possible interface. Note that none of this is binding in any way. So, having said this disclaimer, here it goes:
In this object I could imagine it returning all the data you are now storing. So, it would translate to the following:
As said... any of this is binding... just brainstorming. Have a great Sunday ahead, Nuno |
Hi again, I just remembered that more than one .FOUR command can be issued for the same net and with different frequencies. Cheers, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accepting the pull request.
I'll just move the pandas dependency to be local and only required when Fourier analysis is required.
Later this dependency will be dropped in favor of a dedicated class.
Hi @kgoodrick-rl , I hope you had a good summer and matching holidays. |
Hi @nunobrum, Sorry for the delay in getting back to you! I just moved, so things have been a little hectic the last several weeks. Hope things are going well for you too! I have finally found time to come back to this (thanks for the reminder) and I think I have a solution that I think should work quite well. It's currently implemented with dataclasses, but if we want python <3.6 compatibility I think we could implement the dataclass features we need on our own. The first pass at this can be seen here: https://github.com/kgoodrick-rl/PyLTSpice/tree/fourier_interface As a high level overview, the data is stored in the log's As an example here is how you would use it to get the fourier component of the third harmonic of the V(a) waveform: key = FourierKey('V(a)', frequency, n_harmonics, n_periods) # These are all parameters specified in the Fourier spice command
third_harmonic = log.fourier[0][key][3].fourier_component # 0 is the step The reason I am using a key object instead of just the net name is that someone could theoretically run multiple Fourier analysis on the same waveform, but with different fundamentals, periods, or number of harmonics. We could maybe have a helper function though that figures everything out if there is only one Fourier analysis for a given waveform? The way it's implemented now the step indexing happens at the beginning, but I think it would be possible to have a redefined Let me know what you think. Best, Kyle |
Hi Kyle, sorry for the late reply. This was a very busy week and only today have some time to check your solution. As for the avoiding the dataclasses for 3.6 (Note 1) compatibility, I think we could eventually avoid dataclasses, but, it would make the code much more verbose. I confess to be a bit divided regarding this. I have no real perception of what is the python version adopting across the community, but, I'm going to settle with the gut feeling that most people have versions higher then 3.7. Back to implementation. I like the idea of keying all data in a single structure, which avoids the long sequences of [ ][ ] of the previous implementation. I was worried about the safety of letting the compiler hash all that information, but, as far as I understood of the dataclasses, it is. I was a bit surprised by the implementation of appending the empty dictionary to a list and then filling that dictionary using the last position of the list. Finally, the (Note 1) I think it's actually 3.7 (https://peps.python.org/pep-0557/) All the best and wishing you a good rest of the weekend, Nuno |
Hi Nuno, I'm glad you like the solution! You are correct that dataclasses are python 3.7 and above, my bad. My thought is that anyone writing new code will probably be on python 3.9 or 3.10, for anyone with existing code on older versions they should be ok by just continuing to use the older version of PyLTSpice. That is my understanding of dataclasses as well. I have used them as keys many times and have not had any problems doing so. You are also right that implementing them ourselves will be much more verbose. That was my initial plan, but it ended up being considerably more effort, so I reverted to dataclasses. I'm not particularly set on this way of indexing the results while parsing them, it just seemed the most obvious to me when I was writing it. We could potentially use Should we modify the indexing and think about a fancier Thanks, Kyle |
Hi Kyle, I'm OK with you just opening a pull request. So far I've been testing all the code I write manually, but I'm thinking about using automating the code tests with unittests. Cheers, |
Hi Nuno,
First of all, thank you for creating and sharing this library! I have found it very useful for my work. On a recent project I needed to parse the Fourier analysis output in LTSpice, so I added in the functionality, and this pull request contains those additions.
The Fourier results are stored in the log dataset dictionary with the following structure:
All Fourier analysis is stored in the dataset under the "fourier" key, and the base level is a dictionary keyed by the fundamental frequency as a float. Each value of the base dict is another dictionary keyed by the number of periods of the analysis. The values for this dictionary are another dictionary keyed by the name of the waveform. This is the final dictionary, and it contains a list of the results for each step in the simulation.
The results are a dictionary with three items:
'dc'
'thd'
'harmonics'
a. The dataframe has columns
['Harmonic Number', 'Frequency [Hz]', 'Fourier Component', 'Normalized Component', 'Phase [degree]', 'Normalized Phase [degree]']
b. Each row is a different harmonic
Here is an example of how to get to the result dictionary:
This is functional, though I'm not crazy about how deep the nesting goes (especially if you want to access something in the dataframe), so I'm open to suggestions on alternates.
To get this working with some of the recent changes I had to change the function that detects the encoding as my version of LTSpice outputs files with
cp1252
encoding. During this function I read the entire file to ensure there are no errors at any point. This is not a problem with the log files I am dealing with, but it could be if there were a large log file with many steps.I also changed the way the log file is read such that it is in a context manager.
Let me know what you think and if there's anything in the code you'd like changed or added.
Thanks,
Kyle