JWock82 / PyNite

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

Moving to v1.0.0 w/ API consistency #202

Open connorferster opened 2 weeks ago

connorferster commented 2 weeks ago

Is your feature request related to a problem? Please describe. PyNiteFEA continues to develop as a great project and I love using it (and teaching others to use it!) I know at least one engineer who relies on it for production engineering work (awesome!)

As PyNite is not in v1.0.0 yet, there have been some breaking changes to the API recently (part of which I addressed in #194). This is to be expected since the major version is still 0. I am proposing that PyNite could be ready for a v1.0 after doing some final house-keeping updates to the API. Once doing a v1.0 release, this would mean that the names of all classes, functions, parameters, and parameter order would stay fixed until it is decided that the existing API no longer serves the future of the project.

To be clear, additional features can be added to the v1.0.0 release (e.g. new element types, new solvers, new methods, new attributes, etc.) but the default settings would need to remain and the new features would be optional.

(Apologies if you are nodding your head saying, "yes, yes, I know this..."; I am just stating it for clarity)

Describe the solution you'd like I am proposing to open a PR (off of the DKMQ branch) to perform the following tasks to move towards a consistent API and to facilitate a stable v1.0 release:

Describe alternatives you've considered We could also do nothing and leave things as they are. Breaking changes currently seem to happen unexpectedly though and, when they happen, it gives the impression that PyNite is "buggy". I think that PyNite is stable enough to consider a v1.0 release and give users confidence going forward that their scripts and models written against v1.x releases will always be able to run. This still allows PyNiteFEA to continue to grow rapidly through the addition of new features as attributes, parameters, functions, and classes can always be added without having to change the names of existing attributes, function, and classes.

Additional context Moving to a 1.0.0 release can also facilitate improved git branch management techniques. See #201

bjhowie commented 2 weeks ago

I'll jump in and say that I agree that the package is almost ready for a v1.0 release. I have been using the 1D Member elements in production code for a while now and it has performed flawlessly, though I'll admit it is a little tricky to maintain due to the regular breaking changes. I would like to incorporate the 2D elements (Quads) into production code as well, though it seems they're not quite ready yet.

I think this is a good time to open the discussion around what the API workflow looks like in a v1.0 release. I've addded a few of my own thoughts below:

  1. I think the use of sections for Member elements should be enforced. At the moment, the user can initiate a Member element with either a section name or by manually passing A, Iy, Iz & J parameters to the constructor. We're not saving much by passing those parameters directly to the member, and storing all section parameters in a specific section class feels much cleaner. Additionally, the section parameters should only be extracted from the section object at analysis time, not copied over to the Member object at member construction. This allows for section properties to be altered between iterative analysis runs without manually having to update the member section parameters before each run.
  2. As above, but for material properties. Material parameters should be kept stored in the material object until analysis time for the reasons outlined above.
  3. Does a quad section property class make sense? It would really only need to store the thickness of the quad, but would allow that thickness to be easily changed between analysis runs. I dont feel as strongly about this as for Members.
  4. Plate elements to be removed. They dont seem to be all that usefull when the Quad element is much more flexible. I undertand the implementation is simpler and there is likely a performance benefit to that, but they really just clutter up the codebase.
  5. There is a disconnect in the way the user interacts with the model before and after analysis. To set up a model, we only interact with the model via the methods of FEModel3D, but then to extract the results we need to interact directly with the Node/Member/Quad objects, which can only be accessed by accessing the FEModel3D instances lookup dictionaries. I dont necessarily know what the right approach here is, but the current one feels a little odd.
  6. The Material class is poorly documented. It is also unclear why we need to provide both a shear modulus and a poisson's ratio. Can one not be calculated from the other?
  7. It is difficult to extract quad internal forces (shears/moments) at a given node without interrogating the topology of the quad. In my opinion, the user should not need to interact with the (r, s) natural coordinate system of the quad

I understand much of the above would constitute breaking changes, but I want to make sure they're considered before pushing ahead with a v1.0 release.

SoundsSerious commented 2 weeks ago

I am also quite interested in solidifying the API since I think this library is working great and I think has a good balance of scope and ease of use. I think it might be a good idea to share our wishlists in this threads so we can decide where potential breaks in the interface might occur and design an interface that wont break when these changes are added if not at the 1.0 release.

I have a couple of changes I would like to make and think some of them are significant changes so It would be nice to get those in before 1.0 vs after to prevent breaking changes.

  1. I've taken a stab at integrating a common materials definitions between sectionproperties and PyNite, and in general I think maybe this could be a separate library since we essentially double our user-base for support in this area. It would also be cool to have some kind of scrape of matweb to populate a materials database which is outside the scope of PyNite. https://github.com/Ottermatics/ottermaticslib/blob/master/ottermatics/eng/solid_materials.py

  2. I have an integration between defined 2D member profiles as per PyNite and shapely-defined sections that rely on sectionproperties. This is done with an emphasis on unifying the resultant stress calculation including a prediction of failure for the more computationally intense FEA approach of sectionproperties using a Support Vector Machine interface. This works by calculating the true stress via FEA and adding it to a database when the number of points is below a threshold or when the estimated stress is within the (margin of error x safety factor) to failure. An interesting use of this feature is the ability to quickly determine the structure failure loads via iterative solver vs applying loads and then calculating stresses. This might be useful as a 3rd party library if it doesn't fit in here. https://github.com/Ottermatics/ottermaticslib/blob/7b73db99a784d2da21e1cc599c3ea4b09c5b4d6a/ottermatics/eng/geometry.py

  3. Using a "KDTree" for node definitions, which replaces identifying duplicate nodes, with the added benefit of searching a region for existing nodes.

  4. Saving / Loading Case Results to disk, I've outlined this in #178 but have only progressed a little bit on this

  5. I've added a GPU Solver capability enabled via environmental variable since that would likely be an advanced use case.

  6. I've improved meshing for cylinders and annular meshes to align the starting node angle

Changes I'd want to integrate are here: https://github.com/JWock82/PyNite/compare/main...Ottermatics:PyNite:structural_integration

Another interesting Idea would be to create a github-organization to integrate engineering oriented libraries that many brilliant people have put together. A library I am using capytaine receives support funding from the National Science Foundation and NREL, which has accelerated their growth. No reason the same could happen here!

connorferster commented 1 week ago

Thanks for the input. Here are my thoughts:

@bjhowie (not completely in the same order as you have):

  1. Introduce a Section class so that a Member definition is populated with connectivity information, Section class, and Material class. Basically five parameters (name, start node, end node, section, material). I think that makes sense. I think that might be on @JWock82's road map, too.
  2. QuadSection class for quad sections. From what you are saying, I think that makes sense (even though it seems to be just for name and thickness). It would be consistent with frames and allows for the same behaviour of modifying a section object and have it apply across the model.
  3. I don't know if plate elements should be removed. They seem like they might have their place in membrane-type structures (which I believe are included in the design examples). @Jwock82, thoughts?
  4. Poisson's ratio and shear modulus: Agreed, I always found it odd because it is like duplicated information. Defining one or the other should be sufficient.
  5. Force extraction from quads: I think @JWock82 is addressing this in the new DKMQ branch.
  6. Model interaction before and after analysis: I very much agree with this. It is an awkward but sensible approach. Perhaps the implementation could be wrapped in a method? model.get_member_results("M1", "shear", "Fy", "Combo 1", n_points=200) (for the raw arrays) or model.get_member_diagram("M1", "deflection", "dy", "Combo 1") (for the matplotlib figure). As long as the implementation stays the same (i.e. people can still access dicts directly) and these methods are simply added to the FEModel3D then this does not need to be breaking change but would be a nice quality of life improvement.

@SoundsSerious:

  1. Harmonzing with sectionproperties would be fun but I think is outside the scope of PyNite. PyNite Section objects would basically take results from a sectionproperties analysis. Perhaps there could eventually be a method on the Section class that could accept a sectionproperties.Section object and use it to populate a PyNite Section object? Would not be a breaking API change, just a new feature.
  2. Sounds cool! As you suggest though, I think it is a 3rd party library extra since there is so much extra scope there.
  3. A KDTree would certainly add a lot of "intelligence" to the nodes. Do you think the Node and FEModel3D API could remain as is and this could simply be an implementation detail under-the-hood? It would create the possibility for new method names to do wonderous new things (non breaking changes) but are there any elements of the existing API that would have to change to accommodate the KDTree?
  4. Saving models and results to disk would be a great feature (have been thinking of it myself). It seems we could eventually do that by just creating new method names to handle these capabilities (non-breaking change). Do you see any elements of the existing API that would have to change to accommodate this?
  5. GPU solver with an added environment variable. If the variable is simply an added option, this sounds like a non-breaking change.
  6. I just did a quick read of changes in your branch. I did not see any breaking API changes (just new optional keyword parameters). The one possible breaking change I did see is adding a __hash__ method to the Node object. I don't believe the Node object is immutable so adding a __hash__ method would be an "incorrect" application of the __hash__ method. Making the Node object immutable certainly would be a breaking change. I don't think this is necessary since we can use the node name as a dict key instead of the actual Node.

In summary, @bjhowie is proposing a series of API changes too be considered. I think they make sense but I take direction from @JWock82 :) @SoundsSerious has some very cool features suggested and I think they can all be implemented without API breaking changes (except the __hash__ thing).

