A Moving Look at a Solid Tool: Rigid Dynamics

Most of us who have been doing this simulation thing  for a while (in fact, if you still call it analysis instead of simulation you probably fall in this group) always think of ADAMS or DADDS when someone brings up Rigid Body Dynamics tools. Most people forget that ANSYS, Inc. has had a strong offering in this area for some time, and at R13 it really is mature and full featured. 

imageBackground

A good place to start is to step back and describe what Rigid Dynamics is.  That is the name ANSYS, Inc. uses, but many people refer to it as Kinematic/Dynamic, Multibody Dynamics, Rigid Body Dynamics (RBD), or motion simulation. In most case all of these names refer to a numerical simulation where:

  1. More than one part is connected as an assembly
  2. Joints defined between the parts
  3. Large deflection motion
  4. Time dependent motion (if not, then it is just kinematics)
  5. Parts are rigid (not flexible) in most cases

imageThink back to your dynamics class, probably your sophomore year in college.  Linkages, gears, cams, etc…  You are basically solving for the full equation of motion where inertia, time, gyroscopics. And all that stuff is taken into consideration.  The key thing to remember is that you can model a rigid system as a set of point masses  (Bodies) with inertial properties and six degrees of freedom (UX/Y/Z, and ROTX/Y/Z) You then connect them by defining a relationship (Joints) between their degrees of freedom.  You then apply forces, acceleration, velocity, and/or displacement over time and solve. To solve, the problem must be fully constrained in that there may not be any free DOF’s.

You can learn more background on Wikipedia: http://en.wikipedia.org/wiki/Multibody_system  The most comprehensive jumping off point I found for the topic is a website from a the University of Waterloo: http://real.uwaterloo.ca/~mbody/

Rigid Dynamics in ANSYS

You have been able to do Rigid Dynamics in ANSYS MAPDL for a long time.  MPC184 for joints and MASS21 for the bodies has been there since the Spice Girls were popular.  But, you had to use the implicit solver in MAPDL which can take some time to solve a long problem.  So ANSYS, Inc. has been working for a few releases now on an explicit rigid body solver that is much more efficient for this type of problem. You get access to the kinematic (no inertia terms, just motion) for free if you have ANSYS Professional NLS or better. To solve dynamics problems, you need a license for the Rigid Body Dynamics Solver as an add-on to ANSYS Professional NLS or better.

To create a model you simply insert a “Rigid Dynamics” system into your project:

imageFigure 1: Rigid Dynamics System

It looks and acts a lot like an ANSYS Mechanical system, but is set up a bit differently to deal with the needs of Rigid Dynamics and to take away the things that you need for flexible FEA in ANSYS Mechanical.

Analysis Process

A real model can be very complicated and take some time to create, but the process of making it is the same regardless of the complexity:

  1. Do your normal geometry and material setup
  2. Remove auto contacts if you don’t need them (joints should constrain in most cases)
  3. Add any point masses you want to add
  4. Define Joints
    • Fix to ground as needed
    • Key is to make sure joints are correct and you don’t over/under constrain the problem
    • Coordinate system orientation is critical
    • Constrained DOF’s show up on plot for each joint – Blue=free
    • Lots of nice options
  5. Define Springs
    • Between geometry or geometry to ground
  6. Set Up Analysis
    • Define step controls
    • Solver options
    • Nonlinear Controls
    • Output Controls
  7. Apply Loads
    • Acceleration
    • Gravity
    • Joint Loads
    • Remote Displacement
    • Constraint Equations
  8. Solve
  9. Post Process
    • Deformation, Velocity, Acceleration of Parts
    • Total or Aligned
    • Probes on Parts, Joints, Springs
    • Key thing is to get graphs of behavior
    • Also makes a table you can export
imageimage
imageimage

Joints, Springs & Contact

An important part of any Rigid Dynamics tool is the Joint Library. This defines what you can and can’t connect.  The ANSYS tool has all of the standard options:

Table 1: Joints (Courtesy of the ANSYS User Manual)

image
Revolute
ROTZ Free
image 
Cylindrical
UZ, ROTZ Free
image 
Translational
UX Free
image
Slot
UX, ROTX, ROTY, ROTZ Free
image 
Spherical
ROTX, ROTY, ROTZ Free
image 
Bushing
UX, UY, UZ,
ROTX, ROTY, ROTZ Free
User specified stiffness and damping in all DOF’s
image
Planer
UX, UY, ROTZ Free
image 
Universal
ROTX, ROTY Free
image
General
User defines free DOF’s as well as stiffness and damping on free DOF’s

You define joints by selecting the Connections part of the tree and insert the Joint you want.  Joints can be between two bodies or between a body and fixed ground.  Each details view is a little different but most look like the one for the revolute joint:

 

imageFigure 2: Revolute Details View

In most cases, defining the joints is very easy.  You use the geometric feature that is in your real part to define the joint.  Two holes for a revolute joint or a slot for a slot.  The important thing to pay attention to for joints is which body is the reference body, and which is the mobile body. The first body you click on is the reference and the second is the mobile one.  If you get it wrong, RMB on the joint in the tree and choose Flip Reference/Mobile. 

Next, pay attention to your coordinate systems for the joint. By default, the coordinate system is defined in the reference face of the details and by default is aligned with the geometry you picked to define the reference.  This is because the coordinate system needs to move with the geometry.  If the reference geometry isn’t exactly what you want, you can change it by picking a new face, line, or point.  Make sure you check the graphics window and that the coordinate system is in the right place.

Once your joints are done, you can insert the other type of connection, a spring. They work the same way except the define a force load on the bodies that is related to their relative position.  You can have tension only, compression only, or a normal spring.  They work like joints in that you specify two bodies or a body and ground to connect.  You have more control on where the end points of the spring are, by specifying a piece of geometry or a point in a coordinate system.  You can also add in preload.

imageFigure 3: Spring in Assembly

You can also specify contact.  But before you do, make sure that a joint won’t work first.  If contact makes sense, then put it in just like you would in ANSYS Mechanical.

imageFigure 4: Contact

Other Stuff you Should Know

Sometimes you don’t have geometry? No worries. you can define a point mass and add in its inertia properties to get the same behavior. 

The Rigid Dynamics solver has its own command language and just like when you use MAPDL as the solver, you can use code snippets to send it commands.  It uses Python and the commands are documented in the help.  The most common use for this is creating relationships between DOF’s that joints can’t do.  Gears are the best example, you can specify that body 2 rotate twice for every time body 1 rotates, etc…  It is also a good way to define a non-linear spring or a complicated stop condition. 

imageFigure 5: Code Snippets

Speaking of stops, you can specify stops and locks on the joints to control how far a joint can move and if it reaches that stop, that it freezes (lock).

Conclusions

And, without going into too much detail, that is Rigid Dynamics in ANSYS.  You can see some cool examples on the ANSYS web site:

http://www.ansys.com/Products/Simulation+Technology/Structural+Mechanics/ANSYS+Rigid+Dynamics#/0

The key as always is to take the time to really read understand the tool then crawl-walk-run.  Don’t start with a 100 part assembly and expect it to work.  Pick a simple assembly or a simple subassembly and go from there.  In fact, regardless of your part, start with a four bar linkage and get that going first.

Happy motion!

Keyframe Animation with CFD-Post

Creating useful visualizations of your fluid simulation results can be sometimes be a challenge. Images of contour plots, isosurfaces, or streamlines may not be sufficient to communicate your point. Sometimes you need to combine results together and use visual effects to transition between them. Cue up keyframe animation. In case you are not familiar with the term, “keyframe” means that multiple visualization techniques (zooming, flying around, fading, time-animation, contours, isosurfaces, etc.) are combined together in one animation file.  Keyframe animations generally require some planning and testing before creating the final result, especially as you get more skilled and start adding more results/effects.  However, if you are willing to put in a bit of extra effort, you may find that you have not only communicated your point well, but have also visually engaged your target audience, whether the audience is your managers, your customers, or that hostile crowd at a technical conference.

CFD-Post, the post-processing tool of choice for the ANSYS CFD solvers, presents a straightforward interface for creating keyframe animations. If you click on the “film” icon along the top row of GUI, the Animation Dialog appears.

Figure1

Figure 1 – Animation Dialog in CFD-Post

The basic idea of a keyframe animation is that you create multiple keyframes, which are connected by transition frames.  Each keyframe is essentially a snapshot of whatever is in the GUI window, and when you animate them together CFD-Post interpolates in three-dimensional space to create transitions between your keyframes. 

Let’s walk through an example. I want to do a zoom effect on my race car model. I’ll start from a “global” view of the entire model (Figure 2a).

Figure2a

Figure 2a – Global view of car model

Next, I’ll insert my first keyframe by clicking the blank page icon in the keyframe animation dialog menu. This keyframe will have 80 distinct frames (interpolation points) between itself and the next specified keyframe.

Figure2b

Figure 2b – Create first keyframe: 80 intermediate frames

Now, since I want to animate the “zoom” portion, I’ll zoom in closer (Figure 3a).

Figure3a

Figure 3a – Zoom view of car model

Next, create the second keyframe at this position.

Figure3b

Figure 3b – Create second keyframe: 80 intermediate frames

Click “Save Movie” box, and then select the “Play” button to record it. Here is the result:

keyframe12

Figure 4 – 80-frame animation from Keyframe 1 to Keyframe 2

For the next part of my keyframe animation example, I’ll add in a CFD result: pressure contours on the surface of the car. First, the solid color on the surface of the car will need to be faded out, and then we can fade in the pressure contour.

I can create Keyframe 3 to account for the fading out of the sold blue on the surface. First, I need to specify that the transparency = 1 (“0” denotes no transparency, “1” denotes 100% transparent) for the surface group that comprises the surface of the car (Surface Group 1). This will allow the algorithm to start with transparency = 0 at Keyframe 2 (the default), and end with transparency = 1 at Keyframe 3. Next, I need to account for the fading in of the pressure contour. Thus, I create a contour of the static pressure on the surface of the car (Contour 1) and set its transparency = 1. Then, select the new keyframe icon to create Keyframe 3.

Figure5

Figure 5 – Create Keyframe 3 to represent fade-out of blue surface

Finally, I can create Keyframe 4 to account for the fading in of the pressure contours on the surface. Now, all that is required to set the transparency of the pressure contour (Contour 1) to “Transparency = 0”. Then, select the new keyframe icon for Keyframe 4 (reference Figure 6).

Figure6

Figure 6 – Create Keyframe 4 to represent fade-in of pressure contour

Now,to record/save the keyframe animation, select “Save Movie” in the Animation dialog menu shown in Figure 6. I typically select “Loop”, and “Repeat” = 1.  The “Bounce” option reverses the animation when it reaches the end.  The finished product is shown in Figure 7.keyframe1234

Figure 7– 160-frame animation from Keyframe 1 through Keyframe 4

Now that I have demonstrated how the keyframe functionality works in CFD-Post, it becomes apparent how to create keyframes. The first step is plan out the keyframes in the animation; essentially, visualize an image that represents each keyframe. Then, experiment with the transitions between them; fading, zooming, flying around, etc. Finally, put the entire keyframe animation (keyframes plus transitions) together and let your customer, manager, or colleagues watch in awe.

PADT’s New CUBE HVPC Computers

Editors Note, 7/30/2014:
This was an early post on our CUBE systems, we have since updated and improved our offering.  Visit www.padtinc.com/cube to get the latest information.

Cube_Logo_Trg_100Most of you should have received an e-mail blast yesterday announcing that PADT is now selling  a line computer systems that are configured specifically for CFD and FEA users.  Our CUBE High Value Performance Computers became a product line when a couple of customers asked us about the hardware we ran on and if they could get the same from us.  After doing a couple of systems this way we decided to go mainstream. 

You can read our announcement here:

http://myemail.constantcontact.com/CUBE-HVPC-Systems-from-PADT.html?soid=1102494320275&aid=IAV1Jm5nRSA

And you can download a brochure here:

Cube_Brochure_Img[Replaced]

We also have a new website with more details:

www.padtinc.com/cube

Give us a call (480.813.4884) or send an e-mail (info@padtinc.com) if you need more information or have questions.

What is PADT Up To?

Since The Focus is kind of an engineer-to-engineer exchange, we thought we would use this weeks posting to give the hard core users out there some details on these systems and some things everyone should know.

The thing we want to point out first is that these are not super-fast high performance systems.These are based on the new AMD chips which are fast per cycle, but run at a lower clock speed. You would need the latest Intel Nahelem chips for maximum performance.  What they are is a balance between speed and price.  As an example, you can go to the Dell website and configure a 3.0 GHz Intel based system with 8 cores that is as close to the CUBE w12 (12 core) system for about $9,800.  the w12 is $5,800.  that is 68% more expensive than the Intel based system for maybe a 40% speed increase.  And, if you have enough parallel licenses, it may be about the same speed 12 AMD cores vs. 8 Intel cores.  That is the kind of tradeoff we look at when configuring these systems.  If you need more than 8 cores then the price difference really gets big.

That is one way we get the price down. The other is by cutting back on two big price drivers for someone selling computers: service and inventory.  We just don’t cut back, we pretty much cut it out.  When you buy a system we order the parts to build it. This saves us a ton on inventory and we pass that on to you.  We did this because in reality, you really don’t need it tomorrow.  In fact, it will probably take 3-4 weeks just to get buy-in from your management.  So way then pay a lot of money to get the machine in 2-3 days when you already waited 3-4 weeks? 

We really cut back on support. We test your system here with your software and then ship it off.  You get 8 hours of phone support and a 1 year parts only warranty.  After that, you have to pay for time or parts.  We did this because in the years we have been running computers for simulation, we never use the service and we almost never have a bad part we need replaced after one year.  sometimes you get a bad part right out of the box, but if it lasts a year, it will last till it is out of date.

So, in a nutshell, we are trying to find a sweet spot for users where, for a reasonable amount of money, they can get a reasonable amount of throughput. 

Some Highlights

Portable Mini Cluster – 96 Cores, Two Boxes, 256 GB of RAM: $43,250

One cool thing to point out about our 96 core system is that we built it to be portable.  We felt that if someone needed that much horsepower they probably wanted to share it.  So we build it into its own rack that we put on wheels.  We also add two UPS’s to provide battery backup power.  So if you need to unplug it and roll it down the hallway, go for it.  This also makes these systems perfect for use in security areas where you need to wheel a box around between closed secure rooms as needed. When you get done with a project, pull the drives and wheel it to the next project.

Another cool thing about this system is that it uses a special Infiniband interconnect that does not require a switch when you connect only two systems.  This saves a few thousand dollars.

The “Sweet Spot” – 32 Cores, 128 GB of RAM, $12,300

We feel that the best deal in the CUBE line is the 32 core system.  Not so much because of the hardware but more because of the ANSYS HPC license packs.  If you buy two packs, you get the ability to two jobs on 8 cores each or one job on 32 cores.  It also comes with enough RAM to handle bigger problems (128GB) at a great price.  Even if you add some bells and whistles it should still be under $15,000.  You need to run the numbers but most users will be able to purchase a CUBE w32 and two HPC packs for about the same or less than two 8 cores systems, an interconnect, and 16 parallel tasks, and still run faster.

What To Do with all Those Dang Files! – The 10TB Fileserver

We configured this system after a customer that bought a 96 core system from us told us that they were paying something like $20k for a fancy file storage device.  Now granted, this is a very fast I/O machine with software and hardware that lets you write files to it during a run.  But honestly, why do that?  Why not just put 1-3 TB on your compute server or mini-cluster and then get a cheap box with a bunch of inexpensive disk drive on it. Run on the fast machine and when you get done, copy your files to the inexpensive file server.  Our pre-configured FS10 has 10GB of disk space for $5,000.

But that only solves one problem: what to do with those big files when you need to keep them around.  What do you do for long term storage?  At PADT we use these external hard drive docking stations (eSTAT Drive Bay) and we go out and buy standard 1TB hard drives.  We plug those guys in, copy our files to the hard drive, unplug it, label the drive, and throw it in a drawer.  To be safe, we often make two copies of the files and actually put one in a fire safe.  The fs10 comes with an external eSATA Dual Drive bay to solve your long term archiving problems in a simple and cost effective way.

