Files and Info from “Using APDL Snippets in ANSYS Mechanical”

We just finished the webinar “Using APDL Snippets in ANSYS Mechanical” and wanted to share the files weimage used and some additional information. 

The presentation can be found here.

The sample script for plotting mode shapes is here.

You can view a recording of the presentation here on our WebEx site.

PADT’s ANSYS Webinar Series is now off on Summer Break. We will be back in August!

You can always view past recordings by going to http://padtincevents.webex.com and Click on PADT ANSYS Webinar Series to see a listing.

A Further Assessment of Design Assessment

Last weeks PADT ANSYS Webinar Series webinar was on a little used feature in ANSYS Mechanical called Design Assessment, or DA.  If you missed it, you can view a recording at:

imagehttp://tinyurl.com/PADT-DA-Webinar

And a PDF of the presentation can be found here:

As promised, this weeks The Focus posting will be a follow up on that webinar with an in-depth look at the scripts that were used in the example.  But first, let us review what DA is for those that don’t remember, fell asleep, or don’t want to sit through 60+ minutes of me mumbling into the telephone.

Review of DA

Design Assessment is a tool in ANSYS Workbench that works with ANSYS Mechanical to take results from multiple time or load steps, perform post processing on those results, and bring the calculated values back into ANSYS Mechanical for viewing as contour plots.  It was developed to allow the ANSYS, Inc. developers to add some special post processing tools needed in the off shore industry, but as they were working on it they saw the value of exposing the Application Programmers Interface (API) to the user community so anyone can write their own post processing tools.

image

You use it by adding a Design Assessment system to you project.  In its most basic form, the default configuration, it is set up to do load case combinations.  That in itself is worth knowing how to use it.  But if you want to do more, you can point it to a custom post processing set of files and do your own calculations.

A custom DA is defined by two things.  First is an XML file that tells ANSYS Mechanical what user input you want to capture, how you want to get results out of mechanical, what you want to do with the results, and how you want them displayed in your model tree.  Second is one or more Python scripts that actually do the work of capturing what the user input, getting results, doing the calculation, and sticking the resulting values back in the model.  Both are well documented and, once you get your head around the whole thing, pretty simple.

Right now DA works with Static and Transient Structural models.  It also only allows access to element stress values.  Lots of good enhancements are coming in R14, but R13 is mature enough to use now.

If that review was too quick, review the recording or the PowerPoint presentation.

A Deep Dive Into an Example

For the webinar we had a pretty simple, and a bit silly, example – the custom post processing tool took the results from a static stress model and truncates the stress values if they are above or below a user specified value.  Not a lot of calculating but a good example of how the tool works. 

Note, this posting is going to be long because there is a lot of code pasted in.  For each section of code I’ll also include a link to the original file so you can download that yourself to use.

Here is the XML file for the example (Original File):

   1:  <?xml version="1.0" encoding="utf-8"?>
The first lesson learned was that you have to get all the tags and headers just right. 
It is case sensitive and all the version and other stuff has to be there
Cutting and pasting from something that work is the best way to go
   2:  <!-- 
   3:  www.PADTINC.com 
   4:            XML file for ANSYS DA R13
   5:            Demonstration of how to use DA at R13
   6:            Goes through results and sets stresses below floor value to floor
   7:              value and above ceiling value to ceiling value.     
   8:              
   9:          User adds DA Result to specify Floor and Ceiling
  10:          Attribute group can be used to specify a comment
  11:          
  12:          Calls da_trunc_solve.py and da_trunc_eval.py in c:\temp
  13:          
  14:          Eric Miller
  15:          5/18/2011
  16:  -->
  17:   
Everything is in a DARoot tag.
  18:  <DARoot ObjId ="1" Type="CAERep" Ver="2">
Attributes tags define items you want to ask the user about.  
  19:    <Attributes ObjId="2" Type="CAERepBase" Ver="2">
This first attribute is a drop down for the user to decide which stress value they want 
You use <AttributeType> to make it a dropdown then put the values in <Validation>
  20:      <DAAttribute ObjId="101" Type="DAAttribute" Ver="2">
  21:        <AttributeName PropType="string">Stress Value</AttributeName>
  22:        <AttributeType PropType="string">DropDown</AttributeType>
  23:        <Application PropType="string">All</Application>
  24:        <Validation PropType="vector&amp;lt;string>">
  25:               SX,SY,SZ,SXY,SYZ,SXZ,S1,S2,S3,SEQV
  26:        </Validation>
  27:      </DAAttribute>