@jwock82, would love to hear your thoughts whenever you have the capacity :)

Thanks all!

JWock82 commented 4 days ago

Wow! I appreciate all the insight and interest in Pynite. I'm sorry I've been off the radar for a few weeks as I've been moving my family long distance.

Let me say I agree for the most part. I've been trying to decide when is the right time to do a v1.0.0 release, and I think I'm close. I'm still sorting out a few issues with plates/quads on the DKMQ branch, and I'd like these changes to be part of the v1.0.0 release.

I've generally tried to follow the PEP-8 style guide for python, which uses first letters capitalized for class names. I'm open to other style guide suggestions, but I don't want to disrupt too many users.

Pynite does have a Section class already built in, though it's not mandatory to use at this time. I added this class in preparation for pushover analysis (pushover anlaysis is still a work in progress).

Plates and Quads offer different advantages at the moment, but the DKMQ element will bridge the gap. Plates currently produce better stresses near boundaries, but they have the limitation of being rectangular, and do not consider thick plate behavior properly. They also solve more efficiently. Quads have the advantage of being able to be any quad shape, and of being able to model thick plates. The new DKMQ element will give us the best of both worlds. I'm really close to being done with the DKMQ. The models I'm testing are producing good internal stress results, but for some reason I'm getting strange element corner forces. I think I'm chasing down a typo at this point and when I find it I'll push the DKMQ branch to the main branch and will replace Plates and Quads with just Plates that are based on the DKMQ formulation.

