Skip to content
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

Inclined beam element loads in global direction #87

Closed
eykaraduman opened this issue Jan 29, 2021 · 4 comments
Closed

Inclined beam element loads in global direction #87

eykaraduman opened this issue Jan 29, 2021 · 4 comments

Comments

@eykaraduman
Copy link

Hi,
Beam element loads defined in local coordinate system. It will be also usefull to define loads in global direction.

@tamalone1
Copy link
Contributor

I think this would be a nice feature. The way things are implemented, it may require a procedure to transform the loads to the local coordinate system and add them with the existing AddLoad methods. For point loads, this should be straightforward. For distributed loads, it may be challenging. I'll look into it.

@JWock82
Copy link
Owner

JWock82 commented Feb 9, 2021

It should be pretty simple. It's a matter of transforming loads from local to global coordinates. You should be able to use the element's transformation matrix to do the job. I'd tackle this myself, but I'm focusing on checking and debugging quad elements right now (issue #78). There a a few issues with skewed quads I'm trying to isolate.

@SoundsSerious
Copy link
Contributor

I was able to get a quick solution this with a rotation matrix approach from beam coordinates.

def rotation_matrix_from_vectors(vec1, vec2):
    """ Find the rotation matrix that aligns vec1 to vec2
    :param vec1: A 3d "source" vector
    :param vec2: A 3d "destination" vector
    :return mat: A transform matrix (3x3) which when applied to vec1, aligns it with vec2.
    """
    a, b = (vec1 / numpy.linalg.norm(vec1)).reshape(3), (vec2 / numpy.linalg.norm(vec2)).reshape(3)
    v = numpy.cross(a, b)
    if any(v): #if not all zeros then 
        c = numpy.dot(a, b)
        s = numpy.linalg.norm(v)
        kmat = numpy.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]])
        return numpy.eye(3) + kmat + kmat.dot(kmat) * ((1 - c) / (s ** 2))

    else:
        return numpy.eye(3) #cross of all zeros only occurs on identical directions


  def RotationMatrix(n_vec):
        #FIXME: Ensure that this is the correct orientation
        n_o = [1,0,0] #n_vec is along Z, so we must tranlate from the along axis which is z
        return rotation_matrix_from_vectors(n_o,n_vec)
        
  @property
  def ReverseRotationMatrix(self):
        #FIXME: Ensure that this is the correct orientation
        return self.RotationMatrix.T

Whats going on here is that the rotation matrix essentialy is a 3d transformation mapping influences from the original orientation (along beam) to (node2.POS-node1.POS)

I found this worked well most of the time, but i think there may be a few edge cases. I think maybe its the local beam vector i'm rotating through, [1,0,0] may not always be appropriate. @JWock82 any input here?

In any event once you have the the rotation matrix you're half way there. Pardon some syntax wrapping, in these case self.structure.frame would be the PyNite structure.
Code from: https://github.com/SoundsSerious/ottermaticslib/blob/geometry/ottermatics/structure.py

    def apply_pt_load(self,gFx,gFy,gFz,x,case='Case 1'):
        '''add a force in a global orientation'''

        Fvec = numpy.array([gFx,gFy,gFz])

        self.debug(f'adding pt load {Fvec}')

        Floc = self.ReverseRotationMatrix.dot(Fvec)
        Flx = Floc[0]
        Fly = Floc[1]
        Flz = Floc[2]

        for Fkey,Fval in [('Fx',Flx),('Fy',Fly),('Fz',Flz)]:
            if Fval:
                self.debug(f'adding {Fkey}={Fval}')
                self.structure.frame.AddMemberPtLoad(self.member.Name, Fkey, Fval, x)


    def apply_distributed_load(self,gFx,gFy,gFz,start_factor=1,end_factor=1,case='Case 1'):
        '''add forces in global vector'''
        Fvec = numpy.array([gFx,gFy,gFz])
        
        self.debug(f'adding force distribution {Fvec}')

        Floc = self.ReverseRotationMatrix.dot(Fvec)
        Flx = Floc[0]
        Fly = Floc[1]
        Flz = Floc[2]

        for Fkey,Fval in [('Fx',Flx),('Fy',Fly),('Fz',Flz)]:
            if Fval:
                self.debug(f'adding dist {Fkey}={Fval}')
                self.structure.frame.AddMemberDistLoad(self.member.Name, Fkey, Fval*start_factor,Fval*end_factor,case=case)

In general my experience with beam-style FEA is that maintaining the references and properties is really the pain point, PyNite has a good solution with its declarative approach and visualization tool. Visualization / Interaction I think are really the key to making this easy.

Thinking Forward:
I have been looking for a way to render 3d beam sections, as this would display orientation and and make this more intuitive, with point and click features being a nice bonus. Found this 500 line python render that uses open GL and though it could just plug into this code base. http://aosabook.org/en/500L/a-3d-modeller.html

If you had something like a shapely polygon for the end profile creating the 3d rep would be easy. This library that computes ,section properties is in the process of transitioning to a pre-processor in shapely: https://sectionproperties.readthedocs.io/en/latest/

All together I can visualize a pretty powerful GUI structures tool all combined!

@JWock82
Copy link
Owner

JWock82 commented Aug 15, 2021

This feature has been added in v0.0.48.

@JWock82 JWock82 closed this as completed Aug 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants