Here we will continue with C-Shaped magnet described in the Lesson 1. To go further, now we consider all the geometric properties as variable. Our model becomes fully parametric.
The discussion is divided into six steps:
Step 1: Problem formulation
First of all we learn the problem formulation and the sketch of geometry model.
Step2: Program structure
Here we discuss the program structure, user interface and common variables declaration.
Step 3: Creating a new QuickField problem
Here we learn how to create a new problem and set its properties.
Step 4: Building a geometric model.
Now we have to create a new geometric model document, build the geometric model, assign
labels to the blocks and edges, generate the mesh and save it.
Step 5: Working with data labels.
The next step is assigning values to the field sources, boundary condition and media properties for each model label.
Step 6: Analyzing the solution
Now is time to solve the problem and calculate the magnetic force, acting on the steel keeper.
What's More:
How we can develop the program further.
You can see the Visual Basic version of this sample in the
..\ActiveField\Tutorial\Lesson2\VB_Code\.. folder. Even if you don't have Visual Basic
6.0 installed you can run the compiled example Lesson2.exe for better understanding the following text.
There is also VBA (MS Excel) version. You can find it here: ..\ActiveField\Tutorial\Lesson2\VBA_Code\Magn1_L2.xls
Opening this Excel Workbook please choose Enable Macros when asked. You can view the code by choosing Tools->Macro->Visual Basic Editor command or simply press Atl+F11.
In the following chapters will discuss the Visual Basic (VB) version of our small project. It contains a dialog form as a user interface. Almost all issues discussed here are still valid if you use Visual Basic for Application (VBA) as a development platform. The only difference are user interface issues, but they are not in the focus of our discussion.
The VB project consists of two modules: the form module MainDialog a the standard module called Simulation. The last one contains almost all procedures except the user interface tasks. It begins with the declaration of some common variables:
' Set of geometric dimensions
Public yokeWidth As Double
Public yokeHeight As Double
Public maghetWidth As Double
Public magnetHeight As Double
Public keeperWidth As Double
Public keeperHeight As Double
Public airGap As Double
' Magnet coercive force
Public Hc As Double
' Stored QF pictures to show in the MainDialog
Public geomPicture As StdPicture
Public resultPicture As StdPicture
Public sketchPicture As StdPicture
Private Const PI As Double = 3.1415926
' The most common QuickField objects: Application and Problem
Private QF As QuickField.Application
Private prb As QuickField.Problem
The Calculate button on the main application form calls the DoCalculation procedure:
Public Sub DoCalculation()
Dim force As Double
Set QF = CreateObject("QuickField.Application")
QF.DefaultFilePath = VB.App.Path & "\Magn1"
CreateProblem ' Create a new problem: See Step1
BuildGeometry ' Building a geometry model: See Step2
SetData
' Defining physical properties : See step 3
Solve
' Solve the problem
ViewResults ' View and save the field picture
force = CalculateForce() ' Calculate mechanical force
MainDialog.DisplayForce force ' and display it
QF.Quit ' Finish the QuickField
Set QF = Nothing
End Sub
Concerning the internal structure of our small application, we will learn following things:
Creating of the Problem is very straightforward task: first we add a new empty problem by the Add method of the Problems collection, and then set desired properties for it. The last thing we have to do with new problem is saving it to the disk file. Newly created problem does not have a file name, so we should use SaveAs method.
Sub CreateProblem()
|
|
First let us declare some variables that we use working with the geometric model:
Sub BuildGeometry()
Dim mdl As QuickField.Model ' The model document
Dim shp As QuickField.ShapeRange ' Temporary collection of geometric objects
Dim yBase As Double ' Some geometric values
Dim xBase As Double
End Sub
Then we create a new empty model document and just save it. For saved document we use the filename that the problem refers to. So, just after saving QuickField establishes the link between problem and model documents. The benefit of establishing the link early is that the problem setting for coordinates and length unit will be applied to the model automatically.
Sub BuildGeometry()
.........
Set mdl = QF.Models.Add
mdl.SaveAs prb.ReferencedFile(qfModelFile)
........
End Sub
Now we are ready to start building the edges constituting our model. The AddEgde method we will use belongs to the Shapes collection, so it seems convenient to place all these commands to the With..End With brackets:
Sub BuildGeometry()
........
Set mdl = QF.Models.Add
mdl.SaveAs prb.ReferencedFile(qfModelFile)
With mdl.Shapes
' the Yoke
........
' Right Magnet
........
' Left Magnet
........
' Steel Keeper
........
' Surrounding Air
........
' Set Mesh Spacing
........
' Generate the Mesh
.BuildMesh
End With
' Store the picture to show in the MainDialog
..........
' Save the complete model
mdl.Save
End Sub
The comments (in green) put into the With block outline our next work with several parts of model geometry. When finish with building the model we will get the model picture to show it on the main screen (see discussion later) and save the model document again, this time by simple Save method.
Now consider building of the steel yoke block. We use here the geometric dimension variables, declared at the global level and set by user in the MainDialog form (see Declarations section)
With mdl.Shapes
' The Yoke
Set shp = .AddEdge(QF.PointXY(-yokeWidth / 2, 0),
_
QF.PointXY(yokeWidth / 2, 0))
.AddEdge QF.PointXY(yokeWidth / 2, 0), _
QF.PointXY(yokeWidth / 2, yokeHeight)
.AddEdge QF.PointXY(yokeWidth / 2, yokeHeight), _
QF.PointXY(-yokeWidth / 2, yokeHeight)
.AddEdge QF.PointXY(-yokeWidth / 2, yokeHeight), _
QF.PointXY(-yokeWidth / 2, 0)
shp.Left.Label = "Steel"
..........
There is the sequence of four AddEgde command builds a rectangular block. Please, note the way used to assign label to the block: we remember an edge (generally speaking, group of edges) built by the first command in a shp variable. When the block is completely built, we can refer to it as a block, located from the left side of the shp edge by the shp.Left property. The Label property is used to assigning label to the block.
All other rectangular blocks are built in a similar way. Please, note that the left magnet block is build in such a way that it located from the right (not left) side of the edge.
' The Yoke
..............
' Right Magnet
Set shp = .AddEdge(QF.PointXY(yokeWidth / 2, yokeHeight), _
QF.PointXY(yokeWidth / 2, yokeHeight + magnetHeight))
.AddEdge QF.PointXY(yokeWidth / 2, yokeHeight + magnetHeight), _
QF.PointXY(yokeWidth / 2 - maghetWidth, yokeHeight + magnetHeight)
.AddEdge QF.PointXY(yokeWidth / 2 - maghetWidth, yokeHeight + magnetHeight), _
QF.PointXY(yokeWidth / 2 - maghetWidth, yokeHeight)
shp.Left.Label = "ALNICO down"
' Left Magnet
Set shp = .AddEdge(QF.PointXY(-yokeWidth / 2, yokeHeight), _
QF.PointXY(-yokeWidth / 2, yokeHeight + magnetHeight))
.AddEdge QF.PointXY(-yokeWidth / 2, yokeHeight + magnetHeight), _
QF.PointXY(-yokeWidth / 2 + maghetWidth, yokeHeight + magnetHeight)
.AddEdge QF.PointXY(-yokeWidth / 2 + maghetWidth, yokeHeight + magnetHeight), _
QF.PointXY(-yokeWidth / 2 + maghetWidth, yokeHeight)
shp.Right.Label = "ALNICO up"
' Steel Keeper
yBase = yokeHeight + magnetHeight + airGap
Set shp = .AddEdge(QF.PointXY(-keeperWidth / 2, yBase),
_
QF.PointXY(keeperWidth / 2, yBase))
.AddEdge QF.PointXY(keeperWidth / 2, yBase), _
QF.PointXY(keeperWidth / 2, yBase + keeperHeight)
.AddEdge QF.PointXY(keeperWidth / 2, yBase +
keeperHeight), _
QF.PointXY(-keeperWidth / 2, yBase + keeperHeight)
.AddEdge QF.PointXY(-keeperWidth / 2, yBase +
keeperHeight), _
QF.PointXY(-keeperWidth / 2, yBase)
shp.Left.Label = "Steel Keeper"
' Surrounding air
yBase = yokeHeight + magnetHeight + airGap + keeperHeight
xBase = (yokeWidth + keeperWidth) * 1.5
Set shp = .AddEdge(QF.PointXY(-xBase, -yBase),
QF.PointXY(xBase, -yBase))
.AddEdge QF.PointXY(xBase, -yBase), QF.PointXY(xBase, 2 * yBase)
.AddEdge QF.PointXY(xBase, 2 * yBase),
QF.PointXY(-xBase, 2 * yBase)
.AddEdge QF.PointXY(-xBase, 2 * yBase),
QF.PointXY(-xBase, -yBase)
shp.Left.Label = "Air"
.Boundary(qfOuterOnly).Label = "Zero"
Please note the last line of code for the surrounding air block. It refers to edges constitute the outer border of the area by Boundary property of the Shapes object. We need to assign them a Zero label.
Our next step is to set the mesh spacing values to some vertices and that build the mesh. For simplicity sake we employ a straightforward strategy: assign a small spacing value to all corners of the steel parts and a big spacing (four times bigger than small one) to the outer corners. The auxiliary function fMin returns the smaller of its argument.
When the mesh spacing is set the mesh itself is generated by a single command: BuildMesh method of the Shapes object.
With mdl.Shapes
............
' Set Mesh Spacing
Dim sp As Double
sp = fMin(magnetHeight, airGap) / 5
.LabeledAs(Block:="Steel Keeper").Spacing = sp
.LabeledAs(Block:="Steel").Spacing = sp
.LabeledAs(Block:="ALNICO up").Spacing = sp
.LabeledAs(Edge:="ALNICO down").Spacing = sp
.LabeledAs(Edge:="Zero").Spacing = sp * 4
' Generate the mesh
.BuildMesh
EndWith
Now we have to do only the auxiliary task: store a picture of the geometric model to show it later in the picture box on the MainDialog form. To do it we get a ModelWindow object (win variable) represents the MDI window shows the geometric model. Then we try to make it form close to a square, because our target picture box control is of square form. It is rather hard to do that exactly because we need to know the size of window client area, whereas Width and Height properties of the ModelWindow object returns the size of the whole window.
The Zoom method of the ModelWindow with no parameters sets the scale to view the whole model. Then we can use the GetPicture method that puts the picture to the Windows clipboard like the Edit->Copy command does. The next line of code gets a picture from the clipboard and stores it in the geomPicture object (declared as StdPicture)
..........
' Store the picture to show in the MainDialog
Dim win As QuickField.ModelWindow
Set win = mdl.Windows(1)
win.WindowState = qfNormal
win.Height = win.Width + 10
win.Zoom
win.GetPicture
Set geomPicture = Clipboard.GetData
' Save the complete model
mdl.Save
End Sub
Now the geometric model is ready for further using.
Our current task is to set appropriate physical values to all the labels we have defined in the model. First have a look at the whole procedure below, and then we discuss some important points.
Sub SetData()
Dim lab As QuickField.Label
Dim elem As Variant
' First set properties for block labels
Dim cntBlock As QuickField.LabelBlockMS
For Each elem In prb.Labels(qfBlock)
Set lab = elem
Set cntBlock = lab.Content
Select Case lab.Name
Case "Air"
cntBlock.Kxx = 1
cntBlock.Kyy = 1
Case "Steel", "Steel Keeper"
Dim spl As QuickField.Spline
Set spl = cntBlock.CreateBHCurve
spl.Add QF.PointXY(0.73, 400)
spl.Add QF.PointXY(0.92, 600)
spl.Add QF.PointXY(1.05, 800)
spl.Add QF.PointXY(1.15,
1000)
spl.Add QF.PointXY(1.28,
1400)
spl.Add QF.PointXY(1.42,
2000)
spl.Add QF.PointXY(1.52,
3000)
spl.Add QF.PointXY(1.58,
4000)
spl.Add QF.PointXY(1.6, 6000)
cntBlock.Spline = spl
Case "ALNICO up", "ALNICO down"
If StrComp(lab.Name,
"ALNICO up") = 0 Then
cntBlock.Coercive = QF.PointRA(Hc, PI / 2)
Else
cntBlock.Coercive = QF.PointRA(Hc, -PI / 2)
End If
Set spl = cntBlock.CreateBHCurve
spl.Add QF.PointXY(0.24,
27818 - Hc)
spl.Add QF.PointXY(0.4, 47748
- Hc)
spl.Add QF.PointXY(0.5, 67641
- Hc)
spl.Add QF.PointXY(0.6, 93504
- Hc)
spl.Add QF.PointXY(0.71,
127324 - Hc)
spl.Add QF.PointXY(0.77,
147218 - Hc)
cntBlock.Spline = spl
End Select
lab.Content = cntBlock
Next
' The only edge label
Dim cntEdge As QuickField.LabelEdgeMS
Set cntEdge = prb.Labels(qfEdge).Item("Zero").Content
cntEdge.Dirichlet = 0
prb.Labels(qfEdge).Item("Zero").Content = cntEdge
' Saving data document
prb.DataDoc.Save
End Sub
To look through the block labels defining in the model we employ the Visual Basic For Each loop. There are several way to get the label collection: from the Problem object or from the DataDoc object, represents the QuickField data document (as you remember, with a problem can be associated up to two data documents). In our case, the data document is now empty, and the only way to get the collection of labels is the Labels property of the Problem object. It is used with parameter of QfShapes type denoted which of three collection we want to iterate.
Please note that VB requires the loop variable elem in the For Each construction being of Variant type. So, after receiving the next element from the collection we cast it to the Label type by assigning it to the lab variable. All operations with the content of an individual label we do with an object, accessible by the Content property of the Label object. Exact type of that object depends upon problem and label types as described in the DataDoc topic. When finished with the Content, we must put it back to the parent Label by assigning it to the Label's Content property.
Dim lab As QuickField.Label
Dim elem As Variant
Dim cntBlock As QuickField.LabelBlockMS
For Each elem In prb.Labels(qfBlock)
Set lab = elem
Set cntBlock = lab.Content
...........
lab.Content = cntBlock
Next
Now lets consider parameters setting for each individual label. The most simple looks the code for linear media, like "Air" label:
Case "Air"
cntBlock.Kxx = 1
cntBlock.Kyy = 1
Here we have to assign the relative magnetic permeability value to both components of the permeability tensor. An optional parameter of the Kxx (and Kyy) property allows to deal also with absolute values of permeability.
Working with a label describing the saturated media, we have to create and define a Spline object.
Case "Steel", "Steel Keeper"
Dim spl As QuickField.Spline
Set spl = cntBlock.CreateBHCurve
spl.Add QF.PointXY(0.73, 400)
spl.Add QF.PointXY(0.92, 600)
spl.Add QF.PointXY(1.05, 800)
spl.Add QF.PointXY(1.15, 1000)
spl.Add QF.PointXY(1.28, 1400)
spl.Add QF.PointXY(1.42, 2000)
spl.Add QF.PointXY(1.52, 3000)
spl.Add QF.PointXY(1.58, 4000)
spl.Add QF.PointXY(1.6, 6000)
cntBlock.Spline = spl
The label is empty now, so we have to first create a new spline by the CreateBHCurve method. Then we add each spline node individually by the Add method and finally assign the spline to the label's content.
With a non-linear permanent magnet we should also set the coercive force value. On the code fragment below we first set the coercive force value and then add the spline nodes. In this case we must subtract the coercive force from the magnetic field value to put the curve into the second quadrant.
Case "ALNICO up", "ALNICO down"
If StrComp(lab.Name, "ALNICO up") = 0 Then
cntBlock.Coercive = QF.PointRA(Hc, PI / 2)
Else
cntBlock.Coercive = QF.PointRA(Hc, -PI / 2)
End If
Set spl = cntBlock.CreateBHCurve
spl.Add QF.PointXY(0.24, 27818 - Hc)
spl.Add QF.PointXY(0.4, 47748 - Hc)
spl.Add QF.PointXY(0.5, 67641 - Hc)
spl.Add QF.PointXY(0.6, 93504 - Hc)
spl.Add QF.PointXY(0.71, 127324 - Hc)
spl.Add QF.PointXY(0.77, 147218 - Hc)
cntBlock.Spline = spl
It is possible first to define the curve, located in the first quadrant, and then set the coercive force.
With edge label we do not need to look through the edge labels collection because we have only one edge label. The following code puts zero Dirichlet condition to the "Zero" label:
' The only edge label
Dim cntEdge As QuickField.LabelEdgeMS
Set cntEdge = prb.Labels(qfEdge).Item("Zero").Content
cntEdge.Dirichlet = 0
prb.Labels(qfEdge).Item("Zero").Content = cntEdge
The Dirichlet property set and returns Dirichlet boundary condition that do not depends upon coordinates. If you nee to set Dirichlet condition as a linear function of coordinates, please you the DirichletLinear property.
The last line of the SetData procedure saves the modified data document. It has its file name assigned when the problem was created, so we only need a Save method.
When the model and data are ready, we are able to solve a problem and start to analyze result:
Sub Solve()
If prb.CanSolve Then prb.SolveProblem
If prb.Solved Then prb.AnalyzeResults
End Sub
The AnalyzeResults method loads the solution for analyzing and creates the first FieldWindow object, represents a field picture window. After that we can get the Result object, which gives access to analyzing capabilities.
In our program we have to do two tasks with the problem solution: get the field picture and calculate the force acting to the steel keeper. We get the field picture in a very similar way that we have used for the geometric model picture:
Sub ViewResults()
Dim res As QuickField.Result
Set res = prb.Result
If res Is Nothing Then Exit Sub
Dim win As QuickField.FieldWindow
' Get the field picture window
Set win = res.Windows(1)
' Set the window form close to a square
win.WindowState = qfNormal
win.Height = win.Width + 10
' Set view zoom to see the whole model
win.Zoom
' Put the picture into the clipboard
win.GetPicture
' Store the copied picture
Set resultPicture = Clipboard.GetData
End Sub
To calculate the mechanical force we have to create a contour surrounding the keeper's body. Each FieldWindow object always possesses only one the Contour, even an empty one. We get the Contour object from our FieldWindow object called win and use its AddLineTo method to add lines to the contour. The last AddLineTo call with no parameters builds a line, connecting the last contour point with its starting point.
When the closed contour oriented counter clockwise is ready, we can use the GetIntegral method of the Result object to calculate desired integral value. The force value is really a vector quantities, so being interested only in its absolute value we use its Abs property.
Function CalculateForce() As Double
Dim res As QuickField.Result
Set res = prb.Result
If res Is Nothing Then Exit Function
Dim win As QuickField.FieldWindow
Set win = res.Windows(1)
With win.Contour
.AddLineTo QF.PointXY(-keeperWidth / 2 - maghetWidth,
_
yokeHeight + magnetHeight + airGap / 2)
.AddLineTo QF.PointXY(keeperWidth / 2 + maghetWidth,
_
yokeHeight + magnetHeight + airGap / 2)
.AddLineTo QF.PointXY(keeperWidth / 2 + maghetWidth,
_
yokeHeight + magnetHeight + airGap + keeperHeight * 1.5)
.AddLineTo QF.PointXY(-keeperWidth / 2 - maghetWidth,
_
yokeHeight + magnetHeight + airGap + keeperHeight * 1.5)
.AddLineTo
End With
CalculateForce = res.GetIntegral(qfInt_MaxwellForce).Abs
win.Contour.Delete True
End Function
This very simple application can be considered as an prototype for more developed custom project intended to automate analyzing of some parameterized model. To do it more realistic we obviously have to develop some code for validation user's input. Probably it will be useful to allow the customer to build his or her model from some parameterized "building blocks", implement some methods for optimization an much more.