V1_0.java [src/java/m/rhem/rhem06_riskassessment] Revision: 6430128f5cb718133aa21444f0c889f36bd1efde  Date: Mon Feb 01 09:29:27 MST 2021
/*
 * $Id$
 *
 * This file is part of the Cloud Services Integration Platform (CSIP),
 * a Model-as-a-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.rhem.rhem06_riskassessment;

import csip.Executable;
import csip.ModelDataService;
import csip.ServiceException;
import csip.annotations.Polling;
import csip.annotations.Resource;
import static csip.annotations.ResourceType.OUTPUT;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.Path;
import csip.annotations.Description;
import csip.annotations.Name;
import rhem.utils.DBResources;
import rhem.utils.RHEMUtils;
import rhem.utils.RhemResources;
import static rhem.utils.RhemResources.RHEM_RA_EXE;

/**
 * RHEM-06: Risk Assessment
 *
 * @author rumpal
 * @version 1.0
 */
@Name("RHEM-06: Risk Assessment")
@Description("This service runs risk assessment with a maximum of five detailed output files.")
@Path("m/rhem/riskassessment/1.0")
@Polling(first = 10000, next = 2000)
@Resource(file = "*.out *.run", type = OUTPUT)
@Resource(from = DBResources.class)
@Resource(from = RhemResources.class)
public class V1_0 extends ModelDataService {

  private String baseLineScenarioFileName;
  private List<String> raScenarioFilenameList = new ArrayList<>();
  private List<String> scenarioNames = new ArrayList<>();


  @Override
  public void preProcess() throws ServiceException {
    int numberOfFiles = parameter().getInt("number_of_files");
    if (numberOfFiles > 5) {
      throw new ServiceException("The risk assessment can be performed with a maximum of five scenarios.");
    }
    for (int i = 1; i < numberOfFiles; i++) {
      raScenarioFilenameList.add(parameter().getString("scenario_" + i + "_filename"));
    }
    baseLineScenarioFileName = parameter().getString("base_line_scenario_file");
  }


  @Override
  public void doProcess() throws Exception {
    writeOutFilesToWorkspace();
    generateRARunFile();
    try {
      runRiskAssessment();
    } catch (IOException ex) {
      throw new ServiceException(ex);
    }

    double[][] outRAarray = readRiskAssessmentOutFile();
    List<List> interpolatedResultsArray = calculateReturnPeriods(outRAarray);
    writeInterpolatedResultsArray(interpolatedResultsArray);
  }


  private void writeOutFilesToWorkspace() throws ServiceException {
    attachments().getFile(baseLineScenarioFileName);
    for (String filename : raScenarioFilenameList) {
      attachments().getFile(filename);
    }
  }


  private void generateRARunFile() throws Exception {
    try (PrintWriter writer = new PrintWriter(workspace().getFile("risk_assessment.run"))) {
      writer.println(baseLineScenarioFileName);
      for (String fileName : raScenarioFilenameList) {
        writer.println(fileName);
      }
    }
  }


  private void runRiskAssessment() throws ServiceException, IOException {
    Executable rh = resources().getExe(RHEM_RA_EXE);
    rh.setArguments("-b", workspace().getFile("risk_assessment.run").toPath());
    int run = rh.exec();
    if (run != 0) {
      throw new ServiceException("Problem in running risk assessment.");
    }
  }


  private double[][] readRiskAssessmentOutFile() throws Exception {
    double outputRAArray[][] = new double[12][raScenarioFilenameList.size() + 2];
    int count = -1;
    boolean check = false;
    try (BufferedReader bufferedReader = new BufferedReader(
        new FileReader(workspace().getFile("risk_assessment.OUT")))) {
      String line;
      while ((line = bufferedReader.readLine()) != null && !check) {
        if (line.contains("FREQUENCY ANALYSIS")) {
          check = true;
          for (int i = 0; i < 3; i++) {
            line = bufferedReader.readLine();
            if (i == 2) {
              String[] test = line.trim().split("\\s+");
              for (int j = 1; j < test.length; j++) {
                scenarioNames.add(test[j]);
              }
            }
          }
        }
      }
      do {
        count++;
        String[] test = line.trim().split("\\s+");
        for (int i = 0; i < test.length; i++) {
          outputRAArray[count][i] = Double.parseDouble(test[i].trim());
        }
      } while ((line = bufferedReader.readLine()) != null && check);

    }
    return outputRAArray;
  }