Bottom Line

So, is a CUBE HVPC system right for you?  You need to decide if giving up some speed, support and delivery time is worth the savings. Also, is a system designed for CFD and FEA a better fit, vs. a database server that they label as an HPC machine.  The best way is to contact us, and we’ll help answer those questions.

Not the most exciting The Focus posting, but we think you will agree that this approach to computing can be very efficient, so we thought we would share it with you.

Mapped Meshing–Pt Deux

Eric Miller wrote a nice article back in February explaining the basics of using the mapped face meshing control available within ANSYS Workbench.  Now…it would take a special kind of man to tell one of the directors of the company he works for that well…he just didn’t do a good enough job explaining it.  Lucky for you all, I’m just that kind of man.  “I have a different brain; I have a different heart; I got tiger blood, man.” – Charlie Sheen

CaptureI honestly thought my Stallone references were my best…how wrong I was

First of all, let’s start by discussing some behind-the-scenes operations being done.  When you specify a side-vertex, the mesher internally concatenates the adjacent lines together to map the divisions of one block to another.  So just by changing the side-vertex, you can ‘spin’ the mesh orientation around a given block.  The images below show the difference as you pick different ‘side’ nodes.  I had the local 3rd grade class draw lines over the map mesh source/target, divisions get mapped between same color lines.

mesh1mesh2
You caught me…I never passed 3rd grade art class

So if we look at this a different way, the mapped face meshing is creating some virtual edges while still preserving the vertices for future use (e.g. applying displacements/forces).  However, let’s say we really wanted to  control some of the mapped divisions.  We can do this using actual virtual topology to merge AND split edges (new feature in R13).

Here’s a picture of Eric’s example.  It looks good enough, but for my discerning tastes I would prefer to have all of the divisions above the red dashed line match up, rather than having the divisions meander across the surface (technically speaking).

ex1
Isn’t it cute when managers try and use ANSYS?
 
With a little work, we can get the following:

ex2

Winning!

To get this, we just need to use some virtual topology tricks to split and combine the vertical edges.  This can be done by inserting a virtual topology branch, available on the context toolbar when the ‘Model’ branch is highlighted.  You then have the option of creating virtual cells or splitting edges.

virtual1

virtual2

To insert VT, Select the ‘Model’ level When you have an edge selected, VT tools become available.  ‘Split’ functionality available at R13

To combine two lines into one, simply select both of them and then click ‘Virtual’ Cell’.  If you want to split an existing edge, highlight it and select one of the split options.  If you select the ‘Split Edge at +’ it will split the line where you clicked on it:

edge
‘+’ appears where you LMB on the edge
Once the edge is split, you can move the split location by modifying the split ratio displayed in the Details Window.
edge2
Modify ratio until you’re happy with the edge split
 
To get the masterpiece above, we first split the vertical lines, eyeballing the division to get the new point in-line with the bottom of the ‘notch’.  Next, we combine one of the split edges with the edge above it:
comb1 comb2
Step 1:  Split edges.  Move ratio until it’s ~horizontal with bottom of notch Step 2:  Create virtual edge from split and neighboring edge (indicated by red arrows)
Step 3:  Not shown, take long lunch and bill for 6 hours of work
 
The last step is just to apply a mesh edge sizing control to the proper lines:
meshc1 meshc2
Selected Edges Make sure it’s a ‘Hard’ control.  That way the mesher knows we mean business
 
Next just generate the mesh.  I already know what you’re thinking…there are still some regions of the mesh that are iffy
mesh4
Maybe I should leave the ‘Winning’ to the experts
 
We can play around with some additional settings, go back and modify the geometry, get a huge headache, and ultimately leave at the end of the day frustrated (mostly because of the curved edges that need to have divisions mapped across to a straight line).  Or we can realize that maybe a free-mesh with a hex-only meshing method looks just as pretty.
 

Capture

Looks good to me!

Long story short….

  • Use new virtual topology to fine-tune the geometry to meet your mapped meshing needs
  • Anytime you’re dealing with curved edges, it may be worth your while to use a free mesh

Beyond Clicking: Named Selections Move to the Worksheet

It seems like a minor thing.  For many users who have not known the glory that is APDL selection logic, they are not even aware of the power that surges through your fingers when you grab all the vertices between X = 1.2 and X=1.8, grab the lines attached to those vertices, then the areas defined by those lines.  It sometimes borders on a magical incantation.  It is not a minor thing.  It is reaching into your model and grabbing it by the throat and asserting your control over it.

OK, maybe that is an exaggeration.  But it has been a frustration for many of us to only be able to create named selections by clicking on geometry in Workbench.  At release 13 the friendly developers working on ANSYS Mechanical have added a very cool adaptation of the MAPDL selection logic into the workbench world: Worksheet Named Selections.  Right now they only work with geometry, but look for nodes in future releases. 

Creating a Worksheet Based Named Selection

Most people don’t know about this feature because it is not really obvious that it is there.  If you have no Named Selections in your model, the Named Selections Icon in the toolbar doesn’t even show up unless you click on the Model top branch of your tree:

nsw_F1Figure 1: Accessing Names Selections for the first time in a model

If you have some already in your model, just click on the Named Selection branch and you will see the Named Selection icon on the tool bar.

To create a Named Selection… OK.  Stop.

 <RANT> Someday I’ll find the committee that decided to call these groups of entities “Named Selections” and give them a bill for my coming carpal tunnel syndrome.  I think it is a good descriptive name and as long as I’m just clicking on it in a GUI, the name is fine.  But when I’m talking or typing about it… what a pain to have two words to describe it.  I miss the days of only having 8 characters for entity and routine names in FORTRAN.  There is too much verbosity in the world!  Down with long, descriptive names!  Up with short, non-descriptive names that you have to “know” in order to use!  Why couldn’t we call it a set, or a group, or an assembly – better yet, asmb.  Anyhow, from now on I will refer to Named Selections as NSs or a NS.</RANT>  

To create a NS using a worksheet, you simply clock on the NS icon.  This adds a NS Branch and a NS to that branch.  If you look at the details View for the NS you will see that the Scoping Method is set to Geometry Selection by default.  Click on the Geometry Selection and you get a drop down menu that now offers you the choice of using the Worksheet.  Make that choice:

nsw_f2Figure 2: Telling Workbench you want it to reveal Worksheet method of creating a NS

Now you should see a new tab in your graphics window called Worksheet, and in that Worksheet you should see a Generate button and a table.  If you window is large enough, it will look like this:

imageFigure 3: The NS Worksheet

If it is too small to show the whole table, a scroll bar will be at the bottom and cover up that blank row. Don’t panic, it is supposed to do that.

image
Figure 4: The NS Worksheet with Scroll Bar covering the first row

If you are like me you get to this point and you go “What Next”  It is not obvious what the next step is (well, to me anyway.  But it may be trivial to those with younger brains).  Clicking on the blank row doesn’t do anything. To start creating you selection you right click on the table and choose Add.  You are now ready to specify your NS.

Building a Worksheet Based Named Selection

This tool is pretty clever.  You basically add rows to the table that act on geometry from top to bottom.  In many cases you just use one line.  But for more sophisticated selections, you can really stack them up and get complicated.  After each line is executed, the active set changes based on what is on that row.  The final active set is what goes into the named selection.

Lets take a look at the table and each column in it.

nsw_f5
Figure 5: NS Table

Action
This is the most important column. It tells workbench what you want to do in this step of applying the filter.  Under action you have the following options:

  • Add: Adds the entities defined by the row to the active set.
    • This is a UNION in set theory
    • For the first row in the table:
      • This is a CREATE operation in set theory
      • There is no “active set” so the first line add’s to a null set (xSEL,S,,, in APDL)
    • For the second and subsequent rows:
      • In order for it to work, the value in the Type column has to be the same.  If they are not the same, then nothing gets selected.
      • This is an OR operation.  So if you have one line that selects all vertices with X > 0 and a second with Y > 0 you will get vertices that are X > 0 OR Y>0.
      • xSEL,A,,, in APDL
  • Remove: Removes the entities specified on the row from the set.
    • Think of it as an unselect: xSEL,U,,, in APDL
  • Filter: Selects a subset from the active set
    • This is an INTERSECTION in set theory
    • This is an AND operation.  It puts everything in the active set that is also defined by the row in the active set.
    • xSEL,R,,, in APDL
  • Invert:  Selects everything not in the active set of the current active set type, and makes it active
    • This is a COMPLIMENT in set theory
    • This is a NOT using the everything of the current type vs. the active set.
    • xSEL,INVE in APDL
  • Convert:  Selects all of the entities of the type specified in the Geometry Type column that are connected to the active set and makes those entities the new active set
    • Must go up or down in topology tree by one step:  vertex <-> Edge <-> Face <-> Body
    • When going up it selects ANY entity that is attached to the current select set.  There is no way to specify just those that are fully defined
    • xSLx in APDL:  LSLA, ASLL, KSLL, LSLK, etc…

Geometry Type
Right now it only selects the standard geometry topology types:

Workbench

CAD Name

MAPDL Name

Body

Solid Volume
Face Surface Area
Edge Curve Line
Vertex Vertex Key Point

Remember that you can only have one Geometry Type in the active set at a time.

Criterion
This tells the program what filter you want to apply to select geometry.  The table below shows the different criterion and what Geometry Type’s they apply to:

  Vertex Edge Face Body Comment

Size

 

X

X

X

Selects entities by whose Length (Edge), Area (Face), Volume (Body) fall within the specified values

Type

 

X

X

X

Finds entities of a certain underlying shape:
Body: Solid, Surface, Line
Face: Plane, Cylinder, Cone, torus, Sphere, Spline, Faceted
Edge: Line, Circle, Spline, Faceted

Location X

X

 

X

X

You can specify a value or a range and you can also specify a coordinate system to use!

Location Y

X

X

X

X

You can specify a value or a range and you can also specify a coordinate system to use!

Location Z

X

X

X

X

You can specify a value or a range and you can also specify a coordinate system to use!

Face Connections

 

X

   

Used to pick edges by the number of faces they are connected to:  0 would be all free edges, 1 would be all edge on only one face, 2 would be shared by 2 faces, etc…

Operator
This is where you specify how you want to control your selection.  You have your standard if() type operators as well as the ability to specify a range.  For range you put values in the Lower and Upper Bound columns and for all others, the Value column.  The options are:

  • Equal, Not Equal, Less Than, Less than or Equal, Greater Than, Greater Than or Equal
  • Range

Units
Like most things in Workbench, you can change the units here if you want.

Value
Here is where you put in value you want to select on (unless it is a range).  Put in a numerical value for Criterion = Size or Location X/Y/Z.  Use the values in the table above for Criterion = Type. And lastly, if you are doing Face Connections, put in the number of faces.

Lower Bound, Upper Bound
When Criterion is set to Range, you specify the range here.

The other thing you should know is how to modify your table.  To change a value in a cell, just click on it.  To delete a row, click on a cell  in the row then RMB and select delete.  Do the same thing to insert a new row before the currently selected row.

Lastly, remember to hit the generate button!  If you don’t, it does not update and even though you have a green checkbox next to your NS in the tree, it is not up to date.  You have to hit generate or select the NS branch and RMB->Generate Named Selections

Starting with Picking

A nice way to start a NS using a worksheet is to actually pick an entity on the screen then RMB->Create Named Selection.  this brings up a dialog box where you can specify the name and if you want to turn it into a Worksheet named selection:

imageFigure 6: Selection Name and Options Dialog

If you choose “Apply Selected Geometry” it makes a standard Scoping Method of “Geometry Selection”  This is the default.  But if you pick “Apply geometry items of same:” and then pick one of the options, it creates a NS with a Scoping Method of “Worksheet.”  Now you can go into the worksheet and add rows to it.

Tips on Using the Worksheet Named Selections

That was a lot of information! If you need more clarification you can get even more from the Help menu:

// Mechanical User’s Guide // Basics // The Mechanical Application Interface // Named Selections // Worksheet Rules for Specifying Named Selection Criteria

When you want to use these selections it is fairly simple.  Each time you add a row you modify the selected set.  Like most things it is a good idea to take a crawl-walk-run approach. Add a row and click the generate button and see how many entities you get in the details view.  Or simply click on the graphics tab to visually see what was selected. Then add the next row, and so on until you have what you want.

As always, it is a good idea to give descriptive names to your NSs.  The default of Selection, Selection 1, Selection 2 doesn’t hold up in 4 months when you try to figure out what you were trying to do.

Some Examples

The most common way that we have used this is to select all the holes of a certain size.  There are a couple of ways to do this, but the one I prefer has the following steps.

  1. First you need to know the circumference of an edge on your hole.  You can do this by multiplying your diameter by PI or just select one of the edges and read the length of the edge on the bottom of the screen (because multiplying by PI is too hard)
    nsw_f7
    Figure 7: Avoiding the use of the first thing you learned in 5th grade geometry
  2. Now click on the Named Selection icon and change the Scoping Method to Worksheet.  Add a line and set it to: ADD, EDGE, TYPE, EQUAL, CIRCLE
    This gives you all the circle edges
  3. Next Add another row and set it to: FILTER, EDGE, SIZE, EQUAL, 0.7854 (That is the length of my hole’s circumference for this example).  Note that we used FILTER, not ADD!
    View the results and you should now see the edges of you holes. It is often good to switch to wireframe to see all the edges:

    image                                                   Figure 8: Edges Selected on Holes of Diameter 0.25”

  4. The next step is to get the cylinders.  We do this with a new row that uses Convert To: CONVERT TO, FACE.  This gives us the holes we want, but also the top and bottom faces that are attached to the hole edge:
    image                                                                   Figure 9: Faces attached to hole edges
  5. The last step is to select all the cylinders in the active group.  Add a row with: FILTER, FACE, TYPE, EQUAL, CYLINDER:

    image                                                                      Figure 10: Holes are selected!

Final Thoughts

And that is a basic introduction to using the worksheet to do selections.  As always, the best way to get good with this is to use it.  The help is a pretty good reference as well.  Users can get very creative with this and really make some big improvements to productivity, eliminating the need for a few more APDL Code Snippets in the tree.

Random Thoughts on RSM, ANSYS people, and my new iPad2 and my Son’s 3DS

Warning, when you get done reading this you might be a little disappointed.   A couple of our engineers promised nice chunky articles for this week but then got hammered with billable work.  And I was off doing paperwork all week so I didn’t have time to write anything truly useful.  So, a day late, here are some random thoughts disgorge as a stream of consciousness prattling.

I’m Digging RSM

We have been redoing our compute server setup for a while now and it is getting pretty robust and useful.  When I recently had an opportunity to run a real model I initially submitted it on my desktop, then found that it bogged it down so much that I could not continue my slug through HR documents and financial reports… and the livelihood of PADT depends on my ability to slug through such things. The obvious solution was to batch off my ANSYS Mechanical solve on one of our servers.  And RSM made it easy.

I have to give credit to Jason Krantz and our IT team for getting things set up right a while back.  So when it came time for me to use it, it was real easy.  Now the graph on Ted2 (the server, not Ted Harris) is humming along using 7 CPU’s and about 13GB of RAM and my job is on LS3 and the force value is plunging towards convergence.  Good times… Good times…

To make this article somewhat worthwhile I thought I’d pass along a couple of things I had to remember or that Doug reminded me of.

Controlling the solve options on the remote machine

If you want to set the number of CPU’s, memory usage or any other options that go on the ANSYS command line, you do that by going to Tools->Solve Process Settings… in ANSYS Mechanical.  Pick the queue on the remote machine (if you don’t see it, you don’t have RSM set up right and that is a subject for a real posting…)  then click on Advanced.  Here you will see you options for using DANSYS, specifying your memory (always a good idea in my opinion), the number of CPU’s, additional command line options, Unix options, etc…  the most important ones, and the ones you should always check before you run are the number of processors and memory.

image

Monitoring your run

Since you are running remote your output file and your various convergence graphs don’t get displayed automatically.  I have to admit I sat at may desk for a while before I realized nothing was going to happen on its own.  Then Doug reminded me, only with a small amount of disdain in his voice, that you have to prompt the remote solver for such information.  You do this by Clicking on Solution Information in the tree and RMB –> Retrieve.  It will send off a request to the solving machine to send back the data.  It takes some time but eventually you will get updated Solution Information.

