Fixing Cavern No. 10
The premise - Cavern No. 10
You are a dwarven builder in the fourth age of middle earth. You have been hired to fix the mess in the mines of Moria. In particular the total lack of working bridges. You are going to have to build them yourself. They do not need to be perfect but the bridges do need to be able to support a constant flow of material and people. Whilst investigating the area, you notice there are still quite a few rocks around that you can make use of. They are fixed points that will not move. This problem takes place in the tenth cavern.
The tools
A wizard has also agreed to help you out. He has used his "magic" (you're fairly sure he's not supposed to do that) to create a bridge-building machine that will speed up your work. To operate the machine you have to supply a design and it will build the resulting bridge. The machine builds AND tests the bridge for you. The bridge is considered BROKEN if one, or more, beams are broken during testing. The machine deletes any bridges that break.
You have been given a library of 16 materials to use. The bridge-building machine has the words "BUILDYMCBUILDFACE" emblazoned on its side in all-caps. Har har.
Note: If you are unfamiliar with JupyterLab, please read this article first JupyterLab Quick Start.
Assumptions
You can make a few simple assumptions:
- This is a static problem
- All units are in SI, and metric.
- Middle earth is full of British people, as you know, so is it that much of a surprise?
- The gravitational acceleration in middle-earth is 9.81 m/s/s
- The remaining rock in any chamber is the rock the Balrog couldn't break and so won't break no matter what
- Your bridge is "broken" if even one beam experiences stress exceeding the yield strength
Specification
- You must construct a single continuous steel bridge between the entrance and exit
- Beams can only be constructed between immediately adjacent nodes on the grid (unpopulated nodes are denoted by
·on the visualisation) - Nodes on the load path will all be subject to 1 metric ton of weight each, simultaneously
- The load path is one continuous series of connected nodes starting at node 1 and ending at node 2 (denoted by
1and2respectively on the visualisation). - The load path can only climb or descend via diagonals. NO VERTICAL STEPS!
- The load path may not contain any rock nodes (denoted by
oon the visualisation). - You must minimise the cost of the design
How to begin
- Open the environment in which you are going to be working, with the
ansyscodefestlibrary installed. - Make sure you have imported the
ansyscodefestlibrary. - Run
challenge = acf.Start.codefest(10)to get the codefest challenge object.- This fetches the tenth chamber challenge of the codefest.
- Make sure you request the correct challenge for the problem you are tackling!
- The
challengeobject is an instance of theBuildyMcBuildFaceclass and has all the methods you will need for the following tasks.
import ansyscodefest as acf
challenge = acf.Start.codefest(10)
Note on the ansyscodefest package
Access the reference documentation here:
If you get stuck on the Python during the codefest you can always use the built-in Python function help to access the documentation for a command or class.
For example, try running the cell below to access the documentation for the Beam Cross-section enum object.
help(acf.BeamXn)
Additionally, if you don't know what methods are available on a particular object you can use the "tab-complete" operation to see. Try typing acf. into a cell (make sure the cursor is just after the .) then hit the tab button to see the options available to you. If there are no options, then the only option will be filled in (or nothing will appear if nothing is there or there is a mistake in what you have typed). You can type additional characters and tab-complete will match possible options with those.
Try using tab-complete and help to access the documentation for the following objects:
acf.BeamXnchallengechallenge.submit_design
Additionally, print the following to screen to see what you get.
acf.STEELacf.CONCRETEacf.MATERIALS
acf.MATERIALS is a dictionary of available materials, indexed by ID.
The Challenge
When you arrive at Moria, the cavern you find is bigger than you thought. BUILDYMCBUILDFACE does a scan of the environment and stores the geometry.
View the geometry of the chamber by running
challenge.display_problem()
**The numbers "1" and "2" represent your entrance and exit respectively. "o" represent rocks. BULDYMCBUILDFACE has also stored a list of all the rock coordinates and their node numbers. **
You can see the list of the nodes given in the cavern by calling
challenge.get_fixed_nodes()
It's at this point you wonder how to start. Maybe the machine has some ideas? And you can hardly believe it, but there's a button on it's side that says SUGGEST DESIGN.
design = challenge.suggest_design()
To your surprise it really does suggest a design for you, but less to your surprise, it is over-engineered and very expensive. Refuge in unlimited-budget evidently.
Example Design
Given the following chamber, and design, what would the final bridge look like?
Chamber
1 · 2
· + ·
· o ·
As with the chamber map above, the origin of the coordinate system is at the centre of your example. In this example, this node has been replaced with a +.
The bridge-building machine only accepts one format: a Python dictionary. This is the format your suggested design comes in. It looks like this and comes with some simple rules.
{
'nodes': [],
'beams': [],
'load_path': [],
'cross-section': []
'dimensions': []
}
- Nodes are provided as a list
[node_number, x_coord, y_coord]. - Beams are provided as a list
[start_node, end_node] - The load path is provided as a list of node numbers and must start/end with 1/2.
- The cross-sections, dimensions, and materials of each beam must be provided, also as lists.
- They are in the same order as their corresponding beams in the beam list
- Materials are provided as integer IDs (see
acf.MATERIALSfor the full collection)
- Your design must not redefine any pre-existing nodes! (e.g. rocks)
- The load path must be one continuous path of beams between nodes 1 and 2.
Our example above would look like this:
{
'nodes': [[3, 0, 1], [4, 0, 0]],
'beams': [[1, 3], [3, 2], [3, 4], [4, 10]],
'load_path': [1, 3, 2],
'cross-section': [acf.BeamXn.CIRCLE, acf.BeamXn.CIRCLE, acf.BeamXn.CIRCLE, acf.BeamXn.CIRCLE],
'dimensions': [[0.05], [0.05], [0.05], [0.05]],
'materials': [1, 1, 1, 1]
}
Submit your lowest-costing design here.
Accompanying Material
After thinking about the challenge ahead you notice a button the side of the machine you didn't see before. You press it and a keyboard drops out the bottom of the machine. At the same time a pane drops off one side to reveal a Python screen, with a template script using PyAnsys already filled out.
from ansys.mapdl.core import launch_mapdl
import ansyscodefest as acf
design = {'nodes': [],
'beams': [],
'load_path': [],
'cross-section': [],
'dimensions': []}
fixed_nodes = []
# start mapdl and clear it
mapdl = launch_mapdl()
mapdl.clear() # optional as MAPDL just started
mapdl.units("SI") # SI - International system (m, kg, s, K).
mapdl.prep7()
mapdl.antype("STATIC")
mapdl.et(1, "BEAM188")
mapdl.mp("EX", 1, acf.STEEL.elastic_modulus)
mapdl.mp("PRXY", 1, acf.STEEL.poissons_ratio)
mapdl.mp("DENS", 1, acf.STEEL.density)
for node in design['nodes']:
mapdl.n(node[0], node[1], node[2], 0)
# Note: We have to include 1, 2 and the rock nodes we want to use
for node in fixed_nodes:
mapdl.n(node[0], node[1], node[2], 0)
beams = []
for beam in design['beams']:
# Set beams and materials and cross sections etc on a beam-by-beam basis
mapdl.mat(1) # material ID that we set up earlier
mapdl.sectype(1, "BEAM", "CSOLID")
mapdl.secdata(design['dimensions'][0])
beams.append(mapdl.e(beam[0], beam[1]))
mapdl.nplot(True,cpos='xy')
# constrain fixed nodes by removing their degrees of freedom
mapdl.nsel("ALL")
for n in fixed_nodes:
mapdl.d(n[0], "ALL")
# Apply the 1 ton of force to each node
for node in design['load_path']:
mapdl.f(node, "FY", -9.81*1000.)
mapdl.finish()
mapdl.run("/SOLU")
mapdl.solve()
mapdl.finish()
mapdl.post1()
mapdl.set('LAST')
for beam_num in beams:
equiv = mapdl.get_value('secr', beam_num, 's', 'eqv', 'max')
print(f'Beam Number: {beam_num} '
f'experienced max equivalent stress of: {equiv} Pa')
mapdl.finish()
# This command is arguably the most important!
# Make sure it gets called when you're done otherwise your
# MAPDL instances will hang forever!
mapdl.exit()
It seems the machine will let you use Ansys to run your own tests on designs.
You find the provided code won't run as is. You need to provide a design first, and you need to tell it about nodes 1 and 2 and the rocks in the chamber as well.
Hint: Try using the
challenge.get_fixed_nodes()method.
Finally, you can easily check the cost of a proposed design by running the calculate_design_cost(design) method.
>>> challenge.calculate_design_cost(design)
1230.09
The cost per kg of each material can be found by accessing the Material object.
>>> acf.STEEL.price_per_kg
1.2
The price is given in the well-known currency "Dollarydoos".
Hints & Tips
If you are new to working with files in Python you should consider reading this article Quick Start Guide To Working With Text Files in Python on the Ansys Developer Portal that is a quick start guide to the topic for beginners.
The design dictionary you submit should look like this:
design = {
'nodes': [],
'beams': [],
'load_path': [],
'cross-section': [],
'dimensions': []
}
(But with the empty lists filled in.)
You can check your design by running
bridge = challenge.build_bridge(design, plot=False)
success, feedback, beams = bridge.assess_for_breaks()