  public List<List> calculateReturnPeriods(double outputRAArray[][]) {
    /* Sample outputRAArray
        double[][] outputRAArray = {
            {2, 0.15, 0.28, 1.09, 1.45},
            {5, 0.3, 0.54, 2.03, 2.75},
            {10, 0.4, 0.72, 2.75, 3.76},
            {20, 0.53, 0.96, 3.65, 4.88},
            {30, 0.58, 1.05, 3.9, 5.27},
            {40, 0.59, 1.07, 3.91, 5.33},
            {50, 0.63, 1.14, 4.22, 5.72},
            {60, 0.64, 1.16, 4.34, 5.83},
            {70, 0.66, 1.18, 4.37, 5.91},
            {80, 0.68, 1.22, 4.49, 6.1},
            {90, 0.7, 1.26, 4.67, 6.32},
            {100, 0.71, 1.3, 4.84, 6.52}};
     */
    List<List> interpolatedResultsArray = new ArrayList<>();
    outputRAArray = RHEMUtils.roundValues(outputRAArray);
    double[][] transposedMatrix = RHEMUtils.transposeMatrix(outputRAArray);

    for (int x = 0; x < transposedMatrix[0].length; x++) {
      double currentRP = transposedMatrix[0][x];
      double currentSoilLoss = transposedMatrix[1][x];
      double maxBaselineSoilLoss = transposedMatrix[1][x];
      List<Double> rpPeriodArray = new ArrayList<>();
      rpPeriodArray.add(currentSoilLoss);
      rpPeriodArray.add(currentRP);

      for (int i = 2; i < transposedMatrix.length; i++) {
        List<Double> alt_scenario = new ArrayList<>();
        for (int k = 0; k < transposedMatrix[i].length; k++) {
          if (transposedMatrix[i][k] <= maxBaselineSoilLoss) {
            alt_scenario.add(transposedMatrix[i][k]);
          }
        }
        // default the altenative scenario interpolated value to 1
        double altScenarioInterp = 1;
        if (!alt_scenario.isEmpty()) {
          altScenarioInterp = RHEMUtils.interpolate(maxBaselineSoilLoss, transposedMatrix[i], transposedMatrix[0]);
          altScenarioInterp = Math.round(altScenarioInterp * 1000.0) / 1000.0;

          // set the return period to 100 if the interpolated value is greater than 100
          if (altScenarioInterp > 100 || Double.isNaN(altScenarioInterp)) {
            altScenarioInterp = 100;
          }
        }
        // round the interpolated year to the nearest 10th place
        altScenarioInterp = Math.round(altScenarioInterp * 10) / 10.0;
        rpPeriodArray.add(altScenarioInterp);
      }
      interpolatedResultsArray.add(rpPeriodArray);
    }
    return interpolatedResultsArray;
  }


  public void writeInterpolatedResultsArray(List<List> interpolatedResultsArray) throws Exception {
    try (PrintWriter w = new PrintWriter(workspace().getFile("frequencyAnalysisReturnPeriodTable.out"))) {
      w.print(String.format("%-20s %-20s", "BASELINE SCENARIO", "RETURN PERIOD"));
      for (String name : scenarioNames) {
        w.print(String.format("%-20s", name + "(years)"));
      }
      w.println();

      for (int i = 0; i < interpolatedResultsArray.size(); i++) {
        for (int j = 0; j < interpolatedResultsArray.get(i).size(); j++) {
          w.print(String.format("%-20s", interpolatedResultsArray.get(i).get(j).toString()));
        }
        w.println();
      }
    }
  }
}