Of course the old-foggy way of doing it is to SSH over to the solving machine (I use PUTTY) and go to the solving directory and do a tail on the solve.out file.  The graph is much more useful but having a file scrolling convergence information across your screen is much more impressive and makes you look much busier to people who wander buy and glance at your screen.  At least in contrast to Facebook.

imageimageimage

Which makes you look more impressive or productive?  trail –f solve.out, ANSYS Mechanical Convergence Plot, or Facebook?  Maybe I should set the terminal for the trail file to be green on black…

Getting Files Back

One downside of RSM as it is now configured is that it doesn’t copy back all the files that you may create during your run.  It can’t read your mind so it doesn’t know that “frc_hist.csv” is something you need.  A simple work around is to add an APDL Code Snippet to the post processing part of your tree and add a line at the end with something like:  /sys, cp frc_hist.csv ~

This copies the file to your home directory on the remote machine.  Then, when the job is done you an go over and retrieve it.

ANSYS People

I get to interact a lot with the people that work at ANSYS, Inc.  Sales, Support, Development.  The whole company.  And over the last two weeks we have been fortunate enough to have some folks come out and visit our largest customer, Honeywell.  This has been going on for years now, since we started PADT 17 years ago.

Last night we took three of these ANSYS folks to dinner.  One from HQ here in the US, one from ANSYS India and one from ANSYS France.  We started at 7’ish and got chased out of the restaurant at 10:00.  We talked about old ANSYS stuff, new ANSYS stuff, development methods, solvers, computers, strange animals, traveling, the competition, graphing, jScript, UDF’s, automatic report generation, morphing, blah, blah, blah.  Three hours was gone in a blink of the eye.  Fun. 

My point – the best thing about ANSYS, Inc. is the people who work there.  Smart, passionate, knowledgeable, hard working, and their hearts are always in the right place. 

Just thought I’d pass that along.  If you get a chance to really spend some quality time with any of them, do it.

ANSYS Commercial

And if you haven’t seen it yet, check out the new ANSYS commercial on the home page: http://www.ansys.com  Click on “View Video”  Put aside your engineers detachment and strong desire to pick anything that looks like marketing to little pieces.  I think it is super cool and makes me even well up a bit.  I really like the new tag line: Realize Your Product Promise. 

image

My new iPad2 and my Son’s 3DS

Love it.  3G version.  Take it everywhere. Instant access, on a screen I don’t have to strain to read, to information.

But from an engineering standpoint, I bring it up to not only fill up space in this posting.  I bring it up because we have been wondering a lot about the fact that the type of interface on an iPad is probably going to become the dominant interface.  How should the GUI on an simulation tool change to take advantage of that? 

About the same time as I got my iPad2, my son got a Nintendo 3DS.  It has a small but very clear 3D screen that you don’t need glasses to view.  How cool would it be if one of your monitors was like that. If you could put your graphics window on the 3D monitor and view your model in 3D without dorky glasses? Would that be a productivity increase or just a source of additional eye strain?

Back to paperwork

OK, I’ve filled up a posting.  Now on to filling out FORM BRDI-1A: 2010 BUSINESS R&D AND INNOVATION SURVEY.  The first words under the title are “Your Response is Required by Law” so I better get cracking… wait, my model just converged. Let me go retrieve that csv file and see if the answers make sense. 

ANSYS Mechanical Scripting: HOWTO Part 5

Where we Left Off Last Time…

In the last post of this series, I left you with an introduction to the Microsoft Visual Studio debugger and a description of how to debug scripts that we write.  In this post, I’m going to illustrate how to write what might actually turn out to be a useful script!  The idea for this script came from a request from one of our users, Craig Hildreth, who has always wished that there was a better way to define slice planes in Mechanical. 

Problem Description

Lets say you want to create a slice plane in ANSYS Mechanical that cuts precisely through a certain location in your model and that is precisely oriented to be normal to a certain direction.  How would you do it?  If you’ve used the slice tool in Mechanical before, you know that you basically yank a line segment with the mouse across the screen.  The resulting slice plane is defined such that if you were to extend the line segment you created with the mouse to infinity and then extrude the line in the direction of the current view the resulting plane formed by this operation would be the slice plane.  Can it be more intuitive?  So, of you go spinning and panning your model around to where you think you are looking right down the slice plane, then with the steadiness of a surgeon you try with all your might to draw a straight line with the mouse.  (Good luck!)  Now, to ANSYS’ credit, this is a really cool tool if you just want to whip a quick slice plane on the model to look inside somewhere.  Especially when you’ve got that cool little drag widget.  However, if you are trying to use the slice tool for any kind of meaningful post processing, your probably out of luck.  What would be awfully darn useful would be create or use a Cartesian coordinate system that would then define the slice plane.  That sounds kind of intuitive, right?  Anyway, you might optimistically think that functionality should already be there, but alas to my knowledge it is not.  So lets see if we can fake it.  (If you just want the code and don’t care how we write it, check out this post where we give the code.  This is a long post because it turns out this is trickier than it looks…)

How the Heck Do You Create a Slice Plane… Programmatically that is?

So, step one is to figure out what Mechanical does under the hood when we create a slice plane.  If you haven’t read Part 2of this series, I suggest you do so now.  We’re going to use the techniques in that post to figure out what Mechanical is doing when we create a slice plane. 

Searching the ANSYS Source Files for Info on Creating a Slice Plane

If you haven’t started up Mechanical, do so now and make sure that the slice plane window is visible by ensuring that menu item View->Windows->Section Planes has a check mark beside it.  In the section plane window, hover your mouse of the create section plane button.  You should see a tool tip like the following:

image

We see that the tooltip is called “New Section Plane”.  This is a string for which we can search.  So, search the entire directory tree C:\Program Files\ANSYS Inc\v130\aisol\DesignSpace\ for the string “New Section Plane”.  We see that in C:\Program Files\ANSYS Inc\v130\aisol\DesignSpace\DSPages\Language\en-us\xml\dsstringtable.xml we find that string.  Remember that this is where you are normally going to find GUI related strings.  This string has the string id: ID_NewSectionPlane.  So, lets search for that.  One place we find this string is inside the file: C:\Program Files\ANSYS Inc\v130\aisol\DesignSpace\DSPages\scripts\DSSectionPlanesScript.js  This file name sounds promising.  Here is some code from that file showing where the id shows up.

 

btnAdd = toolbar.Buttons.AddButton( TBT_BUTTON, 
"NewSectionPlane", ID_ADD, 
"NewSectionPlane", false, "", 
localString("ID_NewSectionPlane") );

btnAdd.OnClick.AddCOM(g_sectionPaneObjects, 
"SectionPane_Add");
  

This code is reformatted to fit better in the blog.  You can see that the code appears to be adding a button to a toolbar and also registering a callback for when the button is clicked.  The callback is what we’re interested in, so lets search for “SectionPane_Add”. Searching for that leads us to the following code:

this.SectionPane_Add = function sectionPane_Add(sender, args)
{
    if (!AllowSlicing ())  return;
    doAddSectionPlane();
 }

We’re getting closer!  It looks like we need to peek into doAddSectionPlane().  Let’s search for that.  It turns out that that function is a bit of a dead end, but lucky for us right below it we see the following code:

function doDeleteSectionPlane()
{
    var sliceTool  = ds.Graphics.SliceTool;
    if (sliceTool == null)
        return;
   ...
}

Hey!  There is something called a SliceTool that is a member of the Graphics object associated with the global DesignSpace Object.  Lets quit grepping and slogging through the ANSYS Mechanical code base and fire up the debugger to see what we can learn about the SliceTool.

Using the Visual Studio Debugger to Learn About the Slice Tool

Now that we’ve figured out that there is an object called SliceTool that is a member of the Graphics object, lets use the debugger to interrogate that object.  Create the following javascript code and save it somewhere on your hard drive.

function slice_tool_interrogate() {
    debugger;
    var slice_tool = DS.Graphics.SliceTool;
    // Useless code to give us a place
    // to stop
    var i = 1;
}

slice_tool_interrogate();

The code above will allow us to stop in the debugger and look at the slice tool object.  The following screen shot shows the slice_tool object’s properties and methods in the debugger.

image

Hmm… Well, there is one function there called SetPlane(ix1, iy1, ix2, iy2) that looks kind of promising.  Unfortunately, I don’t see any function that would seem to hint at defining the plane with a point and normal, for example.  It looks like the programmatic interface mimics the user interface.  Arg…  So, lets just guess that given two points in screen coordinates (x1, y1 and x2,y2) we can create a slice plane programmatically.  What should x1, y1 and x2, y2 be?  Are screen coordinates normalized? Where is the screen origin; top left or bottom right or middle of the screen? The fact that the arguments have an “I” in front of them hints that perhaps they are integers, which would rule out normalized values.  What to do?  Well, lets try this.  Lets orient some geometry so that it is mostly centered with the screen and, using the immediate window in the debugger, pass in some values to see what happens.  Here is a screen shot prior to using the immediate window to test things out.

image

Now, I’m going to run our little test script again under the debugger, but after the slice_tool object is created, I’m going to enter the following into the debugger’s immediate window:

slice_tool.SetPlane(0,0.5,1,0.5)

When I do that, I get the following:

image

So, we created a slice plane!!!  Unfortunately, it doesn’t appear that it cut through anything.  We learned a couple of things.

  1. The function DS.Graphics.SliceTool.SetPlane(x1, y1, x2, y2) will indeed create a slice plane object.
  2. The coordinates don’t appear to be normalized.  (Can’t rule this out definitively, but lets try something else.)

Delete this plane, and lets work with the hypothesis that we need to pass in the screen coordinates as integer values.  If I were a programmer, I would choose pixels as my unit of measure for screen coordinates.  Things like mouse clicks in particular can be directly translated easily.  So, now the question is how big is the screen?  Or more precisely, how big is the graphics window?  This DS.Graphics object might hold the answers.  If you poke around this object inside the debugger you’ll notice that it has a property called GfxWindow.  That sounds promising!  If we look inside that object, we see:

image 

Ah ha!  This thing has a height and a width property associated with it, which I assume by the values associated with these properties are in units of pixels.  So lets try the following code.  You can modify your little test script as follows:

function slice_tool_interrogate() {
    debugger;
    var slice_tool = DS.Graphics.SliceTool;
    var height = DS.Graphics.GfxWindow.height;
    var width = DS.Graphics.GfxWindow.width;
    var slice_plane = 
        slice_tool.SetPlane(0, height / 2, width, height / 2);
    // Useless code to give us a place
    // to stop
    var i = 1;
}
slice_tool_interrogate();

When I run this little script.  My “after photo” looks like:

image

Ha!  We got ourselves a genuine slice plane.  And, we got it where we want it.  Well, sort of…  OK, so forgive me for celebrating a minor victory, but we were able to create a slice plane and draw a perfectly straight line right across the middle of the screen.   Now, if only we could programmatically rotate and pan the model around so that we are centered right on the origin of the plane we are interested in and we are looking right down parallel to the plane.  If we can do that, then we just draw a quick line with the SetPlane function and voila, we’re done.

How the Heck to You Rotate and Pan the View… Programmatically that is?

You know, this Graphics object is pretty handy.  Lets see what other goodies it might hold.  If we poke around in it a bit, we see that it has a property called Camera.  Camera sounds like something that would help us look at things from a certain point of view.   Here is a peek into the camera object inside the debugger:

image

This is looking promising.  You’ll notice that we’ve got a pan and rotate function.  And, it looks like we’ve got a focus point and view direction vector.  This is looking good.  Maybe we can write a function that will pan the view around until the origin of our coordinate system is centered where we want it and simultaneously rotate the view around until our view direction is oriented how we want it.  One other thing we’ll have to figure out is that we could rotate the view about the view direction vector so that it spins around the axis we are looking down.  Think of a drill bit spinning.  In that case neither the view direction or the focus point would likely change, but there is that rotateZ() function that might bail us out.  Lets see if we can figure out what all these things do/mean.  Lets start with the following test functions:

function do_pan(up, right) {
    var camera = DS.Graphics.Camera;
    var graphics = DS.Graphics;
    var a = 0;
    for (var i = 0; i < 50; i++) {
        camera.pan(up, right);
        // Just spin her so that the "animation" is more smooth.
        for (var j = 0; j < 1000; j++) {
            a += Math.sin(j);
        }
        graphics.Refresh();
    }
    // I don't know if Javascript does dead code removal, but
    // try to trick any optimizations the javascript interpreter
    // might do to our busy loop.
    return a;
}

function do_rotate(up, right) {
    var camera = DS.Graphics.Camera;
    var graphics = DS.Graphics;
    var a = 0;
    for (var i = 0; i < 50; i++) {
        camera.rotate(up, right);
        // Just spin her so that the "animation" is more smooth.
        for (var j = 0; j < 1000; j++) {
            a += Math.sin(j);
        }
        graphics.Refresh();
    }
    // I don't know if Javascript does dead code removal, but
    // try to trick any optimizations the javascript interpreter
    // might do to our busy loop.
    return a;
}

These two functions basically call the Camera pan and rotate functions repeatedly with some values passed in.  Note that if you include these in your little test script, once you stop in the debugger, you can call these functions inside the immediate window and watch what happens.  Just type do_pan(5,0) for example.  If you play around with these functions, you’ll realize that the rotate function seems to rotate the model about either the screen vertical axis or the screen horizontal axis.  Interestingly, the pan function seems to be backwards.  That is, the amountUp argument seems to move the model to the right and the amountRight argument seems to move the model up.  Ah well…  We’ll just make note of it.  How do we make sense of all these things?

Math, Math… Wonderful Math! Vectors and Rotations Galore!

Note, if math makes your head spin, we’re going to spin.  We’re going to take dot products and cross products and do all kinds of other gyrations.  So, feel free to skip this section if you are not interested.  However, this section constitutes the meat of figuring out what Mechanical is doing with the graphics given the few bits of information we have highlighted above. 

Lets start with rotating the view so that we’re looking where we want to look.  Our rotate function spins the model about the screen vertical and horizontal axes, so I’m going to propose an iterative technique that will sequentially rotate up and rotate right until the view vector is parallel to a supplied orientation vector.  Remember that we need to look edge on to the plane we wish to use as a cut plane so that we can draw a straight line across our screen and create the cut plane.  So, our view vector needs to be “in the plane” of our cut plane.  Consider the image below:

view_direction

The red vector represents the current view direction vector.  Now, assume that we call the rotate function associated with the camera object and request an up rotation of 15 degrees.  The resulting new view direction vector is represented by the green vector above.  Lets call this the test vector.  Taking the cross product of these two vectors (green and red) allows us to determine the normal vector, shown in black above, that represents an axis about which our requested rotation occurred.  We now know that if we call the rotate function with a value for the up rotation given the current view vector, we will rotate about the normal vector above.  So, now rotate the view back to the original view direction.  Let’s assume that our desired view direction is show above as the blue vector.  In general, it will not be in the plane of our current rotation.  However, if we project this vector onto the plane defined by the normal vector and the origin we obtain the purple vector above.  Now I know that I can rotate the view in the “up” direction by some angle and get to the purple vector. Its not the blue vector that I want, but it is the projection of the view vector on the plane in which my rotations are occurring. I can calculate a good guess for this angle by taking the ratio of the angle between the green and red vector with respect to my original trial rotation of 15 degrees.  Then, if I take the angle between the purple vector and the red vector, divide by my ratio, I’ll get a good guess as to what I should try to rotate my view so that I get to the purple vector.  If it works according to plan, this will get me closer to my desired view direction.  Now, I repeat this whole process by rotating in the other direction, that is rotating to the right.  Between each of these update rotations, I check to see if my current view vector is parallel to my desired view vector within some small tolerance.  If things go well, I should walk my way around to the desired view vector.  Consider the code below.

// Calculate the dot product of two vectors
function dot(va, vb) {
    return va[0] * vb[0] + va[1] * vb[1] + va[2] * vb[2];
}

// Calculate the length of a vector
function norm(va) {
    return Math.sqrt(va[0] * va[0] + va[1] * va[1] + va[2] * va[2]);
}

