anyLogistix
Expand
Font size

Phase 2. Product&Distance-based Transportation Cost Calculator

In the previous phase we imported the supply chain scenario, ran the experiment and observed the results. In this phase we will observe the inside of the cost calculator, and prepare it for further customization.

Our supply chain uses the Product&distance-based transportation cost calculator, which is one of the default calculators available in anyLogistix. It estimates the transportation costs by multiplying the Cost per unit value (defined in the Cost Calculation Parameters) by the volume of the product delivered and by the distance between From and To locations.

But what if the default calculators do not satisfy the requirements of calculating the transportation costs. This is where anyLogistix extensions come in handy, which allow us to create a custom implementation of the required transportation calculator.

Let us first observe the inside of the standard Product&distance-based calculator to analyze its structure, and understand how it calculates transportation costs.

Download the Product&distance-based calculator's source file. It is an .alp file that is used for creating and modifying extensions in the anyLogistix Extensions Editor, which is a built-in version of AnyLogic simulation software.

Open the anyLogistix Extensions Editor

  1. Choose Extension > Edit… from the anyLogistix main menu.
    The Extensions Editor will open

Now we need to open the downloaded file

Open the calculator's source file in the Extensions Editor

  1. Choose File > Open… from the menu bar.
  2. In the opened window navigate to the folder with the downloaded file and select it.
  3. Click Open. The calculator will open in the Extension Editor.

The model contains:

  • The calculator's Java class.
  • The Export to ALX control, which creates a .jar file, which will be used in the anyLogistix.
  • The database functionality, allowing us to add and manage databases within the model.

Let us open the TCCTutorialDefaultPolicy Java class.

Each calculator in anyLogistix is a java class with strict structure and a set of the required methods. That means that creating or editing calculators requires java programming skills.

Now we will walk through all the elements of this calculator to have a better understanding of their roles, and to be able later change them to create the required calculator.

Imports section

The first section of each java class contains lists and packages required for this class to work properly. By importing these classes, we allow the class to create and use implementations of these classes, and allow the usage of all public methods from them. The code will not work without these statements.

import com.alx.data.basic.facility.*;
import com.alx.data.basic.product.*;
import com.alx.data.basic.transportation.*;
import com.alx.data.simulation.model.*;
import com.alx.data.simulation.path.*;
import com.alx.data.custom_type.*;
import com.alx.data.basic.units.MeasurementUnit;

In this case all the imported classes are internal anyLogistix classes. The transportation cost calculator that we are analyzing is using the methods and objects of these classes to properly define the costs calculation logic.

CustomTypeClass annotation

Right before the start of the calculator class, we have the annotation containing the editable calculator name. This should be a meaningful name that we will select from the Cost Calculation drop-down list in the Path table.

@CustomTypeClass(name = "TCC Tutorial Default Policy")

Class declaration

The actual class starts after the @CustomTypeClass annotation. The beginning of every class is a class declaration. Here we can see that the TCCTemplate is the name of the java class that we will use for storing the logic of our custom calculator. After the class name we can see the ITransportationCostCalculator interface that our class implements. This interface defines the set of methods the class has to implement to work properly. Each implemented method is marked with the @Override annotation.

public class TCCTemplate implements ITransportationCostCalculator{

Parameters

By selecting the cost calculator in the Path table, we define the way the transportation costs should be estimated. The same calculator can be used for several paths. But the calculator parameters' values can differ.

To be able to use one extension, we need to define calculation logic based on its parameters instead of the actual values. The values for these parameters are defined in the anyLogistix Path.

The calculator we are editing has three parameters defined in the @Parameter annotation.

Each parameter defines the name and the type of the editor to use for value editing. After the annotation and its parameters the variable is created. This variable will be further used in the cost calculation logic, and it will keep the value of the parameter, specified in the Path table.

The type of the variable and the parameter should be the same.

@Parameter(name = "Amount unit", type = EditorType.MeasurementUnitVolumeEditor)
private MeasurementUnit unit;

@Parameter(name = "Cost per unit", type = EditorType.DoubleEditor)
private double costPerUnit;

@Parameter(name = "CO2 per unit", type = EditorType.DoubleEditor)
private double co2PerUnit;

The first parameter is the Amount unit. Its value is selected from the drop-down list in the Cost Calculation Parameters dialog from, and is assigned to the variable unit of MeasurementUnit type. Double parameters also have their own names, but the values are specified via the double editor, and then assigned to the double variables.

Methods

Now comes the extension's main part – the cost calculation logic. Most of the methods defined in this class have an @Override annotation, so all of them came from the ITransportationCostCalculator interface. The methods from the interface are made to be agile, so certain arguments may not be used in the exact calculator. All the methods were divided by the meaning into 4 groups that:

Methods defining the main cost calculation logic

Here we have three overridden methods from the interface: cost(), flowCost(), vehicleFlowCost(), and the additional method getTotalVolume(), which is used in the cost method. Each overridden method has a comment with its description.

The Optimization-based and the Simulation-based experiments use different methods to calculate the transportation cost. So, if you plan to use the calculator in a specific experiment only, you can skip the methods used for the other experiment type.

@Override
public double cost(IShipment shipment, double distance, VehicleType vehicleType) {
	return getTotalVolume(shipment) * distance * costPerUnit;
}

The cost() method calculates the transportation cost for one shipment in the simulation-based experiments as:

total volume of the products in this shipment * the distance over which this shipment was made * the cost of delivery of 1 product unit over 1 distance unit.

The total volume of the shipment is calculated in the additional getTotalVolume() method.

public double getTotalVolume(IShipment shipment) {
    double volume = 0;
    for (IOrder order : shipment.getOrders()) {
        Product product = order.getProduct();
        volume += order.getQuantity() * product.convertToUnit(unit);
    }
    return volume;
}

Here we create a new volume variable of double type. It accumulates the value of the products and returns it to the cost() method.

One shipment can contain several orders, so to calculate the total volume of the product in the shipment we need to get through all the orders in shipment. We can do that with the Java for-each loop.

Now that we have the data of each order in the shipment, we need to know the products this order contains. We created the new product variable and assigned the product from the order to it.

The volume of the order is calculated for each iteration, and added to the volume variable. The order volume is calculated as:

quantity of the product in the specified product unit * converting coefficient from this product unit to the unit defined in the parameter.

@Override
public double flowCost(Product product, double flowSize, double distance, VehicleType vehicleType) {
	return costPerUnit * flowSize * product.convertToUnit(unit) * distance;
}

The flowCost() method is mainly used in the Network Optimization experiments (NO and TO) and rarely in the Simulation-based experiments.

In the optimization experiments the transportation cost is calculated separately for the product-based and vehicle-based policies. The Product-based calculators use the flowCost() method, and the vehicle-based ones use the vehicleFlowCost() method. It makes possible collecting statistics on transportation cost from different sources (in the Product Flows and Vehicle Flows result tables). In this tutorial we are working with the Product&distance-based calculator, so the calculation is made with the flowCost() method. The value of the transportation cost is calculated as:
cost per unit * the size of the flow in the defined unit * the delivery distance.

The Simulation experiment uses this method for the Cheapest sourcing policy types (static and dynamic). It chooses the cheapest pair of source-destination locations by calculating and comparing the transportation cost of 1 product unit between them. The method returns the value of multiplying the costPerUnit variable, the flow size (in this case 1 pcs) and the distance of the delivery.

@Override
public double vehicleFlowCost(double flowSize, double distance, VehicleType vehicleType){
	return 0;
}

As we have discovered earlier the observing policy do not have a vehicle-component of the cost, so the vehicleFlowCost() method returns 0.

CO2 emission calculation methods

The transportation cost calculator also estimates CO2 emissions produced by the vehicle. The methods the calculator uses have the same calculation logic as the methods from the previous group. The differences are:

  • In the variable, which is co2PerUnit this time.
  • The flowCO2Emissions() method is not used in the sourcing calculations.
@Override
public double CO2Emissions(IShipment shipment, double distance, VehicleType vehicleType) {
	return getTotalVolume(shipment) * distance * co2PerUnit;
}

@Override
public double flowCO2Emissions(Product product, double flowSize, double distance, VehicleType vehicleType) {
	return co2PerUnit * flowSize * product.convertToUnit(unit) * distance;
}

@Override
public  double vehicleFlowCO2Emissions(double flowSize, double distance, VehicleType vehicleType){
	return 0;
}

Since we are not going to calculate the CO2 emission in this tutorial, we will not be using these methods.

Return cost calculation

There is only vehicleReturnFlowCost() method in this group. This method is used only in the Transportation Optimization experiments for calculating the transportation cost of the vehicle, which is returning to the sourcing facility after visiting the last customer of the route.

@Override
public double vehicleReturnFlowCost(double flowSize, double distance, VehicleType vehicleType, IFacilityData from, IFacilityData to) {
    return vehicleFlowCost(flowSize, distance, vehicleType, from, to);
}

The cost of the return flow is calculated by calling the vehicleFlowCost() method with the same parameters. This is the default implementation of this method, so it is calculated the same way by the most of the default transportation cost calculators.

Extended methods

Here we have a group of methods, which have similar names to the methods from the first and the second groups, but they have two additional arguments referring to the source and destination facilities. The from and to arguments can be used for defining more advanced conditions and logic. By default, these methods are calling the early defined methods without using the additional arguments.

@Override
public double cost(IShipment shipment, double distance, VehicleType vehicleType, IFacility from, IFacility to) {
return cost(shipment, distance, vehicleType);
}

@Override
public double flowCost(Product product, double flowSize, double distance, VehicleType vehicleType, IFacilityData from, IFacilityData to){
return flowCost(product, flowSize, distance, vehicleType);
}

@Override
public double vehicleFlowCost(double flowSize, double distance, VehicleType vehicleType, IFacilityData from, IFacilityData to){
return vehicleFlowCost(flowSize, distance, vehicleType);
}

@Override
public double CO2Emissions(IShipment shipment, double distance, VehicleType vehicleType, IFacility from, IFacility to){
return CO2Emissions(shipment, distance, vehicleType);
}

@Override
public double flowCO2Emissions(Product product, double flowSize, double distance, VehicleType vehicleType, IFacilityData from, IFacilityData to){
return flowCO2Emissions(product, flowSize, distance, vehicleType);
}

@Override
public double vehicleFlowCO2Emissions(double flowSize, double distance, VehicleType vehicleType, IFacilityData from, IFacilityData to){
return vehicleFlowCO2Emissions(flowSize, distance, vehicleType);
}

Description methods

To be able to observe the values of the inserted parameters in the anyLogistix tables without opening the parameters dialog, a special description is created and placed in the Cost Calculation Parameters and CO2 Calculation Parameters columns of the Paths table. These descriptions are created by the calculator and can be customized if needed.

@Override
public String getDescription() {
return costPerUnit + " * product(" + unit + ") * distance";
}

@Override
public String getDescription2() {
return co2PerUnit + " * product(" + unit + ") * distance";
}

As we can see, the returned value should be of String type, so that we could simply create the required description by adding together parameters values and textual insertions. The String received from the getDescription() method will be placed in the Cost Calculator Parameters cell, and the getDescription2() method in the CO2 Calculator Parameters field.

There. We have reached the end of the calculator logic. Now, since we understand all the methods and the calculator itself, we can move to the next step of the tutorial.

How can we improve this article?