Sunteți pe pagina 1din 215

A Procedure for Generating Finite Element Models (FEM) of Abdominal Aortic Aneurysms with Fluid Boundary Conditions Derived

from Magnetic Resonance Imaging (MRI) Velocimetry

M.S. Thesis

Presented in Partial Fulfillment of the Requirements for the Degree Master of Science in the Graduate School of The Ohio State University

By Mark Allen McElroy, B.S. Graduate Program in Mechanical Engineering

The Ohio State University 2010

Thesis Committee Samir N. Ghadiali, Advisor Orlando Simonetti

Copyright by Mark Allen McElroy 2010

Abstract

Abdominal Aortic Aneurysms (AAAs) are localized bulges in the lower aortic artery tissue. AAAs are prone to rupture, an extremely dangerous event, in which the aorta rips open and blood is allowed to flow freely into the bodys internal cavity. The fatality rate for ruptured AAAs is over 50% so preventative surgery is the preferred method of treatment. However many AAAs never rupture and the risks involved with preventative surgery are not negligible. Clinicians therefore must decide when the risks of AAA rupture outweigh those of preventative surgery.

The current clinical metric for determining the risk of AAA rupture is the transverse diameter. A 5.5 cm diameter is the suggested max allowable size. As many as 20-30% of AAAs below this threshold rupture and in practice, the operating surgeon must account for other risk factors too such as blood pressure and aneurysm shape. As a result, the decision is no more than an educated guess based on a series of known risk factors. There is a clinical desire for a more reliable and comprehensive AAA rupture risk metric Studies have shown that maximum arterial wall Von-Mises stress, calculated using patient-specific finite element (FE) models outperforms diameter with regards to predicting AAA rupture. Modern AAA FE models employ fully coupled dynamic fluidii

structure interaction (FSI) techniques in an effort to accurately measure max wall stress in-vivo and non-invasively. Published boundary conditions (BCs) for dynamic AAA model fluid domains typically involve standard flow rate and pressure conditions being applied at the inlet and outlet of the model respectively. Our lab proposes using in-vivo blood velocity measurements from phase-encoded velocimetry MRI scans to generate patient-specific fluid BCs. A patient-specific flow rate condition is applied at the inlet matching the velocimetry data read in at the inlet. A patient-specific downstream pressure is applied at the outlet. This pressure BC is derived from an optimization routine which seeks to match the modeled and measured outlet flow rates by altering the impedance at the outlet. To date, only one model has been run to convergence, due to a computation run time of over 1 month. While changes were made to the pressure condition at the outlet (a 14% increase in dynamic range) during optimization, these changes had almost no effect of the max arterial wall stress.

iii

Dedication

I would like to dedicate this work to my loving and supportive parents, Paul and Laura McElroy and to my brother, Matt McElroy

iv

Acknowledgments

I would like to acknowledge all those who have worked so hard to help bring this project to fruition. I would like to thank Dr. Yu Ding for aiding with the medical imaging aspects of this project, and Dr. Georgeta Mihai for working so hard on recruiting and scanning patients for this project. Thank you Dr. Sanjay Rajagopalan for providing a hands-on, clinical perspective to the project. Finally, thank you Dr. Orlando Simonetti and Dr. Samir Ghadiali for orchestrating and funding this project.

Vita

June 2004 ............................................................. Stow-Munroe Falls High School Fall 2006, Summer 2006, 2007, 2008 .................. Lexmark International March 2009 .......................................................... B.S. Mechanical Engineering, The Ohio State University June 2009 to present ........................................... Graduate Research Associate, Department of Biomedical Engineering, The Ohio State University

Fields of Study Major Field: Mechanical Engineering

vi

Table of Contents

Abstract ............................................................................................................................... ii Dedication .......................................................................................................................... iv Acknowledgments............................................................................................................... v Vita ..................................................................................................................................... vi Table of Contents .............................................................................................................. vii List of Tables ...................................................................................................................... ix List of Figures ...................................................................................................................... x Chapter 1: Abdominal Aortic Aneurysm Anatomy and Physiology .................................... 1 Chapter 2: Dealing with Abdominal Aortic Aneurysms Clinically ....................................... 5 Chapter 3: Abdominal Aortic Aneurysms from an Engineering Perspective ...................... 8 Chapter 4: In Depth Description of Models ...................................................................... 16 Chapter 5: Results ............................................................................................................. 39 Chapter 6: Future Work .................................................................................................... 52 References ........................................................................................................................ 56 Appendix A: How to Generate CAD data from MRI images ............................................. 59

vii

Appendix B: Generating a CAD model in Rhinoceros ....................................................... 71 Appendix C: Extracting Flow Rate Data from Velocimetry MRI Images ........................... 82 Appendix D: Putting it All Together in The Finite Element Model .................................... 86 Appendix E: The Optimization Routine and Running A Model ......................................... 94 Appendix F: Matlab Script Descriptions .......................................................................... 103 Appendix G: Visual Basic Script Descriptions .................................................................. 112 Appendix H: Utilizing the Ohio Supercomputing Center ................................................ 114 Appendix I: Helpful Contacts .......................................................................................... 115 Appendix J: Matlab Code ................................................................................................ 117 Appendix K: Visual Basic Code ........................................................................................ 176

viii

List of Tables

Table 1: Change in lumen volume with respect to sampling ......................................................... 21 Table 2: Important Dicom properties and descriptions................................................................. 64 Table 3: Suggested time stepping scheme .................................................................................... 91 Table 4: Required fields for settings structure .............................................................................. 95 Table 5: Required fields for ADINAwithMATLAB2 ......................................................................... 98 Table 6: Matlab Script Summary.................................................................................................. 103 Table 7: Visual Basic Script Summary .......................................................................................... 113

ix

List of Figures

Figure 1: Comparison of healthy and aneurismal aorta .................................................................. 1 Figure 2: The composition of a healthy aortic wall with no atherosclerosis ................................... 3 Figure 3: Simplified arterial cross section ........................................................................................ 3 Figure 4: Aortic Dissection ............................................................................................................... 5 Figure 5: Examples of the 2 preventative surgeries ........................................................................ 6 Figure 6: Force diagram for a pipe under pressure ......................................................................... 9 Figure 7: Example of axisymmetric geometry ............................................................................... 10 Figure 8: Modeled blood flow in AAA ............................................................................................ 12 Figure 9: Relationship between material stiffness, pressure and flow rate .................................. 15 Figure 10: Example MRI images ..................................................................................................... 17 Figure 11: The 2D image analysis process ..................................................................................... 20 Figure 12: Sampling artifacts in spline curves ................................................................................ 21 Figure 13: Sampling artifact in lofting ............................................................................................ 21 Figure 14: Initial data in Rhino ....................................................................................................... 23 Figure 15: Rhino processing ........................................................................................................... 23 Figure 16: The development of the blood domain ........................................................................ 23 Figure 17: The solid domain ........................................................................................................... 24 Figure 18: The ILT domain wall inner boundary ............................................................................ 24 x

Figure 19: Selecting a velocimetry region of interest .................................................................... 25 Figure 20: Comparison of flow rate data before and after processing ......................................... 26 Figure 21: Example of rough wall, ILT, and fluid mesh .................................................................. 30 Figure 22: Fluidic circuit diagram of the downstream impedance condition ................................ 31 Figure 23: Frequency response of 4-parameter Windkessel ......................................................... 32 Figure 24: The process for developing a pressure curve ............................................................... 33 Figure 25: Types of Nelder-Mead iterations shown in 2D ............................................................. 35 Figure 26: Convergence Chart........................................................................................................ 36 Figure 27: Iterative cycle for generating a patient specific impedance condition ........................ 37 Figure 28: Refined mesh used to generate mesh independent solutions ..................................... 38 Figure 29: Comparison of initial and final flow rate conditions for patient TK ............................. 40 Figure 30: Comparison of initial and final pressure conditions for Patient TK .............................. 40 Figure 31: Comparison of initial and final max stress values for Patient TK.................................. 41 Figure 32: Comparison of initial and experimental outlet flow rates for Patient MM .................. 42 Figure 33: Comparison of initial and experimental outlet flow rates for Patient AK .................... 42 Figure 34: Peak stress of initial and converged arterial wall ......................................................... 43 Figure 35: Artery wall stiffness variational study .......................................................................... 45 Figure 36: ILT stiffness variational study........................................................................................ 46 Figure 37: Impedance parameter R1 variational study ................................................................. 47 Figure 38: Impedance parameter L variational study .................................................................... 47 Figure 39: Impedance parameter R2 variational study ................................................................. 48 xi

Figure 40: Impedance parameter C variational study ................................................................... 48 Figure 41: Comparison of input function with impedance condition............................................ 50 Figure 42: Stress Maps for unconverged Patient MM and Patient AK models ............................. 51 Figure 43: The measured flow rate ................................................................................................ 52 Figure 44: Typical daw data set ..................................................................................................... 59 Figure 45: How to view data sets using a viewer program ............................................................ 61 Figure 46: A data set after being organized into folders ............................................................... 63 Figure 47: How to use the Crop GUI .............................................................................................. 66 Figure 48: How to use the Edging GUI ........................................................................................... 69 Figure 49: Fixing spline curves in Rhino ......................................................................................... 72 Figure 50: Generating surface A and the blood domain................................................................ 73 Figure 51: Generating surface B and surface C .............................................................................. 74 Figure 52: Rhino geometries .......................................................................................................... 75 Figure 53: How to create a cutting surface .................................................................................... 76 Figure 54: The cutting plane does not intersect across the whole surface. .................................. 76 Figure 55: The cutting planes. ........................................................................................................ 77 Figure 56: Rhino geometries .......................................................................................................... 78 Figure 57: Joining outwall surfaces ................................................................................................ 79 Figure 58: Dealing with problems during outerwall surface joining.............................................. 80 Figure 59: A typical velocimetry image and an image with a well defined lumen ........................ 83 Figure 60: Example region of interest ............................................................................................ 83 xii

Figure 61: Inlet and outlet flow rate data both before and after processing. ............................... 85

xiii

Chapter 1: Abdominal Aortic Aneurysm Anatomy and Physiology


An aneurysm is the localized bulging of an artery. This bulge can include an enlarged blood cavity, a thickening of the artery wall, or both. Aneurysms can develop anywhere, but have strong tendencies towards specific locations, such as the aorta and brain [9]. The aorta is the largest artery in the human body. It emerges directly from the top of the heart, curves downward in what is known as the aortic arch, and then proceeds through the thorax (or chest) to the abdomen. It terminates at the aortic bifurcation, located at the fourth lumbar vertebra, where it bifurcates into the right and left common iliac arteries [10]. Directly above the aortic bifurcation, at the end of the aorta is a common location for aneurysm development. Aneurysms in this abdominal section of the aorta are simply referred to as Abdominal Aortic Aneurysms (AAA). While a clear understanding of why AAAs develop is not well understood, the mechanism through which it occurs is. The bulge in the artery occurs due to the force of the blood flowing through it acting on damaged or diseased tissue. Known risk factors include aging, smoking, high blood pressure, atherosclerosis, and diseases that inflame blood vessels, such as vasculitis [9]. Males and elderly patients are at higher risk for developing AAAs. 1

Figure 1: Comparison of healthy and aneurismal aorta. Reprinted from [2].

Symptoms of an AAA include a throbbing in feeling in the abdomen, a deep pain in the back or side of the abdomen, or a gnawing pain in the abdomen that can last for hours or days. Many AAAs present no symptoms, and are instead detected during other medical procedures. AAAs can sometimes be detected as a throbbing mass in the abdomen or can be seen during abdominal imaging [9]. In order to discuss AAAs further, a clear understanding of the anatomy of an artery is necessary. A healthy arterial wall is composed of a series of layers. The adventitia, or outermost layer, is composed primarily of thick bundles of collagen fibrils in a helical structure. There is no definitive outer boundary to the adventitia. Instead the composition gradually becomes looser connective tissue [7]. The media, or middle layers of the wall, are composed of highly organized laminated layers of smooth muscle cells, elastin, and collagen fibril bundles. This is the primary load bearing tissue of the artery wall. Finally the intima, or inner most layer of the wall is an endothelial cell lining between the artery tissue and the lumen with a thin layer of helically oriented collagen fibrils backing them [7]. Many AAAs also develop intraluminal thrombus (ILT) which is a fibrin structure of blood cells, platelets, blood proteins, and cellular debris which attaches and buils on the lumen [11]. In many AAA this build up can dominate the crosssectional area of the artery wall. Over time calcium deposits can form both in the ILT and in atherosclerotic tissue between the media and intima of the wall as shown in Figure 3. These deposits are much stiffer than any surrounding tissue.

Figure 2: The composition of a healthy aortic wall with no atherosclerosis. It is divided into 3 sections: the intima (I), media (M) and adventitia (A). Reprinted from [7].

For simplification and modeling purposes these layers are often grouped into 3 separate materials that from here on will be referred to as wall tissue, ILT tissue, and calcified tissue. Wall tissue represents the adventia, media, and intima layers and is the primary load bearing surface. ILT is the blood clot like material buildup in the lumen and is relatively soft. The calcified tissue is the very stiff 3
Figure 3: Simplified arterial cross section. Reprinted from [6].

calcium deposits that form in ILT or artery wall as shown in Figure 3. This modeling ignores any heterogenatiy in these three materials.

Chapter 2: Dealing with Abdominal Aortic Aneurysms Clinically


The reason aneurysms are a clinical concern, is that they are prone to dissection and rupture. Dissection occurs when a layer or a few layers of the artery wall tear, and blood begins flowing between the layers of the wall. Rupture is similar to dissection but in this case, the artery wall completely tears, and blood flows freely out of the artery and into the internal cavities of the body. Both are very
Figure 4: Aortic Dissection. Reprinted from [3].

serious, often proving fatal. We will discuss the implications of rupture only. Since AAAs occur in such a large artery, internal bleeding is extremely dangerous. The naturally occurring pressure and flow rate in the aorta are much higher than seen in smaller arteries and as a result victims can bleed out very quickly. Less than 50% of AAA rupture victims make it to the hospital before dying from this internal bleeding [12]. The speed at which AAA rupture can kill makes surgical repair an unreliable solution. Instead, a much safer course of action is to try to prevent AAA rupture before it occurs. Current medical practice is to perform preventative surgery when concern of AAA rupture arises. The goal of these preventative surgeries are to greatly reduce the chance of AAA rupture occurring, in hope of preventing rupture and thehigh mortality rate associated with it.

There are 2 preventative surgeries for AAA rupture open repair and endovascular stent grafting. Open repair involves the abdomen being opened and the diseased AAA vessel being replaced with a synthetic artery. Endovascular stent grafting involves small incisions being made in the femoral artery so that the stent and surgical catheters may be inserted into the arterial tree downstream of the aneurysm. The stent is installed inside the lumen of the AAA to relieve
Figure 5: Examples of the 2 preventative surgeries. Open repair (left) and endovascular stent (right). Reprinted from [5].

pressure on the diseased wall tissue from the blood. Open repair is much more invasive, requiring more recovery time and is more prone to complications such as infection. Endovascular surgery is a quick and easy fix comparatively, but often requires recurring surgeries to make adjustments to the stent. With either method, the risk of complication is not negligible, and while AAA rupture is devastating, not all AAAs rupture. Performing either of these surgeries unnecessarily exposes the patient to unwarranted risk. Therefore, the question of when to perform preventative surgery must somehow be answered. In determining when surgery should be performed, the goal should be to minimize the total risk to the patient, in which case the moment the risk of AAA rupture outweighs the risks involved with surgery is the correct moment. Due to the somewhat unpredictable nature of AAA 6

rupture, there is no definitive way to answer this question. Experience and empirical evidence have produced AAA diameter as the clinically suggested method for gauging rupture risk. As the diameter of an AAA increases, evidence has shown that the chance of rupture does as well. Common practice is to hold off on preventative surgery until the diameter of the AAA exceeds 5.5cm, or the aneurysm is expanding faster than 0.5 1.0 cm/year [1, 6, 13-15]. If either of these values are exceeded, the risk of rupture is deemed to be greater than the risks involved with preventative surgery; if they are not exceeded, then surgery is deemed too risky and the usual recommendation is to wait and continue monitoring the aneurysm. Once an AAA is detected, patients are typically monitored yearly, or in some cases every 6 months, to ensure the AAA is relatively healthy and surgery is not necessary. The AAA is imaged, usually through ultrasound, and the diameter is approximated. While the 5.5cm and rapid expansion rules are considered a good rule of thumb, the final decision is up to the surgeon, who takes into account not only the diameter and growth rate, but other known risk factors such as blood pressure and age. The result is often more of an educated guess as to when it is appropriate to perform surgery, than a carefully evaluated risk assment. This subjectivity is necessary due to the weak correlation between aneurysm diameter and rupture risk. Studies by Simao da Silva, Darling, and Sterpetti all suggest that as many as 20 30% of AAAs with a diameter less than 5.5cm rupture before exceeding 5.5cm [16-18]. Furthermore, AAAs have been recorded to get as large as 17cm before rupturing [16].The clinical community is aware of the weakness of the diameter risk metric, and has expressed a desire for a better metric for determining AAA rupture risk [13, 19]. 7

Chapter 3: Abdominal Aortic Aneurysms from an Engineering Perspective

Rupture is a well documented and studied form of material failure in solid mechanics. There is general agreement in the engineering community that stress is the best way to determine if a material will fail. Stress is a measure of force per area in a material (such as lbs/ft2). While traditional stress values are directional, when analyzing material failure, often a scalar quantity known as von Mises stress, or effective stress is used. This is simply a way of incorporating the stress in all directions into one scalar quantity, which can be used to asses the total amount of stress a point is under independent of direction. Many materials have been noted to have critical amounts of von Mises stress they can withstand before they begin to fail. There are various definitions of material failure, but the first form, and the form that will be referred to in this paper involves a phenomenon known as yield. Yield occurs when a solid begins to permanently change shape. Up to a certain stress, solids will bounce back to their original shape when unloaded, but if loaded past their yield stress the solids will not return to their original shape naturally and instead returns to some new natural shape. While a material yielding does not necessarily mean it will break or rupture, no material can break without first reaching its yielding point. The statement if the stress in an arterial wall is less than the walls yield stress, it will not rupture, but if the stress is greater than the yield stress, the artery has already begun to fail is true by definition. This suggests that stress or perhaps the ratio of stress to yield stress would be an ideal metric for determining if an aneurysm might rupture [20].

While a correlation between diameter and AAA rupture does exist, it is relatively weak. 20 30% of aneurysms with a diameter under 5.5cm still rupture. Stress has the potential to be a much better indicator than diameter. In fact, diameter being correlated with rupture is firmly predicted within solid mechanics theory. The equation for stress in the wall of a thin walled pipe under constant pressure, often referred to in medicine as the Law of Laplace, is shown in Equation (1).

(1)

P is the pressure difference across the pipe wall, t is the thickness of the pipe wall, d is the internal diameter of the pipe, and is the stress in the wall. In this equation, the stress and diameter are proportional. This equation can be derived from the force diagram shown in Figure 6. The blue arrows represent pressure pushing the pipe apart, and the red arrows represent the stress in the pipe wall
Figure 6: Force diagram for a pipe under pressure. As the diameter increases, so does the force from pressure, p. Modified from [4].

holding everything together. Unless the pipe breaks open, these forces must be in balance. As the

diameter of the pipe increases, the interior cross section of the pipe increases. Since the same internal pressure is acting on a larger area, the force exerted by the fluid, and thus the amount 9

of force the wall must exert to maintain balance increases. Since a larger force is required of the same amount of material in the wall, the stress (force/area) increases. While modeling an aneurysm as a thin walled pipe is an oversimplification, the logic relating diameter and stress is still intact. In both cases, when the amount of force required to contain the pressure becomes greater than what the material can provide (in other words when the stress exceeds the yield stress) it will rupture. While diameter and stress are related, diameter does not take into account as much information as stress does. Quantities such as blood pressure and variations in wall thickness, which are both clinically relevant risk factors, are ignored when using diameter to measure patient risk. However, stress can be used to capture all of these risk factors in one metric. It is generally agreed that stress would be a better metric for AAA rupture, however accurately measuring stresses in-vivo is a tricky problem that has not yet been perfected. While there are many solutions for simple geometry, such as the Law of Laplace, for more complex problems such as stress in an aneurysm, the solutions can be much more mathematically intense to derive. In order to better determine in-vivo stresses, researchers began constructing computer models of the artery mechanics to help calculate these stresses [8]. Using a mathematical modeling technique known as 10
Figure 7: Example of axisymmetric geometry. Reprinted from [8].

finite elements (FE), often used in engineering to determine stress, researches have begun modeling aneurysm mechanics. Early models were static with simplified axisymmetric geometry (see Figure 7), due largely to computational constraints [8]. With the concept proven, researches began to look for ways to improve upon these models. Many of the first improvements involved the material models used to describe the mechanical properties of arterial tissue. Mechanical material modeling involves creating a relationship between force and material deformation. Due to the highly heterogeneous, multiscale and largely varying properties of living tissue, modeling biological materials properly can be difficult. Clinical efforts are ongoing to properly define the properties and variations of arterial tissue [13, 21, 22]. Many computational models have been proposed [19, 23, 24]. The latest material models are anisotropic, meaning the artery tissue reacts differently when stressed circumferentially as opposed to longitudinally. This makes sense due to the organizational nature of tissue layers in the artery wall. However the most commonly used material model is the isotropic hyperelastic model first proposed by Raghavan in 2000 mostly due to the easy of application [24]. In 1999 David Vorps lab at the University of Pittsburgh made the jump to creating 3 dimensional patient-specific models [25-27]. This was a vast improvement over the previous, idealized, and often axisymmetric models. These models were generated using image analysis techniques to extract data from computed tomography (CT) scans.

11

The inclusion of the intraluminal thrombus (ILT) by Wang in 2002 proved to be a significant improvement [11]. As mentioned earlier, the intraluminal thrombus is a fatty buildup within the inner layers of the wall with vastly different material properties than the arterial wall tissue. While this tissue is usually considered unhealthy and related to atherosclerosis, FE models have shown it to help distribute forces uniformly through the wall and thus reduce peak stresses. Efforts soon followed to
Figure 8: Modeled blood flow in AAA. Reprinted from [1].

do the same with respect to calcium deposits in the tissue [6, 28]. There is currently no consensus on the effect of calcium deposits on wall stress. More recently a push has developed to improve the accuracy of the loading conditions. Past models have applied a uniform systolic pressure force normal to the surface of the lumen boundary. In reality, the abnormal geometry of aneurysms is known to often cause atypical blood flow patterns as shown in Figure 8 and these complex flow patterns can push harder on the artery wall in some places than in other. To capture these complex forces, the blood flow must be modeled in the lumen and allowed to exert force on the wall tissue, instead of applying loads directly to the inside of the wall. Computer models in which solid and fluid domains are allowed to interact with each other are known as fluid-structure interaction (FSI) models. Some of these models are static and solve for stresses assuming constant unchanging blood flow in 12

the artery [1, 29], while others model the flow dynamically, and as a result also capture changes in stress due to the dynamic fluctuations in the loads [14, 15, 30]. One of the problems with FSI modeling in-vivo is how much more information is required to model both the fluid and in some cases the dynamics. In dynamic models this additional information comes in the form of 2 fluid boundary conditions (BC). A standard cardiac flow rate curve for the aorta is applied at the inlet and a standard pressure curve is applied at the outlet. These are used to model the blood flow, which then exerts time varying and complex forces on the artery wall. These improvements have been vital in creating a more accurate model for predicting patient in-vivo stresses. In 2003 Fillinger showed that stress values in his models were 12% more accurate than diameter at predicting rupture [31]. While beating aneurysm diameter as a predictor is promising, the goal is to predict rupture and no model has yet proven itself able to distinguish between aneurysms near rupture and those that are not with any statistical significance. Therefore, the push to create a more accurate model continues. There are concerns that current FSI models dont have enough measured data to validate their results and that until the stresses produced from FSI models can somehow be validated, they cannot be trusted to reflect the actual system. If the modeled blood flow is incorrect for some reason, the stresses the model predicts will be incorrect as well. In hopes of validating their fluid models, some researchers have begun coupling their FSI models with experiments to reproduce the flow in a laboratory environment, allowing the computational and experimental results to be compared [29, 32].

13

Our lab is looking for ways to further validate these FSI models computationally. By developing patient-specific fluid boundary conditions, assumptions regarding flow rate and pressure can be relaxed. MRI velocimetry data is utilized to measure the inlet and outlet flow rate of the modeled section of artery. A patient-specific impedance condition is then determined by selecting the impedance which maximizes the agreement between the modeled downstream flow rate and the flow rate measurement extracted from MRI velocimetry images at the downstream boundary. This impedance condition is then used to define what pressure the blood is under through the cardiac cycle at the outlet. One weakness of this approach is that changes to the material properties of the artery wall will directly affects the amount of pressure necessary to create a specific downstream flow rate. This requires the assumption that the patient-specific impedance condition is only patient-specific as long as the wall material properties are correct. As shown in Figure 9 varying the material properties will alter the downstream flow in the same way changing the pressure can. Therefore the amount of pressure required to produce a specific flow rate is dependant on the assumed material properties.

14

Figure 9: Relationship between material stiffness (A) and downstream pressure (B) with respect to downstream flow rate

15

Chapter 4: In Depth Description of Models


The modeling procedure described below generates a computational finite element (FE) model that simulates the mechanics of an abdominal aortic aneurysm (AAA). It is a dynamic fully coupled fluid-structure interaction (FSI) model developed using patient-specific geometry and in-vivo blood velocity measurements from phase encoded velocimetry magnetic resonance imaging (MRI) scans. The arterial wall is subdivided into 2 different materials arterial wall tissue and intraluminal thrombus (ILT), each with their own material models. The measured invivo velocity data is utilized along with the patients brachial diastolic blood pressure to generate a patient-specific downstream impedance condition relating the blood flow to pressure that can then be utilized in modeling the blood flow. The procedure begins with a series of MRI scans, lasting approximately 1 hours. The MRI machine is a 1.5T strength MRI capable of achieving 1x1x1 mm spatial resolution and performing phase contrast acquisition. A T1 weighted 3D anatomical scan is performed over the diseased section of the artery. Also, using MRI phase contrast data, the in-vivo velocity is measured at the upstream and downstream boundaries of the anatomical data. The location of a given velocity measurement within the cardiac cycle is measured by recording the amount of time after the patients R-wave the image was taken. The R-wave is the electrocardiogram signal which signifies the beginning of ventricular contraction in the heart.

