Как написать скрипт Python, который автоматизирует анализ методом конечных элементов на консольном балке, импортированном из файла .step? - PullRequest
1 голос
/ 01 июля 2019

У меня есть 10.000 ".step" файлов, содержащих консольные балки со случайными многоугольными сечениями. Я хотел бы автоматически проанализировать максимальное отклонение, изгибающую нагрузку и / или частотную характеристику (резонансную частоту) каждого луча и записать результаты в файл.

В каждом сценарии один конец балки должен быть фиксирован. К другому концу балки я бы хотел приложить нагрузку. Нагрузка и ограничения должны быть одинаковыми для всех балок.

Проблемы: - найти подходящую библиотеку - сетчатые тела для анализа - назначить ограничения граням (не все балки имеют одинаковое количество граней)

Я пытался автоматизировать этот процесс в Fusion360, но, к сожалению, в их API отсутствует поддержка моделирования методом конечных элементов (FEA).

Я пытался автоматизировать процесс с помощью FreeCAD, но я не могу полностью автоматизировать анализ FEA, поскольку FreeCAD использует GMSH и CalculiX, и я не смог найти полезную документацию.

Я счастлив использовать несколько сценариев, если это необходимо, например, автоматизировать создание сетки с помощью GMSH, настроить симуляцию с помощью FreeCAD и решить с помощью Calculix. Если возможно, я бы хотел сделать все это на python.

Ниже приведен код для макроса FreeCAD, который генерирует случайный луч и экспортирует файл .step в папку, заданную в переменной cur_path.

import numpy as np
import Part

###
# Polygon Generator
# (Source: /8285101/algoritm-generatsii-sluchainogo-2d-mnogougolnika)
###
import math, random

def generatePolygon( ctrX, ctrY, aveRadius, irregularity, spikeyness, numVerts ) :
    '''
    Start with the centre of the polygon at ctrX, ctrY, 
    then creates the polygon by sampling points on a circle around the centre. 
    Randon noise is added by varying the angular spacing between sequential points,
    and by varying the radial distance of each point from the centre.

    Params:
    ctrX, ctrY - coordinates of the "centre" of the polygon
    aveRadius - in px, the average radius of this polygon, this roughly controls how large the polygon is, really only useful for order of magnitude.
    irregularity - [0,1] indicating how much variance there is in the angular spacing of vertices. [0,1] will map to [0, 2pi/numberOfVerts]
    spikeyness - [0,1] indicating how much variance there is in each vertex from the circle of radius aveRadius. [0,1] will map to [0, aveRadius]
    numVerts - self-explanatory

    Returns a list of vertices, in CCW order.
    '''

    irregularity = clip( irregularity, 0,1 ) * 2*math.pi / numVerts
    spikeyness = clip( spikeyness, 0,1 ) * aveRadius

    # generate n angle steps
    angleSteps = []
    lower = (2*math.pi / numVerts) - irregularity
    upper = (2*math.pi / numVerts) + irregularity
    sum = 0
    for i in range(numVerts) :
        tmp = random.uniform(lower, upper)
        angleSteps.append( tmp )
        sum = sum + tmp

    # normalize the steps so that point 0 and point n+1 are the same
    k = sum / (2*math.pi)
    for i in range(numVerts) :
        angleSteps[i] = angleSteps[i] / k

    # now generate the points
    points = []
    angle = random.uniform(0, 2*math.pi)
    for i in range(numVerts) :
        r_i = clip( random.gauss(aveRadius, spikeyness), 0, 2*aveRadius )
        x = ctrX + r_i*math.cos(angle)
        y = ctrY + r_i*math.sin(angle)
        points.append( (int(x),int(y)) )

        angle = angle + angleSteps[i]

    return points

def clip(x, min, max) :
    if( min > max ) :  return x    
    elif( x < min ) :  return min
    elif( x > max ) :  return max
    else :    

cur_path = "/GeneratedBeamData"
max_verts = 20
min_verts = 3
max_radius = 30
min_radius = 10
extrude_length = 200.0 #[cm]
IMG_MAX_X = 128
IMG_MAX_Y = 128


# Open New Doc        
App.newDocument("cantilever")
App.setActiveDocument("cantilever")
App.ActiveDocument=App.getDocument("cantilever")

# create new object
App.activeDocument().addObject('PartDesign::Body','Body')

# Start Sketch
App.activeDocument().Body.newObject('Sketcher::SketchObject','Sketch')
App.activeDocument().Sketch.Support = (App.activeDocument().XY_Plane, [''])
App.activeDocument().Sketch.MapMode = 'FlatFace'
App.ActiveDocument.recompute()
print("Started Sketch successfully")

