Abaqus turn surfaces into sets - python

I've been trying to find the centers of two surfaces in my model (see photo), but don't manage to do so. They are element surfaces (faces) and there is no option in the query to find centers of element surfaces, only of element sets. Finding the center of node sets is also fine, but my node sets do not appear in the tools -> query -> mass property options.
And I can't find an option to convert my element surfaces into element sets.
What I eventually want is to find the centers of both red highlighted surfaces and draw a line between them. Can anyone help me with this?

If your surfaces contain elements then you can always retrieve them and create a set on the base of it.
m = mdb.models['myModel']
a = m.rootAssembly
es_l5_inferior_elems = a.surfaces['ES_L5INFERIOR'].elements
es_l5_inferior_elems_set = a.Set(
name='ES_L5INFERIOR_ELEMS_SET',
elements=es_l5_inferior_elems
)
If you want to get a nodes set, I am afraid you have to loop through all the elements in es_l5_inferior_elems and use the getNodes() method of a MeshElement object (keeping in mind that there will be repetitions to delete).
To get the mass/volume center you can use the dedicated method getMassProperties() of the Part or rootAssembly. By the way, to use it you do not need Sets (simply give the elements/nodes array)!
mass_prop = a.getMassProperties(regions=es_l5_inferior_elems)
print(mass_prop['volumeCentroid'])
print(mass_prop['centerOfMass'])

Related

How to remove duplicates from list of objects(according to value inside object), choosing item from dup’s according to other value inside object?

so basically, I have a list of objects. Let's say each object has two properties: A and B. A is a tuple of 3 integers: (A1, A2, A3), and B is an integer. This list may contain objects that have identical A's, and I want to get rid of those duplicates. However, I want to do so in a way that among those objects that have the same A, the one with the lowest B is chosen. In the end, I want a list of objects with all unique A's and with the lowest B's.
I thought about it for a while, and I think I can come up with a really janky way to do this with lots of for loops, but I feel like there must be a much better way built into a function in python or in some sort of library (to do at least a part of this). Anyone have any ideas?
Thanks!
edit: For more detail, this is actually for a tetris AI, for finding all possible moves with a given piece. My objects are nodes in a tree of possible Tetris moves. Each node has two values: A: (x_position, y_position, rotation), and B: the number of frames it takes to reach that position. I start with a root node at the starting position. At each step, I expand the tree by making children by doing one move to the left, one move to the right, one rotation left, one rotation right, or one softdrop downward, and for each child I update both A, the XYR position, and B, the number of frames it took to get there. I add all these to a list of potential moves. After this, I merge all nodes that have the same XYR position, choosing the node that has the least frames to get there. The next step, I expand each node inside of the list of potential moves and repeat the process. Sorry, I realize this explanation might be confusing, which is why I didn't include it in the original explanation. I think it's advantageous to do it this way because in modern tetris, there is a rather complicated rotation system called SRS (Super Rotation System) that allows you to perform complicated spins with various pieces, so by making a pathfinder in this way and simulating the piece making the moves according to SRS is a good way since it tells you if the move was a spin or not (sending more/less dmg), and it also allows you to know the exact movement to execute the placement (I also store a list of series of moves to reach that position) with the least frames. Later, I want to be able to figure out how to hash the states properly so I don't revisit, but I'm still figuring it out.
d = {}
for obj in the_list:
current_lowest = d.setdefault(obj.A, obj)
if obj.B < current_lowest.B:
d[obj.A] = obj
# Get the result
desired_list = list(d.values())
We have a dict d whose keys are tuples (A) and values are objects themselves. The .setdefault ensures that if the A of interest is not seen yet, it sets it with the current object obj. If it was seen already, it returns the value (an object) corresponding to that A. Then we compare that object's B with the one at hand and act dependingly. At the end, the desired result will lie in the values of d.

how to do a selection of specific edges on duplicated objects in Maya?

