[NF] Iron Loss Calculation
SebGue opened this issue · 27 comments
Hello,
I would like to include a first implemention on iron loss calculation based on the new FEA results feature.
Therefore some things have to be discussed to find an apropriate and flexible solution.
At first there has to be an IronLossModel class to regard the different calculation approaches (Steinmetz, ...). Actually I'm not quite sure on how to apply the model on the magnetics output. Maybe the magnetics simulation should have an additional attribute iron_loss_model of the respective class. The only thing importent to me is, that the model can be updated even after the magnetics simulation is done to avoid unnecessary FEA (FEMM) calculations. Even a list of calculation models would be good to compare results.
A nice to have feature would be that IronLossModel is as much flexable, that it also could be applied to analytical magnetic models.
Next thing are the material/model loss parameter. At first I thought of some kind of user specified material attribute or an extra loss parameter data file, but I don't really like that idea. It would be a mess with different loss models and/or parameter sets.
A solution I actually like is to have the (watts per kg) loss data as function of B and f within the material data. Every loss model will then have a method to fit its loss parameters to the material data. The fitting functions could also be objects for instance with different fitting algorithms or weights. Nevertheless the loss models should also have the possiblity to overwrite these parameters.
Furthermore, we have to think on how to apply the loss calculation to the different parts of the machine, i.e. laminated vs. solid materials.
Yet there are some further (minor) points:
- where and how to store the results and estimated/used model parameters
- how to verfiy valid periodicity of the input data
- inclusion of correction factors and cutting edge effects
So, finally what is your opinion on this issue?
Best regards,
Sebastian
Hello,
On my side, I will mostly talk about the architecture part (as I personally don't have knowledge on the iron losses computation yet). Here is my proposal based on your suggestion:
As you said, the first step is to introduce an abstract IronLoss object that has several daughters corresponding to the different model (Steinmetz, ...). We should then introduce a property for this object in the Magnetic abstract object. That way any Magnetic model (including analytical ones) can then call the IronLoss computation. In the run method of Magnetic we can update the code to:
self.comp_time_angle(output)
self.comp_flux_airgap(output)
if self.iron_loss is not None:
self.iron_loss.comp_iron_loss(output)
Indeed the Iron losses computation is not mandatory for the Magnetic model which main output is the airgap flux.
The first part of the code of the method "comp_iron_loss" (or maybe should we name it run ?) is to check if the needed output are available. For instance, the first model would require to have a magnetic MeshSolution object in the output object (and won't work with analytical Magnetic model).
It is possible to run a flux computation without an iron losses model. Then one can add a IronLoss object to the Output(.simu.mag) object and call out.simu.mag.iron_loss.comp_iron_loss(out). It would just run the IronLoss computation and update the Output object. Note that you can also load an existing Output object and compute iron losses with this method.
All the parameter to tune the model should be the properties of the corresponding object with meaningful default value or a method to find such values according to the simulation. The high level parameter of the model should be defined in the abstract IronLoss object.
The output can be gathered in a new object OutIronLoss that would be a new property for OutMag. If the iron losses were not computed this object is None. The OutIronLoss would contain a new MeshSolution corresponding to the iron losses computation.
Finally the first model should use the getter/setter of the MeshSolution object to be as generic as possible. That way this iron loss model can be used what ever software was used to compute the Mesh.
Best regards,
Pierre
Hello Sebastian,
About the FEA calculation part:
Do you need a module for shape functions ?
I can give you a basic object to interpolate the value of the magnetic field on the Triangles.
Of course the shape function is rather simple for FEMM (1 inside the triangle, 0 outside), but it would give a good example for further discussions.
Best regards (and merry christmas!),
Raphaël
Hello Raphaël,
sorry my long response time on this issue. Uptill now I have been working on the most basic GUI features since this will be starting point for most people looking at pyleecan. I will come back to this issue as soon as possible.
In general, shape functions would be a good idea.
BTW: Whats the topic of your PhD? Whats the things your focus is on at the moment?
Best regards,
Sebastian
Hello Sebastian,
Great !
Okay ! I am going to provide you a basic constant by pieces shape function for the magnetic flux/field from FEMM.
We are currently looking for a coupling with Fenics project, especially for the mesh methods and know-how. So we may change things inside the Mesh & Solution Classes to ensure compatibility.
My PhD topic is about magneto-mechanical coupling for noise and vibration in electrical machines ;-)
Hello Pierre, hello Raphaël,
I think this issue need some more discussion on the arcitecture, before I actually start to work on it.
@BonneelP: Your proposal make some sense, nevertheless I was wondering if it would be better to have an general OutLosses attribute within Output class (instead wihtin OutMag).
There are multiple types of losses (iron, magnets, mechanical, ...) that could be stored there. Further, it may be interessting to run multiple loss models on one type of losses without the need to have multiple instances of Output with the same OutMag results or to have different models on the rotor and the stator for instance.
So the only solution I see, would be to use a list to store the output within OutLosses.
Another question is, how do you organize your simulations in general. The "normal" use case for the most users would be to evaluate the machine performance (i.e. the torque per current characterisics). Therefore, one would calculate the magnetics output for multiple current values and maybe also different rotor angles (to average torque).
Do you split the tuples of current and rotor angles into multiple instances of Output class, do you have (internal?) helper class to organize your simulation or do you calulate all at once and split the results?
Hello Sebastian,
The current philosophy of the Output object is to gather the output per physics. But the losses are very interesting Output so it can make sense to gather them in their dedicated object. So why not :) Do you have a more exhaustive list of what the object can contains ?
Regarding the comparison of several losses model, the current workflow would be to run several simulations and then use the out_list argument of the corresponding post-processing (like plot_B_space). To avoid running twice unnecessary computation, maybe we can use some new Input object (we run the magnetic model, then we import the results into another simulation as an input for the losses model).
I agree a list of OutLosses in Output would be interesting but then we would need a list of simulation as well (and then a list for all the other output because other people would want to compare other output). I think that such kind of simulation / comparison will be a significant part of the future use of pyleecan (to help develop new model by comparing to existing one) so it would be great if we find a convenient way to handle it. Note that we have worked on a Optimization module (than we should share soon) that defines several simulations and gather some results. Maybe there will be some things to reuse.
Regarding several current and rotor angle, you can take inspiration from the validation test EM_SPMSM_FL_001. In this one we have 4 time steps so 4 values of torque at the end of the simulation. In solve_FEMM Tem is defined with a size of (Nt_tot,1) and we compute it for each time step. Does it answer your question or are you talking about something else ?
Best regards,
Pierre
Hello Pierre,
thank you for your quick resonse.
Regarding several current and rotor angle, you can take inspiration from the validation test EM_SPMSM_FL_001. In this one we have 4 time steps ...
In this test there are multiple rotor angles and currents, but the equivalent dq-current seems to be constant.
If one would like to compute the torque vs. current charateristic of a machine for instance, it would require a lot of such computations, i.e. a lot of instantces of Output (maybe 10 to 100 or more). So it would be good to collect this outputs somehow. I think this is the key.
I also think that the outlined computation will be one of the main use cases, since one can calculate the well known efficiency maps from it for instance.
(But maybe there is also a smarter way to evaluate machine performance with less computations :-) )
To avoid running twice unnecessary computation, maybe we can use some new Input object ...
Yes, you are right. We should use a new Input and referenced data (to have resonable memory usage) for that purpose.
This in turns require that we also load and save references. But this is something I was thinking about for a while. I think, I will add alternative load/save functions to do this soon.
Note that we have worked on a Optimization module ...
Would you mind to create a branch so I can have a look at your approach?
Do you have a more exhaustive list of what the object can contains ?
For instance, we could split the losses depending on the location of their occurance, i.e.
- stator/rotor lamination
- stator/rotor magnets
- stator/rotor winding
- frame
- shaft
- mechnical losses (bearing, windage)
- addional losses
So the corresponding simulation input object should have the same properties to set the respective models maybe with some defaults for laminatied, solid and semi solid materials. Additional there should be some good default getters for instance for the mean losses on each region.
There is a lot of things to consider but I think we will find a resonable solution.
Best regards,
Sebastian
Hello Pierre,
thank you for your mail. I will answer to it soon.
Meanwhile I had a look at the optimization module and had some thoughts, I would like to share.
I think for the multiple currents, it would be best to use one output object per current value (with multiple rotor positions). On that output one can also calculate losses for instance.
To store the resulting multiple outputs, the comming OutputMulti class seems to be perfect. Don't you think so?
The only question is, what are the attributes 'design_var' and 'design_var_name' for. If it's only for optimization purpose, you could maybe move them to the respective daugther class. Maybe also a 'desc' property could be helpful.
Regarding loss output organisation, I didn't have an idea yet, especially with your new multiple stator/rotor laminations in mind.
Best regard,
Sebastian
... almost forgot.
I would also add a method to Simu1 to simplify setup of simulation by only giving current and rotor angle values.
Hello Sebastian,
I agree with you concerning the OutputMulti class, These attributes were in MultiOutput because they could fit with different type of multiple simulations but OutputMulti should be as generic as possible so I will push the modifications soon.
Best regards,
Cédric
Hello Sebastian,
I think that we should introduce a new abstract class "SimMulti" (not a daughter of Simulation) to gather all the object that would create a list of Simulation and OutputMulti object.
One interest of the OutputMulti is that it stores every single output which enables to call any post-processing for each of the composing simulation. I think that when defining a multi simulation we are always looking to compute (and plot) something as a function of something else. So I think that keeping a equivalent of design_var in OutputMulti can make sense. For instance the "SimMulti" daughter that would compute the torque vs. current characteristic could define the current characteristic as the design parameter of this multi simulation. We then need to find a proper name to replace "fitness" to store the result of these simulations. That way we would have the needed matrices to post-process the simulation directly available in the OutputMulti object instead of having to search each values in the output list. At this point we can even introduce an option to clean the output list to optimize the memory.
Regarding the losses, I think that we can create a generic object that gather all the losses of one lamination. If the lamination has no magnet, its magnet losses would be set to 0 to avoid having to create OutLossLamMag, OutLossLamWind.... For MachineUD, we can have a list of such objects. It is interesting to think about the more complex objects when adding new feature in Pyleecan but we still can say that we will, one day, add a daughter class that would handle the losses for these complex machine to focus on the one which we be used most of the time.
Regarding the Simu1 method to simplify the setup of the simulation maybe it should be a method of InCurrent instead ? One could create the InCurrent empty and call this method to set it more easily. For now all Simu1 begins with an InCurrent, but the aim is to add new model/physics to introduce new starting points.
Best regards,
Pierre
Hello Pierre,
I'm not quiet sure why you would like to have a new "SimMulti" class. What will be the benefit over OutputMulti. I see OutputMulti as a general purpose output data storage. It already contains the Simulation object within the Output objects.
Do you intent to use OutputMulti for optimization purpose only?
Regarding memory, what do you think of the idea to save and restore object references?
Best regards, Sebastian
Hello Sebastian,
As Simulation is an abstract class to gather all the simulation object like "Simu1", SimMulti would be an abstract class to gather all the classes that generates several Simulation object (for instance, optimization and torque VS current).
That way as Output contains the simulation object that produced it, OutputMulti can contain the "SimMulti" that produced it. We could then defined method like "get_design_var" for all multi simulations. The aim is to use OutputMulti for the result of any SimMulti and to be able to reuse the same post-processing (plot a value as a function of an input over several simulations).
I think that using reference is a good idea. It can be handled properly in the SimMulti daughters when we generates the list of simulation to run.
Best regards,
Pierre
Hello Pierre,
I've nearly finished my first 'preview' of the loss calculation and would like to do a pull request to discuss the code with you.
But I'm not quiet sure where I should do the PR. Would you mind to create a 'Losses' branch on your EOMYS-Public account therefore?
BR Sebastian
Hello Sebastian,
I've just created the "Losses" branch so you can do your PR there.
Have a nice day,
Helene
Hello all,
I've just did a PR on my current work on the iron loss computation and would like to add some explanation on the structure here.
- I introduced a loss property to Output class to store the loss output and an OutLoss class for that purpose.
- Further I introduced a loss property to Simu class for an abstract Loss class to contain the loss model.
- As all the Simu properties, derived Loss classes need a run() method to do the simulation.
- There is a new Loss1 class inherited by Loss, that contains the loss models (of abstract class LossModel) to run on different machine parts (e.g. stator lamination).
- The first actual loss model is LossModelBertotti.
- I also added loss data to the 'M400-50A.xlsx' to use these data for setting up loss model parameters automatically.
The LossModelBertotti has the loss model parameters as properties. The coresponding model equation is as follows:
LossDensity = k_hy*f*B^alpha_hy + k_ed*(f*B)^alpha_ed + k_ex*(f*B)^alpha_ex
Every single parameter can be 'None' and will be estimated automatically based on the materials loss data. Nevertheless the parameter estimation is not stable in all cases. So it may be good practice to at least specify the alphas with alpha_ed and alpha_ex having different values. The k parameter also can be zero if a certain loss component (e.g. excess losses) is'nt needed.
At the moment only the stator lamiantion losses are calculated and stored, while the results are not validated yet.
As a first improvement regarding the data structure, I think it could be better to store the loss models as well as the outputs into dicts similar to the groups of meshsolution.
The general question is how to 'tell' the different loss simulation models where to get there input data from and where to store the results.
If someone need some additional explanation don't hesitate to ask. I will try to answer as fast as possible.
Also any suggestions regarding data structure, CO and other improvements are very welcome.
BR Sebastian
I have updated the data structure as mentioned above. Only, I decided to store models and results within lists instead of dicts for now. Respective getter methods are already in the class ref but have to been implemented.
Any feedback is very welcome.
Best regards, Sebastian
Hello Sebastian
I went back from holiday on Monday and I have finally some time to take a look on this great contribution that you shared with us :)
Here are few questions/tough that I have regarding your new module:
- Great idea to provide a dedicated module and output. At the beginning I thought about having it as a part of the Magnetic module but it is better that way: we can then extend to other physics / model.
- During recent work and by reading your code I realize that we can go further than the Simu1 module order (elec => mag => force => structural => acoustic). I starting to think about a "module_list" parameter to define a simulation with what is really meaningful in the correct order. That would introduce a "Module" abstract class and a proper way of indicating what are the required input/output to use the module.
- If I understood well, the point of having models as a list is to provide a different model for each part of machine rather than having a list for the group property of LossModelBertotti. Is that right ?
- In LossModelBertotti, you have some property regarding the material and length of the lamination. Do you think that we can find a way to replace L1, mat_type and name by the lamination object directly ? Having direct access to the corresponding lamination can provide much more informations / methods and would be easier to parametrize.
As a first improvement regarding the data structure, I think it could be better to store the loss models as well as the outputs into dicts similar to the groups of meshsolution.
The general question is how to 'tell' the different loss simulation models where to get there input data from and where to store the results.
I agree that dictionary would be better, in particular when the LossModel have a name property (checked to be unique). What is the list of key that we can expect for these dictionary ? Maybe we can normalize the key so that we can construct a method to set the proper name/key for each machine part. Then output.loss can be a dictionary of dictionary. For instance output.loss["stator"]["loss_type_1"] (maybe one day several physics would be available in the loss module)
At the end, I think that a useful post-processing should be able to gather the data from all dictionary to display the contributions of each part on a loss type. What do you think of this organization ?
Again thank you for your contribution,
Best regards,
Pierre
Hello Pierre,
Thank you for your feedback! I will go though it.
Here are few questions/tough that I have regarding your new module:
During recent work and by reading your code I realize that we can go further than the Simu1 module order (elec => mag => force => structural => acoustic). I starting to think about a "module_list" parameter to define a simulation with what is really meaningful in the correct order. That would introduce a "Module" abstract class and a proper way of indicating what are the required input/output to use the module.
I think that will make some sence, especially regarding optimization module. I.e. if @Cédric introduce somethink like
for module in module_list:
module.run()
check_if_valid()
So, e.g. a mechanical instable machine does'nt need a magnetical simulation.
If I understood well, the point of having models as a list is to provide a different model for each part of machine rather than having a list for the group property of LossModelBertotti. Is that right ?
Yes, each group could have it's own LossModel that way. But further, I can also introduce list of groups so one LossModel is applied to multiple groups as well. (That will also require several a list of laminations to get it's properties.)
In LossModelBertotti, you have some property regarding the material and length of the lamination. Do you think that we can find a way to replace L1, mat_type and name by the lamination object directly ? Having direct access to the corresponding lamination can provide much more informations / methods and would be easier to parametrize.
I think I will adapt Cédric's solution on setter functions (of Opti. module). So, default would e.g. be LossModelBertotti.lam = "machine.stator"
of maybe only "stator" as it should always be "machine".
The 'expert' input would be LossModelBertotti.lam = obj
with an object, that has (for now) the two methods get_length and get_material. But this is more prone to errors since the actual machine could have a different lamination object.
What do you think?
Regarding your dict. proposal I will keep that in mind. For now I will keep the lists and introduce some proper getters that are needed anyway.
Best regards, Sebastian
Hello Sebastian,
Regarding the module discussion, as an example, the computation of the modal basis should be a Structural model that requires no Input. Then the gen_input part of the Input objects will be simplified: they will just import what is set and the check would be done at the beginning of the module. I will open a dedicated issue on that.
Regarding the loss model list, maybe we can find a way to link this list to the get_lam_list method of machine. Maybe we can introduce a new convention, the lamination group are defined as "Rotor_X" or "Stator_X" with X the id in the lam_list (from inner to outer). That could be a first step to simulation machine with more than 2 laminations within FEMM. Then the loss list could then be something like [None, LossModel1, LossModel2, LossModel1] (for a 4 laminations machine).
I think I will adapt Cédric's solution on setter functions (of Opti. module). So, default would e.g. be LossModelBertotti.lam = "machine.stator" of maybe only "stator" as it should always be "machine".
Another way to link loss_list to lam_list would be to store the id of the lamination in the get_lam_list which would be more general (stator and rotor are no longer property of the Machine object). Or even a list of id if we need to apply the model on several laminations.
Best regards,
Pierre
Hello Pierre,
I am realy realy unsure, if I completely understood your proposals on linking laminations and models. I am even not sure if this is needed anyway. I did some changes on my code, where the lamination is reference by a string, e.g. 'machine.stator'.
What is your opinion on that.
I think we should also keep in mind, that the Losses (at least from my point of view) is intended to store all losses, i.e. lamiantion losses but also bearing losses, windage losses, winding losses etc.
So a list Loss list with the same id as the get_lam_list list may not be suited.
Could you also check my least commit a72c7c6 , which is not directly related to this, and tell me what you think.
It enables eg to have all groups of the femm model in the resulting meshsolution but also have automated group creation later on.
Best regards, Sebastion
Hello Sebastian,
No problem, I was just sharing some though to try to link several work but I wasn't fully satisfied with my proposal.
I did some changes on my code, where the lamination is reference by a string, e.g. 'machine.stator'.
What is your opinion on that.
Rather than a string what about using an int for the index in get_lam_list ? That way it would work with machine with more than two laminations. (This doesn't need to be linked to how it is stored in the Output)
Regarding commit a72c7c6, great idea :) It is more general and meaningful that way.
Best regards,
Pierre
Hello Pierre,
I implemented two main changes. First, the iron loss model now uses the index of the lamination as you suggested.
Second, I split the OutLoss.losses property (i.e. the list that contained the results) to several properties, i.e. lamination, magnet, winding coresponding to the main parts of current laminations.
Each of this properties is a list of lists (of DataND) with the first index (i.e. list[index]) equals the lamination index. Every 'inner' list, e.g. OutLoss.lamination[0]
stores the results of the several models.
So the structure will look like e.g. OutLoss.lamination[0] = [result_of_mdl_x, result_of_mdl_y, ...]
I was wondering, if I should also reflect this structure to the Loss class and further if this structre is even a good idea.
What bothers me at the moment is, that there is no reference at the side of the results that points to the model that computed them (besides maybe the name). So it could be a good idea.
What do you think? Further do you have some validation cases? When should we merge this branch?
Best regards, Sebastian
Hello Sebastian,
As inspiration, we defined the Output object to contain the simulation that generated it. We made this for post-processing and for reproducible science. Even two years latter, when opening the Output file, you always know what where the assumption/parameter when the simulation was run (and some post-processing can be improved by accessing to the machine).
Then at some point we had "out_list" in the plot to automatically compare the results from several models. Now we use data_list for comparison.
So maybe we can consider adding the model to the corresponding output or maybe we should just run several simulation and compare them directly with the plot methods. Having the results directly stored as list will be handy when comparing the results.
Unfortunately we don't have validation cases for now. The last webinar is all about "contributing to pyleecan". We plan to create some issue for the potential new contributors. Maybe we can use the webinar to call for help and validation on this topic. So I think that we should merge this branch before the next webinar so people can take a look. What do you think ?
Best regards,
Pierre
Thank you for your feedback Pierre.
I will implement my idea, that may allow reproducability in my optinion.
We will see if this is too fancy :-)
(I was thinking of list of losses not only because of the possibility to compare, but also to e.g. have different loss components (fundamental vs. harmonics) seperated.)
Merge before webinar is great. That will also reduce my 'maintanance' afforts, i.e. merge conflicting new master commits to be up to date. :-)