You are on page 1of 118

IMPERIAL COLLEGE LONDON

Faculty of Engineering

Department of Civil and Environmental Engineering

Meso-scale predictive structural modelling of the lower limb

Alfred Thibon

September 2011

Submitted in fulfilment of the requirements for the MSc and the Diploma of Imperial College London

Declaration:
This submission is my own work. Any quotation from, or description of, the work of others is acknowledged herein by reference to the sources, whether published or unpublished.

Signature : ___________________________________

Acknowledgments

I would like to thank Dr Phillips for his guidance all along this master thesis. I also would like to thank Luca Modenese for his advice and discussions.

Abstract
Based on previous work from Phillips (Phillips, 2011) a predictive 3D structural model of the femur was developed. Trabecular and cortical elements were represented by truss and shell elements respectively. Using more complex load cases than the first model this one have been loaded by 115 muscles segments and 3 joints. The trabecular cross section area and cortical thickness have been adapted to the loads, following Frosts mechanostat concept, to the elements strains calculated with a finite element program. The predicted bone structure showed good correlation with clinical observations and an improvement from Phillips results. This new development stage confirmed the interests of a meso-scale model compared to the micro and macro-scale approaches. Suggested further improvements are outlined.

Content
Abstract .............................................................................................................................. i Content .............................................................................................................................. ii List of figures .................................................................................................................... v List of tables .................................................................................................................... vii List of appendixes ..........................................................................................................viii I II Introduction ............................................................................................................... 1 Literature review ....................................................................................................... 3 II -1 Anatomy ................................................................................................................ 3 II -1-1 Bone geometry ............................................................................................... 3 II -1-2 Bone structure ................................................................................................ 4 II -1-1 Muscles .......................................................................................................... 6 II -1-1-1 Gluteal region ......................................................................................... 6 II -1-1-2 Thigh....................................................................................................... 9 II -1-1-2-1 Anterior compartment of the thigh ................................................ 10 II -1-1-2-2 Medial compartment of the thigh .................................................. 11 II -1-1-2-3 Posterior compartment of the thigh ............................................... 12 II -1-1-3 Posterior compartment of the leg ......................................................... 13 II -1-1-4 Insertion points of the muscle .............................................................. 13 II -1-2 Joints (put there geometry and rotational centre) ........................................ 14 II -2 Bone modelling ................................................................................................... 15 II -2-1 Bone data ..................................................................................................... 15 II -2-2 Mechanostat ................................................................................................. 16 II -3 Macro and micro-scale approaches ..................................................................... 17 II -4 Structural Optimisation: Biomechanics of the Femur state of the model (Phillips, 2011) ........................................................................................................................... 18 III Developed models ................................................................................................... 21

III -1 Entry data ........................................................................................................... 21 III -1-1 What is needed ........................................................................................... 21 III -1-2 OpenSim Plug-in ........................................................................................ 22 ii

III -2 Femur modelling ................................................................................................ 23 III -2-1 Concept ....................................................................................................... 23 III -2-2 Model.......................................................................................................... 23 III -2-3 Software...................................................................................................... 24 III -2-4 Input file ..................................................................................................... 25 III -2-4-1 Geometry ............................................................................................. 25 III -2-4-2 Coordinate system ............................................................................... 25 III -2-4-3 Boundary conditions ........................................................................... 26 III -2-4-4 Loading ............................................................................................... 26 III -2-4-4-1 Loads from the muscles ............................................................... 26 III -2-4-4-2 Loads from the joints ................................................................... 27 III -2-4-4-3 Loads from the weight and the inertial force ............................... 29 III -2-4-5 MatLab implementation ...................................................................... 29 III -2-5 Mechanostat parameters ............................................................................. 30 III -2-6 Load cases considered ................................................................................ 31 III -2-7 Iterative process .......................................................................................... 32 III -2-8 Results analyses .......................................................................................... 36 IV Results ..................................................................................................................... 37 IV -1 Single load cases ................................................................................................ 37 IV -2 First combination method: one after the other................................................... 38 IV -3 Second combination method: several load case at once .................................... 39 IV -3-1 3 motions; maximal hip force frame .......................................................... 40 IV -3-2 3 motions; 4 frames per motion ................................................................. 41 IV -3-3 3 motions; 10 frames per motion ............................................................... 42 IV -3-3-1 Statistical data ..................................................................................... 42 IV -3-3-2 Cortical thickness ................................................................................ 43 IV -3-3-3 Trabecular architecture ....................................................................... 44 IV -3-3-3-1 On the shaft .................................................................................. 44 IV -3-3-3-2 Inside the femoral head................................................................ 45 IV -3-3-3-3 Inside the distal end of the femur ................................................ 46 IV -3-3-4 Porosity ............................................................................................... 46 IV -4 Summary............................................................................................................ 49 iii

IV -4-1 Reaction forces (RF) and reaction moments (RM) for the frames considered ............................................................................................................... 49 IV -4-2 Statistical data summary ............................................................................ 50 V Discussion ............................................................................................................... 51 V -1 Achievements and remarks ................................................................................. 51 V -1-1 Femoral structure ......................................................................................... 51 V -1-2 Truss elements vs. beam elements for the trabeculae .................................. 51 V -2 Limitations .......................................................................................................... 52 V -2-1 In the mesh .................................................................................................. 52 V -2-1-1 Geometry .............................................................................................. 52 V -2-1-2 Joints loading ....................................................................................... 53 V -2-2 In the data .................................................................................................... 53 V -2-2-1 Loads from the muscle ......................................................................... 53 V -2-2-2 Reaction forces and moments .............................................................. 54 V -2-3 In the algorithm ........................................................................................... 55 V -2-3-1 Trabecular elements ............................................................................. 55 V -2-3-1-1 Orientation .................................................................................... 55 V -2-3-1-2 Element types ................................................................................ 55 V -2-3-2 Cortical bone ........................................................................................ 56 V -2-3-3 Initial conditions................................................................................... 56 V -2-3-4 Optimization ......................................................................................... 57 V -3 Further works ...................................................................................................... 57 VI Conclusion .............................................................................................................. 59 VII Bibliography........................................................................................................ 60

iv

