Modifying and improving the example

Increasing the length of the simulation

There are two ways to make modifications on the model. The first, which works for simple changes, is to modify the Fortran code itself. The second is to change the KPP model itself (the .def files etc.) before it gets compiled. This latter method is more general, so this is the one we will focus on this guide. Since all we want for now is to increase the total time, we will base ourselves in the original small_strato model and only modify this parameter.

First, create another directory (anywhere you want) called test2 (with mkdir test2) and enter it (with cd test2). Now create a file called my_strato.kpp (with notepad++ my_strato.kpp or gedit my_strato.kpp or whichever text editor you ended up using) and paste the following lines in the file:

#MODEL      my_strato
#LANGUAGE   Fortran90
#INTEGRATOR rosenbrock
#DRIVER     general

At this point if you run kpp my_strato.kpp you should get an error saying "Fatal error : my_strato.def: Can't read file". Which appears because we instructed KPP to search for the file my_strato.def, which doesn’t exist anywhere. So we first must create the my_strato.def file, which ultimately defines the my_strato model.

Let us define our model based on small_strato, since for now all we want to do is to modify the time length of the simulation. In order to preserve the original small_strato.def we’ll copy it and call it my_strato.def, this way we can do any modification on my_strato and the original small_strato will be safe. You can copy the file from the models directory into our working directory (test2) by issuing the following command:

cp $KPP_HOME/models/small_strato.def my_strato.def

Note

When we run KPP from any directory (say, test2), KPP will first look for the files in the current directory and then in its home directory. So can either put our model files in our KPP “models” directory or in our current directory. In this guide we’ll always prefer to keep the model we create/modify in the current directory.

Now you should open the file we just created (for example with notepad++) and find the lines that look like:

#INLINE F90_INIT
        TSTART = (12*3600)
        TEND = TSTART + (3*24*3600)
        DT = 0.25*3600
        TEMP = 270
#ENDINLINE

These are the lines that define the start, end, time step and global temperature of the model. For now, we’ll change only the end time. Notice that they are given in seconds. For example, 3*24*3600 is the amount of seconds in 3 days, meaning that at the moment the simulation is set to run for 3 days, which we saw is not enough. Let us then replace 3 with 30, meaning we will run it for 30 days, to be sure that equilibrium is reached. Now that line should read TEND = TSTART + (30*24*3600). Please also note that TSTART is set to 12*3600, or 12 hours, which means that the starting time of the simulation is at noon.

After this small change we are ready to test run the model. Run kpp my_strato.kpp, which should end with a succesfully created the model message. Now, just like with the previous example, open again the file Makefile_my_strato and uncomment the line that says COMPILER = GFORTRAN, so we can use gfortran instead of Intel. After this is done compile the model with:

make -f Makefile_my_strato

Again, if everything goes well, the my_strato.exe file should be created. You can run ls -tr (which lists every file on your directory ordered by creation time) and see that the last file listed should be the .exe file.

We can now run the model with ./my_strato.exe, which should now take 10 times longer to complete, since we are running it for 10 times as long as before. We can use the Python code given in the last section to read the results. We just need to adjust the name inside the code from small_strato to my_strato.

The plot of the results is

_images/test2_time.png

Now we can see that the solution reaches equilibrium after roughly 200 hours. This, however, was only a minor change in the model. Let us now learn how to change some other parameters and examine how the model reacts.

Change in the initial conditions

We try this next change in the same model as before (my_strato). Let’s open my_strato.def and consider an atmosphere with more NO (simulating a more polluted condition). Where it says NO = 8.725E+08;, we make it read NO  = 9.00E+09, which is roughly a 10 times increase in the NO concentration. Let us also change the O3 initial condition and make the O3 line read O3  = 5.00E+10;, simulating an atmosphere that has a lower initial O3 concentration.

Let us also change the line that reads:

#LOOKATALL          {File Output}

We will make it read

#LOOKAT O3; NO; NO2; {File Output}

This latter change makes the program write only time, O3, NO and NO2 into the output file. This simplifies the reading process and saves space, but by doing that we are assuming that we’re not interested in the other species.

Note