16

It is important that the velocimetry data be taken as close to the boundaries of the model as possible, as this is where they will be applied. Also, the phase shift between the 2 velocity data sets, resulting from the time it takes for pressure waves to propagate down the modeled section of aorta is important in the optimization of the impedance condition. Typically, the velocity is measured in the 2 in-plane directions of the image as well as in the out of the plane direction. Due to noise, only the out-ofplane data is utilized, so the velocimetry images should also be taken as orthogonal to the direction of flow as possible. Since inplane motion is ignored, efforts should be made to measure the velocity in a location where there are no flow artifacts that might produce excessive in-plane motion, such as circular flows. The patients brachial diastolic
Figure 10: MRI images. (A) is a slice of the anatomical scan. (B) is the out-of-plane velocity map. (C) is an inplane velocity map in the anterior-posterior direction

17

pressure is also taken during this time. Efforts must be made to record an appropriate brachial pressure, as patients might suffer from stress induced high blood pressure during testing. Once all the required data is collected, construction of the model can begin. The anatomical MRI data is run through an in-house image analysis program created using Matlab, which walks the user through the image analysis procedure. It begins with capturing the luminal opening using edge detection. The program first performs some preliminary 3D median pre-filtering (Figure 11.2). During this process each voxel is assigned a value equal to the median value of all the voxels within its 3D neighborhood. In this case, the neighborhood is defined to be a 3x3x3 pixel volume. Then, a series of 2D image analysis procedures for detecting a 2D edge are implemented as shown in Figure 11. The 2D process begins by enlarging the images using bicubic interpolation (Figure 11.3) and applying a 2D median filter to the image (Figure 11.4). The filter neighborhood in this case is a 2D square of pixels. The size is defined by the user. Edge detection is performed using the Canny algorithm (Figure 11.5). This algorithm utilizes 2 different thresholds. The main or primary threshold defines how much of a change in pixel intensity is required before an edge exists. The lower the primary threshold, the more edges will be detected in an image. The secondary threshold also detects edges, however any edge detected using the secondary threshold is only kept as an edge if it touches an existing primary edge. In this way, the secondary threshold can control how long a detected edge is or how many times it branches off. The lower the secondary threshold, the longer and more complex edges appear. After edge 18

detection is performed, a morphological closing operation is implemented on the 2D image (Figure 11.6). This procedure, also referred to as a flood-fill procedure, increases the thickness of all the detected lines a user specified amount, and then erodes the added pixels away again. Anywhere where 2 edges were joined during the flood, they will remain joined after the pixels are eroded away. This helps ensure that the edges detected are closed and define a body of points. The parameters in each of these process steps (magnification, filter neighborhood size, edging thresholds, closure radius) can be altered in real time by the user and an image of the detected edges overlaid onto the pre-filtered image is available to check the accuracy of the detected lumen (Figure 11 .7). Once a series of 2D bodies have been defined, the user is asked to select the body in each image which represents the artery lumen (Figure 11.8).

19

Figure 11: The 2D image analysis process. Raw (1). Pre-filtered (2). Enlarged (3). Filtered (4). Edge detected (5). Closed (6). Overlay (7). Final lumen body (8)

Once the lumen has been completely defined, the next step is to define the outer boundary of the artery wall. The anatomy of an artery is such that the outer edge of an artery is poorly defined and cannot be automatically extracted using any type of edge detection. Therefore, the user is prompted to hand draw the outer boundary for a subset of the slices. Both the luminal and outer boundaries are recorded as a series of points. These points are then written to a Rhinoceros command file in such a way that only every 4th slice is considered and 20

the points in each slice are connected using a closed 6th order spline curve. The reduced sampling and high order spline curves were determined empirically in order to prevent high frequency oscillations in the surface that are obvious artifacts of the sampling. Figure 12 shows this sampling artifact in the spline curves, and Figure 13 shows it when the curves are lofted together. Table 1 has data regarding the change in volume of the models with regards to slice sampling. The change to the model volume as a result of this resampling is only about 1%.

Figure 12: Sampling artifacts in spline curves

Table 1: Change in lumen volume with respect to sampling

1 for 1 1 for 2 1 for 4

Lumen Volume (cm3) 14.88 14.94 15.03

% Change in Volume 0.00 0.39 1.02

Figure 13: Sampling artifact in lofting. (A) 1 for 1 sampling. (B) 1 for 2 sampling. (C) 1 for 4 sampling

21

Rhinoceros is a computer aided design (CAD) package based on nonuniform rational B-splines (NURBS) instead of line segments like traditional CAD packages. As a result, Rhino is excellent for smoothing out digitized geometry extracted from the MRI scans into smooth and continuous shapes. In Rhinoceros, the command file exported from Matlab is read in as a stack of 2 dimensional spline curves a shown in Figure 14. The lumen boundary curves are lofted together and then caped at the ends to form the fluid domain. Due to the resolution of the MRI, many defining features of the wall cannot be resolved such as calcium deposits or the boundary between the artery wall and intraluminal thrombus (ILT). For that reason, the thickness of the arterial wall
Figure 14: Initial data in Rhino

Figure 16: The development of the blood domain

Figure 15: Offsetting the outer surface 1.8mm (center) and subtracting the blood domain (right)

23

tissue is assumed to be 1.8mm. The arterial wall spline curves are lofted together and the resulting surface is offset inwards 1.8mm. The inset wall surface and lumen surface then act as the outer and inner boundaries of the intraluminal thrombus (ILT). To ensure the wall has uniform thickness, the outermost boundary of the combined fluid and ILT domains is then offset 1.8mm out. The arterial wall is defined as the body between these 2 surfaces. Since the outer boundary of the ILT was defined to be 1.8mm in from the defined arterial wall curves, the majority of the arterial wall is exactly the Figure 18: Both the ILT domain (left) same as the surface generated from the hand drawn outer boundary. Wherever the
and wall inner boundary (right) are derived from combinations of the lumen boundary and inset outer boundary

fluid domain extends through the ILT layer, however, the artery wall is expanded slightly past this surface to ensure a 1.8mm thick boundary around these protrusions. Once the 3 domains (blood, ILT, and wall) are defined, the solid and fluid domains of the model are exported from Rhinoceros separately as parasolid files (.x_t).

Figure 17: The solid domain. The thick spots are places where the surface was deemed too thin

24

In order to obtain flow rate data from the MRI velocimetry images, the images are loaded into another in-house image analysis program written in Matlab. This program prompts the user to select a region of interest (ROI) at each end. It then records the flow rate through that region for each image in the stack by averaging the velocity over the region and multiplying it by the area. The trigger time property, recorded in the Dicom images, is used to place each data point in time. This property records how long after the trigger event, in this case the patients R-wave, the image was taken. Doing this for the inlet and outlet of the model results in flow rate vs. time curve defined at both ends of the model. To enforce a steady state flow, the period and flow volume per cycle of the outflow is scaled to match the inlet flow. This prevents the volume of model from slowly changing across many cardiac cycles. Also, a running average is applied to the data to help dampen out noise. Figure 20 compares these raw and processed flow curves.
Figure 19: Selecting a region of the velocimetry image to calculate the flow rate through

25

Experimental Flow Rate Data 50

Flow Rate in Z-direction (cm 3/s)

-50

-100

-150

inlet (raw) inlet (processed) outlet (raw) outlet (processed)

-200

0.1

0.2

0.3

0.4

0.5 0.6 Trigger Time (s)

0.7

0.8

0.9

Figure 20: Comparison of flow rate data before and after processing

The next step is to bring the CAD geometry into the finite element (FE) program ADINA. ADINA works with solid and fluid models separately. This is why the models were exported from Rhino separately. There is error introduced in assuming that the imaged geometry is the natural shape of the artery because in fact the artery was under pressure when imaged. Since the stress of the material during imaging is not known this pre-stress must be ignored, and the imaged geometry is assumed to be the arteries unstressed state. In the fluid model, the blood is modeled as a simple Newtonian fluid with a viscosity of 3.5cP (3.5*10^-3 Pa*s) and density of 1035 kg/m^3 [15]. The flow is assumed to be laminar. This would typically be an unrealistic assumption since abdominal aortic flow is known to be 26

transitory (meaning it is sometimes laminar and sometimes turbulent). This affect the fluid flow because turbulent flow dissipates kinetic energy through the use of eddies while laminar flow does not. While the flow could be modeled as turbulent, this is ill advised due to a lack of information regarding the amount of energy being dissipated. To combat this flaw flowcondition-based interpolation (FCBI) elements are used instead of traditional finite fluid elements. FCBI elements are a hybrid between the finite element and finite volume mathematical techniques for solving fluids models [33]. They utilize the Petrov-Galerkin finite element formulation Equations (2)-(3) to solve the incompressible Navier-Stokes Equations.

(2)

(3)

The Petrov-Galerkin method is a virtual work method for finding the minimum energy solution to a given energy equation. In this case v and p represent a descretized and interpolated solution for the real velocities and pressures of the solution while w and q represent small changes to the velocity and pressure solution. The energy will be minimized when infinitely small changes in either of the imaginary variables have no effect on the energy. In variational calculus, this is known as a stationary point and it signifies a maximum or minimum. When dealing with energy functions, there is typically no maximum value, and so this is equivalent to solving for a minimum. Typically the interpolation equation used to represent v is also used to represent w. This is known as the Bubnov-Galerkin method. In the case of FCBI elements, 27

however, the v function contains exponential terms dependent upon the Reynolds number and the w function is defined as a step function. During the solution process, the exponential terms allow the solution to automatically account for upwinding effects, while the discontinuous virtual velocity function ensures that interpolation between nodes perfectly captures the function shape. This results in conservation of both mass and momentum at the element scale. By enforcing conservation laws and accounting for upwinding effects, FCBI elements can model highly turbulent flows well without the use of a turbulence model [34]. At the inlet to the fluid domain a velocity boundary condition (BC) is chosen to produce the measured inlet flow rate for the given area of the inlet. This is done simply because ADINA does not support a flow rate BC. Applying a uniform velocity across the opening assumes the flow can be modeled as plug flow. A pressure BC is applied to the outlet. The source of this pressure BC comes from the downstream impedance condition and the downstream flow rate velocimetry data. The remaining surfaces are constrained by the solid domain through a fully coupled FSI BC. This BC actually incorporates kinematic, velocity and stress requirements shown in Equations (4)-(6) all into one BC.

(4) (5) (6)

28

The kinematic constraint requires the fluid and solid surfaces remain fixed to one another and displace together. The velocity BC is the no-slip condition, specifying the velocity of the fluid at the wall to be 0, and the stress BC requires the stress to be continuous across the boundary. When the solid model is loaded into ADINA, there will be 2 separate bodies the wall and the ILT. At the shared surface between these 2 bodies, they must be linked together, physically attaching them to one another. If this were not true, the wall and ILT would be able to slide against one another. The arterial wall tissue is modeled using the Mooney-Rivlin hyperelastic material model proposed by Raghavan [24].

(7)

Where W is the strain energy and I1 is the first invariant of the left Cauchy-Green tensor. and are empirical values derived from uniaxial tensile loading of AAA wall tissue excised during surgical repair equal to 174 kPa and 372 kPa respectively. The ILT tissue is modeled using the linear elastic model developed by Di Martino [30, 35], where the Youngs Modulus is 110 kPa and the material is incompressible (Poissons Ratio = 0.49). Other than the FSI BC on the inner surface of the combined solids the only other BCs are at the upper and lower ends of the model. At the top surfaces of the model, both bodies are fully constrained. While this is admittedly nonrealistic, it is required to enforce the fluid flow rate BC at the inlet. Since the flow rate BC is defined as a velocity BC (because ADINA does not support flow rate BCs) it requires the area of the inlet to remain constant. Otherwise, the velocity BC will no longer represent the desired 29

flow rate. At the outlet, the bodies are only constrained in the out-of-plane direction. This is commonly referred to as being longitudinally tethered in the literature and is a better reflection of the true boundary. No BC is applied to the outer surface of the model. This assumes any stabilizing effects from the loose connective tissue surrounding the aorta are negligible. The fluid and solid models are solved using a direct FSI algorithm. Each body is meshed using linear elements at a density of about 2 mm. This mesh is optimized for speed, not accuracy, and does not claim to be mesh independent. Once a model converges to an impedance condition, the mesh can be refined to eliminate mesh dependence, and then rerun through the optimization routine. This speeds up the procedure by minimizing the number of times a dense mesh required to generate a mesh independent solution is run. The mesh initially used is shown in Figure 21. It consists of 34k 1st order fluid elements, 25k 1st order arterial wall elements and 33k 1st order ILT elements. Once the FE models are constructed, the downstream impedance condition can be considered. The downstream impedance condition is a time varying relationship between the amount of blood leaving the model (outlet flow rate) and the amount of resistance to that blood flow (downstream pressure). This complex 30
Figure 21: Example of rough wall, ILT, and fluid mesh (left to right) used during optimization

relationship results from the network of arteries downstream of the model. Each artery, like a pipe, resists fluid flow. This resistance is related to the length and diameter of each artery in the network. Because the arteries are compliant the artery diameter is related to pressure and the artery material properties too and so the impedance condition depends not only on length and diameter of every downstream artery, but also pressure and compliance. The actual impedance condition for an arterial tree is therefore much too complex to derive directly. Instead simplifications are made. The Windkessel 4 parameter model is a common way to approximate the impedance condition in large arteries [36]. Mathematically it is represented by the transfer function shown in Equation (8).
Figure 22: Fluidic circuit diagram of the downstream impedance condition

(8)

Where R1, R2, L, and C are 4 constants, j is the imaginary number, and is the frequency of the input. Using the electrical-fluid flow analogy, this impedance condition can also be depicted using as shown in Figure 22, where the parameters of each component correspond to the 31

constants in Equation (8). R1 and


Bode Diagram

L represent a low-pass filter in which the gain is determined by R1 and the cut-off frequency is determined by L. In a similar
Phase (deg) Magnitude (dB)

200

150

100

50

0 270 225 180 135 90 -8 10

way, R2 and C represent a highpass filter in which the gain is determined by R2 and the cut-off frequency is determined by C. The result of combining these 2 effects is a

10

-6

10

-4

10

-2

10

10

Frequency (rad/sec)

Figure 23: Frequency response of 4-parameter Windkessel

notch filter, which attenuates signals within the 2 cut-off frequencies while amplifying high frequency signals by R1 and low frequencies by R2 as shown by the bode plot in Figure 23. One of the downsides to this method of creating a pressure curve is that it can only react to changes in flow rate and cannot detect any type of baseline pressure. The pressure curve resulting from the Windkessel model assumes the pressure is zero when the flow rate is zero. In order to sidestep this pitfall, the patients brachial diastolic pressure is added to the pressure curve, making the pressure equal to the brachial diastolic pressure when the flow rate is zero and preserving the dynamic pressure trends. This of course assumes that the artery is at diastole when the flow rate is zero, and that diastole in the aorta is the same pressure as diastole in the brachial artery. Since the outlet flow rate is known from MRI velocimetry images, 32

outlet pressure curves can be


Experimental Flow Rate

Flow Rate (cm3/s)

derived through transforming the flow rate to a pressure using the Windkessel model and then adding the measured

200 150 100 50 0 -50 0 0.5 1 1.5

Impedance Transformed Pressure Curve 1

diastolic pressure to the resulting pressure curve as shown in Figure 24. Note that changing the 4 parameters of

Pressure (kPa)

0.5 0 -0.5 -1 0 0.5 1 1.5

Final Offset Pressure Curve 11.5

the Windkessel model will alter the final pressure curve independent of the measured flow rate data. This process is totally automated using in-house Matlab code.

Pressure (kPa)

11 10.5 10

0.5 Time (s)

1.5

Figure 24: The process for developing a pressure curve. Transform the experimental flow rate (middle) and then add the diastolic pressure (bottom)

In order to calibrate the impedance condition to the patient, an outlet pressure curve is derived from an initial guess of the correct impedance condition. The pressure BC is imported into the ADINA FE model and the model is run to completion. Once the model has completed running, the downstream flow rate vs. time curve from the model is exported to Matlab where it is compared with the MRI velocimetry measured flow rate vs. time curve. The error in agreement between the curves is measured as the total area between the curves squared. This 33

error in flow rate agreement is then minimized by varying the 4 impedance parameters. The optimization routine systematically selects values for the 4 parameters of the impedance model, derives pressure curves based on those selected impedance parameters, and then runs a FE model with that pressure curve to completion. Once completed, the error in flow rate agreements is calculated for that specific model. This process is repeated until the functional value has converges to some minimum amount of error. Once the model is converged, the impedance condition is said to be calibrated to the patient. This process is automated using a series of custom made Matlab and Visual Basic programs. The optimization routine used is the Nelder-Mead simplex method which is a nongradient method. This routine makes decisions regarding which impedance parameters to evaluate next by considering a simplex of points (in this case a simplex is 5 points). At the beginning of each iteration, the largest functional valued point in the simplex is reflected across the centroid of the remaining simplex points. Based on the evaluation of this point, a decision is made regarding which point to try next. The method chooses to either end the iteration, expand, contract inside, contract outside or shrink [37].

34

If the reflected point is not as good as the best point, but better than the 2nd worst point, the reflected point is chosen to replace the worst point in the active simplex and the iteration ends. If the new point is less than the lowest value in the simplex, expansion is attempted. A point is calculated which lies further in the same direction than the reflected point did. If the expansion point is less than the reflected point it replaces the largest point in the simplex, otherwise, the reflected point replaces the largest point. If the reflected point is better than the worst point in the simplex but not better than any others, an outside contraction is performed. Another point is evaluated closer to the simplex than the initial reflected point. The best point between these 2 replaces the worst point in the simplex. If the reflected point is worse than any point currently in the simplex, a contract inside operation is performed. During this step, a point close to the centroid is evaluated. If the new point is better than the worst point in the simplex, replace the worst point with it. If the new point is still not better than current worst point, a shrink operation is performed. This means that all of the current points in the
Figure 25: Types of NelderMead iterations shown in 2D. From top to bottom: reflection, expansion, contract outside, contract inside, shirnk.

35

simplex (except the best


10

x 10

-11

Convergence Chart for Unconstrained Optimization of Patient TK

one) are brought towards


9.5

the best simplex point.


Error (m3/entire model)
9

A proper convergence criterion for the optimization routine has

8.5

not yet been decided.


7.5

Convergence is currently declared when the lowest 5

10

20

30

40

50 Iteration

60

70

80

90

100

Figure 26: Convergence Chart

function values (the active simplex) are all within 10-15 of each other. This was chosen based on an observed lack of improvement in the functional values at this point as shown in Figure 26, and an r2 value greater than 0.995. The impedance parameters were still steadily changing value at this point which suggest that while the function appears to be close to convergence, it is not, by definition converged. Due to the large r2 value, an argument can be made that the functional value is close to convergence, however, the impedance parameters cannot be referred to as patient-specific until they also converge. Since the functional value is of primary importance, this is acceptable, however in order to develop a patient-specific impedance condition, more iterations would be necessary. Initial attempts at optimization utilized the Broyden-Fletcher-Goldfarb-Shanno (BFGS) gradient based method. Gradient based methods such as BFGS determine the direction of 36

steepest decent by calculating the gradient at the operating point, usually through a finite difference scheme. They then move in that direction towards a minimum value before recalculating the gradient and changing the direction of search. Gradient methods traditionally outperform non-gradient methods in most cases. They can however become unstable when dealing with signal noise and discontinuous functions due to the impact these have on calculating a reliable gradient value. Reliably calculating a gradient value was the reason the Nelder-Mead method was eventual chosen over the BFGS method despite BFGSs better rate of convergence. Since each function evaluation requires running a complex FE simulation, the whole optimization routine has proven very computationally expensive and can require literally hundreds of FE models to run before convergence is reached.
Optimization Routine Selects an Impedance Condition

The Modeled Flow Rate is Used to Calculate the Error

A Pressure Curve that matches that Impedance Condition is Generated

The Outlet Flow Rate is Extracted From The Model Results

The FE Model is Modified to Include the New Pressure Curve

The FE Model is Run to Completion

Figure 27: Iterative cycle for generating a patient specific impedance condition. Red denotes steps taken in Matlab, while blue represents steps in ADINA

37

Once the model has converged using a minimalistic mesh, the mesh must be refined to assure a mesh independent solution. Figure 28 shows the refined mesh used after the impedance was optimized. This improved mesh contains 111k 1st order fluid elements, 58k 1st order wall elements, and 117k 1st order ILT elements.

Figure 28: Refined mesh used to generate mesh independent solutions

38

Chapter 5: Results
In application, deriving a patient-specific impedance condition has proven time consuming. Once the images are received, generating a model ready to be used in the optimization routine takes anywhere from 4 hours to a couple days depending on what type of problems are encountered, usually with regards to the validity of the geometry in Rhino, or the linking of the two solid bodies in ADINA. With more practice and experience, this will likely drop considerably Each Finite Element (FE) model then takes approximately 5-7 hours to run when given two 3 GHz, 64-bit processing nodes, and requires about 2 Gb of RAM. Since this large computation time results more from the FSI iterations and time steps than from the number of degrees of freedom in the model, a large diminishing return is seen when parallelizing these models across many cores. The Patient TK model required 161 models to be evaluated before converging (functional simplex range less than 10-15). The model therefore required approximately 1000 hours (41 days) of run time to converge starting from a standard Windkessel impedance value for the abdominal aorta [36]. Figure 29 compares the initial and final modeled flow rates with the experimentally measured one, and Figure 30 shows the initial and final outlet pressure boundary condition applied to the model. Figure 31 then compares the calculated max stress values of the converged and unconverged models.

39

Comparision of Outlet Flow Rates 200 Experimental Standard Converged

150
Outlet Flow Rate (cm 3/s)

100

50

-50

0.5 Time (s)

1.5

Figure 29: Comparison of initial and final flow rate conditions for patient TK
Downstream Pressures Derived from Impedance Conditions 12 11.8 11.6
Downstream Pressure (kPa)

Standard Converged

11.4 11.2 11 10.8 10.6 10.4 10.2 10

0.5 Time (s)

1.5

Figure 30: Comparison of initial and final pressure conditions for Patient TK

40

Max Stress Values Before and After Convergence


160 140 Max Von Mises Stress (kPa) 120 100 80 60 40 20 0 Wall ILT Initial Converged

Figure 31: Comparison of initial and final max stress values for Patient TK

While solid conclusions cannot be drawn from a single data set, the data comparing the converged and initial conditions for Patient TK suggests that generating a patient-specific downstream impedance condition has almost no impact on the calculated maximum wall stress. While the range of dynamic pressures increased by about 200 Pa (14%) the max wall stress changed by less than 0.1%. While a much larger change in the ILT stress (15%) occurred, the stress experienced by the ILT is not typically considered when predicting AAA rupture. Figures 32 and 33 compares experimental outlet flow rate with the standard outlet flow rate generated by using a typical impedance condition for 2 other patients. When compared with Patient TKs

41

initial data, both of these data sets show more potential for improvement through the derivation of a patient-specific boundary than Patient TK did.

Comparision of Outlet Flow Rate for Patient MM 120 Experimental Standard

100

Outlet Flow Rate (cm 3/s)

80

60

40

20

-20

0.5 Time (s)

1.5

Figure 32: Comparison of initial and experimental outlet flow rates for Patient MM
Comparision of Outlet Flow Rates for Patient AK 180 160 140 Experimental Standard

Outlet Flow Rate (cm 3/s)

120 100 80 60 40 20 0 -20

0.2

0.4

0.6

0.8

1 1.2 Time (s)

1.4

1.6

1.8

Figure 33: Comparison of initial and experimental outlet flow rates for Patient AK

42

Figure 34: Peak stress of initial (left) and converged (right) arterial wall

The mapped stress values for the converged and unconverged Patient TK models during their peak stress values are shown in Figure 34. While the peak values themselves did not increase much, the amount of total strain the wall is experiencing is slightly larger. This suggests that the increased load is being distributed across a larger area of wall by the ILT. This behavior allows the total load to increase, while not increasing the maximum wall stress values. It is of note that Patient TK has a very large amount of ILT tissue when compared to the other patients, and having less ILT would likely increase the sensitivity of the maximum stress to changes in the

43

fluid load. If this is the case, patient-specific impedance conditions could have a more dramatic effect on max stress values in other patient geometries. At the initial standard impedance value, a parametric study of the wall stiffness and optimization parameters was performed on Patient TK with the goal of better understand the effects of the material properties and optimization parameters on these peak stress values. Figure 35 shows the results of varying the stiffness of the wall material over 2 decades. Because the artery wall is a hyper-elastic material, its stiffness changes as a function of deformation. The presented wall stiffness values represent the stiffness of the material only while completely undeformed.

44

Effects of Artery Wall Initial Stiffness on Max Stress 150

Max Von Mises Stress (kPa)

100

50

0 2 10

10 10 ILT Young's Modulus (kPa)

10

Figure 35: Artery wall stiffness variational study

The astricks markers represent the operating point, in this case the assumed material properties. The ILT shows to be very sensitive to changes in the wall stress. The wall itself shows significant change in max stress as well, though not at the operating point since the astricks is nearly a minimum. If the operating point were moved up to about 6*103 kPa, the sensativity would be about 30 kPA/decade. Figure 36 shows similar results in which the stiffness of the ILT was varied over about 1 decade. Since the ILT is a linear elastic model, the stiffness is constant and equivalent to the Youngs Modulus.

45

Effects of ILT Stiffness on Max Stress 140

120

Max Von Mises Stress (kPa)

100

80

60

40

20

0 1 10

10 10 ILT Young's Modulus (kPa)

10

Figure 36: ILT stiffness variational study

Changes to the stiffness of the ILT affect wall stress significantly more (approximately 110 kPa/decade at the operating point). This helps validate the theory that the ILT is helping distribute forces over the artery wall. Figures 37 40 show the impact of independently varying the 4 impedance parameters around the initial assumed impedance condition.