List of figures
Figure 1-Femur (from Dr F. Gaillard) .............................................................................. 3 Figure 2-Groups of stresses in the proximal femur (Singh et al., 1970) ........................... 5 Figure 3-Left femur, coronal section (Image from: Smithsonian National Museum of Natural History) ................................................................................................................ 5 Figure 4-Muscles in the gluteal region (Drake et al., 2004) ............................................. 7 Figure 5-Transverse section through the mid-thigh (Drake et al., 2004) .......................... 9 Figure 6-Muscles of the anterior compartment of the thigh (Drake et al., 2004) ........... 10 Figure 7-Muscles of the medial compartment of the thigh (Drake et al., 2004) ............. 11 Figure 8-Muscles of the posterior compartment of the thigh (posterior view) (Drake et al., 2004).......................................................................................................................... 12 Figure 9-Muscles in the posterior compartment of leg. Superficial group: A. Posterior view. B. Lateral view. C. Deep group (Drake et al., 2004)............................................. 13 Figure 10-Hip joint (Drake et al., 2004) ......................................................................... 14 Figure 11-Knee joint (Drake et al., 2004) ....................................................................... 15 Figure 12-Mechanostat concept by Frost (Frost, 2003) .................................................. 16 Figure 13-Adopted values of strain associated with the dead zone, bone resorption, the lazyzone, and bone apposition, as well as the target strain (Phillips, 2011) ................... 17 Figure 14-Finite element model of the femur showing boundary conditions and loading. JCF is the joint contact force, ABD is the abductor muscle force, and ITB is the iliotibial band force. (Phillips, 2011) .............................................................................. 19 Figure 15-Sawbones fourth generation composite femur (#3403) (Size: a) 455 mm b) 45 mm c) 31 mm d) 135 degrees e) 27 mm f) 74 mm g) 13 mm canal) .............................. 23 Figure 16-Parts to apply joint loads ................................................................................ 28 Figure 17-First Abaqus input file .................................................................................... 30 Figure 18-Iterative process simplified flowchart ............................................................ 33 Figure 19-Cut of the femur at y=-200 mm for the walking load case ............................ 37 Figure 20-Cut of the femur at y=-200 mm for the three successive load cases .............. 39 Figure 21-Cut of the femur at y=-200 mm for the three simultaneous load cases .......... 41 v

Figure 22-Cut of the femur at y=-200 mm for 4 frames per motion x 3 motions ........... 42 Figure 23-Cut of the femur at y=-200 mm for 10 frames per motion x 3 motions ......... 43 Figure 24-Cortical thickness ........................................................................................... 44 Figure 25-Cut of the femur at y=-200 mm for 10 frames per motion x 3 motions with trabecular and cortical elements ...................................................................................... 44 Figure 26-3mm cut inside the femoral head ................................................................... 45 Figure 27-Coronal and sagittal cut through the distal femur .......................................... 46 Figure 28-Porosity plots on 5mm thick slices on the x-axis ........................................... 48

vi

List of tables
Table 1-Muscles in the gluteal region (Drake et al., 2004) ............................................... 8 Table 2- Muscles of the anterior compartment of the thigh (Drake et al., 2004)............ 10 Table 3- Muscles of the medial compartment of the thigh (Drake et al., 2004) ............. 12 Table 4- Muscle of the posterior compartment of the thigh (Drake et al., 2004) ........... 12 Table 5-Mechanostat values (Frost, 2003) ...................................................................... 17 Table 6Muscles taken into account in the model .......................................................... 27 Table 7-Statistical results for single load cases............................................................... 38 Table 8-Statistical data for the first combination method ............................................... 38 Table 9-Statistical data for the three load cases-comparison of the combination methods ......................................................................................................................................... 40 Table 10-Statistical data for the second combination method-1 and 3 frames per motion ......................................................................................................................................... 41 Table 11-Statistical data for the final model ................................................................... 43 Table 12-Reaction forces and moment at the knee ......................................................... 49 Table 13-Statistical data summary .................................................................................. 50 Table 14-Initial conditions tests results .......................................................................... 56

vii

List of appendixes
Appendix A Source code OpenSim plug-in Appendix B Source code joint parts Appendix C Source code first input file Appendix D Source code iterative process Appendix E Python script: extract data from the Abaqus output file

viii

I Introduction
The aim is to produce a meso-scale, computationally efficient, predictive structural model of the femur. Once such a model will have reached its optimal state it will be possible to use it for several applications. It will be possible to determine the effect on a bone of a change in its loading, due to muscles problems or to a stiffening of a part of the bone due to joint surgery. It will also be possible to study bone failure, due to accidental loading, or assess the effect of osteoporosis. The femur is a long bone located in the thigh, articulated between the knee and the hip. It supports most of the body weight and has to adapt itself to different load cases. Its section is almost circular along its length. At the upper end, at the hip joint, it has a rounded head shifted from the main axis by a short neck and at the lower end, at the knee joint, two condyles create the joint surface. The muscles are attached on the greater trochanter on the upper part of the femur, on the two epicondyles at the lower end, and along the length of the femur (Gray & Standring, 2008). This report presents a meso-scale predictive modelling of this bone, based on Phillips works (Phillips, 2011). The aim of this study is to provide a process to predict the internal and external structures of the femur under different load cases. The applications of such a model are numerous as various diseases or prosthesis can change the loads on the limbs in the human body. It is part of a global project of building a full musculoskeletal model which would have multiple usages in the fields of orthopaedic and implant designing. The femur is made from two different kinds of bone. The external envelop is made of cortical bone, a dense and shell-like structure. The internal structure is made of trabecular bone and looks like a sponge. These two different structures combine themselves to optimize the structure and reduce the amount of material needed to resist the loads applied every day on the femur. The structure of the femur varies from the head to the lower extremity. The two extremities, where are located the joint, are mainly made of trabeculae, to gain in joint surface without increasing the weight, whereas the 1

shaft has a hollow section, mainly made of cortical bone, and is optimized to resist bending moments. The structure of the bone can adapt itself to a change in the loads it resists daily. Frost proposed the concept of mechanostat, saying that the section of a bone element increases or decreases depending on the strain it has undergone (Frost, 2003). This concept is used to predict the structure of the femur in the model developed throughout this report. Phillips developed a preliminary iterative 3D meso-scale structural model of the femur (Phillips, 2011). In this model he managed to produce a model in which the trabecular framework and the cortical thickness distribution were similar to the clinical observations. The trabecular cross sectional areas and the cortical thicknesses were adjusted over successive iterations using Frosts mechanostat. However, if the model was successful predicting the bone structure, Phillips pointed out some limitations in it. This study is the continuation of the work of Phillips whose model had fixed boundary conditions and simplified loads (Phillips, 2011). This study aimed at developed the same kind of model but with less restrictive boundary conditions and a more complete set of loads.

II

Literature review
II -1 Anatomy

II -1-1 Bone geometry The femur is the long bone located in the thigh. Its geometry comprises the

femoral head, which is part of the hip joint, two condyles at the bottom, which are parts of the knee and patella joints, and the shaft. Some features allow the attachment of the muscles, the greater trochanter and the medial and lateral epicondyles can be cited as good examples of these

features. Its purpose is to carry the body weight. It is the strongest human bone. The neck between the shaft and the femoral head allows Figure 1-Femur (from Dr F. Gaillard)

centring the weight under the spine.

II -1-2 Bone structure The bone structure in the long bones can be divided in two different types of sub-structure. The external part of the bone is made from cortical bone whereas the internal part is made of trabecular bone. These structures adapt themselves to the load they carry by changing their thickness and eventually direction. The cortical and trabecular bones are considered as having the same properties at the tissue level (Turner et al., 1999) but different geometrical structures which give them different properties at meso and macro-scale levels (Phillips, 2011). Cortical bone forms the outer layer of the bone. It is a dense structure. The trabecular bone fills the interior of the bone. It has a high porosity and has a sponge like shape. It is composed of strut- and shell-like elements arranged such that the ratio load-bearing capacity/weight is optimized. The femoral structure can be studied on three different parts of the bone: the femoral head, the femoral shaft and the lower extremity, where the femur is articulated with the tibia and patella. The trabecular structure has been studied in the femoral head since the end of the 19th century, starting with Culmann (Culmann, 1886) and von Meyer (Von Meyer, 1887), who believed that trabecular directions follow the tensile and compressive stresses. Wolff in 1986 criticized them and gave the Wolffs law, saying that the trabeculae directions are aligned with the principal stress trajectories which should be orthogonal (Wolff, 1986). Phillips notes that this assumption has not been verified in actual femurs and that an explanation for that could be that the internal structure is optimized to resist not only the compressive and tensile stresses but also a shear effect (Phillips, 2011). Another criticism of these trajectories has been made by Newell (Newell, 1997) based on the works from Dixon (Dixon, 1910). Newell uses the Dixon concept to describe the arrangement of the trabeculae. According to Dixon the arches seen in the classical 2D representation of the proximal femur are simplified sectional profiles of a spiral arrangement of the trabecular bone structure. Anyway, it is commonly accepted that the internal structure of the bone is adapted to the loads it supports and the internal stresses. Five groups of stresses have been defined by Singh, Nagrath and Maini: 4

Figure 2-Groups of stresses in the proximal femur (Singh et al., 1970)

These groups can be observed on actual femur:

Figure 3-Left femur, coronal section (Image from: Smithsonian National Museum of Natural History)

The femoral shaft is mainly made of cortical bone. Its shape is a hollow section, a shape efficient to resist bending moments. The centre is filled with marrow. Still to have an efficient way of carrying the loads with a minimum amount of material the inner structure of the distal femur is adapted. The structure changes from the hollow section of the shape to a larger section to increase the area of contact between the femur and the tibia and thus the stability of the hinge joint. To transmit efficiently the load at the knee joint, without increasing the amount of material, the cortical thickness decreases and the inner structure is filled with trabecular bones, spreading from the shaft and ending perpendicular to the joint surface. These trabecular elements are parallel to each other and braced with perpendicular bars, forming a grid of cubical compartments. Even if the cortical thickness is reduced in the joint areas, it can be thicker at some point where the ligaments are attached. (Gray, 1918) The stresses in the femur can change during ones life and so the bone structure has to be adapted. For example sportsmen have stronger bones in the limb they use more and astronauts have weaker bones when they come back from a long zero-gravity mission. Frost introduced in 2003 a new concept to explain that adaptation, the mechanostat. This notion is developed later on in II -2-2.

II -1-1 Muscles This part is mainly from Grays Anatomy for Students (Drake et al., 2004). The reader is encouraged to go through this book or any other anatomical book for more information. Only the muscles acting on the femur are presented here. These muscles are located within the gluteal region, the thigh and the posterior compartment of the leg. II -1-1-1 Gluteal region This is the region between the proximal end of the femur and the pelvis. The muscles in this region move the femur relatively to the pelvis. They abduct, extend and laterally rotate the femur around the hip joint.

Figure 4-Muscles in the gluteal region (Drake et al., 2004)

Muscle Piriformis

Origin Anterior surface of sacrum between anterior sacral foramina

Insertion Medial side of superior border of greater trochanter of femur Medial side of greater trochanter of femur

Obturator internus

Anterolateral wall of true pelvis; deep surface of obturator membrane and surrounding bone External surface of ischial spine

Gemellus superior

Along length of superior surface of the obturator internus tendon and into the medial side of greater trochanter of femur with obturator internus tendon

Function Laterally rotates the extended femur at hip joint; abducts flexed femur at hip joint Laterally rotates the extended femur at hip joint; abducts flexed femur at hip joint Laterally rotates the extended femur at hip joint; abduction of flexed femur at hip joint

Muscle Gemellus inferior

Origin Upper aspect of ischial tuberosity

Quadratus femoris

Lateral aspect of the ischium just anterior to the ischial tuberosity External surface of ilium between inferior and anterior gluteal lines

Gluteus minimus

Insertion Along length of inferior surface of the obturator internus tendon and into the medial side of greater trochanter of femur with obturator internus tendon Quadrate tubercle on the intertrochanteric crest of the proximal femur Linear facet on the antero-lateral aspect of the greater trochanter

Function Laterally rotates the extended femur at hip joint; abducts flexed femur at hip joint

Laterally rotates femur at hip joint

Gluteus medius

External surface of ilium between anterior and posterior gluteal lines

Elongate facet on the lateral surface of the greater trochanter

Gluteus maximus

Fascia covering gluteus medius, external surface of ilium behind posterior gluteal line, fascia of erector spinae, dorsal surface of lower sacrum, lateral margin of coccyx, external surface of sacrotuberous ligament

Posterior aspect of iliotibial tract of fascia lata and gluteal tuberosity of proximal femur

Abducts femur at hip joint; holds pelvis secure over stance leg and prevents pelvic drop on the opposite swing side during walking; medially rotates thigh Abducts femur at hip joint; holds pelvis secure over stance leg and prevents pelvic drop on the opposite swing side during walking; medially rotates thigh Powerful extensor of flexed femur at hip joint; lateral stabilizer of hip joint and knee joint; laterally rotates and abducts thigh

Table 1-Muscles in the gluteal region (Drake et al., 2004)

II -1-1-2 Thigh In the thigh, three compartments, separated by intermuscular septa, contain the muscles (Figure 5). These three compartments are the anterior, the medial and the posterior compartments of the thigh. The muscles in the thigh can be linked to the spine, the pelvis or the tibia.

Figure 5-Transverse section through the mid-thigh (Drake et al., 2004)

II -1-1-2-1 Anterior compartment of the thigh

Figure 6-Muscles of the anterior compartment of the thigh (Drake et al., 2004)

Muscle Psoas major

Iliacus Vastus medialis

Vastus interme dius Vastus lateralis

Origin Posterior abdominal wall (lumbar transverse processes, intervertebral discs, and adjacent bodies from TXII to LV and tendinous arches between these points) Posterior abdominal wall (iliac fossa) Femur-medial part of intertrochanteric line, pectineal line, medial lip of the linea aspera, medial supracondylar line Femur-upper two-thirds of anterior and lateral surfaces Femur-lateral part of intertrochanteric line, margin of greater trochanter, lateral margin of gluteal tuberosity, lateral lip of the linea aspera

Insertion Lesser trochanter of femur

Function Flexes the thigh at the hip joint

Lesser trochanter of femur Quadriceps femoris tendon and medial border of patella

Flexes the thigh at the hip joint Extends the leg at the knee joint

Quadriceps femoris tendon and lateral margin of patella Quadriceps femoris tendon

Extends the leg at the knee joint Extends the leg at the knee joint

Table 2- Muscles of the anterior compartment of the thigh (Drake et al., 2004) 10

II -1-1-2-2 Medial compartment of the thigh

Figure 7-Muscles of the medial compartment of the thigh (Drake et al., 2004)

Muscle Pectineus

Origin Pectineal line and adjacent bone of pelvis

Adductor longus

Insertion Oblique line extending from base of lesser trochanter to linea aspera on posterior surface of proximal femur External surface of body of Linea aspera on middle onepubis (triangular depression third of shaft of femur inferior to pubic crest and lateral to pubic symphysis)

Adductor brevis Adductor magnus

External surface of body of pubis and inferior pubic ramus Adductor part-ischiopubic ramus

Hamstring part-ischial tuberosity

Function Adducts and flexes thigh at hip joint Adducts and medially rotates thigh at hip joint Posterior surface of proximal Adducts femur and upper one-third of thigh at hip linea aspera joint Posterior surface of proximal Adducts femur, linea aspera, medial and supracondylar line medially rotates thigh at hip joint Adductor tubercle and supracondylar line 11

Muscle Obturator externus

Origin External surface of obturator membrane and adjacent bone

Insertion Trochanteric fossa

Function

Table 3- Muscles of the medial compartment of the thigh (Drake et al., 2004)

II -1-1-2-3 Posterior compartment of the thigh

Figure 8-Muscles of the posterior compartment of the thigh (posterior view) (Drake et al., 2004)

Muscle Biceps femoris

Origin Long head-inferomedial part of the upper area of the ischial tuberosity; short head-lateral lip of linea aspera

Insertion Head of fibula

Function Flexes leg at knee joint; extends and laterally rotates thigh at hip joint and laterally rotates leg at knee joint

Table 4- Muscle of the posterior compartment of the thigh (Drake et al., 2004) 12

II -1-1-3 Posterior compartment of the leg This is the only compartment of the leg wherein there are muscles attached to the femur. This compartment can be divided in two groups, superficial and deep. The muscles in the leg act mainly on the foot but also on the knee for the ones that are linked to the femur. The superficial group comprises the gastrocnemius and the plantaris. The only muscle of the deep group attached to the femur is the popliteus.

Figure 9-Muscles in the posterior compartment of leg. Superficial group: A. Posterior view. B. Lateral view. C. Deep group (Drake et al., 2004)

II -1-1-4 Insertion points of the muscle The attachment of a muscle to a bone can take several shapes and need to be known in order to model the load on the femur. In Klein Horsmann et al. (Klein Horsman et al., 2007) the insertion of each muscle of the lower limb is described as surface, line or point and divided in a number of elements. This has allowed L. Modenese to map the muscle attachment point on the femur. Table 6 (p.27) shows the number of attachment points taken for each muscle in the model. 13

II -1-2 Joints (put there geometry and rotational centre) There are three joints around the femur: the hip, the knee and the patella. The hip can be seen as a pin-joint whereas the knee and the patella transmit some moments. The centre of rotation of the hip is located in the centre of the femoral head. The hip is the link between the upper part of the body and the lower limb. All the body weight goes through this joint.

Figure 10-Hip joint (Drake et al., 2004)

The knee joint and the patella joints are located at the bottom of the femur. They form the link between the femur and the tibia.

14

Figure 11-Knee joint (Drake et al., 2004)

II -2 Bone modelling

II -2-1 Bone data Turner et al. studied the elastic properties of trabecular and cortical bone tissues (Turner et al., 1999). In this study they acknowledge that the properties at tissue level are similar for the trabecular and the cortical bones. Phillips used data from this study in order to build his model (Phillips, 2011). These data are a youngs modulus of 18000N/mm and a Poissons ratio of 0.3.

15

II -2-2 Mechanostat The concept of mechanostat has been introduced by Frost in 2003 (Frost, 2003). According to this concept the bone adapts itself toward a target strain. If under loading a bone element undergoes a strain higher than the strain target, its section will increase. On the other hand, if it undergoes a strain lower than the strain target, its section will decrease. With that, under a future similar load, the strain should be kept near the strain target. In order to have some stability, some hysteresis is taken into account in this mechanostat. Thus Frost introduced in his concept a lazy zone in which the bone does not change its section. The concept is completed with three other values. These values are a low limit for complete bone resorption, one yield limit and one fracture limit. Frost illustrated his concept with this scheme:

Figure 12-Mechanostat concept by Frost (Frost, 2003)

He gave values for each limit in the same article. They are compiled in the following table. One should note that the lazy zone previously cited is between 1000 and 1500 microstrain. 16

Data MESr MESm MESp Fx

Description Bone resorption limit Bone strain target Yield strain Fracture strain

Value (microstrain) 50-100 1000-1500 ~3000 ~25000

Table 5-Mechanostat values (Frost, 2003)

Phillips model is based on this concept (Phillips, 2011). He uses the same values for all the limits but the bone resorption limit. Frost suggests that this value is indeed arguable. The figure below shows the values taken in his model.

Figure 13-Adopted values of strain associated with the dead zone, bone resorption, the lazyzone, and bone apposition, as well as the target strain (Phillips, 2011)

II -3 Macro and micro-scale approaches


Previous development of predictive model of the femur used macro or microscale approaches. Macro-scale models use elements bigger than the ones in an actual femur. The femur is modelled as a continuous element with variable material and geometrical properties along the length (Carter et al., 1989). Micro-scale models use elements smaller than the ones in an actual femur. The femur is built as one unique 17

voided part, so bone is not present everywhere inside the shape of the bone. The voids can be delimited using micro CT scans (Tsubota et al., 2009). The macro-scale approach has the advantage of having a good computational speed but has the limitation of treating the bone as one isotropic material and thus does not represent its internal structure. The micro-scale modelling is on the other hand really slow and sensitive to the scans used to define the location of the void but represents the internal structure. A more complete review of both macro and micro-scale approach can be found in Phillips article (Phillips, 2011). A third approach, similar to the micro-scale modelling, although using a more mathematical point of view, is the topography approach. The internal distribution of the bone material and the void in the femur is found using an iterative process mixing finite element analyses and topology optimisation (Bahari et al., 2011). However this

modelling simulation has not been developed on full 3D bones because it is not time efficient. Thus the objective of this study is to provide an intermediary between the computationally efficient but not very detailed macro-scale modelling and the detailed but time-consuming micro-scale approach. The meso-scale modelling would allow one to see the internal structure of the femur while remaining time-efficient.

II -4 Structural Optimisation: Biomechanics of the Femur state of the model (Phillips, 2011)
Starting with a CT scan of a Sawbones fourth generation composite femur (#3403) Phillips created a mesh of four node tetrahedral elements of the internal volume of the femur. Based on this mesh he defined the cortical and the trabecular elements. The cortical elements were defined as the triangle elements on the external layer of the mesh. They are shell elements. The trabecular elements were defined between each node and their 16 nearest neighbours. Trabeculae are modelled as truss elements. After 18

this process the model is made of 10410 cortical shell elements and 218717 trabecular strut elements. The minimum connectivity of 16 at each node should be enough to let the trabecular architecture have specific regional directionalities. Then bone material properties were assigned to the elements, based on literature values (Turner et al., 1999). The load case taken for this predictive modelling is a simplified set of force simulating single leg stand during the gait. Three forces were applied on the head of the femur while the two condyles on the distal femur where fixed:

Figure 14-Finite element model of the femur showing boundary conditions and loading. JCF is the joint contact force, ABD is the abductor muscle force, and ITB is the iliotibial band force. (Phillips, 2011)

The iterative process uses Frosts mechanostat concept. At each step the trabeculae cross section area and the cortical thickness is adapted to the strain calculated by Abaqus. In order to be more efficient the areas and thicknesses are discretized in 256 different sections. The trabecular elements can be taken out of the model by assigning to them a very small radius. 19

The model showed a good convergence and showed a structure similar to the one that can be seen in actual femurs. However Phillips pointed out some limitations to his model. The reader is invited to consult Phillips article for an exhaustive list of them. But among them one can note the use of a reduced set of muscle forces with simplified boundary conditions that can lead to some inaccuracy in some particular regions of both the two femoral extremities. At the proximal femur, the simplified load case consisting of only three forces can lead to over or under developed trabecular structures in some parts of the femoral head or of the neck. At the distal femur, the investigation of the internal structure is prevented by the fixed boundary conditions. Also the model uses only one load case when an actual femur is loaded in several different ways daily. This study aims at solve this two limitations. The continuation of this report presents the construction of an improved model. Some parts may look similar to the process presented above, but they are presented again to show the whole procedure in a logical way. The remaining limitations will be discussed at the end of this report.

20

III Developed models


III -1 Entry data

III -1-1 What is needed The aim of the model is to provide a predictive modelling of the femur as close as possible from femurs observed on corpses. For that the loads applied on the bone need to be the closest possible from the actual one in the human body. These loads are from muscles, joints, and gravity and inertia. This model uses more data than the one created by Phillips (Phillips, 2011). The geometry of the mesh is taken as the same than the one built by Phillips. The data needed to fully determine the load on the femur in the model are extracted from an OpenSim model developed by L. Modenese, similar to the one he built for his work on the hip joint contact force (Modenese et al., 2011), and are: x x x x x x x Muscle attachment points Muscle forces Joint reaction forces and moments at the hip, knee and patella Force directions Femur orientation Femur velocity Thigh centre of mass

The majority of the data are available in OpenSim but the directions of the muscle forces are not returned by the software. However the software is provided with an API which allows one to develop plug-ins in order to get all the data needed. So a plug-in has been developed to get the direction of the muscle forces.

21

III -1-2 OpenSim Plug-in The plug-in aims to extract the muscle force orientations from a musculoskeletal model. This is a new tool in the study of the biomechanics of the human body that did not exist prior this project. The plug-in has been coded in C++ with the OpenSim SDK. It is based on the structure given in the Opensim API examples but the majority of the code has had to be changed as the objectives were quite different from the ones in the example. The plug-in is an analysis one. It gives the orientation of the forces in an Opensim model during a simulation. It is built to accept different options, to be homogeneous with the analyses already proposed in Opensim. These options are the following ones: x x The bodies on which the analysis has to be carried can be selected. The output is by default a .sto file with labels homogeneous with the other Opensim analysis. x A .csv file can also be printed which is more easy to use if the analysis is made on different bodies at the same time, because it gives inside the file the bodies attached to a muscle. x The coordinate system in which the analysis is made may be chosen and be the ground referential or the local referential of each body selected in the analysis. The analysis takes into account the wrapping of the muscles around the different bones and returns just the direction of the forces at the extremities of the muscle. The source code can be found in the appendix A.

22

III -2 Femur modelling

III -2-1 Concept The concept is globally the same than in Phillips works (Phillips, 2011). An initial input file is loaded by one or several load combinations. The strain in each element of the mesh is compared to the value given in the mechanostat. Then a MATLAB script changes the section of the different elements according to this previous comparison and writes a new input file. This new input file is then taken to start a new iteration of the precedent process. After a certain number of iterations, convergence is expected and the process should stop. At that time a predictive structure of the femur is returned. This structure will then be compared to an actual femur to validate the process.

III -2-2 Model The femoral geometry is based on a Sawbones fourth generation composite femur (#3403).

Figure 15-Sawbones fourth generation composite femur (#3403) (Size: a) 455 mm b) 45 mm c) 31 mm d) 135 degrees e) 27 mm f) 74 mm g) 13 mm canal) 23

The initial mesh is the one taken from Phillips model, it has just been reoriented in the space to fit the International Society of Biomechanics (ISB) recommended coordinates, as presented in III -2-4-2, and slightly scaled to match Luca Modeneses general OpenSim model. To do this scaling Luca Modenese used virtual marker at the centre of rotation of the hip and the knee. The load cases are based on current works made by Luca Modenese. He developed an OpenSim model of the musculo-skeletal system, from the pelvis to the ground. A first attempt has been made to use the whole set of data from Klein Horsmann et al. (Klein Horsman et al., 2007). The major problem was that the geometry of the Sawbones femur was too different from the one presented in Klein Horsmann et al. work. Thus Luca Modenese used this work as a basis but mapped the muscle attachment using a combination of manual work and mapping algorithms, in order to provide attachment points matching the nodes of the mesh. Some muscle are not attached on a single point in the human body but can be fixed along a line or on a surface. To take that into account Luca Modenese divided some muscles into several bundles of sub-muscle.

III -2-3 Software x MatLab

MatLab is used to build the input files, manage the iterative process by calling Abaqus jobs and python scripts and process the results. x Python

Python is used to extract the strain data from the Abaqus output. It is also used to plot a 3D view of the femur from an Abaqus output. These two functions were implemented by Phillips, the scripts have just been modified to allow multiple load case usages (when analysing several load combinations during the same iterative process). A Matlab script has been written to create a python script adapted to the number of steps in the Abaqus input file. An example of the python script is available in Appendix E. x Abaqus 24

Abaqus is used to calculate the strain in each element of the mesh using a finite element analysis. x OpenSim

OpenSim is used to get the muscle and joint forces.

III -2-4 Input file III -2-4-1 Geometry As presented before, the geometry used for the femur is the same one used by Phillips in its previous version of the model (Phillips, 2011). It is based on CT scans of a Sawbones fourth generation composite femur (#3403). The mesh used is the one made available on the Structural Biomechanics in the Department of Civil and Environmental Engineering at Imperial College London website (Imperial College London, 2011) (http://www2.imperial.ac.uk/structuralbiomechanics/). It comprises 20536 nodes, 10410 shell elements to represent the cortical bone and 218717 strut elements to represent the trabecular bone. The initial thickness for the cortical elements has been set to 0.1 mm and the initial radius for the trabecular elements has been set to 0.1 mm also. Using MATLAB the mesh has been translated, rotated and scaled to fit Luca Modeneses model. After these transformations the model has the coordinate system recommended by the ISB. III -2-4-2 Coordinate system The coordinate system chosen is the one recommended by the International Society of Biomechanics (ISB). It is the Joint Coordinate System (JCS) for the hip joint (Wu et al., 2002): O: Centre of the femoral head (that should also be the centre of rotation of the

25

hip joint) y: The line joining the origin and the midpoint between the two femoral epicondyles at the bottom of the femur z: The line perpendicular to the y-axis in the plane defined by the origin and the two femoral epicondyles, pointing to the right x: Defined by the two other axis, so that the basis (O, x, y, z) is orthonormal and direct

III -2-4-3 Boundary conditions Boundary conditions are provided to make sure that the femur is stable during the analysis. If the different loads are equilibrating themselves, the reaction forces at the boundaries should be quite small. In this model the six degrees of freedom have been blocked at the centre of the knee joint. One should bear in mind that by setting some boundary conditions, reaction forces are created. These reactions are checked against the predictive forces at the knee got from the OpenSim model. III -2-4-4 Loading The loads are defined from the OpenSim model and correspond at the muscle forces, joint forces and gravity and inertia forces. There are 3 joints taken into account and 115 muscle attachments. III -2-4-4-1 Loads from the muscles These muscle attachments represent 36 different muscles (or muscle subdivisions). A list of these muscles, alongside with the number of their attachment points can be found in table 6. Several attachment points are taken for muscles which insertions can be described as line or a surface instead of a point (Klein Horsman et al., 2007). The description of these muscles is given in part II -1-1. 26

Muscle

Add. brev. (prox.) Add. brev. 2 (mid.) Add. brev. (dist.) 2 Add. long. Add. magn. (dist.) Add. magn. (mid.) Add. magn.(prox.) Bic. fem. CB Gastrocn. (lat.) Gastrocn. (med.) Gemellus (inf.) Gemellus (sup.) 6 3 6 4 3 1 1 1 1

# Attach 2

Muscle Glut. max. (sup.) Glut. max. (inf.) Glut. med. (ant.) Glut. med. (post.) Glut. min. (ant.) Glut. min. (mid.)

# Attach 6 6 6 6 1 1

Muscle Pectineus Piriformis Plantaris Popliteus Psoas major Quadratis fem. Vastus interm. Vastus lat. (inf.) Vastus lat. (sup.) Vastus med. (inf.) Vastus med.(mid.) Vastus med. (sup.)

# Attach 4 1 1 2 3 4 8 6 2 2 2 6

Glut. min. (post.) 1 Iliacus (lat.) Iliacus (mid.) Iliacus (med.) Obt. ext. Obturator int. 3 3 3 3 3

Table 6Muscles taken into account in the model

III -2-4-4-2 Loads from the joints The joints are a sensitive part of the modelling. In the human body the joints act on important surfaces and can transmit forces and moments, depending on the joint. If the hip joint can be modelled as a pinned joint, and therefore do not transmit any moment, the knee is a hinge which can transmit moments along two different axes. As the knee axes are not collinear with the femur axes there are moments on the three femoral axes. In Phillips model the knee was fixed and the load from the hip joint was applied to a set of seven nodes in the femoral head. In OpenSim the loads for a joint is given at its centre. So the Abaqus model needs a way to apply forces at the centres of each joint. This has been done by building 27

three different parts, one for each joint, with beam elements running to theoretical centre of the joint. This centre can be located outside the femoral mesh. These parts are made of two different layers, one hard on the external layer and one soft on the internal layer to get a better repartition of the stresses on the joint boundary. They are tied to the femur at each joint localisation. Their geometries have been made by scaling the nodes of the femur joint surface from the centre of each joint to create thick triangular elements. For the three joints the soft layer has a youngs modulus 10 MPa of and a Poisson ratio of 0.3. For the knee and the hip the hard layers have the properties of the steel with a youngs modulus of 210 000 MPa and a Poisson ration of 0.3. For the patella joint, which location change a lot during the knee flexion, a longer part has been created along the path of the patella. This longer part has a hard layer with a youngs modulus of only 2 100 MPa. This is to let the hard layer deform and behave as only a smaller part was loaded. Beam elements, allowing transmission of moments, are going from each node of the external layer to the theoretical centre of the joint. These beam elements are made of steel and are circular with a radius of 10 mm. The three joint parts are presented in figure 16 and an example of the source code used to build these parts is given in appendix B.

Figure 16-Parts to apply joint loads 28

III -2-4-4-3 Loads from the weight and the inertial force In order to fulfil as much as possible equilibrium the model must take into account the leg weight and the inertia force created by the movement of the body. These two forces are applied at the legs centre of inertia, taken from the OpenSim model. Because of the mass of the muscles, the centre of inertia is located outside the mesh. So beam elements have been used to transmit the load from this point. These elements are linked to the hip and knee joint parts. However particular attention has been given to not create artificial stiffness in the structure. The weight is given in the ground coordinate system. The femur orientation in this referential is extracted from OpenSim and is used to get the weight force in the local femoral referential. The inertia force is calculated from the leg acceleration. OpenSim does not return the body accelerations, because of an internal bug. So the acceleration is derived from the body velocity. Then the inertia force is added to the weight and applied to the two extremities of the femur through the parts cited previously. III -2-4-5 MatLab implementation The Abaqus input files are built with MatLab. The source code is given in appendix C. The process to build the .inp file is summarized here: x The different data needed (see III-1-1) are loaded from the OpenSim output files. These files give data for all the frame of a body motion. x x x The frame on which the analysis has to be carried is selected. The attachment points of the muscles are linked to nodes on the mesh. The different data are processed to be converted in point loads on the mesh. x An .inp file is printed, respecting the structure needed by Abaqus.

To be more time efficient some parts of the .inp file are prewritten and are just assembled with the variable one. The final input file, with all the loads and additional parts is presented in figure 17. 29

Figure 17-First Abaqus input file

III -2-5 Mechanostat parameters The mechanostat parameters are the same that Phillips used in his work (Phillips, 2011). These data come from works by Frost (Frost, 2003). The mechanostat is centred on a value of 1250 with a range of 250 on each side. So if the strain of an element is under 1000 its section is diminished and if its strain is above 1500 its section is 30

increased. To have a faster convergence the new section is proportional in size with the ratio between the actual strain and the target of 1250 .

III -2-6 Load cases considered The simplified load case was a weakness of Phillips model. In this report several attempts have been made to solve this limitation. Indeed a femur is loaded with many different loads. One can walk, climb stairs, stand up, sit down, run, turn, etc. To be fully exhaustive is not possible but by using several load cases the model can be improved. But using several load cases does not solve the whole problem. The way in which they are combined to determine the femoral structure is also very important. Different methods can be thought up and have been experimented throughout this dissertation. Three load cases have been developed by Luca Modenese in his model: walking, climbing the stairs and standing up. Each motion is divided in a certain number of frame in which are calculated with OpenSim all the loads applied on the femur. This dissertation model has first been developed using the frame when the force on the hip is maximal. The three load cases at the maximal hip force frame have been used alone at first. Then the load cases have been combined to improve the femoral architecture. The first way to combine the load cases is to compute the femoral structure using the loadings one after the other, by setting a minimal trabecular cross-section area or cortical thickness for each element, based on these properties at the convergence state of the previous calculation. A second way of combining the load cases is to use them all at the same time during the building process. This is done by computing in Abaqus the strains in each element for the different load cases and then use for each element the maximum strain it has undergone during the different loading to compute the next section.

31

Analyses have been run by combining the maximal hip force frames for the three load cases using the two approaches. The results are given in IV . However, using only the maximal hip force frame in each motion can be limiting. Thus the structure of the femur has also been computed using four and then ten frames by motion. These frames have been selected to be evenly distributed throughout the motion, starting from the maximal hip force frame.

III -2-7 Iterative process The iterative process is largely inspired from the code developed by Phillips for his model. Some improvements has however been made. The computational time has been reduced by optimizing some functions dealing with massive tables or matrixes or by pre-writing the unchanging part of the output files. The source code can be found in appendix D. This section gives some clues to read the code. Basically the Matlab script controls the whole process. It calls the finite element analysis in Abaqus and the Python script used to read the results of this analysis. At each step an Abaqus input file is run in Abaqus/Standard. The result file is processed by a Python script, adaptable to the number of load case, or step, in the .inp file. This python script reads the results and writes a text file with all the strains exploitable in Matlab. Then the trabecular cross-section areas and cortical thicknesses are modified following Frosts mechanostat rules. Data are saved to allow one to study the results and a new input file is written, based on the invariable parts of the first one and the new sections. Finally the new input file is run and the process continues until convergence. Figure 18 presents a simplified flowchart of this iterative process.

32

Figure 18-Iterative process simplified flowchart

33

The cross-sectional radius of the trabecular elements are allowed to vary between 0.1 mm and 2.0 mm. These values are the same than in Phillips model. The cortical thickness limits chosen by Phillips were 0.1 mm and 10 mm, taken from observed values in the femur (Stephenson & Seedhom, 1998)(Treece et al., 2010). It has been chosen to lower the maximum limit of the cortical thickness to 8 mm in order to get a better distribution of the cortical bone around the femoral shaft. The structure of a femur always presents zones without trabecular bones, in the femoral canal for example. The model needs to be able to suppress some unused trabeculae, but without reducing the stability of the algorithm. So when a trabecular element with a cross-section area at the minimum fixed value has an axial strain below 250 (see -Adopted values of strain associated with the dead zone, bone resorption, the lazyzone, and bone apposition, as well as the target strain figure 13) it is assigned a near zero radius of 0.001 mm (1/100 of the minimum radius). This allows the elements to have no contribution on the stiffness of the femur while keeping them in place in case they would have to regenerate. Indeed the algorithm allow the trabecular element to regenerate if their strain rises above 2,500,000 (based on the ratio between the minimum and the near zero radius and the value for Frosts mechanostat dead zone). Convergence is considered fulfilled when less than 1% of the trabecular elements have their cross-section area changed, less than 1% of the cortical elements have their thickness modified and the change in the numbers of trabecular element in the two last iterations is below 1. The cross-sectional area of the trabecular elements with their strain outside the mechanostat lazy zone is adjusted as the product of the previous area and the ratio between the strain in the element and the target strain. The cortical thickness is adjusted in the same way:

34

Where and are the cross-sectional area and cortical thickness for the current target strain and and the new cross-sectional area and new cortical thickness.

iteration, or the absolute strain for the trabecular or the cortical element, the In order to improve the computational efficiency in Abaqus the values for the

cross-sectional areas and the cortical thicknesses have been discretized into 256 values evenly distributed between the minimum and maximal values for both the areas and the thicknesses. This also allows reading the results more easily by selecting the elements belonging to a certain set in Abaqus. This iterative process has been slightly modified when it came to load the femur with a combination of load cases as presented in III -2-6. Two different ways of combining the load cases have been presented. For the first one, when the load cases are applied one after the other, the iterative process has been modified to set a minimum value for each element area or thickness. This value is based on the cross-sectional area or cortical thickness of the element at the end of the previous load case analysis. This avoid any element to have a lower section than at the end of the previous case and so the femur is certain to be capable of supporting this previous case. For the second way of combining the load cases, when they are used at the same time during one unique analysis, the Matlab script is unchanged. However the Python script is changed. In that case the Abaqus input file contains several steps, one for each load case, that are calculated at each iteration. The Python script is modified to read each step in the Abaqus output file. It extracts the strains for all the elements, takes among the different load cases the maximum absolute strain value for each element and return a text file, with the same framework than the one returned when only one load case is considered, containing these maximum strains. Matlab uses then this text file in the same way as if there was only one load case. Basically the Python script returns an envelope of maximum strains instead of returning the actual strains. A Matlab code has been written to adapt the Python script to the input file number of step.

35

III -2-8 Results analyses The results have been analysed using several tools. The first one is the statistical data extracted during the iterative process. These data are the number of iteration to reach the convergence, the number of effective trabecular elements (non near zero area), the average node connectivity, the standard deviation for this average and the trabecular and cortical bone volumes. Abaqus has been used to plot on the femur some data, as the cortical thickness. For that two methods have been used. The first one is the utilisation of a script, written by Phillips when he developed his model, which allows to plot in Abaqus all the effective trabecular and cortical elements with their own section and thickness. The second method has been to plot directly from the results files the thickness of the cortical elements as a colour plot. Finally a Matlab script has been written to plot the porosity of the bone, and allow a future comparison with CT scans. The method takes for each element the ratio between the element volume and the circumscribed sphere volume of this element and allocate to each of its nodes half (for the trabeculae) or a third (for the cortical elements) of this ratio. By summing the contribution of all the elements at each nodes and plotting that with a colour plot in Matlab a good indication of the porosity is given. To gain a better contrast the value plotted is where is the porosity previously cited. The values under 0.3 are not plotted.

36

IV Results
As explained previously (see III -2-6) several load cases and combinations of them have been considered during the progression of this dissertation. Their results are presented below with a precision depending on the relevance of the information given by the load case to the final model.

IV -1 Single load cases


This single load cases analyses are just the first step of this dissertation. They have allowed the author to validate the iterative process and the input files for each motion. Because of the more complex set of forces, some artefacts in the trabecular architecture, present in Phillips model due to the highly concentrated forces, have disappeared in the femoral head. However the cortical thickness still presents some problem around the shaft. There are some zones, the anterior and posterior parts of the femur, where this thickness is at the minimum value, which is not possible on the shaft. The complex set of forces solves some issues raised by Phillips, but not all of them.

Figure 19-Cut of the femur at y=-200 mm for the walking load case 37

Moreover these single load cases have been computed only for the frame when the hip joint reaction force is maximal during the motion. Table 7 gives the number of iterations before reaching convergence, the number of trabecular elements, the average node connectivity (and the standard deviation) along with the trabecular and cortical volumes. The values from Phillips analysis are given as a comparison.
Motion # of # of iterations trabecular elements 42 27 29 36 63682 91900 100388 110186 average SD node Connectivity 9.96 11.30 11.96 12.24 5.02 5.26 5.17 4.85 trabecular volume 16508 22439 22045 28083 cortical volume 81506 113391 102473 95127

Phillips Walk Stairs Stand

Table 7-Statistical results for single load cases

IV -2 First combination method: one after the other


With this method the structure has first been calculated to stand the load case due to the walking, at the maximal hip joint reaction force frame. Then the trabecular cross-section area and cortical thickness have been given a new lower limit from the results of this first analysis, for each element. The stairs load case structure has been calculated from this starting point, and then the standing up load case from the result of the second analysis. Table 8 presents the same data than table 7 for this analyses.
Motion # of iterations # of trabecular elements 91900 91900 91900 average SD node Connectivity 11.30 11.30 11.30 5.26 5.26 5.26 trabecular volume cortical volume

Walk Stairs Stand

27 7 7

22439 31384 34717

113391 130129 143297

Table 8-Statistical data for the first combination method

38

It has been observed that setting a lower limit for each element depending on the previous analysis avoids the structure to rearrange itself. The already developed trabecular elements take all the stresses, which could have been expected, and the number of trabecular elements does not change after the first analysis. The structure seems curbed, and cannot reach an optimal shape. The bone volume however changes to stand the new loads. Even if the cortical thickness is thicker on the anterior and posterior sides of the femur, compared to the previous analyses, the repartition of the cortical thickness does not match the clinical observations.

Figure 20-Cut of the femur at y=-200 mm for the three successive load cases

This method seems to not be the more appropriate to model the femur because actual limbs are loaded with different load cases at the same time during the development of their structures. This is the reason why the second combination method has been developed.

IV -3 Second combination method: several load case at once


This combination aims to solve the problem caused by the successive loading of the previous combination. By applying all the load cases at each step the structure is 39

expected to be more optimized. The computational time for one analysis is augmented because of the time Abaqus take to compute different steps in one analysis but the number of iteration to reach the convergence stay reasonable and the analysis has just to be run once. The effect of the number of frame per motion has been studied and three different analyses have been made with one, three and ten frames per motion. The first comparison between the different kinds of analyses run will be firstly made on the cortical thicknesses on the shaft. Then the trabecular architecture will be evaluated against clinical observations.

IV -3-1 3 motions; maximal hip force frame This analysis has been the first one to be tested. It can be compared directly to the first combination method results because the load cases considered are the same. The structure uses more trabecular elements and slightly less bone volume. By equilibrating the three load cases at the same time there is no artificial stress path created as it was the case for the first combination method. In this method the load cases were applied successfully and the already stiff parts of the bone took all the stresses. Table 9 shows the comparison between the statistical data for the two ways of combining the load cases.
Load cases applied successively at the same time # of iterations 41 32 # of trabecular elements 91900 149124 average node Connectivity 11.30 15.92 SD trabecular volume 5.26 5.88 34717 33766 cortical volume 143297 136261

Table 9-Statistical data for the three load cases-comparison of the combination methods

The cortical thickness is more evenly distributed. There is no zone with zero thickness. However that is still not close enough to the actual femurs to be fully satisfying. 40

Figure 21-Cut of the femur at y=-200 mm for the three simultaneous load cases

However, this way of combining the load cases can already be chosen as the better method between the two tested during this dissertation. This method has then been developed by including more frames per motion in the structural analysis.

IV -3-2 3 motions; 4 frames per motion In this analysis 12 load cases are computed in every iteration by Abaqus. The frames when the load cases are taken during the motion are evenly distributed among the duration of the movement, starting from the maximal hip joint reaction force frame. It has just been a step between the precedent attempt with only one frame per motion and the final structure using ten frames per motion. The structure uses more trabecular elements when it has to resist different stages of the motion. The total bone volume however seems to be lower.
# frames # of # of per iterations trabecular motion elements 1 4 32 20 149124 175084 average node Connectivity 15.92 17.47 SD trabecular volume cortical volume bone volume

5.88 5.56

33766 45612

136261 109495

170027 155107

Table 10-Statistical data for the second combination method-1 and 3 frames per motion 41

There is some improvement in the way the cortical thickness is distributed among the shaft. There is not anymore a big gap between the coronal and sagittal planes cortical elements.

Figure 22-Cut of the femur at y=-200 mm for 4 frames per motion x 3 motions

IV -3-3 3 motions; 10 frames per motion This is the final model produced during this dissertation. It uses 10 frames per motion and 3 different motions, i.e. 30 different load cases. As before the frames are evenly distributed during the motion, starting with the maximum hip joint reaction force frame. This section presents as for the other analyses the statistical data, but also a full review of the cortical thickness and of the trabecular framework, along with a porosity analysis. IV -3-3-1 Statistical data Table 11 shows the statistical data for the final model, for the two previous analyses presented and for Phillips model. It can be seen that the improvement between 42

4 frames per motion and 10 of them is not as important as the one between 1 and 4 frames per motion. Compared to Phillips model a lot more bone volume is used, but that was expected.
# frames per motion 1 4 10 Phillips # of # of average SD iterations trabecular node elements Connectivity 32 20 20 42 149124 175084 176688 63682 15.92 17.47 17.65 9.96 5.88 5.56 5.63 5.02 trabecular cortical volume volume bone volume

33766 45612 47391 16508

136261 109495 112059 81506

170027 155107 159450 98014

Table 11-Statistical data for the final model

IV -3-3-2 Cortical thickness Cortical thickness is more evenly distributed around the shaft than in the previous and Phillips models as shown in figure 23. However the coronal and sagittal planes cortical elements are still a little bit too small.

Figure 23-Cut of the femur at y=-200 mm for 10 frames per motion x 3 motions

The cortical thickness is small on the two femoral extremities as expected and the thickness along the shape seems to follow a spiral path. 43

Figure 24-Cortical thickness

IV -3-3-3 Trabecular architecture IV -3-3-3-1 On the shaft

Figure 25-Cut of the femur at y=-200 mm for 10 frames per motion x 3 motions with trabecular and cortical elements

On the shaft the femoral canal is apparent. There is no trabecular element at the centre of the femur. However some trabecular elements are developed on the edge of 44

the shaft and take some stresses from the cortical elements. Figure 25 shows the location of the trabecular elements at the same level than the previous cuts, where only the cortical elements appeared. This kind of structure can be shown all along the shaft.

IV -3-3-3-2 Inside the femoral head Compared to Phillips model there is not anymore any artificial void in the trabecular structure, created by a too simple load case. The section of the trabecular elements is also better distributed inside the head. Figure 26 shows a 3 mm cut inside the femoral head. In this figure the different stress group presented in II -1-2 can be observed. The principal compressive and tensile group are highlighted along with the Ward triangle.

Figure 26-3mm cut inside the femoral head 45

IV -3-3-3-3 Inside the distal end of the femur On the distal part of the femur the trabecular elements seem to leave the edge of the femur with a perpendicular angle. The trabecular elements seem also to have lateral bracing. The following figures show the structure through two 3 mm cuts (located on each other by a green line).

Figure 27-Coronal and sagittal cut through the distal femur

IV -3-3-4 Porosity Porosity has been plotted in the femur using the script and colour scale presented in section III -2-8. This value is the easiest one to be compared to CT scans. The porosity has been plotted on 5 mm thick slices along the x-axis. Figure 28 shows the femoral canal with no bone. On the shaft all the bone is located on the edges and the cortical bone. On the proximal and distal ends the porosity is more evenly distributed. However one can note some increase in the ratio bone/void on the centre of the femoral head and in front of the patella path on the distal femur. 46

47

Figure 28-Porosity plots on 5mm thick slices on the x-axis 48

IV -4 Summary

IV -4-1 Reaction forces (RF) and reaction moments (RM) for the frames considered
Excess forces and moment at the knee step Walk 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Force and moment at the knee from OpenSim model Ratio

Stairs

Stand up

RF (N) RM (Nm) RF (N) RM (Nm) RF RM 22.61 55.07 2485.21 61.98 0.91% 88.84% 207.99 97.27 1832.16 112.52 11.35% 86.45% 541.51 118.26 1473.29 147.27 36.76% 80.30% 1687.06 170.44 3206.10 219.08 52.62% 77.80% 1550.41 191.95 3741.35 248.56 41.44% 77.23% 133.40 13.60 1057.08 30.53 12.62% 44.54% 65.73 27.97 362.68 39.56 18.12% 70.71% 50.62 26.83 180.46 36.32 28.05% 73.86% 76.45 16.06 689.74 33.17 11.08% 48.42% 147.50 14.69 803.86 40.35 18.35% 36.40% 98.41 103.34 2690.57 125.94 3.66% 82.06% 327.50 131.33 2439.35 157.26 13.43% 83.51% 610.40 166.60 2500.31 183.03 24.41% 91.02% 1102.16 236.96 2663.06 256.94 41.39% 92.23% 669.43 111.07 1470.06 118.12 45.54% 94.03% 80.01 22.25 290.57 33.84 27.54% 65.75% 32.77 17.23 158.71 23.71 20.65% 72.67% 47.03 13.82 317.15 24.50 14.83% 56.40% 103.39 24.16 378.57 37.58 27.31% 64.28% 30.12 29.94 814.43 23.58 3.70% 126.95% 76.39 48.67 2715.67 62.65 2.81% 77.69% 43.61 40.43 2053.82 52.39 2.12% 77.17% 27.39 47.58 1708.89 59.44 1.60% 80.05% 79.43 53.14 1259.74 60.51 6.31% 87.82% 554.76 71.16 1733.87 101.75 32.00% 69.94% 1193.50 130.96 2719.32 175.58 43.89% 74.59% 1183.33 126.72 2666.32 169.97 44.38% 74.56% 1266.33 129.92 2781.24 176.10 45.53% 73.78% 100.18 23.32 298.32 34.35 33.58% 67.88% 70.48 21.37 205.99 24.14 34.21% 88.52%

Table 12-Reaction forces and moment at the knee 49

Table 12 gives the values for the reaction forces and the reaction moments at the knee for each frame. The first columns give the value for the force and moment in excess at the knee (the sum of all forces and moment at the knee, including the knee joint reaction force and moment), while the followings give the force and moment at the knee calculated with the OpenSim model. The last two columns give the ratios between the previous values. Depending on the frames these ratio can become quite big. Further comment will be made in V -2-2-2.

IV -4-2 Statistical data summary


Analysis # of trabecular elements trabecular volume cortical volume bone volume Ratio trabecular volume / cortical volume 0.20 0.24 0.24 0.25 0.42 0.42 0.20

Motion Walk successively then Stairs loaded then Stand # frames 1 per motion at the same 4 time 10 Phillips

91900 91900 91900 149124 175084 176688 63682

22439 31384 34717 33766 45612 47391 16508

113391 130129 143297 136261 109495 112059 81506

135830 161513 178013 170027 155107 159450 98014

Table 13-Statistical data summary

Table 13 summarizes some of the statistical data previously given. It allows an easier comparison between the different analyses run during the development of the femoral model. This table also shows the ratio between the trabecular and cortical volumes. This ratio is similar to the one found by Phillips when the frames considered are the maximal hip joint reaction frames but changes a lot when several frames are considered during a motion. Actually in the final model trabecular elements are developed on the edge of the shaft and relieve a part of the strain from the cortical elements as shown on figure 25.

50

V Discussion
V -1 Achievements and remarks

V -1-1 Femoral structure The model presents a trabecular framework with all the major characteristics of a long bone. The stress groups are recognizable and the Ward triangle is also visible in the femoral head. In the distal end of the femur the organization of the trabeculae is as expected. The cortical elements are more evenly distributed than in the previous model. On the shaft there is not anymore cortical element with a very low thickness. On the two extremities the cortical thickness is very low. These two different comportments of the cortical bone are coherent with previous clinical observations (Stephenson & Seedhom, 1998) (Treece et al., 2010). The change in the loads applied to the femur has had a huge effect on the improvement of the trabecular structure.

V -1-2 Truss elements vs. beam elements for the trabeculae A test has been run to see if the use of truss element for the trabeculae was a good assumption against the use of beam elements. Starting from the final structure for the walk load case, the truss elements have been changed into beam elements. The displacement at the hip joint centre has been compared between the two output files. The displacement for the model using truss elements is of 12.751 mm whereas it is of 12.736 mm for the model using beam elements. Thus the use of truss element or beam element does not make a significant difference and truss elements will preferably be used to improve the computational efficiency. 51

V -2 Limitations
Even if the final model shows a good correlation with the clinical observations there are still some limitations in the way it has been developed. These limitations come from the mesh, the data used to load the femur and the algorithm.

V -2-1 In the mesh V -2-1-1 Geometry The mesh geometry comes from a CT scan of a Sawbones fourth generation composite femur (#3403). This geometry has been taken because it is a reference in bone modelling. However some limitations have been raised about this geometry during the model development. The curvature of the Sawbones femur is not really marked. Because of that, the localisation of the muscle attachments has been difficult. This was supposed to be done using point clouds representing the femoral mesh and the muscle attachment points from previous study, and a mapping algorithm. But because of the gap between the mesh used and the muscle attachment data collected on corpses it has not been possible. The muscle attachment nodes on the femoral mesh have been determined using data from clinical observations but picked by hand following anatomical knowledge instead of using computational methods. However the OpenSim model has been developed using the same muscle attachment points than the structural iterative model so the muscle forces used are coherent. But this observation made on the curvature of the Sawbones femur may have some consequences on the trabecular framework. A straight femur may have a different comportment than a curved one; some secondary effects can be created in both a favourable or unfavourable way. The geometry concerns raise a bigger issue. There is not any fully integrated study summarizing all the geometrical data for one body. There are studies on the muscle, on the bones, on their interactions but there is not a study giving all of them for the same limb, which could be used in the development on models. 52

V -2-1-2 Joints loading A second issue with the mesh is the way the loads are applied from the joints. Parts have been created to apply the load the more smoothly possible and to not create artificial concentration of forces. This should be good for the hip as the joint is not moving a lot. At the distal end of the femur the system could be more arguable. For the knee joint, even if the joint contact surface is not the same during the whole motion, a part big enough is good to transmit a load properly. However the patella creates a bigger problem. Indeed the patella is moving a lot during the knee flexion and its surface of contact on the femur moves also. In this model the problem has been solved by creating a bigger part, slightly flexible to be able to be deformed and to transmit a load in the axis of the joint force. However it is possible, but it would have take too much time in the scope of this project, to implement inside the Matlab script used to build the input file the same script that have been used to build the patella part. With this functionality and the patella localisation from the OpenSim model, a joint part could be created for each frame and loaded only for the corresponding frame during the Abaqus analyses. However this issue has not caused any problem in this model. It is only something that one should bear in mind and that may become important when the model will be in a more advanced state.

V -2-2 In the data V -2-2-1 Loads from the muscle In the model, the loads from the muscles are only applied as point loads on some mesh nodes. And even if these loads are calculated via the OpenSim model they do not represent as accurately as possible the actual muscle load in the human body. Firstly, the loads norms and directions are taken at the extremities of the muscle whereas, as for a pulley, the direction and attachment point should be taken where the muscle leaves the bone surface. For that it will be necessary to develop a new OpenSim plug-in or at least to modify the one created for this project. 53

Secondly, if the muscles load the bones through the tendons at their extremities, they also load them with a pressure load all along the length of the limbs. This action is not taken into account in the model and that could make a difference in the cortical thickness along the shaft. However the calculation of this pressure is hard to get and thus has not been a priority in the development of this model. V -2-2-2 Reaction forces and moments Depending of the frames it has been noted that the ratio between the resultant of all the forces and moments and the actual force and moment at the knee can become quite important. An explanation has been researched for this behaviour. This is probably because of an issue raised before; the muscles direction and attachment point are taken at the end of the tendon and not at the point where they leave the bone surface. So the direction used in the iterative model is not the same that OpenSim uses to calculate the equilibrium. For example, if we look at the step 5 of the walking motion. The resultant force vector is [-1133, 1055, 71] N. The gastrocnemius lateral and medial, which function is the plantar flexion of the ankle joint and which assists the flexion of the knee, represent the biggest load at this step with norm values of 365 and 1568 N. Their direction vectors are [-0.8526, -0.5194, -0.0574] and [-0.9281, -0.3403, -0.1511]. So they create a load oriented mainly on the x. Yet, if one looks at what happens during the gait movement, it appears that the gastrocnemius is attached at the distal extremity of the femur, leaves it towards the back of the bone but wraps round the condyles and actually leaves the femur oriented towards the ground. So instead of having big reactions on x and y the model should be at the equilibrium with a proper orientation of the gastrocnemius force. The iterative model would be improved by a better extraction of the data from OpenSim. To be the more accurate possible the mesh should be loaded with the loads (point load and pressure) from the attachment points to the point where the muscle leaves the bone. But this creates a new difficulty. The wrapping surface in the OpenSim model needs to be defined as precisely on the surface of the mesh as possible. Indeed 54

the points where the muscles leaves the bone surface change during the motion, so these points needs to be mapped for each frame and so they need to be as close as possible from the mesh. Two solutions are possible to do so. The first one is to do the work upstream and create tables with the mapping points depending of the joints flexions. This solution is better if the wrapping surface does not follow precisely enough the mesh and if each wrapping surface depends only of one joint flexion. If several joint flexions determine the wrapping surface it is better to use a mapping algorithm within the Matlab script used to build the first input file, but in that case the wrapping surface needs to be define precisely as the femoral mesh in the OpenSim model. Finally this issue needs to be dealt with rigorously but would improve seriously the model.

V -2-3 In the algorithm V -2-3-1 Trabecular elements V -2-3-1-1 Orientation This is a limitation already mentioned by Phillips in his work. Even if the initial connectivity of the trabecular elements allows the structure to be free to develop itself in all the directions, the elements are not allowed to move and change their orientations. So the optimization process is curbed. The optimization of the trabeculae should be integrated in the iterative process. It would certainly create a structure with smoother transition angle between the elements and could result in a lower bone volume. V -2-3-1-2 Element types The type of trabecular elements has already been mentioned. The model uses truss elements and it has been verified that using beam elements does not considerably change the deformation of the femur. However, by using beam elements the strain would not be restricted at one unique axial component but shear, bending and twisting

55

strains would be present too. Other strain criteria should be assessed using beam elements. A second limitation of the truss elements is that they do not fully represent the internal structure of an actual bone. Indeed the trabeculae are also made of shell-like elements. An improvement of the algorithm could be made by inserting between two big section area trabeculae a shell element. V -2-3-2 Cortical bone The cortical bone is actually not an isotropic material (Turner et al., 1999). This is not taken into account in the model and could explain partly the difference in the cortical thickness from the clinical observations. V -2-3-3 Initial conditions As it was probable that the initial conditions had an effect on the result some tests have been carried to assess if they effectively had. These tests aimed to see if the initial conditions change the results of the analysis but they did not aim to quantify this effect. The tests have been made on the first analysis, with the femur loaded only by the maximum hip joint reaction force frame in the gait motion. Two additional analyses have been made along the one with the initial conditions presented in III -2-4-1. Table 14 shows the results of these different analyses. The initial conditions appear to have a non negligible impact on the final results. The number of iteration before convergence, the number of effective trabecular elements and the trabecular and cortical volumes change with the initial cortical thickness and trabecular cross section radius.
First cortical thickness 0.1 8 8 First trabecular radius 0.1 2 0.1 # of iterations 27 50 52 Number of trabecular elements 91900 70022 61486 Trabecular volume 22439 25892 14048 Cortical volume 113391 105760 131007 Bone volume 135830 131652 145055

Table 14-Initial conditions tests results 56

These tests need to be followed by a deeper assessment of the impact of the initial condition, and by a research of the appropriate initial conditions or by a change of the algorithm to get rid of this dependency. Along with the initial conditions the first iterations need to be studied. Indeed in the human body the bones are built with a genetic contribution as well as the mechanostat contribution used in this study. This means that the bone already has a structure when it is firstly loaded. So the path to get to a loadable but not final structure has its importance on the final results. That can be controlled inside the iterative process by changing the way the different elements react to a strain, depending on its intensity, during the first iterations. Simple changes can be made on the minimal and maximal cortical thicknesses and trabecular radii or on the thickness and radius adjustment factors presented in III -2-7. V -2-3-4 Optimization It can be seen in table 13 that the optimization process is not fully effective. Indeed the total bone volume is lower when the analysis is made with 4 frames per motion than when 1 frame per motion is used, even if the frames used for the second analysis are included in the first analysis frames. This may be because the bone optimizes its structure to support new loads starting from an initial condition but without fully optimizing the structure. The adaptive process seems to optimize the shape but while limiting the change of sections. This issue needs to be studies along with the initial conditions dependency as the subjects seem to be linked.

V -3 Further works
Despite the strong similarities between the model developed and the clinical observations some further works need to be done in order to have a completely satisfying model.

57

Firstly the previously cited limitations need to be taken care of. These limitations have been developed in the previous section and will not be repeated in this one. They concern the mesh, the OpenSim model and the algorithm. But other improvements can be made to the model, even if they do not concern a limitation of the model. The number of frames on which the analysis is based can be increased and more motion can be integrated. This would maybe improve furthermore the correlation between the predicted structure and the clinical observations. Some improvements may be done also in the way the results are presented. A good way of investigating the structure and of comparing it to clinical observations is to use CT scans and density plots. But in order to do that the script that gives the porosity plot needs to be improved and accurately adjusted to the CT scans standards. Lastly it could be interesting to improve the source code of this project. The scripts are already coded so that their use is semi-automatic. It is possible to integrate all the pieces of code into one unique program, managed by a setting file and a good folder organization. This program could be used from the extraction of the OpenSim data to the plotting of the results. Another advantage of doing that is that a compiled program is generally faster. Also it may be possible to improve the efficiency of the code even if it is already faster than the first Phillips version.

58

VI Conclusion
Starting from previous work from Phillips, a new predictive model of the femur has been developed. Tackling some limitations raised in Phillips article the new model gives improved results. These limitations were mainly linked to the too simplified load case used in the first version of the model. A lower limb model from Luca Modenese has been used to extract data to load the mesh. A total of ten stages per motion for three motions have been put in the iterative process. The structural optimisation approach has shown strong correlation with the clinical observation. The computational time is really efficient compared to micro-scale models, even with the use of 30 load cases per analysis. This computational time which had remain really reasonable despite the increase in the finite element calculations needed let think that some improvement in the accuracy of the modelling are possible. Future works will need to focus on the OpenSim model, in the integration of new trabecular elements and in the development of result interpretation tools, like density and porosity plots or 3D structural plots. Finally, this model is a new step in the development of a promising meso-scale model, which presents huge advantages compared to both macro and micro-scale approach and good correlations with clinical observations. The progress made between the two versions of the model let the author hope that a fully integrated predictive model could be developed in the future. This model will then allow studying bone fractures, joint replacements and bone and muscle diseases.

59

VII Bibliography
Bahari, M.K., Farahmand, F., Rouhi, G. & Movahhedy, M.R., 2011. Prediction of shape and internal structure of the proximal femur using a modified level set method for structural topology optimisation. Computer Methods in Biomechanics and Biomedical Engineering. Preprint. Carter, D.R., Orr, T.E. & Fyhrie, D.P., 1989. Relationships between loading history and femoral. 22, pp.231-44. Culmann, K., 1886. Die Graphische Statik. Verlag Von Meyer & Zeller. Dixon, A.F., 1910. The Architecture of the Cancellous Tissue forming the Upper End of the Femur. 44(3), pp.223-30. Drake, R.L., Vogi, W. & Mitchell, A.W.M., 2004. Grays Anatomy for Students. Frost, H.M., 2003. Bone's Mechanostat: A 2003 Update. The Anatomical Record Part A: Discoveries in Molecular, Cellular, and Evolutionary Biology, 275A(2), pp.1081101. Gray, H., 1918. Anatomy of the human body. 20th ed. Philadelphia: Lea & Fediger. Bartleby.com, 2000. www.bartleby.com/107/. Gray, H. & Standring, S., 2008. Gray's anatomy : the anatomical basis of clinical practice / editor-in-chief, Susan Standring. 40th ed. Edinburgh: Churchill Livingstone. Imperial College London, S.B., 2011. Structural Modelling of the Musculoskeletal System. [Online] Available at: http://www2.imperial.ac.uk/structuralbiomechanics/projects/structuralmusculoskeletal.h tm [Accessed June 2011]. Klein Horsman, M.D. et al., 2007. Morphological muscle and joint parameters for musculoskeletal modelling of the lower extremity. 22(2), pp.239-47. Modenese, L., Phillips, A.T.M. & Bull, A.M.J., 2011. An open source lower limb model: Hip joint validation. Journal of Biomechanics, 44, pp.2185-93. Newell, R.L., 1997. The calcar femorale: A tale of historical neglect. 10(1), pp.27-33. Phillips, A.T.M., 2009. The femur as a musculo-skeletal construct: A free boundary condition modelling approach. Medical Engineering & Physics, 31(6), pp.673 - 680. Phillips, A.T.M., 2011. Structural Optimisation: Biomechanics of the Femur. In Proceedings of the ICE, Engineering and Computational Mechanics., 2011. Singh, M., Nagrath, A.R. & Maini, P.S., 1970. Changes in trabecular pattern of the upper end of the femur as an index of osteoporosis. The Journal of Bone and Joint Surgery, 52(3), pp.457-67. 60

Stephenson, P. & Seedhom, B., 1998. Cross-sectional geometry of the human femur in the mid-third region. Proceedings of the IMechE, Part H: Journal of Engineering in Medicine, 213, pp.159-66. Treece, G.M., Gee, A.H., Mayhew, P.M. & Poole, K.E.S.., 2010. High resolution cortical bone thickness measurement from clinical CT data. Medical Image Analysis, 14, pp.276-90. Tsubota, K.-i. et al., 2009. Computer simulation of trabecular remodelling in human proximal femur using large-scale voxel FE models: approach to understanding Wolff's law. 42, pp.1088-94. Turner, C.H. et al., 1999. The elastic properties of trabecular and cortical bone tissues are similar: results from two microscopic measurement techniques. Journal of Biomechanics, 32, pp.437-41. Von Meyer, G., 1887. Die Architektur der spongiosa. Archiv fr pathologische Anatomie und Physiologie und fr klinische Medizin, pp.615-28. Wolff, J., 1986. The law of bone remodelling. Springer-Verlag. Wu, G. et al., 2002. ISB recommendation on definitions of joint coordinate system of various joints for the reporting of human joint motion--part I: ankle, hip, and spine. International Society of Biomechanics. Journal of Biomechanics, 36(2).

61

Appendix A Source code OpenSim plug-in

ForceDirection.cpp

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

// ForceDirection.cpp //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // AUTHOR: Frank C. Anderson, Ajay Seth // MODIFIED BY: Alfred Thibon //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ /* Copyright (c) 2008 Stanford University * Use of the OpenSim software in source form is permitted provided that the following * conditions are met: * 1. The software is used only for non-commercial research and education. It may not * be used in relation to any commercial activity. * 2. The software is not distributed or redistributed. Software distribution is allowed * only through https://simtk.org/home/opensim. * 3. Use of the OpenSim software or derivatives must be acknowledged in all publications, * presentations, or documents describing work in which OpenSim or derivatives are used. * 4. Credits to developers may not be removed from executables * created from modifications of the source. * 5. Modifications of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR BUSINESS INTERRUPTION) OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ //============================================================================= // INCLUDES //============================================================================= #include <iostream> #include <fstream> #include <string> #include <cmath> #include <OpenSim/Simulation/Model/Model.h> #include <OpenSim/Simulation/SimbodyEngine/SimbodyEngine.h> #include <OpenSim/Simulation/Model/BodySet.h> #include <OpenSim/Simulation/Model/PointForceDirection.h> #include <OpenSim/Simulation/Model/PathPointSet.h> #include "ForceDirection.h"

using namespace OpenSim; using namespace std;

-1-

ForceDirection.cpp

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105

//============================================================================= // CONSTANTS //============================================================================= //============================================================================= // CONSTRUCTOR(S) AND DESTRUCTOR //============================================================================= //_____________________________________________________________________________ /** * Destructor. */ ForceDirection::~ForceDirection() { //deleteStorage(); } //_____________________________________________________________________________ /* * Construct an ForceDirection instance. * * @param aModel Model for which the analysis is to be run. */ ForceDirection::ForceDirection(Model *aModel) : Analysis(aModel), _boolref(_boolProp.getValueBool()), _boolcsv(_boolPropcsv.getValueBool()), _bodyNames(_strArrayProp.getValueStrArray()) { // make sure members point to NULL if not valid. setNull(); if(_model==NULL) return; // DESCRIPTION AND LABELS constructDescription(); constructColumnLabels(); // MARKER _firstrecord = 0; _muscleNames.setSize(0); _muscleNames.append("all"); } //_____________________________________________________________________________ /* * Construct an object from file. * * The object is constructed from the root element of the XML document. * The type of object is the tag name of the XML root element. * * @param aFileName File name of the document. */ ForceDirection::ForceDirection(const std::string &aFileName): Analysis(aFileName, false), _boolref(_boolProp.getValueBool()), _boolcsv(_boolPropcsv.getValueBool()), _bodyNames(_strArrayProp.getValueStrArray()) {
-2-

ForceDirection.cpp

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162

setNull(); // Read properties from XML updateFromXMLNode(); // MARKER _firstrecord = 0; _muscleNames.setSize(0); _muscleNames.append("all"); } // Copy constructor and virtual copy //_____________________________________________________________________________ /* * Copy constructor. * */ ForceDirection::ForceDirection(const ForceDirection &aForceDirection): Analysis(aForceDirection), _boolref(_boolProp.getValueBool()), _boolcsv(_boolPropcsv.getValueBool()), _bodyNames(_strArrayProp.getValueStrArray()) { setNull(); // COPY TYPE AND NAME *this = aForceDirection; // MARKER _firstrecord = 0; _muscleNames.setSize(0); _muscleNames.append("all"); } //_____________________________________________________________________________ /** * Clone * */ Object* ForceDirection::copy() const { ForceDirection *object = new ForceDirection(*this); return(object); } //============================================================================= // OPERATORS //============================================================================= //----------------------------------------------------------------------------// ASSIGNMENT //----------------------------------------------------------------------------//_____________________________________________________________________________ /* * Assign this object to the values of another. * * @return Reference to this object. */
-3-

ForceDirection.cpp

163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218

ForceDirection& ForceDirection:: operator=(const ForceDirection &aForceDirection) { // Base Class Analysis::operator=(aForceDirection); // Member Variables _bodyNames = aForceDirection._bodyNames; _muscleNames = aForceDirection._muscleNames; _groundname = aForceDirection._groundname; _firstrecord = aForceDirection._firstrecord; _boolref = aForceDirection._boolref; _boolcsv = aForceDirection._boolcsv; return(*this); } //_____________________________________________________________________________ /** * SetNull(). */ void ForceDirection:: setNull() { setType("ForceDirection"); setupProperties(); // Property Default Values _boolref = true; _boolcsv = false; _bodyNames.setSize(1); _bodyNames[0] = "all"; _muscleNames.setSize(1); _muscleNames[0] = "all"; } //_____________________________________________________________________________ /* * Set up the properties for your analysis. * * You should give each property a meaningful name and an informative comment. * The name you give each property is the tag that will be used in the XML * file. The comment will appear before the property in the XML file. * In addition, the comments are used for tool tips in the OpenSim GUI. * * All properties are added to the property set. Once added, they can be * read in and written to file. */ void ForceDirection:: setupProperties() { _boolProp.setName("local reference system"); _boolProp.setComment("Flag if the reference systems are local to the bodies"); _propertySet.append(&_boolProp); _boolPropcsv.setName("csv file"); _boolPropcsv.setComment("Flag if you want an additionnal csv file to the storage file");
-4-

ForceDirection.cpp

219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274

_propertySet.append(&_boolPropcsv); _strArrayProp.setName("body_names"); _strArrayProp.setComment("Names of the bodies on which to perform the analysis." "The key word 'All' indicates that the analysis should be performed for all bodies."); _propertySet.append(&_strArrayProp); } //============================================================================= // CONSTRUCTION METHODS //============================================================================= //_____________________________________________________________________________ /** * Construct a description for the body kinematics files. */ void ForceDirection:: constructDescription() { string descrip; descrip = "\nThis file contains muscles directions\n\n"; descrip += "\n The bodies linked to the muscles are given on a separate file"; descrip += "\n\n"; setDescription(descrip); } //_____________________________________________________________________________ /** * Construct column labels for the output results. * * This method needs to be called as necessary to update the column labels. */ void ForceDirection:: constructColumnLabels() { if(_model==NULL) return; Array<string> labels; labels.append("time");

const Set<Muscle>& muscleSet = _model->updMuscles(); if(_muscleNames[0] == "all"){ _muscleIndices.setSize(muscleSet.getSize()); // Get indices of all the muscles. for(int j=0;j<muscleSet.getSize();j++) _muscleIndices[j]=j; } else{ _muscleIndices.setSize(_muscleNames.getSize()); // Get indices of just the muscles listed.
-5-

ForceDirection.cpp

275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331

for(int j=0;j<_muscleNames.getSize();j++) _muscleIndices[j]=muscleSet.getIndex(_muscleNames[j]); } //Do the analysis on the muscles that are in the indices list string labref; if (_boolref && _bodyNames.getSize()==1 && _bodyNames[0]!="all"){ labref=_bodyNames[0]; } else if (_boolref /*&& _bodyNames.getSize()!=1*/){ labref="Local"; } else{ labref="Ground"; } for(int i=0; i<_muscleIndices.getSize(); i++) { const Muscle& muscle = muscleSet.get(_muscleIndices[i]); //%% PathPointSet path=muscle.getGeometryPath().getPathPointSet(); Body body1 = path[0].getBody(); Body body2 = path[path.getSize()-1].getBody(); bool testbod1 = false; bool testbod2 = false; if (_bodyNames[0]=="all"){ testbod1=true; testbod2=true; } else{ for (int j=0;j<_bodyNames.getSize();j++){ if (body1.getName()==_bodyNames[j]){ testbod1=true; } if (body2.getName()==_bodyNames[j]){ testbod2=true; } } } if (testbod1){ labels.append(muscle.getName() labels.append(muscle.getName() labels.append(muscle.getName() } if (testbod2){ labels.append(muscle.getName() labels.append(muscle.getName() labels.append(muscle.getName() }

+ "_X1"+"_on_"+labref); + "_Y1"+"_on_"+labref); + "_Z1"+"_on_"+labref);

+ "_X2"+"_on_"+labref); + "_Y2"+"_on_"+labref); + "_Z2"+"_on_"+labref);

} if (_labels[0]!="time"){ _labels.append("time"); for(int i=0; i<_muscleIndices.getSize(); i++) { const Muscle& muscle = muscleSet.get(_muscleIndices[i]); _labels.append(muscle.getName() + "_X1"); _labels.append(muscle.getName() + "_Y1");
-6-

ForceDirection.cpp

332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388

_labels.append(muscle.getName() _labels.append(muscle.getName() _labels.append(muscle.getName() _labels.append(muscle.getName() } } setColumnLabels(labels); }

+ + + +

"_Z1"); "_X2"); "_Y2"); "_Z2");

