V1_0.java [src/java/m/nfat] Revision: default Date:
/*
* $Id$
*
* This file is part of the Cloud Services Integration Platform (CSIP),
* soilNitro Model-as-soilNitro-Service framework, API, and application suite.
*
* 2012-2017, OMSLab, Colorado State University.
*
* OMSLab licenses this file to you under the MIT license.
* See the LICENSE file in the project root for more information.
*/
package m.nfat;
import csip.ModelDataService;
import csip.ServiceException;
import csip.annotations.Polling;
import csip.annotations.Resource;
import csip.utils.JSONUtils;
import database.DBQueries;
import database.DBResources;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Path;
import rotation_utils.nodes.Event;
import rotation_utils.nodes.Fertilizer;
import rotation_utils.nodes.Rotation;
import csip.annotations.Description;
import csip.annotations.Name;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import rotation_utils.nodes.Crop;
import rotation_utils.nodes.Management;
import rotation_utils.utils.TranslatorException;
/**
* Energy-02: Compute Nitrogen Energy Consumption Awareness Output
*
* @version 1.0
* @author Rumpal Sidhu
*/
@Name("Energy-02:Compute Nitrogen Energy Consumption Awareness Output")
@Description("This service computes fertilizer nitrogen application amounts and "
+ "costs for a crop rotation and uses crop nitrogen requirements and "
+ "optimum fertilization rates to calculate potential reductions to "
+ "the amount of fertilizer applied and cost savings.")
@Path("m/nfat/1.0")
@Polling(first = 10000, next = 2000)
@Resource(from = DBResources.class)
public class V1_0 extends ModelDataService {
private double acres, soilNitrogen = 0;
private ArrayList<LRotation> rotationList = new ArrayList<>();
private ArrayList<Rotation> inputRotationList = new ArrayList<>();
@Override
public void preProcess() throws ServiceException, JSONException, TranslatorException {
acres = getDoubleParam("aoa_acres");
String soilNitroLBS = getStringParam("soil_nitrogen_lbs_acre");
double soilNitrogenLbsAcre = Double.valueOf(soilNitroLBS.equals("null") ? "-1" : soilNitroLBS);
//Validating
JSONArray soilNitrogenArray = getJSONArrayParam("soil_nitrogen_ppm");
if (!soilNitroLBS.equals("null") && soilNitrogenArray.length() > 0) {
throw new ServiceException("Either the soil_nitrogen_lbs_acre should be null or soil_nitrogen_ppm should be an empty array.");
}
//Calculating soil nitrogen
if (soilNitrogenLbsAcre == -1) {
for (int i = 0; i < soilNitrogenArray.length(); i++) {
Map<String, JSONObject> soilNitro = JSONUtils.preprocess(soilNitrogenArray.getJSONArray(i));
double ppm = JSONUtils.getDoubleParam(soilNitro, "ppm", 0);
double depth = JSONUtils.getDoubleParam(soilNitro, "depth", 0);
soilNitrogen += ppm * 2 * depth / 6;
}
} else {
soilNitrogen = soilNitrogenLbsAcre;
}
soilNitrogen *= acres;
//Rotation data
JSONArray rotationsArray = getJSONArrayParam("rotations");
for (int i = 0; i < rotationsArray.length(); i++) {
inputRotationList.add(Rotation.deserializeRot(rotationsArray.getJSONObject(i)));
}
}
@Override
public void doProcess() throws TranslatorException, ServiceException, SQLException {
try (Connection connection = getResourceJDBC(DBResources.CRLMOD);
Statement statement = connection.createStatement();) {
rotationCalc(statement);
}
}
public void rotationCalc(Statement statement) throws ServiceException, SQLException {
for (Rotation rotation : inputRotationList) {
double rotationNitrogenApplied = 0, rotationNitrogenCost = 0, rotationPotentialNSavings = 0, rotationPotentialNCostSavings = 0;
ArrayList<CropInterval> cropIntervalList = null;
for (Management management : rotation.managements) {
List<Event> eventList = management.getManagementData();
if (eventList != null) {
cropIntervalList = cropIntervalCalc(statement, eventList);
for (CropInterval ci : cropIntervalList) {
rotationNitrogenApplied += ci.getNitrogenApplied();
rotationNitrogenCost += ci.getNitrogenCost();
rotationPotentialNSavings += ci.getPotentialNsavings();
rotationPotentialNCostSavings += ci.getPotentialNcostSavings();
}
}
}
rotationList.add(new LRotation(roundValues(rotationNitrogenApplied),
roundValues(rotationNitrogenCost),
roundValues(rotationPotentialNSavings),
roundValues(rotationPotentialNCostSavings),
cropIntervalList));
}
}
public ArrayList<CropInterval> cropIntervalCalc(Statement statement, List<Event> eventList) throws ServiceException, SQLException {
//Initializing list of crop intervals
ArrayList<CropInterval> cropIntervalList = new ArrayList<>();
//Variables to store the event list position for the harvest operation of the previous crop in the rotation
int previousCropHarvet = 0;
//Finding and adding crop intervals to the cropIntervalList
for (int i = 0; i < eventList.size();) {
if (eventList.get(i).getOperation().begin_growth) {
Crop crop = eventList.get(i).getCrop();
double nitrogenYield = 0;
int cropType = 0;
try (ResultSet resultSet = statement.executeQuery(DBQueries.ENERGY01(crop.getID()))) {
if (resultSet.next()) {
cropType = resultSet.getInt("wqm_crop_type");
nitrogenYield = resultSet.getDouble("wqm_nitrogen_yield");
}
}
if (cropType != 5 && cropType != 6) {
CropInterval cropInterval = new CropInterval();
cropInterval.setCropName(crop.getName());
LocalDate plantingDate = eventList.get(i).getDate();
int harvetEvent = 0;
for (int j = i + 1;; j++) {
if (eventList.get(j).getOperation().kill_crop) {
harvetEvent = j;
i = j + 1;
break;
}
}
//Obtaining the list of fertilizer(s) applied to a crop interval
ArrayList<LFertilizer> fertList = getFertilizerList(eventList, plantingDate, previousCropHarvet, harvetEvent);
double cropNitrogenApplied = 0;
double cropNitrogenCost = 0;
double fertCost = 0;
for (LFertilizer fert : fertList) {
cropNitrogenApplied += fert.getNitrogenApplied();
cropNitrogenCost += fert.getNitrogenCost();
fertCost = fert.getPrice();
}
cropInterval.setFerList(fertList);
cropInterval.setNitrogenApplied(roundValues(cropNitrogenApplied));
cropInterval.setNitrogenCost(roundValues(cropNitrogenCost));
double cropYield = crop.getYield() > 0 ? crop.getYield() : crop.defaultYield;
double cropNitrogenRequired = cropYield * nitrogenYield * acres;
cropInterval.setNitrogenReq(roundValues(cropNitrogenRequired));
double targetNitrogenAmount = cropNitrogenRequired * 1.2 - soilNitrogen;
cropInterval.setTargetNitrogenAmt(roundValues(targetNitrogenAmount));
double cropPotentialNSavings = cropNitrogenApplied - targetNitrogenAmount;
cropPotentialNSavings = cropPotentialNSavings < 0 ? 0 : cropPotentialNSavings;
cropInterval.setPotentialNsavings(roundValues(cropPotentialNSavings));
double cropPotentialNCostSavings = cropPotentialNSavings * (fertCost / 2000.0);
cropInterval.setPotentialNcostSavings(roundValues(cropPotentialNCostSavings));
previousCropHarvet = harvetEvent + 1;
cropIntervalList.add(cropInterval);
} else {
i++;
}
} else {
i++;
}
}
return cropIntervalList;
}
public ArrayList<LFertilizer> getFertilizerList(List<Event> eventList, LocalDate plantingDate, int previousCropHarvet, int harvetEvent) {
//Obtaining the list of fertilizer(s) applied to a crop interval
ArrayList<LFertilizer> fertList = new ArrayList<>();
for (int k = previousCropHarvet; k <= harvetEvent; k++) {
LocalDate fertilizingDate;
Event e = eventList.get(k);
if (e.getFertilizer() != null) {
LFertilizer fert = new LFertilizer();
fertilizingDate = e.getDate();
fert.setDate(fertilizingDate);
fert.setOperationId(e.getOperation().id);
fert.setOperationName(e.getOperation().name);
if (fertilizingDate.isBefore(plantingDate)) {
long daysBeforePlanting = fertilizingDate.until(plantingDate, ChronoUnit.DAYS);
fert.setDaysBeforePlanting(daysBeforePlanting);
fert.setDayesAfterPlanting(-1);
} else {
long daysAfterPlanting = plantingDate.until(fertilizingDate, ChronoUnit.DAYS);
fert.setDayesAfterPlanting(daysAfterPlanting);
fert.setDaysBeforePlanting(-1);
}
fertilizationCalc(e, fert);
fertList.add(fert);
}
}
return fertList;
}
public void fertilizationCalc(Event event, LFertilizer fert) {
Fertilizer fertilizer = event.getFertilizer();
if (fertilizer.getFertilizer_placement(event) != null && !fertilizer.getFertilizer_placement(event).isEmpty()) {
String fertilizerPlacement = fertilizer.getFertilizer_placement(event);
fert.setFertilizerPlacement(fertilizerPlacement);
}
double rate = fertilizer.getFertilizer_rate(event) != 0 ? fertilizer.getFertilizer_rate(event) : fertilizer.getDefaultRate();
double nitrogenApplied = rate * fertilizer.getNitrogen_pct() / 100.0 * acres;
fert.setNitrogenApplied(nitrogenApplied);
double price = fertilizer.getFertilizer_price(event) != 0 ? fertilizer.getFertilizer_price(event) : fertilizer.getDefaultPrice();
fert.setPrice(price);
double nitrogenCost = rate * acres * price / 2000.0;
fert.setNitrogenCost(nitrogenCost);
}
public double roundValues(double v) {
return (Math.round(v * 100.0) / 100.0);
}
@Override
public void postProcess() throws JSONException {
JSONArray rotationArray = new JSONArray();
for (LRotation rotation : rotationList) {
JSONObject rotationObject = new JSONObject();
JSONArray cropIntervalArr = new JSONArray();
for (CropInterval cropInterval : rotation.getCropIntervalList()) {
JSONObject cropIntervalObj = new JSONObject();
cropIntervalObj.put("crop_name", cropInterval.getCropName());
cropIntervalObj.put("crop_nitrogen_req", cropInterval.getNitrogenReq());
cropIntervalObj.put("target_nitrogen_amt", cropInterval.getTargetNitrogenAmt());
JSONArray fertilizerEventArray = new JSONArray();
for (LFertilizer fertilizer : cropInterval.getFerList()) {
JSONObject fertilizerEventObj = new JSONObject();
fertilizerEventObj.put("fertilization_date", fertilizer.getDate());
fertilizerEventObj.put("operation_id", fertilizer.getOperationId());
fertilizerEventObj.put("operation_name", fertilizer.getOperationName());
if (fertilizer.getDaysBeforePlanting() != -1) {
fertilizerEventObj.put("days_before_planting", fertilizer.getDaysBeforePlanting());
} else {
fertilizerEventObj.put("days_after_planting", fertilizer.getDayesAfterPlanting());
}
fertilizerEventObj.put("fertilizer_placement", fertilizer.getFertilizerPlacement());
fertilizerEventObj.put("event_nitrogen_applied", fertilizer.getNitrogenApplied());
fertilizerEventObj.put("event_nitrogen_cost", fertilizer.getNitrogenCost());
fertilizerEventArray.put(fertilizerEventObj);
}
cropIntervalObj.put("fertilization_event", fertilizerEventArray);
cropIntervalObj.put("crop_nitrogen_applied", cropInterval.getNitrogenApplied());
cropIntervalObj.put("crop_nitrogen_cost", cropInterval.getNitrogenCost());
cropIntervalObj.put("crop_potential_N_savings", cropInterval.getPotentialNsavings());
cropIntervalObj.put("crop_potential_N_cost_savings", cropInterval.getPotentialNcostSavings());
cropIntervalArr.put(cropIntervalObj);
}
rotationObject.put("crop_interval", cropIntervalArr);
rotationObject.put("rotation_nitrogen_applied", rotation.getNitrogenApplied());
rotationObject.put("rotation_nitrogen_cost", rotation.getNitrogenCost());
rotationObject.put("rotation_potential_N_savings", rotation.getPotentialNSavings());
rotationObject.put("rotation_potential_N_cost_savings", rotation.getPotentialNCostSavings());
rotationArray.put(rotationObject);
}
putResult("rotation", rotationArray);
}
class LRotation {
private double nitrogenApplied, nitrogenCost, potentialNSavings, potentialNCostSavings;
private ArrayList<CropInterval> cropIntervalList;
public LRotation(double nitrogenApplied, double nitrogenCost, double potentialNSavings, double potentialNCostSavings, ArrayList<CropInterval> cropList) {
this.nitrogenApplied = nitrogenApplied;
this.nitrogenCost = nitrogenCost;
this.potentialNSavings = potentialNSavings;
this.potentialNCostSavings = potentialNCostSavings;
this.cropIntervalList = cropList;
}
public ArrayList<CropInterval> getCropIntervalList() {
return cropIntervalList;
}
public double getNitrogenApplied() {
return nitrogenApplied;
}
public double getNitrogenCost() {
return nitrogenCost;
}
public double getPotentialNCostSavings() {
return potentialNCostSavings;
}
public double getPotentialNSavings() {
return potentialNSavings;
}
}
class CropInterval {
private String cropName;
private double nitrogenReq;
private double targetNitrogenAmt;
private double nitrogenApplied, nitrogenCost, potentialNsavings, potentialNcostSavings;
private ArrayList<LFertilizer> ferList;
public CropInterval() {
}
public String getCropName() {
return cropName;
}
public ArrayList<LFertilizer> getFerList() {
return ferList;
}
public double getNitrogenApplied() {
return nitrogenApplied;
}
public double getNitrogenCost() {
return nitrogenCost;
}
public double getNitrogenReq() {
return nitrogenReq;
}
public double getPotentialNcostSavings() {
return potentialNcostSavings;
}
public double getPotentialNsavings() {
return potentialNsavings;
}
public double getTargetNitrogenAmt() {
return targetNitrogenAmt;
}
public void setCropName(String cropName) {
this.cropName = cropName;
}
public void setFerList(ArrayList<LFertilizer> ferList) {
this.ferList = ferList;
}
public void setNitrogenApplied(double nitrogenApplied) {
this.nitrogenApplied = nitrogenApplied;
}
public void setNitrogenCost(double nitrogenCost) {
this.nitrogenCost = nitrogenCost;
}
public void setNitrogenReq(double nitrogenReq) {
this.nitrogenReq = nitrogenReq;
}
public void setPotentialNcostSavings(double potentialNcostSavings) {
this.potentialNcostSavings = potentialNcostSavings;
}
public void setPotentialNsavings(double potentialNsavings) {
this.potentialNsavings = potentialNsavings;
}
public void setTargetNitrogenAmt(double targetNitrogenAmt) {
this.targetNitrogenAmt = targetNitrogenAmt;
}
}
class LFertilizer {
private LocalDate date;
private String operationId, operationName;
private long daysBeforePlanting;
private long dayesAfterPlanting;
private String fertilizerPlacement;
private double price, nitrogenApplied, nitrogenCost;
public LFertilizer() {
}
public void setDate(LocalDate date) {
this.date = date;
}
public void setDayesAfterPlanting(long dayesAfterPlanting) {
this.dayesAfterPlanting = dayesAfterPlanting;
}
public void setDaysBeforePlanting(long daysBeforePlanting) {
this.daysBeforePlanting = daysBeforePlanting;
}
public void setFertilizerPlacement(String fertilizerPlacement) {
this.fertilizerPlacement = fertilizerPlacement;
}
public void setNitrogenApplied(double nitrogenApplied) {
this.nitrogenApplied = nitrogenApplied;
}
public void setNitrogenCost(double nitrogenCost) {
this.nitrogenCost = nitrogenCost;
}
public void setOperationId(String operationId) {
this.operationId = operationId;
}
public void setOperationName(String operationName) {
this.operationName = operationName;
}
public void setPrice(double price) {
this.price = price;
}
public double getPrice() {
return price;
}
public LocalDate getDate() {
return date;
}
public long getDayesAfterPlanting() {
return dayesAfterPlanting;
}
public long getDaysBeforePlanting() {
return daysBeforePlanting;
}
public String getFertilizerPlacement() {
return fertilizerPlacement;
}
public double getNitrogenApplied() {
return nitrogenApplied;
}
public double getNitrogenCost() {
return nitrogenCost;
}
public String getOperationId() {
return operationId;
}
public String getOperationName() {
return operationName;
}
}
}