46

Effects of the R1 Impedance Parameter on Max Stress 140

120

Max Von Mises Stress (kPa)

Wall ILT 100

80

60

40

20 5 10

10

10
5

10

Fluid Resistance (Ns/m )

Figure 37: Impedance parameter R1 variational study


Effects of the L Impedance Parameter on Max Stress 120 110 100 Wall ILT

Max Von Mises Stress (kPa)

90 80 70 60 50 40 30 20 4 10

10

10

10

Fluid Inertance (m 8/kgs)

Figure 38: Impedance parameter L variational study

47

Effects of the R2 Impedance Parameter on Max Stress 120 110 100 Wall ILT

Max Von Mises Stress (kPa)

90 80 70 60 50 40 30 20 -5 10

10

10

10

10

10
5

15

10

20

Fluid Resistance (Ns/m )

Figure 39: Impedance parameter R2 variational study


Effects of the C Impedance Parameter on Max Stress 140 Wall ILT 120

Max Von Mises Stress (kPa)

100

80

60

40

20 -5 10

10

10 Compliance (m 5/N)

10

10

Figure 40: Impedance parameter C variational study

48

The max stresses do not appear to be as sensitive to changes in the impedance model as they are to changes in the material models. This suggests that the optimization routine might be better implemented, if a downstream boundary condition was assumed and patient specific material properties were generated using the optimization routine. Due to the nature of the routine, this should be a generally easy change to make. The lack of change in functional value with respect to the R2 and C parameters is a result of the assumed impedance condition used. As mentioned, the R2 and C parameters act as a lowpass filter, allowing only low frequency components of the outlet flow rate data through, while attenuating higher frequencies. The C value defines what a low frequency is, by affecting the location of the cutoff frequency (the point at which the filter begins attenuating signals). Under the assumed impedance condition, the experimental flow rate curve, which acts as the input to the impedance condition has no low-frequency" components, as shown in Figure 41, where the impedance condition and input flow rate data are compared in the frequency domain. As shown in Figure 40, if the capacitance value were changed enough to include components of the input signal on the low-frequency side of the notch filter, then both C and R2 would have an effect on the stresses.

49

x 10
Magnitude (abs)

-5

Frequency Analysis of Outlet Flow Rate

6 4 2 0 -4 10

10

-3

10

-2

10

-1

10

10

10

Impedance Condition Frequency Response 150


Magnitude (dB)

100

50

0 -4 10

10

-3

10

-2

10

-1

10

10

10

Frequency (Hz)

Figure 41: Comparison of input function with impedance condition validating why R2 and C have no effect on the pressure curve

As the Patient TK model converged, the notched bandwidth actually moves further to the left. If the input frequencies from the outlet flow rate data are found to always lie entirely on the high frequency region of the impedance condition, the low-pass filter components would be unnecessary, and removing them will reduce the degrees of freedom being optimized, thus increasing the rate of convergence. This also explains a phenomenon encountered during the optimization in which the R2 parameter value varies wildly. During these swings in R2 value often attempts negative

50

resistance values, which make no physical sense. The optimization routines erratic behavior with respect to the R2 parameter is a result of the functions complete lack of dependence on R2. While the bulk of data collected so far has been with the Patient TK data set, other models have been generated and proven to work using these techniques. While these models work, they have not yet been run to convergence, and so they do not represent patient-specific impedance data sets. Figure show examples of other geometries and mapped stress that have been successfully calculated using the techniques presented.

Figure 42: Stress Maps for unconverged Patient MM (left) and Patient AK (right) models

51

Chapter 6: Future Work


While the optimization tool developed appears to work well, more data is necessary before any firm conclusions can be made with regards to its significants. Also, there is need for some further validation such as proving the solution is independent of the selected time stepping scheme and creating a more physically significant definition of convergence. With respect to the tool itself, experience suggests it would be a more accurate strategy to define an average velocity from the MRI velocimetry images instead of a flow rate. This would allow the region of interest defined during velocimetry image processing to be placed well within the visible lumen barrier instead of on it. The lumen cross sectional area varies by as much as 20% through the cardiac cycle and attempting to define a stationary boundary of the artery over which to calculate flow rate is an exercise in futility. If too large a region of interest (ROI) is selected unwanted noise is introduced from periphery pixel values. Declaring too small a ROI and artificially reducing the measured flow rate by not integrating over the whole cross sectional area of the lumen. While 52
Figure 43: The measured flow rate is determined by integrating the velocity over a specified ROI. By measuring average velocity instead of flow rate, the ROI could be shrunk to easily fit in the lumen with out introducing error.

calculating the average velocity requires the flow be modeled as plug flow (uniform velocity cross section), this assumtion is already made during modeling, based on how the boundary conditions are introduced. Another possible improvement to the current system would be the development of an ADINA flow rate boundary condition (BC) so that the solid BC on the inlet side can be relaxed to longitudinally tethered instead of being fully constrained. The development of such a BC can be done through the use of the user-defined BC options in ADIANA. This would require a fortran file to be constructed which reacts with the back-end of ADINAs solver. Improving the upstream BCs should prevent the non-physiological stress concentrations at the top of the model which are currently often the location of maximum ILT stress values. The goal of genrating a push button operation that can easily walk medical personel with no engineering experience through the process of designing, running and analysising a model has not yet been reached. While many sections, such as image analysis and optimization have been well packaged, the CAD and FE modeling has not. Now that a foundation has been laid regarding the operation of this AAA modeling tool, efforts could begin to package it into be more user-friendly push button operation. While the above improvements would be beneficial, the primary question when facing the future of this project involves its current inability to decouple fluid force and material stiffness. There are an infinite number of combinations or impedance condition and material properties which can generate a given flow rate. As long as the patient-specific impedance 53

parameter requires assumed material properties to be correct, it cannot be referred to as patient-specific in the true sense of the word. In order to combat this, a different method for determining either downstream pressure or material properties would be required, such as tabulated clinical data. Some might suggest pulse wave velocimetry (PWV) as one possibility for generating this type of data. While it is possible PWV could work, the theory behind PWV is closely tied in with the deformation of the artery, so it is questionable if any more data would be included in a PWV measurement than all ready exists. Also, PWV cannot be used to completely define either the downstream flow rate or the material stiffness. Instead it can provide the dynamic range of the pressure values or the average stiffness of the artery at the operating pressure independant of position or material type. Both the wall and ILT would be averaged together in this arterial stiffness value. However, if empirical data were collected and tabulated in such a way that educated guesses could be made regarding either wall stiffness or downstream pressure based off of a series of indicators (age, PWV, sex, etc.) the amount of error introduced through the assumed value could be reduced. Increasing the accuracy of the assumed value, in conjunction with generating a patient-specific pressure or stiffness condition (depending on which data is tabulated) will validate the procedure. Another possible improvement could be the introduction of variable arterial wall thickness and calcified tissue to the models. To include these details, the resolution of the anatomical data must be increased by about 3 orders of magnitude (from 1x1x1 mm to 54

0.1x0.1x0.1 mm) this desired level of resolution is impossible to obtain using an MRI. Computer Tomography (CT) is a much better imaging technique for achieving this resolution. Also, calcified tissue is easily distinguishable from ILT in CT scans. One of the possible challenges with incorporating CT scans into the procedure will involve properly registering the new anatomical CT images with the still required MRI velocimetry images. As mentioned earlier, the velocimetry data must be well registered with the anatomical data or else it could negatively affect the optimization routine by altering the phase shift between the velocity curves.

55

References

1.

2. 3. 4.

5.

6.

7.

8.

9.

10. 11.

12. 13. 14.

Bluestein, D., et al., Intraluminal thrombus and risk of rupture in patient specific abdominal aortic aneurysm - FSI modelling. Computer Methods in Biomechanics and Biomedical Engineering, 2009. 12(1): p. 73-81. Union Memorial Hospital. Aneurysms. [cited 2010 Aug. 13]; Available from: http://www.unionmemorial.org/body.cfm?id=555789. Cardiolabel Worldwide. Types of Aneurysm. May 1, 2010 [cited 2010 Aug. 26]; Available from: http://www.cardiolabel.eu/Types%20of%20Aneurysm.html. University of Nebraska: Department of Engineering Mechanics. Pressure Vessels. 2000 [cited 2010 Aug. 16]; Available from: http://emweb.unl.edu/negahban/em325/18pressure-vessels/pressure%20vessels.htm. Society of Vascular Surgery. Abdominal Aortic Aneurysm. Vascular Web 2010 Jan. 19, 2010 [cited 2010 Aug. 13]; Available from: http://www.vascularweb.org/vascularhealth/Pages/AbdominalAorticAneurysm.aspx. Maier, A., et al., Impact of calcifications on patient-specific wall stress analysis of abdominal aortic aneurysms. Biomechanics and Modeling in Mechanobiology, 2010: p. 1-11. Gasser, T.C., R.W. Ogden, and G.A. Holzapfel, Hyperelastic modelling of arterial layers with distributed collagen fibre orientations. Journal of the Royal Society Interface, 2006. 3(6): p. 15-35. Mower, W.R., L.J. Baraff, and J. Sneyd, STRESS DISTRIBUTIONS IN VASCULAR ANEURYSMS - FACTORS AFFECTING RISK OF ANEURYSM RUPTURE. Journal of Surgical Research, 1993. 55(2): p. 155-161. U.S. Department of Health & Human Services, National Institutes of Health, and National Heart Lung and Blood Institute. Aneurysm. 2009 April 2009 [cited 2010 August 13]; Available from: http://www.nhlbi.nih.gov/health/dci/Diseases/arm/arm_what.html. aortic bifurcation, in Stedman's Medical Dictionary. 2006, Lippincott Williams & Wilkins. Wang, D.H.J., et al., Effect of intraluminal thrombus on wall stress in patient-specific models of abdominal aortic aneurysm. Journal of Vascular Surgery, 2002. 36(3): p. 598604. Thomas, P.R. and R.D. Stewart, Abdominal aortic aneurysm. Br J Surg, 1988. 75(8): p. 733-6. Thubrikar, M.J., et al., Mechanical properties of abdominal aortic aneurysm wall. Journal of Medical Engineering & Technology, 2001. 25(4): p. 133-142. Scotti, C.M., et al., Wall stress and flow dynamics in abdominal aortic aneurysms: finite element analysis vs. fluid-structure interaction. Computer Methods in Biomechanics and Biomedical Engineering, 2008. 11(3): p. 301-322. 56

15.

16. 17. 18. 19.

20.

21. 22. 23. 24.

25. 26.

27. 28.

29.

30.

Rissland, P., et al., Abdominal Aortic Aneurysm Risk of Rupture: Patient-Specific FSI Simulations Using Anisotropic Model. Journal of Biomechanical Engineering-Transactions of the Asme, 2009. 131(3). da Silva, E.S., et al., Morphology and diameter of infrarenal aortic aneurysms: a prospective autopsy study. Cardiovascular Surgery, 2000. 8(7): p. 526-532. Darling, R.C., et al., Autopsy Study of Unoperated Abdominal Aortic-Aneurysms - Case for Early Resection. Circulation, 1977. 56(3): p. 161-164. Sterpetti, A.V., et al., Factors Influencing the Rupture of Abdominal Aortic-Aneurysms. Surgery Gynecology & Obstetrics, 1991. 173(3): p. 175-178. Raghavan, M.L., M.W. Webster, and D.A. Vorp, Ex vivo biomechanical behavior of abdominal aortic aneurysm: Assessment using a new mathematical model. Annals of Biomedical Engineering, 1996. 24(5): p. 573-582. Geest, J.P.V., et al., A biomechanics-based rupture potential index for abdominal aortic aneurysm risk assessment - Demonstrative application, in Abdominal Aortic Aneurysm: Genetics, Pathophysiology and Molecular Biology, M.D. Tilson, H. Kuivaniemi, and G.R. Upchurch, Editors. 2006, Blackwell Publishing: Oxford. p. 11-21. Santelices, L.C., et al., Relative Contributions of Age and Atherosclerosis to Vascular Stiffness. Cts-Clinical and Translational Science, 2008. 1(1): p. 62-66. Stalhand, J., Determination of human arterial wall parameters from clinical data. Biomechanics and Modeling in Mechanobiology, 2009. 8(2): p. 141-148. Geest, J.P.V., et al., The effects of anisotropy on the stress analyses of patient-specific abdominal aortic aneurysms. Annals of Biomedical Engineering, 2008. 36(6): p. 921-932. Raghavan, M.L. and D.A. Vorp, Toward a biomechanical tool to evaluate rupture potential of abdominal aortic aneurysm: identification of a finite strain constitutive model and evaluation of its applicability. Journal of Biomechanics, 2000. 33(4): p. 475482. Sacks, M.S., et al., In vivo three-dimensional surface geometry of abdominal aortic aneurysms. Annals of Biomedical Engineering, 1999. 27(4): p. 469-479. Raghavan, M.L., et al., Wall stress distribution on three-dimensionally reconstructed models of human abdominal aortic aneurysm. Journal of Vascular Surgery, 2000. 31(4): p. 760-769. Smith, D.B., et al., Surface geometric analysis of anatomic structures using biquintic finite element interpolation. Annals of Biomedical Engineering, 2000. 28(6): p. 598-611. Speelman, L., et al., Effects of wall calcifications in patient-specific wall stress analyses of abdominal aortic aneurysms. Journal of Biomechanical Engineering-Transactions of the Asme, 2007. 129(1): p. 105-109. Dorfmann, A., et al., Evaluating patient-specific abdominal aortic aneurysm wall stress based on flow-induced loading. Biomechanics and Modeling in Mechanobiology, 2010. 9(2): p. 127-139. Papaharilaou, Y., et al., A decoupled fluid structure approach for estimating wall stress in abdominal aortic aneurysms. Journal of Biomechanics, 2007. 40(2): p. 367-377. 57

31.

Fillinger, M.F., et al., Prediction of rupture risk in abdominal aortic aneurysm during observation: Wall stress versus diameter. Journal of Vascular Surgery, 2003. 37(4): p. 724-732. 32. Yang, A.S., C.Y. Wen, and L.Y. Tseng, In vitro characterization of aortic flow using numerical simulation, phase-contrast magnetic resonance imaging, and particle tracking images. Proceedings of the Institution of Mechanical Engineers Part C-Journal of Mechanical Engineering Science, 2008. 222(12): p. 2455-2462. 33. Bathe, K.J. and H. Zhang, A flow-condition-based interpolation finite element procedure for incompressible fluid flows. Computers & Structures, 2002. 80(14-15): p. 1267-1277. 34. Bathe, K.J. and J.P. Pontaza, A flow-condition-based interpolation mixed finite element procedure for higher Reynolds number fluid flows. Mathematical Models & Methods in Applied Sciences, 2002. 12(4): p. 525-539. 35. Di Martino, E., et al., Biomechanics of abdominal aortic aneurysm in the presence of endoluminal thrombus: experimental characterisation and structural static computational analysis. Eur J Vasc Endovasc Surg, 1998. 15(4): p. 290-9. 36. Hlavac, M. and J. Holcik, Windkessel Model Analysis in Matlab, Brno University of Technology FEEC Brno University of Technology Department of Biomedical Engineering: Brno. 37. Lagarias, J.C., et al., Convergence properties of the Nelder-Mead simplex method in low dimensions. Siam Journal on Optimization, 1998. 9(1): p. 112-147.

58

Appendix A: How to Generate CAD data from MRI images

When a data set is received, it usually contains 1 file called DICOMDIR and then a serialized list of files as shown. None of these files will have file extensions. These files are called DICOM images. DICOM is a universal medical imaging format that is becoming popular for transferring medical image files between doctors and hospitals.

Figure 44: Typical raw data set

59

In order to view these images, a DICOM viewer such as MicroDicom will be necessary. In order to open a data set, only the DICOMDIR file needs to be opened in the viewer. This file is a catalog of all the images and how they are organized. Upon opening the data set, the file structure will become much more obvious. Each image is organized in a tree, similar to how windows explorer lays out folders. The most general tier specifies the patient. The next tier identifies which study the image belongs to, and the last tier is which series the image was taken in. The first couple series of any set are known as the localizers. These provide a general map of where the body is in the machine so that the MRI technician can determine where they need to image further. The localizers are of no concern during modeling. In fact, there are only 2 types of series of interest to the modeler: anatomical and velocimetry. Both of these will appear as images taken in the transverse plane of the body.

60

Figure 45: How to view data sets using a viewer program

The velocimetry data is the easiest to spot. It always consists of 4 sequential series, the last 3 of which just look like static. The 3 static looking series are maps of the velocity at a stationary slice position during different times. The series measure velocity in the X, the Y, and the Z directions respectfully. The series preceding these 3 velocity maps, which should look much more normal is the image generated while creating the 3 velocity maps, and is of no real importance other than matching the position of the images to the anatomical data. 61

The anatomical images are all within 1 series and look very similar to the 1st velocimetry image series except with better contrast. Each image is taken at a different location, so when viewing a series of these images you are moving superiorly or inferiorly through space instead of time. There is always another series that looks very similar to the anatomical series towards the beginning. This is one of the localizer series. It covers a larger area and has more distance between the slices than the anatomical data. Notice that each one of the images in the viewer is actually the title of one of the serialized files in the data set. Whatever the numbering scheme for these images is, it becomes quickly apparent that it is not designed allow easy differentiation of image series. In fact, the numbers in each series appear randomly organized. This can make reading specific image series into Matlab very tedious. To work around this a Matlab script (DicomSort.m) was created that breaks these image files into folders based on their place in the organizational hierarchy. Once the images are split up and reorganized, they will no longer be able to be opened in the Dicom viewer, but it will become much easier to locate all the images in a specific series through file browsing.

62

Figure 46: A data set after being organized into folders

To begin extracting the anatomical data go to the anatomical Matlab folder (D:\mark\Aortic Wall Imaging\Matlab Programs\clean-up\Anatomical Data). In this folder are all the scripts needed to extract the anatomical data and write the Rhino command file. The file entitled BATCH_FILE.m is simply a list of commands that must be run to perform the whole process. These commands can be performed one at a time to become familiar with the process and how it works, or the script can be run directly and will walk the user through the whole process. It is nice to understand each step of the batch file so that if mistakes are made midprocess the process can be stopped, the mistake can be fixed, and then the process can be resumed from where it was left off.

63

When dealing with Dicom images in Matlab there are 2 commands of interest: dicomread and dicominfo. The first reads the image in exactly like imread does. The second reads in a large structure or other data recorded in the Dicom format. While many of these values are not that important or self-explanatory, others are can be cryptic and are necessary to properly extract data from the images. Table 2 lists some of these fields and what they mean.
Table 2: Important Dicom properties and descriptions

Field Name SequenceName

Data Type String

Description In velocimetry images, the velocity conversion is the number buried in this string. (ex: if 75 is in the string, then max pixel intensity = 75 cm/s and min pixel intensity = -75 cm/s)

SliceThickness SpacingBetweenSlices

Number Number

The thickness of each image out-of-plane(mm) The percent of slice thickness that is not imaged between each slice in a 3D stack. (ex: SliceThickness=1, SpaceingBetweenSlices=10, therefore 0.1mm are skipped between each image)

ImagePositionPatient

Vector

global XYZ coordinates of the upper left (1,1) pixel (mm)

Continued

64

Table 2 Continued

Field Name PixelSpacing

Data Type Vector

Description size of each pixel in the local xy directions (mm) The first 3 values are a unit vector in the local x direction in terms of global XYZ coordinates. The last 3 are a unit vector in the local y direction in terms of global XYZ coordinates.

ImageOrientationPatient Vector

TriggerTime

Number

In velocimetry images, the amount of time after the triggering event the image was taken (ms)

The image analysis begins with all the images from the anatomical data series being read into Matlab. Then a GUI aids the user in cropping the MRI images of the whole chest down to a localized area around the aorta. Properly cropping down the image will save a lot of computational time during image processing, but will also avoid introducing edge effects in the region of interest, caused by the filter neighborhood falling off the edge of the image. During cropping, if there are images at the beginning or end of the series that need to be taken out, they can be removed using the Remove button, however, if they are in the middle of the series, they should not be removed, because the program will assumes even spacing between all the images. The images can be cropped repeatedly in one run if necessary, but remember to hit the crop button before exiting, otherwise the image will not be cropped.

65

Figure 47: How to use the Crop GUI

After cropping, the data is pre-filtered using a 3x3x3 neighborhood median filter. The command used to do this is medfilt3 which is not a packaged Matlab command but can be downloaded for free. When an image is filtered with a median filter, the new pixel value is defined as the median value of all the pixels in its neighborhood on the original image. The benefit of a median averaging filter over a more traditional mean averaging filter is that a

66

median filter does a better job at preserving edges, whereas mean averaging will erode edges in an image. After the pre-filter, the edging GUI is loaded. The edging GUI handles the magnification, filtering, edging, and closing operations. The magnification operation enlarges the images by interpolating more pixel values using bicubic interpolation. The level of magnification can be controlled using the textbox labeled Magnification, however in practice this appears to be unnecessary and the default (4x) works very well. After being magnified, a 2D median filter is applied to each slice. The filtering helps eliminate noise during the edging operation. The filter neighborhood is always assumed to be square and the size can be controlled by using either the slider or text box in the Filtering group. While not prevented, using even numbered neighborhood sizes is ill advised because there should always be a central pixel in the filtering neighborhood. While the median filter does attempt to preserve the lumen edge, an excessively large neighborhood will shift the edge inward do to its naturally convex shape. Therefore it is advised to keep the median filter size relatively small unless necessary. The filtered image is then passed to the Canny edging algorithm. This algorithm utilizes 2 different thresholds, each with their own slider and textbox located in the Edging button group. The main or primary threshold defines how much of a change in pixel value is required before an edge is declared to exist. The lower the primary threshold, the more edges will be detected in an image. The secondary threshold also detects edges, however any edge detected 67

using the secondary threshold is only kept as an edge if it touches an existing primary edge. In this way, the secondary threshold can control how long an edge is or how many times it branches off. The lower the secondary threshold, the longer and more complex edges will appear. The secondary threshold must always be lower than or equal to the primary threshold. Once the appropriate edging thresholds are set for an image, a morphological closure operation is performed on the edged image. This means that all the edges are thickened by the closure radius, and then eroded back to their original size with the caveat that any edges that were connected during the thickening process (usually referred to as a flooding operation) remain connected when the lines are eroded back down. The result is that any small holes in the boundary are filled in and connected. The amount of flooding performed can be controlled using the textbox labeled Closure Radius. Each image can be viewed at any step along this process by using the radio buttons located at the bottom of the GUI. This is extremely useful for determining exactly why an edge doesnt look right and what should be changed to make it better. The Unaltered option shows the raw image as it was read into the GUI, after the pre-filtering. The Magnified button shows the image after being refined through interpolation. Filtered is the image after being run through the median filter. Edged is the results after the edge detection algorithm has run. Closed looks very similar to edge usually, but it is actually the image after the morphological flood/fill operations were used to help close the lumen boundary. The Overlay option is very

68

helpful for validation because it plots the final detected boundary on top off the original image, to quickly ensure that the detected edge is correct.

Figure 48: How to use the Edging GUI

After exiting the edging GUI the boundaries are used to define filled bodies and the user is prompted to select the body which represents the artery in each image. Once the correct body is selected, it is eroded back down to an outer edge. The user is then prompted to draw a closed polygon on a series of slice images to define the artery outer boundary. These hand drawn points, as well as the points determined to be lumen edges are exported to Rhino. 69

The .txt file exported from Matlab is not just data, but actually a list of commands to be executed in Rhinos command terminal. Upon opening Rhino, all of the Matlab data can be input by going to Tools>Commands>Read from File and selecting the Matlab outputted file.

70

Appendix B: Generating a CAD model in Rhinoceros


Generating the CAD model in Rhino is a less structured step in the process. Each geometry is different and different problems are encountered when attempting to model it. The overall goal in Rhino is to take the spline curve data loaded in from Matlab and generate 3 closed bodies representing the blood, the ILT, and the wall. As you will see when you load the data into Rhino, everything in Rhino is organized into layers. The command file creates a new layer for each spline curve it loads in. The layers are designated either Fluid Domain #, for spline curves representing the lumen boundary, or Outer Wall # for spline curves representing the outer boundary of the artery. The first thing is to look through the data loaded into Rhino and correct any obvious errors, such as the spline curve connecting through points in the incorrect order. To correct an obvious error, activate and show only the layer containing that curve. Then delete the undesired points and recreate the spline curve as shown in Figure 49.

71

Figure 49: Fixing spline curves in Rhino

Once you are happy with the imported data you can begin developing it into solid bodies. I usually start with the inner most layer and work my way out. Create a new layer (surface A), and generate a surface that is all of the lumen slices lofted together. Then create another layer (Blood Domain) and copy the surface you just created into it (Ctrl-C, Ctrl-V, Edit>Layers>Change Object Layer). Cap the ends. Your fluid domain is done. 72

Figure 50: Generating surface A and the blood domain

The next step is to create the ILT domain. The outer wall is assumed to be 1.8 mm thick and so the outer boundary of the ILT should be 1.8mm in from what was determined to be the outer boundary of the wall. Create a new layer (surface B) and loft together the outer wall spline curves using the same procedure used to create surface A, except instead of using a loose loft, as done before, Use a tight loft. Then in another new layer (surface C), offset surface B inwards 1.8mm. 73

Figure 51: Generating surface B and surface C

Surface C then represents the inner boundary of the artery wall and Surface A represents the outer boundary of the fluid domain. Therefore the ILT should occupy the space between these 2 surfaces. In many cases these surfaces will overlap one another in a few spots. In these places there is assumed to be no ILT. Create a layer (ILT domain) and make copies of both surface A and surface C in it. In order to develop a solid body from these two overlapping surfaces, the sections where the fluid domain is outside the wall domain must be deleted. The most dependable way to do this is to use the split command. Split the surface A duplicate in the ILT 74

domain using surface C and split the surface C duplicate in the ILT domain using surface A. The result should be that wherever the 2 surfaces cross each other, they have been divided into separate surfaces. Then simply delete all the patches of unwanted surface to get 2 surfaces similar to those shown in Figure 52.