//_____________________________________________________________________________ /** * Set up storage objects. * */ void ForceDirection:: setupStorage() { // Directions _storeDir.reset(0); _storeDir.setName("Directions"); _storeDir.setDescription(getDescription()); _storeDir.setColumnLabels(getColumnLabels()); }

//============================================================================= // GET AND SET //============================================================================= //_____________________________________________________________________________ /** * Set the model for which this analysis is to be run. * * * @param aModel Model pointer */ void ForceDirection:: setModel(Model& aModel) { // SET THE MODEL IN THE BASE CLASS Analysis::setModel(aModel); // UPDATE VARIABLES IN THIS CLASS constructDescription(); constructColumnLabels(); setupStorage(); //Setup size of work array to hold body positions int numMuscles = _muscleIndices.getSize(); _muscledir.setSize(0); }

//=============================================================================
-7-

ForceDirection.cpp

389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444

// ANALYSIS //============================================================================= //_____________________________________________________________________________ /** * Compute and record the results. * */ int ForceDirection:: record(const SimTK::State& s) { // VARIABLES SimTK::Vec3 vec1,vec2,vec3,vec4; int firstrecord = 1; if (_firstrecord == 0){ firstrecord = 0; } _muscledir.setSize(0); // GROUND BODY const Body& ground = _model->getGroundBody(); _groundname = ground.getName(); // MUSCLE DIRECTIONS Set<Muscle>& muscleSet = _model->updMuscles(); _datagroundref.append(s.getTime()); _databodyref.append(s.getTime()); for(int i=0;i<_muscleIndices.getSize();i++) { const Muscle& muscle = muscleSet.get(_muscleIndices[i]); GeometryPath& path = muscle.getGeometryPath(); Array<PointForceDirection*> PFDs; path.getPointForceDirections(s, &PFDs); // Here each entry frame) // Points SimTK::Vec3 point1 SimTK::Vec3 point2 SimTK::Vec3 point3 SimTK::Vec3 point4 in the PFD array has a point, body & direction (in ground