#
# Draw Polygon
#

# randomly select Nr. of Vertices
avg_rad = random.randint(min_radius,max_radius)
nr_verts = random.randint(min_verts,max_verts)
verts = generatePolygon(IMG_MAX_X/2,IMG_MAX_Y/2, avg_rad, 2, 1, nr_verts)   
while (len(verts) > len(set(verts))):
    avg_rad = random.randint(min_radius,max_radius)
    nr_verts = random.randint(min_verts,max_verts)
    verts = generatePolygon(IMG_MAX_X/2,IMG_MAX_Y/2, avg_rad, 2, 1, nr_verts)

## center vertices
cverts = []
for pt in verts:
    cverts.append((pt[0]-IMG_MAX_X, pt[1]-IMG_MAX_Y))
verts = cverts
print(verts)

# start a line
start_pt = verts[0]
cur_pt = verts[0]
next_pt = verts[1]

# draw initial line segment
App.ActiveDocument.Sketch.addGeometry(
    Part.LineSegment(
        App.Vector(cur_pt[0],cur_pt[1],0),
        App.Vector(next_pt[0],next_pt[1],0)),False)
cur_pt = next_pt
line_nr = 0 # drew 0th line
print("Drew First line successfully")
for i in range(2, len(verts)):
    next_pt = verts[i]
    App.ActiveDocument.Sketch.addGeometry(
        Part.LineSegment(
            App.Vector(cur_pt[0],cur_pt[1],0),
            App.Vector(next_pt[0],next_pt[1],0)),False)
    line_nr += 1 # increment line counter
    # define constraint
    # Sketch.addConstraint(
        #Sketcher.Constraint('Coincident',LineFixed,PointOfLineFixed,LineMoving,PointOfLineMoving))
    App.ActiveDocument.Sketch.addConstraint(
        Sketcher.Constraint("Coincident",line_nr-1,2,line_nr,1)) 
    cur_pt = next_pt
    print("Drew %d line successfully" % line_nr)
# Close the loop
print(cur_pt)
print(start_pt)
App.ActiveDocument.Sketch.addGeometry(
    Part.LineSegment(
        App.Vector(cur_pt[0],cur_pt[1],0),
        App.Vector(start_pt[0],start_pt[1],0)),False)
line_nr += 1
#App.ActiveDocument.Sketch.addConstraint(
#    Sketcher.Constraint('Coincident',line_nr-1,2,0,1))
print("Closed Shape")
# Extrude profile
# Get the profile defined by the polygon
App.getDocument("cantilever").recompute()
App.activeDocument().Body.newObject("PartDesign::Pad","Pad")
App.activeDocument().Pad.Profile = App.activeDocument().Sketch

# Extrusion
App.activeDocument().Pad.Length = extrude_length
App.ActiveDocument.recompute()
App.ActiveDocument.Pad.Length = extrude_length
App.ActiveDocument.Pad.Length2 = 100.00
App.ActiveDocument.Pad.Type = 0
App.ActiveDocument.Pad.UpToFace = None
App.ActiveDocument.Pad.Reversed = 0
App.ActiveDocument.Pad.Midplane = 0
App.ActiveDocument.Pad.Offset = 0.000000
App.ActiveDocument.recompute()
print("Extruded Polygon")

# Save STEP
__objs__=[]
__objs__.append(FreeCAD.getDocument("cantilever").getObject("Body"))
import ImportGui
ImportGui.export(__objs__,(u""+cur_path+"/body.step"))

Ниже вы найдете GMSH-скрипт, который я использовал для генерации и экспорта сетки в виде MSH-файла, используя входной файл .step. (Я надеялся, что смогу импортировать этот файл .msh в FreeCAD для автоматизации симуляции FEA.)

Merge "test.step";

Mesh.Algorithm3D = 5;
// 1=Delaunay, 4=Frontal, 5=Frontal Delaunay, 6=Frontal Hex, 7=MMG3D, 9=R-tree
// default = 1

//Mesh  2;  // surface
Mesh  3;  // volume

Mesh.Format = 2;
// 1=msh, 2=unv, 10=automatic, 19=vrml, 27=stl, 30=mesh, 31=bdf, 32=cgns, 33=med, 40=ply2
// default = 10

Mesh.SaveAll = 1;
// Ignore Physical definitions and save all elements

Save "mymesh.msh";
...