// Normalize a vector
function normalize(va) {
    var len = norm(va);
    if (len != 0.0) {
        return [va[0] / len, va[1] / len, va[2] / len];
    }
}

// Calculate the cross product of two vectors
function cross(va, vb) {
    return [va[1] * vb[2] - va[2] * vb[1],
            va[2] * vb[0] - va[0] * vb[2], 
            va[0] * vb[1] - va[1] * vb[0]];
}

// Scale a vector by a scalar
function scale(va, sc) {
    return [va[0] * sc, va[1] * sc, va[2] * sc];
}

// Add two vectors
function add(va, vb) {
    return [va[0] + vb[0],
            va[1] + vb[1],
            va[2] + vb[2]];
}

// Subtract vector b from vector a
function sub(va, vb) {
    return [va[0] - vb[0],
            va[1] - vb[1],
            va[2] - vb[2]];
}

function are_parallel(va, vb, tol) {
    return 1.0 - (Math.abs(dot(va, vb)) / (norm(va) * norm(vb))) < tol ? true : false;
}

function are_perpendicular(va, vb, tol) {
    return (Math.abs(dot(va, vb)) / (norm(va) * norm(vb))) < tol ? true : false;
}

// Rotate the view around so that we are looking in the 
// direction of the desired view
function rotate_view(desired_view) {
    // Get the camera and graphics objects
    var camera = DS.Graphics.Camera;
    var graphics = DS.Graphics;
    // This is our tolerance for being parallel to the desired view
    var eps = 1e-7;
    // This is the maximum number of iterations we'll try.  
    // While loops with a tolerance check only scare me...
    var max_iter = 200;
    // Get the current view
    var view_c = normalize([camera.ViewDirectionX,
                            camera.ViewDirectionY,
                            camera.ViewDirectionZ]);
    // Make sure we're normalized
    dvd = normalize(desired_view);
    // This should be close to 1 if parallel.

    var cnt = 1;
    var b_first_arg = true;
    var normal = null;
    var trial = null;
    var view_p = null;
    var trial_ang = 15;
    var applied_rotation = null;
    var previous_up = 0;
    var previous_right = 0;
    var right_factor = up_factor = -1.0;
    // Loop until we're parallel, or we give up trying
    do {
        var factor = cnt / max_iter;
        factor *= factor;
        factor *= factor;
        // Get the view direction
        vd = normalize([camera.ViewDirectionX,
                        camera.ViewDirectionY,
                        camera.ViewDirectionZ]);
        // Get the current up vector
        camera.rotate(trial_ang, 0);
        trial = normalize([camera.ViewDirectionX,
                       camera.ViewDirectionY,
                       camera.ViewDirectionZ]);
        current_up = normalize(cross(trial, vd));
        // Rotate back so we don't lose our place
        camera.rotate(-trial_ang, 0);
        

        vd = normalize([camera.ViewDirectionX,
                        camera.ViewDirectionY,
                        camera.ViewDirectionZ]);

        if (b_first_arg) {
            camera.rotate(trial_ang, 0);
        } else {
            camera.rotate(0, trial_ang);
        }
        trial = normalize([camera.ViewDirectionX,
                           camera.ViewDirectionY,
                           camera.ViewDirectionZ]);
        n = normalize(cross(vd, trial));
        // Rotate back
        if (b_first_arg) {
            camera.rotate(-trial_ang, 0);
        } else {
            camera.rotate(0, -trial_ang);
        }
        vd = normalize([camera.ViewDirectionX,
                        camera.ViewDirectionY,
                        camera.ViewDirectionZ]);
        // Only do this rotation if our desired view vector
        // is not nearly parallel to our current axis of rotation
        // If it is, then the code inside the if statement will
        // be skipped.
        if (!are_parallel(dvd, n, eps)) {
            // Project the desired view vector onto our plane of rotation
            vp = normalize(cross(n, normalize(cross(dvd, n))));
            // Calculate the angle between the projected vector
            // and our current view direction
            dot_product = dot(vp, vd);
            needed_rotation = Math.acos(dot(vp, vd)) * 180 / Math.PI
            
            // Knock it down by a factor associated with our iteration
            // scheme.  This helps prevent spurious jittering as we 
            // make our way there.
            needed_rotation *= (1.0 - factor);
            if (b_first_arg) {
                // If we start to diverge, try rotating back the other way
                if(previous_up < needed_rotation) {
                    up_factor = (up_factor < 0.0) ? 1.0 : -1.0;
                    previous_up = needed_rotation;
                }
                camera.rotate(up_factor * needed_rotation, 0.0);
                
                
            } else {
                if (previous_right < needed_rotation) {
                    right_factor = (right_factor < 0.0) ? 1.0 : -1.0;
                    previous_right = needed_rotation;
                }
                camera.rotate(0.0, right_factor * needed_rotation);
            }
        }
        // See if we are there yet
        vd = normalize([camera.ViewDirectionX,
                        camera.ViewDirectionY,
                        camera.ViewDirectionZ]);
        b_rotate_converged = are_parallel(vd, dvd, eps);
        if (b_rotate_converged) break;
        // Flip directions
        b_first_arg = !b_first_arg;
//        DS.Graphics.Refresh();
    } while (cnt++ < max_iter);
        
}

You can try this function out with some test vectors to see if indeed it rotates the view into the desired orientation.  One thing you will notice, however, is that the view sometimes is spun at a funny angle as we look down this axis.  So all we’ve done is ensured that the camera is pointing in a certain direction, but there still is one degree of freedom, so to speak, left unspecified with the camera.  That is, you could spin the camera any way you want around this view direction and still technically be looking down the view direction.  So, now how do we figure out what “spin” has been applied to the camera?  Moreover, what spin do we want applied?

The answer for the second question above is related to the coordinate system that will define our cut plane. Lets assume that we are wanting to look down the Y axis of the coordinate system which will define our cutting plane, and we want the Z axis of this coordinate system to be pointing straight up on the screen.  If we do this, when we draw a straight cut line horizontally across the middle of the screen, we will be defining a cut plane in the XY plane of the specified coordinate system.  That is exactly what we want.  In the code above we’ve got the part that will enable us to look down the Y axis, now we just need to figure out how to make the Z axis point straight up

In most computer graphics implementations, there is this notion of an “Up Vector” which is defined to be perpendicular to the line of sight and is used to determine “which way is up”.  That is, the up vector points in the positive Y axis on the screen.  If you look back up at the screen shot for the camera object displayed in the debugger, you will indeed see that there is an UpVector as a member of that object!  Yippee!!! Unfortunately, it says “Bad Variable Type”.  Bummer.  That’s code for “this variable exists but you can’t access it in the scripting language”.  We do have this variable, AngleRotationZ, that looks promising, but what is the reference for measuring this angle?  Also, we have a function called rotateZ(), which again looks promising, but how are we going to know how much to rotate?  We need to know which way is up, and how much to rotate around the camera’s Z axis so that we can orient our view such that when we draw a straight line across the middle of the screen, we’re cutting the model exactly on our desired plane. 

Remember that the first argument to the function camera.rotate(amountUp, amountRight) will rotate the view about the Up Vector.  Consider the image below:

rotate_z

Lets assume that we start with our current view direction vector, which is represented in the image above by the red vector.  Next, we perform a trial rotation of 15 degrees about the unknown up vector using the camera.rotate(15,0).  This moves our view direction vector the green vector shown above.  By taking the cross product of these two vectors, we can determine which way is “up” in the world coordinate system.  We then rotate the view back to where we started.  Now that we know what the current up vector is, we can take the dot product with the desired up vector, shown in purple above, and determine the angle about the camera Z axis we need to rotate so that up is truly up.  Not that this assumes that the desired up vector is perpendicular to the current view direction.  Consider the code below.

function rotate_up(desired_up_vector) {
    
    // Get the camera and graphics objects
    var camera = DS.Graphics.Camera;
    var graphics = DS.Graphics;
    var view_c = null;
    var trial = null;
    var current_up = null;
    var theta = null;
    var trial_ang = 15;
    var theta1;
    var theta2;
    var eps = 0.01;
    // Make sure our passed in value is normalized
    duv = normalize(desired_up_vector);

    // Which way are we currently looking now?
    vd = normalize([camera.ViewDirectionX,
                    camera.ViewDirectionY,
                    camera.ViewDirectionZ]);

    // Perform our trial rotation about the screen vertical axis
    camera.rotate(trial_ang, 0);
    trial = normalize([camera.ViewDirectionX,
                       camera.ViewDirectionY,
                       camera.ViewDirectionZ]);
    up = normalize(cross(trial, vd));
    // Rotate back so we don't lose our place
    camera.rotate(-trial_ang, 0);
    // How much to we need to rotate?
    theta1 = Math.acos(dot(duv, up)) * 180 / Math.PI;
    // Rotate assuming a positive rotation
    camera.rotateZ(theta1);
    DS.Graphics.Refresh();
    // Now we need to test to see if this is indeed the proper direction
    // Which way are we currently looking now?
    vd = normalize([camera.ViewDirectionX,
                    camera.ViewDirectionY,
                    camera.ViewDirectionZ]);
    // Perform our trial rotation about the screen vertical axis
    camera.rotate(trial_ang, 0);
    trial = normalize([camera.ViewDirectionX,
                       camera.ViewDirectionY,
                       camera.ViewDirectionZ]);
    up = normalize(cross(trial, vd));
    // Rotate back so we don't lose our place
    camera.rotate(-trial_ang, 0);
    // How much to we need to rotate?
    theta2 = Math.acos(dot(duv, up)) * 180 / Math.PI;
    if (theta2 > theta1) {
        // Rotate backwards if our second guess is larger than our first
        camera.rotateZ(-theta2);
//        DS.Graphics.Refresh();
    }
}

In this code we implement the algorithm sketched out above.  The only little trick is that we might end up rotating in the wrong direction.  We can determine this by peforming the same test again, and if we are still off then we can rotate back the other direction.

Now that we can look in a particular direction and we can deduce which was is up, all that is left is to pan the view around so that the location of our cut plane is right in the center of the screen.  This amounts to figuring out how much to pan in the up and right directions. 

If you play with the camera.pan() function you will notice that it appears to use screen coordinates as its arguments.  This makes sense from a GUI interface standpoint in that when the user pans the view, they typically do so with the mouse and therefore start and end screen coordinates of a mouse movement are the most natural inputs to a pan routine.  Unfortunately for us, that makes panning more of a challenge for us.  Nonetheless, lets approach the problem from an iterative viewpoint similar to what we did with the rotations. 

We’re going to attach the panning problem using a similar approach as we used for the rotate view function.  That is, we’re going to start with a trial pan in one of the directions, look to see what that does in world coordinates, then perform a real pan in that direction, or its direct opposite, to try to get closer to our target.  In a similar fashion to the rotate code, we project the pan direction vector in world coordinates onto a plane defined by our current view vector.  At each iteration we test the vector from our focus point to the desired point to see if it is parallel to the view vector.  If it is, then that means our desired point lies right down the axis of the view vector passing through the focus point.  Thus, it would be in the center of the screen.  The following code implements this technique.

function pan_view(desired_point) {
    // Get the camera and graphics objects
    var camera = DS.Graphics.Camera;
    var graphics = DS.Graphics;
    // Maximum amount to pan by
    var height = DS.Graphics.GfxWindow.height;
    var width = DS.Graphics.GfxWindow.width;
    // The current focus point
    var fp = null;
    // The trial focus point
    var fp_trial = null;
    // The trial amount to pan
    var trial_pan = (height + width) / 40;  //Unit is pixels...
    // View direction
    var view_v = null;
    // Vector from the focus point to the desired point
    var fp_to_dp = null;
    // Vector of pan direction
    var pan_dir = null;
    // The amount we are going to pan
    var pan_amount = null;
    // Tolerance for determining if the view vector is parallel
    // to the focus point to desired point vector
    var eps = 1e-4;
    // We'll flip back and forth between panning up and panning right
    var b_first_arg = true;
    // The current count of our iteration
    var cnt = 0;
    // The maximum number of iterations to perform
    var max_iter = 200;

    var look_at = desired_point;
    var max_pan = (height + width) / 20;
    do {
        fp = [camera.FocusPointX,
              camera.FocusPointY,
              camera.FocusPointZ];
        vd = normalize([camera.ViewDirectionX,
                        camera.ViewDirectionY,
                        camera.ViewDirectionZ]);

        if (b_first_arg == true) {
            camera.pan(trial_pan, 0);
        } else {
            camera.pan(0, trial_pan);
        }
        trial = [camera.FocusPointX,
                 camera.FocusPointY,
                 camera.FocusPointZ];
        // In world coordinates, see which direction this pan took us
        pd = sub(trial, fp);
        // Project this onto a plane defined by the current view 
        // vector
        pd_in_view = cross(vd, cross(pd, vd));

        // Move back to our original location
        if (b_first_arg == true) {
            camera.pan(-trial_pan, 0);
        } else {
            camera.pan(0, -trial_pan);
        }
        fp = [camera.FocusPointX,
              camera.FocusPointY,
              camera.FocusPointZ];
        // Take a vector from the current focus point to the
        // desired center point
        fp_to_dp = normalize(sub(look_at, fp));
        // Only try to pan the view if the vector between our focus
        // point and view vector are not parallel.
        if (Math.abs(1.0 - Math.abs(dot(fp_to_dp, vd))) > eps) {
            // Project this onto a plane defined by the view direction
            fp_to_dp_in_view = cross(vd, cross(fp_to_dp, vd));

            // Normalize the pan test vector so we can walk our way there
            pd_in_view = normalize(pd_in_view);

            // Figure out how much we need to pan

            pan_amount = trial_pan * dot(fp_to_dp_in_view, pd_in_view);
            // Knock it down by a factor associated with our iteration
            // scheme.  This helps prevent spurious jittering as we 
            // make our way there.
            var factor = cnt / max_iter;
            pan_amount *= (1.0 - factor * factor * factor);

            if (b_first_arg == true) {
                camera.pan(pan_amount, 0);
            } else {
                camera.pan(0, pan_amount);
            }
            b_pan_converged = false;
        } else {
            b_pan_converged = true;
        }
        // Flip the direction
        b_first_arg = !b_first_arg;
//        graphics.Refresh();
    } while (cnt++ < max_iter && !b_pan_converged);
}

If you’ve hung with me this far, you know that we’ve got a function that will rotate our view so that we’re looking down a particular axis.  We’ve also got a function that will rotate the view about our line of sight so that we can make sure a particular direction is up.  Finally, we’ve got a function that will pan the view around so that a particular point in space shows up right in the middle of the screen.  All of this was accomplished with the limited set of functions and properties we could deduce from the Camera object using the debugger.  We also demonstrated how to create a slice plane using the technique of “drawing” al line straight across the middle of the screen.  With these four functions we should have all we need conceptually to create a slice plane right where we want it, oriented exactly as we want it. 

Implementation

The following code implements these ideas and creates a little dialog box that is presented to the user.  I’ll cover the implementation of a dialog box in a separate post, however, you may be able to decode the secret sauce here anyway.   The user interface works as follows.  You pick a coordinate system from the drop down list for which you would like to create a slice plane.  You can then either create a slice plane from this coordinate system, or have the code automatically reorient the view so that you are looking down the Z axis of this coordinate system.   Below is a screen shot of the dialog box that is presented, and finally, a listing of the entire code for the script.

image

Listing of the Code

Here is a listing of the entire script.