This will change the order of the output in the file. But again, checking the .map will give you the correct order. This time you’ll have to consider only the species you specified to be on the output, so, e.g., in this case the order in the .map file is: 1 = O1D, 2 = O, 3 = O3, 4 = NO, 5 = NO2. But since we are writing only O3, NO and NO2, our output file will have the order (time,) O3, NO, NO2 (which are numbers 3, 4, 5, respectively). This will have to be done every time the #LOOKAT parameter is used.

We again go through the same steps: run it with kpp my_strato.kpp, change the compiler to gfortran, compile if with make -f Makefile_my_strato and run it with ./my_strato.exe. This time, since the output file changed, we have to change the code to read it correctly:

import pandas as pd
from matplotlib import pyplot as plt
concs = pd.read_csv('my_strato.dat', index_col=0, delim_whitespace=True, header=None, dtype=None).apply(pd.to_numeric, errors='coerce')
concs.columns = ['O3', 'NO', 'NO2']
concs.index.name = 'Hours since noon'
concs.plot(ylim=[1.e8, None], logy=True, y=['O3', 'NO', 'NO2'], grid=True)
plt.savefig('test21_time.png')

This code produces the following plot:

_images/test21_time.png

From which we can see that the concentration of ozone stabilized more quickly in this case. As you can see, we can play around with the initial conditions as much as we want and analyse that result of model. In fact, we encourage you to do so. However, let us focus this guide on the next step and modify some more fundamental aspects of the model: the reaction rates.

Modifying the reactions

Now we will alter the reaction rates of some reactions in the model. Keep in mind that these alterations are not meant to be realistic. They are simply done here for the sake of learning how the model works.

Begin again by creating a test3 directory anywhere and going into it (mkdir test3 && cd test3). In this directory, create a file called strato3.kpp with the following contents:

#MODEL      strato3
#LANGUAGE   Fortran90
#INTEGRATOR rosenbrock
#DRIVER     general

This file tells KPP to look for the strato3.def file. So let us create this file by again copying the small_strato.def file to our current working directory. You can do that with:

cp $KPP_HOME/models/small_strato.def strato3.def

Open the file (notepad++ strato3.def) and find the first two lines which originally read

#include small_strato.spc
#include small_strato.eqn

Which still tells KPP to look for the original small_strato model files when defining the species (.spc) and chemical equations (.eqn). You should modify these lines to the following:

#include strato3.spc
#include strato3.eqn

Also, you should do same modification we did in the last example. That is to change the length of the run from 3 to 30 days by modifying the line that reads TEND = TSTART + (3*24*3600) to make it read TEND = TSTART + (30*24*3600), and to change the line that reads #LOOKATALL to #LOOKAT O3; NO; NO2;.

If you try to run KPP now you’ll again get an error because those files still don’t exist. Let’s create them by copying the original small_strato files, which can be done with the following commands:

cp $KPP_HOME/models/small_strato.spc strato3.spc
cp $KPP_HOME/models/small_strato.eqn strato3.eqn

Now if you try running KPP it should work. But this is still not what we want; this is just the small_strato mechanism with another name, so let us move to the actual changes.

If you check the strato3.spc file you’ll see that it only the definitions of the species used, which wouldn’t make much sense to change for now since we’ll be using the same species, so we will leave it how it is. Now we focus on the strato3.eqn file. If you open it you’ll find the following lines:

#EQUATIONS { Small Stratospheric Mechanism }

<R1>  O2   + hv = 2O            : (2.643E-10) * SUN*SUN*SUN;
<R2>  O    + O2 = O3            : (8.018E-17);
<R3>  O3   + hv = O   + O2      : (6.120E-04) * SUN;
<R4>  O    + O3 = 2O2           : (1.576E-15);
<R5>  O3   + hv = O1D + O2      : (1.070E-03) * SUN*SUN;
<R6>  O1D  + M  = O   + M       : (7.110E-11);
<R7>  O1D  + O3 = 2O2           : (1.200E-10);
<R8>  NO   + O3 = NO2 + O2      : (6.062E-15);
<R9>  NO2  + O  = NO  + O2      : (1.069E-11);
<R10> NO2  + hv = NO  + O       : (1.289E-02) * SUN;