Next is the prompt for the stress floor value
  28:      <DAAttribute ObjId="102" Type="DAAttribute" Ver="2">
  29:        <AttributeName PropType="string">Stress Floor</AttributeName>
  30:        <AttributeType PropType="string">Double</AttributeType>
  31:        <Application PropType="string">All</Application>
  32:        <Validation PropType="vector&amp;lt;string>">-1000000,10000000</Validation>
  33:      </DAAttribute>
Then the Ceiling Value
  34:       <DAAttribute ObjId="103" Type="DAAttribute" Ver="2">
  35:        <AttributeName PropType="string">Stress Ceiling</AttributeName>
  36:        <AttributeType PropType="string">Double</AttributeType>
  37:        <Application PropType="string">All</Application>
  38:        <Validation PropType="vector&amp;lt;string>">-1000000,10000000</Validation>
  39:      </DAAttribute>
Finally a user comment, just to show how to do a string.
  40:      <DAAttribute ObjId="201" Type="DAAttribute" Ver="2">
  41:        <AttributeName PropType="string">User Comments</AttributeName>
  42:        <AttributeType PropType="string">Text</AttributeType>
  43:        <Application PropType="string">All</Application>
  44:      </DAAttribute>
  45:    </Attributes>
To expose an attribute, you can put it in an AttributeGroup to get info shared by
all DA result objects.  This one just does the Comment
  46:    <AttributeGroups ObjId ="3" Type="CAERepBase" Ver="2">
  47:      <DAAttributeGroup ObjId="100002" Type="DAAttributeGroup" Ver="2">
  48:        <GroupType PropType="string">User Comments</GroupType>
  49:        <GroupSubtype PropType="string">Failure Criteria</GroupSubtype>
  50:        <AttributeIDs PropType="vector&amp;lt;unsigned int>">201</AttributeIDs>
  51:      </DAAttributeGroup>
  52:    </AttributeGroups>
<DAScripts> is the most important part of the file.  It defines the scripts to run for a 
solve and for a evaluate.  At R13 you do need to specify the whole path to your python files
  53:    <DAScripts ObjId="4" Type="DAScripts" Ver="2">
  54:      <Solve PropType="string">c:\temp\da_trunc_solve.py</Solve>
  55:      <Evaluate PropType="string">c:\temp\da_trunc_eval.py</Evaluate>
  56:      <DAData PropType="int">1</DAData>
  57:      <CombResults PropType="int">1</CombResults>
  58:      <SelectionExtra PropType="vector&amp;lt;string>">DeltaMin, DeltaMax</SelectionExtra>
  59:    </DAScripts>
The other way to get user input is to put attributes into a <Results> object.  
Here we are putting the choice of stresses (101),and the floor and ceiling (102,103)
into an object
  60:    <Results ObjId="5" Type="CAERepBase" Ver="2">
  61:      <DAResult ObjId ="110000"  Type="DAResult" Ver="2">
  62:        <GroupType PropType="string">Ceiling and Floor Values</GroupType>
  63:        <AttributeIDs PropType="vector&amp;lt;unsigned int>">101,102,103</AttributeIDs>
  64:        <DisplayType PropType="string">ElemCont</DisplayType>
  65:      </DAResult>
  66:    </Results>
  67:  </DARoot>

I always have to go over XML files a few times to figure them out. There is a lot of information, but only a small amount that you need to pay attention to.  After a while you figure out which is which.

Now on to the fun part, the Python scripts.  The first one gets executed when they user chooses solve. 