Figure 52: (left) Surface C after split operation. (right) A split surface A and surface C after the extra material was deleted.

Once these surfaces are caped and joined, the ILT solid will be complete. However, using the OffsetSrf command causes the edges of surface C to no longer be planar. In order to cap an object the surfaces must be made planar at the ends. To do this, create a new layer (end 75

surfaces). Unhide a spline curve at each end of the model (it can be a fluid domain or outer wall curve. It doesnt matter because they should both be in the same plane). Create a plane from each of the spline curves. These planes will be used to define the ends of your surfaces.

Figure 53: How to create a cutting surface

Figure 54: The cutting plane does not intersect across the whole surface.

When the planar surface is shown with the ILT geometry, notice that some of the ILT surface doesnt reach the end plane, meaning a cut will still not create a planar surface yet. There are 2 solutions to this. The first is to bring the end plane in a little using OffsetSrf. The second is to extend the ILT surface using the ExtendSrf command. The second is preferred because as it doesnt change the overall length of the model. Once one of these options is performed, the surfaces can be cut (BooleanDifference or Split commands) to produce 76

planar ends, and then can be easily capped to produce the ILT body. If the Cap command is having difficulties, PlanarSrf combined with Join works well also. The only remaining body is the artery wall. While the drawn in slices are meant to represent the outer boundary of the artery wall, there is also a desire to prevent stress concentrations caused by non-existent thin spots in the artery wall. Therefore, the wall is not assumed to end at surface B, but instead at a uniform 1.8mm from the outer boundary of the combined blood and ILT domains. Anywhere that the ILT exists, a 1.8mm offset will be equivalent to using surface B because the outer boundary of the ILT is defined to be a 1.8mm offset of surface B. However, in areas where the fluid domain was closer than 1.8 mm to the defined outer wall boundary (places with no ILT) wall material is added until the wall is exactly 1.8mm. In order to create the wall domain in Rhino, create a new layer (surface D). As done with the ILT, copy surface A and surface C into this layer and split each surface. In this case, however, do not delete the spots where the fluid domain overtakes the ILT and instead delete the inner most surface. In most places, this should be surface A, but in the small patches where surface A is outside of surface C, it will become surface C. Once this is done, join all the surfaces together to get the inner boundary of the artery wall.
Figure 55: The cutting planes.

77

Figure 56: The finished ILT domain (left) and surface D (right). Both are built from surface A and surface C but with different parts of each.

Since the artery wall is defined as being 1.8mm thick, the outer boundary is found by offsetting surface D 1.8mm outward. This is done in its own layer (surface E). When the poly surface is offset, it will be exploded into each component surface and then those components will be offset individually. The result is a collection of surfaces that all have a small amount of

78

overlap that must be removed before they can be rejoined. Split each overlapping surface with the other and delete the extra length. This must be done for both surfaces at each overlap.

Figure 57: The surfaces will initially overlap (A). Both surfaces will need to be split so that the overlap from the buldge (B) and from the main surface (C) can both be deleted. Once this is done, the 2 surfaces should easily join (D).

If youre having difficulties getting the surfaces to split, use the intersect command to make sure there is a closed intersection curve dividing each surface in two. In the example above there wasnt originally a closed intersection and the main surface was extended to close it. In

79

some cases the surfaces will fail to split when the intersection curve is closed. This almost always occurs when dealing with polysurfaces and can be worked out by exploding the polysurface in surfaces and using the closed intersection line to split each surface individually. If all else fails and there are still difficulties with combining the surfaces, the JoinEdge command can be used. This command is somewhat of a built in cheat which can cause problems later on, because Rhino will still not have a good understanding of the geometry, even though its closed.

Figure 58: Initially these surfaces did not divide themselves into 2 parts. The intersection curve was not closed (A). The ExtendSrf command was used on the main surface to create a closed intersection curve.

Once the outer surface is completely joined together into one polysurface assembly of the arterial wall solid body can begin. Create a new layer (wall domain) and copy surface D and surface E into it. Trim the ends of the surfaces as was done with the ILT (the same cutting planes can be used) and cap it to generate a solid model of your surface. At this point, the whole CAD model should exist in Rhino. The next step is exporting the data out of Rhino and into ADINA. ADINA can accept either IGIS or parasolid files. Between these 2 choices of file format, parasolid 80

is by far a superior format. However, the parasolid compiler in Rhino is problamatic and often produces invalid parasolid files. To work around this, export the geometry (File>Export Selected) from Rhino in two seperate step files (.stp) one with the blood domain, and another with the ILT and artery wall domains. Then open each file in SolidWorks and immediately save them (just use save as) again as a parasolid file (.x_t).

81

Appendix C: Extracting Flow Rate Data from Velocimetry MRI Images


In order to extract the flow rate data from the MRI velocimetry images, returning to Matlab. All the programs required to perform this task are in the folder D:\mark\Aortic Wall Imaging\Matlab Programs\clean-up\Experimental Data. Also in this folder is a script called BATCH_FILE.m. As before, this is simply a list of commands to run through in order to extract the flow rate information from the images. They can be run sequentially in the command window, or the script itself can be executed and will walk the user through the process. The process begins with reading the velocimetry images at the inlet in the Z-direction (the out-of plane direction) into Matlab. As mentioned earlier, the velocity scaling information is buried in a string SequenceName located in the Dicom file information. This scalar is extracted from all the images, to ensure there are no problems with getting this data out of the images later and to ensure the correct images were selected. Then, the same cropping GUI that was used with the anatomical data is used again to crop down the images around the area of interest. In this case, the cropping is only used to shrink the images so that the region of interest can be magnified.

82

Figure 59: A typical velocimetry image (left) and an image with a well defined lumen (right)

In Figure 59 notice that the artery geometry can only be resolved when the blood velocity is high enough to get the lumen out of the noise level of the image. During the cropping operation, make sure to note the slice number of a slice with an easily definable lumen. The next step is to select the area that should be averaged over. The user will be prompted to enter the slice number of a slice with a definable lumen cavity, and then draw an ellipse representing the lumen area on that slice. This ellipse defines the area over which the flow rate will be measured. When happy with the ROI, hit enter to move on. The flow rate is calculated by multiplying the velocity value of each pixel within the ROI by the pixel area, to get the flow rate of that specific pixel, and then summing these pixel scale flow rate values for all the pixels in the ROI. During this process, the area of the
Figure 60: Example region of interest (ROI)

83

ROI is also calculated. Despite the integration of data over the area, the noise level of the resulting curve can be quite high. A running average of the flow rate is then taken to help eliminate noise. It is typically over 5 points, but can easily be changed if necessary. While averaging the data does attenuate the large pulsitile spike in flow rate, it also eliminates a lot of noise and has proven much more stable in the model. To prevent the averaging from introducing phase shift, the running averaged is taken with the pixel being averaged in the center of its neighborhood. This process is then repeated for the downstream boundary. After a flow rate has been calculated for the upstream and downstream boundaries they must be normalized to one another. Since these are separate experimental data sets, they are not guaranteed to have the same period or flow rate/cycle. In reality these values must be the same for both sets or else the artery will steadily inflate or deflate over many cycles as more blood is allowed to enter than exits or vice versa. To this end, the user is asked to input the area of the model inlets and outlets. Since the flow rate is defined over the ROI selected in the image, any difference in area between the model opening and the ROI will affect the measured flow rate. To track this error, the difference in area between the given model opening, and the selected ROI is reported. At the inlet, a velocity is defined which provides the measured flow rate over the given model inlet area. The outlet flow rate is then, first normalized to the period of the inlet flow rate. This is done simply by multiplying the outlet time values by the ratio of the inlet period to the outlet period. The ratio multiplier is displayed so the user knows how much the data is being manipulated. But the difference is usually less than 10%. The outlet flow rate is then normalized to the inlet flow rate too. Both the inlet and outlet flow rates are integrated 84

over their cycle to determine a flow per cycle. The ratio of these flows is then used to scale the outlet flow data in the same way the outlet time data was scaled. This flow rate modifier quantity is again displayed for the user, and is often closer to 20%. Once the period and flow per cycle of the outlet data has been set equal to those of the inlet data, these data sets can be applied to a model without integrating error over each cycle modeled. While there are concerns over manipulating the experimental data so much, it is necessary and anatomically correct for these 2 data sets to have identical period and, assuming there are no other outlet arteries, identical flow per cycle.

Experimental Flow Rate Data 50

Flow Rate in Z-direction (cm 3/s)

-50

-100

-150

inlet (raw) inlet (processed) outlet (raw) outlet (processed)

-200

0.1

0.2

0.3

0.4

0.5 0.6 Trigger Time (s)

0.7

0.8

0.9

Figure 61: Inlet and outlet flow rate data both before and after processing.

85

Appendix D: Putting it All Together in The Finite Element Model


Once both the experimental data and the CAD model have been generated, the mechanical model can be constructed. The mechanical model is generated using finite elements in a software package called ADINA. ADINA models the fluid and solid domains separately, but then will solve them together in a fluid-structure interaction solver. The list below will walk the user through generating the models in ADINA.
Solid Domain 1) Set Environment - ADINA Structures - Dynamics-Implicit - FSI

Control>Degrees of Freedom turn off rotational DOFs Control>Analysis Assumptions>Kinematics large strains large stresses

2) Import Wall and ILT geometry 3) "facelink <identification number> <body 1> <face 1> <body 2> <face 2>"

ex: facelink 1 1 3 2 6

86

facelink 2 1 6 2 3

If that doesnt work, here are some other techniques Rigid Links After everything is meshed create a node set of the slave surface without the nodes on the end (slave nodes cannot have constraints applied to them)

Meshing>Nodes>Node Set Node Set 1 from geometry line/edge Select all edges on the slave body (ILT) that are both linked with the master body (Wall) and on a surface with boundary conditions (the ends)

Node Set 2 from geometry surface/face Select all faces on the slave body that are linked with the master body

Node Set 3 subtract sets Subtract node set 1 from node set 2

Node Set 4 from geometry surface/face Select all faces on the master body that contact the slave body

87

Model>Constraints>Rigid Links Make Node Set 3 (ILT) slave and Node Set 4 (wall) the master

Rigid Link (nodes) The program MatchNodeSets_RigidLinks.m written by Francis Sheer can be used to identify a series of nodes to be connected to the closest node of in another set of nodes. The paired data is then exported as a text file to be imported into ADINAs Rigid Link (nodes) options

Glue Mesh Create the 2 meshes, but ensure that Coincident checking is turned off for both meshes. Then utilize the Glue Mesh command to fix the 2 surfaces to each other.

4) Define Materials Material 1 - Artery Wall Mooney-Rivlin Density = 1000 Bulk Modulus = 174000000 C1=174000 C3=1881000 Material 2 - ILT Elastic Isotropic Density = 1000

88

E=0.11e6 nu=0.49

5) Define Element Groups EG 1 - 3D Solid Default Material = 1 EG 2 - 3D Solid Default Material = 2

6) Define Skew System Model>Skew Systems>Define Normal System # 1

Model>Skew System>Apply Faces Select End Faces Select Skew System 1 Set Normal Direction to Aligned with Axis A In a skew system, the ABC directions replace XYZ, so this is equivalent to setting X to the normal direction

7) Apply BCs Fix All DOFs at upper faces Define FIX_NORM as fixing X-TRANSLATION

89

Apply FIX_NORM at lower faces Model>BCs>FSI BC

8) Generate Mesh Subdivide Bodies length ~0.002 Create Mesh>Body 4 element nodes

Fluid Domain 1) Set Environment - ADINA CFD - Transient - FSI - Incompressible

FSI Options FSI Solution Coupling = direct Maximum Number of FSI Iterations = 25 Analysis Options Automatic Time-Stepping = On Maximum Subdivisions Allowed = 10 Controlled By = Subdivision Procedure

90

Model>Flow Assumptions Include Heat Transfer = Off Flow Model = Laminar Control>Solution Process FCBI Elements = Yes Control>Time Steps
Table 3: Suggested time stepping scheme

Number of Steps 10 1 1 4 (Period of Cardiac Cycle)*2/0.01

Step Size 0.0001 0.039 0.16 0.2 0.01

2) Import Geometry 3) Define Material Material 1 - Incompressible Constant Viscosity = 3.5e-3 Density = 1035

4) Define Element Group EG 1 - 3D fluid

5) Apply Fixities

91

Model>Special Boundary Conditions Condition 1 - Fluid-Structure Interface

Import Inflow Velocity to Time Function 1 Import Pressure to Time Function 2 (if using optimization routine, just put filler) Apply Velocity BC Define Load as 1 in the Z direction Apply load to inlet face with time function 1 Apply Nodal Pressure BC Define Load as 1 Apply load to outlet face with time function 2

6) Generate Mesh Subdivide Bodies length ~0.002 Meshing>Create Mesh>Body

Applying a facelink to the solid geometry rarely works, but is a better and simpler way of combining the 2 solid bodies. The solid materials are assumed to be as dense as water. This is due to a lack of data regarding arterial wall tissue densities. The mesh density is a suggestion based on convergence alone. A mesh convergence study is still required in order to validate the model results. The time steps are also only a suggestion and might need to be varied slightly between models. Typically, the first second is designated as ramp-up, and I suggest this be kept as it is hardwired into much of the analysis code. After the first step, the model should run for at

92

least 2 complete cycles to avoid unwanted transient effects in the first cycle. Since each patient will have a different cardiac cycle period, each model will need to be run a different amount of time to complete 2 full cycles. This can be done by varying the number of time steps or the length of the time steps. The example shows varying the number of time steps, but either way is acceptable.

93

Appendix E: The Optimization Routine and Running A Model


Once the mechanical model is complete, the last step is setting up an optimization routine that uses the finite element model for function evaluation. This routine is charged with finding the impedance condition parameters for which the model outlet flow rate has the best agreement with the measured velocimetry outlet flow rate. Since ADINA has no built-in optimization abilities, a process had to be constructed to systematically select an impedance condition, incorporate that condition into the ADINA models, run the models to completion, and then extract data from the models and evaluate the level of agreement between the flow rate data. The first tendency to construct such a process, to perform the same task repeatedly between programs, would be a batch file. Batch files are lists of commands meant to be run like a program in the operating system. The problem with using a batch file in this case is the length of time over which each step can take. For example, the program cannot attempt to extract data until after the model has run to completion. While there are ways in batch file programming to ask the execution of the file to wait until the above commands are complete, problems arise due to the construction of the ADINA executable files. ADINA, like many other programs for Windows, is constructed such that the executable file (the file used to begin an instance of the program) is only a launcher program, and closes quickly after opening the files which will actually remain open. This prevents batch files from easily determining if the file is still running, because it only knows to monitor the launch executable, which quickly closes. 94

To work around this problem, a Visual Basic Program was developed (ADINAwithMATLAB2). This program automates the whole optimization routine by timing the running of both Matlab and ADINA off of each other. Before running ADINAwithMATLAB2, it is important that a Matlab structure variable be saved with the fields below that also matches with the data asked for in the programs GUI.
Table 4: Required fields for settings structure

Field Name experimental_t

Variable Type nx1 vector of doubles

Description Time component of experimental outlet flow rate data (sec)

experimental_outflow nx1 vector of doubles

Flow rate component of experimental outlet flow rate data (m3/s)

diastolic

double

Diastolic brachial blood pressure (mmHg)

x0

4x1 vector of doubles

The initial impedance condition parameters. The design vector, x is defined as x=[C; L; R1; R2], where each parameter is as referenced in Figure 22.

Continued

95

Table 4 continued

Field Name waveform_cycles

Variable Type double

Description Determines the number of cardiac cycles which will be read into the ADINA model before it is run

outletfacenumber

double

The number of the face that represents to outlet in the ADINA fluid model

IDBfile

string

The full path of the fluid internal database (.idb) file

fluidDATfile

string

The full path of the fluid database (.dat) file

solidDATfile

string

The full path of the solid database (.dat) file

INfile

string

The full path of the input (.in) file generated by Matlab to change the pressure function being applied to the model

Continued

96

Table 4 continued

Field Name PLOfile

Variable Type string

Description The full path of the plot (.plo) file generated by Matlab to extract the outlet flow rate from the model

PORfile

string

The full path of the portal (.por) file that contains data for the solved model.

log_file

string

The full path of the function evaluation log file. This file is generated by Matlab and is used to store function evaluation quantities each time a model is completed. Each row is an evaluation. The first 4 numbers are the x vector quantities and the last number is the function quantity.

FlowrateFile

string

The full path of the ADINA output file containing the outlet flow rate data for a completed model.

97

On top of this data all being loaded into a structure called settings and saved as a .mat Matlab workspace file, The fields in the VB script must also be completed. These values are explained below.
Table 5: Required fields for ADINAwithMATLAB2

Text Box Title Matlab Active Directory

Description Full path of the directory containing all the Matlab code that will be run including the .mat file with the settings structure

Matlab Command

Whatever you would like to run in Matlab. Can be used exactly as the Matlab Command Console. Typically contains Optimize(MATFILE) where MATFILE is the name of the .mat file containing the settings structure

ADINA AUI executable file

Fill path of ADINA AUI executable. Typically can be left as default value.

ADINA FSI Solver executable

Fill path of ADINA FSI solver executable. Typically can be left as default value.

ADINA IN file

The full path of the input (.in) file generated by Matlab to change the pressure function being applied to the model. Must match .mat file.

Continued

98

Table 5 continued

Text Box Title ADINA PLO file

Description The full path of the plot (.plo) file generated by Matlab to extract the outlet flow rate from the model. Must match .mat file.

Fluid DAT file

The full path of the fluid database (.dat) file. Must match .mat file

OUT file Solid DAT file

The full path of the fluid output (.out) file. The full path of the solid database (.dat) file. Must match .mat file

Preprocessing Wait Time

Number of minutes to wait after initiating the IN file before proceeding.

Processing Wait Time

Number of minutes the OUT file must remain unchanged before proceeding

# of Processors

Number of processors the FSI solver is allowed to allocate.

Mem Usage

Amount of memory the FSI solver is allowed to allocate

Postprocessing Wait Time

Number of minutes to wait after initiating the PLO file before proceeding.

99

When executed, ADINAwithMATLAB2 runs as follows. 1. Opens Matlab. Changes the active directory as specified. Then executes any commands in the Matlab command text box. Automatically detects when Matlab closes. Once Matlab closes, it proceeds to the next step. The optimization Matlab script ends with a quit command unless the model has converged, causing the program to proceed as soon as Matlab has completed its commands. a. During the running of Optimize in Matlab, the optimization routine will be run from the initial point x0. Function values will be returned from the log file until one of two things happens the model converges, or a function value is requested which is not given in the log file. When the model converges, Matlab will remain open, stopping the routine from running, with a message declaring the model converged. If a function value is requested for which there is no known solution in the log file, Matlab will generate 2 text files the IN file, and the PLO file. The IN file opens the fluid IDB model file and changes the pressure condition. It then generates a new DAT file, and closes the AUI. The PLO file opens the fluid POR file of a solved model, and outputs the flow rate across the face specified by outletfacenumber. 2. Once Matlab has closed, the VB program will instantly open the AUI and execute the IN file that Matlab just generated. Since the VB script cannot determine when ADINA has completed running the IN file, it will wait for a designated amount of time before doing anything else. During this time, the interface of the VB program will appear crashed. 100

This is because the program was not multi-threaded and does not mean the program has crashed. 3. After waiting the prescribed amount of preprocessing time the VB program will open the ADINA FSI solver and begin solving the solid and fluid DAT files. It will do nothing but monitor the size of the fluid OUT file every minute until it has not changed in file size for the prescribed amount of processing time. 4. Once the OUT file has stopped growing for a time, the VB program will again open the AUI, this time running the PLO file. It will again wait the specified postprocessing time in order to give the PLO file time to run its course. During this time, the user interface of the VB script will not refresh and appear crashed, as during the preprocessing. 5. After the OUT file has been run, Matlab will be opened as before, restarting the cycle. The first thing Matlab attempts to do is read the FlowrateFile exported during the PLO files run, and add that entry to the function evaluation log file. That way, when the Optimization routine starts again, it will proceed a step further before encountering another unknown function value.

Obviously problems will be encountered if any of the data fields in the settings Matlab structure variable or the ADINAwithMATLAB2 program fields is incorrect. Another problem area can be the 3 waiting times that must be specified. If any of these times are too short, steps will be taken before appropriate. For example, the model could begin to solve before the pressure condition has been changed. In this case, it will result in incorrect function evaluation values. In 101

other cases, such as if it believes the solver has completed before it has, the process will most likely incorrectly work itself all the way around to opening the solver again, and then will error out because the DAT files it wishes to access are already in use. In most cases, once the program is stopped, errors can be easily corrected by deleting the FlowrateFile, to ensure no extra data is incorrectly read in, and deleting the unwanted or incorrect function evaluation lines from the log file.

102

Appendix F: Matlab Script Descriptions


Table 6 is a brief description of all the Matlab scripts utilized in this process. Some of these files are used during the process, while others are sometimes helpful for debugging or otherwise visualizing the data.
Table 6: Matlab Script Summary

Name AdinaTimeVar2Matlab.m

Location D:\mark\Aortic Wall Imaging\Matlab Programs\clean-up\Model Calibration Procedure

Description Opens ADINA exported variable files and exports the data within, including the title of each variable extracted Checks the function evaluation log file for a requested impedance vector x. If the value is listed it returns that the value was found and what the value is.

CheckLog

D:\mark\Aortic Wall Imaging\Matlab Programs\clean-up\Model Calibration Procedure

Continued

103

Table 6 continued

Name ExportWaveform

Location D:\mark\Aortic Wall Imaging\Matlab Programs\clean-up\Model Calibration Procedure

Description Writes the request impedance condition vector, x into the log file, without a function evaluation, and then generates an ADINA IN file which includes the correct pressure curve for the given impedance.

GetFunc

D:\mark\Aortic Wall Imaging\Matlab Programs\clean-up\Model Calibration Procedure

Checks the log file to see if a requested impedance condition has already been evaluated. If not, runs the ExportWaveform script and returns that no function evaluation exists. Can be set to penalize negative impedance parameters, thus creating a constrained optimization routine or can allow for negative parameters

Continued

104

Table 6 continued

Name ImportWaveforms

Location D:\mark\Aortic Wall Imaging\Matlab Programs\clean-up\Model Calibration Procedure

Description Reads the flow rate file data, and adds the appropriate functional evaluation to the end of the log file.

Optimize

D:\mark\Aortic Wall Imaging\Matlab Programs\clean-up\Model Calibration Procedure

Opens the .mat variable data with the settings structure within, and then begins the optimization routine. If the routine requires a function evaluation to proceed, the file prepares the VB script to run one then closes Matlab. Otherwise it runs until converged, and then displays a message to the user. The optimization procedure is also constructed to produce a convergence chart which is saved as Convergence_Chart.fig in the active Matlab directory.

Continued

105

Table 6 continued

Name BATCH_FILE

Location D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Anatomical Data

Description List of commands that walk through the anatomical image analysis procedure

Crop_GUI

D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Anatomical Data

GUI for defining the desired section of an image stack. Is identical to the one used in velocimetry.

DicomCrop

D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Anatomical Data

Crops data off of a Dicom image. In addition to altering the image, the information within the Dicom image must also be updated. Prompts the user to draw in polygons around a series of images in a stack. Used to define the outer boundary of the artery.

DicomDrawBoundary

D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Anatomical Data

Continued

106

Table 6 Continued

Name DicomMagnify

Location D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Anatomical Data

Description Interpolates more image points in a Dicom image. Also, updates the extra information in the Dicom image Exports a list of 3 dimensional data points from a black and white image, where each white pixel is defined as a point. GUI for handling the image processing of the anatomical lumen data

DicomPic2Pts

D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Anatomical Data

Edging_GUI

D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Anatomical Data

GetBody_GUI

D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Anatomical Data

Goes through a stack of black and white images, and prompt the user to select the white body which represents the aorta

Continued

107

Table 6 continued

Name GetDicomStack

Location D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Anatomical Data

Description Prompts the user to select a series of Dicom images to read in and then reads both the image and information in and sorts both by either height or time

Slice2Rhino

D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Anatomical Data

When given a series of 3D data points organized into slices, generates a Rhino command file to read those data points in as a series of spline curves

BATCH_FILE

D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Experimental Data

List of commands that walk through the velocimetry image analysis procedure

Crop_GUI

D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Experimental Data

Same as Crop_GUI for anatomical data

Continued

108

Table 6 continued

Name DicomCrop

Location D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Experimental Data

Description Same as DicomCrop for anatomical data

DisectSequenceName

D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Experimental Data

Removes the velocity scaling data buried within the SequenceName property of a Dicom stack

ExtractFlowrate

D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Experimental Data

Measures the flow rate through a region of interest defined by the user for a set of velocimetry images

GetDicomStack

D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Experimental Data

Same as GetDicomStack for anatomical data

Continued

109

Table 6 continued

Name Replicate

Location D:\mark\Aortic Wall Imaging\Matlab Programs\cleanup\Experimental Data

Description When given the data for 1 period of a periodic cycle, will repeat this data as many times as needed to generate data covering more than 1 period

SortDicom

D:\mark\Aortic Wall Imaging\Matlab Programs\clean-up\Other Nice Functions

Used to sort the series of raw Dicom images into folders for easier manipulation

Velocity

D:\mark\Aortic Wall Imaging\Matlab Programs\clean-up\Other Nice Functions

Can read in all the velocimetry data provided (including the XYZ directions) and generate a vector field of data that can be viewed in Tecplot

Continued

110

Table 6 continued

Name DicomGetAbsLocation

Location D:\mark\Aortic Wall Imaging\Matlab Programs\clean-up\Other Nice Functions

Description Reads one image and allows the user to select one pixel in that image. Then outputs the global XYZ coordinates of the pixel. Tries to define a 3D body from only 1 selected point. While sound, sometimes screws up and the benefit was deemed usually not worth the hassle.

bw3select

D:\mark\Aortic Wall Imaging\Matlab Programs\clean-up\Other Nice Functions

medfilt3

C:\Documents and Settings\Mark\My Documents\MATLAB

