diff --git a/.github/workflows/gradle-build.yml b/.github/workflows/gradle-build.yml index 350b2fa..e30810a 100644 --- a/.github/workflows/gradle-build.yml +++ b/.github/workflows/gradle-build.yml @@ -5,6 +5,7 @@ on: branches: [ master ] pull_request: branches: [ master ] + workflow_dispatch: jobs: build: @@ -15,9 +16,9 @@ jobs: packages: write steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up JDK 11 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: java-version: '11' distribution: 'adopt' diff --git a/.github/workflows/runWithFairCli.yml b/.github/workflows/runWithFairCli.yml index 47db3d4..cfa7298 100644 --- a/.github/workflows/runWithFairCli.yml +++ b/.github/workflows/runWithFairCli.yml @@ -5,6 +5,7 @@ on: branches: [ master ] pull_request: branches: [ master ] + workflow_dispatch: jobs: build: @@ -15,9 +16,9 @@ jobs: packages: write steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up JDK 11 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: java-version: '11' distribution: 'adopt' @@ -29,14 +30,14 @@ jobs: - name: Install local registry run: | /bin/bash -c "$(curl -fsSL https://data.scrc.uk/static/localregistry.sh)" - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v3 with: python-version: '3.x' - name: install fair-cli from pip run: pip install fair-cli - name: run javaSimpleModel with fair cli run: | - fair init --ci - fair pull src/main/resources/seirs-config.yaml - fair run src/main/resources/seirs-config.yaml + fair init --ci --local + fair pull --local src/main/resources/seirs-config.yaml + fair run --local src/main/resources/seirs-config.yaml diff --git a/README.md b/README.md index e71ae72..394b286 100644 --- a/README.md +++ b/README.md @@ -30,13 +30,7 @@ At this point you can configure `fair` to run in this repo. Either run: fair init ``` -And fill in your own details, or simply run: - -```sh -fair init --ci -``` - -In this case the CLI will initialise the repo with dummy values that are used for continuous integration testing. +And fill in your own details. Finally you can run the example! diff --git a/build.gradle b/build.gradle index 0cef5e1..6e4e47a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,13 @@ plugins { id 'application' + id "com.diffplug.spotless" version "6.4.2" } group 'org.example' version '1.0-SNAPSHOT' application { - mainClass = 'main' + mainClass = 'org.fairdatapipeline.javasimplemodel.JavaSimpleModel' } repositories { @@ -14,8 +15,13 @@ repositories { } dependencies { - implementation 'org.fairdatapipeline:api:1.0.0-beta' - implementation 'com.opencsv:opencsv:5.5.2' - implementation('org.slf4j:slf4j-simple:1.7.25') + implementation 'org.fairdatapipeline:api:1.0.0-rc3' + implementation 'com.opencsv:opencsv:5.7.1' + implementation('org.slf4j:slf4j-simple:1.7.36') } +spotless { + java { + googleJavaFormat() + } +} \ No newline at end of file diff --git a/src/main/java/PrepareParams.java b/src/main/java/PrepareParams.java deleted file mode 100644 index 6b6ea79..0000000 --- a/src/main/java/PrepareParams.java +++ /dev/null @@ -1,24 +0,0 @@ -import org.fairdatapipeline.api.*; - -import java.nio.file.Path; - - -public class PrepareParams { - public void PrepareParams() {} - - public void run(Path configPath, Path scriptPath, String regtoken) { - try (Coderun cr = new Coderun(configPath, scriptPath, regtoken)){ - Data_product_write dp = cr.get_dp_for_write("SEIRS_model/preparedParams"); - dp.getComponent("S").writeEstimate(0.999); - dp.getComponent("E").writeEstimate(0.001); - dp.getComponent("I").writeEstimate(0.0); - dp.getComponent("R").writeEstimate(0.0); - dp.getComponent("inv_gamma").writeEstimate(14.0); - dp.getComponent("inv_sigma").writeEstimate(7.0); - dp.getComponent("inv_omega").writeEstimate(1.0); - dp.getComponent("inv_mu").writeEstimate(76.0); - dp.getComponent("beta").writeEstimate(0.21); - dp.getComponent("alpha").writeEstimate(0.0); - } - } -} diff --git a/src/main/java/SEIRS.java b/src/main/java/SEIRS.java deleted file mode 100644 index a76f74f..0000000 --- a/src/main/java/SEIRS.java +++ /dev/null @@ -1,174 +0,0 @@ -import com.opencsv.exceptions.CsvValidationException; -import org.fairdatapipeline.api.*; -import org.fairdatapipeline.file.CleanableFileChannel; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; -import com.opencsv.CSVReader; - -/****************************************************************************** - * SEIRS ODE model in Java - * Author: Sibylle Mohr - * Compilation: javac SEIRS.java - * Execution: java SEIRS - * SEIRS model with Euler integration - ******************************************************************************/ - -class SEIRS { - - - public void SEIRS(){ - - } - - - public void runFromExternal(Path configPath, Path scriptPath, String regToken) { - try(Coderun cr = new Coderun(configPath, scriptPath, regToken)) { - Map params = new HashMap<>(); - Data_product_read dp = cr.get_dp_for_read("SEIRS_model/parameters"); - try { - Path filepath = dp.getComponent().readLink(); - FileReader fr = new FileReader(filepath.toString()); - CSVReader r = new CSVReader(fr); - String[] line; - int i = 0; - r.readNext(); // ignore header - while ((line = r.readNext()) != null) { - params.put(line[0], Double.parseDouble(line[1])); - i += 1; - } - }catch(FileNotFoundException e){ - System.out.println("FileNotFoundException: can't find the file: " + e); - e.printStackTrace(); - System.exit(1); - }catch(IOException e) { - System.out.println("error reading parameter CSV: " + e); - System.exit(1); - }catch(CsvValidationException e){ - System.out.println("bad CSV: " + e); - System.exit(1); - }catch(Exception e) { - System.out.print("Exception reading parameters: " + e); - System.exit(1); - } - - // set initial state: - params.put("S", 0.999); - params.put("E", 0.001); - params.put("I", 0.0); - params.put("R", 0.0); - Data_product_write dpw = cr.get_dp_for_write("SEIRS_model/results/model_output"); - try { - CleanableFileChannel f = dpw.getComponent().writeFileChannel(); - do_SEIRS(params, f); - }catch(IOException e) { - System.err.println("failed to write output to file: " + e); - } - - } - } - - - public void runFromPrepared(Path configPath, Path scriptPath, String regToken) { - try(Coderun cr = new Coderun(configPath, scriptPath, regToken)) { - Map params = new HashMap<>(); - Data_product_read dp = cr.get_dp_for_read("SEIRS_model/preparedParams"); - params.put("S", (Double) dp.getComponent("S").readEstimate()); - params.put("E", (Double) dp.getComponent("E").readEstimate()); - params.put("I", (Double) dp.getComponent("I").readEstimate()); - params.put("R", (Double) dp.getComponent("R").readEstimate()); - params.put("inv_gamma", (Double) dp.getComponent("inv_gamma").readEstimate()); - params.put("inv_sigma", (Double) dp.getComponent("inv_sigma").readEstimate()); - params.put("inv_omega", (Double) dp.getComponent("inv_omega").readEstimate()); - params.put("inv_mu", (Double) dp.getComponent("inv_mu").readEstimate()); - params.put("beta", (Double) dp.getComponent("beta").readEstimate()); - params.put("alpha", (Double) dp.getComponent("alpha").readEstimate()); - - Data_product_write dpw = cr.get_dp_for_write("SEIRS_model/results/fromPreparedParams"); - try { - CleanableFileChannel f = dpw.getComponent().writeFileChannel(); - do_SEIRS(params, f); - }catch(IOException e) { - System.err.println("failed to write output to file: " + e); - } - - } - } - - void do_SEIRS(Map params, CleanableFileChannel f) throws IOException { - - double N = 1.0; - int total_time = 5 * 365; // 5 years - - int step_per_day_int = 2; - - double step_per_day = step_per_day_int; - - double dt = 1 / step_per_day; - - - int time_steps = total_time * step_per_day_int; - - double[] S = new double[time_steps+1]; - double[] E = new double[time_steps+1]; - double[] I = new double[time_steps+1]; - double[] R = new double[time_steps+1]; - - double[] time = new double[time_steps+1]; - - - S[0] = params.get("S"); - E[0] = params.get("E"); - I[0] = params.get("I"); - R[0] = params.get("R"); - - time[0] = 0; - - double inv_gamma = params.get("inv_gamma"); - double inv_sigma = params.get("inv_sigma"); - double inv_omega = params.get("inv_omega"); - double inv_mu = params.get("inv_mu"); - double beta = params.get("beta"); - double alpha= params.get("alpha"); - - - double gamma_d = 1 / inv_gamma; - double omega_d = 1 / (inv_omega * 365); - double mu_d = 1 / (inv_mu * 365); - double sigma_d = 1 / inv_sigma; - - String line; - - line = "time,S,E,I,R\n"; - f.write(ByteBuffer.wrap(line.getBytes(StandardCharsets.UTF_8))); - line = (0) + "," + S[0] + "," + E[0] + "," + I[0] + "," + R[0] + "\n"; - f.write(ByteBuffer.wrap(line.getBytes(StandardCharsets.UTF_8))); - - for (int i = 0; i < time_steps; i++) { - N = S[i] + E[i] + I[i] + R[i]; - double dSdt = mu_d*N - (beta*S[i]*I[i])/N + omega_d*R[i] - mu_d*S[i]; - double dEdt = -sigma_d*E[i] + (beta*S[i]*I[i])/N - mu_d*E[i]; - double dIdt = -gamma_d*I[i] + sigma_d*E[i] - (mu_d+alpha)*I[i]; - double dRdt = -omega_d*R[i] + gamma_d*I[i] - mu_d*R[i]; - - /* integrate using Euler */ - time[i+1] = time[i] + dt; - - S[i+1] = S[i] + dSdt * dt; - E[i+1] = E[i] + dEdt * dt; - I[i+1] = I[i] + dIdt * dt; - R[i+1] = R[i] + dRdt * dt; - - line = (time[i+1] / 365) + "," + S[i+1] + "," + E[i+1] + "," + I[i+1] + "," + R[i+1] + "\n"; - f.write(ByteBuffer.wrap(line.getBytes(StandardCharsets.UTF_8))); - } - - } -} diff --git a/src/main/java/main.java b/src/main/java/main.java deleted file mode 100644 index a7252ae..0000000 --- a/src/main/java/main.java +++ /dev/null @@ -1,40 +0,0 @@ -import java.io.File; -import java.nio.file.Path; - -public class main { - public static void main(String[] args) { - //System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE"); - if(System.getenv("FDP_LOCAL_TOKEN") == null) { - System.err.println("The fair CLI needs to set the environment variable FDP_LOCAL_TOKEN to the authorization token for the local registry"); - System.exit(2); - } - String confDir = ""; - if(args.length == 1) { - confDir = args[0]; - }else if(args.length == 2) { - confDir = args[1]; - }else{ - System.err.println("Usage: gradle run --args=\"[{--seirsFromPrepared,--prepare}] configDir\""); - System.exit(1); - } - if(!new File(confDir).isDirectory()) { - System.err.println("Config dir " + confDir + " doesn't seem to exist."); - System.exit(1); - } - Path configFile = Path.of(confDir).resolve("config.yaml"); - Path scriptFile = Path.of(confDir).resolve("script.sh"); - if(args.length == 1) { - SEIRS s = new SEIRS(); - s.runFromExternal(configFile, scriptFile, System.getenv("FDP_LOCAL_TOKEN")); - }else if(args[0].equals("--prepare")) { - PrepareParams pp = new PrepareParams(); - pp.run(configFile, scriptFile, System.getenv("FDP_LOCAL_TOKEN")); - }else if(args[0].equals("--seirsFromPrepared")) { - SEIRS s = new SEIRS(); - s.runFromPrepared(configFile, scriptFile, System.getenv("FDP_LOCAL_TOKEN")); - }else { - System.err.println("Usage: gradle run --args=\"[{--seirsFromPrepared,--prepare}] configDir\""); - System.exit(1); - } - } -} diff --git a/src/main/java/org/fairdatapipeline/javasimplemodel/JavaSimpleModel.java b/src/main/java/org/fairdatapipeline/javasimplemodel/JavaSimpleModel.java new file mode 100644 index 0000000..16b7e71 --- /dev/null +++ b/src/main/java/org/fairdatapipeline/javasimplemodel/JavaSimpleModel.java @@ -0,0 +1,46 @@ +package org.fairdatapipeline.javasimplemodel; + +import java.io.File; +import java.nio.file.Path; + +public class JavaSimpleModel { + private static final String ENV_VAR_NAME = "FDP_LOCAL_TOKEN"; + + public static void main(String[] args) { + if (System.getenv(ENV_VAR_NAME) == null) { + System.err.println( + "The fair CLI needs to set the environment variable FDP_LOCAL_TOKEN to the authorization token for the local registry"); + System.exit(2); + } + String confDir = ""; + if (args.length == 1) { + confDir = args[0]; + } else if (args.length == 2) { + confDir = args[1]; + } else { + System.err.println( + "Usage: gradle run --args=\"[{--seirsFromPrepared,--prepare}] configDir\""); + System.exit(1); + } + if (!new File(confDir).isDirectory()) { + System.err.println("Config dir " + confDir + " doesn't seem to exist."); + System.exit(1); + } + Path configFile = Path.of(confDir).resolve("config.yaml"); + Path scriptFile = Path.of(confDir).resolve("script.sh"); + if (args.length == 1) { + SEIRS s = new SEIRS(); + s.runFromExternal(configFile, scriptFile, System.getenv(ENV_VAR_NAME)); + } else if (args[0].equals("--prepare")) { + PrepareParams pp = new PrepareParams(); + pp.run(configFile, scriptFile, System.getenv(ENV_VAR_NAME)); + } else if (args[0].equals("--seirsFromPrepared")) { + SEIRS s = new SEIRS(); + s.runFromPrepared(configFile, scriptFile, System.getenv(ENV_VAR_NAME)); + } else { + System.err.println( + "Usage: gradle run --args=\"[{--seirsFromPrepared,--prepare}] configDir\""); + System.exit(1); + } + } +} diff --git a/src/main/java/org/fairdatapipeline/javasimplemodel/PrepareParams.java b/src/main/java/org/fairdatapipeline/javasimplemodel/PrepareParams.java new file mode 100644 index 0000000..deabd58 --- /dev/null +++ b/src/main/java/org/fairdatapipeline/javasimplemodel/PrepareParams.java @@ -0,0 +1,22 @@ +package org.fairdatapipeline.javasimplemodel; + +import java.nio.file.Path; +import org.fairdatapipeline.api.*; + +public class PrepareParams { + public void run(Path configPath, Path scriptPath, String regtoken) { + try (Coderun cr = new Coderun(configPath, scriptPath, regtoken)) { + Data_product_write dp = cr.get_dp_for_write("SEIRS_model/preparedParams"); + dp.getComponent("S").writeEstimate(0.999); + dp.getComponent("E").writeEstimate(0.001); + dp.getComponent("I").writeEstimate(0.0); + dp.getComponent("R").writeEstimate(0.0); + dp.getComponent("inv_gamma").writeEstimate(14.0); + dp.getComponent("inv_sigma").writeEstimate(7.0); + dp.getComponent("inv_omega").writeEstimate(1.0); + dp.getComponent("inv_mu").writeEstimate(76.0); + dp.getComponent("beta").writeEstimate(0.21); + dp.getComponent("alpha").writeEstimate(0.0); + } + } +} diff --git a/src/main/java/org/fairdatapipeline/javasimplemodel/SEIRS.java b/src/main/java/org/fairdatapipeline/javasimplemodel/SEIRS.java new file mode 100644 index 0000000..fa37c66 --- /dev/null +++ b/src/main/java/org/fairdatapipeline/javasimplemodel/SEIRS.java @@ -0,0 +1,175 @@ +package org.fairdatapipeline.javasimplemodel; + +import com.opencsv.CSVReader; +import com.opencsv.exceptions.CsvValidationException; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import org.fairdatapipeline.api.*; +import org.fairdatapipeline.file.CleanableFileChannel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * **************************************************************************** SEIRS ODE model in + * Java Author: Sibylle Mohr Compilation: javac SEIRS.java Execution: java SEIRS SEIRS model with + * Euler integration **************************************************************************** + */ +class SEIRS { + private static final Logger logger = LoggerFactory.getLogger(SEIRS.class); + private static final String INV_G = "inv_gamma"; + private static final String INV_S = "inv_sigma"; + private static final String INV_O = "inv_omega"; + private static final String INV_M = "inv_mu"; + private static final String ALPHA = "alpha"; + private static final String BETA = "beta"; + + public void runFromExternal(Path configPath, Path scriptPath, String regToken) { + try (Coderun cr = new Coderun(configPath, scriptPath, regToken)) { + Map params = new HashMap<>(); + Data_product_read dp = cr.get_dp_for_read("SEIRS_model/parameters"); + try { + Path filepath = dp.getComponent().readLink(); + FileReader fr = new FileReader(filepath.toString()); + try (CSVReader r = new CSVReader(fr)) { + String[] line; + r.readNext(); // ignore header + while ((line = r.readNext()) != null) { + params.put(line[0], Double.parseDouble(line[1])); + } + } + } catch (FileNotFoundException e) { + logger.error("FileNotFoundException: can't find the file.", e); + System.exit(1); + } catch (IOException e) { + logger.error("error reading parameter CSV.", e); + System.exit(1); + } catch (CsvValidationException e) { + logger.error("bad CSV.", e); + System.exit(1); + } catch (Exception e) { + logger.error("Exception reading parameters: ", e); + System.exit(1); + } + + // set initial state: + params.put("S", 0.999); + params.put("E", 0.001); + params.put("I", 0.0); + params.put("R", 0.0); + Data_product_write dpw = cr.get_dp_for_write("SEIRS_model/results/model_output"); + try { + CleanableFileChannel f = dpw.getComponent().writeFileChannel(); + do_SEIRS(params, f); + } catch (IOException e) { + logger.error("failed to write output to file: ", e); + } + } + } + + public void runFromPrepared(Path configPath, Path scriptPath, String regToken) { + try (Coderun cr = new Coderun(configPath, scriptPath, regToken)) { + Map params = new HashMap<>(); + Data_product_read dp = cr.get_dp_for_read("SEIRS_model/preparedParams"); + params.put("S", (Double) dp.getComponent("S").readEstimate()); + params.put("E", (Double) dp.getComponent("E").readEstimate()); + params.put("I", (Double) dp.getComponent("I").readEstimate()); + params.put("R", (Double) dp.getComponent("R").readEstimate()); + params.put(INV_G, (Double) dp.getComponent(INV_G).readEstimate()); + params.put(INV_S, (Double) dp.getComponent(INV_S).readEstimate()); + params.put(INV_O, (Double) dp.getComponent(INV_O).readEstimate()); + params.put(INV_M, (Double) dp.getComponent(INV_M).readEstimate()); + params.put(BETA, (Double) dp.getComponent(BETA).readEstimate()); + params.put(ALPHA, (Double) dp.getComponent(ALPHA).readEstimate()); + + Data_product_write dpw = cr.get_dp_for_write("SEIRS_model/results/fromPreparedParams"); + try { + CleanableFileChannel f = dpw.getComponent().writeFileChannel(); + do_SEIRS(params, f); + } catch (IOException e) { + logger.error("failed to write output to file.", e); + } + } + } + + void do_SEIRS(Map params, CleanableFileChannel f) throws IOException { + + double N; + int total_time = 5 * 365; // 5 years + + int step_per_day_int = 2; + + double step_per_day = step_per_day_int; + + double dt = 1 / step_per_day; + + int time_steps = total_time * step_per_day_int; + + double[] S = new double[time_steps + 1]; + double[] E = new double[time_steps + 1]; + double[] I = new double[time_steps + 1]; + double[] R = new double[time_steps + 1]; + + double[] time = new double[time_steps + 1]; + + S[0] = params.get("S"); + E[0] = params.get("E"); + I[0] = params.get("I"); + R[0] = params.get("R"); + + time[0] = 0; + + double inv_gamma = params.get(INV_G); + double inv_sigma = params.get(INV_S); + double inv_omega = params.get(INV_O); + double inv_mu = params.get(INV_M); + double beta = params.get(BETA); + double alpha = params.get(ALPHA); + + double gamma_d = 1 / inv_gamma; + double omega_d = 1 / (inv_omega * 365); + double mu_d = 1 / (inv_mu * 365); + double sigma_d = 1 / inv_sigma; + + String line; + + line = "time,S,E,I,R\n"; + f.write(ByteBuffer.wrap(line.getBytes(StandardCharsets.UTF_8))); + line = (0) + "," + S[0] + "," + E[0] + "," + I[0] + "," + R[0] + "\n"; + f.write(ByteBuffer.wrap(line.getBytes(StandardCharsets.UTF_8))); + + for (int i = 0; i < time_steps; i++) { + N = S[i] + E[i] + I[i] + R[i]; + double dSdt = mu_d * N - (beta * S[i] * I[i]) / N + omega_d * R[i] - mu_d * S[i]; + double dEdt = -sigma_d * E[i] + (beta * S[i] * I[i]) / N - mu_d * E[i]; + double dIdt = -gamma_d * I[i] + sigma_d * E[i] - (mu_d + alpha) * I[i]; + double dRdt = -omega_d * R[i] + gamma_d * I[i] - mu_d * R[i]; + + /* integrate using Euler */ + time[i + 1] = time[i] + dt; + + S[i + 1] = S[i] + dSdt * dt; + E[i + 1] = E[i] + dEdt * dt; + I[i + 1] = I[i] + dIdt * dt; + R[i + 1] = R[i] + dRdt * dt; + + line = + (time[i + 1] / 365) + + "," + + S[i + 1] + + "," + + E[i + 1] + + "," + + I[i + 1] + + "," + + R[i + 1] + + "\n"; + f.write(ByteBuffer.wrap(line.getBytes(StandardCharsets.UTF_8))); + } + } +} diff --git a/src/main/resources/fromprep.yaml b/src/main/resources/fromprep.yaml index be97265..27bf1d5 100644 --- a/src/main/resources/fromprep.yaml +++ b/src/main/resources/fromprep.yaml @@ -12,5 +12,6 @@ read: write: - data_product: SEIRS_model/results/fromPreparedParams + file_type: csv use: version: 0.0.1 diff --git a/src/main/resources/seirs-config.yaml b/src/main/resources/seirs-config.yaml index f898970..4d61c0e 100644 --- a/src/main/resources/seirs-config.yaml +++ b/src/main/resources/seirs-config.yaml @@ -1,5 +1,5 @@ run_metadata: - default_input_namespace: bram + default_input_namespace: testing description: SEIRS Model java script: | gradle run --args "${{CONFIG_DIR}}" @@ -10,7 +10,7 @@ register: website: https://ror.org/04p491231 - external_object: SEIRS_model/parameters - namespace: PSU + namespace_name: PSU root: https://raw.githubusercontent.com/ path: FAIRDataPipeline/rSimpleModel/main/inst/extdata/static_params_SEIRS.csv title: Static parameters of the model