I’m new to python, so I'm sorry if my question seems to be silly.
But, I'll be grateful if someone could help.
I'm writing a script in Maya Python.
I have a set of duplicated meshes. I need to select certain poly edges and convert them into curves.
I've managed to write the last part of converting the polyEdge to Curve, but struggling to write the wildcard selection.
I was thinking to write the following way:
list = [list of objects I want to get edges of]
for i in list:
pm.select()
Kind of like that,
But to be honest I don't know what I'm doing here.
I'd appreciate any help.
Thank you
here is an example
# list of your duplicates
myDuplicatedMeshes = ['pShpere1_dup']
# select your edges in the viewport for detecting which edges to transfer
edgeInputList = cmds.ls(sl=True)
# collect the edges ids
edgeIds = [i.split('.')[-1] for i in edgeInputList]
# loop into the duplicated
for dup in myDuplicatedMeshes:
# give the edge ids
targeted_edges = ['{}.{}'.format(dup, id) for id in edgeIds]
# convert to curve
curveBuilded = cmds.polyToCurve(targeted_edges, form=2, degree=3)

Blender changes the names of my objects

I'm using the python API for a "scatterplot" in blender.
The data is a dictionary which maps names to lists of 3D points, so there are named clouds of points.
I need to look at each cloud individually and hide the others, this is my setup:
for each name in the dict, I create an empty object
for each 3D point belonging to this name, I create a small cube, position it and reparent it to the empty object.
Now I can hide the parent objects in the 3D view.
The program works fine, but there is one strange problem: The names are important, I need to be able to find them in the sceneview. But blender changes the names. A name like "TopDown" becomes "TopDown.001". This happens despite the fact that there are no other objects with this name.
Here is my code:
for plotname, positions in points.items():
bpy.ops.object.add(type='EMPTY')
bpy.context.active_object.name = plotname
bpy.context.active_object.location=(0,0,0)
print(plotname) #<---------------here the name is still correct
for position in positions:
me = bpy.data.meshes.new(plotname + 'Mesh')
ob = bpy.data.objects.new(plotname+"Mesh", me)
ob.location = (position[0], position[1], position[2])
ob.show_name = True
bpy.context.scene.objects.link(ob)
me.from_pydata(verts_loc, [], faces)
me.update(calc_edges=True)
ob.parent=bpy.context.active_object
The actual program is a little longer, verts_loc and faces have been set up before this snippet. They represent a cube.
How can I ensure that plotname stays plotname and doesn't become plotname.001 ?
UPDATE
Clarification: It doesn't matter that points within the plot are renamed. Something like "plotnameMesh.001" and "plotnameMesh.002" is no problem. But the parent objects are renamed to.
In fact the sole purpose of appending "Mesh" to the object names is to keep the toplevel plotname unique.
The for position in positions: implies you are creating multiple objects at different locations for each plotname. One will keep the plotname while the others will get numeric extensions.
for position in positions:
me = bpy.data.meshes.new(plotname + 'Mesh')
ob = bpy.data.objects.new(plotname+"Mesh", me)
Is each position unique or are you adding multiple objects at each position?
You will also want to ensure you delete the previous creations before re-running your script.
When you have many objects with similar names you can use Select->Select Pattern to select them. In python you can do that with
[setattr(obj, 'select', True) for obj in bpy.data.objects if obj.name.startswith(plotname)]

How to isolate these (image included) paths in a graph?