Used to implement 3D median filtering on images. Written by Damien Garcia. Very cool program. Allows the user to visualize 3D image stacks in Matlab. Written by Dirk-Jan Kroon

viewer3d

C:\Documents and Settings\Mark\My Documents\MATLAB

111

Appendix G: Visual Basic Script Descriptions


These Visual Basic Programs were written on a 32-bit computer in Microsoft Visual Basic 2010 Express. Each project is on the Data_Transfer drive located on the Seagate BlackArmor network drive. They are located in the folder Visual Basic Programs. To install any of these programs, go to the publish folder within the specific project and use the setup.exe file. To uninstall any of these programs from your computer use Add or Remove Programs located in the Windows Control Panel.

Table 7 is a list and brief description of the Visual Basic scripts.

112

Table 7: Visual Basic Script Summary

Name ADINA Batch

Description Allows multiple ADINA models to be run sequentially. Once the list is completed, the user can choose to be emailed. Current only written to support the FSI solver, but is setup for more to be easily added. Monitors models by detecting changes in the size of the fluid out file.

ADINAwithMATLAB

Used during the optimization routine. Runs commands in Matlab, then executes an IN file in the ADINA AUI. Waits a designated amount of preprocessing time, before then starting the ADINA FSI solver. Monitors the fluid .out file size, until it stops growing, and then executes a PLO file in the ADINA AUI before returning to the beginning of the cycle and running Matlab again. It continues this cycle indefinitely.

Console Model Notifier

Sends an email when the selected file stops changing size. Must be run in the command console, and requires mailsend.exe to be placed in the computers path. This program has been dated by the GUI version and could probably be avoided.

GUI Model Notifier

Sends an email when the selected file stops changing size.

113

Appendix H: Utilizing the Ohio Supercomputing Center


The Ohio Supercomputing Center (OSC) has the potential to significantly improve the throughput of this project by providing a significant number of additional computational resources. Hopefully these resources will not only reduce the amount of time required to run each model to convergence, but also allow for many more models to be run simultaneously. First, an account must be made for the user. If ADINA is to be used on the OSC, the user must be list under Dr. Ghadialis account because the ADINA license is in his name. Connecting to the OSC can be done through an SSH client such as "Putty". Once connected to the OSC, the interface is a text only UNIX command console. A text editor will be necessary to view and edit any text files on the system. Vi is a common choice that seems to work very well once you get the hang of it. Another important function will be transferring files back and forth between the workstation and the OSC account. Pscp is a command line function that is supported by Putty and can be downloaded from their web site, which can be used to transfer files both from and to the OSC account. To utilize multiple processors, jobs must be submitted in batch mode. The OSC website has some information regarding how to use their batch file software. In order to run ADINA on the OSC computer, it must be transferred over using pscp and then installed. Once installed use lsmon to specify the license server. >cd $ADINA/slm >./lsmon license2.osce.edu The files to run ADINA are then located at $ADINA/tools. The OSC does not support ADINA and are providing the ADINA license server for us as a courtesy. Dont expect large amounts of help from the OSC with regards to ADINA, but any questions regarding ADINA at the OSC should be directed to Sid Samsi, who was kind enough to set up the license server. Also, .dat files created in a windows version of ADINA cannot be run directly on the Linux version of ADINA located at the OSC. In order to run models at the OSC, .in files should be created on a windows system with a graphical interface and then a .idb file can be generated from the .in file on the OSC system. Using the linux version of the .idb file, will allow you to generate proper .dat files to be run. The optimization software should should not be affected by any of these changes.

114

Appendix I: Helpful Contacts


Dr. Samir Ghadiali Department of Biomedical Engineering Davis Heart and Lung Research Institute Department of Mechanical Engineering Ghadiali.1@osu.edu Columbus, OH Columbus, OH Columbus, OH

The Ohio State University The Ohio State University The Ohio State University

Project coordinator, Modeling expert

Dr. Orlando Simonetti Davis Heart and Lung Research Institute Department of Internal Medicine and Radiology

The Ohio State University The Ohio State University

Simonetti.9@osu.edu Columbus, OH Columbus, OH

Project coordinator, Medical imaging expert

Dr. Sanjay Rajagopalan Davis Heart and Lung Research Institute Department of Internal Medicine and Radiology Surgeon, AAA expert

The Ohio State University The Ohio State University

Sanjay.Rajagopalan@osumc.edu Columbus, OH Columbus, OH

Dr. Georgeta Mihai Davis Heart and Lung Research Institute

The Ohio State University

Simonetti.9@osu.edu Columbus, OH

Medical imaging expert, Clinical liaison

ADINA Troubleshooting ADINA modeling help. They usually respond within a day. 115

Support@adina.com

OSC Troubleshooting

oschelp@osc.edu

For help using the OSC. Any questions regarding running ADINA on the OSC should be directed to Sid Samsi, but still through the above email.

116

Appendix J: Matlab Code

AdinaTimeVar2Matlab
function [data,Variables]=AdinaTimeVar2Matlab(data_name) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% % % Converts the text file exported from ADINA containing variable data % versus time (in which there is only one value per time) into Matlab % variables % % data_name = path of the ADINA exported text file % % data = cell array of variable values. Each cell is a different variable % Variables = the name of the cell arrays in order %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% if nargin~=1 %Get file paths [data_name, data_path]=uigetfile('*.txt','Select Data File'); data_name=[data_path data_name]; end %Read in variable data file_str=fileread(data_name); %Extract variable names pos_vars=regexp(file_str,'TIME','once'); pos_endvars=pos_vars+regexp(file_str(pos_vars:length(file_str)),'\n','o nce'); var_str=file_str(pos_vars:pos_endvars); i=1; done=0; while done~=1 try done=1; var(i)=textscan(var_str,'%s %*[^\n]'); pos_vars=regexp(var_str,var{i},'once'); var_str=var_str(pos_vars{1}+length(var{i}{1}):length(var_str)); i=i+1;

117

done=0; end end for i=1:length(var)-1 Variables{i}=var{i}{1}; end %Read in variable data data_format='%f'; for i=2:length(Variables) data_format=[data_format ' %f']; end pos_data=pos_endvars; pos_enddata=pos_data + regexp(file_str(pos_data:length(file_str)),'***','once')-1; data=textscan(file_str(pos_data:pos_enddata),data_format);

118

CheckLog
function [exists,func]=CheckLog(x,settings) %checks the logfile for the given "x" and returns whether it exists and its %value try log=csvread(settings.log_file,0,0); catch button=questdlg('Log File Does Not Exist. Would you like to create one?'); if strcmp(button,'Yes'); fid=fopen(settings.log_file,'w'); fclose(fid); end exists=0; func=NaN; return end %search for entry containing the desired x value results=log; for i=1:length(x) if isempty(results) break; end search_id=find(results(:,i)==x(i)); results=results(search_id,:); end %make sure only 1 entry is found if size(results(:,1))>1 warning('Non-unique X values found in the log'); end %return values if isempty(results) exists=0; func=NaN; else exists=1; func=results(length(x)+1); end

119

ExportWaveform
function ExportWaveform(x,settings) %exports a text file ready to import into adina %read log file and determine next entry number try log=csvread(settings.log_file,0,0); entry_num=size(log,1)+1; catch entry_num=1; end

%set up windkessel model %-----------------4 Element Windkessel Model------------------C=x(1); L=x(2); R1=x(3); R2=x(4); num=[R1*R2*L*C (R1+R2)*L R1*R2]; den=[R2*L*C R1*R2*C+L R1]; TransFunc=tf(-1*num,den); omega_n=sqrt(R1/(R2*L*C)); zeta=(R2*C+L/R1)/2*omega_n; t_steadystate = 4.6/(zeta*omega_n); % (+/-)1% error %-------------------------------------------------------------%% convert flowrate to pressure t=settings.experimental_t; q=settings.experimental_outflow; %make the sampling uniformly spaced pp=spline(t,q); t_resampled=linspace(min(t),max(t),length(t))'; q_resampled=ppval(pp,t_resampled); %ensure sampling reaches steady state t_rep=t_resampled; q_rep=q_resampled; while max(t_rep)<t_steadystate t_rep=[t_rep; t_resampled(2:length(t_resampled))+max(t_rep)]; q_rep=[q_rep; q_resampled(2:length(q_resampled))]; end

120