Just for the sake of learning, let us change the photolysis rate (last reaction) to make it a lot slower. We will make the last line read:

<R10> NO2  + hv = NO  + O       : (1.289E-06) * SUN;

Note

This is 4 orders of magnitude slower than it previously was and it may not be realistic! We only make this change for the sake of illustration, so that the output change is easier to see.

You can actually change not only the reaction rate for the equation, but also modify the equations itself here and add (or remove) equations. For now we will leave the equations the way they are.

Now we go through the same steps of running kpp strato3.kpp, changing the compiler to gfortran and running make -f Makefile_strato3. If everything goes well, we’ll see the strato3.exe created. After running ./strato3.exe sure enough strato3.dat is created, which we can plot with the same python code from the last example (only changing the name of the file of course):

_images/test3_time.png

Note

This process of running KPP, then changing the Makefile, then compiling, etc., is pretty cumbersome and straightforward. So we included a file called updatenrun.sh in the directory test3 that can be found in the github repo. This is a bash script that does these steps automatically. To run it, you enter sh updatenrun.sh modelname. In this case, for example it shoudl be used with sh updatenrun.sh strato3.

We can see that once again the final result changed. This time, since NO2 is photolizing a lot slower, we see less NO in comparison with the previous plot. We encourage you to try different reaction rates and initial conditions and see what is the result in the model. With the updatenrun.sh script (check the note above) it should be easy!

Now that we have modified the small_strato example in several ways, let take it a step further and create a new model from scratch.

Creating a model from scratch

Now we do one more step and create a completely new model with our own set of reactions. Basically for our new model to be complete we should give it the initial conditions, numerical constraints, species and reactions list. Let us start with the KPP file and move on from there.

We will try to simulate a very small tropospheric model, which we will call ttropo (meaning tiny tropospheric; let’s write it like that just because it’s easier). First let’s create a new directory for our test with mkdir ttropo and move to that new directory with cd ttropo. Now we create the main KPP file with notepad++ ttropo.kpp and put the following lines in it:

#MODEL      ttropo
#LANGUAGE   Fortran90
#INTEGRATOR rosenbrock
#DRIVER     general

Which means tells KPP to look for the ttropo.def file. If you run KPP now it will finish with an error because it won’t find it. But we will create that later. Let us first define our mechanism, i.e., our chemical reactions.

We create the ttropo.eqn file (e.g. with notepad++ ttropo.eqn). Now we will put our reactions in that file, following the syntax that we saw in the previous example. We choose a simplified set of tropospheric reactions that can be written as:

#EQUATIONS { Tiny Tropospheric Mechanism }
<R2>  O    + O2  = O3            : (8.018E-17);
<R1>  NO2  + hv  = NO  + O       : (1.289E-02) * SUN;
<R3>  NO   + O3  = NO2 + O2      : (6.062E-15);
<R41> O3   + hv  = O   + O2      : (5.500E-04) * SUN;
<R42> O3   + hv  = O1D + O2      : (6.000E-05) * SUN*SUN;
<R5>  O1D  + M   = O   + M       : (7.110E-11);
<R6>  O1D + H2O  = 2OH           : (2.2E-10);
<R7>  CO   + OH  = CO2 + HO2     : (2.2E-13);
<R9>  HO2  + NO  = OH  + NO2     : (8.3E-12);
<R10> OH  + NO2  = HNO3          : (1.1E-11);
<R11> HO2 + HO2  = H2O2          : (5.6E-12);
<R12> O3  + HO2  = OH + 2O2      : (2.0E-15);
<R13> H2O2 + hv  = 2OH           : (1.366E-5) * SUN;
<R14> H2O2       = H2O2aq        : (3.3000e-03);
<R15> HNO3       = HNO3aq        : (2.4000e-03);

So copy and paste those lines into ttropo.eqn, save and exit.

Note

Again, some of these reaction constants might not be exactly accurate for tropospheric conditions, so please double-check if you plan on using them for professional means, since the objective here is to only present this as an example.