= = = =

PFDs[0]->point(); PFDs[1]->point(); PFDs[PFDs.getSize()-2]->point(); PFDs[PFDs.getSize()-1]->point();

// Bodies const Body& const Body& const Body& const Body&

body1 body2 body3 body4

= = = =

PFDs[0]->body(); PFDs[1]->body(); PFDs[PFDs.getSize()-2]->body(); PFDs[PFDs.getSize()-1]->body();

// get the position of the points in the ground frame _model->getSimbodyEngine().getPosition(s,body1,point1,vec1); _model->getSimbodyEngine().getPosition(s,body2,point2,vec2); _model->getSimbodyEngine().getPosition(s,body3,point3,vec3); _model->getSimbodyEngine().getPosition(s,body4,point4,vec4);

-8-

ForceDirection.cpp

445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495

// calculating the directions in the ground frame SimTK::Vec3 dirrefground1 = vec2-vec1; SimTK::Vec3 dirrefground2 = vec3-vec4; // calculating the directions in the bodies frame // each direction is in the body where the muscle extremity is attached SimTK::Vec3 dirrefbody1; SimTK::Vec3 dirrefbody2; _model->getSimbodyEngine().transform(s,ground,dirrefground1,body1,dirrefbody1 ); _model->getSimbodyEngine().transform(s,ground,dirrefground2,body4,dirrefbody2 ); // normalizing the vectors double normground1 = sqrt(pow(dirrefground1[0],2)+pow(dirrefground1[1],2)+pow (dirrefground1[2],2)); double normground2 = sqrt(pow(dirrefground2[0],2)+pow(dirrefground2[1],2)+pow (dirrefground2[2],2)); double normbody1 = sqrt(pow(dirrefbody1[0],2)+pow(dirrefbody1[1],2)+pow( dirrefbody1[2],2)); double normbody2 = sqrt(pow(dirrefbody2[0],2)+pow(dirrefbody2[1],2)+pow( dirrefbody2[2],2)); for (int j=0;j<3;j++){ dirrefground1[j] /=normground1; } for (int j=0;j<3;j++){ dirrefground2[j] /=normground2; } for (int j=0;j<3;j++){ dirrefbody1[j] /=normbody1; } for (int j=0;j<3;j++){ dirrefbody2[j] /=normbody2; } // adding the bodies where the muscle are attached if (firstrecord==0){ _musclebodies.append(body1.getName()); _musclebodies.append(body4.getName()); } // Looking if the data are asked bool testbod1 = false; bool testbod2 = false; if (_bodyNames[0]=="all"){ testbod1=true; testbod2=true; } else{ for (int j=0;j<_bodyNames.getSize();j++){ if (body1.getName()==_bodyNames[j]){ testbod1=true; } if (body4.getName()==_bodyNames[j]){
-9-

