JWock82 / Pynite

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

Possible code reorganisation #120

Open roger1017 opened 2 years ago

roger1017 commented 2 years ago

Hi Craig,

I can see the code for assembling and solving FE are all written in FEModel3D.py. It might be beneficial to have the solution part of the code written in a separate script to maintain the code tidy if more functionalities are added (different nonlinear solvers, dynamic solver, etc) in the future.

BR Roger

JWock82 commented 2 years ago

I agree. Something I've been meaning to do. It should be a fairly simple change.

JWock82 commented 1 year ago

I'm currently working on this. I should have it done shortly.

SoundsSerious commented 1 year ago

Hi @JWock82,

Some thoughts on reorganization. Im parallel processing PyNite load combos with python ray which requires that I mimic these portions of analyze functions. I think you could include some sub-functions for these to help consolidate those methods. This might help with #166 by segmenting combo use.

this function should be called when a load combo is completed (either locally or remotely) to incorporate the solution

    def merge_displacement_results(self,combo,D):
        """adds a remotely calculated displacement vector and applies to nodes ala pynite convention"""
        self._D[combo] = D

        for node in self.Nodes.values():
            node.DX[combo] = D[node.ID*6 + 0, 0]
            node.DY[combo] = D[node.ID*6 + 1, 0]
            node.DZ[combo] = D[node.ID*6 + 2, 0]
            node.RX[combo] = D[node.ID*6 + 3, 0]
            node.RY[combo] = D[node.ID*6 + 4, 0]
            node.RZ[combo] = D[node.ID*6 + 5, 0]

I see this prep code (shown without #166 addins) is common too unless I'm missing something, having this as a separate function helps to ensure you can prep the system if you are running outside of the analyze framework.

    def prep_remote_analysis(self):
        """copies PyNite analysis pre-functions"""
        # Generate all meshes
        for mesh in self.Meshes.values():
            if mesh.is_generated == False:
                mesh.generate()

        # Activate all springs and members for all load combinations
        for spring in self.Springs.values():
            for combo_name in self.LoadCombos.keys():
                spring.active[combo_name] = True

        # Activate all physical members for all load combinations
        for phys_member in self.Members.values():
            for combo_name in self.LoadCombos.keys():
                phys_member.active[combo_name] = True

        # Assign an internal ID to all nodes and elements in the model
        self._renumber()

        # Get the auxiliary list used to determine how the matrices will be partitioned
        D1_indices, D2_indices, D2 = self._aux_list()
JWock82 commented 1 year ago

I just implemented this (a little differently) and quite a bit more. There was (and still is) a lot of repetitive code between the 3 analysis options that I am trying to add helper methods for. As @roger1017 pointed out, this will make it easier to build out other types of solvers in the future.