da_trunc_solve.py is shown below and you can get the original here.   The comments inside pretty much explain it all.  It basically does two things:  creates an ANSYS MAPDL macro that extracts all the element stresses and puts them in a text file, then it runs MAPDL with that macro.

   1:  import subprocess
   2:  import os
   3:  import shutil
   4:   
   5:  #======================================================================
   6:  #
   7:  #   ------------------------------------------------------------  PADT
   8:  #    www.PADTINC.com
   9:  #
  10:  #    da_trunc_solve.py
  11:  #         Demonstration python script for Design Assessment in 
  12:  #            ANSYS R13
  13:  #         Called on solve from ANSYS Mechanical
  14:  #         Bulk of code copied and modified from TsaiWu example
  15:  #            provided by ANSYS, Inc.
  16:  #
  17:  #       E. Miller
  18:  #       5/18/2011
  19:  #======================================================================
  20:  def trunc_solve(DesignAssessment) :
  21:   
  22:      # Get number of elements in model
  23:   
  24:   
  25:      # Change directory to current workspace for DA
  26:      originaldir = os.getcwd()
  27:      os.chdir(DesignAssessment.getHelper().getResultPath())
  28:   
  29:      #Get the path to the results file name and the location of the temp directory
  30:      rstFname = DesignAssessment.Selection(0).Solution(0).getResult().ResultFilePath()
  31:      rstFname = rstFname.rstrip('.rst')
  32:      apath = DesignAssessment.getHelper().getResultPath()
  33:      
  34:      print "Result File:", rstFname
  35:      print "Apath:", apath
  36:   
  37:      # Write an ANSYS APDL macro to start ANSYS, resume the result file, grab the stress
  38:      #   results and write them to a file
  39:   
  40:      macfile = open(DesignAssessment.getHelper().getResultPath()+"\\runda1.inp", "w")
  41:   
  42:      macfile.write("/batch\n")
  43:      macfile.write("/post1\n")
  44:      macfile.write("file,"+rstFname+"\n")
  45:      macfile.write("set,last\n")
  46:      macfile.write("*get,emx,elem,,num,max\n")
  47:      macfile.write("*dim,evls,,emx,10\n")
  48:      macfile.write("etable,esx,s,x\n")
  49:      macfile.write("etable,esy,s,y\n")
  50:      macfile.write("etable,esz,s,z\n")
  51:      macfile.write("etable,esxy,s,xy\n")
  52:      macfile.write("etable,esyz,s,yz\n")
  53:      macfile.write("etable,esxz,s,xz\n")
  54:      macfile.write("etable,es1,s,1\n")
  55:      macfile.write("etable,es2,s,2\n")
  56:      macfile.write("etable,es3,s,3\n")
  57:      macfile.write("etable,eseqv,s,eqv\n")
  58:      macfile.write("*vget,evls(1, 1),elem,1,etab,  esx\n")
  59:      macfile.write("*vget,evls(1, 2),elem,1,etab,  esy\n")
  60:      macfile.write("*vget,evls(1, 3),elem,1,etab,  esz\n")
  61:      macfile.write("*vget,evls(1, 4),elem,1,etab, esxy\n")
  62:      macfile.write("*vget,evls(1, 5),elem,1,etab, esyz\n")
  63:      macfile.write("*vget,evls(1, 6),elem,1,etab, esxz\n")
  64:      macfile.write("*vget,evls(1, 7),elem,1,etab,  es1\n")
  65:      macfile.write("*vget,evls(1, 8),elem,1,etab,  es2\n")
  66:      macfile.write("*vget,evls(1, 9),elem,1,etab,  es3\n")
  67:      macfile.write("*vget,evls(1,10),elem,1,etab,eseqv\n")
  68:   
  69:      macfile.write("*cfopen,darsts,txt\n")
  70:      macfile.write("*vwrite,evls(1,1),evls(1,2),evls(1,3),evls(1,4),
evls(1,5),evls(1,6),evls(1,7),evls(1,8),evls(1,9),evls(1,10)\n")
  71:      macfile.write("(G16.9, X, G16.9, X, G16.9, X, G16.9, X, G16.9, X, 
G16.9, X, G16.9, X, G16.9, X, G16.9, X, G16.9)\n")
  72:      macfile.write("*cfclose\n")
  73:      macfile.write("finish\n")
  74:      macfile.write("/exit,nosave\n")
  75:   
  76:      macfile.close()
  77:   
  78:      # Set up execution of ANSYS MAPDL. 
  79:      #   Note: Right now you need to grab a different license than what Workbench is using
  80:      exelocation = "C:\\Program Files\\ANSYS Inc\\v130\\ansys\\bin\\winx64\\ansys130.exe"
  81:      commandlinestring = " -p ansys -b nolist -i runda1.inp -o runda1.out /minimise"
  82:   
  83:      #Execute MAPDL and wait for it to finish
  84:      proc = subprocess.Popen(commandlinestring,shell=True,executable=exelocation)
  85:      rc = proc.wait()
  86:   
  87:      # Read the output fromt the run and echo it to the DA log file
  88:      File = open("runda1.out","r")
  89:      DesignAssessment.getHelper().WriteToLog(File.read())
  90:      File.close()
  91:   
  92:      # Go back to the original directory
  93:      os.chdir(originaldir)
  94:   
  95:  trunc_solve(DesignAssessment

Some key things you should note about this script:

  • You have to use MAPDL to get your stress values. Right now there is no method in the API to get the values directly.
  • You need a second MAPDL license in order to run this script.  It does not share the license you are using for ANSYS Mechanical at R13.  This should be addressed in R14.
    • One work around right now is to use an APDL code snippet in the ANSYS mechanical run that makes the text file when the original problem is solved.  The SOLVE script is then no longer needed and you can just have an evaluate script.  Not a great solution but it will work if you only have once seat of ANSYS available.
  • Note the directory changes and getting of result file paths.  This is important. Mechanical does stuff all over the place and not in just one directory.
  • Make sure the MAPDL execution stuff is correct for your installation.

Once the solve is done and our text file, darsts.txt, is written, we can start truncating with the evaluate script (Original File).  This script is a little more sophisticated. First it simply reads the darsts.txt file into a python array.  It then has to go through a list of DA Results objects that the user added to their model and extract the stress value wanted as well as the floor and ceiling to truncate to. For each result object requested, it then loops through all the elements truncating as needed.  Then it stores the truncated values.

   
   1:  import subprocess
   2:  import os
   3:  import shutil
   4:  import sys
   5:   
   6:  #======================================================================
   7:  #
   8:  #   ------------------------------------------------------------  PADT
   9:  #    www.PADTINC.com
  10:  #
  11:  #    da_trunc_eval.py
  12:  #         Demonstration python script for Design Assessment in 
  13:  #            ANSYS R13
  14:  #         Called on eval from ANSYS Mechanical
  15:  #         Bulk of code copied and modified from TsaiWu example
  16:  #            provided by ANSYS, Inc.
  17:  #
  18:  # NOTE: Right now it just does SX.  XML and script need to be modified to allow user
  19:  #       to specify component to use (X, Y, or Z)
  20:  #
  21:  #       E. Miller
  22:  #       5/18/2011
  23:  #======================================================================
  24:  def trunc_eval(DesignAssessment) :
  25:   
  26:      # Change directory to current workspace for DA
  27:      originaldir = os.getcwd()
  28:      os.chdir(DesignAssessment.getHelper().getResultPath())
  29:   
  30:      # Find number of elements in DA
  31:      Mesh = DesignAssessment.GeometryMeshData()
  32:      Elements = Mesh.Elements()
  33:      Ecount = len(Elements)
  34:      print "DA number of elements is ",Ecount
  35:      print "Number of Result Groups:",DesignAssessment.NoOfResultGroups()
  36:   
  37:      # get User comment from Atribute Group
  38:      # Note: Assuems one.  Need to use a loop for multiple
  39:      attg = DesignAssessment.AttributeGroups()
  40:      atts = attg[0].Attributes()
  41:      usercomment = atts[0].Value().GetAsString()
  42:      print "User Comment = ", usercomment
  43:   
  44:          # create arrays for SX/Y/Z values
  45:      sx = []
  46:      sy =  []
  47:      sz = []
  48:      sxy = []
  49:      syz =  []
  50:      sxz = []
  51:      s1 = []
  52:      s2 = []
  53:      s3 = []
  54:      seqv = []
  55:      # read file written during solve phase
  56:      #   append stress values to SX/Y/Z arrays
  57:      File = open("darsts.txt","r")
  58:      for line in File:
  59:          words = line.split()
  60:          sx.append(float(words[0]))
  61:          sy.append(float(words[1]))
  62:          sz.append(float(words[2]))
  63:          sxy.append(float(words[3]))
  64:          syz.append(float(words[4]))
  65:          sxz.append(float(words[5]))
  66:          s1.append(float(words[6]))
  67:          s2.append(float(words[7]))
  68:          s3.append(float(words[8]))
  69:          seqv.append(float(words[9]))
  70:      File.close()
  71:   
  72:      # Loop over DA Results created by user
  73:      for ResultGroupIter in range(DesignAssessment.NoOfResultGroups()):
  74:          # Get the Result Group
  75:          ResultGroup = DesignAssessment.ResultGroup(ResultGroupIter)
  76:          # Extract Cieling and Floor for the Group
  77:          strscmp  = ResultGroup.Attribute(0).Value().GetAsString()
  78:          strFloor = float(ResultGroup.Attribute(1).Value().GetAsString())
  79:          strCeil  = float(ResultGroup.Attribute(2).Value().GetAsString())
  80:          print "DA Result", ResultGroupIter+1, ":", strFloor, strCeil
  81:   
  82:          #Add a set of results to store values in
  83:          ResultStructure = ResultGroup.AddStepResult()
  84:   
  85:          print "strscmp", strscmp
  86:   
  87:          # Loop on elements 
  88:          for ElementIter in range(Ecount):
  89:              #Add a place to put the results for this element
  90:              ResultValue = ResultStructure.AddElementResultValue()
  91:              # Get the element number and then grab SX values that
  92:              #   was read from file
  93:              Element = Mesh.Element(ElementIter).ID()
  94:   
  95:              if strscmp == "SX":
  96:                  sss = sx[Element-1]
  97:              elif strscmp == "SY":
  98:                  sss = sy[Element-1]
  99:              elif strscmp == "SZ":
 100:                  sss = sz[Element-1]
 101:              elif strscmp == "SXY":
 102:                  sss = sxy[Element-1]
 103:              elif strscmp == "SYZ":
 104:                  sss = syz[Element-1]
 105:              elif strscmp == "SXZ":
 106:                  sss = sxz[Element-1]
 107:              elif strscmp == "S1":
 108:                  sss = s1[Element-1]
 109:              elif strscmp == "S2":
 110:                  sss = s2[Element-1]
 111:              elif strscmp == "S3":
 112:                  sss = s3[Element-1]
 113:              elif strscmp == "SEQV":
 114:                  sss = seqv[Element-1]
 115:   
 116:              # Compare to Ceiling and Floor and truncate if needed
 117:              if sss > strCeil:
 118:                  sss = strCeil
 119:              if sss < strFloor:
 120:                  sss = strFloor
 121:              # Store the stress value
 122:              ResultValue.setValue(sss)
 123:      # Go back to the original directory
 124:      os.chdir(originaldir)
 125:   
 126:  trunc_eval(DesignAssessment)
 127:   

Some things to note about this script are:

  • The same directory issues hold here.  Make sure you follow them
  • Always loop on ResultGroups.  You can assume the number of attributes is constant but you never know how many results the user has asked for.
  • In this example it is assumed that the stress label is the first attribute and that the floor and ceiling are the second and third.  This is probably lazy on my part and it should be more general.
    • The way to make it more general is to loop on the attributes in a group and grab their label, then use the label to determine which value it represents.
  • Before you can store values, you have to create the result object and then each result value in that structure:
    • ResultStructure = ResultGroup.AddStepResult() for each result object the user adds to the tree
    • ResultValue = ResultStructure.AddElementResultValue() for each element
    • ResultValue.setValue(sss) to set the actual value

And that is our example.  It should work with any model, just make sure you get the paths right for where the files are.

Be a Good Citizen and Share!

If you have the gumption to go and try this tool out, we do ask that you share what you come up with.  A good place is www.ANSYS.NET.  That is the repository for most things ANSYS.  If you have questions or need help, try xansys.org or your ANSYS support provider.

Happy Assessing!

Files for Design Assessment in R13

During the Webinar on 5/18/2011 we discussed a simple example we created to show how to use Design Assessment.  Click here to download the Zip file.

You can get a PDF of the PowerPoint here.

You can watch a recording of the webinar at: http://tinyurl.com/PADT-DA-Webinar

image

I Have the Touch: Check Contact in Workbench Prior to Solving with the Contact Tool

Song quotes Peter Gabriel, “I Have the Touch”

The time I like is the rush hour, cos I like the rush
The pushing of the people – I like it all so much
Such a mass of motion – do not know where it goes
I move with the movement and … I have the touch

Looking back I can see a defining moment in my life when about a month after high school graduation two good friends and I drove four hours from home to see Peter Gabriel in concert.  It’s not that the concert was great, which it was, but it was the trip itself.  It was a first foray after high school, a sort of toe dipping into the freedom of adulthood while in a strange pause between graduating in a small town in the same school system with the same kids and starting engineering school in a big city in the Fall.

Wanting contact
I’m wanting contact
I’m wanting contact with you
Shake those hands, shake those hands

What does all that have to do with ANSYS, you ask?  Primarily, it’s hard to get Peter Gabriel’s “I Have the Touch” out of my head whenever I’m working with contact elements.  Someone once said that we are a product of the music of our youth.  While as I’ve gotten older and hopefully wiser I would hope that we are made up of much more than the product of listening to some songs, I do find it true that certain songs from years ago tend to stick in my head.  So, while Mr. Gabriel plays in my head, let’s discuss checking our contact status in Workbench Mechanical.

For those of us familiar with Mechanical APDL, the CNCHECK command has been a good friend for a lot of years now.  This command can be used to interrogate our contact pairs prior to solving to report back which pairs are closed, what the gap distance is for pairs that are near touching, etc.  More recently, this type of capability has become available in Workbench Mechanical by inserting a Contact Tool under the Connections branch.

Let’s take a look at that in version 13.0.  Here we have inserted a Contact Tool under the Connections branch.  It automatically includes the Initial Information sub-branch, with a yellow lightening bolt meaning no initial information has yet been calculated.

image

By right clicking on the Initial Information sub-branch, we can select Generate Initial Contact Results.  The resulting worksheet view provides significant information on all of the defined contact regions.  By default the information displayed for each contact region includes the name, contact/target side, type (fictionless, no separation, etc.), status, number of elements contacting, initial penetration, initial gap, geometric penetration, geometric gap, pinball radius, and real constant set number.  That last value is useful when reviewing the solver output, as it lists contact info per real constant set number of each contact pair (contact region).

image

Further, by right clicking on that table we have the option to display some additional data, or remove fields of data.  The additional fields that can be added are contact depth, normal stiffness, and tangential stiffness.  We can also sort the table by clicking on any of the headings. 

The colors in the table display four possible status values:

Red = open status for bonded and no separation types

Yellow = open for non bonded/no separation types

Orange = closed by with a large amount of gap or penetration

Gray = inactive due to MPC or Normal Lagrange or auto asymmetric contact

image

If we left click on one or more of the contact regions in the table, we can then right click and “Go to Selected Items in Tree.”  This is a convenient way to view a particular set of contact regions in the graphics view.

Any social occasion, it’s hello, how do you do
All those introductions, I never miss my cue
So before a question, so before a doubt
My hand moves out and … I have the touch

So, what do we do with this information?  Ideally it will prevent us from launching a solution that goes off and cranks for a few hours only to fail due to an improper contact setup.  For example, by viewing the initial status for each pair we can hopefully verify that regions that should be initially touching are in fact touching as far as ANSYS is concerned.  If there is an initial gap or penetration, correcting action can be taken by adjusting the contact settings or even the geometry if needed prior to initiating the solution.

Wanting contact
I’m wanting contact
I’m wanting contact with you
Shake those hands, shake those hands

The Contact Tool > Initial Information is another tool we can use to help us obtain accurate solutions in a timely manner.  If you haven’t had the opportunity to use it, please try it out.  I can’t guarantee that it will trigger fond memories, but maybe you’ll have an enjoyable song playing in your head.

Murphy’s Law of Convergence

Ted Harris was working at tech support problem and got this convergence graph.  Ouch!

He calls it “Murphy’s Law of Convergence”

Jason called it “Asymptotic Sisyphaen Convergence”

Doug commented:

“It’s like a pinball machine.  Just shake the monitor a little, but not too much, don’t want to trip the tilt sensor. “

I call it annoying.  Share your names or comments for this type of convergence below.

clip_image002

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