ForceDirection.cpp

496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552

testbod2=true; } } } if (testbod1){ if (_boolref){ _muscledir.append(dirrefbody1[0]); _muscledir.append(dirrefbody1[1]); _muscledir.append(dirrefbody1[2]); } else{ _muscledir.append(dirrefground1[0]); _muscledir.append(dirrefground1[1]); _muscledir.append(dirrefground1[2]); } } if (testbod2){ if (_boolref){ _muscledir.append(dirrefbody2[0]); _muscledir.append(dirrefbody2[1]); _muscledir.append(dirrefbody2[2]); } else{ _muscledir.append(dirrefground2[0]); _muscledir.append(dirrefground2[1]); _muscledir.append(dirrefground2[2]); } } //%% for(int i=0; i < PFDs.getSize(); i++){ delete PFDs[i]; } _storeDir.append(s.getTime(),_muscledir.getSize(),&_muscledir[0]); // Data for the .csv (more flexible) _datagroundref.append(dirrefground1[0]); _datagroundref.append(dirrefground1[1]); _datagroundref.append(dirrefground1[2]); _datagroundref.append(dirrefground2[0]); _datagroundref.append(dirrefground2[1]); _datagroundref.append(dirrefground2[2]); _databodyref.append(dirrefbody1[0]); _databodyref.append(dirrefbody1[1]); _databodyref.append(dirrefbody1[2]); _databodyref.append(dirrefbody2[0]); _databodyref.append(dirrefbody2[1]); _databodyref.append(dirrefbody2[2]); } if (_firstrecord == 0){ _firstrecord = 1; }

-10-

ForceDirection.cpp

553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609

return(0); } //_____________________________________________________________________________ /** * This method is called at the beginning of an analysis so that any * necessary initializations may be performed. * * This method is meant to be called at the begining of an integration in * Model::integBeginCallback() and has the same argument list. * * @return -1 on error, 0 otherwise. */ int ForceDirection:: begin(SimTK::State& s) { if(!proceed()) return(0); // RESET STORAGE _storeDir.reset(s.getTime()); // RECORD int status = 0; if(_storeDir.getSize()<=0) { status = record(s); } return(status); } //_____________________________________________________________________________ /** * This method is called to perform the analysis. It can be called during * the execution of a forward integrations or after the integration by * feeding it the necessary data. * * When called during an integration, this method is meant to be called in * Model::integStepCallback(), which has the same argument list. * * @return -1 on error, 0 otherwise. */ int ForceDirection:: step(const SimTK::State& s, int stepNumber) { if(!proceed(stepNumber)) return(0); record(s); return(0); } //_____________________________________________________________________________ /** * This method is called at the end of an analysis so that any * necessary finalizations may be performed. * * This method is meant to be called at the end of an integration in * Model::integEndCallback() and has the same argument list. *
-11-

ForceDirection.cpp

610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665

* @return -1 on error, 0 otherwise. */ int ForceDirection:: end(SimTK::State& s) { if(!proceed()) return(0); record(s); return(0); }

//============================================================================= // IO //============================================================================= //_____________________________________________________________________________ /** * Print results. * * The file names are constructed as * aDir + "/" + aBaseName + "_" + ComponentName + aExtension * * @return 0 on success, -1 on error. */ int ForceDirection:: printResults(const string &aBaseName,const string &aDir,double aDT, const string &aExtension) { // DIRECTIONS _storeDir.scaleTime(_model->getTimeNormConstant()); Storage::printResult(&_storeDir,aBaseName+"_"+getName()+"_dir",aDir,aDT, aExtension); // MORE COMPLETE FILES if (_boolcsv){ int mm=6*_muscleIndices.getSize()+1; Array<bool> test; test.append(true); for (int i=0;i<_musclebodies.getSize();i++){ bool testint = false; if (_bodyNames[0]=="all"){ testint=true; } else{ for (int j=0;j<_bodyNames.getSize();j++){ if (_musclebodies[i]==_bodyNames[j]){ testint=true; } } } if (testint){ test.append(true);
-12-

ForceDirection.cpp

666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718

test.append(true); test.append(true); } else{ test.append(false); test.append(false); test.append(false); } } Array<double> time; _storeDir.getTimeColumnWithStartTime(time); //// File for the direction in the ground reference system if (!_boolref){ string filetxtground = aDir + "/" + aBaseName + "_" + getName()+ "_dir.csv"; ofstream fileground(filetxtground.c_str()); fileground << " ; ; ;This file gives the directions and the tendon force for the muscles in the ground reference system." << endl; fileground << " ; ; ;It gives also the bodies on which the muscles are attached."<<endl<<endl; fileground << " ;"; for (int i=1;i<_labels.getSize();i++){ if (test[i]){ fileground << _labels[i] << " ; "; } } fileground << endl; fileground << "Bodies;"; for (int i=0;i<_musclebodies.getSize();i++){ if (test[3*i+2]){ fileground << " ;" << _musclebodies[i] << "; ;"; } } fileground << endl; fileground <<_labels[0]<<endl; for (int i=0;i<_datagroundref.getSize();i++){ if (i%mm==0 && i!=0){ fileground << endl; } if (test[i%mm]){ fileground << _datagroundref[i] << " ; "; } } } //// File for the direction in the bodies reference system else { string filetxtbody = aDir + "/" + aBaseName + "_" + getName()+"_dir.csv"; ofstream filebody(filetxtbody.c_str()); filebody << " ; ; ;This file gives the directions and the tendon force for the muscles in the bodies reference system." << endl;
-13-

ForceDirection.cpp

719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750

filebody << " ; ; ;It gives also the bodies on which the muscles are attached. The directions are relative to these bodies."<<endl<<endl; filebody << " ;"; for (int i=1;i<_labels.getSize();i++){ if (test[i]){ filebody << _labels[i] << " ; "; } } filebody << endl; filebody << "Bodies(ref system);"; for (int i=0;i<_musclebodies.getSize();i++){ if (test[3*i+2]){ filebody << " ;" << _musclebodies[i] << "; ;"; } } filebody << endl << _labels[0]<<endl; for (int i=0;i<_databodyref.getSize();i++){ if (i%mm==0 && i!=0){ filebody << endl; } if (test[i%mm]){ filebody << _databodyref[i] << " ; "; } } } } return(0); }

-14-

ForceDirection.h

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

#ifndef _ForceDirection_h_ #define _ForceDirection_h_ // ForceDirection.h //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // AUTHOR: Frank C. Anderson, Ajay Seth // MODIFIED BY: Alfred Thibon //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ /* * Copyright (c) 2008, Stanford University. All rights reserved. * Use of the OpenSim software in source form is permitted provided that the following * conditions are met: * 1. The software is used only for non-commercial research and education. It may not * be used in relation to any commercial activity. * 2. The software is not distributed or redistributed. Software distribution is allowed * only through https://simtk.org/home/opensim. * 3. Use of the OpenSim software or derivatives must be acknowledged in all publications, * presentations, or documents describing work in which OpenSim or derivatives are used. * 4. Credits to developers may not be removed from executables * created from modifications of the source. * 5. Modifications of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR BUSINESS INTERRUPTION) OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ //============================================================================= // INCLUDES //============================================================================= // Headers define the various property types that OpenSim objects can read #include <OpenSim/Common/PropertyBool.h> #include <OpenSim/Common/PropertyBoolArray.h> #include <OpenSim/Common/PropertyInt.h> #include <OpenSim/Common/PropertyIntArray.h> #include <OpenSim/Common/PropertyDbl.h> #include <OpenSim/Common/PropertyDblArray.h> #include <OpenSim/Common/PropertyDblVec3.h> #include <OpenSim/Common/PropertyStr.h> #include <OpenSim/Common/PropertyStrArray.h> #include <OpenSim/Simulation/Model/Analysis.h>
-1-

ForceDirection.h

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104

// Header to define plugin (DLL) interface #include "osimPluginDLL.h"

//============================================================================= //============================================================================= namespace OpenSim { class Model;