// This string contains the HTML code for the dialog box
// we will present to the user
var html_text = 
'<html xmlns="http://www.w3.org/1999/xhtml" >\n'+
'<head>\n'+
'<title>Slice Plane Creation Tool</title>\n'+
'<style type="text/css">\n'+
'    body\n'+
'    {\n'+
'        background: buttonface;\n'+
'        font: messagebox;\n'+
'    }\n'+
'    table\n'+
'    {\n'+
'        border-collapse:collapse;\n'+
'        font: messagebox;\n'+
'    }\n'+
'    select\n'+
'    {\n'+
'        margin: 2px 8px 2px 4px;\n'+
'        width: 250px;    \n'+
'        height: 28px;\n'+
'    }\n'+
'    input\n'+
'    {\n'+
'        margin: 0px 4px 0px 0px;\n'+
'    }\n'+
'</style>\n'+
'<script type="text/javascript">\n'+
'    var wb, xfer;\n'+
'    function window_onload() {\n'+
'        xfer = window.external;\n'+
'        wb = xfer("wb");\n'+
'        var cs_group = xfer(\'csys_group\');\n'+
'        var cs_select = xfer(\'csys_select\');\n'+
'        if (cs_group != null) {\n'+
'            populate_coordinate_systems(cs_group, cs_select);\n'+
'        }\n'+
'    }\n'+
'    function window_onunload() {\n'+
'        xfer("CSYS") = parseInt(csys_select.value);\n'+
'        xfer("CreateSlicePlane") = create_plane.checked;\n'+
'        xfer("ViewPlane") = view_plane.checked;\n'+
'    }\n'+
'    function populate_coordinate_systems(cs_group, cs_select) {\n'+
'        csys_select.options.length=0;\n'+
'        for (var i = 1; i <= cs_group.Children.Count; i++) {\n'+
'            var csys = cs_group.Children.Item(i);\n'+
'            var csys_name = csys.Name;\n'+
'            var csys_id = csys.ID;\n'+
'            if (csys_id == cs_select) {\n'+
'                csys_select.options[i - 1] = new Option(csys_name, csys_id, true, true);\n'+
'            } else {\n'+
'                csys_select.options[i - 1] = new Option(csys_name, csys_id, false, false);\n'+
'            }\n'+
'        }\n'+
'    }\n'+
'</script>\n'+
'</head>\n'+
'<body onload="window_onload()" onunload="window_onunload()" scroll="no">\n'+
'<table>\n'+
'<tr>\n'+
'<td>\n'+
'<table>\n'+
'<tr>\n'+
'<td nowrap="true">Coordinate System: </td>\n'+
'<td><select id="csys_select"></select></td>\n'+
'</tr>\n'+
'</table>\n'+
'</td>\n'+
'</tr>\n'+
'<tr>\n'+
'<td>\n'+
'<input id="create_plane" type="checkbox" checked="checked" />Create Slice Plane\n'+
'</td>\n'+
'</tr>\n'+
'<tr>\n'+
'<td>\n'+
'<input id="view_plane" type="checkbox"  />Look at Plane\n'+
'</td>\n'+
'</tr>\n'+
'</table>\n'+
'</body>\n'+
'</html>\n';


var SC = DS.Script;

// Entry point for the whole script.
function main() {
 
    // Create the dialog
    var slice_plane_dialog =
        SC.CreateActiveXObject(
            SC.GenWBProgId('WBControls.WBHTMLDialog')
            );
    var dlg_OK = 1;
    var dlg_Cancel = 2;
    var dlg_VScroll = 4;
    var flags = dlg_OK | dlg_Cancel | dlg_VScroll;
    var caption = 'Slice Plane From Coordinate System';
    var width = 410;
    var height = 110;
    var b_modal = false;
    // Create the html code from a temporary file
    var fso = SC.fso;
    var tfolder = fso.GetSpecialFolder(2);
    var tname = fso.GetTempName();
    var tfile = tfolder.CreateTextFile(tname, true);
    tfile.WriteLine(html_text);
    tfile.Close();
    var path = fso.BuildPath(tfolder.Path, tname);
    var xfer =
        SC.CreateActiveXObject(
            SC.GenWBProgId('WBControls.DlgArgs')
            );

    xfer('wb') = WB;
    xfer('dlg') = slice_plane_dialog;
    var csys_group = get_coordinate_system_group();
    
    if(csys_group == null) {
        SC.WBScript.Out('Cannot find the coordinate system group in the tree.' +
                        ' Please create a coordinate system first', true);
        return;
    }
    // Pass over to the dialog the coordinate system group.
    xfer('csys_group') = csys_group;
    var active_obj = DS.Tree.FirstActiveObject;
    if (active_obj.Class == SC.id_CoordinateSystem) {
        xfer('csys_select') = active_obj.ID;
    } else {
        xfer('csys_select') = -1;
    }
    // Show the dialog
    var ret = slice_plane_dialog.DoDialog(WB.hWnd,
                                          path,
                                          xfer,
                                          b_modal,
                                          width,
                                          height,
                                          flags, 
                                          caption);
    if (ret == 1) {
        // We get here if the user presses OK on the dialog, 
        // so lets grab the user's choices back from the selection.
        var selected_csys = xfer('CSYS');
        var create_plane = xfer('CreateSlicePlane');
        var look_at= xfer('ViewPlane');
        // Do the work here
        var origin = get_coordinate_system_origin(selected_csys);
        var z_axis = get_coordinate_system_z_axis(selected_csys);
        var y_axis = get_coordinate_system_y_axis(selected_csys);
        // Bail out if we can't create the vectors for the coordinate system
        if (origin == null || z_axis == null || y_axis == null) {
            SC.WBScript.Out('Cannot determine the coordinate system vectors.' + 
                            ' Unable to create slice plane', true);
            return;
        }
        if (create_plane == true) {
            create_slice_plane(origin, z_axis, y_axis);
        }
        if (look_at == true) {
            look_at_plane(origin, z_axis, y_axis);
        }
    }

    // Delete the html file to clean up
    fso.DeleteFile(path);
}

// Very simple utility function that is used to write out an html
// file to another file in javascript string format.  This is useful
// for pasting into a source file to package the html file into
// a single source script for distribution purposes.
function write_html(path) {
    var fso = SC.fso;
    var input = fso.OpenTextFile(path, 1, false);
    var output = fso.CreateTextFile(path + '.out', true);

    while (!input.AtEndOfStream) {
        var line = input.ReadLine();
        output.WriteLine('\'' + line + '\\' + 'n\'+');

    }
    output.WriteLine('\'\';');
    output.Close();
    input.Close();
}    

// Return the origin of a given coordinate system
function get_coordinate_system_origin(csys_id) {
    var csys = get_coordinate_system(csys_id);
    if (csys == null) {
        return null;
    }
    return [csys.OriginXLocation, 
            csys.OriginYLocation,
            csys.OriginZLocation];
}

// Return a vector oriented along the z axis of a given
// coordinate system
function get_coordinate_system_z_axis(csys_id) {
    var csys = get_coordinate_system(csys_id);
    if (csys == null) {
        return null;
    }
    return normalize([csys.ZDirectionXValue,
                      csys.ZDirectionYValue,
                      csys.ZDirectionZValue]);
}

// Return a vector oriented along the y axis of a given
// coordinate system
function get_coordinate_system_y_axis(csys_id) {
    var csys = get_coordinate_system(csys_id);
    if (csys == null) {
        return null;
    }
    return normalize([csys.YDirectionXValue,
                      csys.YDirectionYValue,
                      csys.YDirectionZValue]);
}

// Return an actual coordinate system object given
// a tree ID.
function get_coordinate_system(csys_id) {
    var group = get_coordinate_system_group();
    if (group == null) {
        return null;
    }
    for (var i = 1; i <= group.Children.Count; i++) {
        var child = group.Children.Item(i);
        if (child.ID == csys_id) {
            return child;
        }
    }
    return null;
}

// Return the coordinate system group in the tree.
function get_coordinate_system_group() {
    var branch = SC.getActiveBranch();
    if (branch == null) {
        return null;
    }
    return branch.CoordinateSystemGroup;
}

// Create a slice plane given a point, plane normal and up
// vector.  This function uses the orientation functions
// below
function create_slice_plane(point, plane_normal, inplane_up) {
    // Get a handle to the slicetool
    var slice_tool = DS.Graphics.SliceTool;

    // Orient the view using our view orientation functions
    for (var i = 0; i < 3; i++) {
        rotate_view(inplane_up);
        pan_view(point);
        rotate_up(plane_normal);
        DS.Graphics.Refresh();
    }

    // Get the window's dimensions
    var height = DS.Graphics.GfxWindow.height;
    var width = DS.Graphics.GfxWindow.width;

    // Create the slice plane
    var slice_plane =
        slice_tool.SetPlane(0, height / 2, width, height / 2);
    DS.Graphics.Refresh();
}

// Look at a particular point along an axis.  This is useful
// for looking at a slice plane after it has been created.
function look_at_plane(point, normal, up) {
    // Orient the view using our view orientation functions

    // Orient the view using our view orientation functions
    for (var i = 0; i < 3; i++) {
        rotate_view(normal);
        pan_view(point);
        rotate_up(up);
        DS.Graphics.Refresh();
    }

}

// Calculate the dot product of two vectors
function dot(va, vb) {
    return va[0] * vb[0] + va[1] * vb[1] + va[2] * vb[2];
}

// Calculate the length of a vector
function norm(va) {
    return Math.sqrt(va[0] * va[0] + va[1] * va[1] + va[2] * va[2]);
}

// Normalize a vector
function normalize(va) {
    var len = norm(va);
    if (len != 0.0) {
        return [va[0] / len, va[1] / len, va[2] / len];
    }
}

// Calculate the cross product of two vectors
function cross(va, vb) {
    return [va[1] * vb[2] - va[2] * vb[1],
            va[2] * vb[0] - va[0] * vb[2], 
            va[0] * vb[1] - va[1] * vb[0]];
}

// Scale a vector by a scalar
function scale(va, sc) {
    return [va[0] * sc, va[1] * sc, va[2] * sc];
}

// Add two vectors
function add(va, vb) {
    return [va[0] + vb[0],
            va[1] + vb[1],
            va[2] + vb[2]];
}

// Subtract vector b from vector a
function sub(va, vb) {
    return [va[0] - vb[0],
            va[1] - vb[1],
            va[2] - vb[2]];
}

function are_parallel(va, vb, tol) {
    return 1.0 - (Math.abs(dot(va, vb)) / (norm(va) * norm(vb))) < tol ? true : false;
}

function are_perpendicular(va, vb, tol) {
    return (Math.abs(dot(va, vb)) / (norm(va) * norm(vb))) < tol ? true : false;
}

// Rotate the view around so that we are looking in the 
// direction of the desired view
function rotate_view(desired_view) {
    // Get the camera and graphics objects
    var camera = DS.Graphics.Camera;
    var graphics = DS.Graphics;
    // This is our tolerance for being parallel to the desired view
    var eps = 1e-7;
    // This is the maximum number of iterations we'll try.  
    // While loops with a tolerance check only scare me...
    var max_iter = 200;
    // Get the current view
    var view_c = normalize([camera.ViewDirectionX,
                            camera.ViewDirectionY,
                            camera.ViewDirectionZ]);
    // Make sure we're normalized
    dvd = normalize(desired_view);
    // This should be close to 1 if parallel.

    var cnt = 1;
    var b_first_arg = true;
    var normal = null;
    var trial = null;
    var view_p = null;
    var trial_ang = 15;
    var applied_rotation = null;
    var previous_up = 0;
    var previous_right = 0;
    var right_factor = up_factor = -1.0;
    // Loop until we're parallel, or we give up trying
    do {
        var factor = cnt / max_iter;
        factor *= factor;
        factor *= factor;
        // Get the view direction
        vd = normalize([camera.ViewDirectionX,
                        camera.ViewDirectionY,
                        camera.ViewDirectionZ]);
        // Get the current up vector
        camera.rotate(trial_ang, 0);
        trial = normalize([camera.ViewDirectionX,
                       camera.ViewDirectionY,
                       camera.ViewDirectionZ]);
        current_up = normalize(cross(trial, vd));
        // Rotate back so we don't lose our place
        camera.rotate(-trial_ang, 0);
        

        vd = normalize([camera.ViewDirectionX,
                        camera.ViewDirectionY,
                        camera.ViewDirectionZ]);

        if (b_first_arg) {
            camera.rotate(trial_ang, 0);
        } else {
            camera.rotate(0, trial_ang);
        }
        trial = normalize([camera.ViewDirectionX,
                           camera.ViewDirectionY,
                           camera.ViewDirectionZ]);
        n = normalize(cross(vd, trial));
        // Rotate back
        if (b_first_arg) {
            camera.rotate(-trial_ang, 0);
        } else {
            camera.rotate(0, -trial_ang);
        }
        vd = normalize([camera.ViewDirectionX,
                        camera.ViewDirectionY,
                        camera.ViewDirectionZ]);
        // Only do this rotation if our desired view vector
        // is not nearly parallel to our current axis of rotation
        // If it is, then the code inside the if statement will
        // be skipped.
        if (!are_parallel(dvd, n, eps)) {
            // Project the desired view vector onto our plane of rotation
            vp = normalize(cross(n, normalize(cross(dvd, n))));
            // Calculate the angle between the projected vector
            // and our current view direction
            dot_product = dot(vp, vd);
            needed_rotation = Math.acos(dot(vp, vd)) * 180 / Math.PI
            
            // Knock it down by a factor associated with our iteration
            // scheme.  This helps prevent spurious jittering as we 
            // make our way there.
            needed_rotation *= (1.0 - factor);
            if (b_first_arg) {
                // If we start to diverge, try rotating back the other way
                if(previous_up < needed_rotation) {
                    up_factor = (up_factor < 0.0) ? 1.0 : -1.0;
                    previous_up = needed_rotation;
                }
                camera.rotate(up_factor * needed_rotation, 0.0);
                
                
            } else {
                if (previous_right < needed_rotation) {
                    right_factor = (right_factor < 0.0) ? 1.0 : -1.0;
                    previous_right = needed_rotation;
                }
                camera.rotate(0.0, right_factor * needed_rotation);
            }
        }
        // See if we are there yet
        vd = normalize([camera.ViewDirectionX,
                        camera.ViewDirectionY,
                        camera.ViewDirectionZ]);
        b_rotate_converged = are_parallel(vd, dvd, eps);
        if (b_rotate_converged) break;
        // Flip directions
        b_first_arg = !b_first_arg;
//        DS.Graphics.Refresh();
    } while (cnt++ < max_iter);
        
}

function rotate_up(desired_up_vector) {
    
    // Get the camera and graphics objects
    var camera = DS.Graphics.Camera;
    var graphics = DS.Graphics;
    var view_c = null;
    var trial = null;
    var current_up = null;
    var theta = null;
    var trial_ang = 15;
    var theta1;
    var theta2;
    var eps = 0.01;
    // Make sure our passed in value is normalized
    duv = normalize(desired_up_vector);

    // Which way are we currently looking now?
    vd = normalize([camera.ViewDirectionX,
                    camera.ViewDirectionY,
                    camera.ViewDirectionZ]);

    // Perform our trial rotation about the screen vertical axis
    camera.rotate(trial_ang, 0);
    trial = normalize([camera.ViewDirectionX,
                       camera.ViewDirectionY,
                       camera.ViewDirectionZ]);
    up = normalize(cross(trial, vd));
    // Rotate back so we don't lose our place
    camera.rotate(-trial_ang, 0);
    // How much to we need to rotate?
    theta1 = Math.acos(dot(duv, up)) * 180 / Math.PI;
    // Rotate assuming a positive rotation
    camera.rotateZ(theta1);
    DS.Graphics.Refresh();
    // Now we need to test to see if this is indeed the proper direction
    // Which way are we currently looking now?
    vd = normalize([camera.ViewDirectionX,
                    camera.ViewDirectionY,
                    camera.ViewDirectionZ]);
    // Perform our trial rotation about the screen vertical axis
    camera.rotate(trial_ang, 0);
    trial = normalize([camera.ViewDirectionX,
                       camera.ViewDirectionY,
                       camera.ViewDirectionZ]);
    up = normalize(cross(trial, vd));
    // Rotate back so we don't lose our place
    camera.rotate(-trial_ang, 0);
    // How much to we need to rotate?
    theta2 = Math.acos(dot(duv, up)) * 180 / Math.PI;
    if (theta2 > theta1) {
        // Rotate backwards if our second guess is larger than our first
        camera.rotateZ(-theta2);
//        DS.Graphics.Refresh();
    }
}