t_rep=[t_rep; t_resampled(2:length(t_resampled))+max(t_rep)]; q_rep=[q_rep; q_resampled(2:length(q_resampled))]; %calculate pressure [p_rep,t_rep]=lsim(TransFunc,q_rep,t_rep); p_rep=p_rep+settings.diastolic*133.322368; %------------For Checking Steady State Response-----------------%figure; %plot(t_rep,p_rep) %---------------------------------------------------------------%extract non-transient response data p_nontransient=p_rep(length(p_rep)-length(t)+1:length(p_rep)); %extract desired amount of cycles t=t_resampled; p=p_nontransient; for i=2:settings.waveform_cycles t=[t; t_resampled(2:length(t_resampled))+max(t)]; p=[p; p_nontransient(2:length(p_nontransient))]; end %add rampup t=[0; t+1]; p=[0; p]; %% Write Output Files %ADINA IN file, used to modify the model delete(settings.INfile); fid=fopen(settings.INfile,'w'); fprintf(fid,'%s \r\n','FEPROGRAM ADINA-F'); fprintf(fid,'%s%s%s \r\n','DATABASE OPEN ''', settings.IDBfile, ''' PROMPT=NO'); fprintf(fid,'%s \r\n','TIMEFUNCTION NAME=2 IFLIB=1'); for i=1:length(t) fprintf(fid,'\t %10.20e \t %10.20e \r\n',t(i),p(i)); end fprintf(fid,'\t %s \r\n','DATAEND'); fprintf(fid,'%s%s%s \r\n','ADINA-F FILE=''',settings.fluidDATfile,''' OVERWRITE=YES'); fprintf(fid,'%s \r\n','EXIT IMMEDIATE=YES'); fclose(fid); %ADINA PLO file, used to postprocess the needed data delete(settings.PLOfile) fid=fopen(settings.PLOfile,'w');

121

fprintf(fid,'%s%s%s \r\n','DATABASE OPEN ''', settings.IDBfile, ''' PROMPT=NO'); fprintf(fid,'%s%s%s \r\n','LOADPORTHOLE OPERATION=CREATE FILE=''',settings.PORfile,'''') fprintf(fid,'%s \r\n','ELFACESET 1 OPTION=SURFACE-FA'); fprintf(fid,'\t %s \t %s \r\n',num2str(settings.outletfacenumber),'1'); fprintf(fid,'%s \r\n','FRAME'); fprintf(fid,'%s \r\n','MESHPLOT ELFACESET=1'); fprintf(fid,'%s \r\n','MESHINTEGRATION NAME=OUTLET'); fprintf(fid,'%s%s%s \r\n','FILELIST OPTION=FILE FILE=''',settings.FlowrateFile,''''); fprintf(fid,'%s \r\n','POINTLIST POINTNAME=OUTLET VAR=VOLUME_FLUX_SURFACE'); fprintf(fid,'%s \r\n','END IMMEDIATE=YES'); fclose(fid); %write x to log file log(entry_num,1:length(x))=x'; dlmwrite(settings.log_file,log,'delimiter',',', 'precision','%10.20e');

122

GetFunc
function [exists,f]=GetFunc(x,settings) %checks if the function has already been evaluated at x. If not, it adds it %to the que non_negative='enforced'; if strcmp(non_negative,'enforced') && min(x) < 0 exists=1; f=1e4; else [exists,f]=CheckLog(x,settings); end if ~exists ExportWaveform(x,settings); end

123

ImportWaveforms
function ImportWaveforms(settings) %reads the data in the flowrate file and calcuates a function evaluation %which is then added to the log file if exist(settings.FlowrateFile) t=settings.experimental_t; q=settings.experimental_outflow; pp=spline(t,q); %calculate the error with respect to fitting the curves input=AdinaTimeVar2Matlab(settings.FlowrateFile); %ignore model ramp-up a=find(input{1}==1); input{1}=input{1}(a:length(input{1}))-1; input{2}=input{2}(a:length(input{2})); error=(input{2}-ppval(pp,input{1})).^2; tot_error=trapz(input{1},error); %write error values to log file log=csvread(settings.log_file,0,0); entry_num=size(log,1); log(entry_num,length(settings.x0)+1)=tot_error; dlmwrite(settings.log_file,log,'delimiter',',', 'precision','%10.20e'); end

124

Optimize
% settings.experimental_t % settings.experimental_outflow % settings.log_file='F:\Mark\Optimization_Testing\Matlab_Toolbox\logfile. txt'; % settings.INfile=''; % settings.PLOfile=''; % settings.IDBfile=''; % settings.fluidDATfile=''; % settings.solidDATfile=''; % settings.FlowrateFile=''; % settings.PORfile=''; %TODO: define PORfile off of fluidDATfile % settings.diastolic=80; % settings.waveform_cycles=4; % settings.x0=[9.1508e-3; 679.9442e3; 7.4661e6; 105.3247e6]; %JUST FOR REFERENCE % x=[C L R1 R2]'

function Optimize(settings_path) warning('off', 'all'); history.f=[]; history.evals=[]; history.iter=[]; open(settings_path); settings=ans.settings; ImportWaveforms(settings) options=optimset('Display','iter','OutputFcn', @PlotFunc,'TolX',1e-20); [x,fval,exitflag,output] = fminsearch(@Function_Call,settings.x0,options) %options=optimset('LargeScale','off','Display','iter','OutputFcn',@Plot Func,'TolFun',1e-5,'TolX',1e-5,'TypicalX',settings.x0); %

125

%[x,fval,exitflag,output,grad,hessian]=fminunc(@Function_Call,settings. x0,options) save settings_path msgbox('Model Converged!') function stop=PlotFunc(x, optimValues, state, a) stop=false; if ~strcmp(state,'init') history.f=[history.f; optimValues.fval*(1000)^3/2]; history.iter=[history.iter; optimValues.iteration]; h=plot(history.iter,history.f); xlabel('Iteration'); ylabel('Error (mm^3/cycle)'); hold off title(settings.log_file); saveas(h,'Convergance_Chart','fig'); end end function [f]=Function_Call(x) [exists,f]=GetFunc(x,settings); if ~exists || f==0 quit; end end end

126

BATCH_FILE (anatomical)
%This is the executable file for obtaining anatomical AAA data from a 3D %MRI Dicom images. %edge extraction default settings settings.size=9; %default size of median filter neighborhood (pixels) settings.mag=4; %image magnification factor settings.rad=1; %morphological closure default radius (pixels) %sampling settings slice_sampling=1; %how many (1=every slice, 2=every other outerwall_slices=8; %how many spline_order=6; %order of slice

slices should be sampled when exporting slice, 3=every third slice, ect.) outerwall slices should be calculated spline connecting the points in each

%Read in MRI data [data_raw, data_info]=GetDicomStack('*.*','Open MRI Dicom Images','D:\mark\Aortic Wall Imaging\data','height'); %Crop Images window=Crop_GUI(data_raw); [data_raw,data_info]=DicomCrop(data_raw,data_info,window); %Prefilter data_filt=medfilt3(data_raw,[3 3 3]); %Image Analysis % Enlarge / Filter / Edge / Close image [data_edge,settings]=Edging_GUI(data_filt,settings); [data_info_analyzed]=DicomMagnify(data_info,settings.mag); inv=abs(data_edge-1); data_body=GetBody_GUI(inv); % %Find point in fluid domain % int_pt=GetPoint_GUI(data_raw(:,:,1),'Select Center of Arterial Body'); % int_pt=MagnifyPixelPos(int_pt,settings.mag); % int_pt=round(int_pt); % % %Extract / Close body image and get Surface % h=waitbar(.5,'Extracting Body...');

127

% %

inv=abs(data_edge-1); data_body=bwselect3(inv,[int_pt 1]);

h=waitbar(.7,'Generating Surface...'); se=strel('disk',5); data_surf=zeros(size(data_body)); for i=1:length(data_body(1,1,:)) data_body(:,:,i)=bwmorph(data_body(:,:,i),'open',1); data_surf(:,:,i)=bwmorph(data_body(:,:,i),'remove'); end %Create Points %inner wall [x,y,z]=DicomPic2Pts(data_surf,data_info_analyzed); close(h); %outer wall [ow_x,ow_y,ow_z]=DicomDrawBoundary(imresize(data_raw,settings.mag,'near est'),data_surf,data_info_analyzed,outerwall_slices); %Exporting h=waitbar(0,'Exporting Data...'); [ex_file,ex_path]=uiputfile('*.txt','Save Output File As','D:\mark\Aortic Wall Imaging\data'); fname=[ex_path ex_file]; if exist(fname) delete(fname) end Slice2Rhino(x,y,z,spline_order,fname,'Fluid Domain',slice_sampling); Slice2Rhino(ow_x,ow_y,ow_z,spline_order,fname,'Outer Wall'); close(h) beep

128

Crop_GUI
function varargout = Crop_GUI(varargin) %Utility for Cropping down 3D MRI data stacks. % %Inputs: % varargin{1}=3d stack of image data % %Outputs: % varargout{1}=structure defining the cropped out region. % feilds: xmin, xmax, ymin, ymax, zmin, zmax % % %CROP_GUI M-file for Crop_GUI.fig % Begin initialization code - DO NOT EDIT gui_Singleton = 1; gui_State = struct('gui_Name', mfilename, ... 'gui_Singleton', gui_Singleton, ... 'gui_OpeningFcn', @Crop_GUI_OpeningFcn, ... 'gui_OutputFcn', @Crop_GUI_OutputFcn, ... 'gui_LayoutFcn', [] , ... 'gui_Callback', []); if nargin && ischar(varargin{1}) gui_State.gui_Callback = str2func(varargin{1}); end if nargout [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin[4]); else gui_mainfcn(gui_State, varargin[4]); end % End initialization code - DO NOT EDIT

function Crop_GUI_OpeningFcn(hObject, eventdata, handles, varargin) %load images handles.pics=varargin{1}; %scale sliders to image size set(handles.xslider1,'Min',1) set(handles.xslider1,'Max',length(handles.pics(1,:,1))) set(handles.xslider2,'Min',1) set(handles.xslider2,'Max',length(handles.pics(1,:,1))) set(handles.yslider1,'Min',1) set(handles.yslider1,'Max',length(handles.pics(:,1,1))-1)

129

set(handles.yslider2,'Min',1) set(handles.yslider2,'Max',length(handles.pics(:,1,1))-1) %set values to sliders handles.x1=get(handles.xslider1,'Max'); handles.x2=get(handles.xslider1,'Min'); handles.y1=get(handles.yslider1,'Min'); handles.y2=get(handles.yslider1,'Max'); set(handles.xslider1,'Value',handles.x1); set(handles.xslider2,'Value',handles.x2); set(handles.yslider1,'Value',handles.y1); set(handles.yslider2,'Value',handles.y2); %set values to listbox handles.list_length=length(handles.pics(1,1,:)); handles.list=cell(handles.list_length,1); for i=1:length(handles.pics(1,1,:)) handles.list{i}=['Slice ' num2str(i)]; end

handles.selected=1; set(handles.listbox1,'String',handles.list) set(handles.listbox1,'Value',handles.selected); %set cropping variables handles.cropx=[1 length(handles.pics(1,:,1))]; handles.cropy=[1 length(handles.pics(:,1,1))]; handles.cropz=[1 length(handles.pics(1,1,:))]; %initialize the plot set(hObject, 'WindowScrollWheelFcn', @(src,evnt)doScroll(evnt)); refresh_axes(handles)

% Update handles structure guidata(hObject, handles); uiwait(handles.figure1); function varargout = Crop_GUI_OutputFcn(hObject, eventdata, handles) % Get default command line output from handles structure window.xmin=handles.cropx(1); window.xmax=handles.cropx(2); window.ymin=handles.cropy(1); window.ymax=handles.cropy(2); window.zmin=handles.cropz(1); window.zmax=handles.cropz(2); varargout{1}=window;

130

close(hObject) function xslider1_Callback(hObject, eventdata, handles) handles.x1=get(handles.xslider1,'Value'); guidata(hObject, handles); refresh_axes(handles) function xslider1_CreateFcn(hObject, eventdata, handles) if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor',[.9 .9 .9]); end function xslider2_Callback(hObject, eventdata, handles) handles.x2=get(handles.xslider2,'Value'); guidata(hObject, handles); refresh_axes(handles) function xslider2_CreateFcn(hObject, eventdata, handles) if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor',[.9 .9 .9]); end function yslider1_Callback(hObject, eventdata, handles) handles.y1=get(handles.yslider1,'Value'); guidata(hObject, handles); refresh_axes(handles) function yslider1_CreateFcn(hObject, eventdata, handles) if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor',[.9 .9 .9]); end function yslider2_Callback(hObject, eventdata, handles) handles.y2=get(handles.yslider2,'Value'); guidata(hObject, handles); refresh_axes(handles) function yslider2_CreateFcn(hObject, eventdata, handles) if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor',[.9 .9 .9]); end function listbox1_Callback(hObject, eventdata, handles)

131

handles.selected=get(hObject,'Value'); guidata(hObject, handles); refresh_axes(handles) function listbox1_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end function button_remove_Callback(hObject, eventdata, handles) if (handles.selected ~= 1) && (handles.selected ~=handles.list_length) button = questdlg('This slice is located between 2 others. The distances between slices will be incorrect. Are you sure you want to continue?','WARNING'); if strcmp(button,'Yes')==0 return end end if handles.selected == 1 handles.list=handles.list(2:handles.list_length); handles.pics=handles.pics(:,:,2:handles.list_length); handles.cropz(1)=handles.cropz(1)+1; elseif handles.selected == handles.list_length handles.list=handles.list(1:handles.list_length-1); handles.selected=handles.selected-1; handles.pics=handles.pics(:,:,1:handles.list_length-1); handles.cropz(2)=handles.cropz(2)-1; else handles.list=[handles.list(1:handles.selected-1) handles.list(handles.selected+1:handles.list_length)]; handles.pics=cat(3, handles.pics(:,:,1:handles.selected-1), handles.pics(:,:,handles.selected+1:handles.list_length)); handles.cropz(2)=handles.cropz(2)-1; end handles.list_length=handles.list_length-1; set(handles.listbox1,'String',handles.list); set(handles.listbox1,'Value',handles.selected); guidata(gcf, handles); refresh_axes(handles) function button_crop_Callback(hObject, eventdata, handles)

132

if handles.x2>handles.x1 temp=handles.x1; handles.x1=handles.x2; handles.x2=temp; clear temp end if handles.y2<handles.y1 temp=handles.y1; handles.y1=handles.y2; handles.y2=temp; clear temp end %convert crop lines to integers ylength=length(handles.pics(:,1,1)); xlength=length(handles.pics(1,:,1)); handles.x1=round(handles.x1); handles.x2=round(handles.x2); handles.y1=round(handles.y1); handles.y2=round(handles.y2); handles.cropx(1)=handles.cropx(1)+handles.x2-1; handles.cropx(2)=handles.cropx(2)-(xlength-handles.x1); handles.cropy(1)=handles.cropy(1)+(ylength-handles.y2)-1; handles.cropy(2)=handles.cropy(2)-handles.y1; handles.pics=handles.pics((ylength-handles.y2):(ylengthhandles.y1),handles.x2:handles.x1,:); ylength=length(handles.pics(:,1,1)); xlength=length(handles.pics(1,:,1)); %rescale and reset sliders set(handles.xslider1,'Min',1) set(handles.xslider1,'Max',xlength) set(handles.xslider2,'Min',1) set(handles.xslider2,'Max',xlength) set(handles.yslider1,'Min',0) set(handles.yslider1,'Max',ylength-1) set(handles.yslider2,'Min',0) set(handles.yslider2,'Max',ylength-1) guidata(hObject, handles); handles.x1=get(handles.xslider1,'Max'); handles.x2=get(handles.xslider1,'Min'); handles.y1=get(handles.yslider1,'Min'); handles.y2=get(handles.yslider1,'Max'); set(handles.xslider1,'Value',handles.x1);

133

set(handles.xslider2,'Value',handles.x2); set(handles.yslider1,'Value',handles.y1); set(handles.yslider2,'Value',handles.y2); guidata(hObject, handles); refresh_axes(handles) function button_done_Callback(hObject, eventdata, handles) uiresume()

function refresh_axes(handles) %Used to show the lines moving as sliders are changed imshow(handles.pics(:,:,handles.selected),[]) hold on width=[1; get(handles.xslider1,'Max')]; height=[1; get(handles.yslider1,'Max')]; line(width, [height(2)-handles.y1; height(2)handles.y1],'Color','r'); line(width, [height(2)-handles.y2; height(2)handles.y2],'Color','r'); line([handles.x1; handles.x1], height,'Color','r'); line([handles.x2; handles.x2], height,'Color','r'); hold off

function doScroll(evnt) %Shifts through image slices when the mouse is scrolled handles=guidata(gcf); if (evnt.VerticalScrollCount>0) % scroll down for i=1:evnt.VerticalScrollCount if handles.selected+1 > handles.list_length handles.selected=1; else handles.selected=handles.selected+1; end end else % scroll up for i=1:abs(evnt.VerticalScrollCount) if handles.selected == 1 handles.selected=handles.list_length; else handles.selected=handles.selected-1; end

134

end end set(handles.listbox1,'Value',handles.selected); guidata(gcf, handles); refresh_axes(handles)

135

DicomCrop
function [new_data, new_info]=DicomCrop(data,info,win) %Crop down Dicom Images while maintaing the validity of their info file. % %Inputs: % % data - uncropped image stack % info - cell array where each cell i corresponds to image data(:,:,i) % win - structure defining a cropped window of pixels. % feilds: xmin, xmax, ymin, ymax, zmin, zmax % % %Outputs: % % new_data - cropped image stack % new_info - info array modified to match the cropped images new_data=data(win.ymin:win.ymax , win.xmin:win.xmax , win.zmin:win.zmax); new_info=cell((win.zmax - win.zmin)+1,1); for i=1:(win.zmax - win.zmin)+1 new_info{i}=info{win.zmin+i-1}; end for i=1:length(new_info) xscale=info{i}.PixelSpacing(2); yscale=info{i}.PixelSpacing(1); pos=info{i}.ImagePositionPatient;

%mm/pixel %mm/pixel %mm

xvect=info{i}.ImageOrientationPatient(1:3); yvect=info{i}.ImageOrientationPatient(4:6); zvect=cross(xvect,yvect); T=[xvect yvect zvect]; %transform from xyz to XYZ shift_xyz=[(win.xmin-1)*xscale; (-1)*(win.ymin-1)*yscale; 0]; shift_XYZ=T*shift_xyz; pos_new=pos+shift_XYZ; new_info{i}.ImagePositionPatient=[pos_new];

136

end

137

DicomDrawBoundary
function %Prompts Then %exports %array. %Exports [X,Y,Z]=DicomDrawBoundary(all_pic,all_surf,all_info,numslice) the user to draw in the artery wall for a number of slices. an array of points coorosponding to the artery wall in a cell cell arrays of points. Each cell is from a single slice.

zcoord=round(linspace(1,length(all_pic(1,1,:)),numslice)); pic=all_pic(:,:,zcoord); count=0; for i=zcoord count=count+1; [r,c]=find(all_surf(:,:,i)~=0);%%%%%%%%%%%%%%%%%%%% surf{count}=[r c];%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% info{count}=all_info{i}; end X=cell(0); Y=cell(0); Z=cell(0); %draw in outer wall points h=figure; for i=1:length(info) xscale=info{i}.PixelSpacing(2); yscale=info{i}.PixelSpacing(1); base_XYZ=info{i}.ImagePositionPatient;

%mm/pixel %mm/pixel %mm

xvect=info{i}.ImageOrientationPatient(1:3); yvect=info{i}.ImageOrientationPatient(4:6); zvect=cross(xvect,yvect); T=[xvect yvect zvect]; %transform from xyz to XYZ imshow(pic(:,:,i),[],'InitialMagnification','fit'); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% hold on for j=1:length(surf{i}(:,1)) plot(surf{i}(j,2),surf{i}(j,1),'.r') end title('Draw In Outerwall of Artery')%%%%%%%%%%% hold off %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% line=impoly();

138

pos=getPosition(line); x=pos(:,1); y=pos(:,2); for j=1:length(x); shift_xyz=[x(j)*xscale; (-1)*y(j)*yscale; 0]; shift_XYZ=T*shift_xyz; XYZ=(base_XYZ+shift_XYZ)*.001; X{i}(j)=XYZ(1); Y{i}(j)=XYZ(2); Z{i}(j)=XYZ(3); end end close(h) %converts to meters

139

DicomMagnify
function [new_info]=DicomMagnify(info,mag) %Corrects a stack of DICOM info files after the data has been resized % %Inputs: % % info - cell array of dicom information structures % mag - the rescalling factor or magnification % % new_info - corrected cell array of dicom information new_info=cell(length(info),1); for i=1:length(info) new_info{i}=info{i}; new_info{i}.PixelSpacing=new_info{i}.PixelSpacing/mag; end

140

DicomPic2Pts
function [X,Y,Z]=DicomPic2Pts(img,info) %Exports cell arrays of points. Each cell is from a single slice X=cell(0); Y=cell(0); Z=cell(0); for i=1:length(info) xscale=info{i}.PixelSpacing(2); yscale=info{i}.PixelSpacing(1); pos=info{i}.ImagePositionPatient;

%mm/pixel %mm/pixel %mm

xvect=info{i}.ImageOrientationPatient(1:3); yvect=info{i}.ImageOrientationPatient(4:6); zvect=cross(xvect,yvect); T=[xvect yvect zvect]; %transform from xyz to XYZ [r,c]=find(img(:,:,i)); for j=1:length(r); shift_xyz=[c(j)*xscale; (-1)*r(j)*yscale; 0]; shift_XYZ=T*shift_xyz; pos_new=(pos+shift_XYZ)*.001; X{i}(j)=pos_new(1); Y{i}(j)=pos_new(2); Z{i}(j)=pos_new(3); end end %converts points to meters

141

Edging_GUI
function varargout = Edging_GUI(varargin) % EDGING_GUI M-file for Edging_GUI.fig % EDGING_GUI, by itself, creates a new EDGING_GUI or raises the existing % singleton*. % % H = EDGING_GUI returns the handle to a new EDGING_GUI or the handle to % the existing singleton*. % % EDGING_GUI('CALLBACK',hObject,eventData,handles,...) calls the local % function named CALLBACK in EDGING_GUI.M with the given input arguments. % % EDGING_GUI('Property','Value',...) creates a new EDGING_GUI or raises the % existing singleton*. Starting from the left, property value pairs are % applied to the GUI before Edging_GUI_OpeningFcn gets called. An % unrecognized property name or invalid value makes property application % stop. All inputs are passed to Edging_GUI_OpeningFcn via varargin. % % *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one % instance to run (singleton)". % % See also: GUIDE, GUIDATA, GUIHANDLES % Begin initialization code - DO NOT EDIT gui_Singleton = 1; gui_State = struct('gui_Name', mfilename, ... 'gui_Singleton', gui_Singleton, ... 'gui_OpeningFcn', @Edging_GUI_OpeningFcn, ... 'gui_OutputFcn', @Edging_GUI_OutputFcn, ... 'gui_LayoutFcn', [] , ... 'gui_Callback', []); if nargin && ischar(varargin{1}) gui_State.gui_Callback = str2func(varargin{1}); end if nargout

142

[varargout{1:nargout}] = gui_mainfcn(gui_State, varargin[4]); else gui_mainfcn(gui_State, varargin[4]); end % End initialization code - DO NOT EDIT function Edging_GUI_OpeningFcn(hObject, eventdata, handles, varargin) %#ok<*INUSL> h=waitbar(0,'Initializing GUI'); %initialize inputs handles.img_raw=varargin{1}; handles.zlength=length(handles.img_raw(1,1,:)); handles.ylengthr=length(handles.img_raw(:,1,1)); handles.xlengthr=length(handles.img_raw(1,:,1)); defaults=varargin{2}; %initialize default data if length(defaults.size)==1 handles.size=defaults.size*ones(length(handles.img_raw(1,1,:)),1); else handles.size=defaults.size; end if length(defaults.rad)==1 handles.rad=defaults.rad*ones(length(handles.img_raw(1,1,:)),1); else handles.rad=defaults.rad; end handles.mag=defaults.mag; handles.xlength=handles.xlengthr*handles.mag; handles.ylength=handles.ylengthr*handles.mag; %get initial thresholds if isfield(defaults,'thresh') if length(defaults.thresh)==1 handles.thresh=cell(1,zlength); for i=1:zlength handles.thresh{i}=defaults.thresh; end else handles.thresh=defaults.thresh; end else

143

for i=1:handles.zlength img_interp=imresize(handles.img_raw(:,:,i),handles.mag); img_filt=medfilt2(img_interp,handles.size(i)*[1 1]); [img_edge,handles.thresh{i}]=edge(img_filt,'canny'); end end %initialize GUI strings=cell(handles.zlength,1); for i=1:handles.zlength strings{i}=['Slice ' num2str(i)]; end set(handles.listbox1,'String',strings) handles.selected=1; handles.plot='closed'; set(handles.edit_mag,'String',num2str(handles.mag)); set(handles.edit_rad,'String',num2str(handles.rad(handles.selected))); set(handles.uipanel3,'SelectionChangeFcn',@plot_options); close(h) guidata(hObject, handles); refresh(handles); uiwait(handles.figure1); function varargout = Edging_GUI_OutputFcn(hObject, eventdata, handles) h=waitbar(0,'Compiling Images...'); %create analysed images img_close=zeros(handles.ylength,handles.xlength,handles.zlength); for i=1:handles.zlength img_interp=imresize(handles.img_raw(:,:,i),handles.mag); img_filt=medfilt2(img_interp,handles.size(i)*[1 1]); img_edge=edge(img_filt,'canny',handles.thresh{i}); img_close(:,:,i)=imclose(img_edge,strel('disk',handles.rad(i))); end %export desired values varargout{1}=img_close;

144

settings.size=handles.size; settings.thresh=handles.thresh; settings.mag=handles.mag; settings.rad=handles.rad; varargout{2}=settings; close(hObject) close(h);

function listbox1_CreateFcn(hObject, eventdata, handles) %#ok<*DEFNU,*INUSD> % Hint: listbox controls usually have a white background on Windows. % See ISPC and COMPUTER. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end function edit_size_CreateFcn(hObject, eventdata, handles) % Hint: edit controls usually have a white background on Windows. % See ISPC and COMPUTER. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end function slider_size_CreateFcn(hObject, eventdata, handles) % Hint: slider controls usually have a light gray background. if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor',[.9 .9 .9]); end function edit_prime_CreateFcn(hObject, eventdata, handles) % Hint: edit controls usually have a white background on Windows. % See ISPC and COMPUTER. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end function slider_prime_CreateFcn(hObject, eventdata, handles) % Hint: slider controls usually have a light gray background.

145

if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor',[.9 .9 .9]); end function edit_second_CreateFcn(hObject, eventdata, handles) % Hint: edit controls usually have a white background on Windows. % See ISPC and COMPUTER. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end function slider_second_CreateFcn(hObject, eventdata, handles) % Hint: slider controls usually have a light gray background. if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor',[.9 .9 .9]); end function edit_mag_CreateFcn(hObject, eventdata, handles) % Hint: edit controls usually have a white background on Windows. % See ISPC and COMPUTER. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end function edit_rad_CreateFcn(hObject, eventdata, handles) % Hint: edit controls usually have a white background on Windows. % See ISPC and COMPUTER. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end

function listbox1_Callback(hObject, eventdata, handles) %Update handles and refresh handles.selected=get(hObject,'Value'); guidata(hObject, handles); refresh(handles); function edit_size_Callback(hObject, eventdata, handles)

146

try %#ok<TRYNC> %Check that new value is an integer > 0 if str2double(get(handles.edit_size,'String'))>0 str=get(handles.edit_size,'String'); value=round(str2double(str)); %if > max_slider disable the slider bar /if < max_slider active the slider bar slider_max=get(handles.slider_size,'Max'); if value>slider_max set(handles.slider_size,'Value',slider_max); set(handles.slider_size,'Enable','off'); else set(handles.slider_size,'Enable','on'); end %Update handles and refresh handles.size(handles.selected)=value; guidata(hObject, handles); refresh(handles) end end function slider_size_Callback(hObject, eventdata, handles) value=round(get(handles.slider_size,'Value')); %Update handles and refresh handles.size(handles.selected)=value; guidata(hObject, handles); refresh(handles) function edit_prime_Callback(hObject, eventdata, handles) try %#ok<TRYNC> %Check that new value is a number >0 and < secondary threshold if str2double(get(handles.edit_prime,'String'))>0 str=get(handles.edit_prime,'String'); value=str2double(str); if value >= handles.thresh{handles.selected}(2) warning('Primary Threshold must be less than Secondary Threshold'); %#ok<WNTAG> value=handles.thresh{handles.selected}(2)-.01; end %Update handles and refresh handles.thresh{handles.selected}(1)=value; guidata(hObject, handles);

147

refresh(handles) end end

function slider_prime_Callback(hObject, eventdata, handles) value=get(handles.slider_prime,'Value'); %Check value is not less than secondary value if value >= handles.thresh{handles.selected}(2) warning('Primary Threshold must be less than Secondary Threshold'); %#ok<WNTAG> value=handles.thresh{handles.selected}(2)-.01; end %Update handles and refresh handles.thresh{handles.selected}(1)=value; guidata(hObject, handles); refresh(handles) function edit_second_Callback(hObject, eventdata, handles) try %#ok<TRYNC> %Check that new value is a number <1 and > primary threshold if str2double(get(handles.edit_second,'String'))>0 str=get(handles.edit_second,'String'); value=str2double(str); if value <= handles.thresh{handles.selected}(1) warning('Secondary Threshold must be greater than Primary Threshold'); %#ok<WNTAG> value=handles.thresh{handles.selected}(1)+.01; end %Update handles and refresh handles.thresh{handles.selected}(2)=value; guidata(hObject, handles); refresh(handles) end end function slider_second_Callback(hObject, eventdata, handles) value=get(handles.slider_second,'Value'); %Check value is not less than secondary value if value <= handles.thresh{handles.selected}(1)

148

warning('Secondary Threshold must be greater than Primary Threshold'); %#ok<WNTAG> value=handles.thresh{handles.selected}(1)+.01; end %Update handles and refresh handles.thresh{handles.selected}(2)=value; guidata(hObject, handles); refresh(handles) function edit_mag_Callback(hObject, eventdata, handles) try %#ok<TRYNC> %Check that new value is an integer > 0 if str2double(get(handles.edit_mag,'String'))>0 str=get(handles.edit_mag,'String'); value=round(str2double(str)); end %Clear previous image data since it is now the wrong dimension handles.img_interp=[]; handles.img_filt=[]; handles.img_edge=[]; handles.img_close=[]; %Update handles and refresh handles.mag=value; guidata(hObject, handles); refresh(handles) end

function edit_rad_Callback(hObject, eventdata, handles) try %#ok<TRYNC> %Check that new value is an integer > 0 if str2double(get(handles.edit_rad,'String'))>0 str=get(handles.edit_rad,'String'); value=round(str2double(str)); %Update handles and refresh handles.rad(handles.selected)=value; guidata(hObject, handles); refresh(handles) end end

149

function plot_options(hObject,eventdata) handles=guidata(gcf); handles.plot=get(eventdata.NewValue,'Tag'); guidata(gcf, handles); refresh(handles) function button_apply_all_Callback(hObject, eventdata, handles) doit=questdlg('Are you sure you want to overwrite all slice settings?','WARNING','Yes','No','No'); if strcmp(doit,'Yes') a=handles.selected; mag=handles.mag(a); size=handles.size(a); thresh=handles.thresh{a}; rad=handles.rad(a); for i=1:handles.zlength handles.mag(i)=mag; handles.size(i)=size; handles.thresh{i}=thresh; handles.rad(i)=rad; end end function button_detect_thresh_Callback(hObject, eventdata, handles) img_interp=imresize(handles.img_raw(:,:,handles.selected),handles.mag); img_filt=medfilt2(img_interp,handles.size(handles.selected)*[1 1]); [x,handles.thresh{handles.selected}]=edge(img_filt,'canny'); guidata(gcf, handles); refresh(handles)

function button_done_Callback(hObject, eventdata, handles) uiresume();

function refresh(handles)

150

a=handles.selected; %Synchronize sliders and text boxes set(handles.edit_size,'String',num2str(handles.size(a))); set(handles.slider_size,'Value',handles.size(a)); set(handles.edit_prime,'String',num2str(handles.thresh{a}(1))); set(handles.slider_prime,'Value',handles.thresh{a}(1));

set(handles.edit_second,'String',num2str(handles.thresh{a}(2))); set(handles.slider_second,'Value',handles.thresh{a}(2)); set(handles.edit_rad,'String',num2str(handles.rad(a))); %Plot the requested image switch handles.plot case 'unaltered' imshow(handles.img_raw(:,:,a),[]); case 'magnified' handles.xlength=handles.xlengthr*handles.mag; handles.ylength=handles.ylengthr*handles.mag;

img_interp=imresize(handles.img_raw(:,:,a),handles.mag); imshow(img_interp,[]); case 'filtered' handles.xlength=handles.xlengthr*handles.mag; handles.ylength=handles.ylengthr*handles.mag;

img_interp=imresize(handles.img_raw(:,:,a),handles.mag); img_filt=medfilt2(img_interp,handles.size(a)*[1 1]); imshow(img_filt,[]); case 'edged' handles.xlength=handles.xlengthr*handles.mag; handles.ylength=handles.ylengthr*handles.mag;

img_interp=imresize(handles.img_raw(:,:,a),handles.mag); img_filt=medfilt2(img_interp,handles.size(a)*[1 1]); img_edge=edge(img_filt,'canny',handles.thresh{a}); imshow(img_edge); case 'closed'

151

handles.xlength=handles.xlengthr*handles.mag; handles.ylength=handles.ylengthr*handles.mag;

img_interp=imresize(handles.img_raw(:,:,a),handles.mag); img_filt=medfilt2(img_interp,handles.size(a)*[1 1]); img_edge=edge(img_filt,'canny',handles.thresh{a}); img_close=imclose(img_edge,strel('disk',handles.rad(a))); imshow(img_close); case 'overlay' handles.xlength=handles.xlengthr*handles.mag; handles.ylength=handles.ylengthr*handles.mag;

img_interp=imresize(handles.img_raw(:,:,a),handles.mag); img_filt=medfilt2(img_interp,handles.size(a)*[1 1]); img_edge=edge(img_filt,'canny',handles.thresh{a}); img_close=imclose(img_edge,strel('disk',handles.rad(a))); overlay=-0.75*double(img_close)+1; image=double(imresize(handles.img_raw(:,:,a),handles.mag,'nearest')); plot=overlay.*image; imshow(plot/max(plot(:)),[]); end

function figure1_WindowScrollWheelFcn(hObject, eventdata, handles) if (eventdata.VerticalScrollCount>0) % scroll down for i=1:eventdata.VerticalScrollCount if handles.selected+1 > handles.zlength handles.selected=1; else handles.selected=handles.selected+1; end end else % scroll up for i=1:abs(eventdata.VerticalScrollCount) if handles.selected == 1 handles.selected=handles.zlength; else handles.selected=handles.selected-1;

152

end end end set(handles.listbox1,'Value',handles.selected); guidata(hObject, handles); refresh(handles)

153

GetBody_GUI
function [bod]=GetBody_GUI(img) h=figure; for i=1:length(img(1,1,:)) imshow(img(:,:,i),'InitialMagnification','fit') title('Select Body') [c,r]=ginput(1); bod(:,:,i)=bwselect(img(:,:,i),c,r,4); end close(h);

154

GetDicomStack
function [img info]=GetDicomStack(FilterSpec,DialogTitle,DefaultName,Sort) if nargin<3 DefaultName=pwd; if nargin<2 DialogTitle='Open Dicom Images'; if nargin<1 FilterSpec='*.*'; end end end

[file,path]=uigetfile(FilterSpec,DialogTitle,DefaultName,'MultiSelect', 'on'); if ~iscell(file) temp=file; clear file file{1}=temp; clear temp end h=waitbar(0,'Importing Data...'); %Initialize for loop %Get Information sliceinfo=dicominfo([path file{1}]); try info{1,1}.SequenceName=sliceinfo.SequenceName; end try info{1,1}.SliceThickness=sliceinfo.SliceThickness; end try info{1,1}.SpacingBetweenSlices=sliceinfo.SpacingBetweenSlices; end try info{1,1}.ImagePositionPatient=sliceinfo.ImagePositionPatient; end try info{1,1}.PixelSpacing=sliceinfo.PixelSpacing; end try info{1,1}.TriggerTime=sliceinfo.TriggerTime; end try info{1,1}.ImageOrientationPatient=sliceinfo.ImageOrientationPatient; end try info{1,1}.SliceLocation=sliceinfo.SliceLocation; end try info{1,1}.InstanceNumber=sliceinfo.InstanceNumber; end %Get Image img(:,:,1)=dicomread([path file{1}]);

155

%Define Variables img(:,:,2:length(file))=zeros(length(img(:,1,1)),length(img(1,:,1)),len gth(file)-1); for i=2:length(file) %Get Information sliceinfo=dicominfo([path file{i}]); try info{i,1}.SequenceName=sliceinfo.SequenceName; end try info{i,1}.SliceThickness=sliceinfo.SliceThickness; end try info{i,1}.SpacingBetweenSlices=sliceinfo.SpacingBetweenSlices; end try info{i,1}.ImagePositionPatient=sliceinfo.ImagePositionPatient; end try info{i,1}.PixelSpacing=sliceinfo.PixelSpacing; end try info{i,1}.TriggerTime=sliceinfo.TriggerTime; end try info{i,1}.ImageOrientationPatient=sliceinfo.ImageOrientationPatient; end try info{i,1}.SliceLocation=sliceinfo.SliceLocation; end try info{i,1}.InstanceNumber=sliceinfo.InstanceNumber; end %Get Image img(:,:,i)=dicomread([path '\' file{i}]); end %Sort Data if nargin==4 switch Sort case 'height' for i=1:length(file) crit(i,1)=info{i}.ImagePositionPatient(3); crit(i,2)=i; end case 'time' for i=1:length(file) crit(i,1)=info{i}.TriggerTime; crit(i,2)=i; end end crit=sortrows(crit,1); info_tmp=info; img_tmp=img; for i=1:length(file)

156

info{i}=info_tmp{crit(i,2)}; img(:,:,i)=img_tmp(:,:,crit(i,2)); end end close(h)

157

Slice2Rhino
function Slice2Rhino(x,y,z,spline_order,fname,sname,samp) if nargin<7 samp=1; end ex_slice=round(linspace(1,length(x),round(length(x)/samp))); %Start File fid=fopen(fname,'a'); fprintf(fid,'%s\r\n','NoEcho'); fprintf(fid,'%s\r\n','SetRedrawOff'); %Add Layers and Points for i=ex_slice sectionname=['"' sname ' ' num2str(i,'%g') '"']; fprintf(fid,'%s\r\n','-Layer'); fprintf(fid,'%s %s\r\n', 'New ', sectionname); fprintf(fid,'%s %s\r\n', 'Current ', sectionname); fprintf(fid,'%s %s\r\n', 'Off ',sectionname); fprintf(fid,'%s\r\n','_Enter'); fprintf(fid,'%s\r\n','_Enter'); fprintf(fid,'%s\r\n','_SelAll'); fprintf(fid,'%s\r\n','_Points'); for k=1:length(x{i}) fprintf(fid,'%s%s%s%s%s\r\n',num2str(x{i}(k),'%6.5E'),',',num2str(y{i}( k),'%6.5E'),',',num2str(z{i}(k),'%6.5E')); end fprintf(fid,'%s\r\n','_Enter'); fprintf(fid,'%s\r\n','_Invert'); fprintf(fid,'%s\r\n','_CurveThroughPt'); fprintf(fid,'%s\r\n','CurveType=ControlPoint'); fprintf(fid,'%s\r\n',['Degree=' num2str(spline_order)]); fprintf(fid,'%s\r\n','Closed=Yes'); fprintf(fid,'%s\r\n','_Enter'); fprintf(fid,'%s\r\n','SelNone'); fprintf(fid,'%s\r\n','SelPts');

158

fprintf(fid,'%s\r\n','Hide'); end %Close out file fprintf(fid,'%s\r\n','SetRedrawOn'); fprintf(fid,'%s\r\n','Zoom All Extents'); fprintf(fid,'%s\r\n','Echo'); fclose(fid);

159

BATCH_FILE (velocimetry)
cycles=5; %%%%%%%% avg_points=5; %%%%%%%%%%% %% Import Data %Import input images [in_data, in_info]=GetDicomStack('*.*','Select Inlet Z-velocity Data','D:\mark\Aortic Wall Imaging\data'); %make sure its velocity data try in_info=DisectSequenceName(in_info); catch err if strcmp(err.identifier,'MATLAB:badsubscript') beep; warning('SequenceName unrecognizable. Ensure that selected data is velocity information. Information was not imported.'); else rethrow(err) end end %extract flowrate data [in_t, in_Q, in_intA]=ExtractFlowrate(in_data,in_info); %%%%%%%%%%% in_Q=RunningAvg(in_Q,avg_points); %%%%%%%%%% %Import outlet images [out_data, out_info]=GetDicomStack('*.*','Select Outlet Z-velocity Data','D:\mark\Aortic Wall Imaging\data'); %make sure its velocity data try out_info=DisectSequenceName(out_info); catch err if strcmp(err.identifier,'MATLAB:badsubscript') beep; warning('SequenceName unrecognizable. Ensure that selected data is velocity information. Information was not imported.'); else rethrow(err) end

160

end %extract flowrate data [out_t, out_Q, out_intA]=ExtractFlowrate(out_data,out_info); %%%%%%%%% out_Q=RunningAvg(out_Q,avg_points); %%%%%%%%% %Get Model Area information (intA is the integrated area. modA is the model area) in_modA=input('What is the area of the models input: '); disp(['%Diff in areas = ' num2str((in_modA-in_intA)/in_intA*100) '%']) disp(' ') out_modA=input('What is the area of the models output: '); disp(['%Diff in areas = ' num2str((out_modA-out_intA)/out_intA*100) '%']) disp(' ') % % % % % % % % % %Get Pressure Point %%%%%%%%Requests a pressure point%%%%%%%% % disp('Input Pressure Value Info:') % tp=input(' Time(s)? '); % p=input ('Pressure(mmHg)? ')*133.322368;

%s %Pa %Pa %Pa

disp('Input Pressure Value Info:') systolic=input(' Systolic(mmHg)? ')*133.322368; diastolic=input(' Diatstolic(mmHg)? ')*133.322368;

%% Calculate Inlet Velocity Boundary Condition in_V=in_Q/in_modA; [t_rep,V_rep]=Replicate(in_t,in_V',cycles); inletV_BC=[t_rep V_rep]; %% Calculate Outlet Flowrate Boundary Condition %Match outlet period to inlet period in_period=range(in_t); out_period=range(out_t); tshift=in_period/out_period; t_norm=out_t*tshift; disp(['Period Modifier = ' num2str(tshift)]) %Correct flowrate for modified period

161

Qshift=trapz(in_t,in_Q)/trapz(t_norm,out_Q); %Qshift=fminunc(@(shift) trapz(t_norm, (in_Q-out_Q*shift)^2),1); Q_norm=out_Q*Qshift; disp(['Flowrate Modifier = ' num2str(Qshift)])

%Replicate Flowrate Data [t_rep,Q_rep]=Replicate(t_norm,Q_norm',cycles); outletQ_BC=[t_rep Q_rep]; outletV_BC=[t_rep Q_rep/out_modA]; % % % % % % % %% Generate Downstream Pressure Curve Pscale=(systolic-diastolic)/(max(Q_rep)-min(Q_rep)); P_rep=Q_rep*Pscale; Pshift=systolic-max(P_rep); P_rep=P_rep+Pshift; outletP_BC=[t_rep P_rep];

%% Generate Experimental Data Structure data.t=inletV_BC(:,1); data.period=in_period; data.Vin=inletV_BC(:,2); data.Qout=outletQ_BC(:,2); data.Vout=outletV_BC(:,2); data.Ain=in_modA; data.Aout=out_modA; data.tshift=tshift; data.Qshift=Qshift; %data.Pout=[tp, p]; %data.Pout=outletP_BC(:,2); clear Q_norm Q_rep Qshift V_rep ans cycles in_Q in_V in_data in_info in_intA in_modA in_period in_t inletV_BC out_Q out_data out_info out_intA out_modA out_period out_t outletBC t_norm t_rep tshift

162

DisectSequenceName
function [new_info]=DisectSequenceName(info) new_info=info; template='velocimetry'; switch template case 'velocimetry' for i=1:length(info) str=info{i}.SequenceName; str=str(findstr('_v',str)+2:length(str)); isnum=isstrprop(str,'digit'); j=1; vel=''; while isnum(j) vel=[vel str(j)]; j=j+1; end str=str(length(vel)+1:length(str)); switch str case 'rl' dir='x'; case 'ap' dir='y'; case 'in' dir='z'; end new_info{i}.VelocityScale=str2double(vel); new_info{i}.Direction=dir; end end

163

ExtractFlowrate
function [time, flowrate, area]=ExtractFlowrate(data,info) %Crop images down to ROI window=Crop_GUI(data); [data,info]=DicomCrop(data,info,window); %Draw in area to integrate over slice=input('Pick Slice number to show: '); h_im = imshow(data(:,:,slice),[],'InitialMagnification','fit'); e=imellipse(); input('Finished?'); %Define area of integration mask=createMask(e,h_im); close(gcf); [r,c]=find(mask); %integrate velocity over area to get flowrate Q=zeros(length(info),1); T=zeros(length(info),1); for slice=1:length(info) vel=(double(data(:,:,slice))2048).*(double(info{slice}.VelocityScale)/2048);

%cm/s

pixel_area=info{slice}.PixelSpacing(1)*info{slice}.PixelSpacing(2)*(1/1 0)^2; %cm^2 T(slice)=info{slice}.TriggerTime; for i=1:length(r) Q(slice)=Q(slice)+pixel_area*vel(r(i),c(i)); end A(slice)=pixel_area*length(r); end %make sure the area of integration was constant and define it if min(A)~=max(A) warning('Slices appear to have different scaling. Calculations may be incorrect'); area=mode(A); else area=A(1); end %standardize units T=T/1000; %sec Q=Q/100^3; %m^3/sec

164

area=area/100^2;

%m^2

%sort data by time table=sortrows([T Q]); time=table(:,1); flowrate=table(:,2);

165

Replicate
function [t_rep, F_rep]=Replicate(t,F,cycles) %given 1 period of data on a function, F over time, t this function %replicates the data for a give number of cycles t_rep=t; F_rep=F; for i=1:cycles-1 t_rep=[t_rep; t(2:length(t))+max(t_rep)]; F_rep=[F_rep; F(2:length(F))]; end

166

SortDicom
function SortDICOM(fname,fpath) if nargin==0 [fname fpath]=uigetfile('*.*','Select DICOM Header File'); end h=waitbar(0,'Reading in data'); A=dicominfo([fpath '\' fname]); list=A.DirectoryRecordSequence; fields=fieldnames(list); waitbar(0,h,'Sorting files'); for i=1:length(fields) type=eval(['list.' fields{i} '.DirectoryRecordType']); switch type case 'PATIENT' Patient=eval(['list.' fields{i} '.PatientName.FamilyName']); if ~exist([fpath Patient]) mkdir([fpath Patient]); end case 'STUDY' Study=eval(['list.' fields{i} '.StudyDescription']); if ~exist([fpath Patient '\' Study]) mkdir([fpath Patient '\' Study]); end case 'SERIES' Series=num2str(eval(['list.' fields{i} '.SeriesNumber'])); if ~exist([fpath Patient '\' Study '\Series ' Series]) mkdir([fpath Patient '\' Study '\Series ' Series]); end case 'IMAGE' Image=num2str(eval(['list.' fields{i} '.InstanceNumber'])); fileID=eval(['list.' fields{i} '.ReferencedFileID']); source=[fpath fileID]; destination=[fpath Patient '\' Study '\Series ' Series '\Image ' Image]; copyfile(source,destination); end waitbar(i/length(fields),h); end close(h)

167

Velocity
% Import all of the velocity data data=[]; info=cell(0); more=1; while more==1 [new_data, new_info]=GetDicomStack('*.*','Select Dicom Images','D:\mark\Aortic Wall Imaging\data'); %check to make sure it is velocity data try new_info=DisectSequenceName(new_info); data=cat(3,data,new_data); info=[info; new_info]; catch err if strcmp(err.identifier,'MATLAB:badsubscript') beep; warning('SequenceName unrecognizable. Ensure that selected data is velocity information. Information was not imported.'); else rethrow(err) end end q=questdlg('Would you like to import more data?','Continue Importing?','Yes'); if strcmp(q,'No') more=0; end end window=Crop_GUI(data); [data,info]=DicomCrop(data,info,window); h=waitbar(0,'Converting Data...'); [dimy, dimx, dimz]=size(data); column=ones(dimy,1)*(1:dimx); row=(1:dimy)'*ones(1,dimx); T=[]; X=[]; Y=[]; Z=[]; U=[]; V=[]; W=[]; for i=1:dimz slice=data(:,:,i); loc_info=info{i};

168

%Slice Info spacing=loc_info.PixelSpacing; dir=loc_info.Direction; time=loc_info.TriggerTime; xvect=loc_info.ImageOrientationPatient(1:3); yvect=loc_info.ImageOrientationPatient(4:6); localxyz=loc_info.ImagePositionPatient; zvect=cross(xvect,yvect); TF=[xvect yvect zvect]; %Slice Local Data xloc=column*spacing(2); yloc=(-1)*row*spacing(1); zloc=zeros(dimy, dimx); vel=(double(slice)2048).*(double(loc_info.VelocityScale)/2048); %Globalized Data xabs=localxyz(1) + TF(1,1)*xloc + TF(1,2)*yloc + TF(1,3)*zloc; yabs=localxyz(2) + TF(2,1)*xloc + TF(2,2)*yloc + TF(2,3)*zloc; zabs=localxyz(3) + TF(3,1)*xloc + TF(3,2)*yloc + TF(3,3)*zloc; switch dir case 'x' uabs=TF(1,1)*vel; vabs=TF(1,2)*vel; wabs=TF(1,3)*vel; case 'y' uabs=TF(2,1)*vel; vabs=TF(2,2)*vel; wabs=TF(2,3)*vel; case 'z' uabs=TF(3,1)*vel; vabs=TF(3,2)*vel; wabs=TF(3,3)*vel; end X=[X; Y=[Y; Z=[Z; T=[T; U=[U; V=[V; W=[W; end reshape(xabs,[],1)]; reshape(yabs,[],1)]; reshape(zabs,[],1)]; time*ones(dimx*dimy,1)]; reshape(uabs,[],1)]; reshape(vabs,[],1)]; reshape(wabs,[],1)];

169

%Sort Data waitbar(0,h,'Sorting Data...'); Values=[X*.001 Y*.001 Z*.001 T*.001 U*.01 V*.01 W*.01]; %converts from mm,ms,cm/s to m,s,m/s clear X Y Z T U V W Values=sortrows(Values,1); Values=sortrows(Values,2); Values=sortrows(Values,3); Values=sortrows(Values,4); time=Values(1,4); zone=1; point=1; for i=1:3:length(Values(:,1)) if Values(i,4)==time X{zone}(point)=Values(i,1); Y{zone}(point)=Values(i,2); Z{zone}(point)=Values(i,3); T{zone}(point)=Values(i,4); U{zone}(point)=Values(i,5)+Values(i+1,5)+Values(i+2,5); V{zone}(point)=Values(i,6)+Values(i+1,6)+Values(i+2,6); W{zone}(point)=Values(i,7)+Values(i+1,7)+Values(i+2,7); point=point+1; else time=Values(i,4); zone=zone+1; point=1; X{zone}(point)=Values(i,1); Y{zone}(point)=Values(i,2); Z{zone}(point)=Values(i,3); T{zone}(point)=Values(i,4); U{zone}(point)=Values(i,5)+Values(i+1,5)+Values(i+2,5); V{zone}(point)=Values(i,6)+Values(i+1,6)+Values(i+2,6); W{zone}(point)=Values(i,7)+Values(i+1,7)+Values(i+2,7); point=point+1; end end %Write TecPlot Data File [ex_file,ex_path]=uiputfile('*.txt','Save Output File As',pwd); fname=[ex_path ex_file]; waitbar(0,h,'Exporting Data...'); fid=fopen(fname,'w');

170

for i=1:length(X) fprintf(fid,'%s\r\n','TITLE="Experimental Velocity Data"'); fprintf(fid,'%s\r\n','VARIABLES="X", "Y", "Z", "U", "V", "W"'); fprintf(fid,'%s\r\n',['ZONE T="TIME=' num2str(T{i}(1)) '", I=' num2str(length(X{i})) ', DATAPACKING=POINT']); for j=1:length(X{i}) x=num2str(X{i}(j),'%9.8E'); y=num2str(Y{i}(j),'%9.8E'); z=num2str(Z{i}(j),'%9.8E'); u=num2str(U{i}(j),'%9.8E'); v=num2str(V{i}(j),'%9.8E'); w=num2str(W{i}(j),'%9.8E'); fprintf(fid,'%s\r\n',[x ' ' y ' ' z ' ' u ' ' v ' ' w ]); end end fclose(fid); close(h); drawnow; beep;

171

DicomGetAbsLocation
function [pos]=DicomGetAbsLocation(file) info=dicominfo(file); pic=dicomread(file); %get point h=figure(); imshow(pic,[]); title('Select Point') [x,y]=ginput(1); close(h);

%position in local pixel coordinates

TF(:,1)=info.ImageOrientationPatient(1:3); TF(:,2)=info.ImageOrientationPatient(4:6); TF(:,3)=cross(TF(:,1),TF(:,2)); x=x*info.PixelSpacing(1); y=-y*info.PixelSpacing(2); X=x*TF(1,1)+y*TF(1,2); absolute coordinates (mm) Y=x*TF(2,1)+y*TF(2,2); Z=x*TF(3,1)+y*TF(3,2); %position in local coordinates (mm) %(negative accounts for pixel notation) %distance from local origin to point in

Xpos=X+info.ImagePositionPatient(1); coordinates (mm) Ypos=Y+info.ImagePositionPatient(2); Zpos=Z+info.ImagePositionPatient(3); pos=[Xpos; Ypos; Zpos];

%position in absolute

172

bw3select
function bod=bwselect3(BW,pt,n) % Extracts 3D body from image stack % % Inputs: % % BW - black and white image stack % pt - point inside the body in the form [col row slice] % n - definition of fill function connectivity. can be 4 % (up/down/left/right) or 8 (includes corners). default is 4. % % Outputs: % % bod - black and white image stack where only the body is white % Check inputs if nargin<4 n=4; end if (pt(1)<1) || (pt(1)>length(BW(1,:,1))) error('Selected X point is not in the image'); end if (pt(2)<1) || (pt(2)>length(BW(:,1,1))) error('Selected Y point is not in the image'); end if (pt(3)<1) || (pt(3)>length(BW(1,1,:))) error('Selected Z point is not in the image'); end %Initialize c=pt(1); r=pt(2); z=pt(3); bod=zeros(size(BW)); bod(:,:,z)=bwselect(BW(:,:,z),c,r,n); %figure; imshow([BW(:,:,z) bod(:,:,z)]); title(['slice ' num2str(z)]); %for slices > given point if z~=length(BW(1,1,:)) for i=(z+1):length(BW(1,1,:)) center=bwmorph(bod(:,:,i-1),'shrink',Inf); [r,c]=find(center); if length(c)>1 figure; imshow([BW(:,:,i-1) bod(:,:,i-1)]);

173

title(['slice ' num2str(i-1)]); figure; imshow([BW(:,:,i)]); title(['slice ' num2str(i)]); error(['Multiple bodies detected in slice' num2str(i)]); end %search for white pixel nearby if BW(c,r,i)~=1 r_new=0; c_new=0; search=[0 1 0 1 1 1 0 1 0]; ne=BW(c-1:c+1,r-1:r+1)*search; try [r_new,c_new]=find(ne,1); end if r_new==0 search=ones(3); ne=BW(c-1:c+1,r-1:r+1)*search; try [r_new,c_new]=find(ne,1); end if r_new==0 error('Cant find White Pixel at Selection'); end end r=r_new; c=c_new; end bod(:,:,i)=bwselect(BW(:,:,i),c,r,n); %figure; imshow([BW(:,:,i) bod(:,:,i)]); title(['slice ' num2str(i)]); end end %for slices < given point if z~=1 for i=(z-1):-1:1 center=bwmorph(bod(:,:,i+1),'shrink',Inf); [r,c]=find(center); if length(c)>1 disp(['Center Pointer Row: ' num2str(r)]) disp(['Center Pointer Col: ' num2str(c)]) figure; imshow([BW(:,:,i+1) bod(:,:,i+1)]); title(['slice ' num2str(i+1)]); figure; imshow([BW(:,:,i)]); title(['slice ' num2str(i)]); error(['Multiple bodies detected in slice' num2str(i)]);

174

end bod(:,:,i)=bwselect(BW(:,:,i),c,r,n); %figure; imshow([BW(:,:,i) bod(:,:,i)]); title(['slice ' num2str(i)]); end end

175

Appendix K: Visual Basic Code

ADINA Batch (Main.vb)


Imports System.IO Imports System.Net Imports System.Net.Mail Public Class Main

Private Sub Main_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load 'load values from settings ComboBox_Solver.Text = My.Settings.SelectedSolver TextBox_NumModels.Text = CStr(My.Settings.NumModels) TextBox_NumProcessors.Text = CStr(My.Settings.NumProc) TextBox_NumMemory.Text = CStr(My.Settings.NumMemory) TextBox_SampleRate.Text = CStr(My.Settings.SampleRate) CheckBox_SendNotification.Checked = My.Settings.SendNotification TextBox_To.Text = My.Settings.EmailTo TextBox_From.Text = My.Settings.EmailFrom TextBox_Subject.Text = My.Settings.EmailSubject TextBox_Message.Text = My.Settings.EmailMessage TextBox_SMTPServer.Text = My.Settings.EmailSMTP TextBox_Port.Text = CStr(My.Settings.EmailPort) CheckBox_TLS.Checked = My.Settings.EmailTLS CheckBox_Authenticate.Checked = My.Settings.EmailAuthentication TextBox_UserName.Text = My.Settings.EmailUsername TextBox_Password.Text = My.Settings.EmailPassword End Sub Private Sub ComboBox_Solver_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox_Solver.SelectedIndexChanged 'Checks that the executable file exists for whichever solver is selected Select Case (ComboBox_Solver.Text) Case "ADINA" If Not System.IO.File.Exists(My.Settings.ADINAexe) Then MsgBox("Cannot Locate Required Executable") Dim FilePath As String = Path.GetFullPath(My.Settings.ADINAexe) OpenFileDialog1.InitialDirectory = FilePath OpenFileDialog1.Title = "Please Select ADINA Executable File" OpenFileDialog1.Filter = "Executables (.exe)|*.exe|All Files|*.*" OpenFileDialog1.Multiselect = False

176

OpenFileDialog1.ShowDialog() My.Settings.ADINAexe = OpenFileDialog1.FileName End If Case "ADINA-F" If Not System.IO.File.Exists(My.Settings.ADINAFexe) Then MsgBox("Cannot Locate Required Executable") Dim FilePath As String = Path.GetFullPath(My.Settings.ADINAFexe) OpenFileDialog1.InitialDirectory = FilePath OpenFileDialog1.Title = "Please Select ADINA-F Executable File" OpenFileDialog1.Filter = "Executables (.exe)|*.exe|All Files|*.*" OpenFileDialog1.Multiselect = False OpenFileDialog1.ShowDialog() My.Settings.ADINAFexe = OpenFileDialog1.FileName End If Case "ADINA-FSI" If Not System.IO.File.Exists(My.Settings.ADINAFSIexe) Then MsgBox("Cannot Locate Required Executable") Dim FilePath As String = Path.GetFullPath(My.Settings.ADINAFSIexe) OpenFileDialog1.InitialDirectory = FilePath OpenFileDialog1.Title = "Please Select ADINA-FSI Executable File" OpenFileDialog1.Filter = "Executables (.exe)|*.exe|All Files|*.*" OpenFileDialog1.Multiselect = False OpenFileDialog1.ShowDialog() My.Settings.ADINAFSIexe = OpenFileDialog1.FileName End If Case "ADINA-T" If Not System.IO.File.Exists(My.Settings.ADINATexe) Then MsgBox("Cannot Locate Required Executable") Dim FilePath As String = Path.GetFullPath(My.Settings.ADINATexe) OpenFileDialog1.InitialDirectory = FilePath OpenFileDialog1.Title = "Please Select ADINA-T Executable File" OpenFileDialog1.Filter = "Executables (.exe)|*.exe|All Files|*.*" OpenFileDialog1.Multiselect = False OpenFileDialog1.ShowDialog() My.Settings.ADINATexe = OpenFileDialog1.FileName End If Case "ADINA-TMC" If Not System.IO.File.Exists(My.Settings.ADINATMCexe) Then MsgBox("Cannot Locate Required Executable") Dim FilePath As String = Path.GetFullPath(My.Settings.ADINATMCexe)

177

OpenFileDialog1.InitialDirectory = FilePath OpenFileDialog1.Title = "Please Select ADINA-TMC Executable File" OpenFileDialog1.Filter = "Executables (.exe)|*.exe|All Files|*.*" OpenFileDialog1.Multiselect = False OpenFileDialog1.ShowDialog() My.Settings.ADINATMCexe = OpenFileDialog1.FileName End If End Select My.Settings.SelectedSolver = ComboBox_Solver.Text End Sub Private Sub TextBox_NumModels_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox_NumModels.LostFocus 'Checks for valid entries Try My.Settings.NumModels = CInt(TextBox_NumModels.Text) Catch ex As Exception Beep() TextBox_NumModels.Focus() End Try TextBox_NumModels.Text = My.Settings.NumModels End Sub 'TODO: Check numproc <= actual number of processors Private Sub TextBox_NumProcessors_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox_NumProcessors.LostFocus 'Checks for valid entries Try My.Settings.NumProc = CInt(TextBox_NumProcessors.Text) Catch ex As Exception Beep() TextBox_NumProcessors.Focus() End Try TextBox_NumProcessors.Text = My.Settings.NumProc End Sub 'TODO: Check nummemory <= total memory Private Sub TextBox_NumMemory_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox_NumMemory.LostFocus 'Checks for valid entries Try My.Settings.NumMemory = CInt(TextBox_NumMemory.Text) Catch ex As Exception Beep() TextBox_NumMemory.Focus() End Try TextBox_NumMemory.Text = My.Settings.NumMemory End Sub

178

Private Sub TextBox_SampleRate_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox_SampleRate.LostFocus 'Checks for valid entries Try My.Settings.SampleRate = CDbl(TextBox_SampleRate.Text) Catch ex As Exception Beep() TextBox_SampleRate.Focus() End Try TextBox_SampleRate.Text = My.Settings.SampleRate End Sub Private Sub CheckBox_SendNotification_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox_SendNotification.CheckedChanged My.Settings.SendNotification = CheckBox_SendNotification.Checked 'enables/diables email buttons If CheckBox_SendNotification.Checked Then TextBox_To.Enabled = True TextBox_From.Enabled = True TextBox_Subject.Enabled = True TextBox_Message.Enabled = True TextBox_SMTPServer.Enabled = True TextBox_Port.Enabled = True CheckBox_TLS.Enabled = True CheckBox_Authenticate.Enabled = True Button_TestEmail.Enabled = True If CheckBox_Authenticate.Checked Then TextBox_UserName.Enabled = True TextBox_Password.Enabled = True End If Else TextBox_To.Enabled = False TextBox_From.Enabled = False TextBox_Subject.Enabled = False TextBox_Message.Enabled = False TextBox_SMTPServer.Enabled = False TextBox_Port.Enabled = False CheckBox_TLS.Enabled = False CheckBox_Authenticate.Enabled = False Button_TestEmail.Enabled = False TextBox_UserName.Enabled = False TextBox_Password.Enabled = False End If End Sub 'TODO: Check that string is a proper email address

179

Private Sub TextBox_To_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox_To.LostFocus My.Settings.EmailTo = TextBox_To.Text End Sub 'TODO: Check that string is a proper email address Private Sub TextBox_From_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox_From.LostFocus My.Settings.EmailFrom = TextBox_From.Text End Sub Private Sub TextBox_Subject_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox_Subject.LostFocus My.Settings.EmailSubject = TextBox_Subject.Text End Sub Private Sub TextBox_Message_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox_Message.LostFocus My.Settings.EmailMessage = TextBox_Message.Text End Sub 'TODO: check that string is a proper server name Private Sub TextBox_SMTPServer_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox_SMTPServer.LostFocus My.Settings.EmailSMTP = TextBox_SMTPServer.Text End Sub Private Sub TextBox_Port_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox_Port.LostFocus 'Checks for valid entries Try My.Settings.EmailPort = CInt(TextBox_Port.Text) Catch ex As Exception Beep() TextBox_Port.Focus() End Try TextBox_Port.Text = My.Settings.EmailPort End Sub Private Sub CheckBox_TLS_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox_TLS.CheckedChanged My.Settings.EmailTLS = CheckBox_TLS.Checked End Sub Private Sub CheckBox_Authenticate_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox_Authenticate.CheckedChanged 'enables/diables authentication buttons If CheckBox_Authenticate.Checked Then TextBox_UserName.Enabled = True

180

TextBox_Password.Enabled = True Else TextBox_UserName.Enabled = False TextBox_Password.Enabled = False End If End Sub Private Sub TextBox_UserName_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox_UserName.LostFocus My.Settings.EmailUsername = TextBox_UserName.Text End Sub Private Sub TextBox_Password_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox_Password.LostFocus My.Settings.EmailPassword = TextBox_Password.Text End Sub Private Sub Button_TestEmail_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button_TestEmail.Click 'Read in mailing commands Dim mail As New MailMessage() Dim smtp As New SmtpClient() Try mail = GenMailVariable() Catch MsgBox("ERROR: Failed to construct mail object. Check settings.") Exit Sub End Try Try smtp = GenSMTPVariable() Catch MsgBox("ERROR: Failed to initialize SMTP server. Check settings.") Exit Sub End Try Try smtp.Send(mail) MsgBox("Message sent.") Catch MsgBox("ERROR: Failed to send e-mail.") Exit Sub End Try End Sub Public Function GenMailVariable() As MailMessage Dim mail As New MailMessage()

181

Mail.To.Add(TextBox_To.Text) Mail.From = New MailAddress(TextBox_From.Text) Mail.Subject = TextBox_Subject.Text Mail.Body = TextBox_Message.Text Return Mail End Function Public Function GenSMTPVariable() As SmtpClient Dim smtp As New SmtpClient() smtp.Host = TextBox_SMTPServer.Text smtp.Port = TextBox_Port.Text If CheckBox_Authenticate.Checked Then smtp.Credentials = New NetworkCredential(TextBox_UserName.Text, TextBox_Password.Text) End If If CheckBox_TLS.Checked Then smtp.EnableSsl = True End If Return smtp End Function 'TODO: Write Code for all solvers Private Sub Button_Next_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button_Next.Click Me.Visible = False Select Case ComboBox_Solver.Text Case "ADINA-FSI" Dim form2 As New FSIrunner form2.ShowDialog() Me.Close() Case Else MsgBox("This solver is not yet supported. Sorry") Exit Sub End Select End Sub End Class

182

ADINA Batch (FSIrunner.vb)


Imports System.IO Imports System.Net.Mail Imports System.Net Public Class FSIrunner Dim FluidDATFiles As New ArrayList Dim FluidDAT_FilesOnly As New ArrayList Dim SolidDATFiles As New ArrayList Dim SolidDAT_FilesOnly As New ArrayList Dim RunningIndex As Integer() Dim NextModelPointer As Integer = 0 Dim FileLength1 As Integer() Dim FileLength2 As Integer() Dim RunningOutFiles As String() Dim ListLength As Integer

Private Sub FSIrunner_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load ReDim RunningIndex(My.Settings.NumModels - 1) ReDim FileLength1(My.Settings.NumModels - 1) ReDim FileLength2(My.Settings.NumModels - 1) ReDim RunningOutFiles(My.Settings.NumModels - 1) For i = 0 To My.Settings.NumModels - 1 RunningIndex(i) = -1 Next End Sub '''''''''''''''''''''Fluid Text Box''''''''''''''''''''''' Private Sub Button_GetFluidDAT_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button_GetFluidDAT.Click OpenFileDialog1.InitialDirectory = My.Settings.DATFolder OpenFileDialog1.Filter = "DAT File (*.dat) |*.dat" OpenFileDialog1.Title = "Open Fluid .DAT Files" OpenFileDialog1.Multiselect = True OpenFileDialog1.ShowDialog() My.Settings.DATFolder = Path.GetDirectoryName(OpenFileDialog1.FileName) FluidDATFiles.AddRange(OpenFileDialog1.FileNames) FluidDAT_FilesOnly.AddRange(OpenFileDialog1.SafeFileNames) ListBox_FluidDAT.Items.Clear() ListBox_FluidDAT.Items.AddRange(FluidDAT_FilesOnly.ToArray) End Sub

183

Private Sub ListBox_FluidDAT_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles ListBox_FluidDAT.KeyUp 'deletes selected file if delete is pressed If e.KeyData = Keys.Delete And ListBox_FluidDAT.SelectedIndex <> -1 Then If ListBox_FluidDAT.SelectedIndex < NextModelPointer Then MsgBox("ERROR: Cannot delete run/running models from list") Exit Sub End If FluidDATFiles.RemoveAt(ListBox_FluidDAT.SelectedIndex) FluidDAT_FilesOnly.RemoveAt(ListBox_FluidDAT.SelectedIndex) ListBox_FluidDAT.Items.Clear() ListBox_FluidDAT.Items.AddRange(FluidDAT_FilesOnly.ToArray) End If End Sub Private Sub ListBox_FluidDAT_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ListBox_FluidDAT.DrawItem e.DrawBackground() For i = 0 To RunningIndex.Length - 1 If e.Index = RunningIndex(i) Then e.Graphics.FillRectangle(Brushes.LightGreen, e.Bounds) End If Next If e.Index <> -1 Then Using b As New SolidBrush(e.ForeColor) e.Graphics.DrawString(ListBox_FluidDAT.GetItemText(ListBox_FluidDAT.Items(e.Index) ), e.Font, b, e.Bounds) End Using End If e.DrawFocusRectangle() End Sub

Private Sub ListBox_FluidDAT_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox_FluidDAT.MouseDown If ListBox_FluidDAT.SelectedIndex >= NextModelPointer Then ListBox_FluidDAT.DoDragDrop(ListBox_FluidDAT.Text, DragDropEffects.All) End If End Sub Private Sub ListBox_FluidDAT_DragOver(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox_FluidDAT.DragOver e.Effect = DragDropEffects.Move End Sub

184

Private Sub ListBox_FluidDAT_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox_FluidDAT.DragDrop Dim DragPoint As Integer Dim Destination As Integer Dim Source As Integer DragPoint = ListBox_FluidDAT.IndexFromPoint(ListBox_FluidDAT.PointToClient(New Point(e.X, e.Y))) If DragPoint <> -1 Then Source = ListBox_FluidDAT.SelectedIndex Destination = ListBox_FluidDAT.IndexFromPoint(ListBox_FluidDAT.PointToClient(New Point(e.X, e.Y))) If Source < Destination Then FluidDATFiles.Insert(Destination, FluidDATFiles(Source)) FluidDATFiles.RemoveAt(Source) FluidDAT_FilesOnly.Insert(Destination, FluidDAT_FilesOnly(Source)) FluidDAT_FilesOnly.RemoveAt(Source) Else FluidDATFiles.Insert(Destination, FluidDATFiles(Source)) FluidDATFiles.RemoveAt(Source + 1) FluidDAT_FilesOnly.Insert(Destination, FluidDAT_FilesOnly(Source)) FluidDAT_FilesOnly.RemoveAt(Source + 1) End If ListBox_FluidDAT.Items.Clear() ListBox_FluidDAT.Items.AddRange(FluidDAT_FilesOnly.ToArray) End If End Sub

'''''''''''''''''''''Solid Text Box''''''''''''''''''''''' Private Sub Button_GetSolidDAT_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button_GetSolidDAT.Click OpenFileDialog1.InitialDirectory = My.Settings.DATFolder OpenFileDialog1.Filter = "DAT File (*.dat) |*.dat" OpenFileDialog1.Title = "Open Solid .DAT Files" OpenFileDialog1.Multiselect = True OpenFileDialog1.ShowDialog() My.Settings.DATFolder = Path.GetDirectoryName(OpenFileDialog1.FileName) SolidDATFiles.AddRange(OpenFileDialog1.FileNames) SolidDAT_FilesOnly.AddRange(OpenFileDialog1.SafeFileNames) ListBox_SolidDAT.Items.Clear() ListBox_SolidDAT.Items.AddRange(SolidDAT_FilesOnly.ToArray) End Sub

185

Private Sub ListBox_SolidDAT_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles ListBox_SolidDAT.KeyUp 'deletes selected file if delete is pressed If e.KeyData = Keys.Delete And ListBox_SolidDAT.SelectedIndex <> -1 Then If ListBox_SolidDAT.SelectedIndex < NextModelPointer Then MsgBox("ERROR: Cannot delete run/running models from list") End If SolidDATFiles.RemoveAt(ListBox_SolidDAT.SelectedIndex) SolidDAT_FilesOnly.RemoveAt(ListBox_SolidDAT.SelectedIndex) ListBox_SolidDAT.Items.Clear() ListBox_SolidDAT.Items.AddRange(SolidDAT_FilesOnly.ToArray) End If End Sub Private Sub ListBox_SolidDAT_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ListBox_SolidDAT.DrawItem e.DrawBackground() For i = 0 To RunningIndex.Length - 1 If e.Index = RunningIndex(i) Then e.Graphics.FillRectangle(Brushes.LightGreen, e.Bounds) End If Next If e.Index <> -1 Then Using b As New SolidBrush(e.ForeColor) e.Graphics.DrawString(ListBox_SolidDAT.GetItemText(ListBox_SolidDAT.Items(e.Index) ), e.Font, b, e.Bounds) End Using End If e.DrawFocusRectangle() End Sub Private Sub ListBox_SolidDAT_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox_SolidDAT.MouseDown If ListBox_SolidDAT.SelectedIndex >= NextModelPointer Then ListBox_SolidDAT.DoDragDrop(ListBox_SolidDAT.Text, DragDropEffects.All) End If End Sub Private Sub ListBox_SolidDAT_DragOver(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox_SolidDAT.DragOver e.Effect = DragDropEffects.Move End Sub Private Sub ListBox_SolidDAT_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox_SolidDAT.DragDrop Dim DragPoint As Integer

186

Dim Destination As Integer Dim Source As Integer DragPoint = ListBox_SolidDAT.IndexFromPoint(ListBox_SolidDAT.PointToClient(New Point(e.X, e.Y))) If DragPoint <> -1 Then Source = ListBox_SolidDAT.SelectedIndex Destination = ListBox_SolidDAT.IndexFromPoint(ListBox_SolidDAT.PointToClient(New Point(e.X, e.Y))) If Source < Destination Then SolidDATFiles.Insert(Destination, SolidDATFiles(Source)) SolidDATFiles.RemoveAt(Source) SolidDAT_FilesOnly.Insert(Destination, SolidDAT_FilesOnly(Source)) SolidDAT_FilesOnly.RemoveAt(Source) Else SolidDATFiles.Insert(Destination, SolidDATFiles(Source)) SolidDATFiles.RemoveAt(Source + 1) SolidDAT_FilesOnly.Insert(Destination, SolidDAT_FilesOnly(Source)) SolidDAT_FilesOnly.RemoveAt(Source + 1) End If ListBox_SolidDAT.Items.Clear() ListBox_SolidDAT.Items.AddRange(SolidDAT_FilesOnly.ToArray) End If End Sub

'''''''''''''''''''Execute Commands''''''''''''''''''' Private Sub Button_Run_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button_Run.Click Timer1.Interval = CInt(60000 * My.Settings.SampleRate) Timer1_Tick(sender, e) 'Initialize() Timer1.Enabled = True End Sub

Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick For i = 0 To RunningIndex.Length - 1 If RunningIndex(i) <> -1 Then FileLength2(i) = FileLength1(i) FileLength1(i) = FileLen(RunningOutFiles(i)) If FileLength1(i) - FileLength2(i) = 0 Then

187

RunningIndex(i) = -1 End If End If Next 'Add models to the que If FluidDATFiles.Count > SolidDATFiles.Count Then ListLength = SolidDATFiles.Count Else ListLength = FluidDATFiles.Count End If If NextModelPointer < ListLength Then Initialize() Exit Sub End If 'check if done For i = 0 To RunningIndex.Length - 1 If RunningIndex(i) <> -1 Then Exit Sub End If Next If My.Settings.SendNotification Then Dim mail As New MailMessage() Dim smtp As New SmtpClient() Try mail = GenMailVariable() Catch MsgBox("ERROR: Failed to construct mail object. Check settings.") Exit Sub End Try Try smtp = GenSMTPVariable() Catch MsgBox("ERROR: Failed to initialize SMTP server. Check settings.") Exit Sub End Try Try smtp.Send(mail) Catch MsgBox("ERROR: Failed to send e-mail.") Exit Sub End Try End If Me.Close()

188

End Sub Sub Initialize() Dim ModelDone As Boolean Dim Index As Integer Index = 0 While Index <> RunningIndex.Length If RunningIndex(Index) = -1 And NextModelPointer < ListLength Then ModelDone = True Exit While End If Index = Index + 1 End While 'MsgBox(CStr(RunningIndex(0) & " " & RunningIndex(1) & " " & RunningIndex(2) & " " & RunningIndex(3))) 'MsgBox(CStr(RunningIndex(0))) If ModelDone Then RunningIndex(Index) = NextModelPointer NextModelPointer = NextModelPointer + 1 RunningOutFiles(Index) = Replace(FluidDATFiles(RunningIndex(Index)), ".dat", ".out") FileLength1(Index) = 0 Dim Command As String Command = Chr(34) & My.Settings.ADINAFSIexe & Chr(34) & " -b -s -mm " & CStr(My.Settings.NumMemory) & "mb -t " & CStr(My.Settings.NumProc) & " " & Chr(34) & FluidDATFiles(RunningIndex(Index)) & Chr(34) & " " & Chr(34) & SolidDATFiles(RunningIndex(Index)) & Chr(34) Shell(Command) 'refresh listboxs ListBox_FluidDAT.Items.Clear() ListBox_FluidDAT.Items.AddRange(FluidDAT_FilesOnly.ToArray) ListBox_SolidDAT.Items.Clear() ListBox_SolidDAT.Items.AddRange(SolidDAT_FilesOnly.ToArray) End If End Sub Public Function GenMailVariable() As MailMessage Dim mail As New MailMessage() mail.To.Add(My.Settings.EmailTo) mail.From = New MailAddress(My.Settings.EmailFrom) mail.Subject = My.Settings.EmailSubject mail.Body = My.Settings.EmailMessage Return mail End Function

189

Public Function GenSMTPVariable() As SmtpClient Dim smtp As New SmtpClient() smtp.Host = My.Settings.EmailSMTP smtp.Port = My.Settings.EmailPort If My.Settings.EmailAuthentication Then smtp.Credentials = New NetworkCredential(My.Settings.EmailUsername, My.Settings.EmailPassword) End If If My.Settings.EmailTLS Then smtp.EnableSsl = True End If Return smtp End Function End Class

190

ADINAwithMATLAB (Form1.vb)
Imports System.Diagnostics Public Class Form1 Dim FileLen1 As Integer Dim FileLen2 As Integer Dim FileChg As Integer = 0 Dim TimeLimit As Integer = 10 'minutes measured every minute 'Dim ActiveDIR As String = "C:\Documents and Settings\Mark\Desktop\Automated Testing\Matlab" 'Dim MatlabCmd As String = "Optimize('test.mat')" 'Dim ADINA_AUI As String = "C:\ADINA86\bin\aui.exe" 'Dim ADINA_FSI As String = "C:\ADINA86\bin\adfsi.exe" 'Dim INfile As String = "C:\Documents and Settings\Mark\Desktop\Automated Testing\RunFolder\INfile.in" 'Dim PLOfile As String = "C:\Documents and Settings\Mark\Desktop\Automated Testing\RunFolder\PLOfile.plo" 'Dim DATfile1 As String = "C:\Documents and Settings\Mark\Desktop\Automated Testing\RunFolder\fluid.dat" 'Dim DATfile2 As String = "C:\Documents and Settings\Mark\Desktop\Automated Testing\Models\solid.dat" 'Dim OUTfile As String = "C:\Documents and Settings\Mark\Desktop\Automated Testing\RunFolder\fluid.out" Dim Running As Boolean = False Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click If Not Running Then Running = True Button1.Text = "Stop" PrepWork() Else Running = False Button1.Text = "Start" End If End Sub Public Sub PrepWork() Dim ProcProp1 As New ProcessStartInfo ProcProp1.FileName = "matlab" ProcProp1.Arguments = "-sd " & Chr(34) & My.Settings.MatDir & Chr(34) & " -r " & Chr(34) & My.Settings.MatCmd & Chr(34) & " -nosplash -wait" Dim Proc1 As Process = Process.Start(ProcProp1) Proc1.WaitForExit()

191

If Running Then PreProcess() End If End Sub Public Sub PreProcess() Dim ProcProp2 As New ProcessStartInfo ProcProp2.FileName = Chr(34) & My.Settings.ADINA_AUI & Chr(34) ProcProp2.Arguments = Chr(34) & My.Settings.ADINA_IN & Chr(34) Dim Proc2 As Process = Process.Start(ProcProp2) Threading.Thread.Sleep(60000 * My.Settings.PreProcWait) If Running Then Solver() End If End Sub Public Sub Solver() Dim ProcProp3 As New ProcessStartInfo ProcProp3.FileName = Chr(34) & My.Settings.ADINA_FSI & Chr(34) ProcProp3.Arguments = "-b -s -mm " & CStr(My.Settings.MemUsage) & "gb -t " & CStr(My.Settings.NumProc) & " " & Chr(34) & My.Settings.FluidDAT & Chr(34) & " " & Chr(34) & My.Settings.SolidDAT & Chr(34) Dim Proc3 As Process = Process.Start(ProcProp3) FileLen1 = 0 Timer.Interval = 60000 '1 minute Timer.Enabled = True End Sub

Private Sub Timer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer.Tick 'Re-evaluate file size FileLen2 = FileLen1 FileLen1 = FileLen(My.Settings.ADINA_OUT) If FileLen2 - FileLen1 = 0 Then 'If file doesnt change size flag it FileChg = FileChg + 1 If FileChg >= My.Settings.SolverThresh Then Timer.Enabled = False If Running Then PostProcess() End If End If Else 'if program changes in size reset flag FileChg = 0

192

End If End Sub Public Sub PostProcess() Dim ProcProp4 As New ProcessStartInfo ProcProp4.FileName = Chr(34) & My.Settings.ADINA_AUI & Chr(34) ProcProp4.Arguments = Chr(34) & My.Settings.ADINA_PLO & Chr(34) Dim Proc4 As Process = Process.Start(ProcProp4) Threading.Thread.Sleep(60000 * My.Settings.PostProcWait) If Running Then PrepWork() End If End Sub

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load TextBox_MatDir.Text = My.Settings.MatDir TextBox_MatCmd.Text = My.Settings.MatCmd TextBox_AUI.Text = My.Settings.ADINA_AUI TextBox_FSI.Text = My.Settings.ADINA_FSI TextBox_IN.Text = My.Settings.ADINA_IN TextBox_OUT.Text = My.Settings.ADINA_OUT TextBox_PLO.Text = My.Settings.ADINA_PLO TextBox_FluidDAT.Text = My.Settings.FluidDAT TextBox_SolidDAT.Text = My.Settings.SolidDAT TextBox_PreProcWait.Text = CStr(My.Settings.PreProcWait) TextBox_PostProcWait.Text = CStr(My.Settings.PostProcWait) TextBox_SolverThresh.Text = CStr(My.Settings.SolverThresh) TextBox_NumProc.Text = CStr(My.Settings.NumProc) TextBox_MemUsage.Text = CStr(My.Settings.MemUsage) End Sub Private Sub TextBox_MatDir_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox_MatDir.TextChanged Try My.Settings.MatDir = TextBox_MatDir.Text Catch ex As Exception Beep() End Try TextBox_MatDir.Text = My.Settings.MatDir End Sub Private Sub TextBox_MatCmd_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox_MatCmd.TextChanged Try My.Settings.MatCmd = TextBox_MatCmd.Text Catch ex As Exception Beep()

193

End Try TextBox_MatDir.Text = My.Settings.MatDir End Sub Private Sub TextBox_AUI_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox_AUI.TextChanged Try My.Settings.ADINA_AUI = TextBox_AUI.Text Catch ex As Exception Beep() End Try TextBox_AUI.Text = My.Settings.ADINA_AUI End Sub Private Sub TextBox_FSI_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox_FSI.TextChanged Try My.Settings.ADINA_FSI = TextBox_FSI.Text Catch ex As Exception Beep() End Try TextBox_FSI.Text = My.Settings.ADINA_FSI End Sub Private Sub TextBox_IN_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox_IN.TextChanged Try My.Settings.ADINA_IN = TextBox_IN.Text Catch ex As Exception Beep() End Try TextBox_IN.Text = My.Settings.ADINA_IN End Sub Private Sub TextBox_PLO_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox_PLO.TextChanged Try My.Settings.ADINA_PLO = TextBox_PLO.Text Catch ex As Exception Beep() End Try TextBox_PLO.Text = My.Settings.ADINA_PLO End Sub Private Sub TextBox_OUT_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox_OUT.TextChanged Try My.Settings.ADINA_OUT = TextBox_OUT.Text Catch ex As Exception Beep()

194

End Try TextBox_OUT.Text = My.Settings.ADINA_OUT End Sub Private Sub TextBox_FluidDAT_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox_FluidDAT.TextChanged Try My.Settings.FluidDAT = TextBox_FluidDAT.Text Catch ex As Exception Beep() End Try TextBox_FluidDAT.Text = My.Settings.FluidDAT End Sub Private Sub TextBox_SolidDAT_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox_SolidDAT.TextChanged Try My.Settings.SolidDAT = TextBox_SolidDAT.Text Catch ex As Exception Beep() End Try TextBox_SolidDAT.Text = My.Settings.SolidDAT End Sub Private Sub TextBox_PreProcWait_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox_PreProcWait.TextChanged Try My.Settings.PreProcWait = CInt(TextBox_PreProcWait.Text) Catch ex As Exception Beep() End Try TextBox_PreProcWait.Text = CStr(My.Settings.PreProcWait) End Sub Private Sub TextBox_SolverThresh_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox_SolverThresh.TextChanged Try My.Settings.SolverThresh = CInt(TextBox_SolverThresh.Text) Catch ex As Exception Beep() End Try TextBox_SolverThresh.Text = CStr(My.Settings.SolverThresh) End Sub Private Sub TextBox_NumProc_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox_NumProc.TextChanged Try My.Settings.NumProc = CInt(TextBox_NumProc.Text) Catch ex As Exception Beep()

195

End Try TextBox_NumProc.Text = CStr(My.Settings.NumProc) End Sub Private Sub TextBox_MemUsage_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox_MemUsage.TextChanged Try My.Settings.MemUsage = CInt(TextBox_MemUsage.Text) Catch ex As Exception Beep() End Try TextBox_MemUsage.Text = CStr(My.Settings.MemUsage) End Sub Private Sub TextBox_PostProcWait_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox_PostProcWait.TextChanged Try My.Settings.PostProcWait = CInt(TextBox_PostProcWait.Text) Catch ex As Exception Beep() End Try TextBox_PostProcWait.Text = CStr(My.Settings.PostProcWait) End Sub End Class

196

Console Mode Notifier (Module1.vb)


Imports System.Threading Module Module1 Dim file_location As String Dim sampling_min As Integer Dim sampling_ms As Integer Dim threshold As Integer executing Dim DOScommand As String Dim Dim Dim Dim Dim 'location of file being checked for size 'sample rate in minutes 'sample rate in miliseconds 'number of consecutive positve samplings before 'command to be executed when file stops growing

fileinfo1 As System.IO.FileInfo fileinfo2 As System.IO.FileInfo sizechange As Integer consecutive As Integer = 0 testrun As String

Sub Main() System.Console.Write("Locate File: ") file_location = System.Console.ReadLine System.Console.Write("Sample Rate (min): ") sampling_min = System.Console.ReadLine sampling_ms = sampling_min * 60000 System.Console.Write("How Many Positive Samples Before Executing: ") threshold = System.Console.ReadLine System.Console.Write("DOS Command to execute at completion:") DOScommand = System.Console.ReadLine System.Console.Write("Test command? (y/n)") testrun = System.Console.ReadLine If testrun = "y" Then System.Console.WriteLine("Performing Test Run...") Shell(DOScommand) End If System.Console.WriteLine("Beginning File Monitoring at " & Now) fileinfo1 = My.Computer.FileSystem.GetFileInfo(file_location) Thread.Sleep(sampling_ms) Do fileinfo2 = fileinfo1 fileinfo1 = My.Computer.FileSystem.GetFileInfo(file_location) sizechange = fileinfo2.Length - fileinfo1.Length If sizechange = 0 Then

197

consecutive = consecutive + 1 System.Console.WriteLine(Now & " File Unchanged for " & consecutive * sampling_min & " minute(s)") If consecutive >= threshold Then System.Console.WriteLine("Threshold Reached. Executing Command and Terminating") Shell(DOScommand) Exit Do End If Else consecutive = 0 End If Thread.Sleep(sampling_ms) Loop Until False Thread.Sleep(60000) System.Console.WriteLine("Finished!") System.Console.Read() End Sub End Module

198

GUI Model Notifier (Form1.vb)


Imports System.Net.Mail Imports System.Net

Public Class Form1 Dim mail As New MailMessage() Dim smtp As New SmtpClient() Dim FileLen1 As Integer Dim FileLen2 As Integer Dim Change As Integer Dim Consecutive As Integer = 0

Private Sub Form1_HandleCreated(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.HandleCreated FileLocation.Text = My.Settings.FileLoc SampRate.Text = My.Settings.SampRate SampThresh.Text = My.Settings.SampThresh ToBox.Text = My.Settings.EmailTo FromBox.Text = My.Settings.EmailFrom SubBox.Text = My.Settings.EmailSub MessBox.Text = My.Settings.EmailMess TestBox.Checked = My.Settings.EmailTest SMTPBox.Text = My.Settings.EmailSMTP PortBox.Text = My.Settings.EmailPort TLSBox.Checked = My.Settings.EmailTLS AuthBox.Checked = My.Settings.EmailAuth UserBox.Text = My.Settings.EmailUser PassBox.Text = My.Settings.EmailPass CloseBox.Checked = My.Settings.CloseProg End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BrowseButton.Click OpenFileDialog1.InitialDirectory = My.Settings.FileLoc OpenFileDialog1.Title = "Select file to monitor" OpenFileDialog1.ShowDialog() FileLocation.Text = OpenFileDialog1.FileName End Sub Private Sub AuthBox_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AuthBox.CheckedChanged If AuthBox.Checked = True Then UserBox.Enabled = True PassBox.Enabled = True Else

199

UserBox.Enabled = False PassBox.Enabled = False End If End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RunButton.Click 'Check inputs for errors If Dir(FileLocation.Text) = "" Then MsgBox("ERROR: Monitored File Does Not Exist") Exit Sub End If If Val(SampRate.Text) Mod 1 <> 0 Or Val(SampRate.Text) <= 0 Then MsgBox("ERROR: 'Sample Rate' must be an integer") Exit Sub End If If Val(SampThresh.Text) Mod 1 <> 0 Or Val(SampRate.Text) <= 0 Then MsgBox("ERROR: 'Sample Threshold' must be an integer") Exit Sub End If If Val(PortBox.Text) Mod 1 <> 0 Or Val(PortBox.Text) <= 0 Then MsgBox("ERROR: 'Port' must be an integer") Exit Sub End If LogBox.Text = LogBox.Text & Now & " Program Started" LogBox.SelectionStart = LogBox.Text.Length LogBox.ScrollToCaret() LogBox.Refresh() TabControl1.SelectedTab = TabPage3 'Read in mailing commands Try mail.To.Add(ToBox.Text) Catch MsgBox("ERROR: 'To' Address is not valid") Exit Sub End Try Try mail.From = New MailAddress(FromBox.Text) Catch MsgBox("ERROR: 'From' Address is not valid") Exit Sub End Try mail.Subject = SubBox.Text mail.Body = MessBox.Text smtp.Host = SMTPBox.Text

200

smtp.Port = PortBox.Text If AuthBox.Checked Then smtp.Credentials = New NetworkCredential(UserBox.Text, PassBox.Text) End If If TLSBox.Checked Then smtp.EnableSsl = True End If 'Test Run If TestBox.Checked Then Try smtp.Send(mail) Catch MsgBox("ERROR: Failed to send e-mail.") Exit Sub End Try LogBox.Text = LogBox.Text & vbNewLine & Now & " successfully" LogBox.Refresh() End If 'Save Settings If SaveSettings.Checked Then My.Settings.FileLoc = FileLocation.Text My.Settings.SampRate = SampRate.Text My.Settings.SampThresh = SampThresh.Text My.Settings.EmailTo = ToBox.Text My.Settings.EmailFrom = FromBox.Text My.Settings.EmailSub = SubBox.Text My.Settings.EmailMess = MessBox.Text My.Settings.EmailTest = TestBox.Checked My.Settings.EmailSMTP = SMTPBox.Text My.Settings.EmailPort = PortBox.Text My.Settings.EmailTLS = TLSBox.Checked My.Settings.EmailAuth = AuthBox.Checked My.Settings.EmailUser = UserBox.Text My.Settings.EmailPass = PassBox.Text My.Settings.CloseProg = CloseBox.Checked My.Settings.Save() End If 'Begin Monitoring LogBox.Text = LogBox.Text & vbNewLine & Now & " Monitoring..." LogBox.SelectionStart = LogBox.Text.Length LogBox.ScrollToCaret() LogBox.Refresh() Timer.Interval = 60000 * Val(SampRate.Text) FileLen1 = FileLen(FileLocation.Text) Timer.Enabled = True

Test email sent

Beginning

201

End Sub Private Sub Timer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer.Tick 'Re-evaluate file size FileLen2 = FileLen1 FileLen1 = FileLen(FileLocation.Text) Change = FileLen2 - FileLen1 If Change = 0 Then 'If file doesnt change size flag it Consecutive = Consecutive + 1 LogBox.Text = LogBox.Text & vbNewLine & Now & " File unchanged for " & Consecutive * Timer.Interval / 60000 & " minute(s)" LogBox.SelectionStart = LogBox.Text.Length LogBox.ScrollToCaret() LogBox.Refresh() If Consecutive >= Val(SampThresh.Text) Then 'if flags exceed threshold execute command LogBox.Text = LogBox.Text & vbNewLine & "Threshold reached. Executing..." LogBox.SelectionStart = LogBox.Text.Length LogBox.ScrollToCaret() LogBox.Refresh() smtp.Send(mail) Timer.Enabled = False 'close application If CloseBox.Checked = True Then Application.Exit() Else Consecutive = 0 LogBox.Text = LogBox.Text & vbNewLine & "Done!" & vbNewLine & vbNewLine LogBox.SelectionStart = LogBox.Text.Length LogBox.ScrollToCaret() LogBox.Refresh() End If End If Else 'if program changes in size reset flag Consecutive = 0 End If End Sub End Class

202

S-ar putea să vă placă și