Now we create the species file, which has to have all the species we used in the reactions above properly defined. We can define a species as being variable (when its concentration can vary according to the kinetics) or fixed (when its concentration is a constant). In this case, we define only M, H2O and O2 as fixed quantities and the other ones as variables:

#include atoms

#DEFVAR
O   = O;            { Oxygen atomic ground state }      
O1D = O;            { Oxygen atomic excited state }
O3  = O + O + O;    { Ozone }
NO2 = N + O + O;    { Nitrogen dioxide }
NO  = N + O;        { Nitric oxide }         
HNO3 = H + N + O+O+O;
H2O2 = H+H + O+O;
CO2  = C + O + O;
CO   = C + O;
OH   = O + H;
HO2  = H + O + O;
H2O2aq = IGNORE;
HNO3aq = IGNORE;

#DEFFIX
M   = O + O + N + N;{ Atmospheric generic molecule }
O2  = O + O;        { Molecular oxygen }
H2O = H + H + O;    { Water }

Again, copy and paste those lines into ttropo.spc, save, exit, and let’s proceed to the .def file. Create ttropo.def with notepad++ ttropo.def. In that file you will write the following lines:

#include ttropo.spc
#include ttropo.eqn

#JACOBIAN SPARSE_LU_ROW      {Use Sparse DATA STRUCTURES}
#DRIVER general
#DOUBLE ON
#STOICMAT ON

#LOOKATALL;                 {File Output}}
#MONITOR O3;N;O;NO;O1D;NO2; {Screen Output}

#CHECK O; N;                   {Check Mass Balance}

#INITVALUES                    {Initial Values}
CFACTOR = 1.    ;              {Conversion Factor}
O3  = 7.65E+11 ; 
NO  = 2.55E+10 ;
NO2 = 0.0E+09 ;
O2  = 1.697E+19 ;
M   = 2.550E+19 ;
H2O = 3.0E+17;
CO  = 2.55E13;

OH     = 0.;
HO2    = 0.;
H2O2   = 0.;
H2O2aq = 0.;
HNO3   = 0.;
HNO3aq = 0.;
O1D    = 0. ;
O      = 0. ;

#INLINE F90_INIT
       TSTART = (12*3600)
       TEND = TSTART + (15*24*3600)
       DT = 0.2*3600
       TEMP = 270
#ENDINLINE

You can see that with this set of definitions we chose to run the model for 15 days, with a time step of 0.2 hours and that many of the initial concentrations are set to zero. Note also that we are again starting the simulation at noon.

With these files we have the complete ttropo model and are ready to run it. We can use the updatenrun.sh script as sh updatenrun.sh ttropo (you’ll have to copy it to the current directory with cp first). It should run successfully now. Note that we again have to check out ttropo.map to find out the order of the species in the output file. We can use the following Python script to plot the results (the correct output order is already included in it):

import pandas as pd
from matplotlib import pyplot as plt

concs = pd.read_csv('ttropo.dat', index_col=0, delim_whitespace=True, header=None, dtype=None).apply(pd.to_numeric, errors='coerce')
concs.columns = ['CO2', 'H2O2aq', 'HNO3aq', 'HNO3', 'H2O2', 'CO', 'O1D', 'O', 'OH', 'HO2', 'O3', 'NO', 'NO2', 'M', 'O2', 'H2O']
concs.index.name = 'Hours since noon'
concs.plot(ylim=[1.e4, None], logy=True, y=['O3', 'NO', 'NO2', 'CO', 'HNO3', 'OH', 'HO2'], grid=True)

plt.savefig('test4_time.png', bbox_inches='tight')
plt.show()

The output of this model can be seen in this figure:

_images/test4_time.png

As you can see, in 15 days we ran the model for, it hasn’t reached equilibrium yet. This system of equations will take longer to reach equilibrium state than previous models. You can, once again, investigate the effects of different initial concentrations in the final result, change the reactions and reaction rates, or even fix some quantities in the .spc file.

The creation of any new model from scratch follows the same paths that we just described here. So for other models, following these steps should produce the correct result. For model detailed information not included here, we refer the user to the official manual for KPP.