function pan_view(desired_point) {
    // Get the camera and graphics objects
    var camera = DS.Graphics.Camera;
    var graphics = DS.Graphics;
    // Maximum amount to pan by
    var height = DS.Graphics.GfxWindow.height;
    var width = DS.Graphics.GfxWindow.width;
    // The current focus point
    var fp = null;
    // The trial focus point
    var fp_trial = null;
    // The trial amount to pan
    var trial_pan = (height + width) / 40;  //Unit is pixels...
    // View direction
    var view_v = null;
    // Vector from the focus point to the desired point
    var fp_to_dp = null;
    // Vector of pan direction
    var pan_dir = null;
    // The amount we are going to pan
    var pan_amount = null;
    // Tolerance for determining if the view vector is parallel
    // to the focus point to desired point vector
    var eps = 1e-4;
    // We'll flip back and forth between panning up and panning right
    var b_first_arg = true;
    // The current count of our iteration
    var cnt = 0;
    // The maximum number of iterations to perform
    var max_iter = 200;

    var look_at = desired_point;
    var max_pan = (height + width) / 20;
    do {
        fp = [camera.FocusPointX,
              camera.FocusPointY,
              camera.FocusPointZ];
        vd = normalize([camera.ViewDirectionX,
                        camera.ViewDirectionY,
                        camera.ViewDirectionZ]);

        if (b_first_arg == true) {
            camera.pan(trial_pan, 0);
        } else {
            camera.pan(0, trial_pan);
        }
        trial = [camera.FocusPointX,
                 camera.FocusPointY,
                 camera.FocusPointZ];
        // In world coordinates, see which direction this pan took us
        pd = sub(trial, fp);
        // Project this onto a plane defined by the current view 
        // vector
        pd_in_view = cross(vd, cross(pd, vd));

        // Move back to our original location
        if (b_first_arg == true) {
            camera.pan(-trial_pan, 0);
        } else {
            camera.pan(0, -trial_pan);
        }
        fp = [camera.FocusPointX,
              camera.FocusPointY,
              camera.FocusPointZ];
        // Take a vector from the current focus point to the
        // desired center point
        fp_to_dp = normalize(sub(look_at, fp));
        // Only try to pan the view if the vector between our focus
        // point and view vector are not parallel.
        if (Math.abs(1.0 - Math.abs(dot(fp_to_dp, vd))) > eps) {
            // Project this onto a plane defined by the view direction
            fp_to_dp_in_view = cross(vd, cross(fp_to_dp, vd));

            // Normalize the pan test vector so we can walk our way there
            pd_in_view = normalize(pd_in_view);

            // Figure out how much we need to pan

            pan_amount = trial_pan * dot(fp_to_dp_in_view, pd_in_view);
            // Knock it down by a factor associated with our iteration
            // scheme.  This helps prevent spurious jittering as we 
            // make our way there.
            var factor = cnt / max_iter;
            pan_amount *= (1.0 - factor * factor * factor);

            if (b_first_arg == true) {
                camera.pan(pan_amount, 0);
            } else {
                camera.pan(0, pan_amount);
            }
            b_pan_converged = false;
        } else {
            b_pan_converged = true;
        }
        // Flip the direction
        b_first_arg = !b_first_arg;
//        graphics.Refresh();
    } while (cnt++ < max_iter && !b_pan_converged);
}

main();

ANSYS 13.0 Enhanced Modal Analyses with Linear Perturbation

“Rest, rest, perturbed spirit!”  – William Shakespeare

If you have ever performed a large deflection prestressed modal analysis in ANSYS Mechanical APDL prior to version 13.0, your spirit might have been perturbed as well.  The procedure was not very user friendly, to sum it up.  For example, unless you were careful, the modal results would over-write the static preload results.  Thankfully, at 13.0 we have a smoother and more capable tool for handling large deflection prestressed modal analyses.  This new procedure is called Linear Perturbation.

We’ll focus on modal analyses in this article, but be aware that linear perturbation also applies to linear buckling analyses at 13.0, but only following a linear preload solution, and only in Workbench.  The capability for modal analyses is supported in both Workbench and Mechanical APDL.  Also, the preload, or ‘base’ analysis has to have multiframe restart capability turned on.  This will happen by default for a nonlinear analysis but needs to be manually activated in MAPDL for a linear analysis by issuing the command RESCONTROL,LINEAR.

In fairly simple terms, the prestress effects are included in a modal analysis via the change in the stiffness matrix that occurs during the prestress (typically nonlinear static) analysis.  This is the method that has been used in ANSYS for years.  What’s new at 13.0 is that the program keeps track of different components of the augmented tangent stiffness matrix.  The five possible contributing components are material property effects, stress stiffening effects, load stiffening effects, contact element effects, and spin softening effects.

While the material effects must remain linear, the contact stiffness can be altered, if desired, in the subsequent modal analysis.  More on that later.

The typical Mechanical APDL procedure to perform a nonlinear static structural prestress run followed by a modal analysis which utilizes those prestress effects is as follows:

! With static model prepared

/solu                            ! enter solution module
antype,0                         ! specify static analysis type
nlgeom,1                         ! turn on large deflection effects (nonlinear)
pstres,on                        ! turn on prestress effects for subsequent modal
nsub,10,10,10                    ! specify substep range
save                             ! save the database
solve                            ! solve the nonlinear static prestress case
finish                           ! leave the solution module
/solu                            ! re-enter solution so we can do a new analysis
antype,,restart,1,10,perturb     ! specify restart option for linear perturbation
! from last substep in this case
perturb,modal                    ! specify modal as next analysis
solve,elform                     ! calculate element formulation with solve command
modopt,lanb,12                   ! specify modal options for solution
mxpand,12                        ! specify number of modes for results calc
solve                            ! solve the prestress modal analysis
/post1                           ! enter general postprocessor
INRES,ALL                        ! make sure we read in all results from file
FILE,’nonlinear_static’,’rstp’   ! specify special results file for modal results
set,first                        ! read in results for first mode
plns,u,sum                       ! plot mode shape
set,next                         ! read in results for next mode
/repl                            ! plot mode shape, etc.

 

Note that the linear perturbation (modal) analysis has its own results file, the .rstp file.  Because of this, the preload results are still available in their own .rst file as it does not get overwritten by the modal step.

Here is a table of frequency results for a simple test case.  Three modal analyses were run:

1.  No prestress at all.

2.  With a linear static prestress state.

3.  With a nonlinear static prestress state.

image

Here is the model used for these runs in its initial configuration.  The block at the base was fixed in all DOF’s and the preload applied was a pressure load on one side of the vane.

undeformed

Here is the model with the deformed mesh due to the nonlinear prestress:

nonlin_prestress_deformed

Here is a mode shape plot for mode 12:

nonlin_prestress_mode12

The above example is all well and good but could have been done in prior versions of ANSYS using the old partial solve method.  What’s nice about the newer linear perturbation method is that it’s easy to get the mode shape plots relative to the deformed mesh from the prior prestress run, and you don’t need to worry about over-writing the prestress results with the modal results, since the corresponding results files are different.

Further, we can now perform modal analyses using different restart points in the static prestress run, assuming multiple restart points are available.

Finally, we can actually change some contact options between the static prestress solution and the modal solution.  For example, if the prestress analysis was run using frictional contact, the subsequent modal analysis can be run utilizing the prestressed state of the structure but with one of three contact states for the modal analysis: true status (that of the prior static analysis), force (to be) sticking, or  force (to be) bonded. The sticking option applies only to contact elements with a nonzero coefficient of friction. The bonded option will force contact pairs that are in contact in the static analysis to be bonded in the modal analysis.

The Mechanical APDL command sequence for this procedure would be something like this:

 

! first perform nonlinear static prestress run, then

/solu

antype,,restart,,,perturbation

perturb,modal,,BONDED,PARDELE                    ! pre-stress modal analysis, switch contact to bonded, delete

                                                                                              ! loads in case future MSUP

solve                                                                ! Generate matrices needed for perturbation analysis

! Next perform modal analysis

modopt,lanb,6

mxpand                    ! default expand in case of complex solution

solve

! modal results are now available for postprocessing

In Workbench Mechanical, the appropriate command sequence is sent to the solver when we link a modal analysis to a prior prestress analysis.  If the model involves contact, then in the modal analysis we’ll have choices for how the contact should be treated in the Pre-Stress branch under the Modal branch in the Outline Tree.  For frictional contact in the static prestress analysis, the choices in the Details view for the Pre-Stress branch in the modal analysis will be Use True Stress, Force Sticking, or Force Bonded as described above.

Here are some example plots for this scenario:

Two 3D plates subject to in plane bending, fixed at right ends, frictional contact between them.

image

Resulting contact status for static run (sliding is occurring)

image

Resulting static deformation:

def_c

 

Mode 6 result, “true” contact behavior:

mode6_nonlin_true_c.

 

Mode 6 result, “force bonded” contact behavior:

mode6_nonlin_force_bonded_c

Those last two images show a dramatic difference in modal results simply by changing the contact status behavior in the modal analysis.  In the first of those images, the contact status is set to ‘true’, meaning essentially the same as in the prestress analysis, subject to the linear nature of the modal analysis.  In this example, the frictional behavior in the static prestress run becomes no separation in the modal analysis, so the two plates can have mode shapes in which the plates slide relative to each other.  In the last plot, the contact status has been changed to ‘force bonded’ for the modal solution.  As the plot shows, mode shapes can only exist in which the two plates are bonded together.  Both modal analyses have the same prestress condition however.

Here is a frequency table comparing the first six modes of the two modal analyses.  Note that with the contact forced to be bonded we get a stiffening response as we might expect.

WB_freq_compare

So, although on the surface it might initially appear to be a black art, linear perturbation is a nice enhancement in ANSYS 13.0 that gives us a more robust and capable method for performing modal analyses with prestress effects included.  The prestress run is typically a linear or nonlinear static analysis, but it will also work with a full transient analysis to define the prestress state.  The ANSYS 13.0 Help has more information (see section 9.2 of the Mechanical APDL Structural Analysis Guide and section 17.8 of the Theory Reference).  We also recommend you try out the procedure on your own models.

Making Pretty Plots in ANSYS Mechanical and Mechanical APDL

imageI was looking for some new images for our web page and all of the plots I had were pretty much screen grabs.  Not the high quality I was looking for.  And putting a picture of Angelina behind a mode plot probably won’t go over too well, or will go over too well and will distract everyone.  So I brought up the original models and started playing around trying to get a better quality image, and did pretty well.  And, needing an article for this week, I thought I’d share what I found. If you don’t use MAPDL, skip on down to the ANSYS Mechanical bit.

ANSYS Mechanical APDL

For my MAPDL plots I actually referred back to an old “The Focus” article I did in 2002. Yes, 2002.  You can go back to that article for the details but here are the important bits below:

  • Invest in a good image editing tool. If you are ultra-cheap, use the open source GIMP.  But Adobe Photoshop and Corel Paint Shop Pro are worth the investment.  With Paint Shop Pro having the price/performance advantage if you don’t already have Photo Shop
  • You don’t want to do screen grabs.  MAPDL has great plotting to file abilities and you want to use those.
  • Use the /show command to output high quality graphics and skip using the pixels on your screen. You have two great options:
    • Use /SHOW,PNG to tell MAPDL to write to a PNG file when you issue plot commands
      •   PNG is the best choice for smaller files and high resolution.  JPG may be more common but you will find that the images can be fuzzy, especially around edges.  Go PNG.

image

  • If you are really hard-core and you want an exact representation of you model that can be scaled, then do a /show,PSCR,,,8.  This makes an encapsulated postscript file.
    • If you don’t know what that is, we advice you don’t bother.
    • But if you do, pop it into Illustrator, group it and now you have a vector graphic you can scale, trim, edit and really do cool things with.
  • Get to know how to use the /ERASE, /NOERASE button for making multiple plots on a page.  You can use /win with the standard view commands to show results from different modes, different load steps, or if you get real clever, even different models.   Here is an example for plotting 4 different mode shapes:

! Plots first 4 modes
/erase
/win,1,ltop $/win,2,rtop
/win,3,lbot $/win,4,rbot
/view,all,1,1,1,1
/vup,all,Z
/angle,all,0
/dist,all,1.3
/focus,all,.2,.08,.05
/win,2,off $/win,3,off $/win,4,off
/plopt,info,0
/annot,delete
/TSPEC, 15,0.700,1,0,0
*dim,anx,,4
*dim,any,,4
*dim,frq,,4
anx(1) = -.98, .4, -.98, .4
any(1) = .01, .01, -.92,-.92
*do,i,1,4
set,1,I
*get,frq(i),mode,i,freq
plnsol,u,sum
/win,i,off
/win,i+1,on
/noerase
*enddo
/tlab,anx(1),any(1),Mode 1: %frq(1)% Hz/t
/tlab,anx(2),any(2),Mode 2: %frq(2)% Hz/t
/tlab,anx(3),any(3),Mode 3: %frq(3)% Hz/t
/tlab,anx(4),any(4),Mode 4: %frq(4)% Hz/t
/replot
/erase
anx=
any= 
image
  • Get rid of that black or blue-blue background by changing your screen to reverse video.
    • Through the GUI: PlotCtrls->Style->Colors->Reverse Video
    • Or put the following in a macro:
      /RGB,INDEX,100,100,100, 0
      /RGB,INDEX, 80, 80, 80,13
      /RGB,INDEX, 60, 60, 60,14
      /RGB,INDEX, 0, 0, 0,15

image

  • Use /TYPE to turn on slower, but higher quality hidden line removal
    • Default is z-buffer, which is fast but not a accurate
    • Precise is the best: /TYPE,,4 for hidden
  • Get smoother curved surfaces by increasing the number of facets on your polygons with /EFACET
    • /GRAPH,POWER is needed for it to work (default)
    • /EFACET,4 puts 16 (4/edge) faces on every element face.

image

  • Getting lots of pixels is the most important.  Just make a big file with lots of pixels by using /GFILE, 2400.
    • This gives you a file that is 2400 pixels tall
    •   You can then scale it down when you want to use it and get nice clean images.

There is a lot more you can do, like annotation, backgrounds, changing fonts, etc… But this covers the basics that everyone should know.  Take a look at the original article and help on the following command for more: /PLOPT, /EDGE, /TRIAD, /LIGHT, /DEV,FONT, /TEXTRE, /GFORMAT, /ANNOT.

ANSYS Mechanical

Now things get a little different. In that MAPDL is old, older than some of you reading this article, it has some wicked cool tools that were put in there from the days when many of us didn’t even have a color monitor.  Now the world is online and everything is about an 200pixel wide JPEG file to be uploaded onto Facebook, you don’t have as many options to get high quality images out of the Workbench interface.  But the good news is that the default image, is better than most of the high quality ones you have to work for to get out of MAPDL.

The Basics for ANSYS Mechanical

So, for a lot of cases, just doing a screen grab of your graphics window in ANSYS Mechanical is good enough. But if you want nicer plots, here are some suggestions:

  • If you are on Windows Vista or 7, then you need to turn of Windows Aero.  This is the transparent fancy-pants options for how windows are displayed. But the screen capture used in ANSYS Mechanical doesn’t work if it is turned on.  So if you are going to be doing Images or Animations, you need to turn it off.
  • Beyond the basic screen grab you should become familiar with the options under the little “New Figure or Image” Icon on the main icon bar

image

  • If you choose Figure or Image it adds a Figure or Image object under whatever object you have selected.
    • A figure is a 3D image that updates every time you click on it.  It stores the view settings when it is made, but you can rotate and zoom at any time.  It is a 3D picture
    • An image is a screen capture that is stored in the tree.  You can also import an image from your hard drive.
  • The easiest way to get a nice plot is to get what you want on the screen and use “Image to File”.
    • By default this will make a PNG file that is the resolution of the image on your screen.
  • For a higher quality image, make your graphics window bigger.  Remember, all ANSYS is doing is grabbing your screen and saving it to a file.
  • The default background is a bit annoying for plots, especially if you want just your model.  To turn it off go back to your project page and use Tools->Options->Appearance and set Background Style to Solid and Background color to white.
    • If you want to go old school and have white text and black background, you can set that here as well.

