 So, yesterday morning, Jay and I both mentioned the Python modeling tool, or PyMT, which is a product that we hope to roll out in its fully featured glorious form under system 3.0, but it actually exists now as a beta product, and we thought we'd tell you a little bit about it and how it works and show you a bit of a demo. One way to tell you about what PyMT does is to share with you kind of a wish list of things that I wish I could have done when I was in grad school. There are many things I wish I could have done in grad school, actually, but this is a subset. One is it would have been cool in running models if it were possible to run a model in some kind of an interactive environment that has plotting and analysis capabilities built in, something sort of a MATLAB-B, IDLE, kind of a thing. You run your model right there and then, so you've got all your data produced by your model right there and don't have to muck around with writing to a file and reading from a file and whatnot. That would have been good. It would have been nice to play with a model, actually, in that same mode, to be able to, let's say, run a model for a certain amount of time and then pause it and plot some output and maybe change a parameter or inspect some values or actually change some state variables in the model. Maybe if it's a model of terrain, I want to suddenly dig a big hole and say, what if I build a big mine here? What if nature popped up a mountain here or something like that? That would have made it a lot easier to do the kind of playful experimentation that often leads to new insights. For that matter, when it comes to coupling models, it would be nice if you could do that in an interactive environment as well, that if I could run model one for a little bit and then take its outputs and feed them in the model too. Maybe it's a delta pile of sediment and I want to take that sediment and figure out the flexural isostatic response to the changing sediment load. That's one way to couple models. Maybe if I, that's a loose way of coupling. Maybe if I wanted to do tighter coupling, I could even query each model in my interactive environment for its derivatives and then take those and do a matrix inversion as a numerical solution because my interactive environment has matrix tools built into it. Finally, it would have been cool if there was some standardization among models. I think those of you who work with model codes find that they're often fairly idiosyncratic. Each model is a little bit different and sort of have a new learning curve. I don't know how many of you rented cars who have come here from out of town. You sort of take it for granted that when you rent a car, it's got a steering wheel, a brake, an accelerator and they're all in about the same place and they work about the same way. It would have been nice if there was some standardization. Although models are very different depending on what they are, there are some common ingredients among them. You run them and you initialize them and so on. As you guessed, these are some of the things that one can do with the Python modeling tool. One of the things that makes that possible is the concept of a basic standard model interface, the so-called BMI. Let me tell you a few things about BMI that you've heard of before. Some of you have worked with it before. The basic model interface is a standard definition that systems has developed. You can learn more about it. It's published in a paper by Scott Peckerman colleagues in 2013 in computers and geosciences. Thank you, Scott. The concept is fairly straightforward. It's the steering wheel, brake, and accelerator for a model. It consists of a set of standardized functions or subroutines in FORTRAN speak that have standardized names, standardized lists of arguments that you give it, and standardized things that they return. They do just a set of basic things, things that almost every model will do. It'll initialize a model as a function to advance a model in time, a function to clean up, a function to interrogate values of some particular parameter or variable, and even functions to change those if you want to tweak it in mid-run. There's a little bit more to it than that. There's some devils in the details when it comes to actually applying it, but that's the essence of it. It's fairly straightforward. So the thing is, if a model developer adds this set of functions to your code, then you can hand that code to the integration facility team, and they will do a few more additional processing steps, and the result will be, your code will then be a component that can be run through the Python modeling tool or through the web modeling tool, as the case may be. This will work for a model that is written in any of the CSTMS-compatible languages, so C, C++, Fortran, Python, and Java. Okay, so Eric is going to tell you a little bit about how that process works and then actually show you PyMT in action. Thanks, Greg. Is my mic working? Yeah. All right, so I'm going to talk to you first about what happens when your model graduates from BMI school and becomes fully BMI compliant, and so that's when you give it to us, and then we'll take over and we'll do a few things. It's kind of hard to read with the ferns here, so the first thing that we'll do, we'll write a recipe that builds your model so that we can automatically build your model, and then we will deploy that to our repository so people can download a built version of the model, and we, excuse me, we test that every night. You may ask, Eric, I gave you a model. It works. Why do you need to test it every night? The reason is, well, for one, maybe we'll make a change to it, but then we check for all your dependencies, so you'll have lots of dependencies, or maybe a few, but you'll have some dependencies. Maybe it's just a GCC version, but we want to make sure that your model doesn't break when the dependency changes, so we'll do that for you, and then we deliver it to the bakery, which is where the recipes are stored and the models are baked every morning and tested, and we do that every day, as I said, and then the thing for is we babelize your component, so this allows it to talk in different languages or to communicate with other models in different languages. Primarily for PyMT, we're going to just use the Python bindings, but we will babelize, the babelizer will work on, as Greg said, Java, Fortran, C, C++, Python, but BMI is language agnostic, so you can write BMI for other languages, but that's just what the babelizer works on, and we also have a BMI for, for instance, web APIs, so our WMT is based on a BMI for a web API, and we could communicate with that as well, and again, we test nightly on your babelized version, and, but then today I'm going to talk to you about PyMT components, so we'll load your model into PyMT so that it can be run as a PyMT component. I, I just realized this this morning, think one and think two, they made a terrible mess of the house when the, the mother left, and so I shouldn't have put that in because we don't make a mess of your model. Sorry, so I'm going to run through some examples, and I'm going to try to do this live, I get nervous enough when Mark is staring over my shoulder typing, so bear with me, and a big thanks to Mariela for thanks to Mariela for convincing me to do this live, so we'll see how that goes. This is an experiment, so that, so I'm going to do, go around through three examples that sort of run from one to the next. I'm going to show you how to run a generic BMI model in PyMT, so hopefully we're going to make it more accessible for people to run models and get models. I'm going to actually run child, which is a landscape evolution model, and then I'm not going to actually show you, or I'm going to show you how to couple landscape evolution model child with a seascape evolution model, said flex 3D. It takes too long to run, so I can't show you it live, but I'll show you the code, and hopefully convince you that's not too difficult to do. All right, here we go. Okay, so everyone can see that, I hope. So this first line is you don't have to worry about, it's just help plotting so that I can show you some plots. So the first thing we're going to do is just import a component from PyMT, so how many people are familiar with Python here? Okay, good. I wasn't going to change the presentation if no one said they were. It's just good to know. So from PyMT.components, import, and then we're going to import said flex 3D, and for now I'm just going to rename it to be model. I wouldn't normally do this, but you'll see why. So now we will instantiate model, so now we have a said flex 3D model. We could have more than one model, but for now we're just going to have one version of said flex. And we can get some help on, so this is doc string for said flex 3D. So we have a description of it, but I wanted to show you here a couple things. One, we want to be very careful that we give credit where credit's due. So when you give us your component, I don't want to get lost in our framework. So when people use the framework, and they're using your component in our framework, I want to make sure that they know that it's your component. So we list the author, and we list DOIs, and we links to the source code. This is all open source, so we want you to, if you're using this model, we want you to be able to see the source. And then we also have a citation with the model, and it could be more than one citation. So that you can, so if you do that use this component in PyMT, this is what you would cite. And we're going to add, we don't have this yet into PyMT, but we will, where PyMT will keep track of all the components you use, and even maybe components that you don't even know you're using. Maybe one component uses another one under the hood. So that at the end of your simulation, you can say PyMT, what are all the citations for the simulation that you're having. Then the other thing that we add in PyMT is the ability to change the input file parameters. So these are all parameters that you would change normally through an input file, which is very different in different models. But now we can change them as keywords with dynamically within Python. So that we're going to, I was going to do some cleanup just in case, and so first of all, we're going to set up a model simulation. So we're going to get a config file, we're going to get initialization directory, and then we're going to set up set flex in a folder called underscore model. Now if we look inside model, now you see all the set flex input files have been placed in there with some default parameters. You could change the the parameters from the defaults to the setup method. But for now, I'll show you how to do it at the child. So the set flex has a bunch of complicated input files, there's six of them, but they're all set up for you now. So now we'll get to the DMI methods that Greg was talking about. So this is it. So we're going to initialize set flex. So now set flex has read in all the input files, it's initialized itself, so now it's ready to run. And we'll just, just for fun, we'll run it for just 10 times. So now this is the update method. So this will advance the model forward in time by one time step. And just to see that it's working, or it's doing something, well, I print while I get current time. So now we've printed. So now we've run it to what looks to be 10 years. And we can double check that those units are correct. So model dot. So yes, those were days. We have, we can have a little bit more fine grain control. You don't have to update it just one time step. If you do model dot, update until a particular time. Let's do 20 years. So we could do 20 times 365 set flexes and days, or we can just say we're going to specify units of years. So now if we did well get time, we're at 7300 years. We also give the ability to change the units in set flex because when you're coupling models, oftentimes the time units will be different. So I could reset if I wanted to, I could go back up here and reset the units to be years, which would match set flex. I'm not going to do that right now. We can also get, now we've run it, then we want to get some information from us. We can query some output variables from set flex. So model dot. So now we get a list of the output variable names. There's lots of them. And then we use as Greg showed the get value function. And let's look at C water depth. So in this case you can see it was initialized to water depth of 50 meters and on land it's 30 meters. And you can also, there's unit conversion. So if you want to change units, you could change the parsecs. I don't know why you would, but you could. So that's how you run a general BMI model in PyMT. So I hope that was pretty simple. And then I want to demonstrate that it's the same, you know how to run one, you know how to run them all. So let's add child. So now we're going to instantiate child. So now we'll get some help. So all the commands are going to be the same. So now you have a child with a couple citations, the authors versions, links to source code. I'm going to do some cleanup. Now we're going to set up child, but I'm going to do it a little bit differently this time just to show you that you can change the input parameters from the defaults. So it's simple. So instead of editing the input files, you could just change variables as keywords here. So we're going to change the node spacing. I think the default was probably a kilometer. Now it's three quarters of a kilometer. The width of the domain and the extraction is 20 kilometers. Let's see. So I believe it's going to be 40 kilometers that way. Yep. And then just so we don't get any trouble when I'm running, I'm going to have the end time be really big just so I don't hit it. So there now child is initialized and it's the input folder is just with the child input file. This is one input file. And then we just do the same commands as we did before. Initialize child, that's ready to go. We're going to run child for 10 time steps. Now, we've ended up at 100 years. And as before, we could update child to 200 years through the update until math. So again, you know how to run one model. You know how to run them all. So now we're going to play around. So Greg was talking about playing with models. So now we're going to play with this child model that we've created. So we're going to look at child output variables. So child dot output bars. So these are output variables for child. There aren't too many. So that makes it easy to work with. But we're going to concentrate on land's surface elevation. So we can get information about that variable. So the nice thing about the BMI is that it's self describing. So you'll see that if I do child, then we're going to get some information about land's surface elevation. So land's surface elevation. So it's an array of n nodes long. It's defined at the nodes of the grid. It's an input and output variable. It has units of meters. And it's on grid zero. So a model has a solution grid. It can have multiple ones. And a variable is defined on the grid on each of the grids. So in this case, land's surface elevation is defined on grid zero. We can get some information about that if we'd like to. So it's an X array dataset. This is more or less in the U grid format. So it's a standard format for describing the topology of the grid. And let's plot it up just to see what it looks like. So we'll just do a land surface. And this is just to make the graph look a little prettier. That's not big enough. So what I want to show here is that the child grid is an unstructured mesh. And we're going to couple it to said flux, which has uniform rectilinear mesh. So there's a whole bunch of triangles there you may not be able to see. But the elevations are defined on the corners of the triangles. So what I just showed you, there was child to set initialized with flat topography with some random noise in it. So we want to, if we're going to couple this with said flux, we want some land and some ocean. So I'm going to change the elevations to include some topography and some bithymetry. So first we're going to get the X and Ys of child. And remember, we're looking for grid zero here. And we're going to get values of land. So the Xs and Ys. And so we're going to have a shoreline. Let's do it at 10 kilometers. Now we're going to change the, so if for Y less than the shoreline, we're going to subtract 100 meters. And for Y greater than equal to shoreline, we're going to add 100 meters. So that's our new bithymetry, our new land surface elevation grid. And so we have to do one more thing for child to see this, we have to do child dot set value, land surface elevation. And then so the first argument here is the name of the variable we want to set in a second one is going to be a numpy array of values at the nodes. So then if we plot this up, we've just changed child's input landser's elevations to be something that we can couple with two with said flex. We've got some land and we've got some ocean. So that's not something you could have done before. This is we're doing this dynamically. And let's let's do child dot update until we'll just update it for 5000 years. And I will show you so you can see child is starting to erode the landscape. But it's not doing anything in the ocean yet. Because that's not what child does. So what we're going to do now we're going to play around with a little bit. So we're going to add a an uplift component to child. We're just going to do this dynamically here. So it's going to be a little bit of typing here. So there with me. So we're going to uplift the block in the upper right corner of that grid you just saw. I'm just going to show you how you might do that. And then we're going to advance it in time. So here we've got a time loop that goes from now, which is wherever we have child at. And then 5000 years in the future taking steps of 100 years. So this is this is basically how you could couple it to another another model. So we're going to update it until this new time. We're going to get. I'm just going to copy this. We're going to get the land surface elevations. And now we're going to add some uplift. So we're just so as I said, it's going to be a block in the upper right corner. So yeah. Oh yeah, sure. Yeah, I'll do that in just a second here. Yep. Yep. That's as high as I can. Okay, and set value. So what we've done here is, if you can see it, we've updated child for one one time step, we get the surface elevations, we change them. Just those elevations. And then we set the values. And then we just do that loop until five for another 5000 years. So now we've added this block in the upper right corner. And we will update it for another 5000 years, we'll road into the block, see what happens. Now we're running for another 5000 years. And then so we've eroded into the block. It's not coming out very well in the graph there. Oh, it's because I've up. So that's what I get for doing this. So I multiply by 1000 instead of 100 right there. But you get the idea, you can uplift the block and you wrote into it, we're doing this all dynamically. And just about out of time. So I'm going to take you over to how we might couple child and said flux. And so most of this, I've written that there's a lot of here. So I've written it all out for you. Most of it is very similar to what you've seen already. So if we're going to couple the two miles together, we're going to instantiate child and then said flux, we're going to run the setup method for both of them so that they set up their model runs in different folders. And we're going to initialize them. Now we're going to do something just a little bit different. We're going to set the child's elevations to include a little bit of land and a little bit of water. And we're going to set the elevations in child. But now you want to make sure that said flux gets those same gets that same input grid. So before you remember, we ran the set value. So I'm down on this line right now. So before we did a set value where the second argument was an umpire array, just an array of values. But now we're going to say, okay, get your values from child for from land surface elevation, map them from child's unstructured mesh to said flux is uniform rectilinear grid, we do this using the ESMF mappers and then set the values in said flux. And then we could so the final step would be to for the time limit. So we'd run so here time sets of 10 years, we update child to the next time step. We set said fluxes that we map the was essentially the bed load flux from child. So child's eroded land, sent it to the coast line, we're going to take those values, map it to said flux said flex will pick them up. And then we will update said flex to the next time step so that said flex will disperse that sediment into the ocean through surface plumes. And will though can be acted on by currents and things. And then we get values and then we have to map. So because said flexes change the elevations, we need to map those elevations back to child. So that child will see this new delta that's that's forming. And then we just do that over and over again. And I'm going to show you a movie. We have time of what this might look like. So again, so we so the landscape evolution model is the top seascape evolution models the bottom. So that as we evolve in time, child's eroding into the landscape, delivering said flux to set to set sediment to said flux, which then is building a delta that child runs over. And it really isn't too much harder than those lines of code that I showed you. So there's lots of different river mouths that child is the main river mouth obviously is that big delta on the right hand side. So this is taking two very big complex models and coupling them in not too much Python code. And then so that's it. So if there's any questions, I'd be happy to answer, but you don't have too much time. Nicole, yet. Yeah, you could definitely change them. And it's easy to do. You would do it in that set up set step. So if I go back to, you know, so when I when I did child, I did change these parameters. But you know, as you know, the child input file is very, very long. So there could be many parameters here that we could anything's up for grabs. Well, that's a good question. No, it doesn't have to have the update until it certainly is recommended in many models. It's not very difficult to add that. But what we would do if we say you had a model that ran just on nearly time sets, it couldn't run partial years. What we would do is run it for and see you want to run to your one and a half, we'd run it to year two and then linearly interpolate the values between years, but that's not an ideal solution. It's computationally intensive. And it's maybe it doesn't get the best result. So yeah. Yeah, so the so yes, that's basically right. The landscape is being eroded by child and the care the sediment is carried by child to the coastline where it stops. And child passes those fluxes to set to set flux, which then sends it to a surface boom that's sent to the ocean between. So we do that with the, let's say I'll go back to here. So that is communicated in this step right here in this step here. So this is so said, flex is saying child, give me your bed load fluxes. And the bed load fluxes here is actually a grid on child. So the bed load fluxes are defined at nodes on child's grid. And this says, okay, child, give me those and map them to my grid, said flex grid. And then said, flux can deal with that given a grid of bed load fluxes set flux. Sure. Yeah. Yeah. So, so one thing that sorry, I'm cutting into your coffee break here. So one very important part of this that I didn't mention is these standard names for each of the variables. And this is very important because you want to make sure that you're coupling two models, the variables are exchanging between models or what you think they are. And so there's a very, so Scott can talk a lot more about this, but we use Scott's CSMS standard names to define, so this is part of the BMI. I guess it's not, so if when a model becomes a BMI, we want their names to be in this format so that we can more easily couple two models together. You'll notice that so for the bed load flux, both child and said flux use that same name. So that makes it much easier. Notice up here, though, it's a little bit different. Child has something called land surface elevation, because when we wrapped child with a BMI, it was only run on land. So we said land surface elevation. So but maybe this should now be in child with this new version of child. It should be Scott would be like land or sea surface sediment surface elevation or something along those lines to indicate that it's could be both land and ocean to get this. Oh, to change the standard name. Oh, it would take about two minutes. So it would be very simple. I probably should have done it for this. This is a learning learning experience. The website, well, Scott might be better to answer that. I mean, if you go to our website for the CSDMS standard names, we've got lots of stuff on that. And I think we've linked to Scott's newest versions, I believe. And on GitHub, we have also links to that. So I think would be so in GitHub would be CSDMS slash standard names, I think. So that's where that would be. Yeah, but for sure you could talk to me or go to the website. Well, we'll get you hooked up right beside you. It's not very difficult. We haven't found a name that couldn't be a standard name yet.