class OSIMPLUGIN_API ForceDirection : public Analysis { //============================================================================= // DATA //============================================================================= private: protected: /** Boolean Property */ PropertyBool _boolProp; bool &_boolref; PropertyBool _boolPropcsv; bool &_boolcsv; /** String Array Property */ PropertyStrArray _strArrayProp; Array<std::string> &_bodyNames; /*PropertyStrArray _strArrayPropMuscle;*/ Array<std::string> _muscleNames;

// Indices of bodies for kinematics to be reported and muscle for direction Array<int> _muscleIndices; Array<std::string> _musclebodies; /** Storage for recording body positions and muscle direction*/ Storage _storeDir;

/** Internal work arrays to hold muscle directions at each time step. */ Array<double> _muscledir; /** Internal array for storing data to write a second result file (without the storage class) */ Array<double> _datagroundref; Array<double> _databodyref; Array<std::string> _labels; // Markers std::string _groundname; int _firstrecord;

//=============================================================================
-2-

ForceDirection.h

105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161

// METHODS //============================================================================= public: /** * Construct an ForceDirection instance with a Model. * * @param aModel Model for which the analysis is to be run. */ ForceDirection(Model *aModel=0);

/** * Construct an object from file. * * @param aFileName File name of the document. */ ForceDirection(const std::string &aFileName); /** * Copy constructor. */ ForceDirection(const ForceDirection& aObject); //------------------------------------------------------------------------// DESTRUCTOR //------------------------------------------------------------------------virtual ~ForceDirection(); /** Clone of object */ virtual Object* copy() const; private: /** Zero data and set pointers to Null */ void setNull(); /** * Set up the properties for the analysis. */ void setupProperties(); public: #ifndef SWIG /** * Assign this object to the values of another. * * @return Reference to this object. */ ForceDirection& operator=(const ForceDirection &aForceDirection); #endif //========================== Required Methods ============================= //------------------------------------------------------------------------// GET AND SET //------------------------------------------------------------------------virtual void setModel(Model& aModel); //-------------------------------------------------------------------------3-

ForceDirection.h

162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193

// INTEGRATION //------------------------------------------------------------------------virtual int begin(SimTK::State& s); virtual int step(const SimTK::State& s, int stepNumber); virtual int end(SimTK::State& s); //------------------------------------------------------------------------// IO //------------------------------------------------------------------------virtual int printResults(const std::string &aBaseName,const std::string &aDir="", double aDT=-1.0,const std::string &aExtension=".sto");

protected: //========================== Internal Methods ============================= int record(const SimTK::State& s); void constructDescription(); void constructColumnLabels(); void setupStorage(); //============================================================================= }; // END of class ForceDirection }; //namespace //============================================================================= //============================================================================= #endif // #ifndef __ForceDirection_h__

-4-

Appendix B Source code joint parts

Joints.m

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

%%% Creates a joint part to load the femur %%% Uses external files to load and store data %% Hip joint file clear all; close all; %% load mesh data nodes = dlmread('NodesSolidMeshEL5.txt',','); nodeCoords = nodes(1:5207,2:end); nodes(:,2:end)=[]; nodes(5208:end,:)=[]; originalCorticalElements = dlmread('SurfaceElementsMeshEL5.txt',','); originalCorticalNodes = originalCorticalElements(:,2:end); headnodes = importdata('Headnodes.xls'); headNodes = headnodes.Feuil1; % write all the Abaqus input file in several files to a future copy paste in the % constant 'sample.inp' first part of the first abaqus input file used to run the % iterative process file = fopen('parts.txt','w'); fprintf(file,'*Heading\n** Job name: Job-1 Model name: Femoral head cap\n'); fprintf(file,'** Generated by: Abaqus/CAE 6.10-1\n*Preprint, echo=NO, model=NO, history=NO, contact=NO\n**\n** PARTS\n**\n'); %% Soft part fprintf(file,'*Part, name=HEADSOFT\n*Node\n'); for n=1:size(headNodes,1) fprintf(file,'%i, %f, %f, %f\n',[headNodes(n) nodeCoords(headNodes(n),:)]); end % create a part by scaling external nodes of the femur from the joint centre S = diag([1.1 1.1 1.1]); outerNodes = zeros(size(nodes,1),3); for n=1:size(nodes,1) outerNodes(n,1:3) = (S * nodeCoords(n,:)')'; end for n=1:size(headNodes,1) fprintf(file,'%i, %f, %f, %f\n',[(headNodes(n)+5207) outerNodes(headNodes(n),:)]); end fprintf(file,'*ELEMENT, type=C3D6\n'); j=1; for n=1:size(originalCorticalElements,1) if (size(find(headNodes==originalCorticalNodes(n,1),1),1)~=0 && size(find( headNodes==originalCorticalNodes(n,2),1),1)~=0 && size(find(headNodes== originalCorticalNodes(n,3),1),1)~=0); fprintf(file,'%i, %i, %i, %i, %i, %i, %i\n',[n originalCorticalNodes(n,:) originalCorticalNodes(n,:)+5207]); elset(j) = n; j=j+1; end end fprintf(file,'*Elset, elset=ELS\n'); for n=1:size(elset,2) fprintf(file,'%i, ',elset(n)); if n/16 == round(n/16); fprintf(file,'\n'); end
-1-

43 44 45 46 47 48 49 50 51 52 53

Joints.m

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82

end fprintf(file,'\n**\n*Solid Section, elset=ELS, material=HSOFT\n , \n**\n'); fprintf(file,'*End Part\n**\n'); %% Hard part fprintf(file,'*Part, name=HEADHARD\n*Node\n'); for n=1:size(headNodes,1) fprintf(file,'%i, %f, %f, %f\n',[headNodes(n) outerNodes(headNodes(n),:)]); end S = diag([1.2 1.2 1.2]); outerNodes2 = zeros(size(nodes,1),3); for n=1:size(nodes,1) outerNodes2(n,1:3) = (S * nodeCoords(n,:)')'; end for n=1:size(headNodes,1) fprintf(file,'%i, %f, %f, %f\n',[(headNodes(n)+5207) outerNodes2(headNodes(n ),:)]); end fprintf(file, '20000, -1.56913, 0.22810, -0.38644\n'); fprintf(file, '20001, 10.63371, -167.79122, 15.78757\n'); fprintf(file,'*ELEMENT, type=B31\n'); for n=1:size(headNodes,1) fprintf(file,'%i, %i, %i\n',[20000+n headNodes(n)+5207 20000]); end for n=1:size(headNodes,1) fprintf(file,'%i, %i, %i\n',[30000+n headNodes(n)+5207 20001]); end fprintf(file,'*ELEMENT, type=C3D6\n'); for n=1:size(originalCorticalElements,1) if (size(find(headNodes==originalCorticalNodes(n,1),1),1)~=0 && size(find( headNodes==originalCorticalNodes(n,2),1),1)~=0 && size(find(headNodes== originalCorticalNodes(n,3),1),1)~=0); fprintf(file,'%i, %i, %i, %i, %i, %i, %i\n',[n originalCorticalNodes(n,:) originalCorticalNodes(n,:)+5207]); end end fprintf(file,'*Elset, elset=BEA1\n'); for n=1:size(headNodes,1) fprintf(file,'%i, ',20000+n); if n/16 == round(n/16); fprintf(file,'\n'); end end fprintf(file,'\n'); fprintf(file,'*Elset, elset=BEA2\n'); for n=1:size(headNodes,1) fprintf(file,'%i, ',30000+n); if n/16 == round(n/16); fprintf(file,'\n'); end end fprintf(file,'\n'); fprintf(file,'*Elset, elset=ELS\n'); for n=1:size(elset,2) fprintf(file,'%i, ',elset(n)); if n/16 == round(n/16); fprintf(file,'\n');
-2-

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106

Joints.m

107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141

end end fprintf(file,'\n**\n*Solid Section, elset=ELS, material=HHARD\n , \n**\n'); fprintf(file,'** Section: Section-beam Profile: Profile-circ\n*Beam general section, elset=BEA1, section=CIRC\n'); fprintf(file,'10.\n0.,0.,-1.\n210000.,85000.\n'); fprintf(file,'** Section: Section-beam Profile: Profile-circ\n*Beam general section, elset=BEA2, section=CIRC\n'); fprintf(file,'10.\n0.,0.,-1.\n210000.,85000.\n'); fprintf(file,'*End Part\n**\n**\n'); close all; file = fopen('assembly.txt','w'); fprintf(file,'**\n** ASSEMBLY**\n*Assembly, name=Assembly\n**\n'); fprintf(file,'*Instance, name=HEADSOFT-1, part=HEADSOFT\n*End Instance\n**\n'); fprintf(file,'*Instance, name=HEADHARD-1, part=HEADHARD\n*End Instance\n**\n'); fprintf(file,'*Elset, elset=_CP-1-HEADSOFT-1_S2, internal, instance=HEADSOFT-1\n'); for n=1:size(elset,2) fprintf(file,'%i, ',elset(n)); if n/16 == round(n/16); fprintf(file,'\n'); end end fprintf(file,'\n**\n'); fprintf(file,'*Surface, type=ELEMENT, name=CP-1-HEADSOFT-1\n_CP-1-HEADSOFT-1_S2, S2\n**\n'); fprintf(file,'*Elset, elset=_CP-1-HEADHARD-1_S1, internal, instance=HEADHARD-1\n'); for n=1:size(elset,2) fprintf(file,'%i, ',elset(n)); if n/16 == round(n/16); fprintf(file,'\n'); end end fprintf(file,'\n**\n'); fprintf(file,'*Surface, type=ELEMENT, name=CP-1-HEADHARD-1\n_CP-1-HEADHARD-1_S1, S1\n**\n'); fprintf(file,'*End Assembly'); dlmwrite('elhead.txt',elset); % write the element in the part to a future use in the iterative process close all

-3-

Appendix C Source code first input file

femur_attachment_data_processing.m

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

%%% Matlab script to build the first Abaqus input file %%% Creates a 10 steps input files from the gait motion clear all; close all; frames = 10; xlslabels = cell(9); xlslabels = [{'Step'} {'RF'} {'RF1'} {'RF2'}... {'RF3'} {'RM'} {'RM1'} {'RM2'} {'RM3'}]; xlswrite('reactioninfo.xls',xlslabels,'A1:I1'); for k=0:frames-1 %% Parameters plotBool = false; mapping = false; frame = 'hip'; % 'hip' for maximal hip force frame % 'knee' for maximal knee force frame repartition = true; frames = 10; framesnber = k; % <frames step = framesnber+1; %% location of file parts % load in the muscle force file forcefilepath = ['Data\WALK_HSRWN4-2.0-NO_Flv_StaticOptimization_force','.sto']; Force = importdata(forcefilepath); % load in the joint force file jointfilepath = ['data\WALK_HSRWN4-2.0-NO_Flv_JointReaction_ReactionLoads','.sto' ]; Joint = importdata(jointfilepath); % dispatch the data Hip.data = Joint.data(:,[1:7]); Knee.data = Joint.data(:,[1,8:13]); Patella.data = Joint.data(:,[1,14:19]); % load in the force direction file dirfilepath = ['Data\WALK_HSRWN4_ForceDirection_dir','.sto']; Dir = importdata(dirfilepath); % load in the body position posfilepath = ['Data\WALK_HSRWN4_BodyKinematics_pos_global','.sto']; Pos = importdata(posfilepath); % load in the body velocity velfilepath = ['Data\WALK_HSRWN4_BodyKinematics_vel_global','.sto']; Vel = importdata(velfilepath); % load in the attachment point file AttachPt = importdata('Data\Attachnodes.csv');
-1-

femur_attachment_data_processing.m

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110

%% Creating a structure for storing data for each muscle % Selecting the frame switch frame case 'hip' hipF=0; for n=1:size(Hip.data,1) if (Hip.data(n,2)^2+Hip.data(n,3)^2+Hip.data(n,4)^2>hipF); hipF = (Hip.data(n,2)^2+Hip.data(n,3)^2+Hip.data(n,4)^2); nFrame=n; end end case 'knee' kneeF=0; for n=1:size(Knee.data,1) if (Knee.data(n,2)^2+Knee.data(n,3)^2+Knee.data(n,4)^2>kneeF); kneeF = (Knee.data(n,2)^2+Knee.data(n,3)^2+Knee.data(n,4)^2); nFrame=n; end end end if repartition; nFrame = rem(nFrame + framesnber*fix(size(Hip.data,1)/frames),size(Hip.data,1 )) % to allow the calculation of the acceleration from the velocity even if nFrame==1 if nFrame == 1; nFrame = 2; end if nFrame == size(Hip.data,1); nFrame = size(Hip.data,1)-1; end end % Extracting the data (files need to be checked to be sure that the muscle % are in the same order in all of them) Muscle.colheaders = AttachPt.textdata'; index = [8:32,34:36,49:79,82:100,110:120,144:169]; % muscles attached to the femur Muscle.force = Force.data(nFrame,index); Muscle.direction = zeros(3,size(AttachPt.textdata,1)); for n=1:size(AttachPt.textdata,1) Muscle.direction(1:3,n) = Dir.data(nFrame, 2+3*(n-1):1+3*n)'; Muscle.direction(1:3,n) = Muscle.direction(1:3,n)/abs(norm(Muscle.direction(1 :3,n))); end %% Node map Muscle.node = AttachPt.data(:,4); %% Calculation of the inertia force and of the weight legmass = 7.718110541; c1 = cosd(Pos.data(nFrame,5));
-2-

femur_attachment_data_processing.m

111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159

s1 = sind(Pos.data(nFrame,5)); c2 = cosd(Pos.data(nFrame,6)); s2 = sind(Pos.data(nFrame,6)); c3 = cosd(Pos.data(nFrame,7)); s3 = sind(Pos.data(nFrame,7)); A = [c2*c3 (-c2*s3) s2;(c1*s3+c3*s1*s2) (c1*c3-s1*s2*s3) -c2*s1;(s1*s3-c1*c3*s2) c1*s2*s3+c3*s1 c1*c2]; weight = [0 -9.80665000 0]; % Inertial force acc =zeros(1, 3); acc(1) = (Vel.data(nFrame-1,2)-Vel.data(nFrame+1,2))/(Vel.data(nFrame-1,1)-Vel. data(nFrame+1,1)); acc(2) = (Vel.data(nFrame-1,3)-Vel.data(nFrame+1,3))/(Vel.data(nFrame-1,1)-Vel. data(nFrame+1,1)); acc(3) = (Vel.data(nFrame-1,4)-Vel.data(nFrame+1,4))/(Vel.data(nFrame-1,1)-Vel. data(nFrame+1,1)); weight = weight - acc; weight = (A'*weight').*legmass;

%% Print load file (from Assembly to end) file = fopen('Assembly_Gait.txt','w'); % Instance fprintf(file,'****\n'); fprintf(file,'**** End of section data\n'); fprintf(file,'****\n'); fprintf(file,'*End Part\n'); fprintf(file,'** \n'); fprintf(file,'**\n'); fprintf(file,'** ASSEMBLY\n**\n*Assembly, name=Assembly\n**\n'); fprintf(file,'*Instance, name=STRUCTURALFEMUR-1, part=STRUCTURALFEMUR\n*End Instance\n** \n'); fprintf(file,'*Instance, name=HEADSOFT-1, part=HEADSOFT\n*End Instance\n** fprintf(file,'*Instance, name=HEADHARD-1, part=HEADHARD\n*End Instance\n** fprintf(file,'*Instance, name=KNEESOFT-1, part=KNEESOFT\n*End Instance\n** fprintf(file,'*Instance, name=KNEEHARD-1, part=KNEEHARD\n*End Instance\n** fprintf(file,'*Instance, name=PATELLASOFT-1, part=PATELLASOFT\n*End Instance\n** \n'); fprintf(file,'*Instance, name=PATELLAHARD-1, part=PATELLAHARD\n*End Instance\n** \n'); % Muscle nodes for i=1:size(Muscle.colheaders,2) fprintf(file,'*Nset, nset=_PTLOADSET_%03i, internal, instance=STRUCTURALFEMUR-1\n',i); fprintf(file,'%i\n',Muscle.node(i)); end % Knee ctr fprintf(file,'*Nset, nset=_KNEECTR, internal, instance=KNEEHARD-1\n'); fprintf(file,'20000\n'); % Hip ctr
-3-

\n'); \n'); \n'); \n');

femur_attachment_data_processing.m

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211

fprintf(file,'*Nset, nset=_HIPCTR, internal, instance=HEADHARD-1\n'); fprintf(file,'20000\n'); % Patella ctr fprintf(file,'*Nset, nset=_PATCTR, internal, instance=PATELLAHARD-1\n'); fprintf(file,'20000\n'); % Leg ctr (x2 linked to the hip or the knee parts) fprintf(file,'*Nset, nset=_LEGCTR1, internal, instance=HEADHARD-1\n'); fprintf(file,'20001\n'); fprintf(file,'*Nset, nset=_LEGCTR2, internal, instance=KNEEHARD-1\n'); fprintf(file,'20001\n'); % Tie the joint parts to the femur, loading set of element from external files fprintf(file,'*Elset, elset=_CP-HEADSOFT-1, internal, instance=HEADSOFT-1\n'); elsetHead = dlmread('Data/elhead.txt'); for n=1:size(elsetHead,2) fprintf(file,'%i, ',elsetHead(n)); if n/16 == round(n/16); fprintf(file,'\n'); end end fprintf(file,'\n'); fprintf(file,'*Surface, type=ELEMENT, name=CP-1-HEADSOFT-1\n_CP-HEADSOFT-1, S1\n' ); fprintf(file,'*Surface, type=ELEMENT, name=CP-2-HEADSOFT-1\n_CP-HEADSOFT-1, S2\n' ); fprintf(file,'*Elset, elset=_CP-HEADHARD-1, internal, instance=HEADHARD-1\n'); for n=1:size(elsetHead,2) fprintf(file,'%i, ',elsetHead(n)); if n/16 == round(n/16); fprintf(file,'\n'); end end fprintf(file,'\n'); fprintf(file,'*Surface, type=ELEMENT, name=CP-1-HEADHARD-1\n_CP-HEADHARD-1, S1\n' ); fprintf(file,'*Elset, elset=_CP-KNEESOFT-1, internal, instance=KNEESOFT-1\n'); elsetKnee = dlmread('Data/elknee.txt'); for n=1:size(elsetKnee,2) fprintf(file,'%i, ',elsetKnee(n)); if n/16 == round(n/16); fprintf(file,'\n'); end end fprintf(file,'\n'); fprintf(file,'*Surface, type=ELEMENT, name=CP-1-KNEESOFT-1\n_CP-KNEESOFT-1, S1\n' ); fprintf(file,'*Surface, type=ELEMENT, name=CP-2-KNEESOFT-1\n_CP-KNEESOFT-1, S2\n' ); fprintf(file,'*Elset, elset=_CP-KNEEHARD-1, internal, instance=KNEEHARD-1\n'); for n=1:size(elsetKnee,2) fprintf(file,'%i, ',elsetKnee(n)); if n/16 == round(n/16); fprintf(file,'\n'); end
-4-

femur_attachment_data_processing.m

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257

end fprintf(file,'\n'); fprintf(file,'*Surface, type=ELEMENT, name=CP-1-KNEEHARD-1\n_CP-KNEEHARD-1, S1\n' ); fprintf(file,'*Elset, elset=_CP-PATELLASOFT-1, internal, instance=PATELLASOFT-1\n'); elsetPatella = dlmread('Data/elpatella.txt'); for n=1:size(elsetPatella,2) fprintf(file,'%i, ',elsetPatella(n)); if n/16 == round(n/16); fprintf(file,'\n'); end end fprintf(file,'\n'); fprintf(file,'*Surface, type=ELEMENT, name=CP-1-PATELLASOFT-1\n_CP-PATELLASOFT-1, S1\n'); fprintf(file,'*Surface, type=ELEMENT, name=CP-2-PATELLASOFT-1\n_CP-PATELLASOFT-1, S2\n'); fprintf(file,'*Elset, elset=_CP-PATELLAHARD-1, internal, instance=PATELLAHARD-1\n'); for n=1:size(elsetPatella,2) fprintf(file,'%i, ',elsetPatella(n)); if n/16 == round(n/16); fprintf(file,'\n'); end end fprintf(file,'\n'); fprintf(file,'*Surface, type=ELEMENT, name=CP-1-PATELLAHARD-1\n_CP-PATELLAHARD-1, S1\n'); fprintf(file,'*Elset, elset=_CP-STRUCTURALFEMUR-1, instance=STRUCTURALFEMUR-1, generate\n'); fprintf(file,'1, 10410, 1\n'); fprintf(file,'*Surface, type=ELEMENT, name=CP-1-STRUCTURALFEMUR-1\n_CP-STRUCTURALFEMUR-1, SPOS\n'); fprintf(file,'** CONSTRAINTS\n**\n'); fprintf(file,'** Constraint: CP-1-HEADSOFT-1-STRUCTURALFEMUR-1\n'); fprintf(file,'*Tie, name=CP-1-HEADSOFT-1-STRUCTURALFEMUR-1, adjust=yes, no thickness\n'); fprintf(file,'CP-1-HEADSOFT-1, CP-1-STRUCTURALFEMUR-1\n'); fprintf(file,'** Constraint: CP-1-KNEESOFT-1-STRUCTURALFEMUR-1\n'); fprintf(file,'*Tie, name=CP-1-KNEESOFT-1-STRUCTURALFEMUR-1, adjust=yes, no thickness\n'); fprintf(file,'CP-1-KNEESOFT-1, CP-1-STRUCTURALFEMUR-1\n'); fprintf(file,'** Constraint: CP-1-PATELLASOFT-1-STRUCTURALFEMUR-1\n'); fprintf(file,'*Tie, name=CP-1-PATELLASOFT-1-STRUCTURALFEMUR-1, adjust=yes, no thickness\n'); fprintf(file,'CP-1-PATELLASOFT-1, CP-1-STRUCTURALFEMUR-1\n'); fprintf(file,'** Constraint: CP-1-HEADSOFT-1-HEADHARD-1\n'); fprintf(file,'*Tie, name=CP-1-HEADSOFT-1-HEADHARD-1, adjust=yes, no thickness\n'); fprintf(file,'CP-2-HEADSOFT-1, CP-1-HEADHARD-1\n');
-5-

femur_attachment_data_processing.m

258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308

fprintf(file,'** Constraint: CP-1-KNEESOFT-1-KNEEHARD-1\n'); fprintf(file,'*Tie, name=CP-1-KNEESOFT-1-KNEEHARD-1, adjust=yes, no thickness\n'); fprintf(file,'CP-2-KNEESOFT-1, CP-1-KNEEHARD-1\n'); fprintf(file,'** Constraint: CP-1-PATELLASOFT-1-PATELLAHARD-1\n'); fprintf(file,'*Tie, name=CP-1-PATELLASOFT-1-PATELLAHARD-1, adjust=yes, no thickness\n'); fprintf(file,'CP-2-PATELLASOFT-1, CP-1-PATELLAHARD-1\n'); fprintf(file,'*End Assembly\n**\n'); % Define the materials fprintf(file,'** MATERIALS\n**\n*Material, 0.3\n**\n'); fprintf(file,'** MATERIALS\n**\n*Material, fprintf(file,'** MATERIALS\n**\n*Material, 0.3\n**\n'); fprintf(file,'** MATERIALS\n**\n*Material, 0.3\n**\n');

name=BONE\n*Elastic\n18000., name=HSOFT\n*Elastic\n10., 0.3\n**\n'); name=HHARD\n*Elastic\n210000., name=HPHARD\n*Elastic\n2100.,

% Boundary conditions fprintf(file,'** BOUNDARY CONDITIONS\n**\n'); fprintf(file,'** Name: BC-1 Type: Displacement/Rotation\n*Boundary\n'); fprintf(file,'_KNEECTR, 1, 6\n'); fclose(file); % Print the loads filename = ['Load_Gait',num2str(step,'%02i'),'.txt']; file = fopen(filename,'w'); fprintf(file,'** ----------------------------------------------------------------\n**\n'); fprintf(file,'** STEP: Step-%02i\n**\n*Step, name=Step-%02i\n*Static\n1., 1., 1e-05, 1.\n** ',[step step]); % loads fprintf(file,'** LOADS\n** \n'); % muscle loads for i=1:size(Muscle.colheaders,2) s=Muscle.colheaders{1,i}; fprintf(file,'** Name: CFORCE-%03i-%s Type: Concentrated force\n',[i s]); fprintf(file,'*Cload, op=NEW\n'); x = Muscle.direction(1,i)*Muscle.force(i); y = Muscle.direction(2,i)*Muscle.force(i); z = Muscle.direction(3,i)*Muscle.force(i); fprintf(file,'_PTLOADSET_%03i, 1, %f\n',[i x]); fprintf(file,'_PTLOADSET_%03i, 2, %f\n',[i y]); fprintf(file,'_PTLOADSET_%03i, 3, %f\n',[i z]); end % Hip joint reaction force fprintf(file,'** Name: HIPJRF Type: Concentrated force\n'); fprintf(file,'*Cload, op=NEW\n'); fprintf(file,'_HIPCTR, 1, %f\n',Hip.data(nFrame,2)); fprintf(file,'_HIPCTR, 2, %f\n',Hip.data(nFrame,3)); fprintf(file,'_HIPCTR, 3, %f\n',Hip.data(nFrame,4));
-6-

femur_attachment_data_processing.m

309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351

% Knee joint reaction force fprintf(file,'** Name: KNEEJRF Type: Concentrated force\n'); fprintf(file,'*Cload, op=NEW\n'); fprintf(file,'_KNEECTR, 1, %f\n',Knee.data(nFrame,2)); fprintf(file,'_KNEECTR, 2, %f\n',Knee.data(nFrame,3)); fprintf(file,'_KNEECTR, 3, %f\n',Knee.data(nFrame,4)); fprintf(file,'** Name: KNEEJRM Type: Moment\n'); fprintf(file,'*Cload, op=NEW\n'); fprintf(file,'_KNEECTR, 4, %f\n',Knee.data(nFrame,5)*1000); fprintf(file,'_KNEECTR, 5, %f\n',Knee.data(nFrame,6)*1000); fprintf(file,'_KNEECTR, 6, %f\n',Knee.data(nFrame,7)*1000); % Patella joint reaction force fprintf(file,'** Name: PATELLAJRF Type: Concentrated force\n'); fprintf(file,'*Cload, op=NEW\n'); fprintf(file,'_PATCTR, 1, %f\n',Patella.data(nFrame,2)); fprintf(file,'_PATCTR, 2, %f\n',Patella.data(nFrame,3)); fprintf(file,'_PATCTR, 3, %f\n',Patella.data(nFrame,4)); fprintf(file,'** Name: PATELLAJRM Type: Moment\n'); fprintf(file,'*Cload, op=NEW\n'); fprintf(file,'_PATCTR, 4, %f\n',Patella.data(nFrame,5)*1000); fprintf(file,'_PATCTR, 5, %f\n',Patella.data(nFrame,6)*1000); fprintf(file,'_PATCTR, 6, %f\n',Patella.data(nFrame,7)*1000); % Weight and Inertia forces fprintf(file,'** Name: WEIGHT1 Type: Concentrated force\n'); fprintf(file,'*Cload, op=NEW\n'); fprintf(file,'_LEGCTR1, 1, %f\n',weight(1)/2); fprintf(file,'_LEGCTR1, 2, %f\n',weight(2)/2); fprintf(file,'_LEGCTR1, 3, %f\n',weight(3)/2); fprintf(file,'** Name: WEIGHT2 Type: Concentrated force\n'); fprintf(file,'*Cload, op=NEW\n'); fprintf(file,'_LEGCTR2, 1, %f\n',weight(1)/2); fprintf(file,'_LEGCTR2, 2, %f\n',weight(2)/2); fprintf(file,'_LEGCTR2, 3, %f\n',weight(3)/2); fprintf(file,'**\n');

fprintf(file,'** OUTPUT REQUESTS\n** \n*Restart, write, frequency=0\n** \n** FIELD OUTPUT: F-Output-2\n**\n *Output, field\n*Element Output, directions=YES\nSTH, \n** \n** FIELD OUTPUT: F-Output-1\n** \n*Output, field, variable=PRESELECT\n** \n** HISTORY OUTPUT: H-Output-1\n** \n*Output, history, variable=PRESELECT\n*End Step\n'); fclose(file); % Bind the different files to create the input file % Also keep the different files separatly to bind them with other motion files doscommand = ['copy /b .\Data\sample.inp + Assembly_Gait.txt+Load_Gait',num2str( step,'%02i'),'.txt CorticalAndTrabecularIteration_Gait_0001.inp']; dos(doscommand); doscommand = ['copy /b Load_Gait',num2str(step,'%02i'),'.txt ..\Copy_loads\Load_Gait',num2str(step,'%02i'),'.txt'];
-7-

352 353 354 355 356 357 358 359

femur_attachment_data_processing.m

360 361 362 363 364 365 366 367 368 369 370

dos(doscommand); % print reaction forces and reaction moment at the knee RF = norm([Knee.data(nFrame,2) Knee.data(nFrame,3) Knee.data(nFrame,4)]); RM = norm([Knee.data(nFrame,5) Knee.data(nFrame,6) Knee.data(nFrame,7)]); infomatrix = [framesnber+1 RF Knee.data(nFrame,2) Knee.data(nFrame,3) Knee.data( nFrame,4) RM Knee.data(nFrame,5) Knee.data(nFrame,6) Knee.data(nFrame,7)]; aa1=['A', num2str(framesnber+2)]; aa2=['I', num2str(framesnber+2)]; aa3=[aa1,':',aa2]; xlswrite('reactioninfo.xls',infomatrix,aa3); sumload = sum(Muscle.force) + norm([Knee.data(nFrame,2) Knee.data(nFrame,3) Knee. data(nFrame,4)]) + norm([Hip.data(nFrame,2) Hip.data(nFrame,3) Hip.data(nFrame,4 )]) + norm([Patella.data(nFrame,2) Patella.data(nFrame,3) Patella.data(nFrame,4 )]); xlswrite('reactioninfo.xls',sumload,['J',num2str(framesnber+2),':J',num2str( framesnber+2)]); if k~=frames-1 clear all; close all; end end disp 'Ended without error';

371 372 373 374 375 376 377 378

-8-

Appendix D Source code iterative process

CorticalAndTrabecularIterations.m

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

% Cortical and Trabecular Element Iterations by Andrew Phillips % Modified by Alfred Thibon %% Init clear all close all % will always start with itnum=1 % and write this to file itnum=1; itnumfile=fopen('iteration.txt','w'); fprintf(itnumfile,'%i',itnum); fclose(itnumfile); disp(['Iteration Number ',num2str(itnum,'%04i')]); % clean dos('del .\Elements\*.txt'); dos('del *.lck'); dos('del abaqus.rpy.*'); dos('move .\Abaqus\CorticalAndTrabecularIteration0001.inp'); dos('del .\Abaqus\*.odb'); dos('del .\Abaqus\*.inp'); stop=0; trabecularstop=0; corticalstop=0; xlslabels = cell(13); xlslabels = [{'Itnum'} {'Number of trab elt'} {'node Connectivity mean'} {'stdev'}... {'min'} {'max'} {'trab similarity'} {'cort similarity'} {'trab elt ratio'} { 'trab volume'} {'cortical volume'}... {'hour'} {'date'}]; xlswrite('iterationinfo.xls',xlslabels,'A2:M2'); %% Original elements % getting the original trabecular element definitions originalElements = dlmread('trabecularelements.txt',','); nodes = dlmread('NodesSolidMeshEL5.txt',','); nodeCoords = nodes(:,2:end); nodes(:,2:end)=[]; originalElementNums = originalElements(:,1); originalElementNodes = originalElements(:,2:3); originalElementLengths = sqrt(sum((nodeCoords(originalElementNodes(:,2),:)-nodeCoords (originalElementNodes(:,1),:)).^2,2)); numElements = size(originalElements,1); numNodes = size(nodes,1); % getting the original cortical element definitions originalCorticalElements = dlmread('SurfaceElementsMeshEL5.txt',','); % finding face area for each of the cortical elements originalCorticalNodes = originalCorticalElements(:,2:end); % side vectors faceside01 = [nodeCoords(originalCorticalNodes(:,2),:)-nodeCoords( originalCorticalNodes(:,1),:)]; faceside02 = [nodeCoords(originalCorticalNodes(:,1),:)-nodeCoords(
-1-

CorticalAndTrabecularIterations.m

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

originalCorticalNodes(:,3),:)]; facecross = cross(faceside01,faceside02); facesize = 0.5.*sqrt(sum((facecross.^2),2)); %% First abaqus job % using a dos command to run the input file abaquscommand01=['abaqus job=CorticalAndTrabecularIteration',num2str(itnum,'%04i'),' int ask_delete=OFF cpus=2']; disp([' Number of Trabecular Elements: ', num2str(numElements)]); disp(' Running the Input file...'); [~,~]=dos(abaquscommand01,'-echo'); %% % using a dos command to extract the strain data abaquscommand02=['abaqus cae noGUI=trabeculariterations.py']; disp(' Extracting information from the Output Database...'); [~,~]=dos(abaquscommand02); %% Iterations while itnum<100 && stop==0 %% Strains from FEA % load in the trabecular strain information strainfile = ['.\Elements\strains_trab_bars_iteration',num2str(itnum,'%04i'), '.txt']; elementStrains = dlmread(strainfile,','); elementNums = elementStrains(:,1); elementRefs = elementNums-min(originalElementNums)+1; % load in the cortical strain information cortstrainfile01 = ['.\Elements\strains_cortSP1_bars_iteration',num2str(itnum, '%04i'),'.txt']; cortstrainfile02 = ['.\Elements\strains_cortSP2_bars_iteration',num2str(itnum, '%04i'),'.txt']; elementcortStrains01 = dlmread(cortstrainfile01,','); elementcortStrains02 = dlmread(cortstrainfile02,','); elementCorticalNums = elementcortStrains01(:,1); numCorticalElements=size(elementcortStrains01,1); corticalStrain = max(abs([elementcortStrains01(:,2:3) elementcortStrains02(:,2:3 )]),[],2); % corticalStrainTC = zeros(numCorticalElements,1); for n=1:numCorticalElements if corticalStrain(n) == max([elementcortStrains01(n,2:3) elementcortStrains02 (n,2:3)]); corticalStrainTC(n) = corticalStrain(n); elseif corticalStrain(n)*-1 == min([elementcortStrains01(n,2:3) elementcortStrains02(n,2:3)]); corticalStrainTC(n) = corticalStrain(n)*-1; end end % finding the nodes assocaited with the existing trabecular elements % can be used when elements are removed elementNodes = zeros(size(elementRefs,1),2); for n=1:size(elementRefs,1)
-2-

CorticalAndTrabecularIterations.m

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159

elementNodes(n,1:2) = originalElements(elementRefs(n),2:3); end % setting up a reference set of absolute strains % zero values for elements that have been deleted axialStrain=abs(elementStrains(:,2)); axialStrainTC=elementStrains(:,2); %% location of file parts disp(' Performing Matlab calculations...');

% load in the previous input file oldinputfile = ['CorticalAndTrabecularIteration',num2str(itnum,'%04i'),'.inp']; inputfile=fopen(oldinputfile); %read into one long string A=fscanf(inputfile,'%c',inf); fclose(inputfile); % Convert into cell array, where rows=lines B=strread(A,'%s','delimiter','\n'); %store first set of invariable lines location1=find(strcmp(B,'**** Start of trabecular elements')==true,1); C1=B(1:location1+2); %store second set of invariable lines location2=find(strcmp(B,'**** End of section data')==true,1); C2=B(location2-1:size(B,1)); % setting it up so that the trabecular and cortical elements are arranged into different sections if itnum==1; previousRadii = 0.1.*ones(numElements,1); previousThickness = 0.1*ones(numCorticalElements,1); else location3=find(strcmp(B,'**** Trabecular Cross-Sectional Radii')==true,1); C3=B{location3+1}; previousRadii = (str2num(C3(1,6:end)))'; location4=find(strcmp(B,'**** Cortical Thicknesses')==true,1); C4=B{location4+1}; previousThickness = (str2num(C4(1,6:end)))'; end %% Assigning the new properties previousArea = pi.*previousRadii.^2; target = 1250e-6; range = 250e-6; % increase dead zone over a number of increments deadzone = min([250e-6 250e-6*itnum/5]); numtrabsections = 256; numcortsections = 256;
-3-

CorticalAndTrabecularIterations.m

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214

bonedensity=1600/(1000^3); % kg/m^3 to kg/mm^3 %% start of trabecular elements nearzeroRadius=1e-3; nearzeroArea=pi*nearzeroRadius^2; % setting maxradius % setting minradius an upper limit = 2.0; a lower limit = 0.1;

sectionRadius = [linspace(minradius, maxradius, numtrabsections-1)]; sectionArea = pi.*sectionRadius.^2; newArea=zeros(1,numElements); for n=1:numElements if (axialStrain(n) > (target+range) || axialStrain(n) < (target-range)) ... && previousRadii(elementRefs(n)) > nearzeroRadius newArea(n) = previousArea(n).*axialStrain(n)./target; % allowing near zero high strain elements to 'regrow' elseif axialStrain(n) > deadzone*(minradius/nearzeroRadius)^2 && previousRadii(elementRefs(n)) == nearzeroRadius newArea = sectionArea(1); else newArea(n) = previousArea(n); end end % total volume of trabecular bone trabecularVolume = sum(originalElementLengths.*newArea'); % works when elements are not being eliminated elementNonZeroAreaNodes=zeros(numElements,2); for n=1:numElements if previousRadii(n) > nearzeroRadius elementNonZeroAreaNodes(n,1:2) = originalElementNodes(n,1:2); end end elementNonZeroAreaNodes(elementNonZeroAreaNodes==0) = []; %% nodeConnectivity nodeConnectivitytest=zeros(max(nodes),1); for n=1:[size(elementNonZeroAreaNodes,2)*size(elementNonZeroAreaNodes,1)] nodeConnectivitytest(elementNonZeroAreaNodes(n)) = nodeConnectivitytest( elementNonZeroAreaNodes(n))+1; end nodeConnectivity=zeros(numNodes,1); for n=1:numNodes nodeConnectivity(n)=nodeConnectivitytest(nodes(n)); end %% connectivity data

-4-

CorticalAndTrabecularIterations.m

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266

nodeConnectivitymean = mean(nodeConnectivity(nodeConnectivity~=0)); nodeConnectivitystdev = std(nodeConnectivity(nodeConnectivity~=0)); nodeConnectivitymin = min(nodeConnectivity(nodeConnectivity~=0)); nodeConnectivitymax = max(nodeConnectivity(nodeConnectivity~=0)); % finding the nearest section area value trabecularSection=zeros(1,numElements); for n=1:size(elementNums,1); [~,trabecularSection(elementRefs(n))]=min(abs(sectionArea-newArea(elementRefs (n)))); % getting rid of low radius, low strain elements if trabecularSection(elementRefs(n))==1 && axialStrain(elementRefs(n))< deadzone trabecularSection(elementRefs(n))=0; end % this if statement could be removed to allow elements to 'regrow' if newArea(elementRefs(n))==nearzeroArea trabecularSection(elementRefs(n))=0; end end trabecularSection=trabecularSection+1; sectionArea = [nearzeroArea sectionArea]; sectionRadius = [nearzeroRadius sectionRadius]; newArea = sectionArea(trabecularSection); newRadii = sectionRadius(trabecularSection); trabecularsimilarity = sum(abs(newArea - previousArea') < 1e-6)/numElements; trabecularelementratio = sum(newRadii>nearzeroRadius)/sum(previousRadii> nearzeroRadius); if trabecularsimilarity > 0.99 && abs(1-trabecularelementratio) < 0.001; trabecularstop=1; end %%%% end of trabecular elements %% start of cortical elements newThickness=zeros(1,numCorticalElements); for n=1:numCorticalElements if corticalStrain(n) > (target+range) || corticalStrain(n) < (targetrange) newThickness(n) = previousThickness(n).*corticalStrain(n)./target; else newThickness(n) = previousThickness(n); end end

% setting an upper limit upperthicknesslimit=8; % setting a lower limit lowerthicknesslimit=0.1; sectionThickness = [linspace(lowerthicknesslimit, upperthicknesslimit, numcortsections)];

-5-

CorticalAndTrabecularIterations.m

267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320

% finding the nearest section area value corticalSection=zeros(1,numCorticalElements); for n=1:numCorticalElements [~,corticalSection(n)]=min(abs(sectionThickness-newThickness(n))); % unlike trabecular elements, no cortical elements are removed end newThickness = sectionThickness(corticalSection); % total volume of cortical bone corticalVolume = sum(facesize.*newThickness'); corticalsimilarity = sum(abs(newThickness - previousThickness') < 1e-6)./ numCorticalElements; if corticalsimilarity >= 0.99; corticalstop=1; end %%%% end of cortical elements %% Info files % write trabecular information to another file % as well as the Abaqus input file % leaving out nearzeroRadius elements (trabecularSection(n)==1); trabinfo=['trabecularelementsiteration',num2str(itnum,'%04i'),'.txt']; trabfile=fopen(trabinfo,'w'); for n=1:numElements if trabecularSection(n)~=1 fprintf(trabfile,'%i, %i, %i, %1.9e, %+1.9e\n',originalElements(n,1), originalElements(n,2),originalElements(n,3),... sectionRadius(trabecularSection(n)),axialStrainTC(n)); end end fclose(trabfile); % write cortical information to another file % as well as the Abaqus input file cortinfo=['corticalelementsiteration',num2str(itnum,'%04i'),'.txt']; cortfile=fopen(cortinfo,'w'); for n=1:numCorticalElements fprintf(trabfile,'%i, %i, %i, %i, %1.9e, %+1.9e\n',originalCorticalElements(n ,1),originalCorticalElements(n,2),... originalCorticalElements(n,3),originalCorticalElements(n,4),... sectionThickness(corticalSection(n)),corticalStrainTC(n)); end fclose(cortfile); if trabecularstop == 1 && corticalstop == 1 stop=1; end % update the iteration number % and write to file itnum=itnum+1; itnumfile=fopen('iteration.txt','w'); fprintf(itnumfile,'%i',itnum);
-6-

CorticalAndTrabecularIterations.m

321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369

fclose(itnumfile); disp(['Iteration Number ',num2str(itnum,'%04i')]); disp([' Number of Trabecular Elements: ', num2str(sum(newRadii>nearzeroRadius ))]); disp([' Node Connectivity']); disp([' Mean: ', num2str(nodeConnectivitymean), ' SD: ', num2str( nodeConnectivitystdev),... ' Min: ', num2str(nodeConnectivitymin), ' Max: ', num2str(nodeConnectivitymax)]); disp([' Convergence']); disp([' Trabecular: ', num2str(trabecularsimilarity), ' Cortical: ', num2str( corticalsimilarity)]); disp([' Trabecular Element Number Ratio: ', num2str(trabecularelementratio)]); disp([' Mass']); disp([' Trabecular: ', num2str(trabecularVolume), 'mm^3 Cortical: ', num2str( corticalVolume), 'mm^3']); infomatrix = [itnum sum(newRadii>nearzeroRadius) nodeConnectivitymean nodeConnectivitystdev... nodeConnectivitymin nodeConnectivitymax trabecularsimilarity corticalsimilarity trabecularelementratio... trabecularVolume corticalVolume {datestr(now-floor(now))} {datestr(floor(now ))}]; aa1=['A', num2str(itnum+2)]; aa2=['M', num2str(itnum+2)]; aa3=[aa1,':',aa2]; xlswrite('iterationinfo.xls',infomatrix,aa3); %% Abaqus input file %%%% %%%% Writing the NEW Abaqus input file %%%% newinputfile=['CorticalAndTrabecularIteration',num2str(itnum,'%04i'),'.inp']; outputfile=fopen(newinputfile,'w'); % Print first set of invariable lines for i=1:1:size(C1,1) fprintf(outputfile,'%s\n',C1{i,1}); end % writing trabecular element data out to the Abaqus input file for n=1:numElements fprintf(outputfile,'%i, %i, %i\n',originalElements(n,1),originalElements( n,2),originalElements(n,3)); end % writing the bit between element and section definitions fprintf(outputfile,'****\n**** End of trabecular elements\n****\n'); fprintf(outputfile,'****\n**** Start of section data\n****\n'); % writing section data out to the Abaqus input file fprintf(outputfile,'*Elset, elset=ES_CORTICAL, generate\n'); fprintf(outputfile,'1, 10410, 1\n'); fprintf(outputfile,'*Elset, elset=ES_TRABECULAR, generate\n'); fprintf(outputfile,'10411,229127,1\n');
-7-

CorticalAndTrabecularIterations.m

370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424

% defining the cortical element sets for n=1:numcortsections if sum(corticalSection==n)>0; fprintf(outputfile,'*Elset, elset=ES_CORTICAL_%04i\n',n); clear a1; a1=find(corticalSection==n); for m=1:sum(corticalSection==n); fprintf(outputfile,'%i, ',elementCorticalNums(a1(m))); % listed 8 elements per row if m/8 == round(m/8) fprintf(outputfile,'\n'); end end fprintf(outputfile,'\n'); end end % defining the trabecular element sets for n=1:numtrabsections if sum(trabecularSection==n)>0; fprintf(outputfile,'*Elset, elset=ES_TRABECULAR_%04i\n',n); clear a1; a1=find(trabecularSection==n); for m=1:sum(trabecularSection==n); fprintf(outputfile,'%i, ',originalElementNums(a1(m))); % listed 8 elements per row if m/8 == round(m/8) fprintf(outputfile,'\n'); end end fprintf(outputfile,'\n'); end end % defining the cortical sections for n=1:numcortsections if sum(corticalSection==n)>0; fprintf(outputfile,'*Shell Section, elset=ES_CORTICAL_%04i, material=BONE\n',n); fprintf(outputfile,'%1.9e, 5 \n',sectionThickness(n)); end end % defining the trabecular sections for n=1:numtrabsections if sum(trabecularSection==n)>0; fprintf(outputfile,'*Solid Section, elset=ES_TRABECULAR_%04i, material=BONE\n',n); fprintf(outputfile,'%1.9e\n',sectionArea(n)); end end % putting a comment in so the cortical thicknesses can be found next time round fprintf(outputfile,'**** Cortical Thicknesses\n**** '); fprintf(outputfile,'%1.9e, ',sectionThickness(corticalSection)); fprintf(outputfile,'\n'); % putting a comment in so the trabecular areas can be found next time round fprintf(outputfile,'**** Trabecular Cross-Sectional Radii\n**** '); for n=1:numElements if trabecularSection(n)~=0
-8-

CorticalAndTrabecularIterations.m

425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466

fprintf(outputfile,'%1.9e, ',sectionRadius(trabecularSection(n))); else fprintf(outputfile,'0.0, '); end end fprintf(outputfile,'\n'); fclose('all'); if stop==1; dos(['copy /b ',newinputfile,' _final.inp']); end merge = ['copy /b ',newinputfile,'+ Assembly.txt'] dos(merge); fclose('all'); %%%% %%%% Finished writing the NEW Abaqus input file %%%% %% running the new input file % using a dos command to run the new input file abaquscommand01=['abaqus job=CorticalAndTrabecularIteration',num2str(itnum,'%04i' ),' int ask_delete=OFF cpus=2']; disp(' Running the Input file...'); [~,~]=dos(abaquscommand01,'-echo'); % using a dos command to extract the strain data for the % NEW output database abaquscommand02=['abaqus cae noGUI=trabeculariterations.py']; disp(' Extracting information from the Output Database...'); [~,~]=dos(abaquscommand02); fclose('all'); end %% Cleaning dos('move *.inp .\Abaqus'); dos('move *.odb .\Abaqus'); dos('move *00*.txt .\Elements'); dos('del abaqus.rpy*'); dos('del CorticalAndTrabecularIteration0*');

-9-

Appendix E Python script: extract data from the Abaqus output file

trabeculariterations.py

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

# # # #

Python script to read the Abaqus output file Originally written by Andrew Phillips Modified to accept several steps in one analysis by Alfred Thibon Example with 3 steps

# Importing Abaqus # Do not alter these lines ################### IMPORTS ################### from abaqus import * # from sketch import * # from part import * # from material import * # from section import * # from assembly import * # from load import * from visualization import * # from interaction import * # from step import * # from mesh import * # from job import * from odbAccess import * # from shutil import * # import section # import regionToolset # import displayGroupMdbToolset as dgm # import part # import material # import assembly # import step # import interaction # import load # import mesh # import job # import sketch import visualization import xyPlot import displayGroupOdbToolset as dgo # import connectorBehavior import odbAccess import os

# Iteration number print '\nReading iteration number...\n' f=open('iteration.txt','r') itnum=f.readlines() f.close() # open the output database odb=session.openOdb('CorticalAndTrabecularIteration'+"%04d" % int(itnum[0])+'.odb', readOnly=True) #extract the axial strain values for the trabecular elements strains1=odb.steps['Step-1'].frames[1].fieldOutputs['E']
-1-

trabeculariterations.py

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

strains2=odb.steps['Step-2'].frames[1].fieldOutputs['E'] strains3=odb.steps['Step-3'].frames[1].fieldOutputs['E'] #### # getting data for the Trabecular Bone outputpointstrab=odb.rootAssembly.instances['STRUCTURALFEMUR-1'].elementSets[ 'ES_TRABECULAR'] straintrab01 = strains1.getSubset(region=outputpointstrab, position=INTEGRATION_POINT) straintrabvalues01 = straintrab01.values straintrab02 = strains2.getSubset(region=outputpointstrab, position=INTEGRATION_POINT) straintrabvalues02 = straintrab02.values straintrab03 = strains3.getSubset(region=outputpointstrab, position=INTEGRATION_POINT) straintrabvalues03 = straintrab03.values #open a file to put the data in straintrabdata=open('.\Elements\strains_trab_bars_iteration'+"%04d" % int(itnum[0])+ '.txt',"w") print 'Writing Trabecular Strain Data...' # write the date to the file i=0 for v in straintrabvalues01: straintrabdata.write('%d,' % (straintrabvalues01[i].elementLabel)) straintrabdata.write('%1.12e\n' % (max(abs(straintrabvalues01[i].data[0 ]),abs(straintrabvalues02[i].data[0]),abs(straintrabvalues03[i].data[0 ])))) i=i+1 # close the file straintrabdata.close() print 'Done.' #### #### # getting data for the Cortical Bone outputpointscort=odb.rootAssembly.instances['STRUCTURALFEMUR-1'].elementSets[ 'ES_CORTICAL'] # using different section points (SP1 - Bottom) straincort01SP1 = strains1.getSubset(region=outputpointscort, position= INTEGRATION_POINT, sectionPoint=strains1.locations[0].sectionPoints[0]) straincortvalues01SP1 = straincort01SP1.values straincort02SP1 = strains2.getSubset(region=outputpointscort, position= INTEGRATION_POINT, sectionPoint=strains2.locations[0].sectionPoints[0]) straincortvalues02SP1 = straincort02SP1.values straincort03SP1 = strains3.getSubset(region=outputpointscort, position= INTEGRATION_POINT, sectionPoint=strains3.locations[0].sectionPoints[0]) straincortvalues03SP1 = straincort03SP1.values #open a file to put the data in straincortdataSP1=open('.\Elements\strains_cortSP1_bars_iteration'+"%04d" % int(itnum [0])+'.txt',"w")

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104

-2-

trabeculariterations.py

105 106 107 108 109 110 111

print 'Writing Cortical Strain Data, SP1' # write the date to the file i=0 for v in straincortvalues01SP1: straincortdataSP1.write('%d,' % (v.elementLabel)) straincortdataSP1.write('%1.12e,' % (max(abs(straincortvalues01SP1[i]. minPrincipal),abs(straincortvalues02SP1[i].minPrincipal),abs( straincortvalues03SP1[i].minPrincipal)))) straincortdataSP1.write('%1.12e\n' % (max(abs(straincortvalues01SP1[i]. maxPrincipal),abs(straincortvalues02SP1[i].maxPrincipal),abs( straincortvalues03SP1[i].maxPrincipal)))) i = i+1 # close the file straincortdataSP1.close() # using different section points (SP2 - Top) straincort01SP2 = strains1.getSubset(region=outputpointscort, position= INTEGRATION_POINT, sectionPoint=strains1.locations[0].sectionPoints[1]) straincortvalues01SP2 = straincort01SP2.values straincort02SP2 = strains2.getSubset(region=outputpointscort, position= INTEGRATION_POINT, sectionPoint=strains2.locations[0].sectionPoints[1]) straincortvalues02SP2 = straincort02SP2.values straincort03SP2 = strains3.getSubset(region=outputpointscort, position= INTEGRATION_POINT, sectionPoint=strains3.locations[0].sectionPoints[1]) straincortvalues03SP2 = straincort03SP2.values #open a file to put the data in straincortdataSP2=open('.\Elements\strains_cortSP2_bars_iteration'+"%04d" % int(itnum [0])+'.txt',"w") print 'Writing Cortical Strain Data, SP2' # write the date to the file i = 0 for v in straincortvalues01SP2: straincortdataSP2.write('%d,' % (v.elementLabel)) straincortdataSP2.write('%1.12e,' % (max(abs(straincortvalues01SP2[i]. minPrincipal),abs(straincortvalues02SP2[i].minPrincipal),abs( straincortvalues03SP2[i].minPrincipal)))) straincortdataSP2.write('%1.12e\n' % (max(abs(straincortvalues01SP2[i]. maxPrincipal),abs(straincortvalues02SP2[i].maxPrincipal),abs( straincortvalues03SP2[i].maxPrincipal)))) i = i+1

112

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136

137

138 139 140 141 142 143 144 145 146 147

# close the file straincortdataSP2.close() print 'Done.' #close the output database odb.close()

-3-

You might also like