JWock82 / Pynite

A 3D structural engineering finite element library for Python.
MIT License
454 stars 93 forks source link

Load cases added to load combos #148

Closed connorferster closed 1 year ago

connorferster commented 1 year ago

Is your feature request related to a problem? Please describe. When adding a load case to a model (e.g. dead loads ("D") and live loads ("L")), it would be nice to be able to review the loading in the VTK visualizer without having to manually add these load cases as load combos (e.g. "1.0D", "1.0L").

Describe the solution you'd like I am proposing that, when a load is added to member (frame, plate, or quad), that a load combination to match the load case name is created also.

Example:

my_model.add_member_pt_load("M1", "Fy", -500, 0, "D")

Then, in FEModel3D.add_member_pt_load the following code would be added:

if case not in self.LoadCombos:
    add_load_combo(case, {case: 1.0}, combo_type='service')

Similar code would be added to the four "loading" methods:

Describe alternatives you've considered I can add these myself in my analysis. However, I was surprised that I was only able to visualize and extract results for load combos and not load cases.

If there is a later intention of being able to build future features off of the combo_type, I thought that this feature would fit in nicely with this intention.

Additional context If this is something you are interested in, let me know and I can write a PR (with additional tests) to implement.

JWock82 commented 1 year ago

PyNite can already do this. The Renderer object has an attribute case that can be used to specify which load case you want to see. Just make sure that combo_name is set to None when rendering by case. Here's an example:

# Import the Renderer class
from Visualization import Renderer

# Instantiate a renderer
my_renderer = Renderer()

# Select the desired load case and turn off load combination rendering
my_renderer.case = 'D'
my_renderer.combo_name = None

# Render the model
my_renderer.render_model()

Let me know if you have any problems, or if I've misunderstood your question.

connorferster commented 1 year ago

Ah, yes, I see that now. However, I need to get access to the underlying resulting arrays in order to run through my load factors.

The use case is like this where I have a cantilever that has loads upon it coming from different sources. For a given action of the beam (reactions, shear, moment) a different load case may govern each, depending on the loading configurations. Therefore, I need to have access to resulting arrays of each load case to find out.

I read through the code for the Renderer class but how it retrieved these arrays was not obvious to me. What are your thoughts on making all resultant arrays (instead of just the load combos) available from the FEModel object instead of from the Renderer?

connorferster commented 1 year ago

I also understand something else now:

When an analysis is performed (using .analyze and .analyze_PDelta), the global stiffness matrix is assembled per load combination. This makes load combinations central to the analysis process and means that as the number of load combinations increases, the total analysis time increases linearly.

I am guessing you have considered alternatives including assembling the stiffness matrix for each load case instead? Or perhaps it was an early design decision that was made and has remained in place?

EDIT: Ah, I see. I think this is what .analyze_linear is for, correct? Which can be used when there are no compression/tension-only elements.

I tell you, the more I work with PyNite, the more I like it! It's really great work.

JWock82 commented 1 year ago

You've got it figured out. PyNite was set up with non-linear analysis in mind. For larger models where non-linear effects are not important the analyze_linear method is much faster since it only assembles the stiffness matrix once. I added it primarily to help with plate/quad models which can get large very fast.

For non-linear models the stiffness matrix is a function of the loading, so superimposing results from multiple load cases is invalid, because each of those cases has a different stiffness matrix. In those cases you need to combine the loads first and then form the stiffness matrix.

You should be aware that PyNite doesn't do second-order effects on plates/quads. I haven't derived a non-linear matrix for those at this point. It's hard to find literature for that. I've been using manual moment magnification whenever I need second order effects in plates/quads.

I've spent a lot of time profiling PyNite's code to root out inefficiencies. It's not as fast as some commercial software, but I've been able to solve models with several thousand nodes in a few minutes, which hasn't been too bad to deal with.