Commit 383387cf authored by Mark van Turnhout's avatar Mark van Turnhout
Browse files

cast documentation done

parent 7bbce2ee
...@@ -482,13 +482,13 @@ for b = 1:numel(basil) ...@@ -482,13 +482,13 @@ for b = 1:numel(basil)
fprintf(fid,'\n# create ridge\n'); fprintf(fid,'\n# create ridge\n');
fprintf(fid,'p = myModel.parts[''cast'']\n'); fprintf(fid,'p = myModel.parts[''cast'']\n');
fprintf(fid,'e = p.edges\n'); fprintf(fid,'e = p.edges\n');
fprintf(fid,'\n# find hole on cast\n'); fprintf(fid,'# find hole on cast\n');
fprintf(fid,'ed = e.getClosest(coordinates=((%f, %f, %f),))\n',pcw-bcom(1,:)); fprintf(fid,'ed = e.getClosest(coordinates=((%f, %f, %f),))\n',pcw-bcom(1,:));
fprintf(fid,'t = ed[0]\n'); fprintf(fid,'t = ed[0]\n');
fprintf(fid,'\n# loft\n'); fprintf(fid,'# loft\n');
fprintf(fid,'p.ShellLoft(loftsections=((e[0], ), (t[0], )), startCondition=NONE, endCondition=NONE)\n'); fprintf(fid,'p.ShellLoft(loftsections=((e[0], ), (t[0], )), startCondition=NONE, endCondition=NONE)\n');
% surface and sets % surface and sets
fprintf(fid,'# surface and sets\n'); fprintf(fid,'\n# surface and sets\n');
fprintf(fid,'f = myPart.faces\n'); fprintf(fid,'f = myPart.faces\n');
fprintf(fid,'faces = f.getSequenceFromMask(mask=(''[#3 ]'', ), )\n'); fprintf(fid,'faces = f.getSequenceFromMask(mask=(''[#3 ]'', ), )\n');
fprintf(fid,'myPart.Set(faces=faces, name=''cast'')\n'); fprintf(fid,'myPart.Set(faces=faces, name=''cast'')\n');
......
...@@ -558,5 +558,126 @@ for i in range(0, 19): ...@@ -558,5 +558,126 @@ for i in range(0, 19):
# create loft of edge list # create loft of edge list
myPart.ShellLoft(loftsections=((ledges[0], ), (ledges[1], ), (ledges[2], ), (ledges[3], ), (ledges[4], ), (ledges[5], ), (ledges[6], ), (ledges[7], ), (ledges[8], ), (ledges[9], ), (ledges[10], ), (ledges[11], ), (ledges[12], ), (ledges[13], ), (ledges[14], ), (ledges[15], ), (ledges[16], ), (ledges[17], ), (ledges[18], ), ), startCondition=NONE,endCondition=NONE) myPart.ShellLoft(loftsections=((ledges[0], ), (ledges[1], ), (ledges[2], ), (ledges[3], ), (ledges[4], ), (ledges[5], ), (ledges[6], ), (ledges[7], ), (ledges[8], ), (ledges[9], ), (ledges[10], ), (ledges[11], ), (ledges[12], ), (ledges[13], ), (ledges[14], ), (ledges[15], ), (ledges[16], ), (ledges[17], ), (ledges[18], ), ), startCondition=NONE,endCondition=NONE)
\end{lstlisting} \end{lstlisting}
Note that we do not close the cast to form a volume as we did with the leg (figure \ref{abaqus_castLofted}): the cast only limits radial displacement of the soft tissue, not the longitudinal displacement. Note also that the part is initially modelled as `deformable' (line \texttt{2}): we will convert the cast to a rigid body later on.\\
\noindent Next, we make a hole in the cast for the indenter. With the local leg orientation that was calculated earlier for partitioning of the skin, \bas{buildFEM} now calculates 3D coordinates for a `partitioning wire' that is projected onto the cast (similar as before with the assist-disc, figure \ref{abaqus_castWire}). The part of the cast inside the projected edge can then be removed:
\begin{lstlisting}[language=apython, escapechar=!]
# write partitioning wire
pts = [(-2.724500, -7.424954, -0.529141), (-3.159280, -7.252343, -1.342870), !\textnormal{(20 triplets of 3D coordinates)}! ]
myPart.WireSpline(points=pts, mergeType=IMPRINT, meshable=ON, smoothClosedSpline=ON)
myPart.Stitch
# partition cast with wire
f = myPart.faces
pickedFaces = f.getSequenceFromMask(mask=('[#1 ]', ), )
e = myPart.edges
# find wire
ed = e.getClosest(coordinates=((-2.724500, -7.424954, -0.529141),))
pedge=list()
t = ed[0]
pedge.append( t[0] );
# partition
myPart.PartitionFaceByProjectingEdges(faces=pickedFaces, edges=pedge, extendEdges=False)
# find face to delete
f = myPart.faces
fa = f.getClosest(coordinates=((-5.345964, -6.433176, 0.540578),))
dface=list()
t = fa[0]
dface.append( t[0] );
# cut hole
myPart.RemoveFaces(faceList = dface, deleteCells=False)
\end{lstlisting}
Abaqus expects a `list' for \texttt{PartitionFaceByProjectingEdges}, so even if there is only one edge, we put it into a list (lines \texttt{10--13}). When the cast is partitioned with the wire (line \texttt{15}), we find the surface inside the contour and put it into a list (lines \texttt{17--21}). When we delete this partitioned surface (line \texttt{23}), there is a hole in the cast for the indenter.\\
\begin{figure}[p!]\center
\subfloat[\label{abaqus_castWire}]{%
\includegraphics[width=.85\linewidth]{abaqus_castWire.png}}\\
\subfloat[\label{abaqus_castRidge}]{%
\includegraphics[width=.85\linewidth]{abaqus_castRidge.png}}
\caption{The cast in Abaqus (rotated for clarity). With \textbf{(a)} the lofted cast with the partitioning wire (red) and the projected edge that will define the hole (black); and \textbf{(b)} the cast with the hole and ridge. \label{abaqus_castLofted}}
\end{figure}
\noindent Contrary to the assist-disc, we cannot leave the partitioning wire `as is'. If we delete wire after partitioning, also the partition itself is deleted and the hole that we created will be closed again. If we leave it hovering in the model similar to the assist-disc, the simulation will not run. This is because the partitioning wire is modelled in the \texttt{part}, not as an individual \texttt{instance} as the assist-disc.
Therefore we connect the wire to the cast with a loft, creating a `ridge' in the proccess (figure \ref{abaqus_castRidge}):
\begin{lstlisting}[language=apython]
# create ridge
p = myModel.parts['cast']
e = p.edges
# find hole on cast
ed = e.getClosest(coordinates=((-4.377070, -4.263596, 1.000000),))
t = ed[0]
# loft
p.ShellLoft(loftsections=((e[0], ), (t[0], )), startCondition=NONE, endCondition=NONE)
\end{lstlisting}
Note that the indenter can pass freely through the cast (ridge), as long as we do not define any interaction between the indenter and the cast (ridge). \\
\noindent With the geometry defined (figure \ref{abaqus_castLofted}), we can assign a surface and some sets to use later on:
\begin{lstlisting}[language=apython]
# surface and sets
f = myPart.faces
faces = f.getSequenceFromMask(mask=('[#3 ]', ), )
myPart.Set(faces=faces, name='cast')
s = myPart.faces
side1Faces = s.getSequenceFromMask(mask=('[#1 ]', ), )
side2Faces = s.getSequenceFromMask(mask=('[#2 ]', ), )
myPart.Surface(side1Faces=side1Faces, side2Faces=side2Faces, name='cast')
myPart.ReferencePoint(point=(3, 3, 3))
lastRP = myPart.referencePoints.values()
myPart.Set(referencePoints=lastRP, name='castRP')
\end{lstlisting}
The (inside) surface of the cast needs to be defined for modelling contact with the skin later on; and because we are going to convert the cast to a rigid body, we need to define a reference point (lines \texttt{9-11}). The actual position of this reference point is irrelevant and \bas{buildFEM} puts it at \texttt{(3, 3, 3)} (line \texttt{9}).\\
\noindent The cast needs to be meshed before it can be converted to rigid surface because of the geometry (similar to the rigid surface of the tibia). So we assign a section to the cast, instance it in the model assembly, and mesh it with linear shell elements:
\begin{lstlisting}[language=apython]
# cast section assignment
myModel.SurfaceSection(name='cast', useDensity=OFF)
p = myModel.parts['cast']
region = p.sets['cast']
p = myModel.parts['cast']
p.SectionAssignment(region=region, sectionName='cast', offset=0.0, offsetType=MIDDLE_SURFACE, offsetField='', thicknessAssignment=FROM_SECTION)
# instance cast
a1 = myModel.rootAssembly
p = myModel.parts['cast']
a1.Instance(name='cast-1', part=p, dependent=OFF)
# mesh cast
elemType1 = mesh.ElemType(elemCode=SFM3D4, elemLibrary=STANDARD)
elemType2 = mesh.ElemType(elemCode=SFM3D3, elemLibrary=STANDARD)
a = myModel.rootAssembly
f1 = a.instances['cast-1'].faces
faces1 = f1.getSequenceFromMask(mask=('[#3 ]', ), )
pickedRegions =(faces1, )
a.setElementType(regions=pickedRegions, elemTypes=(elemType1, elemType2))
partInstances =(a.instances['cast-1'], )
a.seedPartInstance(regions=partInstances, size=0.500000, deviationFactor=0.1, minSizeFactor=0.1)
partInstances =(a.instances['cast-1'], )
a.generateMesh(regions=partInstances)
\end{lstlisting}
After the section assignment (lines \texttt{2--6}), the cast can be instanced `as is' (lines \texttt{8--10}).
The size for the seed of the cast mesh (line \texttt{21}) depends on the element order of the leg mesh. For linear elements in the leg, the seed size is equal to that of the leg: \texttt{param.seed}. For quadratic elements, \bas{buildFEM} uses a seed size that is half the seed size of the leg: \texttt{param.seed/2}. This ensures that the node density is similar between the (either linear or quadratic) leg mesh and (linear) cast mesh, which will give Abaqus less trouble with the contact interaction between leg and cast.\\
\noindent Once meshed, we can tie the nodes of the cast into a rigid surface, and define the contact interaction between leg and cast:
\begin{lstlisting}[language=apython]
# cast to rigid surface
a = myModel.rootAssembly
region4=a.instances['cast-1'].sets['cast']
a = myModel.rootAssembly
region1=a.instances['cast-1'].sets['castRP']
myModel.RigidBody(name='rigidCast', refPointRegion=region1, tieRegion=region4)
# cast contact
a = myModel.rootAssembly
region1=a.instances['cast-1'].surfaces['cast']
region2=a.instances['leg-1'].surfaces['skin']
myModel.SurfaceToSurfaceContactStd(name='castInteraction', createStepName='Initial', master=region1, slave=region2, sliding=FINITE, thickness=ON, interactionProperty='contact', adjustMethod=NONE, initialClearance=OMIT, datumAxis=None, clearanceRegion=None)
\end{lstlisting}
The cast is converted into a rigid surface similar as the tibia surface (lines \texttt{2--6}). Contact between cast and leg (line \texttt{12}) is modelled with the same contact interaction as that between the indenter and leg.\\
\noindent If you run the main \texttt{basil<basilid>\-\_job\-.py\index{basil<basilid>\_job.py@\texttt{basil<basilid>\_job.py}}}-script up until this point, Abaqus should display something like figure \eqref{abaqus_fullAssembly}.
\begin{figure}[t!]\center
\includegraphics[width=\linewidth]{abaqus_fullAssembly.png}\\
\caption{The full model assembly (including cast) in Abaqus (rotated for clarity). The yellow lines are the symmetry axis of the indenter and assist-disc (construction lines in the sketches), RP's are the reference points for the indenter, bone, and cast. The assist-disc will be dismissed when the simulation is submitted for analysis. \label{abaqus_fullAssembly}}
\end{figure}
\subsection{Loading steps (boundary conditions)}
\ No newline at end of file
...@@ -25,7 +25,7 @@ morekeywords = {WireSpline, ...@@ -25,7 +25,7 @@ morekeywords = {WireSpline,
getSequenceFromMask, getSequenceFromMask,
Set, Set,
side1Faces, side1Faces,
side2faces, side2Faces,
ReferencePoint, ReferencePoint,
referencePoints, referencePoints,
values, values,
...@@ -113,7 +113,15 @@ morekeywords = {WireSpline, ...@@ -113,7 +113,15 @@ morekeywords = {WireSpline,
clearanceRegion, clearanceRegion,
RigidBody, RigidBody,
refPointRegion, refPointRegion,
tieRegion}} tieRegion,
points,
mergeType,
meshable,
smoothClosedSpline,
RemoveFaces,
deleteCells,
Stitch
}}
\lst@definelanguage{basillab} \lst@definelanguage{basillab}
{alsolanguage = Octave, {alsolanguage = Octave,
keywordsprefix = {bas_}, keywordsprefix = {bas_},
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment