JPAD test within Jupyter running a BeakerX Java kernel

— Agostino De Marco, University of Naples Federico II

JPAD stands for Java Program toolchain for Aircraft Designers. JPAD is a Java software library containing classes and utility functions that can be used to build software systems. Typically, these are calculation programs providing aerodynamic and performance predictions for given aircraft designs.

JPAD is open source and its public repository is on GitHub.

This Jupyter notebook uses the BeakerX Java kernel. The purpose is demonstrating:

  • the use of Java with Jupyter
  • the use of JPAD and its companion external native libraries in the context of a notebook

Running this notebook

Let us print the current directory content. We are Java users, so let start using Java in our first code cell to this purpose:

In [1]:
import java.nio.file.*;
Files.list(Paths.get("."))
    .forEach(System.out::println);
.\.ipynb_checkpoints
.\;
.\beakerx-jpad.bat
.\images
.\jar
.\lib
.\Test_JPAD_1.ipynb
.\Test_JPAD_2.ipynb
.\Test_JPAD_3.ipynb
.\tmp
Out[1]:
null

This notebook is named Test_JPAD_2.ipynb. The jar subdirectory contains the two Jar files

  • JPADConfigs_DDMM2018.jar
  • JPADCore_v2_DDMM2018.jar

where JPAD is bundled, and all the Jar's that JPAD depends upon.

In addition, the The lib subdirectory contains all the native libraries (Win64) needed for JPAD to work correctly.

The BeakerX kernel for this notebook is launched on Windows with the following beakerx-jpad.bat file:

setlocal
set PATH=%PATH%;.\lib;.\jar;
set JUPYTER_RUNTIME_DIR=%JUPYTER_RUNTIME_DIR%;.\lib
set JUPYTER_EXEC="beakerx"

%JUPYTER_EXEC%

It adds lib and jar directories to the PATH environment variables as well as to the JUPYTER_RUNTIME_DIR. Then, it runs Jupyter with the beakerx command. Once Jupyter with the BeakerX kernel is set up and running, the notebook file can be opened and executed.

Preliminaries

Classpath extras

The following cell uses BeakerX magic command %classpath that directs the Java kernel to include all the Jar files located in the path ./jar. The command %classpath alone prints out all the included Jars.

In [2]:
%classpath add jar .\jar\*
%classpath
Added jars: [JPADCore_v2_03042018.jar, xstream-1.4.7.jar, jarh4obj.jar, jxls-core-1.0.5.jar, jarh5obj.jar, flanagan.jar, dom4j-1.6.1.jar, log4j-1.2.13.jar, xmlbeans-2.6.0.jar, JPADConfigs_31032018.jar, processing-core.jar, freebuilder-1.10.5.jar, commons-codec-1.5.jar, jfreesvg-3.0.jar, MOEAFramework-2.12.jar, jdom-2.0.5.jar, unit-api-0.6.1.jar, commons-lang3-3.7.jar, jarhdf-2.10.1.jar, vecmath.jar, trove-3.1a1.jar, commons-logging-1.1.jar, slf4j-api-1.7.5.jar, jfreechart-1.5.0.jar, jarhdfobj.jar, jxls-reader-1.0.5.jar, orsonpdf-1.7.jar, fxgraphics2d-1.3.jar, commons-logging-1.2.jar, jfreesvg-3.3.jar, commons-math3-3.6.1.jar, log4j-core-2.0.2.jar, commons-jexl-2.1.1.jar, reflectutils-0.9.16.jar, jarhdf5-2.10.1.jar, jfreechart-nofx-2.0-pre1.jar, commons-collections4-4.0.jar, javaslang-2.0.4.jar, gtmat.jar, orsoncharts-1.5.jar, poi-3.10.1-20140818.jar, junit-4.11.jar, tigl-2.2.1.jar, stax-api-1.0.1.jar, jscience.jar, log4j-api-2.0.2.jar, guava-16.0.1.jar, poi-ooxml-3.10.1-20140818.jar, icu4j-59_1.jar]
C:\Users\DeMarco-PC\AppData\Local\Temp\beaker5855167260645293051\outDir
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\JPADCore_v2_03042018.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\xstream-1.4.7.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\jarh4obj.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\jxls-core-1.0.5.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\jarh5obj.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\flanagan.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\dom4j-1.6.1.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\log4j-1.2.13.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\xmlbeans-2.6.0.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\JPADConfigs_31032018.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\processing-core.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\freebuilder-1.10.5.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\commons-codec-1.5.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\jfreesvg-3.0.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\MOEAFramework-2.12.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\jdom-2.0.5.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\unit-api-0.6.1.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\commons-lang3-3.7.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\jarhdf-2.10.1.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\vecmath.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\trove-3.1a1.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\commons-logging-1.1.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\slf4j-api-1.7.5.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\jfreechart-1.5.0.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\jarhdfobj.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\jxls-reader-1.0.5.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\orsonpdf-1.7.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\fxgraphics2d-1.3.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\commons-logging-1.2.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\jfreesvg-3.3.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\commons-math3-3.6.1.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\log4j-core-2.0.2.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\commons-jexl-2.1.1.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\reflectutils-0.9.16.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\jarhdf5-2.10.1.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\jfreechart-nofx-2.0-pre1.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\commons-collections4-4.0.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\javaslang-2.0.4.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\gtmat.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\orsoncharts-1.5.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\poi-3.10.1-20140818.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\junit-4.11.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\tigl-2.2.1.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\stax-api-1.0.1.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\jscience.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\log4j-api-2.0.2.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\guava-16.0.1.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\poi-ooxml-3.10.1-20140818.jar
C:\Users\DeMarco-PC\Dropbox\Jupyter_2\beakerx\jpad\test2\jar\icu4j-59_1.jar

Check the native libs

The native libs stored in the ./lib directory are listed by the following code cell.

In [3]:
import java.nio.file.*;
Files.list(Paths.get("./lib")) // a stream of paths
    .map(path -> path.toString().toLowerCase())
    .filter(name -> name.matches("([^\\s]+(\\.(?i)(lib|dll))$)")) // use regex
    .forEach(System.out::println);
.\lib\jhdf.dll
.\lib\jhdf.lib
.\lib\jhdf5.dll
.\lib\jhdf5.lib
.\lib\libhdf.lib
.\lib\libhdf5.lib
.\lib\libjpeg.lib
.\lib\libmfhdf.lib
.\lib\libszip.lib
.\lib\libxdr.lib
.\lib\libzlib.lib
.\lib\msvcp100.dll
.\lib\msvcr100.dll
.\lib\tbb.dll
.\lib\tbbmalloc.dll
.\lib\tigl.dll
.\lib\tigl.lib
.\lib\tixi.dll
.\lib\vcomp100.dll
Out[3]:
null

Calling JPAD functions

At this point we can start using the JPAD API functions.

Global scope imports

The following are BeakerX magics, a bunch of %import commands that make a set of Java packages globally visible by all the subsequent code cells.

In [4]:
%import java.io.File
%import java.io.IOException
%import java.io.OutputStream
%import java.io.PrintStream

%import java.nio.file.Path
%import java.nio.file.Paths
%import java.nio.file.Files

%import java.util.ArrayList
%import java.util.List
%import java.util.stream.Collectors

%import javax.measure.quantity.*
%import javax.measure.unit.SI
%import javax.measure.unit.NonSI


%import org.jscience.physics.amount.Amount

%import org.apache.poi.openxml4j.exceptions.InvalidFormatException

%import ncsa.hdf.hdf5lib.exceptions.HDF5LibraryException

%import configuration.*
%import configuration.enumerations.*

%import aircraft.*
%import aircraft.components.*

%import aircraft.components.FuelTank

%import aircraft.components.fuselage.*
%import aircraft.components.liftingSurface.*
%import aircraft.components.liftingSurface.creator.*
%import aircraft.components.nacelles.*
%import aircraft.components.powerplant.*

// TODO: analyses
// TODO: cad

%import calculators.*
%import calculators.aerodynamics.*
%import calculators.balance.*
%import calculators.costs.*
%import calculators.geometry.*
%import calculators.performance.*
%import calculators.stability.*
%import calculators.weights.*

%import database.*
%import database.databasefunctions.*
%import database.databasefunctions.aerodynamics.*
%import database.databasefunctions.aerodynamics.fusDes.*
%import database.databasefunctions.aerodynamics.vedsc.*
%import database.databasefunctions.engine.*

%import standaloneutils.*
%import standaloneutils.atmosphere.*
%import standaloneutils.cpacs.*
%import standaloneutils.customdata.*
%import standaloneutils.database.*
%import standaloneutils.jsbsim.*
%import standaloneutils.launchers.*
%import standaloneutils.mathtools.*
%import standaloneutils.aerotools.*

%import standaloneutils.aircraft.*

%import writers.*

Setup database files and create an Aircraft object: class JPADTest

We will read the input file ../in/aircraft_IRON_NEW.xml that defines a specific aircraft design named IRON. The following image is a top view of this airplane.

To do so we create a package named test.jpad that wraps a test class named JPADTest. Objects of this class incorporate the paths to all the files necessary for JPAD to work correctly.

In [5]:
package test.jpad;

public class JPADTest {
    
    public String pathToXML;
    public String aircraftFileName;
    
    public String dirAirfoil;
    public String dirFuselages;
    public String dirLiftingSurfaces;
    public String dirEngines;
    public String dirNacelles;
    public String dirLandingGears;
    public String dirCabinConfiguration;
    
    public String databaseFolderPath;
    public String inputFolderPath;
    public String outputFolderPath;

    public AerodynamicDatabaseReader aeroDatabaseReader;
    public HighLiftDatabaseReader highLiftDatabaseReader;
    public FusDesDatabaseReader fusDesDatabaseReader;
    public VeDSCDatabaseReader veDSCDatabaseReader;

    public Aircraft aircraft; // the main recipient object
    
    public JPADTest(String fileName) {
        
        aircraftFileName = fileName;
        
        System.out.println("--------------------------");
        System.out.println("Initialize JPADTest");
        System.out.println("--------------------------");
        
        MyConfiguration.setDir(
            FoldersEnum.DATABASE_DIR,
            Paths.get("../data").toString()
        );
        MyConfiguration.setDir(
            FoldersEnum.INPUT_DIR,
            Paths.get("../in").toString()
        );

        MyConfiguration.setDir(
            FoldersEnum.OUTPUT_DIR,
            Paths.get("../out").toString()
        );

        String aerodynamicDatabaseFileName = "Aerodynamic_Database_Ultimate.h5";
        String highLiftDatabaseFileName = "HighLiftDatabase.h5";
        String fusDesDatabaseFilename = "FusDes_database.h5";
        String vedscDatabaseFilename = "VeDSC_database.h5";

        databaseFolderPath = MyConfiguration.getDir(FoldersEnum.DATABASE_DIR);
        inputFolderPath = MyConfiguration.getDir(FoldersEnum.INPUT_DIR);
        outputFolderPath = MyConfiguration.getDir(FoldersEnum.OUTPUT_DIR);

        System.out.println("Database dir: " + databaseFolderPath);
        System.out.println("Input dir: " + inputFolderPath);
        System.out.println("Output dir: " + outputFolderPath);

        aeroDatabaseReader = DatabaseManager.initializeAeroDatabase(
            new AerodynamicDatabaseReader(
                databaseFolderPath,
                aerodynamicDatabaseFileName
                ),
            databaseFolderPath
            );

        highLiftDatabaseReader = DatabaseManager.initializeHighLiftDatabase(
            new HighLiftDatabaseReader(
                databaseFolderPath,
                highLiftDatabaseFileName),
            databaseFolderPath
            );

        fusDesDatabaseReader = DatabaseManager.initializeFusDes(
            new FusDesDatabaseReader(
                databaseFolderPath,
                fusDesDatabaseFilename),
            databaseFolderPath
            );

        veDSCDatabaseReader = DatabaseManager.initializeVeDSC(
            new VeDSCDatabaseReader(
                databaseFolderPath,
                vedscDatabaseFilename),
            databaseFolderPath
            );

        pathToXML = inputFolderPath + "/" + aircraftFileName;
        dirAirfoil = inputFolderPath + "/lifting_surfaces/airfoils";
        dirFuselages = inputFolderPath + "/fuselages";
        dirLiftingSurfaces = inputFolderPath + "/lifting_surfaces";
        dirEngines = inputFolderPath + "/engines";
        dirNacelles = inputFolderPath + "/nacelles";
        dirLandingGears = inputFolderPath + "/landing_gears";
        dirCabinConfiguration = inputFolderPath + "/cabin_configurations";
        // atmosphere = AtmosphereCalc.getAtmosphere(1000.);

        System.out.println("--------------------------");
        System.out.println("Initialization done");
        System.out.println("--------------------------");
        
        try {

            System.out.println("--------------------------");
            System.out.println("Aircraft import");
            System.out.println("--------------------------");

            aircraft = Aircraft.importFromXML(
                this.pathToXML,
                this.dirLiftingSurfaces,
                this.dirFuselages,
                this.dirEngines,
                this.dirNacelles,
                this.dirLandingGears,
                this.dirCabinConfiguration,
                this.dirAirfoil,
                this.aeroDatabaseReader,
                this.highLiftDatabaseReader,
                this.fusDesDatabaseReader,
                this.veDSCDatabaseReader
                );

        } catch (Throwable t) {
            t.printStackTrace();
            throw t;
        }        
    }
}
Out[5]:
test.jpad.JPADTest

