2 More advanced package

This package was developed on windows and all the files will be stored in

FileNameJoin[{$UserBaseDirectory, "Applications"}]
 

which on windows is located at

C:\Users\Owner\AppData\Roaming\Mathematica\Applications

the Owner is replaced by your user name. On Linux the above gives

FileNameJoin[{$UserBaseDirectory, "Applications"}] 
 (* /home/me/.Wolfram/Applications *)
 

Now the package can be loaded by just typing

<<nma`
 

Where nma is our application name that we will make below. This way the application is loaded automatically from anywhere since Mathematica will search that folder automatically.

The application name is the same as the top level package name which lives in the file nma.m.

The application name is the name of the folder where we will put the files in under Applications/ folder.

The package name is nma. So I created a folder called

C:\Users\Owner\AppData\Roaming\Mathematica\Applications\nma
 

And put all the m files there. The structure of the folder nma/ is the following

/home/me/.Wolfram/Applications 
                   | 
                  nma/ 
                   | 
                  nma.m   <-- top level package 
                  dsolver.m         <-- subpackage 
                  firstOrderODE.m   <-- subpackage 
                  secondOrderODE.m  <-- subpackage 
                  Kernel/ 
                    | 
                  init.m   <-- important file. loads all packages 
                           This file is automatically read by 
                           Mathematica when doing <<nma` 
                           but is NOT read when doing Get["nma.m"]
 

Logically the application nma is structured as follows. There is one top level package called nma which is always the same name as the application name (i.e. the name of the folder under Application). This package contains 3 packages, these are dsolver,firstOrderODE,secondOrderODE. Each one is in its own file as shown above.

The init.m purpose is to load the whole application when one does

<<nma`
 

Inside init.m we will put the code to read all the files.

(*file init.m*) 
getFile[file_]:=Internal`InheritedBlock[{$ContextPath},Get[file]] 
getFile["nma`firstOrderODE`"]; 
getFile["nma`secondOrderODE`"]; 
getFile["nma`dsolver`"]; 
getFile["nma`nma`"];
 

The above code avoids the shadowing problem when same public symbol in subpackage is used as top package.

Important note. The above structure of the tree, does not have to be put in FileNameJoin[{$UserBaseDirectory, "Applications"}] to work.

It can be put in your own local folder anywhere on system. But for <<nma` to work, the tree has to be on the $Path. Note that Get["nma.m"] will not work. It has to be <<nma`, the reason is that we need to have init.m in the Kernel folder read and this only happens if we do <<nma` and the tree has the special folder Kernel there with init.m in it.

So to put the tree anywhere, say in your local folder instead of in the system Application folder, simply add your local folder to the path as follows (in your notebook)

SetDirectory[NotebookDirectory[]]; 
$Path = Prepend[$Path, NotebookDirectory[]]
 

And now you can do <<nma` and it will load the package and read Kernel/init.m from the local tree instead of from the system Application folder.

But it is important to have your local tree have Applications/ folder ! else it will not work. So my local tree is this

/home/me/my_mathematica_apps/Applications 
                   | 
                  nma/ 
                   | 
                  nma.m   <-- top level package 
                  dsolver.m         <-- subpackage 
                  firstOrderODE.m   <-- subpackage 
                  secondOrderODE.m  <-- subpackage 
                  test_notebook.nb 
                  Kernel/ 
                    | 
                  init.m   <-- important file. loads all packages 
                           This file is automatically read by 
                           Mathematica when doing <<nma` 
                           but is NOT read when doing Get["nma.m"]
 

And then the command is

$Path = Prepend[$Path, "/home/me/my_mathematica_apps/Applications"]
 

Now <<nma` will read Kernel/init.m OK from the above instead from the system Application folder. If the name Applications/ was not there, it will not work. So watch out for this if you plane to move your Applications/ folder.

Now we will talk about the content of the actual packages.

The top level package nma.m is

BeginPackage["nma`"] (*do not add ,{"nma`dsolver`"} *) 
 
Unprotect @@ Names["nma`*"]; 
ClearAll @@ Names["nma`*"]; 
 
dsolve::usage = "dsolve[ode,y[x],x] or dsolve[{ode,ic},y[x],x]" 
 
Begin["`Private`"] 
    dsolve[ode_,y_[x_],x_] := Module[{}, 
        nma`dsolver`dsolve[ode,y[x],x] (*need to have nma` there *) 
    ]; 
 
End[]; 
Protect @@ Names["nma`*"]; 
EndPackage[]
 

Notice it has one entry point, function called dsolve[ode_,y_[x_],x_] which calls one of its internal packages. Also note that we do not explicitly load any subpackages in the code. This is because this is all done in init.m.

The subpackage is dsolver.m which is

BeginPackage["nma`dsolver`"] (*subpackage of nma *) 
 
Unprotect @@ Names["nma`dsolver`*"]; 
ClearAll @@ Names["nma`dsolver`*"]; 
 
dsolve::usage = "dsolve[ode,y[x],x]"; 
 
Begin["`Private`"] 
    dsolve[ode_,y_[x_],x_] := Module[{}, 
        parseODE[ode,y[x],x] 
    ]; 
 
    parseODE[ode_,y_[x_],x_] := Module[{}, 
        nma`firstOrderODE`step[ode,y[x],x]; 
        nma`secondOrderODE`step[ode,y[x],x] 
    ]; 
 
End[]; 
Protect @@ Names["nma`dsolver`*"]; 
EndPackage[]
 

Notice that the same symbol dsolve shows in the subpackage and in the main package. This is OK, since the init.m file had code to avoid shadowing problem.

The above package calls functions in two other packages. Here they are firstOrderODE.m

BeginPackage["nma`firstOrderODE`"] (*subpackage of nma *) 
 
Unprotect @@ Names["nma`firstOrderODE`*"]; 
ClearAll @@ Names["nma`firstOrderODE`*"]; 
 
step::usage = "step[ode,y[x],x]"; 
 
Begin["`Private`"] 
 
    step[ode_,y_[x_],x_] := Module[{}, 
        Print["in nma`firstOrderODE`step[]"] 
    ]; 
 
End[]; 
Protect @@ Names["nma`firstOrderODE`*"]; 
EndPackage[]
 

And finally secondOrderODE.m

BeginPackage["nma`secondOrderODE`"] (*subpackage of nma *) 
 
Unprotect @@ Names["nma`secondOrderODE`*"]; 
ClearAll @@ Names["nma`secondOrderODE`*"]; 
 
step::usage = "step[ode,y[x],x]"; 
 
Begin["`Private`"] 
 
    step[ode_,y_[x_],x_] := Module[{}, 
        Print["in nma`secondOrderODE`step[]"] 
    ]; 
 
End[]; 
Protect @@ Names["nma`secondOrderODE`*"]; 
EndPackage[]
 

This completes the template package nma. Now we can add more functions and more packages if needed. Any time we make changes we just need to do

<<nma`
 

To reload it. The above command can be done from any notebook without having to set any path, and Mathematica will find the application automatically.