bg1bg2bg3

  • If you want to make an image that can float in your PowerPoint (no background, transparent) then I like to turn off all the decorations.  Do this by making sure your background is solid (white is usually good enough) and then go to View in the menu and uncheck Ruler, Legend, and Triad.
    • Once in PowerPoint, use the “Set Transparent Color” feature to set the white bits to clear.
    • Or, use you fancy image editing software to remove the background. (and put a cheesy one in if you want, as shown here:)
      image
  • Play with the options to get the image you want.  The contour style, if you want your elements shown or not, etc…
  • Another thing to know is how to change the default format for saving the images.  The default is PNG, which we recommend.  But if you need JPEG or BMP you can change it in Mechanical under  Tools>Options>Mechanical>Miscellaneous>Image>Image Transfer Type.

Getting Better Resolution on a Single Plot in ANSYS Mechanical

The above will work for most plots, but what to do if the image you get from screen grabs is just not good enough?  Maybe you are on a laptop and you can’t get the resolution you need. I especially find this true when I want to view my mesh.  This is the default I get with mesh on:

sc1

By using the Print Preview tab, I can get a very high resolution image:

  • Get the view you want in the graphics window.
  • Click on the Print Preview tab at the bottom of the graphics window
  • You should see what you saw on the screen.  Don’t panic.
  • On the Print Preview tool bar click on the little picture and choose High Resolution
  • It should update with a very nice high-rez image.
  • This is an HTML page, so you can right click on the image and copy it, or save it to disk.

preview

Getting Better Resolution on a Multiple Plots in ANSYS Mechanical

The above will work for a single image but can be a pain if you want a bunch.  For that we recommend that you leverage the Report Writer capability, even if you don’t want the report.  The Report Writer makes an HTML report of everything in your Model tree.  So if you put Figures in your tree, it will make those for you when you do a Report Preview, all in one nice little directory.  Here is how to use it:

  • For every plot you want, click on the object you want the plot of and then sue the “New Figure or Image” drop down to insert figures for every view you want.
    • Don’t use Images, remember those are just screen grabs and won’t give you better resolution.
  • When you are done, click on the “Report Preview” tab under the Graphics Window.
  • ANSYS Mechanical will go of and think for a while, then it will show you your HTML report.
  • You can cut and paste from the report if you don’t have a lot of images.
  • If you do have a lot of images, you will want to click on the Publish button
    • This brings up a dialog.  Don’t use the default Single file format, change it to “Page, Figures in Subfolder (*.html)” and pick a directory
    • they will be sitting in the Project_Files directory.
  • The default images are 600 pixels wide, which may be OK.  But if not, then you need to go into Options and tell ANSYS to make bigger, better images.
  • Go to Tools->Options->Mechanical->Report->Figure Dimensions
    • Set the Graphics Height and Width higher.  I like 1800 x 1500.  That is three time more than the default
    • You can get even more resolution by changing the Graphics Resolution to 2:1 or 4:1.  This makes the pictures 2 or 4 times larger.  But that can take a while…
  • Now go back and redo the Report Preview and you should see much larger images, and much higher quality.
  • To save to a folder, publish them.
  • Click on the images below to see them in their full resolution:

Figure0008
Default 600px wide image

Figure0008
1800px wide image

Figure0008
3600px wide image

Conclusion

And those are the high points.  Like everything else, the help is your friend and has much more detailed information.  And don’t be afraid to just try stuff out.  Explore the options and settings, make lots of images.  That worst thing that can happen is that you will create some files you need to delete.

We hope this will help you make pretty pictures for your next report so that project guy won’t grill you on stuff he really doesn’t understand…

To 40 Gb/s Infiniband QDR and Beyond! HOW-TO

In this article I will show you how to install and configure two 64-bit Linux servers with a 40Gb/s Infiniband interconnect. The two servers are connected directly together with a 7mm QDR Cable. No switch is used in this configuration or setup document.

The Inifinband cards used for the How-To have the Mellanox® ConnectX®-2 IB QDR chip MT26428. They are a dual Infiniband QSFP port with a 40Gb/s data rate per port. The cards use a PCI-E 2.0 x8 (5GT/s) slot in a UIO low-profile half-length form factor designed by SuperMicro.

Step 1: Install OpenFabric software – CentOS

  • Select OpenFabrics Enterprise Distribution

f1

  • Next, install the openib and mstflint
    • Select Optional Packages

f2

      • Select openib
      • Select mstflint
      • Click Close
    • Click Apply
    • Allow download and install to complete

Step 2: Verify that you have the most recent firmware for the Infiniband cards.

  • Go to the manufacture website and locate the firmware update bin file for your Infiniband card.
    • While you are at the site you also may want to download the latest software drivers for your specific Infiniband card.
    •  www.mellanox.com – Mellanox Technologies
  • Next, update the firmware for our Mellanox Technologies MT26428 Infiniband card
  • Open a Terminal session and type the following commands: your output will look similar to the output listed below. lspci

f3

Please note: the device id of your Infiniband card. In the above screen shot you will see that the pci device id for the card is 41:00.0

 

  • Next, begin the firmware update:
    • Make sure that your terminal window session is in your firmware update location. I saved the update.bin file on the Desktop of the root user.
    • Next type: mstflint –d 41:00.0 -i AOC.bin b
      • Checkout the screen shot below, amake sure within your file name there are no are in the file name of the update.bin file. If in the below command line entry the bin file read AOC-1.bin the firmware update would fail.

f4

  • After successful firmware update, restart the server and move on to Step 3.

Step 3: Configuring the network-scripts on Linux

  • In a terminal window change your path location to /etc/sysconfig/network-scripts/
  • Type vi ifcfg-ib0 to enter into text editor mode.

f5

  • Next, enter in the following text for the Infiniband card.

DEVICE=ib0
ONBOOT=yes
IPADDR=192.168.0.x
NETMASK=255.255.255.0
TYPE=Ethernet

PLEASE NOTE: Pertaining to the IPADDR entry above, a good rule of thumb rule that I use is to add an additional digit to the first IP number of your server. For example, if your server IP is 10.0.0.100 I would make the Infiniband IP address for IB port 0: 11.0.0.100 as well as using the correct subnetmask for your IP address range.

  • Finally, save the ifcfg-ib0 file by performing the following keystroke commands
    • Press shift and then :
    • Next type wq
      • Here is link to a blog article that I wrote listing several vi commands that I use the most. (You will need to scroll down they are under Step 2 )

http://www.padtinc.com/blog/post/2011/01/07/IT-Tips-Setting-Up-FreeNX-for-Remote-Access-to-Linux-Machines-from-Windows.aspx

Step 4: Verify that the following Infiniband services startup on reboot:

  • Open Service configuration manager off of the file menu click >System >Administration >Services
    • Select start on boot
  • Select opensm & openib on MASTER node
    • Please Note: Only one subnet manager needs to be running on any given Infiniband interconnect network
  • openib on each additional server within the Infiniband interconnect network

f6

  • Select the check box. Click Start and click Save
  • Restart servers

Done – The installation and configuration of the Infiniband card is now completed.

Important Linux Infiniband commands that I used during the installations

  • lspci – Lists all of the device ids
  • Ibv_devinfo – checks that the IB driver is running on all nodes and shows port status
  • Sminfo – check to see if the subnet manager is running.
  • Ibchecknet – checks the network connectivity status
  • Ibdiagnet – performs a set of tests on the IB network
  • Ibhosts – simple discover IB hosts
  • Ibstat – checks state of local IB port
  • Ibnodes – discovery of nodes

ANSYS Mechanical Scripting: HOWTO Part 4

Where we left Off Last Time…

In the third part of this series, I left you with an introduction to the ANSYS Selection Manager and Geometry B-Rep.  We also created a sample script that iterated over all of the faces in a selected body and created a named selection for each face.   We built on our technique described in Part 2 for discovering ANSYS functionality. 

Introducing the Microsoft Visual Studio Script Debugger

In this post I want to introduce you to the Visual Studio Script Debugger.  Running ANSYS Mechanical underneath the debugger is definitely the most powerful tool we have at our disposal when it comes to creating custom scripts.  If you can learn to use this tool effectively, you will be able to watch the inner workings of Mechanical interact with your scripts in a precisely controlled environment.  Furthermore, the rich debugger displays will allow you to see most all of the mechanical objects in suspended animation and will also allow you to investigate their properties and methods in real time.  Finally, the “Immediate Window” in the debugger will allow you to execute random pieces of code that you type in on the fly while Mechanical is suspended in the debugger.  This will allow you to test out various techniques and code sequences in one debugging session.   Simply put, you must become proficient at using the debugger to have any real hope of successfully scripting the ANSYS Mechanical tool.

Some Preliminaries

There are a number of steps you need to take to actually get the debugger environment set up properly.  Here are a list of steps you need to take:

  1. You obviously need to install Visual Studio.  I use Visual Studio Professional 2010, but I think any version including 2005 and above will probably be sufficient.  I cannot speak to whether or not the Express Editions will work for this task, so if someone has experience with one of these editions and can shed some light, please post a comment.
  2. In the Internet Explorer “Internet Options” dialog (arrived at through Tools->Internet Options…) you need to make sure that “Disable Script Debugging (Other)” is unchecked.  See image below.
  3. Finally, you need to adjust the registry setting HKEY_CURRENT_USER\Software\Microsoft\Windows Script\Settings\JITDebug = 1

 

image

 

Debugging Code

The easiest way to stop Mechanical in the debugger is to insert the javascript “debugger” command wherever you would like to stop execution and look around.  So, for example, lets assume we have the following script:

function my_func() {
    debugger;
    var my_array = new Array();
    for(var i = 0; i < 10; i++) {
        my_array[i] = 2*i;
    }
}

my_func();

As you can see above, this simple script consists of one function called my_func that just populates an array.  The first statement in this function is the keyword debugger.   If all works according to plan, when we execute this script, we’ll end up in the Microsoft Debugger.  To make this all happen, you need to follow a ten step program:

  1. Start MS Visual Studio
  2. Start ANSYS Mechanical
  3. Copy the above script to a file named debug_test.js anywhere on your harddrive.
  4. Inside MS Visual Studio, navigate to Debug->Attach to Process…image
  5. In the dialog that pops up, look for the AnsysWBU.exe executable in the list.  image
  6. Select AnsysWBU.exe and press the Attach button.
  7. Switch to the Mechanical Application
  8. From the tools Menu, choose Tools->Run Macro…
  9. Navigate to the location of debug_test.js and click open.
  10. Start debugging…

At this point you can set breakpoints, step into and out of functions, interrogate variable values, etc…  The debugger is truly a remarkable tool, as we shall briefly touch on next.

Whirlwind Tour of the MS Debugger

Below is a screen shot of our little script inside the visual studio debugger.

image

As you can see, the current execution line is located over the debugger statement.  Pressing F10 will advance one line at a time.  Also, you will notice that there is a window below the code window with a tab called “Locals”  This tab shows the local variables defined in this function.  At this point in the execution, they are not defined.  However, as we step the execution, these variables will come into play.  You can watch what happens to variables as you step through your code here.

Also, you will notice a second window called the “Immediate Window”.  This window may not be visible for your default debugging layout.  If you don’t see it, navigate to Debug->Windows->Immediate Window

image

In the immediate window you can enter in code that will be executed “immediately” while the program is stopped in the debugger.  So, for example, if you see a problem in your script that might be fixed with a code change you may be tempted to leave the debugger fix it and then re-run.  However, if you are unsure of your fix, or if you want to try a number of alternatives, you can just type them into the immediate window and see what happens.  It works as if you were executing new code at the current breakpoint location that isn’t in your script.  You can then step forward in your script to execute a few lines, enter something in the immediate window, and then continue on stepping.  It is a very powerful technique.

Lets step through a few lines.  I’m going to press F10 four or five times.  At that point I get the following screen.

image

You can see that my_array now has an ellipse next to it and I is equal to 1.  If I click on the plus symbol next to my_array in the locals pane, I get the following:

image

You can see that we’ve filled the zero-th item in the array with the value zero.  Lets hit F10 some more…  Note, you can also hover your mouse over the array in the code window.  You can see I’ve filled in a few more values

image

The print screen function doesn’t capture it well, but you can see the values in the array have changed.  Now, lets say I just realized that for some code below to work properly, the fifth value in this array needs to be less than 10.  (We don’t have any code below, and this is quite contrived, but bear with me and use your imagination…)  So, I just pop down into the immediate window and type:

my_array[5] = 8;

image

When I press return, this will execute that single line of code. You can see that my_array[5] now holds the value 8 instead of 10.

image

 

You can also set break points in your code.    Just right click on the line, and choose Breakpoint->Insert Breakpoint

image

Breakpoints work kind of like the debugger command, only they don’t litter your code with commands.  I usually insert one debugger command at the top of my script and then breakpoints where needed.   Breakpoints can also be conditional.  That is, they won’t break unless some condition is true.  This is great for executing most of a loop, but breaking on the last or next to last item.  After you have inserted a breakpoint, just right click on the line and choose Breakpoint->Condition…

image

You can insert a condition like:

image

From here, press F5 which is the Run command, and the code will execute inside the loop until I == 8.

Conclusion

The debugger is your friend; plain and simple.  Use it wisely and use it often.  In fact, if you want to script in Mechanical, you are going to have to become intimately familiar with the debugger.  In a future article, I will show you more powerful debugging tips when the debugger by itself lets you down. Stay tuned…

IT Tips – Is SP1 for Windows 7 Going to Slow Down my Machine?

ANSYS R13 Lemans CPU Wall Clock Benchmark Results Before and After a Windows 7 64-bit SP1 Update

I was curious what sort of impact the Windows 7 SP1 upgrade would have on a new install of ANSYS R13. Would the Service Pack upgrade have a positive or negative impact on the ANSYS R13 and additionally the Lemans benchmarks?  Here is what we found:

  • Hardware test machine: Single socket 6 core AMD C32 running 2.6GHz, 8GB of RAM, 2 x 160GB Intel SSD drives in RAID0, Windows 7 Professional
  • Benchmarks completed by Clinton Smith, Consulting Mechanical Engineer CFD and Thermal Analysis at PADT, Inc.

Results:

  • The benchmark results appear approximately the same before and after the install of the Service Pack.
  • No apparent issues or crashes with ANSYS R13 after installing the Windows 7 Service Pack 1 update
  • Six core benchmarks and one core benchmarks are slightly faster after SP1 upgrade.
  • Four core and two core benchmarks are slightly slower after SP1 upgrade:

ANSYS Le Mans Model, 1,864,025 Nodes

Cores

Wall Time (s)
Pre SP1

Wall Time (s)
With SP1

Diff

% Diff

1

7,220

7,190

-30

-0.4%

2

3,970

3,980

10

0.3%

4

2,300

2,320

20

0.9%

6

1,850

1,840

10

-0.5%

Conclusion:

It appears that overall the impact of the upgrade is a positive. No system stability or ANSYS R13 issues related to the upgrade.

FE Modeler Part 3: Morphing Meshes

Morph-lady-catIn the first two parts of this series we talked about how to use FE Modeler to translate meshes and how to create geometry from meshes.  The third “big” thing that this tool does is allow you to morph a mesh to a new shape.  There are a few less important things that FE Modeler does, and we will cover those in the last article of this series.

First, let’s take a good long look at the picture to the left.  Mesh morphing is nothing like what the image shows, but it is pretty creepy to look at so I thought I’d throw it up there.

Mesh morphing in FE Modeler works by taking the faceted surface geometry you can create, covered in the previous article, and allowing the user to apply transformations or a projection to that geometry.  You would use this if you want to change your geometry while keeping the same basic mesh.  This is because when you are doing optimization or parametric study and changing the CAD geometry, you force a remesh every time and sometimes the change in mesh is large enough to effect your study.  You would also need to use this method if all you had was an FE mesh.

Basic Process

Let’s start with a very simple example to show how it is done. 

Figure 1 shows a piece of example geometry.  It has some nice features that we will do more complex morphs on.

f1_geometry
Figure 1: Simple Example Geometry, Itching to be Morphed

To start off we do the same geometry (covered in detail in Part 2 of this series):

  1. Read in geometry
  2. Select Geometry Synthesis in the tree and make sure Use Node components is Yes
  3. Click on the Initial Geometry Icon
  4. If everything works you should have a solid that you can now work with

Now you need to define how you want to modify the faceted geometry.  Do this by selecting Initial Geometry in the tree and then clicking on the “New Target Configuration” Icon.  See Figure 2.

 