I have a graph and want to isolate distinct paths from it. Since I can't phrase this easily in graph jargon, here is a sketch:
On the left side is a highly simplified representation of my source graph. The graph has nodes with only 2 neighbors (shown as blue squares). Then it has intersection and end nodes with more than 2 neighbors or exactly 1 neighbor (shown as red dots).
On the right side, coded in three different colors, are the paths I want to isolate as a result. I want to isolate alls paths connecting the red dots. The resulting paths must not cross (go through) any red dots. Each edge may only be part of one distinct result path. No edges should remain (shortest path length is 1).
I am sure this is a known task in the graph world. I have modeled the graph in NetworkX, which I'm using for the first time, and can't find the proper methods to do this. I'm sure I could code it the hard way, but would be glad to use a simple and fast method if it existed. Thanks!
Edit: After randomly browsing the NetworkX documentation I came across the all_simple_paths method. My idea is now to
iterate all nodes and identify the red dots (number of neighbors != 2)
use all_simple_paths() pairwise for the red dots, collect the resulting paths
deduplicate the resulting paths, throw away everything that contains a red dot except as the start and end node
Step 2, of course, won't scale well. With ~2000 intersection nodes, this seems still possible though.
Edit 2: all_simple_paths appears to be way too slow to use it this way.
I propose to find all straight nodes (i. e. nodes which have exactly two neighbors) and from the list of those build up a list of all your straight paths by picking one straight node by random and following its two leads to their two ends (the first non-straight nodes).
In code:
def eachStraightPath(g):
straightNodes = { node for node in g.node if len(g.edge[node]) == 2 }
print straightNodes
while straightNodes:
straightNode = straightNodes.pop()
straightPath = [ straightNode ]
neighborA, neighborB = g.edge[straightNode].keys()
while True: # break out later if node is not straight
straightPath.insert(0, neighborA)
if neighborA not in straightNodes:
break
newNeighborA = (set(g.edge[neighborA]) ^ { straightPath[1] }).pop()
straightNodes.remove(neighborA)
neighborA = newNeighborA
while True: # break out later if node is not straight
straightPath.append(neighborB)
if neighborB not in straightNodes:
break
newNeighborB = (set(g.edge[neighborB]) ^ { straightPath[-2] }).pop()
straightNodes.remove(neighborB)
neighborB = newNeighborB
yield straightPath
g = nx.lollipop_graph(5, 7)
for straightPath in eachStraightPath(g):
print straightPath
If your graph is very large and you do not want to hold a set of all straight nodes in memory, then you can iterate through them instead, but then the check whether the next neighbor is straight will become less readable (though maybe even faster). The real problem with that approach would be that you'd have to introduce a check to prevent straight paths from being yielded more than once.

How to access a specific class instance by attribute in python?

Say I have a class Box with two attributes, self.contents and self.number. I have instances of box in a list called Boxes. Is there anyway to access/modify a specific instance by its attribute rather than iterating through Boxes? For example, if I want a box with box.number = 40 (and the list is not sorted) what would be the best way to modify its contents.
If you need to do it more frequently and you have unique numbers, then create a dictionary:
numberedBox = dict((b.number, b) for b in Boxes)
you can then access your boxes directly with numbers:
numberedBox[40]
but if you want to change their number, you will have to modify the numberedBox dictionary too...
Otherwise yes, you have to iterate over the list.
The most straightforward way is to use a list comprehension:
answer=[box for box in boxes if box.number==40]
Be warned though. This actually does iterate over the whole list. Since the list is not sorted, there is no faster method than to iterate over it (and thus do a linear search), unless you want to copy all the data into some other data structure (e.g. dict, set or sort the list).
Use the filter builtin:
wanted_boxes = filter(lambda box: box.number == 40, boxes)
Although not as flexible as using a dictionary, you might be able to get by using a simple lookup table to the map box numbers to a particular box in boxes. For example if you knew the box numbers could range 0...MAX_BOX_NUMBER, then the following would be very fast. It requires only one full scan of the Boxes list to setup the table.
MAX_BOX_NUMBER = ...
# setup lookup table
box_number = [None for i in xrange(MAX_BOX_NUMBER+1)]
for i in xrange(len(Boxes)):
box_number[Boxes[i].number] = Boxes[i]
box_number[42] # box in Boxes with given number (or None)
If the box numbers are in some other arbitrary range, some minor arithmetic would have to be applied to them before their use as indices. If the range is very large, but sparsely populated, dictionaries would be the way to go to save memory but would require more computation -- the usual trade-off.

Categories

Resources