V1_2.java [src/java/m/dssat/substor] Revision: default Date:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package m.dssat.substor;
import csip.Config;
import csip.api.server.Executable;
import csip.ModelDataService;
import csip.api.client.ModelDataServiceCall;
import csip.api.server.PayloadParameter;
import csip.api.server.ServiceException;
import csip.annotations.Resource;
import javax.ws.rs.Path;
import csip.annotations.*;
import csip.utils.Parallel;
import csip.utils.ZipFiles;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import m.dssat.utils.DSSATresultParser;
import m.dssat.utils.PlantGro_datastructure;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
//TODO: add check on String length DSSBatch
@Name("DSSAT simulation execution")
@Description("DSSAT SUBSTOR csip service")
@VersionInfo("1.2")
@Path("m/substor/1.2")
@Options(timeout = "P2H")
@Resource(type = ResourceType.EXECUTABLE, file = "/DSSAT_4.7.5.8/dscsm047", id = "DSSAT")
@Resource(type = ResourceType.ARCHIVE, file = "/DSSAT_4.7.5.8/data.zip")
public class V1_2 extends ModelDataService {
private static final String TMP_DSSAT = "tmp.DSSAT";
static final String KEY_FILE = "param";
static final String SIM_TYPE = "simulation_type";
static final String KEY_LOGLEVEL = "loglevel";
static final String ID_SOIL = "id_soil";
static final String ID_WEATHER = "wsta....";
static final String LONGITUDE = "longitude";
static final String LATITUDE = "latitude";
static final String FILE_NAME = "file_name";
static final String COKEY = "cokey";
static final String START_DATE = "start_date";
static final String END_DATE = "end_date";
static final String SOL_EXT = ".SOL";
static final String WTH_EXT = ".WTH";
static final String OUT_PARAMS = "output_params";
public final String workspace = workspace().getPath().toString();
public String getWorkspace() {
return workspace;
}
@Override
protected void doProcess() throws Exception {
PayloadParameter inputParams = parameter();
String paramFile = inputParams.getString(KEY_FILE);
double longitude = inputParams.getDouble(LONGITUDE);
double latitude = inputParams.getDouble(LATITUDE);
String cokey = inputParams.getString(COKEY);
String start_date = inputParams.getString(START_DATE);
String end_date = inputParams.getString(END_DATE);
String simulation_type = inputParams.getString(SIM_TYPE);
String[] colNames = inputParams.getStringArray(OUT_PARAMS);
String flag = getSimulationType(simulation_type, paramFile);
XfileIDs ids = new XfileIDs();
parseXfile(ids, paramFile);
if (flag.toLowerCase().equals("a")) {
Parallel.run(Config.getBoolean("dssat.serial.datafetch", false),
() -> {
fetchClimate(ids.getWthID(), longitude, latitude, start_date, end_date);
},
() -> {
fetchSoil(ids, longitude, latitude, cokey);
}
);
} else {
fetchSoil(ids, longitude, latitude, cokey);
int startYear = Integer.valueOf(start_date.split("-")[0]);
int endYear = Integer.valueOf(end_date.split("-")[0]);
List<Years> wthFetch = new ArrayList<>();
for (int year = startYear; year <= endYear; year++) {
wthFetch.add(new Years(year, ids.getWthID()));
}
wthFetch.stream()
.parallel()
.forEach(y -> fetchClimate(
y.getFileName(),
longitude,
latitude,
y.getStart(),
y.getEnd()));
}
Executable p = resources().getExe("DSSAT");
p.addArguments(
flag,
paramFile
);
int ret = p.exec();
if (ret != 0) {
throw new ServiceException("Error running DSSAT 4.7.5.8: " + ret);
}
if (new File(workspace().getFile("PlantGro.OUT").getAbsolutePath()).exists()) {
DSSATresultParser parser = new DSSATresultParser(colNames, workspace().getFile("PlantGro.OUT"));
PlantGro_datastructure results = parser.parse();
results().put("results", results.get());
} else {
throw new FileNotFoundException("PlantGro.OUT");
}
}
public class Years {
private final String start;
private final String end;
private final String weatherFileName;
Years(int start, String weatherFileName) {
this.weatherFileName = weatherFileName + String.valueOf(start).substring(2) + "01";
this.start = start + "-01-01";
this.end = start + "-12-31";
}
public String getStart() {
return start;
}
public String getEnd() {
return end;
}
public String getFileName() {
return weatherFileName;
}
}
private String getSimulationType(String simulation_type, String paramFile) throws IOException {
switch (simulation_type.toLowerCase()) {
case "single":
return "A";
// case "batch":
// return "B";
// case "seasonal":
// return "N";
case "sequence":
createConfig(paramFile);
return "Q";
// case "spatial":
// return "S";
default:
String msg = "Simulation " + simulation_type + " not implemented yet";
throw new IllegalArgumentException(msg);
}
}
private void parseXfile(XfileIDs ids, String paramFile) throws IOException {
String file;
if (paramFile.matches("(.*).v[1-9][0-9]")) {
file = getFileX(paramFile);
} else {
file = paramFile;
}
getSoilWeatherFileIds(ids, file);
}
private String getFileX(String paramFile) throws IOException {
BufferedReader bf = new BufferedReader(new FileReader(workspace().getFile(paramFile)));
String line;
String filex = null;
Boolean toParse = Boolean.FALSE;
while ((line = bf.readLine()) != null) {
if (toParse) {
String[] parse = line.split("\\s+");
filex = parse[0];
toParse = Boolean.FALSE;
}
if (line.startsWith("@")) {
toParse = Boolean.TRUE;
}
}
bf.close();
String[] pathx = filex.split("/");
String namex = pathx[pathx.length - 1];
return namex;
}
private void getSoilWeatherFileIds(XfileIDs ids, String fileName) throws IOException, FileNotFoundException {
BufferedReader bf = new BufferedReader(new FileReader(workspace().getFile(fileName)));
String line;
while ((line = bf.readLine()) != null) {
if (line.toLowerCase().contains(ID_SOIL) && line.toLowerCase().contains(ID_WEATHER)) {
String[] tmp = line.split("\\s+");
int index = 0;
int soilIndex = 0;
int weatherIndex = 0;
for (String s : tmp) {
if (s.toLowerCase().equals(ID_SOIL)) {
soilIndex = index;
}
if (s.toLowerCase().equals(ID_WEATHER)) {
weatherIndex = index;
}
index++;
}
line = bf.readLine();
String[] lineSplit = line.trim().split("\\s+");
String solID = lineSplit[soilIndex];
String wthID = lineSplit[weatherIndex];
ids.setSolID(solID);
ids.setWthID(wthID);
break;
}
}
bf.close();
}
@Override
protected void postProcess() throws Exception {
File zip = ZipFiles.zip(new File(workspace().getDir().getAbsolutePath()));
results().put(zip);
}
public static void onContextInit() {
System.out.println("Init simple service");
}
public static void onContextDestroy() {
System.out.println("destroy simple service");
}
private void fetchClimate(String weatherFile_name, double longitude, double latitude, String start_date, String end_date) {
ModelDataServiceCall res;
try {
res = new ModelDataServiceCall()
.put(FILE_NAME, weatherFile_name)
.put(LONGITUDE, longitude)
.put(LATITUDE, latitude)
.put(START_DATE, start_date)
.put(END_DATE, end_date)
.url(Config.getString("dssat.weather.service", "http://csip.engr.colostate.edu:8088/csip-climate/d/csm_weather/1.0"))
.withDefaultLogger()
.call();
} catch (Exception ex) {
throw new RuntimeException("Fetchin weather");
}
if (res.serviceFinished()) {
String fileName = weatherFile_name + WTH_EXT;
try {
res.download(fileName, workspace().getFile(fileName));
} catch (Exception ex) {
throw new RuntimeException("donwloading");
}
} else {
throw new RuntimeException(res.getError());
}
}
private void fetchSoil(XfileIDs ids, double longitude, double latitude, String cokey) throws Exception {
String soilFile_name = ids.getSolID().substring(0, 2);
ModelDataServiceCall res = new ModelDataServiceCall()
.put(FILE_NAME, soilFile_name)
.put(LONGITUDE, longitude)
.put(LATITUDE, latitude)
.put(COKEY, cokey)
.put(ID_SOIL, ids.getSolID())
.url(Config.getString("dssat.soil.service", "http://csip.engr.colostate.edu:8088/csip-soils/d/csm_soils/1.0"))
.withDefaultLogger()
.call();
if (res.serviceFinished()) {
String fileName = soilFile_name + SOL_EXT;
res.download(fileName, workspace().getFile(fileName));
} else {
throw new ServiceException(res.getError());
}
}
private File renameInputTemplate(String template) {
String sourcePath = workspace().getFile(template).getAbsolutePath();
String destPath = workspace().getDir().getAbsolutePath() + "/" + TMP_DSSAT;
File sourceTemplate = new File(sourcePath);
File destTemplate = new File(destPath);
try {
Files.copy(sourceTemplate.toPath(), destTemplate.toPath());
} catch (IOException ex) {
throw new RuntimeException(ex.getMessage());
}
try {
if (!Files.readAllLines(sourceTemplate.toPath()).equals(Files.readAllLines(destTemplate.toPath()))) {
throw new RuntimeException("File not fully copied");
} else {
Files.delete(sourceTemplate.toPath());
}
} catch (IOException ex) {
throw new RuntimeException(ex.getMessage());
}
return sourceTemplate;
}
private File createConfig(String template) throws IOException {
VelocityEngine velocity = new VelocityEngine();
velocityInit(velocity);
VelocityContext context = new VelocityContext();
context.put("dssat", this);
File filledTemplate = renameInputTemplate(template);
try (FileWriter w = new FileWriter(filledTemplate.getAbsolutePath())) {
Template vt = velocity.getTemplate(TMP_DSSAT, "utf-8");
vt.merge(context, w);
}
return filledTemplate;
}
public void velocityInit(VelocityEngine velocity) {
velocity.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH,
workspace().getDir().getAbsolutePath());
velocity.init();
}
class XfileIDs {
private String solID;
private String wthID;
public void setSolID(String solID) {
this.solID = solID;
}
public void setWthID(String wthID) {
this.wthID = wthID;
}
public String getSolID() {
return solID;
}
public String getWthID() {
return wthID;
}
}
}