Use of JPADTest: getting the shape of some airfoils

For the sake of simplicity, the class JPADTest has public member variables. Some of them are of types provided by the JPAD API. The class constructor JPADTest() defines the paths of all auxiliary input files and directories and takes as an input variable the name of the aircraft definition file (a file in XML format).

The following code cell defines an object test of type JPADTest, that incorporates all the necessary definitions to read the IRON aircraft design. The object test is initialized by passing the main aircraft definition file stored ../in/aircraft_IRON_NEW.xml

The test object has a public member variable aircraft of type Aircraft, which is populated with all the necessary data at construction time. The object test.aircraft models the chosen aircraft design.

In [6]:
package test.jpad;

import aircraft.components.liftingSurface.airfoils.*;

final PrintStream originalOut = System.out;
PrintStream filterStream = new PrintStream(
    new OutputStream() {
        public void write(int b) {
            // write nothing
        }
    });

Path pathToAirfoilsSVG = Paths.get("./images/IRON-airfoils.svg");
Path pathToAircraftTopViewSVG = Paths.get("./images/IRON-aircraft-topview.svg");
Path pathToAircraftSideViewSVG = Paths.get("./images/IRON-aircraft-sideview.svg");

System.setOut(filterStream); // deactivating System.out

JPADTest test = new JPADTest("aircraft_IRON_NEW.xml");

System.setOut(originalOut); // reactivating System.out

Airfoil airfoil0 = test.aircraft
    .getWing().getLiftingSurfaceCreator().getAirfoilList().get(0);

Airfoil airfoil1 = test.aircraft
    .getHTail().getLiftingSurfaceCreator().getAirfoilList().get(0);

boolean status = standaloneutils.aircraft.WriteUtils
    .writeAirfoilsToSVG(pathToAirfoilsSVG.toString(), "IRON wing/htail airfoils", new Airfoil[]{airfoil0, airfoil1});

System.out.println("--------------------------");
if (status)
    System.out.println("File " + pathToAirfoilsSVG.toString() + " created.");
else
    System.out.println("No airfoil file created.");

status = standaloneutils.aircraft.WriteUtils
    .writeAircraftTopViewToSVG(pathToAircraftTopViewSVG.toString(), "IRON aircraft top view", test.aircraft);

System.out.println("--------------------------");
if (status)
    System.out.println("File " + pathToAircraftTopViewSVG.toString() + " created.");
else
    System.out.println("No aircraft-topview file created.");

status = standaloneutils.aircraft.WriteUtils
    .writeAircraftSideViewToSVG(pathToAircraftSideViewSVG.toString(), "IRON aircraft side view", test.aircraft);

System.out.println("--------------------------");
if (status)
    System.out.println("File " + pathToAircraftSideViewSVG.toString() + " created.");
else
    System.out.println("No aircraft-sideview file created.");

return status;
--------------------------
File .\images\IRON-airfoils.svg created.
--------------------------
File .\images\IRON-aircraft-topview.svg created.
--------------------------
File .\images\IRON-aircraft-sideview.svg created.
Out[6]:
true

Showing the output SVG file

The above cell produces some SVG files which are shown below:

And the top view of the aircraft:

And the side view of the aircraft: