 And so, one of the places where Python actually excels is actually becoming what they term glue that takes disparate parts or pieces from different applications and puts them together in a single application. And in fact, you've already done this, you haven't done the interfacing process, but if you've called anything in NumPy, you're calling down into C. If you're calling anything in SciPy, you're either calling C or Fortran. So all of our applications are very much a conglomeration of C, Python and Fortran, even if we're just using things at the Python level. And that's nice because we don't have to debug all those old algorithms that are in the Python or SciPy libraries. Well, you often do get a legacy application. So what I'm going to go over here is a case study of taking a Fortran 90, existing Fortran 90 application, and how do you transition it into Python. And hopefully this will give you an idea of how to deal with a legacy application, the issues that will come up, especially with Fortran. We'll focus on that a little bit. But also, some of them are the same as C, but also it'll give you an idea of how to construct any new applications that you might have. You have at the center of things the thing that really matters. And in this case I've pointed out it's a serial electromagnetic solver, meaning it runs on a single CPU. This one actually, there is also a parallel EM solver that can be plugged in just as easily. And the code I'll talk about here, that we had two versions of this code, one that had a less advanced algorithm, but worked very well for small problems. One had more advanced algorithm that worked well for large problems. I'm going to talk about the simpler one, the smaller code, when we get into examples on things. But so we have this kernel here, and then look around it. You probably, in this case, we have the serial electromagnetic solver, but it needs, this particular problem is solving for the radar cross-section of a landmine or something like that buried in the ground. And it's a frequency domain solver, so you solve it 100 megahertz, 110 megahertz, 120 megahertz. And what you need to do then is take that into the time domain, right, and you can convolve it with some signal to find out what the actual response in the time domain, if you send a ping in, radar ping into this thing, what's the response going to be? Well, so what do you have there? Well, you have a convolution that you're dealing with, you have some FFTs that you're dealing with to be able to go back and forth. Well, all of this stuff had to be added into this application as well, right? So that's your signal processing piece down here. There are also a lot of special functions. There's the simulation, but there's a lot of setup for the simulation. And, you know, you have problems where you have material values, and in this case, we're solving a problem that really looks like. So we're looking at problem sets where you may have some target buried under the earth, and so you have material one, material two, and these have some mu and epsilon, and some loss associated with them. So these are just electromagnetic parameters. I mean, you can take this to be a fluid dynamics problem or whatever you want. You have some material values, very common. So we have an environment, right? We have a material one, material two, and then you have a source located up here, beams down the signal, and this is going to be some shape like this or whatever pulse that comes out. And then when it hits, it's going to come back, or maybe it goes to another place and you have another signal that comes off of it. Very typical problem that we all kind of might work on in various places. But if you look at this problem, you know that there's a lot of pieces of this to, when you're setting up this problem set, you have to calculate a lot of properties based on these material values and the environment that it's in, this half space. So there's a lot of calculation of special functions like Bessel functions and Hankel functions that go into it as well. Well, again, all of those have to be included in this Fortran code and you have to get all of that working. Along the way is the, to get an application running, usually this is a typical Linux or Unix program that has text input and text output, right? And that's kind of nice. It's actually, that process worked very well for a long time and still works very well. I mean, you have all these little scripts like SED and AUK and all of these things and you just end up piping, you make your workflow and by piping all these little programs together. That's a nice way of doing that and so this really has the same sort of flavor in that there's some text command input file that's read in by this and that sets up what are these material values? What is that signal? What is the, the, the frequency of that signal? Things like that. There's also, you know, you have a target down here that whatever's buried in the ground, that target has a mesh associated with it. And so the shape of that thing, you have to be able to read that in. So you need to read in ASCII files or whatever format some CAD tool might put out so you can read those into your tool. Well, in Fortran 90 you're going to have to write an interpreter to read this file. You have to write one to read this file. Those are just string processing really tools and not very complex and then you have to output files here or at least they're initially not very complex and then you might output a whole lot of plots and in this case we format them in a tech plot format. That's just some file format so they could be read into tech plot but you could read these into anything. So I think this is a very common structure and one of the issues here is right here, this file interpreter. That guy, one of the comments by, I can't remember the guy's name now. He wrote one of the first HCI books that everybody uses but he made the comment that in every large C application, we can substitute Fortran application here, you end up with half of an implementation of a list interpreter, right? And the comment there is that you always want to do things, you always kind of want to write your own little programming language to control how you're programming or you're at program executes. And so initially this text file starts with a start, you can say start frequency equals 100 megahertz, stop frequency equals 300 and step value equals 10 megahertz or whatever. Then you go, well really, I don't want to specify those, I want to have them based on the wavelength of the target size or something like that. So then you start going in, you say start frequency equals lambda times x or whatever it might be. So you go, well I can do that, I know how to do that and I'll just write into my little interpreter down here, something that recognizes these little expressions, parses them and does the math on them. And then it just starts ballooning and all of a sudden you are not a language designer, you're not Guido, you're not Larry Wall and who wrote Perl. And so you end up with this kind of not very good format that somebody has to learn a new language to use your tool. Whenever there are any bugs in your application, half the time they're reported right in here because it doesn't understand the syntax as well or it doesn't handle new line characters from a Macintosh or whatever it may be. This thing grows into a big hair ball. So how can you get around this? Well the thought is, one way of thinking about this is this Fortran program has a main that reads in this stuff. What happens if instead of having that main you just get rid of main altogether and you say Python is my main? All I'm going to do is write Python scripts and have Python execute those scripts. So that's the whole idea here is that you write Python scripts and that Python script replaces this whole thing right here. You don't have this anymore. It becomes Python. Well you've all of a sudden gotten rid of a lot of bugs. You've gotten rid of a lot of implementation that you have to do. And 10 to one, it's a better language than the one that you are going to write. And now you have all these other capabilities of all these other libraries that you can bring in immediately. And you want to make use of that. So how do you do this? Well the first thing to do is to recognize this segmentation and hopefully you have a program that allows you to do this. This program was beautifully written. It had a lot of issues from a wrapping side but for Fortran 90 this was a beautiful application. And it was well segmented other than having global variables that we'll talk about in a minute. But we can pull out this piece and then we can wrap it. So we'll talk about how you put that wrapper over the top and that's available now. And then the next thing that you do is you know you still are going to have to read in these meshes. But Fortran 90 is probably not your favorite string processing language. You can pull that out and just write it in a short amount of Python and make it an object of a handy thing where you can probably do more work with it. For example, we made mesh objects that you could say translate and it would just move the object around by x, y, z values. Before you had to use an external program to read in the file, do a translation on it, write out a new place just to take a target and either raise it or lower it in the earth. So it's kind of painful. And then you're going to have to have some code here to do the electro magnetics and handle the materials and the layers and things like that that are domain specific to your problem. So there's some domain specific objects that are methods or functions or classes that you're going to have to write for your problem. And then you have all the generic modules down here to make use of to help you with visualization, sometimes to help you with parallelization, signal processing, web integration, things like that. So this is the goal. I mean, this is where you're going or one approach. And here's just a simple script that we ended up with to run this. So this is our main in some sense. And here we have a library called EM from EM dot material, you import a couple of materials. So we're going to have air in the sky, and then some soil on the ground. And then we grab a standard mesh here out of the library of standard meshes. We create a layered media just by creating a list here like of air and soil. And then we create an environment on these layers, right? We the environment we have this class called layered media that really represents the problem domain we're in. And then we create a solver. In this case, it's a method of moment solver, where we pass in the environment that we created and the mesh that we read in. And now we just call this solve. We loop over a set of frequencies. We create a source that's a plane wave in this case, specify in spherical coordinates where it's coming from and what its frequency is. We solve for the currents from the solver and then we solve for the radar cross section. And then we print that out. So if you look at this, even for people that don't know the problem set, it's fairly readable as you go through it. And more than that, you can probably think about whatever problem you're solving. And it's not very different than this. You know, you can think about making your solver an object and that sort of thing. So this is what it became. It's a fairly nice interface. And if I don't expect you to read this at all, but this is a slightly larger script. And I just listed at the top what it does. Here we're going to do a parallel simulation. So you remember I said we have multiple frequencies. And each of those frequencies can be handled independently. So if you have a cluster of machines, you can, it's called embarrassingly parallel. It's really simple, just a segment. If you have 10 machines and 100 frequencies, give each machine 10 frequencies, have them solve it, give you back the results. You just cut your time pretty much by a factor of 10. So you do, so this does a parallel solving that way, in this case using Cal. And then it creates a plot using plot commands. It then builds an HTML page. That's over here where we're just, this is recreating the plot through here. And then we create an HTML page just by writing out some little HTML code. And then right here we're uploading using FTP. We're sending that to a server. We also update the upload the image or the plot that we created. And down here we're sending an email to a list of your collaborators, saying the simulation just finished. These simulations ran for a long, long time, right, for a day or so. So this is just something at the end. It's just going to email you and tell you, hey, the simulation's over, and here's the web page to go view the results. So if you, that's not that much code to have all of that stuff done, right? And you think about trying to do that in Fortran or in C, and you're probably not. I mean, you're just not going to choose to do that. You're going to probably chain together a lot of tools outside. You know, the issues that come up with Fortran are Fortran 77, which most Fortran programmers learned on. It really encourages people to create global variables. You know, when you create your main, you know, right before that you create a common block with a lot of variables in it. And so there's an issue with this. It causes states to occur. When you call a function, you're writing something to some global state that's not encapsulated. They use the person who called the function, doesn't know everything about what's changed in your application. So that's the first part. The second thing is when you have globals and you have this global state, it really makes threading difficult. Threading means executing two versions, or calling an algorithm in the same process and having execution occur there in both of these. And if you call the same method in two different threads and they're both writing to a global variable, one of them is going to stomp on the other one and then when the other one goes to the next thing and it's expecting to have the global variable set to a value, you're in trouble. So we'll talk about that. The other problem is I had mentioned that the code we dealt with was actually very nice and it was. Still, it had fairly large routines. And if you look at a lot of Fortran applications and see as well, I mean, these really aren't specific to Fortran, you see the same issues in C, right? When you're dealing with people's code, you'll end up with a main that's 10,000 lines long sometimes. And you go and look for a subroutine call and there aren't any. They've just written their whole application with for loop, for loop, for loop, if, if, for the whole things in one module. Bad idea. It's not very modular. It doesn't, when somebody comes and reads the code, it's hard for them to figure it out. It's very hard to debug. And it's extremely hard to wrap because when you wrap, what you want to do is you want to split all these things out. And if you have in the same routine, the part where you read, calculate, write, where do you break this thing, right? Somebody has to go in and figure out how do I slice and dice this function into pieces that can be used. So that's one of the other things that you have to deal with. Fortran 77 doesn't have memory allocation. This actually is a benefit for us because that means that we do all the allocation in Python. We don't have to deal with somebody else having allocated memory and we have to do the dealloc and all of that sort of stuff or, or coordinate with the, the memory allocation of the underlying program. And then this last one, Fortran 90 is the new Fortran that everybody is starting to use, which is, I think they're, the spec is probably the 2000 or 2005. But Fortran 90, 17 years later, is really the one that seems to be the spec that everybody's using. It's amazing how long it takes for things to catch on. The Fortran 90 has, has an object-oriented flair to it. I mean it is an object-oriented language. The problem is that they haven't tightened down the spec of the language very much and that was intentional so that Cray and SGI and Sun could be, could tailor the, the underlying data structures in the Fortran 90 to be optimal on their machines. And so as a result, when you move these data structures across compilers and even across, and across architectures, things aren't in the same spots in memory. They've, they've, if they've defined an array in one place they'll put the strides and the dimensions in one order and the other one they might put them in a different place. And as a result, when you're trying to interface this into C, which is, we always make the jump from Fortran to C to Python. You always have to write the C side. It's very difficult to wrap these things. There's some tools to help with this. There's one out of Lawrence Livermore, or excuse me, out of Los Alamos by Craig Rasmussen that helps with this. And we've used it to some good use, but it's still an issue. So my preference is to wrap Fortran 77 if I can avoid Fortran 90. So I talked about global variables and the problem. Just to give you an idea of the problem, imagine we've wrapped a library and we've called it F90 module and we have two functions in there. And one's called F1 and one's called F2. So you call F1, you pass in five and six, and you get a variable back and we call it A. That's fine, and Danny, that works well, but unbeknownst to you, under the covers in the common variable block, ZZZ was set equal to five. All right, so this was happened in the Fortran code and they've stored that value out into inside the Fortran library. Now you call F2 with A and you get some result XXX. And that's fine. It all worked exactly as you wanted, but under the covers, this Fortran 2 not only took A, but it actually also used this ZZZ variable that it grabbed out of the global memory in the Fortran application. All right, now what happens in a scripting environment? We've given control over to users, right, in a scripting environment. They have a lot more capability here. Well, so they call this to get into the internals of your program if you allow them to. So here they call F1 with five and six, great, they get A and ZZZ was set to five. Then they go, you know, actually that's not what I expected A to B. Maybe they plotted it or whatever and they go, what happens if I try calling F1 with 20 and 30? They get some new value and they look at it. Oh, that's what was going on. I understand now. Okay, well I'll just call F2 now with the values that I got from A. All of a sudden you get, you don't get XXX, which is what you expected to get. You get YYY. And the reason is, is this F1 call set ZZZ equal to 20 and now F2 is using that ZZZ variable. Well, this is a problem, right? I mean, what you've really done, the way to look at this is that F1 actually has two outputs. It has A and ZZZ as outputs. And F1, F2 here has two inputs, A and ZZZ. And so we haven't exposed one of the inputs or outputs, inputs and outputs to these functions. So how do you deal with this? Well, there's several ways of doing it. I hear that I, there are really three and I've only listed two. I'll talk about the third. But you can create a wrapper. So if this is a problem, I mean really what we've done is F1 and F2 are paired, right? They, they, they aren't independent of one another. So what you can do is just say, listen, I'm not going to let the user call F1 and F2 independently. I'm just going to put a wrapper function around this called wrapper for F1 and F2. Nice name. And call it with A and B. And it's going to return the result that was returned by F2. So that's one way of doing this. Now, you've removed some capability from your user if they needed to be able to call these separately, but you've also prevented them from making very difficult to debug problems. Something that they would never be able to figure out just by looking at the Python code. So that's one thing. The, the second thing is, it's actually, I guess I'll inject a second one in between here. It's also possible to actually create wrappers that set the global variable. So we could, we could make a wrapper for F1 that actually returned A and Z. So in our wrapper code, we can do anything we want. I mean, it's, it's code, right? So what we can do is we can call F1, we get A, and then we grab ZZZ out of the global variable. And we return both of those to the end user. So from Python, you don't notice that there was that global variable. And we wrap F2 in the same way where we passed in A and ZZZ. This works pretty well. In many cases, the places where it's tricky is in a parallel application. Because if you do this in parallel, you don't have control over, if somebody calls F1 at the same time, and then F2 at the same time, there's no guarantee about what the value of ZZZ is for either of these guys, right? They can change in the middle of the stream, so to speak. So that solves one of the problems, but it doesn't solve the threading problem. So the second value, or second approach that I list here, is actually the best. And that's to get rid of global variables. Go back through the code, read through it, find out where they have global variables, and then make them part of the function signatures, so that you don't have these side effects that are being created. So if you do that, then now what you're going to do, I've added more of it, if you call function 1, maybe it's actually setting a whole lot of variables. Instead of just returning A, you're going to have to return B, C, and D. And then when you call function 2, you actually pass in these B, C, and Ds. And you've created, I mean, this is the for-train code has been changed to do this. The issue with this one is, it's a lot of work a lot of the time. I mean, you have to understand the code, find out where these things can be changed, or where they need to be changed. And more than that, you have to have privileges to change the code. If you're working in, you know, XYZ corporation, and they have their numerical simulator for casting steel, or whatever it is, and they've used it for 20 years, and you come in and you say, oh, I just need to change about, you know, 2,000 lines of your code to make it this way. Chances are, they're going to look at you and ask you what planet you're on. You know, they're not going to let you do this. And so you may not have, you may not have the opportunity to change the underlying code. You have to do every thing that you can do at the wrapper level, and sit on top of the code. So that's, these are common problems across every code base you're going to run into. If you have control of the code, then reworking it is very, that's the best solution. So this is the easiest. The second one that I talked about is the second easiest, you know where you grab the variable out of common. You have to do more work because you have to know what the global variables are, and that sort of thing. But it doesn't work with threading. The third one is the hardest but gives you the most flexibility when you're programming with it. The second one is, again, a hard problem to deal with. I mean what do you do when you get a program that's not modular? You get the main that's 10,000 lines. Well, they're really two solutions, and we don't, we're not going to talk about one of them. I'll do it right now just to cover it. But one of the choices here is to punt. I mean say, you know, I can't, we can't wrap this. There's no way to get in. There's no place to hook into this application. Worse than that, they named all the variables A, B, C, D, E, F, G, H, I, J, K, L, M, and O, P, and I can't tell what this thing's supposed to be doing anywhere. And so there's no hope of restructuring the program. And you say, you know, the best thing I can do is, it does, the great thing here is, it does have an input file. It does have an output file. I'm just going to write Python programs that generate this input file, do a shell call out to make this a sub process, launch the application, have it read that file, or create a pipe to send the values to it, then read the values back out of a pipe or off of another file back into your Python file. And when you get those values, you can parse them out and do whatever calculations you need to do. That is a very, I mean, we do that quite a bit. In fact, I did a whole lot of that in parallel with this application, with another application I couldn't figure out how to split up. The issue with it is, though, the program I'm talking about that I used, you know, executions would happen in a second or two. And so they were very fast. And I went down, or they didn't, my Python application, I went down and I started timing things and looking at how long actually my heavy, you know, numeric calculation, linear algebra, filling up matrices, doing the linear algebra, getting the results back, how much of the time was being spent there versus writing to the file system, reading from the file system, parsing ASCII values back into floating point. And my simulation was 10% of the time and 90% of the time was spent with the file I owe, writing to disk and reading back and interpreting the ASCII file. So I just took a factor of 10 hit on my application by having to call out and do this process. On the other hand, computer time is really cheap. My time is very, very valuable. And I didn't want to spend the time trying to figure out how to split this thing up. So in some cases, that's the right trade-off to make. It just depends. So we had the four trend, you know, we had this issue. Now, if you have the opportunity to go in, guaranteed if you get to go in and split up this application, you have the patience to do it, you're going to end up with a better application. There are multiple reasons for this. One, if you split things out of into actual routines when somebody comes through and reads it, they can see, read file, you know, execute, fill matrix, solve matrix, write file. It's very readable. When something's broken, they know where to go look. And so that's very valuable. It's much more readable. Second, it's much more testable. And this is a, we're not, we are not doing anything on software engineering in this class, which is a shame to me. I mean, that's one of the places I think that we need to have more classes about. How do you, you know, we teach kind of the techniques about how do you do this, but then you go back to work and you sit down and it's like, how do I do this whole process of getting code checked into a subversion or a source repository? How do I structure my code so it's well tested, make it where I have a robust application? Well, this is one of the things where you can improve the testability of your application. If you split it up into these modules, you can write what are called unit tests that exercise these things. I mean, if you have a solve method for your linear algebra, you can have some routines that are separate from your main application that just go through, run several problems that you know the solution to, run those through and get the results and check it against that. Then when somebody comes in, goes you know, I know a faster way of doing this linear algebra. I'm going to go in and change the code to do it. You have a set of tests that you can run immediately and you can catch a bug that they may have put in or they can be more confident that the application is going to run. So if you can split these things up, it's a better approach anyways. And you have a nicer wrapper. The more modular you have things, the more places you give for an individual to hook in. So just as a point, you know, if we look at this application I talked about, many thousand lines of code. But if you looked in there, there were really three methods that mattered. This is like an AX times, AX equals B type problem. So you have to fill in, and it's actually instead of AX equals B, it's Zi equals B. And the impedance matrix times the current equals the driving voltages. So we have to fill in the impedance matrix. We have to fill in the voltages. And then once we get the currents that are on all of these mesh elements, we need to calculate what the far field is. So there are three things we have to do. All right. All the other things we can do, I mean, once we have Zi equals V, we don't need to use any of the linear algebra in the application. We can just use Laypack, the tools that we have, and the FFTs for the far field, all of that kind of stuff, or for the signal processing, all of that sort of stuff can be done with tools in Python. So now that we've decided what we want to do, we know that we have these functions that are, we only have three functions to wrap. Well let's look at the argument list for one of these functions. Here is one of those functions. And this is, you know, triangle patch mass, half space, I can't remember what PWI is, combined field integral equation V drive. So that's a fairly long name, but it's nice, it's descriptive, we know what it's supposed to do if you're working on the code anyways. But look at its argument list. There are 21 arguments to this. So you think about, hey, I'm going to wrap this thing and expose it to a user. And the user looks at this, and they're like, I don't know what to do with this. You know, what are these 21 parameters? And so it's kind of painful to look at, on the other hand, from a wrapping standpoint, you're happy to see this, right? At least there's not one parameter in 20 globals, right? I mean that would be the other option here. So at least they're passing everything in. All right, so we have that. Well let's look at this. I mean if we break this down, one of these arguments affects the algorithm, the field integral equation. A few of these are describing the source, its location, its frequency, and things like that. A few are in the environment, the material values, whether it's a half space, things like that. This orange section here is describing the actual target, the mesh. How big is the mesh? Do we look in here, how many elements are, how many vertices are in the mesh? Or this is the, the array of vertices, x, y, z, n by 3 array, basically. And then how big is that array, right? How many elements? You always have kind of, when you're passing arrays in Fortran or C, you have the array and then the size of the array that you pass in most of the time. So that's what we're passing in here. And then the blue values, we had multiple returns, so this is, you know, these are the, the, the voltages and then the error messages that would be returned from this function. If we just wrap this with Python, we'll talk about this in Fortran, what can we do? Well the first thing that we can do is we have numpy arrays, right? They encode all that extra information we're talking about, about links and things like that. So we just have to pass in those values. So that reduces, kind of, cuts at least our arrays. Instead of having two variables per array, we have one variable per array when we look at our Python version. The second thing is we can move those output variables to be returned. A user doesn't have to know that they have to pass in four variables for, to get the output variables. We can just fill those in and return them to them. So now we've gotten down to nine arguments, which is better, but it's still quite large. I mean, that's a lot to know. So what do we do to solve this? Well, the next step is object-oriented programming and looking at classes. And what you can do is really look at your problem set and find, think about what are the nouns that I'm dealing with if I think about my problem. In this case, we had an environment, right? Where we defined whether we had a free space or a half-space problem. And then in part of our environment was the material, material values, like what are the electromagnetic properties? And then we had a target, or our mesh. In this case, you know, you could, we'll call it a mesh in this case. And then we have a source that's that's going to act on this. And then we actually have our algorithm that needs to be used to compute these things. So if we think about these five items as our domain objects that we're going to work with, then we can build classes for those and maybe cut down the size of our scripts. And this is the script that I showed, I think, pretty much at the beginning, or very similar. And so this is what you end up with now instead of doing those nine parameters into those functions. What we can do is set up our problem set and this is what a user would see if they were going to write it. And as you read through this, you have to get your mesh. And you remember I mentioned being able to offset meshes around. You can just put in XYZ offsets. We bury this sphere. And then we we create our environment here with our layered materials. We create a solver that encapsulate this method of moment algorithm. Give it our environment and mesh again. We create a source, solve for the currents. And in here you noticing the the solve for the currents goes through and it sets up and does that vDrive stuff for us under the covers. Because all of these things are kind of encapsulated underneath the covers. You can actually get to them if you need to, but you can also put these wrappers over the top. And in the end you can call CALC-RCS to calculate the radar cross-section at a certain angle. And now if somebody is reading through this, I think it's fairly intuitive to work with this interface and they can modify it and do whatever they want. And you know if you think about this, all of a sudden you have the ability, maybe you're trying to optimize some parameter on a target or whatever it might be. Then it's quick to wrap an optimization loop around this. Right? You can really add this really to, we've talked about optimization tools. We can make a call out to a function that was solving for different values and get those. Or if you need to do a parametric study, you just write a little loop. I want to loop over frequency or I want to loop over the size of this target, whatever it might be, over offsets. Maybe you want to do a stochastic study where you have three or four parameters that you want to pick from a Gaussian distribution. You know, all of these things are about ten lines of code that you can write in here to do a lot of these. They may expand to being large applications as well, but you can think about doing a lot of these things just with a small for loop. And so it's very nice to kind of play with in that case. All right, so in review, when you have an application C or Fortran, you want to reduce the use of globals as much as possible, whenever practical. If you have access to the code and you can figure it out and you have the time, do that. It gives you both, you get rid of the states and you make it threadable. So the second thing is if you have really large functions, divide those into smaller function blocks and wrap those individual function blocks. That gives you more control over the application. It makes it more testable, more readable in the long run. Use Python to allocate your arrays. You're going to pass those in to the Fortran and get those values back. In C it's the same way. The only issue there is sometimes you have to coordinate. Sometimes the C algorithms you're using are actually creating memory themselves and handing them back to you. That's more of an effort sometimes to coordinate with the C or C++ tool about memory creation and deletion. Be careful with F90 if you're using fancy constructs. You may end up with a very non-portable wrapper if you're not careful. And then in the end learn object-oriented programming at least to the point where you can put wrappers over the top of these low level algorithms to try to make them easier to use.