f2_MakeTargetConfigurationFigure 2: Creating the Target Configuration

When you do this the program will go out and configure some things internally and then add two new things to the tree: 1) a “Target Configuration” that is a child of Initial Geometry and 2) a Parameterized configuration branch. Figure 3 shows the result in the tree.

 

f3_Tree_After_Target_ConfigFigure 3: Model Tree After Creation of Target Configuration

So, at this point you might be trying to figure out why there are branches and children where they are. The way I think about it is that the Geometry Synthesis listing is not a branch and is not a child of something.  It is it’s own “tree”  So everything on the first level under it, its children, are different things you can do with Geometry Synthesis:  Find skins, define working Geometry,  Set up mesh Morphing (Initial Geometry), and view what the current mesh looks like based on the current parameters (Parameterized Configuration).  I feel that the Initial Geometry branch is mislabeled and should say Mesh Morphing, but it is too late to change the name now.

Back to morphing meshes…  The way you specify what you want morphed is to define Design Points that have some sort of translation or projection.  Then you specify how far to translate with a parameter that you control in the Project Schematic.  For our first run through, we are going to offset a surface. 

Do this by clicking on Design Point and then on the Transformation Icon and Select “Face Offset” (or RMB –>Insert->Face Offset) and fill out the details view.  For this example I used the end face and set the maximum offset value to 30 mm.  Note, even though it doesn’t say it in the GUI, the help points out that you should put in your maximum value that you might use.  the program then scales from the original to this value as you change the offset parameter.  Figure 4 shows the setup for this example.

f4_Offset_face
Figure 4: Specifying the Face Offset, Before Generation

Click on Generate the Design Point to see what the offset looks like:

f5_Offset_Face_2
Figure 5: The Offset Face after Generation

Here is where it gets a little tricky.  All you did was define a potential geometry change.  That change is not applied to the model until you actually set parameters and apply them.   And, the parameter is controlled at the Project Schematic level, not within FE Modeler.  The value you put in under Design Point, that is simply a maximum value that the program uses to figure out how to do the Morphing.  The actual morphing uses the parameter as defined in the Project Schematic.

To see this click on Parameterized Configuration and you will see that there is 1 parameter, it is called Mesh.Morpher.1, and its value is 0.  (Figure 6).

f6_ParamValues
Figure 6: Configuration Information after Offset is Defined

Therefore, your next step is to go to the project schematic and change the offset value to some new value you want to morph the mesh to (remember, the value we put in for the Design Point is our guess at the maximum value, we can use any value we want for the parameter value).  To change the value in the Project Schematic, bring up the schematic and click on the parameter bar.  You should now see the parameter in the Outline view. For this example, I changed it to 17.354.  Now exit back to the parameter manager and click on Update Project.  Go back to your FE Modeler window, click on Parameterized Mesh, and you should see the morphed mesh:

f7_morphed_mesh
Figure 7: Morphed Mesh

Figure 8 shows an animation from an offset of 0 to 30: 

SimpleMorph1

Figure 8: Mesh Morphing from an Offset of 0mm to 30mm

To get the mesh out and usable in your model you have one more step.  If you click on Write Solver File now you will get your original geometry.  You need to click on Parameterized Mesh then click on the Update FE Modeler Mesh icon.  Now set your Target System and chose Write Solver file.  Or, if you are staying in workbench, drag a new system onto the Model in the Project Schematic.  The cool thing about this is that you now have a parametric model that is linked to an analysis in Workbench. It is therefore easy to do design studies, optimization, and the rest of the cool things Workbench is great at, and without CAD geometry!  Figure 9 shows an example of using the mesh for a modal analysis.

f9_Use_Morphed_MeshFigure 9: Using the Morphed Mesh in a Modal Analysis

Getting More Complicated

Although the process is the same, you can get a lot more complicated.  The program allows for five different translations, offsets or projections, and some of them have multiple options within. They are all kind of self explanatory.

  f10_tranlation_options
Figure 10: Morphing Options

Here is the description from the help as a reference:

  • Translation (of vertices, edges, surfaces, or parts): A translation is given in the global Cartesian coordinate system or by the definition of a translation vector between two points.
  • Rotation (of vertices, edges, surfaces, or parts): You must define a rotation axis between two points or a point and a vector and then give a rotation angle in degrees or radians.
  • Face (Surface) Offset – a Face Offset can be:
    • Uniform – Enter a negative or positive Offset Value to move the face inward or outward.
    • Non Uniform – Enter a negative or positive Offset Value to move the face inward or outward. With this transformation, you can offset a surface with a nonlinear curve. In addition, a Non Uniform surface offset includes the following options:
      • Distance to the edges – Define the distance from the edges to the maximum displacement of the transformed face.
      • Function type – Select a function type based on the shape you want to obtain, options include: Linear, Double Tangent, Linear-tangent, Tangent-linear.
      • Immobile edges – By default, all of the edges for the target surface are selected. You can de-select edges if desired.
  • Edge Offset: An offset of one edge along a face by a specified distance; always with a given sign depending on the edge normal.
  • Projection: a projection of a face, an edge, or a vertex onto a face, edge, or vertex or a group of faces or a group of edges. The Projection transformation works in tandem with the Working Geometries feature. Using an imported Working Geometry, you can project the entities of a Target Configuration onto the entities of the imported (Working) geometry.

For this example I added a rotation for the block sticking up, a translation for the top circle face of the cylinder, and an edge offset for the lower right edge.  the result is shown in Figure 11. 

 f11_Morphed-Unmorphed
Figure 11: Part with Offset, Rotation, Translation and Edge Offset Applied. Overlaid on Original Mesh

f12_Morphed_With_labels

Figure 12: Part with Offset, Rotation, Translation and Edge Offset Applied. Labeled. 

Mesh_Morph2

Figure 13: Animation of Morphing

Whew, that is a lot of material, and we are out of time to get this article back.  Look for a Part 3.5 in the near future filling in some missing pieces and a few more examples.

If you want to try this yourself, ask you sales professional for  temp Key of the ANSYS Mesh Morpher. And as always, start simple and work your way to more complex parts.

Mapped Face Meshing in ANSYS Workbench

In the era of automatic tetrahedral meshing, many have lost their way.  Wondering analysts simply read in their geometry, specify a few sizing controls, hit a mesh button and get a mesh.  But in the end, they receive a mesh that is not ideal, especially on the surface.  What these meandering meshers need is a map… a mapped mesh. 

OK, that is a pretty lame introduction, but I’ve run out of Monty Python references and I don’t have Doug’s B movie knowledge. 

Bad intro aside, many users are not aware of the strong capabilities available in Workbench meshing for creating really nice mapped meshes on surfaces.  Once created, these mapped meshes can be used to mesh 2D and shell models as well as to extrude a mesh (sweep method) for a 3D Hex mesh or a ‘seed’ mesh for a tetrahedral mesh (patch conforming algorithm).  By using a controlled mapped mesh, users can ‘find’ their way to a better mesh.

Mapped Mesh Method

The Mapped Mesh Method has been an Mesh Control that has been in ANSYS for a while.  It is found in the Mesh Control menu item when you are in the Mesh part of the tree, or by RMB->Insert->Mapped Face Meshing.  For simple geometry, and some not so simple, you simply slap that control on a face or faces and let ANSYS figure out the best way to make a mapped mesh.

F1-Add_MethodFigure 1: Insert Mapped Face Meshing

In the past, the area had to be a pretty “square” topology – four easily identified edges.  But over the last couple of releases more intelligence has been added to recognize complex geometries as mapable. Figure 2 shows several different topologies and how the mesher does a nice job of mapping them with no user modification.

F2-MapExamplesFigure 2: Automatic Meshing of Complex Topologies with Mapped Meshes

Note that the last topology did not map mesh.  We will come back to that.  But the other five all meshed with a nice looking mapped mesh. So how does ANSYS figure out how to do this? 

What they do is take geometry and break it up into 4 sided chunks.  They call this making Submaps.  But to do this they need to identify the outside edges in terms of squarish topology. The secret to doing this is identifying the vertices (points where edges connect) as either a corner, a side or an edge.  Figure 3 (stolen from the user manual) shows an example of each:

F3-Vertex_Types
Figure 3: Vertex Types

The algorithm identifies the vertex type by looking at the inside (mesh side) angle formed by the two edges attached to the vertex using based on the following table:

Vertex Type

Angle Range

Number of Elements Connected

Image

End

0°-135°

1

T1-EndVertex

Side

136°-224°

2

T2-SideVertex

Corner

225°-314°

3

T3-CornerVertex

You can get a feel for the algorithm (but not the actual one, it is much more complex) by stepping through breaking up the geometry in Figure 3 and follow the following steps in your head:

  1. Mesh the edges creating nodes on the edges based on local and global meshing settings
  2. March around the edges making virtual edges that combine by combining any edges linked by Side vertices (the only one is F-G-A in this example).
  3. Go to the first corner you encountered.  Shoot a line along the vector from B-C until you intersect with an edge. Find F-E.
  4. Count number of nodes from A-B, call it N. Then count N nodes from F toward E and make the Nth node a virtual vertex H.
  5. Draw a virtual edge from C-H. Note, B-C-H is now one virtual edge because in B-C-H, C is now a Side vertex.
  6. Keep marching around from H till you get back to A.  You end up with 4 corners so now you have a sub map.
  7. Go back to where you broke off, C, and march around to C (remember H-C is now an edge and C looks like an End in this topology) until you get another corner.  You now have the second sub map.

Pretty cool, huh. I wish I would have thought of that.  The actual method is of course much more complex and has allsorts of checks and “if this, than do that” stuff in it.  

Helping the Algorithm Out

Sometimes the angles just don’t work out so you need to go in and tell the mesher what is a corner, side, or end.  You do this in the Details view for the Mapped Face Meshing method you attach to the face.

F4-DetailsArea
Figure 4: Details View for Mapped Face Meshing Method

You basically click on the cell for Sides, Corners and Ends and then identify the vertices for each.  The last example in figure three has rounded corners so the algorithm identifies the vertices on the rounds as Sides because they are 180° apart. The result is the free mesh shown in Figure 5. If you want a mapped mesh, you need to specify the vertices on the rounds. Figure 6 shows the result. Well, maybe sometimes the free mesh is better.

F5_RoundedCornerFreeMesh
Figure 5: Resulting Free Mesh with Automatic Identification of Vertices

f6-RoundedCornerMapMeshed
Figure 6: Mapped Mesh after Manual Identification of Vertices

The other option on really nasty geometry is to take it into DesignModeler and imprint the surfaces to create your own sub maps.

Conclusions

In most cases, just putting a Mapped Face Meshing control on a surface will give you a nice mapped mesh.  The mapped mesh is usually more uniform, has less distorted elements, no triangles, and usually has less nodes. But a mapped mesh is not always better, you need to use your engineering judgment to decide which is best in each application.  I like to use this control on fillets and on blocky parts.

So, when you are not liking the look of the default surface mesh, even if you are not hex meshing your model, turn off the autopilot, and try a Mapped Face Meshing control. 

Mid-side nodes: Do they really help?

Do those pesky mid-side nodes really do anything other than increase my node count and runtime?  Over the years, I have heard that question over and over. “Do I really need them?”  If you are using tetra elements, then the answer is absolutely, “Yes!”  They even do more than just give you nice curvature to follow your geometry.  For tetras, the midside nodes are necessary to predict proper stresses.  Lower ordered tetras are overly stiff and will under predict deflection and stresses.  Let’s look at the underlying equations that make up the shape functions in both element types.

image

Yeah, the equations for tetras are a little more complicated than that, but I really didn’t think you  wanted to go down that road. But I will remind you that low-order elements have linear shape functions, while high-order elements have quadratic shape functions. So let’s look at an example, instead, to show that lower-order tetras will give unconservative results.

For the example, I made a simply-supported, rectangular-cross-section plank.  It’s 100 inches long, by 10 inches wide, by 1 inch thick, with a 1 psi pressure load. I set the mesh size at 1 inch and solved with low-order tetras, high-order tetras, and also with low and high-order hex elements.  The results are tabulated below along with the hand-calculated solution from the 7th Edition of Roark’s Formulas for Stress and Strain, Table 8.1, case 2e.

image

image

  Deflection Def. % Diff Stress Stress  % Diff Nodes Elements
Roark’s -0.53864   7500.0 psi      
20-Node Hex -0.54159 0.55% 7527.0 psi 0.36% 7553 1,000
8-Node Hex -0.54142 0.52% 7529.5 psi 0.39% 2222 1,000
10-Node Tetra -0.54158 0.55% 7525.9 psi 0.35% 20661 11,998
4-Node Tetra* -0.27151 49.6% 2249.3 psi  –70.0% 3222 11,999

As you can see, the hex elements along with the 10-node tetras get close to the solution and provide conservative results.  The 4-node tetras, however, which are actually degenerate 8-node hex elements because ANSYS removed their 4-node tetra elements along time ago, show only half the deflection under the same load.   The extra stiffness also causes the modal frequencies to be higher. In this case, the frequencies of the low-order tetras were about 40% higher than the high-order tetras.  When you’re looking at operating ranges, this could also lead to unconservative conclusions.

Of course this is a pretty coarse mesh with only one element through the thickness. So how do the results change as the mesh is refined? We should only need to cut the element size by half to get the extra nodes and the same results, right? 

    Deflection Def. % Diff Stress Stress  % Diff Nodes Elements
Roark’s -0.53864”   7500.0 psi      
1”  -0.27151”

–49.6%

2249.3 psi

-70.0%

3,222 11,999
0.5” -0.39768”

-26.2%

4879.1 psi

-34.9%

17,624 77,612
0.3” -0.47582”

-11.7%

6069.5 psi

-19.1%

69,040 334,433
0.2” -0.51023”

-5.3%

6760.9 psi

-9.8%

213,122 1,105,641
0.1” -0.53345”

-0.96%

7309.6 psi

-2.5%

1,599,327 8,877,326

As you can see the answer is, “No.”  Because  the high-order elements have quadratic shape functions , it takes far more linear elements to make up the difference. Here is a little animation to show why. You can see the error left by each set of linear elements when try to match a quadratic function.JoeAnim1

For another example, I made a long square-cross-section bar with holes in it.  It is cantilevered with a shear load at the other end.  I set the Physics preference to Mechanical and the Relevance Center to ‘Fine’ and I got the mesh that you see below. (I’d say its not too bad for 15 seconds of works. )image

   imageimage

Just to make sure that my mesh was the same in both cases, I told Mechanical to Drop the midside nodes and then I added a Command Object in the high-order  case to convert the elements to Solid 187 tetras during the run. Since no geometry is sent from Mechanical, the midside nodes were added and the edges were kept straight, so no further curvature was picked up.  Here’s the command object to try out yourself.

imageimage

 

By scoping the results to just the hole closest to the fixed end we can see the difference in the stress values for the two runs.  The low-order tetras yield results that are 12% lower for deflection and over 19% lower for stress than the same mesh with midside nodes.  (But look, Boss! I saved 4 seconds on the solve time!!!)

  Elements Nodes Total
Deflection
SEQV Solver Runtime
Low-order 18383 4854 0.28744” 177.51 Ksi 6.677 Sec.
High-order 18383 31332 0.32633” 219.46 Ksi 10.951 Sec.

 

imageimage

imageimage

So how many low-order tetras are needed to get the same accuracy this time?   I used the Convergence Tool in Workbench to find out this time. The Convergence tool refines the mesh only for the scoped region of the results plot.  So the resultant mesh looks like this.

image

imageimage

 

clip_image001[6]

  Elements Nodes SEQV Solver Runtime
Low-order

1,271,619

234,484

21.951 Ksi

349.785 Sec.

High-order

18,383

31,332

21.946 Ksi

10.951 Sec.

The last pass took just under 6 minutes to run (349.8 sec). The stress finally gets as high as it is with the high-order elements, but the deflection results are still incorrect because the model was only refined at the hole.  And say it with me, “If the deflection is wrong, then the stress ain’t gonna be right!!!”  So basically I’ve wasted a half an hour getting the wrong solution so that you won’t have to do the same. Just remember, mid-side nodes are your friends. And just like any good friend, take advantage of them when you can!