The only disadvantage I see between tying shear modulus to poisson's ratio is that sometimes for non-linear material approximation users may want to adjust one without adjusting the other. We may want to keep them separate. For example, adjusting the shear modulus to account for shear cracking while not affecting the poisson effect for axial loads. This is a pretty far fetched case, especially if the built-in stiffness modification factors kx_mod and ky_mod are used for the plates, but there may be other scenarios where it could be useful to have them separated.

To interact directly with Members/Quads/Plates without using the dictionaries we'd have to build some helper methods as you've noted into the FEModel3D class. Easy enough to do, but not a priority to me personally. There's actually a lot that Pynite can do that is not exposed through the FEModel3D class. I'm developing features faster than I'm making them accessible.

I like the idea of integrating Pynite with other programs, thereby increasing its capabilities and increasing the support pool for both programs.

These are great ideas. Let me get the DKMQ branch sorted out, and then I'll start looking at pull requests. Also, if any of you want to become maintainers, let me know. Pynite's usage has exploded in the last year, and I'm getting a lot more e-mails about it than I have in the past. I'm struggling to keep up with them all. Lot's of great ideas, but only one Craig at this point, and I tend to focus on the features that are most useful to me - I have a killer ShearWall class completed I'm looking forward to sharing in the near future - first I want to make sure it's compatible with the DKMQ element.