From 15ab802600c03e1d3c844697b708925d53dcbe80 Mon Sep 17 00:00:00 2001 From: Nathan Zimmerberg <39104088+nhz2@users.noreply.github.com> Date: Tue, 3 Oct 2023 17:51:20 -0400 Subject: [PATCH] Script interface instead of cli tool. BREAKING (#23) * work * work * file saving work * file saving work * work * work * more tests * file saving work * work * work * work * testing work * bump version * add CLI help * docs * Delete test/example/output-ref partial 1/1/logs/2023-10-02T12:56:35_B51x3OUz2CdA directory * Delete test/example/output-ref/1/logs/2023-10-02T12:56:35_B51x3OUz2CdA directory * fix windows 1.9 * CI and windows fixes * CI fix * rename prev_hash to prev_sha256 --- .github/workflows/CI.yml | 3 +- Comonicon.toml | 1 - Project.toml | 11 +- README.md | 199 ++----- deps/build.jl | 2 - src/MEDYANSimRunner.jl | 560 +----------------- src/cli-parsing.jl | 77 +++ src/constants.jl | 6 + src/file-saving.jl | 126 ++++ src/listparse.jl | 135 ----- src/outputdiff.jl | 98 +-- src/rng-load-save.jl | 22 + src/run-sim.jl | 338 +++++++++++ src/timeout.jl | 95 --- src/treehash.jl | 35 -- test/Project.toml | 5 +- .../loading timeout/input => example}/main.jl | 37 +- .../error.log | 0 .../2023-10-03T09-54-17_QhkWY1GZP7wr/info.log | 40 ++ .../warn.log | 0 .../output-ref-1_10/a/traj/footer.json | 4 + .../output-ref-1_10/a/traj}/header.json | 0 .../output-ref-1_10/a/traj/snap0.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_10/a/traj/snap1.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_10/a/traj/snap10.zarr.zip | Bin 0 -> 1124 bytes .../output-ref-1_10/a/traj/snap11.zarr.zip | Bin 0 -> 1124 bytes .../output-ref-1_10/a/traj/snap2.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_10/a/traj/snap3.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_10/a/traj/snap4.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_10/a/traj/snap5.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_10/a/traj/snap6.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_10/a/traj/snap7.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_10/a/traj/snap8.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_10/a/traj/snap9.zarr.zip | Bin 0 -> 1123 bytes .../error.log | 0 .../2023-10-03T09-44-43_8sqWTFYgVxjq/info.log | 38 ++ .../warn.log | 0 .../example/output-ref-1_9/a/traj/footer.json | 4 + .../output-ref-1_9/a/traj}/header.json | 0 .../output-ref-1_9/a/traj/snap0.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_9/a/traj/snap1.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_9/a/traj/snap10.zarr.zip | Bin 0 -> 1124 bytes .../output-ref-1_9/a/traj/snap11.zarr.zip | Bin 0 -> 1124 bytes .../output-ref-1_9/a/traj/snap2.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_9/a/traj/snap3.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_9/a/traj/snap4.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_9/a/traj/snap5.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_9/a/traj/snap6.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_9/a/traj/snap7.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_9/a/traj/snap8.zarr.zip | Bin 0 -> 1123 bytes .../output-ref-1_9/a/traj/snap9.zarr.zip | Bin 0 -> 1123 bytes .../output partial/1/detect-mult-process | 1 - .../good partial/output partial/1/info.log | 75 --- .../good partial/output partial/1/list.txt | 10 - .../1/snapshots/snapshot0.zarr.zip | Bin 587 -> 0 bytes .../1/snapshots/snapshot1.zarr.zip | Bin 587 -> 0 bytes .../1/snapshots/snapshot2.zarr.zip | Bin 587 -> 0 bytes .../1/snapshots/snapshot3.zarr.zip | Bin 587 -> 0 bytes .../1/snapshots/snapshot4.zarr.zip | Bin 587 -> 0 bytes .../1/snapshots/snapshot5.zarr.zip | Bin 587 -> 0 bytes test/examples/good/input/Manifest.toml | 343 ----------- test/examples/good/input/Project.toml | 7 - test/examples/good/input/main.jl | 47 -- test/examples/good/jobnames.txt | 5 - .../good/output-ref/1/detect-mult-process | 1 - test/examples/good/output-ref/1/info.log | 75 --- test/examples/good/output-ref/1/list.txt | 15 - .../output-ref/1/snapshots/snapshot0.zarr.zip | Bin 587 -> 0 bytes .../output-ref/1/snapshots/snapshot1.zarr.zip | Bin 587 -> 0 bytes .../1/snapshots/snapshot10.zarr.zip | Bin 587 -> 0 bytes .../1/snapshots/snapshot11.zarr.zip | Bin 587 -> 0 bytes .../output-ref/1/snapshots/snapshot2.zarr.zip | Bin 587 -> 0 bytes .../output-ref/1/snapshots/snapshot3.zarr.zip | Bin 587 -> 0 bytes .../output-ref/1/snapshots/snapshot4.zarr.zip | Bin 587 -> 0 bytes .../output-ref/1/snapshots/snapshot5.zarr.zip | Bin 587 -> 0 bytes .../output-ref/1/snapshots/snapshot6.zarr.zip | Bin 587 -> 0 bytes .../output-ref/1/snapshots/snapshot7.zarr.zip | Bin 587 -> 0 bytes .../output-ref/1/snapshots/snapshot8.zarr.zip | Bin 587 -> 0 bytes .../output-ref/1/snapshots/snapshot9.zarr.zip | Bin 587 -> 0 bytes .../loading timeout/input/Manifest.toml | 343 ----------- .../loading timeout/input/Project.toml | 7 - .../loading timeout/output-ref/1/list.txt | 2 - .../missing manifest/input/Project.toml | 7 - test/examples/missing manifest/input/main.jl | 47 -- test/file-saving-stresstest.jl | 65 ++ test/list-examples/list done.txt | 7 - test/list-examples/list empty.txt | 0 test/list-examples/list partial clean.txt | 7 - test/list-examples/list partial too small.txt | 5 - test/list-examples/list partial.txt | 8 - test/list-examples/list start error.txt | 2 - test/potential-errors.md | 38 -- test/runtests.jl | 5 +- test/test_arg-parsing.jl | 48 ++ test/test_file-saving.jl | 65 ++ test/test_listparse.jl | 142 ----- test/test_normalize_job_idx.jl | 24 - test/test_ref_out.jl | 123 ++-- test/test_timeout.jl | 92 --- 99 files changed, 1027 insertions(+), 2425 deletions(-) delete mode 100644 Comonicon.toml delete mode 100644 deps/build.jl create mode 100644 src/cli-parsing.jl create mode 100644 src/constants.jl create mode 100644 src/file-saving.jl delete mode 100644 src/listparse.jl create mode 100644 src/rng-load-save.jl create mode 100644 src/run-sim.jl delete mode 100644 src/timeout.jl delete mode 100644 src/treehash.jl rename test/{examples/loading timeout/input => example}/main.jl (52%) rename test/{examples/good partial/output partial/1 => example/output-ref-1_10/a/logs/2023-10-03T09-54-17_QhkWY1GZP7wr}/error.log (100%) create mode 100644 test/example/output-ref-1_10/a/logs/2023-10-03T09-54-17_QhkWY1GZP7wr/info.log rename test/{examples/good partial/output partial/1 => example/output-ref-1_10/a/logs/2023-10-03T09-54-17_QhkWY1GZP7wr}/warn.log (100%) create mode 100644 test/example/output-ref-1_10/a/traj/footer.json rename test/{examples/good partial/output partial/1 => example/output-ref-1_10/a/traj}/header.json (100%) create mode 100644 test/example/output-ref-1_10/a/traj/snap0.zarr.zip create mode 100644 test/example/output-ref-1_10/a/traj/snap1.zarr.zip create mode 100644 test/example/output-ref-1_10/a/traj/snap10.zarr.zip create mode 100644 test/example/output-ref-1_10/a/traj/snap11.zarr.zip create mode 100644 test/example/output-ref-1_10/a/traj/snap2.zarr.zip create mode 100644 test/example/output-ref-1_10/a/traj/snap3.zarr.zip create mode 100644 test/example/output-ref-1_10/a/traj/snap4.zarr.zip create mode 100644 test/example/output-ref-1_10/a/traj/snap5.zarr.zip create mode 100644 test/example/output-ref-1_10/a/traj/snap6.zarr.zip create mode 100644 test/example/output-ref-1_10/a/traj/snap7.zarr.zip create mode 100644 test/example/output-ref-1_10/a/traj/snap8.zarr.zip create mode 100644 test/example/output-ref-1_10/a/traj/snap9.zarr.zip rename test/{examples/good/output-ref/1 => example/output-ref-1_9/a/logs/2023-10-03T09-44-43_8sqWTFYgVxjq}/error.log (100%) create mode 100644 test/example/output-ref-1_9/a/logs/2023-10-03T09-44-43_8sqWTFYgVxjq/info.log rename test/{examples/good/output-ref/1 => example/output-ref-1_9/a/logs/2023-10-03T09-44-43_8sqWTFYgVxjq}/warn.log (100%) create mode 100644 test/example/output-ref-1_9/a/traj/footer.json rename test/{examples/good/output-ref/1 => example/output-ref-1_9/a/traj}/header.json (100%) create mode 100644 test/example/output-ref-1_9/a/traj/snap0.zarr.zip create mode 100644 test/example/output-ref-1_9/a/traj/snap1.zarr.zip create mode 100644 test/example/output-ref-1_9/a/traj/snap10.zarr.zip create mode 100644 test/example/output-ref-1_9/a/traj/snap11.zarr.zip create mode 100644 test/example/output-ref-1_9/a/traj/snap2.zarr.zip create mode 100644 test/example/output-ref-1_9/a/traj/snap3.zarr.zip create mode 100644 test/example/output-ref-1_9/a/traj/snap4.zarr.zip create mode 100644 test/example/output-ref-1_9/a/traj/snap5.zarr.zip create mode 100644 test/example/output-ref-1_9/a/traj/snap6.zarr.zip create mode 100644 test/example/output-ref-1_9/a/traj/snap7.zarr.zip create mode 100644 test/example/output-ref-1_9/a/traj/snap8.zarr.zip create mode 100644 test/example/output-ref-1_9/a/traj/snap9.zarr.zip delete mode 100644 test/examples/good partial/output partial/1/detect-mult-process delete mode 100644 test/examples/good partial/output partial/1/info.log delete mode 100644 test/examples/good partial/output partial/1/list.txt delete mode 100644 test/examples/good partial/output partial/1/snapshots/snapshot0.zarr.zip delete mode 100644 test/examples/good partial/output partial/1/snapshots/snapshot1.zarr.zip delete mode 100644 test/examples/good partial/output partial/1/snapshots/snapshot2.zarr.zip delete mode 100644 test/examples/good partial/output partial/1/snapshots/snapshot3.zarr.zip delete mode 100644 test/examples/good partial/output partial/1/snapshots/snapshot4.zarr.zip delete mode 100644 test/examples/good partial/output partial/1/snapshots/snapshot5.zarr.zip delete mode 100644 test/examples/good/input/Manifest.toml delete mode 100644 test/examples/good/input/Project.toml delete mode 100644 test/examples/good/input/main.jl delete mode 100644 test/examples/good/jobnames.txt delete mode 100644 test/examples/good/output-ref/1/detect-mult-process delete mode 100644 test/examples/good/output-ref/1/info.log delete mode 100644 test/examples/good/output-ref/1/list.txt delete mode 100644 test/examples/good/output-ref/1/snapshots/snapshot0.zarr.zip delete mode 100644 test/examples/good/output-ref/1/snapshots/snapshot1.zarr.zip delete mode 100644 test/examples/good/output-ref/1/snapshots/snapshot10.zarr.zip delete mode 100644 test/examples/good/output-ref/1/snapshots/snapshot11.zarr.zip delete mode 100644 test/examples/good/output-ref/1/snapshots/snapshot2.zarr.zip delete mode 100644 test/examples/good/output-ref/1/snapshots/snapshot3.zarr.zip delete mode 100644 test/examples/good/output-ref/1/snapshots/snapshot4.zarr.zip delete mode 100644 test/examples/good/output-ref/1/snapshots/snapshot5.zarr.zip delete mode 100644 test/examples/good/output-ref/1/snapshots/snapshot6.zarr.zip delete mode 100644 test/examples/good/output-ref/1/snapshots/snapshot7.zarr.zip delete mode 100644 test/examples/good/output-ref/1/snapshots/snapshot8.zarr.zip delete mode 100644 test/examples/good/output-ref/1/snapshots/snapshot9.zarr.zip delete mode 100644 test/examples/loading timeout/input/Manifest.toml delete mode 100644 test/examples/loading timeout/input/Project.toml delete mode 100644 test/examples/loading timeout/output-ref/1/list.txt delete mode 100644 test/examples/missing manifest/input/Project.toml delete mode 100644 test/examples/missing manifest/input/main.jl create mode 100644 test/file-saving-stresstest.jl delete mode 100644 test/list-examples/list done.txt delete mode 100644 test/list-examples/list empty.txt delete mode 100644 test/list-examples/list partial clean.txt delete mode 100644 test/list-examples/list partial too small.txt delete mode 100644 test/list-examples/list partial.txt delete mode 100644 test/list-examples/list start error.txt delete mode 100644 test/potential-errors.md create mode 100644 test/test_arg-parsing.jl create mode 100644 test/test_file-saving.jl delete mode 100644 test/test_listparse.jl delete mode 100644 test/test_normalize_job_idx.jl delete mode 100644 test/test_timeout.jl diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index a46a204..462b36e 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -21,7 +21,8 @@ jobs: fail-fast: false matrix: version: - - '1.8' + - '1.9' + - '~1.10.0-0' - 'nightly' os: - ubuntu-latest diff --git a/Comonicon.toml b/Comonicon.toml deleted file mode 100644 index ca63445..0000000 --- a/Comonicon.toml +++ /dev/null @@ -1 +0,0 @@ -name = "medyansimrunner" \ No newline at end of file diff --git a/Project.toml b/Project.toml index dff20dc..0d9868d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,26 +1,25 @@ name = "MEDYANSimRunner" uuid = "b58a3b99-22e3-44d1-b5ea-258f082a6fe8" authors = ["nhz2 "] -version = "0.3.0" +version = "0.4.0" [deps] -Comonicon = "863f3e99-da2a-4334-8734-de3dacbe5542" +ArgCheck = "dce04be8-c92d-5529-be00-80e4d2c0e197" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" DeepDiffs = "ab62b9b5-e342-54a8-a765-a90f495de1a6" -Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" +FileWatching = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" LoggingExtras = "e6f89c97-d47a-5376-807f-9c37f3926c36" +OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce" SmallZarrGroups = "d423b6e5-1c84-4ae2-8d2d-b903aee15ac7" -TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" [compat] -Comonicon = "1" DeepDiffs = "1" JSON3 = "1" LoggingExtras = "1" -SmallZarrGroups = "0.5" +SmallZarrGroups = "0.5, 0.6" julia = "1.8" diff --git a/README.md b/README.md index a946e5f..c37f5f2 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ Manage long running restartable MEDYAN.jl simulations. -Simulations run using code stored in an `input` directory and write outputs to an `output` directory. +Simulations run using julia code in a `main.jl` script and write outputs to an `output` directory. + +Inspired by how build scripts work in https://github.com/JuliaPackaging/BinaryBuilder.jl ## Installation First install and run Julia https://julialang.org/downloads/ @@ -12,89 +14,56 @@ First install and run Julia https://julialang.org/downloads/ Then in Julia install this repo as a regular Julia package. ```julia import Pkg - -Pkg.add("https://github.com/medyan-dev/MEDYANSimRunner.jl") -``` - -This will add a `medyansimrunner` to `~/.julia/bin`, so add `~/.julia/bin` to your PATH. - -Run: -```sh -medyansimrunner -h +Pkg.add("MEDYANSimRunner") ``` -To see the help. ## Example -Run the following in the root of this project. +Run the following in the root of this repo. ```sh -cd test/examples/good -medyansimrunner run input output 1 +julia --project=test -e 'using Pkg; pkg"dev ."; pkg"instantiate";' +JULIA_LOAD_PATH="@" julia --project=test --startup-file=no test/example/main.jl --out=test/output --batch=1 --continue ``` -This will run the example simulation in `test/examples/good/input` with job index `"1"` and store the output in `test/examples/good/output/1`. - -The `job_idx` string gets passed to the `setup` function in `main.jl`. +This will run the 1st batch of the example simulation in `test/example/main.jl` +with the `test/` environment and store the output in `test/output/`. -The `job_idx` is hashed and set as the default RNG seed right before `setup` is called. +The output directory will be created if it doesn't already exist. -Any backslash in the job index will be replaced with a "/". +If the `"--batch="` option is not included, all jobs specified in `main.jl` will be run. -The job index must be valid utf-8. -Job index must not be empty. +### `main.jl` script -Each part of job index when split by "/" must not contain any of the following characters: +This file contains the julia functions used when running the simulation. +These functions can modify the input state variable, but in general should return the state. +These functions can also use the default random number generator, this will automatically saved and loaded. +At the end of `main.jl` there should be the lines: ```julia -[ ',', '\r', '\n', '\0', '*', '|', ':', '<', '>', '?', '"',] -``` - -Each part must not end or start in a period or dot. - -The output directory will be created if it doesn't already exist. - -The job index string can be loaded from a line of a file. - -For example, to run a job with a index in the third line of file `jobnames.txt` use: - -```sh -medyansimrunner run input output jobnames.txt 3 +if abspath(PROGRAM_FILE) == @__FILE__ + MEDYANSimRunner.run_sim(ARGS; jobs, setup, loop, load_snapshot, save_snapshot, done) +end ``` - -## input kwargs - -- `step-timeout`: the maximum amount of time in seconds each step is allowed to take before the job is killed, defaults to infinity. - -- `max-steps`: the maximum number of steps a job is allowed to take before the job is killed. - -- `startup-timeout`: the maximum amount of time in seconds to load everything and run the first loop, defaults to infinity. - -- `max-snapshot-MB`: the maximum amount of hard drive space each snapshot is allowed to use in megabytes. - -## `input` directory - -The input directory must contain a `main.jl` file, a `Manifest.toml`, and a `Project.toml`. - -The input directory will be the working directory of the simulation and can include other data needed for the simulation, including an `Artifacts.toml` - -The input directory should not be mutated during or after a simulation. - -### `main.jl` file - -This file contains the julia functions used when running the simulation. -These functions can modify any input state variables, but in general should return the state. -These functions can also use the default random number generator, this will automatically saved and loaded. +To run the simulation if `main.jl` is called as a julia script. #### Standard input parameters. - `step::Int`: starts out at 0 after setup and is auto incremented right after every `loop`. -#### `setup(job_idx::String; kwargs...) -> header_dict, state` -Return the header dictionary to be written as the `header.json` file in output. +#### `jobs::Vector{String}` +A vector of jobs to run. Each job represents one variant of the simulation that can be run. +This is useful if many simulations need to be run in parallel. The `"--batch="` argument +can be used to pick just one job to run. + +The selected `job` string gets passed to the `setup` function in `main.jl`. +The `job` string is also used to seed the default RNG right before `setup` is called. + +#### `setup(job::String; kwargs...) -> header_dict, state` +Return the header dictionary to be written as the `header.json` file in output trajectory. Also return the state that gets passed on to `loop` and the state that gets passed to `save_snapshot` and `load_snapshot`. -`job_idx::String`: The job index. This is used for multi job simulations. +`job::String`: The job. This is used for multi job simulations. -#### `save_snapshot(step::Int, state; kwargs...)::SmallZarrGroups.ZGroup` +#### `save_snapshot(step::Int, state; kwargs...)-> group::SmallZarrGroups.ZGroup` Return the state of the system as a `SmallZarrGroups.ZGroup` This function should not mutate `state` @@ -116,20 +85,14 @@ This function should not mutate `state` Return the state that gets passed to `save_snapshot` - -### `Manifest.toml` and `Project.toml` - -These contain the julia environment used when running the simulation. -These must contain SmallZarrGroups, JSON3, and LoggingExtras, because these are required for saving data. - ### Main loop pseudo code ``` activate and instantiate the environment include("main.jl") -create output directory based on job_idx if it doesn't exist -Random.seed!(collect(reinterpret(UInt64, sha256(job_idx)))) -job_header, state = setup(job_idx) +create output directory based on job if it doesn't exist +Random.seed!(collect(reinterpret(UInt64, sha256(job)))) +job_header, state = setup(job) save job_header step = 0 SmallZarrGroups.save_dir(snapshot_zip_file, save_snapshot(step, state)) @@ -149,87 +112,23 @@ end ## `output` directory -The output directory has an `out$job_idx` subdirectory for job `job_idx`'s output. - -Each out subdirectory has the following files. - -### `info.log` -Any logs, warnings, and errors generated by the simulation are saved in this file. +The output directory has a subdirectory for each job's output. +The job string is the name of the subdirectory. -### `warn.log` -Any warnings, and errors generated by the simulation are saved in this file. +Each job's output subdirectory has the following files. -### `error.log` -Any errors generated by the simulation are saved in this file. +### `logs//{info|warn|error}.log` +Any logs, warnings, and errors generated by the simulation are saved in these files. -### `header.json` +### `traj/header.json` A description of the system. -### `list.txt` -Data describing the saved snapshots, and if the simulation is done or errored, or needs to be continued. - -The last element in each line is the sha256 of the line, not including the last comma space, and hash value. - - -The first line is. -``` -version = 1, job_idx = 1, input_tree_hash = 5a936e..., 54bf8d69288... -``` -- `version`: version of the info.txt format. -- `job_idx`: index of the job. -- `input_tree_hash`: hash of input directory calculated with [`my_tree_hash`](src/treehash.jl) - -The second line is: -``` -header_sha256 = 2cf934..., 312f788... -``` -- `header_sha256`: hash of header.json. -Or: -``` -Error starting job, 8d69288... -``` - -After these lines each of the next lines correspond to a saved snapshot. - -These have the format: -``` -yyyy-mm-dd HH:MM:SS, step number, nthreads, julia versioninfo, rng state, snapshot sha256, line sha256 -``` - -`snapshot sha256` is the sha256 of the snapshot zip file. - -The final line explains how the simulation ended it can be one of the following: -``` -Error starting job, line sha256 -``` - -``` -Error running job, line sha256 -``` - -``` -Error startup_timeout of $startup_timeout seconds reached, line sha256 -``` - -``` -Error step_timeout of $step_timeout seconds reached, line sha256 -``` - -``` -Error max_steps of $max_steps steps reached, line sha256 -``` - -``` -Error max_snapshot_MB of $max_snapshot_MB MB reached, line sha256 -``` - -``` -Done, line sha256 -``` - -See the log files for more details and error messages. - +### `traj/snap.zarr.zip` +Contains `snap$i.zarr.zip` files where `i` is the step of the simulation. +The state returned by `setup` is stored in `snap0.zarr.zip` +The user data is stored in the `"snap"` sub group. The root group contains +some metadata used by `MEDYANSimRunner`. -### `snapshots` subdirectory -Contains `snapshot$i.zarr.zip` files where `i` is the step of the simulation. -The state returned by `setup` is stored in `snapshot0.zarr.zip` +### `traj/footer.json` +This is created to show a trajectory is complete. +It contains some metadata about the trajectory. diff --git a/deps/build.jl b/deps/build.jl deleted file mode 100644 index 0371800..0000000 --- a/deps/build.jl +++ /dev/null @@ -1,2 +0,0 @@ -using MEDYANSimRunner -MEDYANSimRunner.comonicon_install() \ No newline at end of file diff --git a/src/MEDYANSimRunner.jl b/src/MEDYANSimRunner.jl index 8f7c3e7..2433d34 100644 --- a/src/MEDYANSimRunner.jl +++ b/src/MEDYANSimRunner.jl @@ -1,559 +1,11 @@ module MEDYANSimRunner -import Comonicon -import InteractiveUtils -using LoggingExtras -using Logging -using TOML -using Dates -using SHA -using Distributed -import Random - -const THIS_PACKAGE_VERSION::String = TOML.parsefile(pkgdir(MEDYANSimRunner, "Project.toml"))["version"] - -include("timeout.jl") -include("treehash.jl") - -const DATE_FORMAT = dateformat"yyyy-mm-dd HH:MM:SS" - -# amount to pad step count by with lpad -const STEP_PAD = 7 - - - # Don't use regular Base.open with append=true to make log files - # because it overwrites appends from other processes. - # Maybe add Base.Filesystem.JL_O_SYNC? -const LOG_FLAGS = Base.Filesystem.JL_O_APPEND | Base.Filesystem.JL_O_CREAT | Base.Filesystem.JL_O_WRONLY -const LOG_PERMISSIONS = Base.Filesystem.S_IROTH | Base.Filesystem.S_IRGRP | Base.Filesystem.S_IWGRP | Base.Filesystem.S_IRUSR | Base.Filesystem.S_IWUSR - -""" -Return a string describing the state of the rng without any newlines or commas -""" -function rng_2_str(rng = Random.default_rng())::String - myrng = copy(rng) - if typeof(myrng)==Random.Xoshiro - "Xoshiro: $(repr(myrng.s0)) $(repr(myrng.s1)) $(repr(myrng.s2)) $(repr(myrng.s3))" - else - error("rng of type $(typeof(myrng)) not supported yet") - end -end - -""" -Decode an rng stored in a string. -""" -function str_2_rng(str::AbstractString)::Random.AbstractRNG - parts = split(str, " ") - parts[1] == "Xoshiro:" || error("rng type must be Xoshiro not $(parts[1])") - state = parse.(UInt64, parts[2:end]) - Random.Xoshiro(state...) -end - -""" -Shared startup code -""" -const WORKER_STARTUP_CODE = Expr(:toplevel, (quote - copy!(LOAD_PATH,["@","@stdlib",]) - import Pkg; Pkg.instantiate() - import Dates - import LoggingExtras - import Logging - import JSON3 - import SmallZarrGroups - import Random - worker_timestamp_logger(logger) = LoggingExtras.TransformerLogger(logger) do log - merge(log, (; message = "$(Dates.format(Dates.now(), $DATE_FORMAT)) $(log.message)")) - end - - const worker_version_info::String = """ - Julia Version: $VERSION \ - MEDYANSimRunner Version: $($THIS_PACKAGE_VERSION) \ - OS: $(Sys.iswindows() ? "Windows" : Sys.isapple() ? "macOS" : Sys.KERNEL) ($(Sys.MACHINE)) \ - CPU: $(Sys.cpu_info()[1].model) \ - WORD_SIZE: $(Sys.WORD_SIZE) \ - LIBM: $(Base.libm_name) \ - LLVM: libLLVM-$(Base.libllvm_version) ($(Sys.JIT) $(Sys.CPU_NAME)) \ - Threads: $(Threads.nthreads()) on $(Sys.CPU_THREADS) virtual cores \ - """ - - """ - Return a string describing the state of the rng without any newlines or commas - """ - function worker_rng_2_str(rng = Random.default_rng())::String - myrng = copy(rng) - if typeof(myrng)==Random.Xoshiro - "Xoshiro: $(repr(myrng.s0)) $(repr(myrng.s1)) $(repr(myrng.s2)) $(repr(myrng.s3))" - else - error("rng of type $(typeof(myrng)) not supported yet") - end - end - - const worker_rng_str = Ref("") - const worker_rng_copy = copy(Random.default_rng()) - global jobout::String = "" - global step::Int = 0 - global job_idx::String = "" - module UserCode - include("main.jl") - end - - function save_load_snapshot_dir(step, jobout, worker_rng_str, state) - snapshot_path = joinpath(jobout,"snapshots","snapshot$step.zarr.zip") - SmallZarrGroups.save_dir(snapshot_path, UserCode.save_snapshot(step, state)) - worker_rng_str[] = worker_rng_2_str() - UserCode.load_snapshot(step, SmallZarrGroups.load_dir(snapshot_path), state) - end - - function setup_logging(jobout) - LoggingExtras.TeeLogger( - # Logging.global_logger(), - worker_timestamp_logger(Logging.ConsoleLogger(Base.Filesystem.open(joinpath(jobout,"info.log"), $LOG_FLAGS, $LOG_PERMISSIONS), Logging.Info)), - worker_timestamp_logger(Logging.ConsoleLogger(Base.Filesystem.open(joinpath(jobout,"warn.log"), $LOG_FLAGS, $LOG_PERMISSIONS), Logging.Warn)), - worker_timestamp_logger(Logging.ConsoleLogger(Base.Filesystem.open(joinpath(jobout,"error.log"), $LOG_FLAGS, $LOG_PERMISSIONS), Logging.Error)), - ) |> Logging.global_logger - end - nothing -end).args...) - -include("listparse.jl") +include("constants.jl") +include("rng-load-save.jl") +include("file-saving.jl") +include("cli-parsing.jl") +include("run-sim.jl") include("outputdiff.jl") -timestamp_logger(logger) = TransformerLogger(logger) do log - merge(log, (; message = "$(Dates.format(now(), DATE_FORMAT)) $(log.message)")) -end - -""" -Return true if the input_dir is valid. - -Otherwise log errors and return false. -""" -function is_input_dir_valid(input_dir::AbstractString) - if !isdir(input_dir) - @error "input directory $input_dir not found." - return false - end - required_toml_files = [ - "Manifest.toml", - "Project.toml", - ] - required_files = [ - required_toml_files; - "main.jl"; - ] - # check that required files exist - iszero(sum(required_files) do required_file - if !isfile(joinpath(input_dir,required_file)) - @error "$required_file missing from $input_dir." - true - else - false - end - end) || return false - - # check that toml files don't have syntax errors - iszero(sum(required_toml_files) do required_toml_file - ex = TOML.tryparsefile(joinpath(input_dir,required_toml_file)) - if ex isa TOML.ParserError - @error "invalid toml syntax." exception=ex - true - else - false - end - end) || return false - - return true -end - -""" -Print a `str` and add ", (hex sha256 of the line)\n", and then flush -""" -function println_list(io::IO, str::AbstractString) - println(io,str, ", ", bytes2hex(sha256(str))) - flush(io) - nothing -end - - -""" -Return the job_idx string and job_seed, or error if invalid. -""" -function normalize_job_idx(job_idx_or_file::AbstractString, job_line::Int = -1)::Tuple{String, Vector{UInt64}} - local unnorm_job_idx::String = if job_line == -1 - job_idx_or_file - else - job_line > 0 || throw(ArgumentError("job_line must be greater than 0 if used")) - isfile(job_idx_or_file) || throw(ArgumentError("job_file: $(job_idx_or_file) missing")) - readlines(job_idx_or_file)[job_line] - end - local job_idx_parts = split(replace(unnorm_job_idx, '\\'=>'/'), '/', keepempty=false) - isempty(job_idx_parts) && throw(ArgumentError("job_idx is empty")) - # each part of job_idx must be a valid part of a filename, on windows, linux and mac. - # if job_idx contains references to parent directories, it could be a big issue. - # job_idx is also stored in the csv file, so it cannot contain comma or newline. - banned_chars = [ - ',', - '\r', - '\n', - '\\', - '/', - '\0', - '*', - '|', - ':', - '<', - '>', - '?', - '"', - ] - for part in job_idx_parts - @assert !isempty(part) - # Check that parts are valid utf8 - isvalid(part) || throw(ArgumentError("invalid utf-8 $(collect(part))")) - if any(occursin(part), banned_chars) - throw(ArgumentError("job_idx part: $(repr(part)) cannot contain $banned_chars")) - end - endswith(part, '.') && throw(ArgumentError("job_idx part: $(repr(part)) cannot end with \".\"")) - startswith(part, '.') && throw(ArgumentError("job_idx part: $(repr(part)) cannot start with \".\"")) - end - job_idx = join(job_idx_parts, "/") - job_seed = collect(reinterpret(UInt64, sha256(job_idx))) - job_idx, job_seed -end - - -""" -Start or continue a simulation job. - -# Args - -- `input_dir`: The input directory. -- `output_dir`: The output directory. -- `job_idx_or_file`: The job index for multi-job simulations, or a filename. -- `job_line`: If specified, get the job index from this line in the `job_idx_or_file` file. - -# Options - -- `--step_timeout`: max amount of time a step can take in seconds. -- `--max_steps`: max number of steps. -- `--startup_timeout`: max amount of time job startup can take in seconds. -- `--max_snapshot_MB`: max amount of disk space one snapshot can take up. - -# Flags - -- `--force, -f`: delete existing "/out") if it exists, and restart the simulation. -- `--ignore_error, -i`: ignore previous error when restarting the simulation. - -""" -Comonicon.@cast function run( - input_dir::AbstractString, - output_dir::AbstractString, - job_idx_or_file::AbstractString, - job_line::Int = -1, - ; - step_timeout::Float64=Inf64, - max_steps::Int=1_000_000, - startup_timeout::Float64=Inf64, - max_snapshot_MB::Float64=1E3, - force::Bool=false, - ignore_error::Bool=false, - )::Int - job_idx::String, job_seed::Vector{UInt64} = normalize_job_idx(job_idx_or_file, job_line) - - if force - rm(joinpath(output_dir,job_idx); force=true, recursive=true) - end - - # first make the output folder - jobout = abspath(mkpath(joinpath(output_dir,job_idx))) - - # next set up logging - info_logger = ConsoleLogger(Base.Filesystem.open(joinpath(jobout,"info.log"), LOG_FLAGS, LOG_PERMISSIONS), Logging.Info) - warn_logger = ConsoleLogger(Base.Filesystem.open(joinpath(jobout,"warn.log"), LOG_FLAGS, LOG_PERMISSIONS), Logging.Warn) - error_logger = ConsoleLogger(Base.Filesystem.open(joinpath(jobout,"error.log"), LOG_FLAGS, LOG_PERMISSIONS), Logging.Error) - logger = TeeLogger( - global_logger(), - timestamp_logger(info_logger), - timestamp_logger(warn_logger), - timestamp_logger(error_logger), - ) - # now start writing to a file every second to detect multiple processes trying to run the same job - # any process that detects this should log an error and exit. - detect_mult_runners_f = Base.Filesystem.open(joinpath(jobout, "detect-mult-process"), LOG_FLAGS, LOG_PERMISSIONS) - detect_mult_runners_size = Ref(filesize(detect_mult_runners_f)) - detect_mult_runners_t = Timer(0.0; interval=0.5) do t - write(detect_mult_runners_f, 0x41) - flush(detect_mult_runners_f) - detect_mult_runners_size[] += 1 - if filesize(detect_mult_runners_f) != detect_mult_runners_size[] - @error "multiple runners are running this job, exiting" - exit(1) - end - end - - # This next section is inside a do block, so detect_mult_runners_t can be closed - # if an error occurs - return_code = with_logger(logger) do - - - detect_mult_runners_startup = @async sleep(1.1) - # wait on detect_mult_runners_startup before writing to list.txt - # the actual wait happens after some setup that cannot change list.txt - - - input_dir_valid = is_input_dir_valid(input_dir) - if !input_dir_valid - return 1 - end - - list_info, list_file_good_lines = try - parse_list_file(joinpath(jobout,"list.txt");ignore_error) - catch ex - @error "invalid list.txt syntax." exception=ex - return 1 - end - - # check if simulation is aready done - # note: this doesn't validate the snapshot files, or input files. - if !isempty(list_info.final_message) - @info "simulation already complete, exiting" - return 0 - end - - input_tree_hash = my_tree_hash(input_dir) - - # start the worker - # add processes on the same machine with the specified input dir - worker = addprocs(1; - topology=:master_worker, - exeflags="--project", - dir=input_dir, - )[1] - - worker_nthreads = remotecall_fetch(Threads.nthreads, worker) - wait(detect_mult_runners_startup) - - - "Log start up error and return 1" - function startup_error(list_file, status, result) - @error "failed to startup, status: $status" - if status == :timed_out - println_list(list_file, "Error startup_timeout of $startup_timeout seconds reached") - else - @error result - println_list(list_file, "Error starting job") - end - return 1 - end - - - # if list is empty start new job, otherwise continue the job - local step::Int - local worker_versioninfo::String - if list_info.isempty - @info "starting new job" - step = 0 - # delete stuff from dir and remake it - snapshots_dir = joinpath(jobout,"snapshots") - rm(snapshots_dir; force=true, recursive=true) - rm(joinpath(jobout,"list.txt"); force=true) - rm(joinpath(jobout,"header.json"); force=true) - list_file = Base.Filesystem.open(joinpath(jobout,"list.txt"), LOG_FLAGS, LOG_PERMISSIONS) - mkdir(snapshots_dir) - println_list(list_file, - "version = 1, job_idx = $job_idx, input_tree_hash = $(bytes2hex(input_tree_hash))" - ) - - @info "starting up simulation" - status, result = run_with_timeout(worker, startup_timeout, WORKER_STARTUP_CODE) - status == :ok || return startup_error(list_file, status, result) - status, result = run_with_timeout(worker, startup_timeout, quote - global step = $step - global job_idx = $job_idx - global jobout = $jobout - setup_logging(jobout) - # set seed on worker - Random.seed!($job_seed) - job_header, state = UserCode.setup(job_idx) - open(joinpath(jobout,"header.json"), "w") do io - JSON3.pretty(io, job_header; allow_inf = true) - end - state = save_load_snapshot_dir(step, jobout, worker_rng_str, state) - copy!(worker_rng_copy, Random.default_rng()) - worker_rng_str[], worker_version_info - end) - status == :ok || return startup_error(list_file, status, result) - worker_versioninfo = result[2] - header_sha256 = open(joinpath(jobout,"header.json")) do io - sha256(io) - end - println_list(list_file, "header_sha256 = $(bytes2hex(header_sha256))") - snapshot0_sha256 = my_tree_hash(joinpath(snapshots_dir,"snapshot$step.zarr.zip")) - println_list(list_file, - "$(Dates.format(now(),DATE_FORMAT)), \ - $(lpad(step,STEP_PAD)), \ - $worker_nthreads, \ - $worker_versioninfo, \ - $(result[1]), \ - $(bytes2hex(snapshot0_sha256))" - ) - @info "simulation started" - else - # Continue from an old job - @info "continuing job" - # check list_info is valid - if list_info.input_tree_hash != input_tree_hash - @error "input_tree_hash was $(bytes2hex(list_info.input_tree_hash)) now is $(bytes2hex(input_tree_hash))" - return 1 - end - if list_info.job_idx != job_idx - @error "job_idx was $(list_info.job_idx) now is $job_idx" - return 1 - end - # header isn't needed to continue the simulation, if the input hasn't changed, lets assume the header is still OK - - @info "finding last valid snapshot to load" - snapshot_i = length(list_info.snapshot_infos) - while snapshot_i > 0 - snapshot_info = list_info.snapshot_infos[snapshot_i] - snapshot_path = joinpath(jobout,"snapshots","snapshot$(snapshot_info.step_number).zarr.zip") - if isfile(snapshot_path) - snapshot_sha256 = my_tree_hash(snapshot_path) - if snapshot_info.snapshot_sha256 == snapshot_sha256 - break - end - end - snapshot_i -= 1 - end - if iszero(snapshot_i) - @error "none of the recorded snapshots are valid." - return 1 - end - snapshot_info = list_info.snapshot_infos[snapshot_i] - step = snapshot_info.step_number - - @info "restarting simulation from step $step" - - # delete stuff from dir and remake it - #rewrite list file to remove broken lines. - list_file_str = join(list_file_good_lines[begin:end-(length(list_info.snapshot_infos)-snapshot_i)], "\n")*"\n" - write(joinpath(jobout,"list.txt"), list_file_str) - list_file = Base.Filesystem.open(joinpath(jobout,"list.txt"), LOG_FLAGS, LOG_PERMISSIONS) - status, result = run_with_timeout(worker, startup_timeout, WORKER_STARTUP_CODE) - status == :ok || return startup_error(list_file, status, result) - # set globals - worker_rng_state = (snapshot_info.rngstate.s0, snapshot_info.rngstate.s1, snapshot_info.rngstate.s2, snapshot_info.rngstate.s3) - status, result = run_with_timeout(worker, startup_timeout, quote - global step = $step - global job_idx = $job_idx - global jobout = $jobout - setup_logging(jobout) - Random.seed!($job_seed) - job_header, state = UserCode.setup(job_idx) - copy!(Random.default_rng(), Random.Xoshiro(($worker_rng_state)...)) - state = UserCode.load_snapshot(step, SmallZarrGroups.load_dir(joinpath(jobout,"snapshots","snapshot$step.zarr.zip")), state) - isdone::Bool, expected_final_step::Int64 = UserCode.done(step, state) - copy!(worker_rng_copy, Random.default_rng()) - isdone, expected_final_step, worker_version_info - end) - status == :ok || return startup_error(list_file, status, result) - - @info "done restarting simulation from step $step" - worker_versioninfo = result[3] - - if result[1] - @info "simulation completed" - println_list(list_file, "Done") - return 0 - end - end - first_step::Bool = true - while step < max_steps - # On the first step use startup_timeout because the sim needs to compile. - timeout = if first_step - startup_timeout - else - step_timeout - end - status, result = run_with_timeout(worker, timeout, quote - copy!(Random.default_rng(), worker_rng_copy) - state = UserCode.loop(step, state) - yield() - step += 1 - state = save_load_snapshot_dir(step, jobout, worker_rng_str, state) - isdone::Bool, expected_final_step::Int64 = UserCode.done(step, state) - copy!(worker_rng_copy, Random.default_rng()) - GC.gc() # TODO: remove this when Julia v1.9 is released - isdone, expected_final_step, worker_rng_str[] - end) - step += 1 - if status != :ok - @error "failed to step, status: $status" - if status === :timed_out - if first_step - println_list(list_file, "Error startup_timeout of $startup_timeout seconds reached") - else - @error "step_timeout of $step_timeout seconds reached" - println_list(list_file, "Error step_timeout of $step_timeout seconds reached") - end - else - @error result - println_list(list_file, "Error running job") - end - return 1 - end - snapshot_path = joinpath(jobout,"snapshots","snapshot$step.zarr.zip") - snapshot_sha256 = my_tree_hash(snapshot_path) - println_list(list_file, - "$(Dates.format(now(),DATE_FORMAT)), \ - $(lpad(step,STEP_PAD)), \ - $worker_nthreads, \ - $worker_versioninfo, \ - $(result[3]), \ - $(bytes2hex(snapshot_sha256))" - ) - @info "step $step of $(result[2]) done" - - if result[1] - @info "simulation completed" - println_list(list_file, "Done") - return 0 - end - snapshot_disk_size = filesize(snapshot_path) - if snapshot_disk_size > max_snapshot_MB*2^20 - @error "snapshot too large, $(snapshot_disk_size/2^20) MB" - @error "max_snapshot_MB of $max_snapshot_MB MB reached" - println_list(list_file, "Error max_snapshot_MB of $max_snapshot_MB MB reached") - return 1 - end - first_step = false - end - @error "max_steps of $max_steps steps reached" - println_list(list_file, "Error max_steps of $max_steps steps reached") - return 1 - end # logging end - close(detect_mult_runners_t) - return_code -end - - -Comonicon.@main - - -# @precompile_setup begin -# # Putting some things in `setup` can reduce the size of the -# # precompile file and potentially make loading faster. -# test_out = mktempdir() -# test_out2 = mktempdir() -# input_dir = joinpath(dirname(@__DIR__),"test","examples","good","input") -# @precompile_all_calls begin -# # all calls in this block will be precompiled, regardless of whether -# # they belong to your package or not (on Julia 1.8 and higher) - -# MEDYANSimRunner.run(input_dir, test_out, 1) -# MEDYANSimRunner.run(input_dir, test_out, 1) -# # cp("../test/examples/good partial/output partial", test_out2; force=true) -# # MEDYANSimRunner.run("../test/examples/good/input/", test_out2, 1) -# end -# end -end # module MEDYANSimRunner +end \ No newline at end of file diff --git a/src/cli-parsing.jl b/src/cli-parsing.jl new file mode 100644 index 0000000..a03c7d7 --- /dev/null +++ b/src/cli-parsing.jl @@ -0,0 +1,77 @@ +@kwdef struct CLIOptions + continue_sim::Bool + batch::Int + out_dir::String +end + +Base.:(==)(a::CLIOptions, b::CLIOptions) = all((isequal(getfield(a,k), getfield(b,k)) for k in 1:fieldcount(typeof(a)))) + +const CLI_HELP = ( + """ + Usage: julia main.jl --out=output_dir --batch=1 --continue + + Options: + + --out= Save the trajectories and logs to this directory. + This directory will be created if it does not exist. + Defaults to the current working directory. + + --batch= Run just one of the jobs. + By default all jobs will run. + + --continue Try to continue from existing snapshots + in the output. By default, existing snapshots will + be deleted, and the simulation will start from + scratch. + + --help Print out this message. + """ +) + +function parse_cli_args(cli_args, jobs::Vector{String})::Union{CLIOptions, Nothing} + if any(startswith("--h"), cli_args) || any(startswith("-h"), cli_args) + println(CLI_HELP) + return + end + + continue_sim = parse_flag!(cli_args, "--continue") + + out_dir = something(parse_option!(cli_args, "--out"), ".") + + batch_str = something(parse_option!(cli_args, "--batch"), "-1") + batch = tryparse(Int, batch_str) + batch_range = 1:length(jobs) + if isnothing(batch) + @error "--batch must be a integer, instead got $(repr(batch_str))" + return + end + if !(batch == -1 || batch ∈ batch_range) + @error "--batch must be -1 or in $(batch_range), instead got $(batch)" + return + end + + unused_args = cli_args + if !isempty(cli_args) + @warn "not all ARGS used" unused_args + end + + return CLIOptions(; + continue_sim, + batch, + out_dir, + ) +end + +function parse_flag!(args, flag)::Bool + r = flag ∈ args + filter!(!=(flag), args) + r +end + +function parse_option!(args, option)::Union{Nothing, String} + i = findlast(startswith(option*"="), args) + isnothing(i) && return + v = split(args[something(i)], '='; limit=2)[2] + filter!(!startswith(option*"="), args) + String(v) +end \ No newline at end of file diff --git a/src/constants.jl b/src/constants.jl new file mode 100644 index 0000000..00008ae --- /dev/null +++ b/src/constants.jl @@ -0,0 +1,6 @@ +const THIS_PACKAGE_VERSION::String = string(pkgversion(@__MODULE__)) + + +const SNAP_PREFIX = "snap" + +const SNAP_POSTFIX = ".zarr.zip" \ No newline at end of file diff --git a/src/file-saving.jl b/src/file-saving.jl new file mode 100644 index 0000000..619d47c --- /dev/null +++ b/src/file-saving.jl @@ -0,0 +1,126 @@ +#= +Helper functions to try to reliably save trajectories on distributed file systemssrc/file-saving.jl + +None of these functions try to be thread safe, or prevent file system attacks. +=# + + +using SHA: sha256 +using Random: RandomDevice +import Dates +import LoggingExtras +using Logging +using ArgCheck + +const DATE_FORMAT = Dates.dateformat"yyyy-mm-ddTHH:MM:SS" + +function timestamp_logger(file, level) + LoggingExtras.MinLevelLogger( + LoggingExtras.TransformerLogger( + LoggingExtras.FileLogger(file; append=true), + ) do log + merge(log, (; message = "$(Dates.format(Dates.now(), DATE_FORMAT)) $(log.message)")) + end, + level, + ) +end + + +function in_new_log_dir(f, job_out::String) + date_part = Dates.format(Dates.now(),"yyyy-mm-ddTHH-MM-SS") + rand_part = Random.randstring(RandomDevice(), 12) + new_name = date_part*"_"*rand_part + all_logs = mkpath(joinpath(job_out, "logs")) + logs = mkdir(joinpath(all_logs, new_name)) + logger = LoggingExtras.TeeLogger( + current_logger(), + timestamp_logger(joinpath(logs, "info.log"), Logging.Info), + timestamp_logger(joinpath(logs, "warn.log"), Logging.Warn), + timestamp_logger(joinpath(logs, "error.log"), Logging.Error), + ) + with_logger(f, logger) +end + + + +""" +Ensure a file with the contents `data` exists at the path `joinpath(dir_name, file_name)` + +This function can fail or be interrupted, in that case, an existing file +or other thing at the path +will either not be modified, or will be replaced with a file with contents of `data`. + +If there is no existing file at path, +there will either be a file with contents of `data`, or no file. + +If interrupted, there may be a temporary file left behind in `dir_name`. +""" +function write_traj_file( + dir_name::String, + file_name::String, + data::AbstractVector{UInt8}, + )::Nothing + basic_name_check(file_name) + new_hash = sha256(data) + file_path = joinpath(dir_name, file_name) + if isfile(file_path) + if filesize(file_path) == length(data) + existing_hash = open(sha256, file_path) + if new_hash == existing_hash + # file exists and is correct, return + return + end + end + end + # safely create the new file. + mktemp(dir_name) do temp_path, temp_out + nb = write(temp_out, data) + if nb != length(data) + error("short write of $(repr(file_name)) data") + end + close(temp_out) + # mv(temp_path, file_path; force=true) + err = ccall(:jl_fs_rename, Int32, (Cstring, Cstring), temp_path, file_path) + # on error, check if file was made by another process, and is still valid. + if err < 0 + if isfile(file_path) + if filesize(file_path) == length(data) + existing_hash = open(sha256, file_path) + if new_hash == existing_hash + # file exists and is correct, return + return + end + end + end + # otherwise error + error("$(repr(file_path)) is corrupted") + end + nothing + end +end + + + + + +function basic_name_check(name::String)::Nothing + @argcheck !isempty(name) + @argcheck isvalid(name) + @argcheck !contains(name, '/') + @argcheck !contains(name, '\0') + @argcheck !contains(name, '\\') + @argcheck !contains(name, ':') + @argcheck !contains(name, '"') + @argcheck !contains(name, '*') + @argcheck !contains(name, '<') + @argcheck !contains(name, '>') + @argcheck !contains(name, '?') + @argcheck !contains(name, '|') + @argcheck !contains(name, '\x7f') + @argcheck all(>('\x1f'), name) # \n, \t, \e and other control chars are not allowed + @argcheck !endswith(name, ".") + @argcheck !endswith(name, " ") + # TODO check for reserved DOS names maybe + # From some testing on windows 11, the names seem "fine". + # if they are written as absolute paths with a prefix of \\?\ +end \ No newline at end of file diff --git a/src/listparse.jl b/src/listparse.jl deleted file mode 100644 index bee0120..0000000 --- a/src/listparse.jl +++ /dev/null @@ -1,135 +0,0 @@ -""" -Represents a snapshot in a parsed list.txt file. -""" -Base.@kwdef struct SnapshotInfoV1 - time_stamp::DateTime - step_number::Int - nthreads::Int - julia_versioninfo::String - rngstate::Random.Xoshiro - snapshot_sha256::Vector{UInt8} -end - - -function parse_snapshot_info_v1(line::Vector{<:AbstractString})::SnapshotInfoV1 - SnapshotInfoV1(; - time_stamp = DateTime(line[1], DATE_FORMAT), - step_number = parse(Int, line[2]), - nthreads = parse(Int, line[3]), - julia_versioninfo = line[4], - rngstate = str_2_rng(line[5]), - snapshot_sha256 = hex2bytes(line[6]), - ) -end - - -""" -Represents a parsed list.txt file. -""" -Base.@kwdef struct ListFileV1 - isempty::Bool = true - job_idx::String = "" - input_tree_hash::Vector{UInt8} = [] - header_sha256::Vector{UInt8} = [] - snapshot_infos::Vector{SnapshotInfoV1} = [] - final_message::String = "" -end - - -""" -Parse a list file. -Return a ListFileV1, and a vector of the good lines of the file, if successful, -If the file doesn't exist or is too short, return an empty ListFileV1. -If there is some error parsing, throw an error. - -if `ignore_error == true` ignore the last good line if it starts with "Error" -""" -function parse_list_file(listpath::AbstractString; ignore_error::Bool=false) - if !isfile(listpath) - return ListFileV1(), String[] - end - @assert isfile(listpath) - rawlines = readlines(listpath) - # remove lines that don't have a good checksum. - good_rawlines = filter(rawlines) do rawline - l = rsplit(rawline, ", "; limit=2) - length(l) == 2 || return false - linestr, linesha = l - reallinesha = bytes2hex(sha256(linestr)) - reallinesha == linesha - end - if ignore_error && !isempty(good_rawlines) && startswith(good_rawlines[end], "Error") - pop!(good_rawlines) - end - - # Return empty list file if too short - if length(good_rawlines) < 2 - return ListFileV1(), String[] - end - - lines = map(good_rawlines) do good_rawline - split(good_rawline, ", ")[begin:end-1] - end - firstlineparts = split.(lines[begin], " = ") - if firstlineparts[1] != ["version","1"] - @error firstlineparts[1] - error("list file has bad version") - end - - # now try and parse first line - if firstlineparts[2][1] != "job_idx" - error("expected \"job_idx\" got $(firstlineparts[2][1])") - end - job_idx = join(firstlineparts[2][2:end]," = ") - if firstlineparts[3][1] != "input_tree_hash" - error("expected \"input_tree_hash\" got $(firstlineparts[3][1])") - end - input_tree_hash = hex2bytes(firstlineparts[3][2]) - - # check if last line is error or done - maybemessage = lines[end][1] - final_message = if startswith(maybemessage, "Error") || startswith(maybemessage, "Done") - maybemessage - else - "" - end - - # error before header file written - if length(lines) == 2 && !isempty(final_message) - return ListFileV1(; - isempty=false, - job_idx, - input_tree_hash, - final_message, - ), good_rawlines - end - - # too few snapshots, not finished - if isempty(final_message) && length(lines) < 4 - return ListFileV1(), String[] - end - - if length(lines[begin+1]) != 1 - error("second line should just be header_sha265") - end - secondlineparts = split(lines[begin+1][1], " = ") - if secondlineparts[1] != "header_sha256" - error("expected \"header_sha256\" got $(secondlineparts[1])") - end - - header_sha256 = hex2bytes(secondlineparts[2]) - - num_snapshots = length(lines) - 2 - !isempty(final_message) - @assert num_snapshots ≥ 0 - snapshot_infos = map(1:num_snapshots) do i - parse_snapshot_info_v1(lines[i+2]) - end - return ListFileV1(; - isempty=false, - job_idx, - input_tree_hash, - header_sha256, - snapshot_infos, - final_message, - ), good_rawlines -end \ No newline at end of file diff --git a/src/outputdiff.jl b/src/outputdiff.jl index 62e79ed..cf49584 100644 --- a/src/outputdiff.jl +++ b/src/outputdiff.jl @@ -20,54 +20,10 @@ function print_json_diff(io::IO, json1::AbstractString, json2::AbstractString) end -""" - print_list_file_diff(io::IO, list1::AbstractString, list2::AbstractString) - -Print the difference in two list.txt files. - -ignoring the nthreads, timestamp, and julia version info. -""" -function print_list_file_diff(io::IO, list1::AbstractString, list2::AbstractString) - l1, _ = parse_list_file(list1) - l2, _ = parse_list_file(list2) - if l1.isempty || l2.isempty - # one list is empty or non existent - if l1.isempty & !(l2.isempty) - println(io, list1, " file missing or empty") - elseif l2.isempty & !(l1.isempty) - println(io, list2, " file missing or empty") - end - else - # both exist - for fname in (:isempty, :job_idx, :input_tree_hash, :final_message) - if getproperty(l1, fname) != getproperty(l2, fname) - println(io, list1," ",fname,": ", getproperty(l1, fname)) - println(io, list2," ",fname,": ", getproperty(l2, fname)) - end - end - if length(l1.snapshot_infos) != length(l2.snapshot_infos) - println(io, list1, " has ", length(l1.snapshot_infos), " snapshots recorded") - println(io, list1, " has ", length(l2.snapshot_infos), " snapshots recorded") - else - for i in eachindex(l1.snapshot_infos) - s1 = l1.snapshot_infos[i] - s2 = l2.snapshot_infos[i] - for fname in (:step_number, :rngstate) - if getproperty(s1, fname) != getproperty(s2, fname) - println(io, "$list1 snapshot $i $fname: $(getproperty(s1, fname))") - println(io, "$list2 snapshot $i $fname: $(getproperty(s2, fname))") - end - end - end - end - end -end - - """ Prints the difference between two job output directories. -Ignores time stamp differences and julia version differences in the list.txt file. +Ignores log files. Ignores anything in the snapshot files that has a name starting with a # @@ -77,31 +33,18 @@ Ignores anything in the snapshot files that has a name starting with a # - `jobout2`: The second output directory. """ -Comonicon.@cast function diff(jobout1::AbstractString, jobout2::AbstractString) - diff(stdout, jobout1, jobout2) +function print_traj_diff(jobout1::AbstractString, jobout2::AbstractString) + print_traj_diff(stdout, jobout1, jobout2) end -""" -Prints the difference between two job output directories. - -Ignores time stamp differences and julia version differences in the list.txt file. - -Ignores anything in the snapshot files that has a name starting with a # - -# Args - -- `jobout1`: The first output directory. -- `jobout2`: The second output directory. - -""" -function diff(io::IO, jobout1::AbstractString, jobout2::AbstractString) +function print_traj_diff(io::IO, jobout1::AbstractString, jobout2::AbstractString) isdir(jobout1) || throw(ArgumentError("$jobout1 path not found")) isdir(jobout2) || throw(ArgumentError("$jobout2 path not found")) # header.json - header1 = joinpath(jobout1,"header.json") - header2 = joinpath(jobout2,"header.json") + header1 = joinpath(jobout1, "traj", "header.json") + header2 = joinpath(jobout2, "traj", "header.json") if isfile(header1) & isfile(header2) print_json_diff(io, read(header1, String), read(header2, String)) elseif isfile(header1) & !isfile(header2) @@ -111,19 +54,14 @@ function diff(io::IO, jobout1::AbstractString, jobout2::AbstractString) else end - # list.txt - list1 = joinpath(jobout1,"list.txt") - list2 = joinpath(jobout2,"list.txt") - print_list_file_diff(io, list1, list2) - # snapshots sub dir - snapshot1dir = joinpath(jobout1, "snapshots") - snapshot2dir = joinpath(jobout2, "snapshots") + snapshot1dir = joinpath(jobout1, "traj") + snapshot2dir = joinpath(jobout2, "traj") snapshot1dir_exists = isdir(snapshot1dir) && !isempty(readdir(snapshot1dir; sort=false)) snapshot2dir_exists = isdir(snapshot2dir) && !isempty(readdir(snapshot2dir; sort=false)) if snapshot1dir_exists && snapshot2dir_exists - snapshots1 = sort(readdir(snapshot1dir; sort=false); by=(x->(length(x),x))) - snapshots2 = sort(readdir(snapshot2dir; sort=false); by=(x->(length(x),x))) + snapshots1 = filter(startswith(SNAP_PREFIX), sort(readdir(snapshot1dir; sort=false); by=(x->(length(x),x)))) + snapshots2 = filter(startswith(SNAP_PREFIX), sort(readdir(snapshot2dir; sort=false); by=(x->(length(x),x)))) for snapshotname in setdiff(snapshots1, snapshots2) println(io, joinpath(jobout2, "snapshots"), " missing: ", snapshotname) end @@ -131,8 +69,8 @@ function diff(io::IO, jobout1::AbstractString, jobout2::AbstractString) println(io, joinpath(jobout1, "snapshots"), " missing: ", snapshotname) end for snapshotname in (snapshots1 ∩ snapshots2) - full_name1 = joinpath(jobout1, "snapshots", snapshotname) - full_name2 = joinpath(jobout2, "snapshots", snapshotname) + full_name1 = joinpath(snapshot1dir, snapshotname) + full_name2 = joinpath(snapshot2dir, snapshotname) group1 = SmallZarrGroups.load_dir(full_name1) group2 = SmallZarrGroups.load_dir(full_name2) SmallZarrGroups.print_diff(io, group1, group2, full_name1, full_name2, "", startswith("#")) @@ -143,4 +81,16 @@ function diff(io::IO, jobout1::AbstractString, jobout2::AbstractString) println(io, snapshot1dir, " dir missing or empty") else end + + # header.json + footer1 = joinpath(jobout1, "traj", "footer.json") + footer2 = joinpath(jobout2, "traj", "footer.json") + if isfile(footer1) & isfile(footer2) + print_json_diff(io, read(footer1, String), read(footer2, String)) + elseif isfile(footer1) & !isfile(footer2) + println(io, footer2, " file missing") + elseif !isfile(footer1) & isfile(footer2) + println(io, footer1, " file missing") + else + end end \ No newline at end of file diff --git a/src/rng-load-save.jl b/src/rng-load-save.jl new file mode 100644 index 0000000..8826638 --- /dev/null +++ b/src/rng-load-save.jl @@ -0,0 +1,22 @@ +""" +Return a string describing the state of the rng without any newlines or commas +""" +function rng_2_str(rng = Random.default_rng())::String + myrng = copy(rng) + T = typeof(myrng) + if T == Random.Xoshiro + "Xoshiro: "*join((repr(getfield(myrng, i)) for i in 1:fieldcount(T)), " ") + else + error("rng of type $(typeof(myrng)) not supported yet") + end +end + +""" +Decode an rng stored in a string. +""" +function str_2_rng(str::AbstractString)::Random.AbstractRNG + parts = split(str, " ") + parts[1] == "Xoshiro:" || error("rng type must be Xoshiro not $(parts[1])") + state = parse.(UInt64, parts[2:end]) + Random.Xoshiro(state...) +end \ No newline at end of file diff --git a/src/run-sim.jl b/src/run-sim.jl new file mode 100644 index 0000000..c05b2c0 --- /dev/null +++ b/src/run-sim.jl @@ -0,0 +1,338 @@ +import InteractiveUtils +import LoggingExtras +import JSON3 +using Logging +import Dates +using SHA: sha256 +using ArgCheck +using SmallZarrGroups +import Random +import FileWatching +import OrderedCollections + + + +function get_version_string() + """ + Julia Version: $VERSION + MEDYANSimRunner Version: $(THIS_PACKAGE_VERSION) + OS: $(Sys.iswindows() ? "Windows" : Sys.isapple() ? "macOS" : Sys.KERNEL) ($(Sys.MACHINE)) + CPU: $(Sys.cpu_info()[1].model) + WORD_SIZE: $(Sys.WORD_SIZE) + LLVM: libLLVM-$(Base.libllvm_version) ($(Sys.JIT) $(Sys.CPU_NAME)) + Threads: $(Threads.nthreads()) on $(Sys.CPU_THREADS) virtual cores + """ +end + +""" + run_sim(ARGS; setup, loop, load_snapshot, save_snapshot, done) + +This function should be called at the end of a script to run a simulation. +It takes keyword arguments: + + - `jobs::AbstractVector{String}` +is a list of jobs. Each job is a string. +The string should be a valid directory name because +it will be used as the name of a subdirectory in the output directory. + + - `setup(job::String; kwargs...) -> header_dict, state` +is called once at the beginning of the simulation. + + - `loop(step::Int, state; kwargs...) -> state` +is called once per step of the simulation. + +- `save_snapshot(step::Int, state; kwargs...) -> group::SmallZarrGroups.ZGroup` +is called to save a snapshot. + + - `load_snapshot(step::Int, group::SmallZarrGroups.ZGroup, state; kwargs...) -> state` +is called to load a snapshot. + + - `done(step::Int, state; kwargs...) -> done::Bool, expected_final_step::Int` +is called to check if the simulation is done. + +`ARGS` is the command line arguments passed to the script. + +$(CLI_HELP) +""" +function run_sim(cli_args; + jobs::Vector{String}, + setup, + loop, + save_snapshot, + load_snapshot, + done, + kwargs... + ) + @nospecialize + @argcheck !isempty(jobs) + @argcheck allunique(jobs) + maybe_options = parse_cli_args(deepcopy(cli_args), jobs) + if isnothing(maybe_options) + return + end + options::CLIOptions = something(maybe_options) + if options.batch == -1 + # TODO run all jobs in parallel + for job in jobs + if options.continue_sim + continue_job(options.out_dir, job; + setup, + loop, + save_snapshot, + load_snapshot, + done, + ) + else + start_job(options.out_dir, job; + setup, + loop, + save_snapshot, + load_snapshot, + done, + ) + end + end + else + job = jobs[options.batch] + if options.continue_sim + continue_job(options.out_dir, job; + setup, + loop, + save_snapshot, + load_snapshot, + done, + ) + else + start_job(options.out_dir, job; + setup, + loop, + save_snapshot, + load_snapshot, + done, + ) + end + end + return +end + + +function start_job(out_dir, job::String; + setup, + loop, + save_snapshot, + load_snapshot, + done, + ) + basic_name_check.(String.(split(job, '/'; keepempty=true))) + # first set up logging + job_out = mkpath(joinpath(abspath(out_dir), job)) + in_new_log_dir(job_out) do + FileWatching.Pidfile.mkpidlock(joinpath(job_out,"traj.lock"); wait=false) do + @info "Starting new job." + @info get_version_string() + + # remove old snapshot data + rm(joinpath(job_out, "traj"); recursive=true, force=true) + traj = mkpath(joinpath(job_out, "traj")) + + rng_state = Random.Xoshiro(reinterpret(UInt64, sha256(job))...) + copy!(Random.default_rng(), rng_state) + job_header, state = setup(job) + copy!(rng_state, Random.default_rng()) + + @info "Setup complete." + header_str = sprint() do io + JSON3.pretty(io, job_header; allow_inf = true) + end + prev_sha256 = bytes2hex(sha256(header_str)) + write_traj_file(traj, "header.json", codeunits(header_str)) + local step::Int = 0 + + state, prev_sha256 = save_load_state!(rng_state, step, state, traj, save_snapshot, load_snapshot, prev_sha256) + @info "Simulation started." + while true + copy!(Random.default_rng(), rng_state) + state = loop(step, state) + copy!(rng_state, Random.default_rng()) + + step += 1 + state, prev_sha256 = save_load_state!(rng_state, step, state, traj, save_snapshot, load_snapshot, prev_sha256) + + copy!(Random.default_rng(), rng_state) + isdone::Bool, expected_final_step::Int64 = done(step::Int, state) + copy!(rng_state, Random.default_rng()) + + @info "step $step of $expected_final_step done" + if isdone + save_footer(traj, step, prev_sha256) + return + end + end + end + end +end + + + +function continue_job(out_dir, job; + setup, + loop, + save_snapshot, + load_snapshot, + done, + ) + basic_name_check.(String.(split(job, '/'; keepempty=true))) + # first set up logging + job_out = mkpath(joinpath(abspath(out_dir), job)) + traj = mkpath(joinpath(job_out, "traj")) + in_new_log_dir(job_out) do + @info "Continuing job." + @info get_version_string() + pidlock = try + FileWatching.Pidfile.mkpidlock(joinpath(job_out,"traj.lock"); wait=false) + catch ex + ex isa InterruptException && rethrow() + @warn "failed to get traj.lock, continuing." + nothing + end + try + # Figure out what step to continue from + snaps = readdir(traj) + if "footer.json" ∈ snaps + @info "Simulation already finished, exiting." + return + end + local step::Int = if "header.json" ∈ snaps + local steps = Int64[] + for file_name in snaps + isascii(file_name) || continue + startswith(file_name, SNAP_PREFIX) || continue + endswith(file_name, SNAP_POSTFIX) || continue + ncodeunits(file_name) > ncodeunits(SNAP_PREFIX) + ncodeunits(SNAP_POSTFIX) + local step_part = file_name[begin+ncodeunits(SNAP_PREFIX):end-ncodeunits(SNAP_POSTFIX)] + local step_maybe = tryparse(Int, step_part) + if !isnothing(step_maybe) + push!(steps, step_maybe) + end + end + maximum(steps; init=-1) + else + -2 + end + @info "Setting up simulation." + rng_state = Random.Xoshiro(reinterpret(UInt64, sha256(job))...) + copy!(Random.default_rng(), rng_state) + job_header, state = setup(job) + copy!(rng_state, Random.default_rng()) + @info "Setup complete." + if step == -2 || step == -1 + header_str = sprint() do io + JSON3.pretty(io, job_header; allow_inf = true) + end + prev_sha256 = bytes2hex(sha256(header_str)) + write_traj_file(traj, "header.json", codeunits(header_str)) + step = 0 + state, prev_sha256 = save_load_state!(rng_state, step, state, traj, save_snapshot, load_snapshot, prev_sha256) + @info "Simulation started." + else + @info "Continuing simulation from step $(step)." + snapshot_data = read(joinpath(traj, SNAP_PREFIX*string(step)*SNAP_POSTFIX)) + snapshot_group = unzip_group(snapshot_data) + reread_sub_snapshot_group = snapshot_group["snap"] + rng_state = str_2_rng(attrs(snapshot_group)["rng_state"]) + copy!(Random.default_rng(), rng_state) + state = load_snapshot(step, reread_sub_snapshot_group, state) + copy!(rng_state, Random.default_rng()) + prev_sha256 = bytes2hex(sha256(snapshot_data)) + if step > 0 + # check if done here. + copy!(Random.default_rng(), rng_state) + isdone::Bool, expected_final_step::Int64 = done(step::Int, state) + copy!(rng_state, Random.default_rng()) + @info "step $step of $expected_final_step done" + if isdone + save_footer(traj, step, prev_sha256) + return + end + end + end + while true + copy!(Random.default_rng(), rng_state) + state = loop(step, state) + copy!(rng_state, Random.default_rng()) + + step += 1 + state, prev_sha256 = save_load_state!(rng_state, step, state, traj, save_snapshot, load_snapshot, prev_sha256) + + copy!(Random.default_rng(), rng_state) + isdone, expected_final_step = done(step::Int, state) + copy!(rng_state, Random.default_rng()) + + @info "step $step of $expected_final_step done" + if isdone + save_footer(traj, step, prev_sha256) + return + end + end + finally + isnothing(pidlock) || close(pidlock) + end + end +end + + +function save_load_state!( + rng_state, + step::Int, + state, + traj::String, + save_snapshot, + load_snapshot, + prev_sha256::String, + ) + snapshot_group = ZGroup() + + copy!(Random.default_rng(), rng_state) + sub_snapshot_group = save_snapshot(step, state) + copy!(rng_state, Random.default_rng()) + + snapshot_group["snap"] = sub_snapshot_group + attrs(snapshot_group)["rng_state"] = rng_2_str(rng_state) + attrs(snapshot_group)["step"] = step + attrs(snapshot_group)["prev_sha256"] = prev_sha256 + snapshot_data = zip_group(snapshot_group) + reread_sub_snapshot_group = unzip_group(snapshot_data)["snap"] + + copy!(Random.default_rng(), rng_state) + state = load_snapshot(step, reread_sub_snapshot_group, state) + copy!(rng_state, Random.default_rng()) + + write_traj_file(traj, SNAP_PREFIX*string(step)*SNAP_POSTFIX, snapshot_data) + state, bytes2hex(sha256(snapshot_data)) +end + +function save_footer(traj, step, prev_sha256) + job_footer = OrderedCollections.OrderedDict([ + "steps" => step, + "prev_sha256" => prev_sha256, + ]) + footer_str = sprint() do io + JSON3.pretty(io, job_footer; allow_inf = true) + end + write_traj_file(traj, "footer.json", codeunits(footer_str)) + @info "Simulation completed." +end + +function zip_group(g::ZGroup)::Vector{UInt8} + io = IOBuffer() + writer = SmallZarrGroups.ZarrZipWriter(io) + SmallZarrGroups.save_dir(writer, g) + SmallZarrGroups.closewriter(writer) + take!(io) +end + +function unzip_group(data::Vector{UInt8})::ZGroup + SmallZarrGroups.load_dir(SmallZarrGroups.ZarrZipReader(data)) +end + + diff --git a/src/timeout.jl b/src/timeout.jl deleted file mode 100644 index 91801bf..0000000 --- a/src/timeout.jl +++ /dev/null @@ -1,95 +0,0 @@ -# run code on a worker with a timeout - -# A lot of this code is copied from -# https://github.com/ararslan/Timeout.jl/blob/master/src/Timeout.jl - - -using Base: _UVError - -using Base: SIGTERM - -using Distributed - - -""" - run_with_timeout(worker::Int, timeout::Float64, expr::Expr; verbose=true, poll=0.1) - -Run code on a worker in `Main` with a time out in seconds and return the result. -The worker must be on the same computer. - -Will return one of four possibilities. -1. `(:timed_out, nothing)` -2. `(:worker_exited, nothing)` -3. `(:errored, sprint(showerror, real_e, real_bt))` -4. `(:ok, fetched_value)` - -If the status is :timed_out or :worker_exited, `worker` will no longer be available. - -This function can also error if `expr` causes something -to be returned which cannot be interpreted on the master process. -""" -function run_with_timeout(worker::Int, timeout::Float64, expr::Expr; verbose=true, poll=0.1) - nprocs() > 1 || throw(ArgumentError("No worker processes available")) - worker in workers() || throw(ArgumentError("Unknown worker process ID: $worker")) - worker == myid() && throw(ArgumentError("Can't run on the current process")) - poll > 0 || throw(ArgumentError("Can't poll every $poll seconds")) - - # We need the worker process to be on the same host as the calling process, otherwise - # sending a SIGTERM to the result of getpid might kill off something local - if gethostname() != remotecall_fetch(gethostname, worker) - throw(ArgumentError("Can't run with a worker on a different host")) - end - - # Now start by getting the OS process ID for the worker so that we have something to - # forcibly kill if need be - ospid = remotecall_fetch(getpid, worker) - - # This remote call is put into a task bound to a channel, - # so the result can be polled - # with timedwait without any blocking. - channel = Channel() do ch - put!(ch, remotecall_fetch(Core.eval, worker, Main, expr)) - end - - timedout = timedwait(timeout; pollint=poll) do - # isready becomes true if put! happens, !isopen becomes true if channel has an error - isready(channel) || !isopen(channel) - end - - if timedout == :timed_out - verbose && @warn "Time limit for computation exceeded, forcibly kill the worker process..." - rc = ccall(:uv_kill, Cint, (Cint, Cint), ospid, SIGTERM) - rc == 0 || throw(_UVError("kill", rc)) - close(channel) - try - rmprocs(worker) - catch e - if e isa Base.IOError - #Windows creates an IOError here for some reason - else - rethrow() - end - end - return (:timed_out, nothing) - end - - @assert isready(channel) || !isopen(channel) - try - return (:ok, take!(channel)) - catch e - if e isa TaskFailedException - result_error = e.task.result - if result_error isa RemoteException - real_e = e.task.result.captured.ex - real_bt = e.task.result.captured.processed_bt - return (:errored, sprint(showerror, real_e, real_bt)) - elseif result_error isa ProcessExitedException - return (:worker_exited, nothing) - else - rethrow() - end - else - rethrow() - end - end -end \ No newline at end of file diff --git a/src/treehash.jl b/src/treehash.jl deleted file mode 100644 index 9536535..0000000 --- a/src/treehash.jl +++ /dev/null @@ -1,35 +0,0 @@ -# custom tree hash function - - -""" -Return a vector of bytes representing the hash of a directory. - -This is just used as a weak kind of checksum, not for cryptographic purposes. - -Any sub-directories or files with names beginning with a "." will be ignored except for files with the following names: -".zgroup", ".zarray", ".zattrs" - -Empty directories will NOT be ignored. -Any link will be ignored. -File permission and other metadata will be ignored. -""" -function my_tree_hash(path::AbstractString)::Vector{UInt8} - if isfile(path) - open(path) do f - sha256(f) - end - elseif isdir(path) - names = filter(readdir(path)) do name - namepath = joinpath(path,name) - !islink(namepath) && (!startswith(name,'.') || name in [".zgroup", ".zarray", ".zattrs"]) - end - content_hashs = UInt8[] - for name in names - append!(content_hashs, my_tree_hash(joinpath(path,name))) - append!(content_hashs, sha256(name)) - end - sha256(content_hashs) - else - error("$path must be a file or directory") - end -end diff --git a/test/Project.toml b/test/Project.toml index 5d2a4c0..478d2ce 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,7 +1,10 @@ [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" +Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" +LoggingExtras = "e6f89c97-d47a-5376-807f-9c37f3926c36" MEDYANSimRunner = "b58a3b99-22e3-44d1-b5ea-258f082a6fe8" +OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +SmallZarrGroups = "d423b6e5-1c84-4ae2-8d2d-b903aee15ac7" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a" diff --git a/test/examples/loading timeout/input/main.jl b/test/example/main.jl similarity index 52% rename from test/examples/loading timeout/input/main.jl rename to test/example/main.jl index 65e2cb4..81dd87b 100644 --- a/test/examples/loading timeout/input/main.jl +++ b/test/example/main.jl @@ -1,26 +1,25 @@ -# fibonacci sequence +# Install dependencies with: +# julia --project=.. -e 'using Pkg; pkg"dev ../.."; pkg"instantiate";' + +# Run this with: +# JULIA_LOAD_PATH="@" julia --project=.. --startup-file=no main.jl --out=output + +# Continue crashed simulations this with: +# JULIA_LOAD_PATH="@" julia --project=.. --startup-file=no main.jl --out=output --continue using SmallZarrGroups using Random -using SHA using OrderedCollections: OrderedDict +import MEDYANSimRunner -const a = Ref(0) -while true - a[] += 1 -end - +jobs = [ + "a", + "b", + "c", +] - -""" -Return the header dictionary to be written as the `header.json` file in output. -Also return the states that get passed on to `loop` and the states that get passed to `save_snapshot` and `load_snapshot`. -Also set the default random number generator seed. - -`job_idx::String`: The job index starting with job 1. This is used for multi job simulations. -""" -function setup(job_idx::String; kwargs...) +function setup(job::String; kwargs...) header = OrderedDict([ "version" => "1.0.0", "model_name" => "fibonacci sequence" @@ -30,7 +29,7 @@ function setup(job_idx::String; kwargs...) end function save_snapshot(step::Int, state; kwargs...)::ZGroup - @info "saving states" state + # @info "saving states" state group = ZGroup() group["states"] = state group @@ -51,3 +50,7 @@ function loop(step::Int, state; kwargs...) state[2] = a state end + +if abspath(PROGRAM_FILE) == @__FILE__ + MEDYANSimRunner.run_sim(ARGS; jobs, setup, loop, load_snapshot, save_snapshot, done) +end \ No newline at end of file diff --git a/test/examples/good partial/output partial/1/error.log b/test/example/output-ref-1_10/a/logs/2023-10-03T09-54-17_QhkWY1GZP7wr/error.log similarity index 100% rename from test/examples/good partial/output partial/1/error.log rename to test/example/output-ref-1_10/a/logs/2023-10-03T09-54-17_QhkWY1GZP7wr/error.log diff --git a/test/example/output-ref-1_10/a/logs/2023-10-03T09-54-17_QhkWY1GZP7wr/info.log b/test/example/output-ref-1_10/a/logs/2023-10-03T09-54-17_QhkWY1GZP7wr/info.log new file mode 100644 index 0000000..8bebacc --- /dev/null +++ b/test/example/output-ref-1_10/a/logs/2023-10-03T09-54-17_QhkWY1GZP7wr/info.log @@ -0,0 +1,40 @@ +┌ Info: 2023-10-03T09:54:18 Continuing job. +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:189 +┌ Info: 2023-10-03T09:54:19 Julia Version: 1.10.0-beta2 +│ MEDYANSimRunner Version: 0.4.0 +│ OS: Linux (x86_64-linux-gnu) +│ CPU: Intel(R) Core(TM) i7-10700KF CPU @ 3.80GHz +│ WORD_SIZE: 64 +│ LLVM: libLLVM-15.0.7 (ORCJIT skylake) +│ Threads: 1 on 16 virtual cores +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:190 +┌ Info: 2023-10-03T09:54:19 Setting up simulation. +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:222 +┌ Info: 2023-10-03T09:54:19 Setup complete. +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:227 +┌ Info: 2023-10-03T09:54:21 Simulation started. +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:236 +┌ Info: 2023-10-03T09:54:21 step 1 of 11 done +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:271 +┌ Info: 2023-10-03T09:54:21 step 2 of 11 done +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:271 +┌ Info: 2023-10-03T09:54:21 step 3 of 11 done +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:271 +┌ Info: 2023-10-03T09:54:21 step 4 of 11 done +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:271 +┌ Info: 2023-10-03T09:54:21 step 5 of 11 done +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:271 +┌ Info: 2023-10-03T09:54:21 step 6 of 11 done +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:271 +┌ Info: 2023-10-03T09:54:21 step 7 of 11 done +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:271 +┌ Info: 2023-10-03T09:54:21 step 8 of 11 done +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:271 +┌ Info: 2023-10-03T09:54:21 step 9 of 11 done +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:271 +┌ Info: 2023-10-03T09:54:21 step 10 of 11 done +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:271 +┌ Info: 2023-10-03T09:54:21 step 11 of 11 done +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:271 +┌ Info: 2023-10-03T09:54:21 Simulation completed. +└ @ MEDYANSimRunner /home/nathan/juliadev/MEDYANSimRunner/src/run-sim.jl:323 diff --git a/test/examples/good partial/output partial/1/warn.log b/test/example/output-ref-1_10/a/logs/2023-10-03T09-54-17_QhkWY1GZP7wr/warn.log similarity index 100% rename from test/examples/good partial/output partial/1/warn.log rename to test/example/output-ref-1_10/a/logs/2023-10-03T09-54-17_QhkWY1GZP7wr/warn.log diff --git a/test/example/output-ref-1_10/a/traj/footer.json b/test/example/output-ref-1_10/a/traj/footer.json new file mode 100644 index 0000000..0a5233d --- /dev/null +++ b/test/example/output-ref-1_10/a/traj/footer.json @@ -0,0 +1,4 @@ +{ + "steps": 11, + "prev_sha256": "cf1c5eaf14608cc3618966e476c4856888afbd1d1df7c8a64bb2b20b80af9996" +} \ No newline at end of file diff --git a/test/examples/good partial/output partial/1/header.json b/test/example/output-ref-1_10/a/traj/header.json similarity index 100% rename from test/examples/good partial/output partial/1/header.json rename to test/example/output-ref-1_10/a/traj/header.json diff --git a/test/example/output-ref-1_10/a/traj/snap0.zarr.zip b/test/example/output-ref-1_10/a/traj/snap0.zarr.zip new file mode 100644 index 0000000000000000000000000000000000000000..41e128f581969316c724eb2bb0bbdfb66e3ffe58 GIT binary patch literal 1123 zcmbVLv2NQi5S8sF4U85@(R_fwu1+F}l0_MvGt`TR4h4$BI8jt=M79i4G8!8m`WacW zq-(#Rd)A^qk)c1(wMSV(CBa=v;e_KI-#g;naWd{bJ2Q+EqgbcM_xUUIv^_V@jEl{+ zO!L*<-RVAG`)|nxq@2d7TtG%f&Ti6R`fWTs{>0u{WFyuxNcW3}1r8akB9r7Jl_}q45CSo=OA46g2G9e&l zp6@d&49H+#NM&M$HLP;Gl43noGjP12t_*YtRJfKEv6xau9oOdp_gF}g!*`>=9Xb)^ zmdjigc$yx9*z=;$1_%QH(r+nI*YpKys+O?2{C{D;{pvlLD5|!X6F@fJai@FU``2@+ zgf)z>U$kHLxD9H)j5_FD0V>`}8`K=4I_N$2u|865WOH8X$cGA9O-~!zoP|2H+bxTh V&~h>+O?X?YNfd^QUj@7la$#Wnx*T^ubI$3k0d;dZIIyg=lD@ z_o<#mDjiz(jmt3=L`v-Of$#$gCh248f%w#y!leUalAPGi6ap!893sHP11}ha0#nKe zBRoL;T_K%?OQ_+L$E6Ba6FpgMTaybR4>l5h>9OBxx=Ldui0^vGS8_KiL z4jfD{a~+Qco^Zk-;MC@**I=T`=`-L|Cv187|HNKx=5H(@)#&qmx`H=-X&<7u({@ix z`-~b;8`Q{tt@rK6_g6oEgV*TGM*0iWadvyC$Yw_^pXJG11C1Z>$C6&2E`VXJQw8P? zn#L)g>u3#Kw)=?3;VO*L(1&fp7GOm2+CzO53D`oc=5WxnT*?@n&bCY{vfE`bCs)y9 zTV)i-lNF0|<4E#2E@Q!-h00v2Voa3?&4tkG$EldHBIVzk*XG;8y=`wiYIn}N|9UQl z)SW!snYHY36I6d0B?6X*ySD|XcqdIzeTb@{chJY`NHvl5d8s15?veZHX+rC>P=$6n YWziB^PR4k2bZo+o;Ikn6dDoo&0Gj0CKL7v# literal 0 HcmV?d00001 diff --git a/test/example/output-ref-1_10/a/traj/snap10.zarr.zip b/test/example/output-ref-1_10/a/traj/snap10.zarr.zip new file mode 100644 index 0000000000000000000000000000000000000000..63c8d9b420aacf79b6635246767bef4b433caf5a GIT binary patch literal 1124 zcmbVLy^hmB5OxyoI7J{G;zVsNB_U_8e_}^hQ;;SN5)z(gIUKBwiO!I8t?Axd^4+Wz8#M`56&#>#45(=@m>A|R>mG$XI6iG zsnUFTdwaS&ulzS?O_Z9*RLx0-hIl)!W%`1?fBFgG#&(%VmMMKOQ`d%p)Z%iYGm;53 zw9(sCPiHC}+Rk+#a3tkm;9w?Mj0Ly^4Lr_;OC*UUl`go5FWrz~A!Et{L40sg-ye81 z6b@n7BLj(gyFxk>OQ_*G<$kHeYNDqE2capAJ>T=B6LTky+&J(gju;LjmpeXXQpA23 zyKdkS%6$>gAS6@3>W%yM_4$wAaANFfBYoeroZK8Lve|LVrupJZ1C8(Shmu~F z&VgaAQw8n~qDjiG^lS}nc6x{OJ6-&Z+HlF5oBxj9(mNm9mwKNHGas^Ux)7v@6f)gBHCC{q5tc}?Ip?ryu| zQLFv3^RM?(@(I)ZN3$+_+yvF1MiunB02L3V395^z3VPGBYLaRq>w2jo@At^PdYaI> c7OK!ryDVBlOJ$5lN5>}Y2tFIK_d90y2k_G73jhEB literal 0 HcmV?d00001 diff --git a/test/example/output-ref-1_10/a/traj/snap11.zarr.zip b/test/example/output-ref-1_10/a/traj/snap11.zarr.zip new file mode 100644 index 0000000000000000000000000000000000000000..bf1c7141e787e5f394cd1ddef4ccdc8b8001e05e GIT binary patch literal 1124 zcmbVLJ&)5s5OosnI9VVa;(ma&v?1rUH~xsIDM*tB2?8Iie&U8F9E{wtE zT4u%S?(TfQU;E#Z4M;gnGP!`9jGW!1!F2Wc*Y}@zT*saX@?5H;Os0i{$mR7^<&X<9 zvdEiE&E_&2S@wIIx}1hd06s7~02@=32EarV3|R~w_ajU(N`Z45Y#!3k4**ks;8Gq5 zC*(0r7{sK%FQjs@!WxuT>y;GishWW^^mJ(?5!7Z0_?!v{;)r{$@SNDg>Ve>#Gbf=D zja*=f6L2RSvcTos7jD9aBJ_yalA#LGQ7GeE=cXj?u7G8`<BPA^V`GSAPH!QRQ9$ literal 0 HcmV?d00001 diff --git a/test/example/output-ref-1_10/a/traj/snap2.zarr.zip b/test/example/output-ref-1_10/a/traj/snap2.zarr.zip new file mode 100644 index 0000000000000000000000000000000000000000..5c2ff3a7e00329e2b5eb6d3cfa590b49615e37ea GIT binary patch literal 1123 zcmbVL&5qMB5KdcmSydn{VjmzfH`LvAod0Ui9O$J74oC=9j^k{jO_Rz_Dz>Euo?y>= z;R0{LeXoco;DUG##`zIRR4zE0cs%wuV}0{^J{vtc)3g(<9H+;hi)Y{|d#s&l7u#!* z73=%^(`vu<-=QrPVi9FxMRPPI`*|mmGkSUV1CA@JGSNI2@?fU14g-nwdLeU~b2Qb_ z+e|KFkxg~{0UH+Q%<;Kpa*9I-LZ-v8Z(_$Fm>8~WLdXgn*Ri9JGuLDmw;)6~4Y*4g zcWn}SMua9+A(`_v)FAb8E%;_3mz3C!s?4mA1p&8g&j~OkZs?no5Mof?nKQOMjs{PHztt*;UlCWs%-Upz$4fU(?I76)-Gi zCLp{+L6Wf>8E>J>c!F5MH#|X88{CwxAc&H!g(fHt!G*Ju!9p&JC`w?{*w;zJ{B~XH zwnlN1EH*SL)J9q)NqrXLITz|utzafXt}cXLHE>Wsney+=JNnkict4)c2E+5w zzn)9YCrn>IDZlPQ94AgvAw~webYMV2sB#_qT3y;yc2eE7>cGN( zxP=A&0V6wNL;MMT2hT|=lBg^=dhzqKzxS-~eIAc`56&#>#45(=@u&O=c*Y)DXV&HB zTBZ5w{{FPwul=`Z!<3rHR4rJBhVE|M$n^5rbNmaA8!I!hEK~YmroIgWspa)VXDkzF zXrs5Op3PM{w4GZVxqcLKf-x0TP8oy(E+Wh#7c)-=#KRDx-1S);IWA>W>Jhg9iZ~=r zOhx4Tf-y8G3+YU(pa!XyD<#$wJ!7sM4i(|z^Puc)z$wK`}N1ia~nuC_IRJJ;7wmShv@Bg z$cbs6F$3y?nb9jJtJ$~j-_C#jh8<&1Tj?)N$JyPXBHNN$KFgOk8fbjyzOU%z=>izm zI#m$fqG^)y8$I7Zmz@FPiCBvS4SjHzYym-(Y&t2r$6ESEBYO}wj;#@XGf z(8-H=xNobRC&^^ZlH6=8^CYRxLOc`7T&fmKRV>Vf(5pQh6i}r6d-I0AZMVPck4K&E zMekqFrQ#E&FCUCw^|%dczKlBPbpa~gNgLD@Q62QAV>KkzMmF_QM}FQT_v&dwn_8$t byC9-M#}%|v#&~peY>pklXG8XjLSugbs5Rly literal 0 HcmV?d00001 diff --git a/test/example/output-ref-1_10/a/traj/snap4.zarr.zip b/test/example/output-ref-1_10/a/traj/snap4.zarr.zip new file mode 100644 index 0000000000000000000000000000000000000000..daa3d5165b12360f8613b7a7b8c57e08dcd33b45 GIT binary patch literal 1123 zcmbVL&5qMB5KddVtSXQeu@4ZLYj@Yqe=5!#=%oh^NC;Jq?QEk>o61fqwp9-+B;H{! zT!5$G&J`^W!E-Q96Olyaf}@G;@q9DZH=k$I!J~7-I5o;~cJjG+1}klkjdSC2do8nK zeSd#;IB)!SXiKG>$C+Hx98IkKtd;5E*T+u}5U%ZziRQUfM>CB~7)Y+J=PIYUKob+a z&D0{1*~G*jd}a|$T;{ov<5(^M7Y;>e#f(@ypnkwYa5>m=Fkze%$H!3+XqOvtk5L@j zwk<>$q4A-R%EcOLIMsG7#b&M+)N(ytnb@I2JjQq+V$3`u9Oio%)7asd#oQ+r3%N~P zPehmoI0>n8#eo8R5R~p;3p4CZ|t;Q?;V&tN$nV+w5*?0;$@b*6B*#)D=EPZ@*`s z>h@{VpaGa3!^32>`}X7ei=V&YMBDRD`b*t$dV8$M?m#VD6ssErG`_RmSM;)M2@ETh zNpSB_lxFNkC0pn+9wU~DjY!eNh2@Ga!HLqXgT^S~u!LB}V51gA9H($d_El1z->yrY zyi9_+t#Oj3^9@Z4eXuIhw2B3PE~LIx!xP1+`yB6ZEWcu{&?$s{{*H&f{S(d7UnFb~dlrAr)Dic|V zMkacnsM$PCMkcyV#mtp}<6mKXRqIm#Ih*c6cYL-V)42RaPN*ZK0 zt3oHQ=X~GRXdcJYwTN?lu*~DQiUofbrutGfXOae?z7Tq|hl2u&lz(sD(6>njyTN4K z>7Moe^;|Tm<#@Q&bJgQEsQEG~1WXUNZwpZIPTHWRi0Yts9it(sHnORgI`ZotxmQmc e+SEcF+U-_FD`=&R$+&-{!}{=j$K3+f|}PA=SZ3LlYK~+4W2nv=C@) zqF1?^Co&(K_?_*$HgSZ{s1xML8DzGRfr|@@ap4Iiq%ZbDRDjB*%cHE7KhXgd1U#_jY90Yf=5s^j%-S~Z8?rj zFt;7g3S$B;>iQmuu+PIFM#ByhO->&Hrz)e%%l{|#)7vj!O(0d<<9)e`H+6{*(cACY zCwhF^G^idmfOo4sw{M@Heg6#sZBIMpFZ9Ie?V%={9kpy;W;Y6Gd`;fe^s;;b3@epO zaBonQ=Ill#YnU<~A(o1jNYU7ZZAKU1MCsZ=Bb0F1Lat(PQ1dd5QwUnyx+pJhmz7SQ zC&7NKagwI96-`SW$jUUWbHQH-sV~)V=8_A2A#}Beg956Oe{bH>w@HTE;dIjLpAY`^ zTxvey@8f&DuV>r^wO>XP^r8Y)@1zTAi>L{D*)v*_>LS~EX(B)Dk$d%Yp=~WRq5Xc{ Ww1(Eom`;w4b=nd9OsIZ7(5F9p(B{NktK;jNZ#7poTjFVI(QMuq~;_=wujP=du@u+w2%(70bVw@g-${&Ge?7nqoU2Lw> zBwyX#otFEx{}ydnnod-bE?9uj;SINLIMT}MJO02 zQ4~aIP!`geTtSVXTCP&Lp6D5KJl|9fnVd>53M{ol?lB%X zp^rn3l`pt6rB1*@-;o|c{T35-P9FfLddXIo|4;1mk6)kKK&r8a`*a0w`Vt?ax7%?~ zO#6(LAaGzjDJQGhx9{Jc{rn9(#vZrQUzm=w+e1aRCAEB(FRwMw_||z>(aVzsFsyZw zLU@a&al)_ld;?v^1H@ywmN6Q7;4aw$f+*flG(d9!E}YdI7J8N|6~iXkRY^s5yDD_@ zVjk|>s^)P#S+h7d8_PV7tFsW#WNI!|3nrKHdZ{Bn?~!};w4qHc)S=yO XRkVUu${3H1j?J+n_-x32-ZQH|=;Y$k literal 0 HcmV?d00001 diff --git a/test/example/output-ref-1_10/a/traj/snap8.zarr.zip b/test/example/output-ref-1_10/a/traj/snap8.zarr.zip new file mode 100644 index 0000000000000000000000000000000000000000..93050372185f9a76717973f4a833eb7910094baf GIT binary patch literal 1123 zcmbVLF^|(Q6i!?2I8}fSaqN+ALhAM0IJT3DnSm}H7?2REoW#CXmo`1mRa7AvY{E?Kx4@QX9_m)vX2^-#PCqdc$N13~QaF z;NGDiPQqKg*g}`_0EMyK$QX?&ELUO)P84rlG(d|8mJq8$*ywq#R1AlseU&uI?$(7) zUM{>tTh$_tXB!dc=3te_aTN>xOs3{iHD{7W(p(6=I>12zMasW7Z|K{0`uqNL(&=9H z{`FicK4JRu(X6W;w?WO9Q3t&)K*c+0gPJ0$gWhzkhNRlare5mE&j;i|J#A=H3w3C> YTNSOKl`^K2lT#CR0-p`puX<+p2N+J_WB>pF literal 0 HcmV?d00001 diff --git a/test/example/output-ref-1_10/a/traj/snap9.zarr.zip b/test/example/output-ref-1_10/a/traj/snap9.zarr.zip new file mode 100644 index 0000000000000000000000000000000000000000..f9348b8b153b545b97e76babf09f1e1c16f359d5 GIT binary patch literal 1123 zcmbVL!H&}~5KUWlSyg}*u^$k5uif3)iJhe4v zEtU@tr@Qmoe}mRM%O-J}&3TT7WIJwT`uOe5vtJNyY?q1Wd8Q9$>f10-wz!$-oaYJ+ zZS*eH(^-}dZRb-!X~09qBJ2_-un#V(BnblHU@2lwDFv77$S{_!&j|5cD%=t%NTfKH zu}2)ANi^6M(z#kfjj-A-GqsxNDJPVf$|7`dM19PK?@1hzm|*4+#)xD%@_7&n>XOK1 zJc@W62H16dp&aTeC-yLN1nRe#sB`)RIMoZjy!wA)^ZDx922zbZ-KQ&g(^t+Rdb=I> z#I(%Cc5J#K@VFQX26U4qJY(grm}R0qB7SPe0u{WFyuxNcW3}1r8akB9r7Jl_}q45CSo=OA46g2G9e&l zp6@d&49H+#NM&M$HLP;Gl43noGjP12t_*YtRJfKEv6xau9oOdp_gF}g!*`>=9Xb)^ zmdjigc$yx9*z=;$1_%QH(r+nI*YpKys+O?2{C{D;{pvlLD5|!X6F@fJai@FU``2@+ zgf)z>U$kHLxD9H)j5_FD0V>`}8`K=4I_N$2u|865WOH8X$cGA9O-~!zoP|2H+bxTh V&~h>+O?X?YNfd^QUj@7la$#Wnx*T^ubI$3k0d;dZIIyg=lD@ z_o<#mDjiz(jmt3=L`v-Of$#$gCh248f%w#y!leUalAPGi6ap!893sHP11}ha0#nKe zBRoL;T_K%?OQ_+L$E6Ba6FpgMTaybR4>l5h>9OBxx=Ldui0^vGS8_KiL z4jfD{a~+Qco^Zk-;MC@**I=T`=`-L|Cv187|HNKx=5H(@)#&qmx`H=-X&<7u({@ix z`-~b;8`Q{tt@rK6_g6oEgV*TGM*0iWadvyC$Yw_^pXJG11C1Z>$C6&2E`VXJQw8P? zn#L)g>u3#Kw)=?3;VO*L(1&fp7GOm2+CzO53D`oc=5WxnT*?@n&bCY{vfE`bCs)y9 zTV)i-lNF0|<4E#2E@Q!-h00v2Voa3?&4tkG$EldHBIVzk*XG;8y=`wiYIn}N|9UQl z)SW!snYHY36I6d0B?6X*ySD|XcqdIzeTb@{chJY`NHvl5d8s15?veZHX+rC>P=$6n YWziB^PR4k2bZo+o;Ikn6dDoo&0Gj0CKL7v# literal 0 HcmV?d00001 diff --git a/test/example/output-ref-1_9/a/traj/snap10.zarr.zip b/test/example/output-ref-1_9/a/traj/snap10.zarr.zip new file mode 100644 index 0000000000000000000000000000000000000000..63c8d9b420aacf79b6635246767bef4b433caf5a GIT binary patch literal 1124 zcmbVLy^hmB5OxyoI7J{G;zVsNB_U_8e_}^hQ;;SN5)z(gIUKBwiO!I8t?Axd^4+Wz8#M`56&#>#45(=@m>A|R>mG$XI6iG zsnUFTdwaS&ulzS?O_Z9*RLx0-hIl)!W%`1?fBFgG#&(%VmMMKOQ`d%p)Z%iYGm;53 zw9(sCPiHC}+Rk+#a3tkm;9w?Mj0Ly^4Lr_;OC*UUl`go5FWrz~A!Et{L40sg-ye81 z6b@n7BLj(gyFxk>OQ_*G<$kHeYNDqE2capAJ>T=B6LTky+&J(gju;LjmpeXXQpA23 zyKdkS%6$>gAS6@3>W%yM_4$wAaANFfBYoeroZK8Lve|LVrupJZ1C8(Shmu~F z&VgaAQw8n~qDjiG^lS}nc6x{OJ6-&Z+HlF5oBxj9(mNm9mwKNHGas^Ux)7v@6f)gBHCC{q5tc}?Ip?ryu| zQLFv3^RM?(@(I)ZN3$+_+yvF1MiunB02L3V395^z3VPGBYLaRq>w2jo@At^PdYaI> c7OK!ryDVBlOJ$5lN5>}Y2tFIK_d90y2k_G73jhEB literal 0 HcmV?d00001 diff --git a/test/example/output-ref-1_9/a/traj/snap11.zarr.zip b/test/example/output-ref-1_9/a/traj/snap11.zarr.zip new file mode 100644 index 0000000000000000000000000000000000000000..bf1c7141e787e5f394cd1ddef4ccdc8b8001e05e GIT binary patch literal 1124 zcmbVLJ&)5s5OosnI9VVa;(ma&v?1rUH~xsIDM*tB2?8Iie&U8F9E{wtE zT4u%S?(TfQU;E#Z4M;gnGP!`9jGW!1!F2Wc*Y}@zT*saX@?5H;Os0i{$mR7^<&X<9 zvdEiE&E_&2S@wIIx}1hd06s7~02@=32EarV3|R~w_ajU(N`Z45Y#!3k4**ks;8Gq5 zC*(0r7{sK%FQjs@!WxuT>y;GishWW^^mJ(?5!7Z0_?!v{;)r{$@SNDg>Ve>#Gbf=D zja*=f6L2RSvcTos7jD9aBJ_yalA#LGQ7GeE=cXj?u7G8`<BPA^V`GSAPH!QRQ9$ literal 0 HcmV?d00001 diff --git a/test/example/output-ref-1_9/a/traj/snap2.zarr.zip b/test/example/output-ref-1_9/a/traj/snap2.zarr.zip new file mode 100644 index 0000000000000000000000000000000000000000..5c2ff3a7e00329e2b5eb6d3cfa590b49615e37ea GIT binary patch literal 1123 zcmbVL&5qMB5KdcmSydn{VjmzfH`LvAod0Ui9O$J74oC=9j^k{jO_Rz_Dz>Euo?y>= z;R0{LeXoco;DUG##`zIRR4zE0cs%wuV}0{^J{vtc)3g(<9H+;hi)Y{|d#s&l7u#!* z73=%^(`vu<-=QrPVi9FxMRPPI`*|mmGkSUV1CA@JGSNI2@?fU14g-nwdLeU~b2Qb_ z+e|KFkxg~{0UH+Q%<;Kpa*9I-LZ-v8Z(_$Fm>8~WLdXgn*Ri9JGuLDmw;)6~4Y*4g zcWn}SMua9+A(`_v)FAb8E%;_3mz3C!s?4mA1p&8g&j~OkZs?no5Mof?nKQOMjs{PHztt*;UlCWs%-Upz$4fU(?I76)-Gi zCLp{+L6Wf>8E>J>c!F5MH#|X88{CwxAc&H!g(fHt!G*Ju!9p&JC`w?{*w;zJ{B~XH zwnlN1EH*SL)J9q)NqrXLITz|utzafXt}cXLHE>Wsney+=JNnkict4)c2E+5w zzn)9YCrn>IDZlPQ94AgvAw~webYMV2sB#_qT3y;yc2eE7>cGN( zxP=A&0V6wNL;MMT2hT|=lBg^=dhzqKzxS-~eIAc`56&#>#45(=@u&O=c*Y)DXV&HB zTBZ5w{{FPwul=`Z!<3rHR4rJBhVE|M$n^5rbNmaA8!I!hEK~YmroIgWspa)VXDkzF zXrs5Op3PM{w4GZVxqcLKf-x0TP8oy(E+Wh#7c)-=#KRDx-1S);IWA>W>Jhg9iZ~=r zOhx4Tf-y8G3+YU(pa!XyD<#$wJ!7sM4i(|z^Puc)z$wK`}N1ia~nuC_IRJJ;7wmShv@Bg z$cbs6F$3y?nb9jJtJ$~j-_C#jh8<&1Tj?)N$JyPXBHNN$KFgOk8fbjyzOU%z=>izm zI#m$fqG^)y8$I7Zmz@FPiCBvS4SjHzYym-(Y&t2r$6ESEBYO}wj;#@XGf z(8-H=xNobRC&^^ZlH6=8^CYRxLOc`7T&fmKRV>Vf(5pQh6i}r6d-I0AZMVPck4K&E zMekqFrQ#E&FCUCw^|%dczKlBPbpa~gNgLD@Q62QAV>KkzMmF_QM}FQT_v&dwn_8$t byC9-M#}%|v#&~peY>pklXG8XjLSugbs5Rly literal 0 HcmV?d00001 diff --git a/test/example/output-ref-1_9/a/traj/snap4.zarr.zip b/test/example/output-ref-1_9/a/traj/snap4.zarr.zip new file mode 100644 index 0000000000000000000000000000000000000000..daa3d5165b12360f8613b7a7b8c57e08dcd33b45 GIT binary patch literal 1123 zcmbVL&5qMB5KddVtSXQeu@4ZLYj@Yqe=5!#=%oh^NC;Jq?QEk>o61fqwp9-+B;H{! zT!5$G&J`^W!E-Q96Olyaf}@G;@q9DZH=k$I!J~7-I5o;~cJjG+1}klkjdSC2do8nK zeSd#;IB)!SXiKG>$C+Hx98IkKtd;5E*T+u}5U%ZziRQUfM>CB~7)Y+J=PIYUKob+a z&D0{1*~G*jd}a|$T;{ov<5(^M7Y;>e#f(@ypnkwYa5>m=Fkze%$H!3+XqOvtk5L@j zwk<>$q4A-R%EcOLIMsG7#b&M+)N(ytnb@I2JjQq+V$3`u9Oio%)7asd#oQ+r3%N~P zPehmoI0>n8#eo8R5R~p;3p4CZ|t;Q?;V&tN$nV+w5*?0;$@b*6B*#)D=EPZ@*`s z>h@{VpaGa3!^32>`}X7ei=V&YMBDRD`b*t$dV8$M?m#VD6ssErG`_RmSM;)M2@ETh zNpSB_lxFNkC0pn+9wU~DjY!eNh2@Ga!HLqXgT^S~u!LB}V51gA9H($d_El1z->yrY zyi9_+t#Oj3^9@Z4eXuIhw2B3PE~LIx!xP1+`yB6ZEWcu{&?$s{{*H&f{S(d7UnFb~dlrAr)Dic|V zMkacnsM$PCMkcyV#mtp}<6mKXRqIm#Ih*c6cYL-V)42RaPN*ZK0 zt3oHQ=X~GRXdcJYwTN?lu*~DQiUofbrutGfXOae?z7Tq|hl2u&lz(sD(6>njyTN4K z>7Moe^;|Tm<#@Q&bJgQEsQEG~1WXUNZwpZIPTHWRi0Yts9it(sHnORgI`ZotxmQmc e+SEcF+U-_FD`=&R$+&-{!}{=j$K3+f|}PA=SZ3LlYK~+4W2nv=C@) zqF1?^Co&(K_?_*$HgSZ{s1xML8DzGRfr|@@ap4Iiq%ZbDRDjB*%cHE7KhXgd1U#_jY90Yf=5s^j%-S~Z8?rj zFt;7g3S$B;>iQmuu+PIFM#ByhO->&Hrz)e%%l{|#)7vj!O(0d<<9)e`H+6{*(cACY zCwhF^G^idmfOo4sw{M@Heg6#sZBIMpFZ9Ie?V%={9kpy;W;Y6Gd`;fe^s;;b3@epO zaBonQ=Ill#YnU<~A(o1jNYU7ZZAKU1MCsZ=Bb0F1Lat(PQ1dd5QwUnyx+pJhmz7SQ zC&7NKagwI96-`SW$jUUWbHQH-sV~)V=8_A2A#}Beg956Oe{bH>w@HTE;dIjLpAY`^ zTxvey@8f&DuV>r^wO>XP^r8Y)@1zTAi>L{D*)v*_>LS~EX(B)Dk$d%Yp=~WRq5Xc{ Ww1(Eom`;w4b=nd9OsIZ7(5F9p(B{NktK;jNZ#7poTjFVI(QMuq~;_=wujP=du@u+w2%(70bVw@g-${&Ge?7nqoU2Lw> zBwyX#otFEx{}ydnnod-bE?9uj;SINLIMT}MJO02 zQ4~aIP!`geTtSVXTCP&Lp6D5KJl|9fnVd>53M{ol?lB%X zp^rn3l`pt6rB1*@-;o|c{T35-P9FfLddXIo|4;1mk6)kKK&r8a`*a0w`Vt?ax7%?~ zO#6(LAaGzjDJQGhx9{Jc{rn9(#vZrQUzm=w+e1aRCAEB(FRwMw_||z>(aVzsFsyZw zLU@a&al)_ld;?v^1H@ywmN6Q7;4aw$f+*flG(d9!E}YdI7J8N|6~iXkRY^s5yDD_@ zVjk|>s^)P#S+h7d8_PV7tFsW#WNI!|3nrKHdZ{Bn?~!};w4qHc)S=yO XRkVUu${3H1j?J+n_-x32-ZQH|=;Y$k literal 0 HcmV?d00001 diff --git a/test/example/output-ref-1_9/a/traj/snap8.zarr.zip b/test/example/output-ref-1_9/a/traj/snap8.zarr.zip new file mode 100644 index 0000000000000000000000000000000000000000..93050372185f9a76717973f4a833eb7910094baf GIT binary patch literal 1123 zcmbVLF^|(Q6i!?2I8}fSaqN+ALhAM0IJT3DnSm}H7?2REoW#CXmo`1mRa7AvY{E?Kx4@QX9_m)vX2^-#PCqdc$N13~QaF z;NGDiPQqKg*g}`_0EMyK$QX?&ELUO)P84rlG(d|8mJq8$*ywq#R1AlseU&uI?$(7) zUM{>tTh$_tXB!dc=3te_aTN>xOs3{iHD{7W(p(6=I>12zMasW7Z|K{0`uqNL(&=9H z{`FicK4JRu(X6W;w?WO9Q3t&)K*c+0gPJ0$gWhzkhNRlare5mE&j;i|J#A=H3w3C> YTNSOKl`^K2lT#CR0-p`puX<+p2N+J_WB>pF literal 0 HcmV?d00001 diff --git a/test/example/output-ref-1_9/a/traj/snap9.zarr.zip b/test/example/output-ref-1_9/a/traj/snap9.zarr.zip new file mode 100644 index 0000000000000000000000000000000000000000..f9348b8b153b545b97e76babf09f1e1c16f359d5 GIT binary patch literal 1123 zcmbVL!H&}~5KUWlSyg}*u^$k5uif3)iJhe4v zEtU@tr@Qmoe}mRM%O-J}&3TT7WIJwT`uOe5vtJNyY?q1Wd8Q9$>f10-wz!$-oaYJ+ zZS*eH(^-}dZRb-!X~09qBJ2_-un#V(BnblHU@2lwDFv77$S{_!&j|5cD%=t%NTfKH zu}2)ANi^6M(z#kfjj-A-GqsxNDJPVf$|7`dM19PK?@1hzm|*4+#)xD%@_7&n>XOK1 zJc@W62H16dp&aTeC-yLN1nRe#sB`)RIMoZjy!wA)^ZDx922zbZ-KQ&g(^t+Rdb=I> z#I(%Cc5J#K@VFQX26U4qJY(grm}R0qB7SPe09Kae%tYFMX$fBlkV;UNSqQzcgE9;MT?MQL(6>Ii*_1H`C)=6O}jp(dPZ|Inr}@VtFtTNln4EEeIH$QU>lNx*1841Xf>w1xbi5 zz7+umBCeu{Ve+g|MZx^)OI#Sf32exUA1GicU8ONZ&fd?N-e|p_Iom~<9Wk- HvD)YlhOB&j diff --git a/test/examples/good partial/output partial/1/snapshots/snapshot1.zarr.zip b/test/examples/good partial/output partial/1/snapshots/snapshot1.zarr.zip deleted file mode 100644 index 5c9b99b01a75222074bd034a7a13945579d4e7ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmZ`$%}T>S5N=wnB?qa{2MFUe)EIw|;Mq$q@#00Ktm(#tZhqL!7MirL;3IhTA-wnM zBX|*?!`ZqL6L7-E%zU%oWS9y3!QP~&%)TXeE{hr}!uc%9c}hy~ z+)dC@eXKvX7QR*(Aw)?^Do&ifaW)!bu3#YygtGpR>$SUV-;UqEu%d8?&|RkL*JM?j z78-=H%oYMX>)w5^HI=6Yr$Xc$=^7qc9xg;uQSclaLPnRAfp>vnMp7h!Rp($sGRF|# ziVzbK%P3-4Jl*J~^Kw;SZozeO1)7Tz#%4>xq$*^Rv4$@xS3~_Fb3UhPAh`LJiHU8Xy*Takb-JSNIU3PrvsS08>aM?0ts9j-M&R!cRegZpEBfc% GWxoN}c72Ng diff --git a/test/examples/good partial/output partial/1/snapshots/snapshot2.zarr.zip b/test/examples/good partial/output partial/1/snapshots/snapshot2.zarr.zip deleted file mode 100644 index f4aacd9b3e6508cbc39fdd870fe2fe3bdd5f1400..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmZ`$%}T>S5N_I9OAb<@4-m$a5NfPHNbn|j$t7OAh?F(i*w9Tj>?VaK<`sMduY#}O z$%|Lt!sl>SS7HKA_?Ve*_L~edzBd?7d&=x9ay=P66}uQKY$Imnz2HSwK}m#IL|h~! z2X|ikE!E50#lD5F6~+jelbp%}r*9mMCzvZ(h(1Eu@W=JqUEW{!K0dLcu!GQDX6n~u zQ<)YT1j`~_NpP*3`K_%fo@9bb$pz9C+%X=kWL#2k9UB5h*OY;Kf?-M$B!QL3U_%^Y zh;L!xA8$uPIzJU#=>MKNQGHDQG+qy=LQpHrcR`a^ONQZ*1<{>tT& ze79^SZ!OiDo3-hCMt|D-{~mgA+Ue_bMcs2WqSI!rPFK}kf1`S6RQedcw>47r0e;Wu I9}Sg#0r0GQG5`Po diff --git a/test/examples/good partial/output partial/1/snapshots/snapshot3.zarr.zip b/test/examples/good partial/output partial/1/snapshots/snapshot3.zarr.zip deleted file mode 100644 index fc96826bf9c469067ae8aabf3135fe5a59d364f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmZ`$%}&BV5T<}gI2cTLfX#Sq5b-Cb@$99SdhuciS6%<^lag?bU z&%qn6gFV&T`{;+^YeE~LbDj%*I;cD6?JmXyhG-(ls504pmCuipuWw`tM+jS`Pha!p zs>9GAj7>V#;IW6{$hA~9n=7GprjTC2qs+poP6`3uz=cqXg^=J~qMPy=k|2uVh8S$6{8srPFC?*qrOV|F`LQFR@q;tJjEa;mE8_)FDusj|xm{DT(d8+cyv I-Z&Kf0t-NV;{X5v diff --git a/test/examples/good partial/output partial/1/snapshots/snapshot4.zarr.zip b/test/examples/good partial/output partial/1/snapshots/snapshot4.zarr.zip deleted file mode 100644 index f2ec3a0880b4c5dd7ebbe436398698edb9d75a5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmZ`$%}T>S5N=v+YY?f>2MFV}));?~;Mq$r@#00Ktm(#tZn|YRDKu%pC-4!x`Vii| z`WC*1&ge>PzzH8S^UZ#fVI~;0w)zdS!)tcaYd`1v7!x{(Vez13J};qQN{u6_QkH@5 zt%D8KPUrK{!PkT?LT4=F`p9isC*2%sd6RtS3t(`87%X3Nrc z&>)QS*;Iq?+zIAF ztR~-Bs^i&fI~ZBbe&hdpn8g`qu2UD)&rylatF^jyRek-HYE`MsF@n){o9Zq6UeG^T GlzjnN1$wmr diff --git a/test/examples/good partial/output partial/1/snapshots/snapshot5.zarr.zip b/test/examples/good partial/output partial/1/snapshots/snapshot5.zarr.zip deleted file mode 100644 index e69907cf8374c1b0ab68866b645b7f5e3e3f79af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmZ`$%}#?r5QbK3LyXqg2O#6MR-|eS@$7{QUc4C7taK65b4 zeG8w%SzUvWIONOBe6!y$nep8I##obfctx&9gXdxoV~H)qw7lnOkyTI<&gVhOW0Heo z*Ir9C+kJa9@wLPdA###aab)%M*>Hrpgo)TjNZbE$TD!~p>%qq-RwQ;1s>@XVnyf3s zL<2u8lBEF0ys>X5ib~^*Qz250bOm=T^_Lyd`4H4fpdysLSiI=l_xNPXn`TV z6+R{+EP{Yx@wiq^7x_BF+=Pqh926HsjLlbs6|#^Nj5T~txg6>bneqjd1Ht95T!iGi zWixqesitkzhUe "1.0.0", - "model_name" => "fibonacci sequence" - ]) - state = [0, 1] - header, state -end - -function save_snapshot(step::Int, state; kwargs...)::ZGroup - @info "saving states" state - group = ZGroup() - group["states"] = state - group -end - -function load_snapshot(step::Int, group, state; kwargs...) - state .= collect(group["states"]) - state -end - -function done(step::Int, state; kwargs...) - step > 10, 11 -end - -function loop(step::Int, state; kwargs...) - a = sum(state) + rand(0:1) - state[1] = state[2] - state[2] = a - state -end diff --git a/test/examples/good/jobnames.txt b/test/examples/good/jobnames.txt deleted file mode 100644 index cda79b5..0000000 --- a/test/examples/good/jobnames.txt +++ /dev/null @@ -1,5 +0,0 @@ -6 -3 -9 -11 -0 diff --git a/test/examples/good/output-ref/1/detect-mult-process b/test/examples/good/output-ref/1/detect-mult-process deleted file mode 100644 index 08b6f77..0000000 --- a/test/examples/good/output-ref/1/detect-mult-process +++ /dev/null @@ -1 +0,0 @@ -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \ No newline at end of file diff --git a/test/examples/good/output-ref/1/info.log b/test/examples/good/output-ref/1/info.log deleted file mode 100644 index ac3c070..0000000 --- a/test/examples/good/output-ref/1/info.log +++ /dev/null @@ -1,75 +0,0 @@ -[ Info: 2023-03-01 10:09:11 starting new job -[ Info: 2023-03-01 10:09:11 starting up simulation -┌ Info: 2023-03-01 10:09:22 saving states -│ state = -│ 2-element Vector{Int64}: -│ 0 -└ 1 -[ Info: 2023-03-01 10:09:25 simulation started -┌ Info: 2023-03-01 10:09:25 saving states -│ state = -│ 2-element Vector{Int64}: -│ 1 -└ 2 -[ Info: 2023-03-01 10:09:25 step 1 of 11 done -┌ Info: 2023-03-01 10:09:25 saving states -│ state = -│ 2-element Vector{Int64}: -│ 2 -└ 4 -[ Info: 2023-03-01 10:09:25 step 2 of 11 done -┌ Info: 2023-03-01 10:09:25 saving states -│ state = -│ 2-element Vector{Int64}: -│ 4 -└ 7 -[ Info: 2023-03-01 10:09:26 step 3 of 11 done -┌ Info: 2023-03-01 10:09:26 saving states -│ state = -│ 2-element Vector{Int64}: -│ 7 -└ 12 -[ Info: 2023-03-01 10:09:26 step 4 of 11 done -┌ Info: 2023-03-01 10:09:26 saving states -│ state = -│ 2-element Vector{Int64}: -│ 12 -└ 19 -[ Info: 2023-03-01 10:09:26 step 5 of 11 done -┌ Info: 2023-03-01 10:09:26 saving states -│ state = -│ 2-element Vector{Int64}: -│ 19 -└ 32 -[ Info: 2023-03-01 10:09:26 step 6 of 11 done -┌ Info: 2023-03-01 10:09:26 saving states -│ state = -│ 2-element Vector{Int64}: -│ 32 -└ 52 -[ Info: 2023-03-01 10:09:26 step 7 of 11 done -┌ Info: 2023-03-01 10:09:26 saving states -│ state = -│ 2-element Vector{Int64}: -│ 52 -└ 85 -[ Info: 2023-03-01 10:09:27 step 8 of 11 done -┌ Info: 2023-03-01 10:09:27 saving states -│ state = -│ 2-element Vector{Int64}: -│ 85 -└ 137 -[ Info: 2023-03-01 10:09:27 step 9 of 11 done -┌ Info: 2023-03-01 10:09:27 saving states -│ state = -│ 2-element Vector{Int64}: -│ 137 -└ 222 -[ Info: 2023-03-01 10:09:27 step 10 of 11 done -┌ Info: 2023-03-01 10:09:27 saving states -│ state = -│ 2-element Vector{Int64}: -│ 222 -└ 360 -[ Info: 2023-03-01 10:09:27 step 11 of 11 done -[ Info: 2023-03-01 10:09:27 simulation completed diff --git a/test/examples/good/output-ref/1/list.txt b/test/examples/good/output-ref/1/list.txt deleted file mode 100644 index 6b5cc20..0000000 --- a/test/examples/good/output-ref/1/list.txt +++ /dev/null @@ -1,15 +0,0 @@ -version = 1, job_idx = 1, input_tree_hash = d5376cd8eab0cf31d7c3f59c7bcde79ea42f38dadd320201fa7ed92f9d6869aa, e7d97c684248bd8f6799a7dc88d19e06ecc10f15cd6964c0f410ba535928186a -header_sha256 = a36a3e400d8f3383247c9c58b380b74d9462d3c0484895eaf8a9f55db1aab9aa, ba0a9922e92027291ec5e3f9f8a416ca8766ad69b040d755148753634e5a7eda -2023-03-01 10:09:25, 0, 1, Julia Version: 1.8.4 MEDYANSimRunner Version: 0.2.0 OS: Linux (x86_64-linux-gnu) CPU: 12th Gen Intel(R) Core(TM) i5-1240P WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-13.0.1 (ORCJIT goldmont) Threads: 1 on 16 virtual cores , Xoshiro: 0x14887de98f4d2e9c 0xb905424b754ede30 0xff1c231567e97cc2 0x800211cb407333c4, c084ef1bb6b09582bec372724bba2ec02fd20ae44074439afdc0a38adf73d9ef, 7a431c93ff1b043d3edd29545659db20aaeb999500a6557402fb9f56b112d6be -2023-03-01 10:09:25, 1, 1, Julia Version: 1.8.4 MEDYANSimRunner Version: 0.2.0 OS: Linux (x86_64-linux-gnu) CPU: 12th Gen Intel(R) Core(TM) i5-1240P WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-13.0.1 (ORCJIT goldmont) Threads: 1 on 16 virtual cores , Xoshiro: 0x2d8f2e69ba70c368 0x52911cb79dea8c6e 0x6f02b46154c4525e 0xbdbe8720ea7006a7, 5383a9a8e73da7fc109b1232f6f1f38f61617ecff6086e11882412a26fa43542, 7ddd0b26be8570581cf15633c0ebc057cfee1f176d855550129ef04e887fba1a -2023-03-01 10:09:25, 2, 1, Julia Version: 1.8.4 MEDYANSimRunner Version: 0.2.0 OS: Linux (x86_64-linux-gnu) CPU: 12th Gen Intel(R) Core(TM) i5-1240P WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-13.0.1 (ORCJIT goldmont) Threads: 1 on 16 virtual cores , Xoshiro: 0xc2a0b5fecdea49a1 0x101c86bf735e1d58 0x7be2a1ddf6689136 0x51593de5f372eef3, dc8c6139107cc3860a35a9ce42d8029d87607804db2f71d4acb969ef730063cf, 46ed2968efbaf77d0e8d5801cab58a9d668d0260fb57edef2a24d6583f2d4644 -2023-03-01 10:09:26, 3, 1, Julia Version: 1.8.4 MEDYANSimRunner Version: 0.2.0 OS: Linux (x86_64-linux-gnu) CPU: 12th Gen Intel(R) Core(TM) i5-1240P WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-13.0.1 (ORCJIT goldmont) Threads: 1 on 16 virtual cores , Xoshiro: 0x83e50ea44dc6ba0a 0xa95e929c48dcc5cf 0xb43cf29f0132d897 0x9e756828b76b5005, 1ab6b0565260d1fc9a29c1a96faff39fc08d738070c2e52f6a4ce7b57400beb2, 14842b029a06748132cd2ffabe3abfe1b7aafd8ba6202f91a3670dcd53be6d38 -2023-03-01 10:09:26, 4, 1, Julia Version: 1.8.4 MEDYANSimRunner Version: 0.2.0 OS: Linux (x86_64-linux-gnu) CPU: 12th Gen Intel(R) Core(TM) i5-1240P WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-13.0.1 (ORCJIT goldmont) Threads: 1 on 16 virtual cores , Xoshiro: 0xb4cef410b2712fc0 0x9e876ea70428a752 0x12e16d82c76a629d 0xf2b946e57f569ff6, e4f53455e232c18f3a6b1c0c4a9dfe01b7156c7e1a88dd3feba38e32d43fef29, e1e04c2b648e9d57ed7991bac070b6d97a3f1e3ff15a29c474b4c0510ee6edaa -2023-03-01 10:09:26, 5, 1, Julia Version: 1.8.4 MEDYANSimRunner Version: 0.2.0 OS: Linux (x86_64-linux-gnu) CPU: 12th Gen Intel(R) Core(TM) i5-1240P WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-13.0.1 (ORCJIT goldmont) Threads: 1 on 16 virtual cores , Xoshiro: 0xd8f0dc52c90f1764 0x38a8f7357133ea0f 0x7b6191c33bbf4d5d 0xc7148d87c5084f6f, d7fe6f1b8035eb6cd774031ed69062e89f693ba773b9cdf017405675e5208260, 8089eb90fb053925caeacc11e489c2a1c4e729b952f8ceb509ba75c438a1dd44 -2023-03-01 10:09:26, 6, 1, Julia Version: 1.8.4 MEDYANSimRunner Version: 0.2.0 OS: Linux (x86_64-linux-gnu) CPU: 12th Gen Intel(R) Core(TM) i5-1240P WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-13.0.1 (ORCJIT goldmont) Threads: 1 on 16 virtual cores , Xoshiro: 0x274ca6e07d34b204 0x9b39baa48383b036 0x4dfbaff626ae5a39 0x74ac1ff78f565687, a9dbd17e791327d0a4a6cbd9143a58f354d50db53500bd838a46eb6f97a37f8c, def1f47c3cbba3e611399ae53d773031e182139bf6580d58253d94d78c47efb1 -2023-03-01 10:09:26, 7, 1, Julia Version: 1.8.4 MEDYANSimRunner Version: 0.2.0 OS: Linux (x86_64-linux-gnu) CPU: 12th Gen Intel(R) Core(TM) i5-1240P WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-13.0.1 (ORCJIT goldmont) Threads: 1 on 16 virtual cores , Xoshiro: 0xc8d903b371e154b5 0xf18eb3b2d819580b 0x1ffe0e113bf6e83d 0xbcd63df2b4aa619a, 6c7bf3434068b2954b082a68c79a864c2a2a0645bd276aee25820c7da1af2cbe, fed5e9e3a7d151844ee946ae6b38d047ea0faba9d3587d89118bcaddf9df587a -2023-03-01 10:09:27, 8, 1, Julia Version: 1.8.4 MEDYANSimRunner Version: 0.2.0 OS: Linux (x86_64-linux-gnu) CPU: 12th Gen Intel(R) Core(TM) i5-1240P WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-13.0.1 (ORCJIT goldmont) Threads: 1 on 16 virtual cores , Xoshiro: 0x85818df31d526d24 0x26a9be10920ee483 0xb042bd90fa01bc88 0x673229ab11c80d96, cc621041faa3b739e36c3f9f0b73473cc82e961458563cf47aaad57d516a3682, be1d176ef6c4e8552b1778d793c82eb4a7ce346ba183b144db652b40310a4050 -2023-03-01 10:09:27, 9, 1, Julia Version: 1.8.4 MEDYANSimRunner Version: 0.2.0 OS: Linux (x86_64-linux-gnu) CPU: 12th Gen Intel(R) Core(TM) i5-1240P WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-13.0.1 (ORCJIT goldmont) Threads: 1 on 16 virtual cores , Xoshiro: 0xc41a1a489e948431 0x136a8e73755d352f 0x49e2147e2e55d1ac 0xdd22a83372f77078, 22b12f8bfceb9e9dc5248574581a041d687a54d6e60c677dcf147e2e895f1c6e, cf86b45e2a0b9658947d8a37ad20acd6ad7372e83fc04d0b176b5744a58200f3 -2023-03-01 10:09:27, 10, 1, Julia Version: 1.8.4 MEDYANSimRunner Version: 0.2.0 OS: Linux (x86_64-linux-gnu) CPU: 12th Gen Intel(R) Core(TM) i5-1240P WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-13.0.1 (ORCJIT goldmont) Threads: 1 on 16 virtual cores , Xoshiro: 0x0a523c08993ec166 0x9e928045c59c60b2 0x911ee48cda9f559d 0x48aaf9c904c800f5, 8d2c87ccd26f55a42e24fac3d1787c20e85f12ff497958fb4435941a3c7d1fd1, db6e015763dbec8afedf553ccfdfc6da515a66540dd52d63fd7cf684a5a3900e -2023-03-01 10:09:27, 11, 1, Julia Version: 1.8.4 MEDYANSimRunner Version: 0.2.0 OS: Linux (x86_64-linux-gnu) CPU: 12th Gen Intel(R) Core(TM) i5-1240P WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-13.0.1 (ORCJIT goldmont) Threads: 1 on 16 virtual cores , Xoshiro: 0xdc6a4584586aa121 0x05de58c1863df449 0x9bc753bc82c594fb 0x8c08fac70f31982a, d8d671e83dad463a723371e80306a5005d20dd3edac92272cdf095e600ca9754, 9eaa40943cec4bfb3824399911d14f95aeeff85056150cace35c96a841a0b121 -Done, 11a6767d5674c7e45f7e00dc525762275b3a48491ad6045427d2609cc496c516 diff --git a/test/examples/good/output-ref/1/snapshots/snapshot0.zarr.zip b/test/examples/good/output-ref/1/snapshots/snapshot0.zarr.zip deleted file mode 100644 index 9edc8a75a579175703388dd5856265084cb088cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmZ`$J5Iwu5RGF9kqZ>09Kae%tYFMX$fBlkV;UNSqQzcgE9;MT?MQL(6>Ii*_1H`C)=6O}jp(dPZ|Inr}@VtFtTNln4EEeIH$QU>lNx*1841Xf>w1xbi5 zz7+umBCeu{Ve+g|MZx^)OI#Sf32exUA1GicU8ONZ&fd?N-e|p_Iom~<9Wk- HvD)YlhOB&j diff --git a/test/examples/good/output-ref/1/snapshots/snapshot1.zarr.zip b/test/examples/good/output-ref/1/snapshots/snapshot1.zarr.zip deleted file mode 100644 index 5c9b99b01a75222074bd034a7a13945579d4e7ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmZ`$%}T>S5N=wnB?qa{2MFUe)EIw|;Mq$q@#00Ktm(#tZhqL!7MirL;3IhTA-wnM zBX|*?!`ZqL6L7-E%zU%oWS9y3!QP~&%)TXeE{hr}!uc%9c}hy~ z+)dC@eXKvX7QR*(Aw)?^Do&ifaW)!bu3#YygtGpR>$SUV-;UqEu%d8?&|RkL*JM?j z78-=H%oYMX>)w5^HI=6Yr$Xc$=^7qc9xg;uQSclaLPnRAfp>vnMp7h!Rp($sGRF|# ziVzbK%P3-4Jl*J~^Kw;SZozeO1)7Tz#%4>xq$*^Rv4$@xS3~_Fb3UhPAh`LJiHU8Xy*Takb-JSNIU3PrvsS08>aM?0ts9j-M&R!cRegZpEBfc% GWxoN}c72Ng diff --git a/test/examples/good/output-ref/1/snapshots/snapshot10.zarr.zip b/test/examples/good/output-ref/1/snapshots/snapshot10.zarr.zip deleted file mode 100644 index fa0dcffb4f24455de5af3a04cb0ee47e61838139..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmZ`$%}#?r5QbK3!@<;~4?xB%R>a>B&tACT#fve`3X6~~U9t;JO4|qU;v;zV6`FYW z>Kl0UIh@rsSQ3YPnVE0)8zwVh(A^pu$_}o{&7k*O?P092gBaBhf>*@?Y9hoq;UXs` z_}(hqP@U)NwT-V8`UqK)lFCE3V;=Vhm@C+bM})GEA7^ZKd3)V||HO*IE<$%1sb7=% z!m`mIN~`Haf^Xk?caElbUI;2B7f3JQp7CfRvzmhMIuJ2BqYV6045uVV5?Fl#4rDQg z_*O=k$h1llhQ*_mZW@>K0&^QKvkTB%k})=(5mu?fv|_B`ODfb*e@HH3ss@6qU%5=l zcgtq-jiuV2wX(v%>zc$(#Hsc?VhT4@q0o4 HWUA~78Q6Uj diff --git a/test/examples/good/output-ref/1/snapshots/snapshot11.zarr.zip b/test/examples/good/output-ref/1/snapshots/snapshot11.zarr.zip deleted file mode 100644 index 03d2e17f953ec1ccedc855be93a4a34734679e98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmWIWW@Zs#00G^=#4rK2d8L9tHVCr=v0hbrQGRJbwNh1LQBizaeo<~>iISC3Z2*d{ zO`YGY6@aQim;;E5OA<>`i}ekd7>zjufP4^E0OET<3<4R95E|8#W4o2lKLbjDFdq;@ zP0<5comg4Tr2qs<$r+`2*~Lm$3eiTfI#8kf+=8Oi;^O=wpm4QPQciwyc5!A^s*;s~ zj#6??YFTQIl9eftnU|Og6jaKoGEve|%1i+=fohABft=!u(zLW3prm0f)Xn*zXg4x diff --git a/test/examples/good/output-ref/1/snapshots/snapshot2.zarr.zip b/test/examples/good/output-ref/1/snapshots/snapshot2.zarr.zip deleted file mode 100644 index f4aacd9b3e6508cbc39fdd870fe2fe3bdd5f1400..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmZ`$%}T>S5N_I9OAb<@4-m$a5NfPHNbn|j$t7OAh?F(i*w9Tj>?VaK<`sMduY#}O z$%|Lt!sl>SS7HKA_?Ve*_L~edzBd?7d&=x9ay=P66}uQKY$Imnz2HSwK}m#IL|h~! z2X|ikE!E50#lD5F6~+jelbp%}r*9mMCzvZ(h(1Eu@W=JqUEW{!K0dLcu!GQDX6n~u zQ<)YT1j`~_NpP*3`K_%fo@9bb$pz9C+%X=kWL#2k9UB5h*OY;Kf?-M$B!QL3U_%^Y zh;L!xA8$uPIzJU#=>MKNQGHDQG+qy=LQpHrcR`a^ONQZ*1<{>tT& ze79^SZ!OiDo3-hCMt|D-{~mgA+Ue_bMcs2WqSI!rPFK}kf1`S6RQedcw>47r0e;Wu I9}Sg#0r0GQG5`Po diff --git a/test/examples/good/output-ref/1/snapshots/snapshot3.zarr.zip b/test/examples/good/output-ref/1/snapshots/snapshot3.zarr.zip deleted file mode 100644 index fc96826bf9c469067ae8aabf3135fe5a59d364f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmZ`$%}&BV5T<}gI2cTLfX#Sq5b-Cb@$99SdhuciS6%<^lag?bU z&%qn6gFV&T`{;+^YeE~LbDj%*I;cD6?JmXyhG-(ls504pmCuipuWw`tM+jS`Pha!p zs>9GAj7>V#;IW6{$hA~9n=7GprjTC2qs+poP6`3uz=cqXg^=J~qMPy=k|2uVh8S$6{8srPFC?*qrOV|F`LQFR@q;tJjEa;mE8_)FDusj|xm{DT(d8+cyv I-Z&Kf0t-NV;{X5v diff --git a/test/examples/good/output-ref/1/snapshots/snapshot4.zarr.zip b/test/examples/good/output-ref/1/snapshots/snapshot4.zarr.zip deleted file mode 100644 index f2ec3a0880b4c5dd7ebbe436398698edb9d75a5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmZ`$%}T>S5N=v+YY?f>2MFV}));?~;Mq$r@#00Ktm(#tZn|YRDKu%pC-4!x`Vii| z`WC*1&ge>PzzH8S^UZ#fVI~;0w)zdS!)tcaYd`1v7!x{(Vez13J};qQN{u6_QkH@5 zt%D8KPUrK{!PkT?LT4=F`p9isC*2%sd6RtS3t(`87%X3Nrc z&>)QS*;Iq?+zIAF ztR~-Bs^i&fI~ZBbe&hdpn8g`qu2UD)&rylatF^jyRek-HYE`MsF@n){o9Zq6UeG^T GlzjnN1$wmr diff --git a/test/examples/good/output-ref/1/snapshots/snapshot5.zarr.zip b/test/examples/good/output-ref/1/snapshots/snapshot5.zarr.zip deleted file mode 100644 index e69907cf8374c1b0ab68866b645b7f5e3e3f79af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmZ`$%}#?r5QbK3LyXqg2O#6MR-|eS@$7{QUc4C7taK65b4 zeG8w%SzUvWIONOBe6!y$nep8I##obfctx&9gXdxoV~H)qw7lnOkyTI<&gVhOW0Heo z*Ir9C+kJa9@wLPdA###aab)%M*>Hrpgo)TjNZbE$TD!~p>%qq-RwQ;1s>@XVnyf3s zL<2u8lBEF0ys>X5ib~^*Qz250bOm=T^_Lyd`4H4fpdysLSiI=l_xNPXn`TV z6+R{+EP{Yx@wiq^7x_BF+=Pqh926HsjLlbs6|#^Nj5T~txg6>bneqjd1Ht95T!iGi zWixqesitkzhUe-qHJh=dCzIUtSh$H~_>W;6~ErpieFny#xJ zL4$B!WD5m8c^E$q4JFd0WJ(E%bOldbgbS6F418}0A!jSb!M{Q`qbZWW%S#wS5}}K4 zRfvI_7jeund0v~MQNCVcOyDM&fZ^hV^Vy2>LMO6ZbmXLPktC1h>C(HK&`B zw)3r_l5wx@1yi>*aQ@$i8JxMycABE*I;`m0j@4_bn&Y>s7ppSM2&TIoop0m!j`ypp FqaTj$cx3

S5KdaHB?oDt4-m#{tTk4V(6g6Z;>C+dS(A+k-Tbhd6q?vq@DaTF5Z(nr z@GX1}XJt#8fD^vmnQ!)+3^ShFKbUCJPH)NGXz*H&(U&+vOsglJm3a*n;d~ipJRt=* z_SS2uKEK8b6JJXV5uzXk6=$=aelZ+jEMY3(rOoAm)~@pLe)9c;ti&-wRhi0PlTB@y zXy8X>x)R`+_x6LOs4U4j6(ZwE*YLyHGDz2Y-*3pctB-CaI=$(i2Rjo zI&Tftw2iIdxq5G+{l5R*9cgyLjy5nzD&y7kg!*dS@GT+DV4eu9S FM!%$Hdz=6O diff --git a/test/examples/good/output-ref/1/snapshots/snapshot8.zarr.zip b/test/examples/good/output-ref/1/snapshots/snapshot8.zarr.zip deleted file mode 100644 index e5da92770a8a059c4d0c1e46d39489e795b52bfa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmZ`$u};H43~gE{RR&a0XB3$=lu!yn5p$O;VSojp>PRjm>a|zhC6$`cU*Hp1`3QDa z{(?VX#|MXwM8eUN?dN-rWrsm$YtT}5bVY9Z-REKtV}%{WuzV1_SXNLHA*KlzbCQEU z+JrUL=fPy(!Pg2sgv?1!<+0bcPJ4aK6&%DEq3rs{wd!5oUJu_tu%fVw&|QY=*JNGU z4jM#hv6xBlo!il!t0_KT3MwTRNLO&rcr=q)Nx}DAh!|Z_2L2g_3o=I%SUCn4vKT{r zD;>kugjq~*qa|bT73(#DWF*aQhR;a?FV9fA26>6wIBo{GN1Ht51E>rT| z(o9}ksxz`Tb{JUgLF@l}=*4NLuhSGY&tXL8X02XR)m*<(JsOohMi^{&RlS4X3;HKZ FWnbR)dgA~9 diff --git a/test/examples/good/output-ref/1/snapshots/snapshot9.zarr.zip b/test/examples/good/output-ref/1/snapshots/snapshot9.zarr.zip deleted file mode 100644 index 02ec0a3f51704b60ccd1f5160ccb9bda91f8e3c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmZ`$y-veG3{F}oRR>h0K0svFP(t_-ikMllgaH "1.0.0", - "model_name" => "fibonacci sequence" - ]) - state = [0, 1] - header, state -end - -function save_snapshot(step::Int, state; kwargs...)::ZGroup - @info "saving states" state - group = ZGroup() - group["states"] = state - group -end - -function load_snapshot(step::Int, group, state; kwargs...) - state .= collect(group["states"]) - state -end - -function done(step::Int, state; kwargs...) - step > 10, 11 -end - -function loop(step::Int, state; kwargs...) - a = sum(state) + rand(0:1) - state[1] = state[2] - state[2] = a - state -end diff --git a/test/file-saving-stresstest.jl b/test/file-saving-stresstest.jl new file mode 100644 index 0000000..01a1750 --- /dev/null +++ b/test/file-saving-stresstest.jl @@ -0,0 +1,65 @@ +using Base: _UVError + +using Base: SIGKILL + +using Distributed + +@everywhere begin + using MEDYANSimRunner +end + +out_dir = mktempdir() +@show readdir(out_dir) +out_file = joinpath(out_dir, "foo") + +worker = workers()[end] +ospid = remotecall_fetch(getpid, worker) + +data1 = remotecall(ones, worker, UInt8, 1<<20) + +remotecall_fetch(worker, data1) do data + MEDYANSimRunner.write_traj_file(out_dir, "foo", fetch(data)) +end + +fetch(data1) + +N = 1<<20 +data2 = remotecall(ones, worker, UInt8, N) + +r = remotecall(worker, data2) do data + MEDYANSimRunner.write_traj_file(out_dir, "foo", fetch(data)) +end + +fetch(r) + +N = 1<<30 +data3 = remotecall(ones, worker, UInt8, N+1) + +remotecall_fetch(x->length(fetch(x)), worker, data3) + +r = remotecall(worker, data3) do data + MEDYANSimRunner.write_traj_file(out_dir, "foo", fetch(data)) +end + +# rc = ccall(:uv_kill, Cint, (Cint, Cint), ospid, SIGKILL) +# rc == 0 || throw(_UVError("kill", rc)) +# try +# rmprocs(worker) +# catch e +# if e isa Base.IOError +# #Windows creates an IOError here for some reason +# else +# rethrow() +# end +# end + +@show length(read(out_file)) +for i in 1:10 + sleep(0.5) + @show readdir(out_dir) +end +@show length(read(out_file)) + +fetch(r) + +@show length(read(out_file)) \ No newline at end of file diff --git a/test/list-examples/list done.txt b/test/list-examples/list done.txt deleted file mode 100644 index 41e0b62..0000000 --- a/test/list-examples/list done.txt +++ /dev/null @@ -1,7 +0,0 @@ -version = 1, job_idx = 2, input_tree_hash = f4fde7178433b85c216b15d4678cacfa8650b7289cb2bd8f0ce05c0041924473, ad323c37e61c3e48798cb2dc59558f493eabf20e7107ba5efae070e19a622fd2 -header_sha256 = a36a3e400d8f3383247c9c58b380b74d9462d3c0484895eaf8a9f55db1aab9aa, ba0a9922e92027291ec5e3f9f8a416ca8766ad69b040d755148753634e5a7eda -2022-11-06 17:30:03, 0, 1, Long Julia Version String, Xoshiro: 0xfff0241072ddab67 0xc53bc12f4c3f0b4e 0x56d451780b2dd4ba 0x50a4aa153d208dd8, 6575c62733b459ec9ee5f7308229a090f1116f0164ab8ef5713e71b9d58dd220, e804a3dceeedc43b8155593bef78e767289c84f1743444cb794d076287727d09 -2022-11-06 17:30:03, 1, 1, Long Julia Version String, Xoshiro: 0xfff0241072ddab67 0xc53bc12f4c3f0b4e 0x56d451780b2dd4ba 0x50a4aa153d208dd8, 6f52d44936bef59e543126cbd610a09538f5b5aeb568d616a99750240002b000, 6437bdfa2aaacd3386abe8c238290742a77a91d63b5ba8f8ade2c687e0eb178b -2022-11-06 17:30:03, 2, 1, Long Julia Version String, Xoshiro: 0x2007f3c789b9526d 0x085f596501ec49e6 0x8048aeb561cb1458 0xf95c00361c064979, 7c02a201a9f2326c924f961b479ca6570493cb6f15fc173a332c30fa6556c34a, 59325667128e200f256931a189ebdb456c40f271102277ab8a73f1fce143b0d9 -2022-11-06 17:30:04, 3, 1, Long Julia Version String, Xoshiro: 0x0d78c6c5f2400a9c 0xcf341e2315f17db6 0x01ceb25a2560ce54 0x609e89462d6ec715, 4edb8ed71bf2126da7900a58923f96b181a4b4eb9c001da824a9b1a059eb5f9c, 7fb84ac989357df9d03aea7eb72cda4fb55b18bed8c647d00de679acc5ac65b2 -Done, 11a6767d5674c7e45f7e00dc525762275b3a48491ad6045427d2609cc496c516 \ No newline at end of file diff --git a/test/list-examples/list empty.txt b/test/list-examples/list empty.txt deleted file mode 100644 index e69de29..0000000 diff --git a/test/list-examples/list partial clean.txt b/test/list-examples/list partial clean.txt deleted file mode 100644 index ec1d0ca..0000000 --- a/test/list-examples/list partial clean.txt +++ /dev/null @@ -1,7 +0,0 @@ -version = 1, job_idx = 2, input_tree_hash = f4fde7178433b85c216b15d4678cacfa8650b7289cb2bd8f0ce05c0041924473, ad323c37e61c3e48798cb2dc59558f493eabf20e7107ba5efae070e19a622fd2 -header_sha256 = a36a3e400d8f3383247c9c58b380b74d9462d3c0484895eaf8a9f55db1aab9aa, ba0a9922e92027291ec5e3f9f8a416ca8766ad69b040d755148753634e5a7eda -2022-11-06 17:30:03, 0, 1, Long Julia Version String, Xoshiro: 0xfff0241072ddab67 0xc53bc12f4c3f0b4e 0x56d451780b2dd4ba 0x50a4aa153d208dd8, 6575c62733b459ec9ee5f7308229a090f1116f0164ab8ef5713e71b9d58dd220, e804a3dceeedc43b8155593bef78e767289c84f1743444cb794d076287727d09 -2022-11-06 17:30:03, 1, 1, Long Julia Version String, Xoshiro: 0xfff0241072ddab67 0xc53bc12f4c3f0b4e 0x56d451780b2dd4ba 0x50a4aa153d208dd8, 6f52d44936bef59e543126cbd610a09538f5b5aeb568d616a99750240002b000, 6437bdfa2aaacd3386abe8c238290742a77a91d63b5ba8f8ade2c687e0eb178b -2022-11-06 17:30:03, 2, 1, Long Julia Version String, Xoshiro: 0x2007f3c789b9526d 0x085f596501ec49e6 0x8048aeb561cb1458 0xf95c00361c064979, 7c02a201a9f2326c924f961b479ca6570493cb6f15fc173a332c30fa6556c34a, 59325667128e200f256931a189ebdb456c40f271102277ab8a73f1fce143b0d9 -2022-11-06 17:30:04, 3, 1, Long Julia Version String, Xoshiro: 0x0d78c6c5f2400a9c 0xcf341e2315f17db6 0x01ceb25a2560ce54 0x609e89462d6ec715, 4edb8ed71bf2126da7900a58923f96b181a4b4eb9c001da824a9b1a059eb5f9c, 7fb84ac989357df9d03aea7eb72cda4fb55b18bed8c647d00de679acc5ac65b2 - diff --git a/test/list-examples/list partial too small.txt b/test/list-examples/list partial too small.txt deleted file mode 100644 index e6d0510..0000000 --- a/test/list-examples/list partial too small.txt +++ /dev/null @@ -1,5 +0,0 @@ -version = 1, job_idx = 2, input_tree_hash = f4fde7178433b85c216b15d4678cacfa8650b7289cb2bd8f0ce05c0041924473, ad323c37e61c3e48798cb2dc59558f493eabf20e7107ba5efae070e19a622fd2 -header_sha256 = a36a3e400d8f3383247c9c58b380b74d9462d3c0484895eaf8a9f55db1aab9aa, ba0a9922e92027291ec5e3f9f8a416ca8766ad69b040d755148753634e5a7eda -2022-11-06 17:30:03, 0, 1, Long Julia Version String, Xoshiro: 0xfff0241072ddab67 0xc53bc12f4c3f0b4e 0x56d451780b2dd4ba 0x50a4aa153d208dd8, 6575c62733b459ec9ee5f7308229a090f1116f0164ab8ef5713e71b9d58dd220, e804a3dceeedc43b8155593bef78e767289c84f1743444cb794d076287727d09 -2022-11-06 17:30:03, 1, 1, Long Julia Version String, Xoshiro: 0xfff024107 - diff --git a/test/list-examples/list partial.txt b/test/list-examples/list partial.txt deleted file mode 100644 index d501c2d..0000000 --- a/test/list-examples/list partial.txt +++ /dev/null @@ -1,8 +0,0 @@ -version = 1, job_idx = 2, input_tree_hash = f4fde7178433b85c216b15d4678cacfa8650b7289cb2bd8f0ce05c0041924473, ad323c37e61c3e48798cb2dc59558f493eabf20e7107ba5efae070e19a622fd2 -header_sha256 = a36a3e400d8f3383247c9c58b380b74d9462d3c0484895eaf8a9f55db1aab9aa, ba0a9922e92027291ec5e3f9f8a416ca8766ad69b040d755148753634e5a7eda -2022-11-06 17:30:03, 0, 1, Long Julia Version String, Xoshiro: 0xfff0241072ddab67 0xc53bc12f4c3f0b4e 0x56d451780b2dd4ba 0x50a4aa153d208dd8, 6575c62733b459ec9ee5f7308229a090f1116f0164ab8ef5713e71b9d58dd220, e804a3dceeedc43b8155593bef78e767289c84f1743444cb794d076287727d09 -2022-11-06 17:30:03, 1, 1, Long Julia Version String, Xoshiro: 0xfff0241072ddab67 0xc53bc12f4c3f0b4e 0x56d451780b2dd4ba 0x50a4aa153d208dd8, 6f52d44936bef59e543126cbd610a09538f5b5aeb568d616a99750240002b000, 6437bdfa2aaacd3386abe8c238290742a77a91d63b5ba8f8ade2c687e0eb178b -2022-11-06 17:30:03, 2, 1, Long Julia Version String, Xoshiro: 0x2007f3c789b9526d 0x085f596501ec49e6 0x8048aeb561cb1458 0xf95c00361c064979, 7c02a201a9f2326c924f961b479ca6570493cb6f15fc173a332c30fa6556c34a, 59325667128e200f256931a189ebdb456c40f271102277ab8a73f1fce143b0d9 -2022-11-06 17:30:04, 3, 1, Long Julia Version String, Xoshiro: 0x0d78c6c5f2400a9c 0xcf341e2315f17db6 0x01ceb25a2560ce54 0x609e89462d6ec715, 4edb8ed71bf2126da7900a58923f96b181a4b4eb9c001da824a9b1a059eb5f9c, 7fb84ac989357df9d03aea7eb72cda4fb55b18bed8c647d00de679acc5a - - diff --git a/test/list-examples/list start error.txt b/test/list-examples/list start error.txt deleted file mode 100644 index 48486ac..0000000 --- a/test/list-examples/list start error.txt +++ /dev/null @@ -1,2 +0,0 @@ -version = 1, job_idx = 1, input_tree_hash = 8eaa2ae599032df7a3322615aa7fe75c2d4488f8a7b547954f1899b8553b94a4, 301c7b6b8ca812c18eeb2fee105cee39499f5a0b8ffbdd805a15a4ae628c4520 -Error starting job, e2c221baebced44c7302197363c1861eede8ad4b44c0c6d96eff9f01f1bc763d diff --git a/test/potential-errors.md b/test/potential-errors.md deleted file mode 100644 index 6f280e9..0000000 --- a/test/potential-errors.md +++ /dev/null @@ -1,38 +0,0 @@ -- bad-manifest -- clean-partial -- corrupted-init-output -- corrupted-snapshot -- done-error -- done-timeout -- first-loop-timeout -- first-save-timeout -- load-error -- load-timeout -- loop-error -- loop-timeout -- max-steps -- missing-hdf5 -- missing-job -- missing-json3 -- missing-main -- missing-manifest -- missing-project -- partially-started -- save-error -- save-timeout -- setup-error -- setup-timeout -- startup-error -- startup-timeout -- too-big-snapshot - -The following states can by detected by statically analyzing the input and output directories. -- clean-partial : retry -- corrupted-init-output : retry -- corrupted-snapshot : retry -- missing-input-file : exit -- input-changed : exit -- partially-started : retry -- ended in success : exit -- ended in error : exit -- clean not started : retry diff --git a/test/runtests.jl b/test/runtests.jl index 882c255..694ecbb 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,3 @@ -include("test_normalize_job_idx.jl") -include("test_timeout.jl") -include("test_listparse.jl") +include("test_arg-parsing.jl") +include("test_file-saving.jl") include("test_ref_out.jl") \ No newline at end of file diff --git a/test/test_arg-parsing.jl b/test/test_arg-parsing.jl new file mode 100644 index 0000000..67b9a3c --- /dev/null +++ b/test/test_arg-parsing.jl @@ -0,0 +1,48 @@ +using Test +using Logging +using MEDYANSimRunner + + +@testset "arg parsing" begin + @test MEDYANSimRunner.parse_cli_args( + String[], ["job1", "job2"] + ) == MEDYANSimRunner.CLIOptions(; + continue_sim=false, + batch=-1, + out_dir=".", + ) + @test MEDYANSimRunner.parse_cli_args( + String["--continue"], ["job1", "job2"] + ) == MEDYANSimRunner.CLIOptions(; + continue_sim=true, + batch=-1, + out_dir=".", + ) + @test MEDYANSimRunner.parse_cli_args( + String["--out=/home/nathan/sim1"], ["job1", "job2"] + ) == MEDYANSimRunner.CLIOptions(; + continue_sim=false, + batch=-1, + out_dir="/home/nathan/sim1", + ) + @test MEDYANSimRunner.parse_cli_args( + String["--batch=-1"], ["job1", "job2"] + ) == MEDYANSimRunner.CLIOptions(; + continue_sim=false, + batch=-1, + out_dir=".", + ) + @test (@test_logs (:error, "--batch must be -1 or in 1:2, instead got 3") MEDYANSimRunner.parse_cli_args( + String["--batch=3"], ["job1", "job2"] + )) === nothing + @test (@test_logs (:error, "--batch must be a integer, instead got \"dfgdf\"") MEDYANSimRunner.parse_cli_args( + String["--batch=dfgdf"], ["job1", "job2"] + )) === nothing + @test (@test_logs (:warn, "not all ARGS used") MEDYANSimRunner.parse_cli_args( + String["--bach=dfgdf"], ["job1", "job2"] + )) == MEDYANSimRunner.CLIOptions(; + continue_sim=false, + batch=-1, + out_dir=".", + ) +end \ No newline at end of file diff --git a/test/test_file-saving.jl b/test/test_file-saving.jl new file mode 100644 index 0000000..817e839 --- /dev/null +++ b/test/test_file-saving.jl @@ -0,0 +1,65 @@ +using Test +using MEDYANSimRunner + +@testset "write_traj_file unit tests" begin + # write empty file + mktempdir() do path + MEDYANSimRunner.write_traj_file(path, "empty.txt", b"") + @test read(joinpath(path, "empty.txt")) == UInt8[] + @test readdir(path) == ["empty.txt"] + end + # write file with no existing file + mktempdir() do path + MEDYANSimRunner.write_traj_file(path, "snap1.txt", [0x01, 0x02]) + @test read(joinpath(path, "snap1.txt")) == [0x01, 0x02] + @test readdir(path) == ["snap1.txt"] + end + # write file with existing same file + mktempdir() do path + MEDYANSimRunner.write_traj_file(path, "snap1.txt", [0x01, 0x02]) + MEDYANSimRunner.write_traj_file(path, "snap1.txt", [0x01, 0x02]) + @test read(joinpath(path, "snap1.txt")) == [0x01, 0x02] + @test readdir(path) == ["snap1.txt"] + end + # write file with existing different file + mktempdir() do path + MEDYANSimRunner.write_traj_file(path, "snap1.txt", [0x02, 0x02]) + MEDYANSimRunner.write_traj_file(path, "snap1.txt", [0x01, 0x02]) + @test read(joinpath(path, "snap1.txt")) == [0x01, 0x02] + @test readdir(path) == ["snap1.txt"] + end + # write file with existing dir + mktempdir() do path + mkdir(joinpath(path, "snap1.txt")) + @test_throws ErrorException MEDYANSimRunner.write_traj_file(path, "snap1.txt", [0x01, 0x02]) + @test isdir(joinpath(path, "snap1.txt")) + @test readdir(path) == ["snap1.txt"] + end + # write file with existing open file with same content + mktempdir() do path + MEDYANSimRunner.write_traj_file(path, "snap1.txt", [0x01, 0x02]) + open(joinpath(path, "snap1.txt")) do f + MEDYANSimRunner.write_traj_file(path, "snap1.txt", [0x01, 0x02]) + @test read(joinpath(path, "snap1.txt")) == [0x01, 0x02] + @test read(f) == [0x01, 0x02] + @test readdir(path) == ["snap1.txt"] + end + end + # write file with existing open file with different content + # this errors safely on windows + mktempdir() do path + MEDYANSimRunner.write_traj_file(path, "snap1.txt", [0x02, 0x02]) + open(joinpath(path, "snap1.txt")) do f + if Sys.iswindows() + @test_throws ErrorException MEDYANSimRunner.write_traj_file(path, "snap1.txt", [0x01, 0x02]) + @test read(joinpath(path, "snap1.txt")) == [0x02, 0x02] + @test read(f) == [0x02, 0x02] + else + MEDYANSimRunner.write_traj_file(path, "snap1.txt", [0x01, 0x02]) + @test read(joinpath(path, "snap1.txt")) == [0x01, 0x02] + @test read(f) == [0x02, 0x02] + end + @test readdir(path) == ["snap1.txt"] + end + end +end \ No newline at end of file diff --git a/test/test_listparse.jl b/test/test_listparse.jl deleted file mode 100644 index 91e8fb5..0000000 --- a/test/test_listparse.jl +++ /dev/null @@ -1,142 +0,0 @@ -using Test - -using MEDYANSimRunner -using Dates -using Random - - -@testset "list.txt doesn't exist" begin - list_info, _ = MEDYANSimRunner.parse_list_file("not a file") - @test list_info.isempty - list_info, _ = MEDYANSimRunner.parse_list_file("not a file";ignore_error=true) - @test list_info.isempty -end -@testset "list.txt is empty" begin - list_info, _ = MEDYANSimRunner.parse_list_file("list-examples/list empty.txt") - @test list_info.isempty - list_info, _ = MEDYANSimRunner.parse_list_file("list-examples/list empty.txt";ignore_error=true) - @test list_info.isempty -end -@testset "list.txt partial too small" begin - list_info, _ = MEDYANSimRunner.parse_list_file("list-examples/list partial too small.txt") - @test list_info.isempty - list_info, _ = MEDYANSimRunner.parse_list_file("list-examples/list partial too small.txt";ignore_error=true) - @test list_info.isempty -end -@testset "list.txt startup error" begin - list_info, good_rawlines = MEDYANSimRunner.parse_list_file("list-examples/list start error.txt") - @test !(list_info.isempty) - @test list_info.job_idx == "1" - @test list_info.input_tree_hash == hex2bytes("8eaa2ae599032df7a3322615aa7fe75c2d4488f8a7b547954f1899b8553b94a4") - @test list_info.header_sha256 == [] - @test list_info.snapshot_infos == [] - @test list_info.final_message == "Error starting job" - @test length(good_rawlines) == 2 - list_info, good_rawlines = MEDYANSimRunner.parse_list_file("list-examples/list start error.txt";ignore_error=true) - @test list_info.isempty - @test isempty(good_rawlines) -end -@testset "list.txt partial run clean lines" begin - for ignore_error in (false,true) - list_info, _ = MEDYANSimRunner.parse_list_file("list-examples/list partial clean.txt";ignore_error) - @test !(list_info.isempty) - @test list_info.job_idx == "2" - @test list_info.input_tree_hash == hex2bytes("f4fde7178433b85c216b15d4678cacfa8650b7289cb2bd8f0ce05c0041924473") - @test list_info.header_sha256 == hex2bytes("a36a3e400d8f3383247c9c58b380b74d9462d3c0484895eaf8a9f55db1aab9aa") - @test length(list_info.snapshot_infos) == 4 - - @test list_info.snapshot_infos[1].time_stamp == DateTime("2022-11-06T17:30:03") - @test list_info.snapshot_infos[1].step_number == 0 - @test list_info.snapshot_infos[1].nthreads == 1 - @test list_info.snapshot_infos[1].julia_versioninfo == "Long Julia Version String" - @test list_info.snapshot_infos[1].rngstate == Xoshiro(0xfff0241072ddab67, 0xc53bc12f4c3f0b4e, 0x56d451780b2dd4ba, 0x50a4aa153d208dd8) - @test list_info.snapshot_infos[1].snapshot_sha256 == hex2bytes("6575c62733b459ec9ee5f7308229a090f1116f0164ab8ef5713e71b9d58dd220") - - @test list_info.snapshot_infos[2].time_stamp == DateTime("2022-11-06T17:30:03") - @test list_info.snapshot_infos[2].step_number == 1 - @test list_info.snapshot_infos[2].nthreads == 1 - @test list_info.snapshot_infos[2].julia_versioninfo == "Long Julia Version String" - @test list_info.snapshot_infos[2].rngstate == Xoshiro(0xfff0241072ddab67, 0xc53bc12f4c3f0b4e, 0x56d451780b2dd4ba, 0x50a4aa153d208dd8) - @test list_info.snapshot_infos[2].snapshot_sha256 == hex2bytes("6f52d44936bef59e543126cbd610a09538f5b5aeb568d616a99750240002b000") - - @test list_info.snapshot_infos[4].time_stamp == DateTime("2022-11-06T17:30:04") - @test list_info.snapshot_infos[4].step_number == 3 - @test list_info.snapshot_infos[4].nthreads == 1 - @test list_info.snapshot_infos[4].julia_versioninfo == "Long Julia Version String" - @test list_info.snapshot_infos[4].rngstate == Xoshiro(0x0d78c6c5f2400a9c, 0xcf341e2315f17db6, 0x01ceb25a2560ce54, 0x609e89462d6ec715) - @test list_info.snapshot_infos[4].snapshot_sha256 == hex2bytes("4edb8ed71bf2126da7900a58923f96b181a4b4eb9c001da824a9b1a059eb5f9c") - - @test list_info.final_message == "" - end -end -@testset "list.txt partial run not clean lines" begin - list_info, _ = MEDYANSimRunner.parse_list_file("list-examples/list partial.txt") - @test !(list_info.isempty) - @test list_info.job_idx == "2" - @test list_info.input_tree_hash == hex2bytes("f4fde7178433b85c216b15d4678cacfa8650b7289cb2bd8f0ce05c0041924473") - @test list_info.header_sha256 == hex2bytes("a36a3e400d8f3383247c9c58b380b74d9462d3c0484895eaf8a9f55db1aab9aa") - @test length(list_info.snapshot_infos) == 3 - - @test list_info.snapshot_infos[1].time_stamp == DateTime("2022-11-06T17:30:03") - @test list_info.snapshot_infos[1].step_number == 0 - @test list_info.snapshot_infos[1].nthreads == 1 - @test list_info.snapshot_infos[1].julia_versioninfo == "Long Julia Version String" - @test list_info.snapshot_infos[1].rngstate == Xoshiro(0xfff0241072ddab67, 0xc53bc12f4c3f0b4e, 0x56d451780b2dd4ba, 0x50a4aa153d208dd8) - @test list_info.snapshot_infos[1].snapshot_sha256 == hex2bytes("6575c62733b459ec9ee5f7308229a090f1116f0164ab8ef5713e71b9d58dd220") - - @test list_info.snapshot_infos[2].time_stamp == DateTime("2022-11-06T17:30:03") - @test list_info.snapshot_infos[2].step_number == 1 - @test list_info.snapshot_infos[2].nthreads == 1 - @test list_info.snapshot_infos[2].julia_versioninfo == "Long Julia Version String" - @test list_info.snapshot_infos[2].rngstate == Xoshiro(0xfff0241072ddab67, 0xc53bc12f4c3f0b4e, 0x56d451780b2dd4ba, 0x50a4aa153d208dd8) - @test list_info.snapshot_infos[2].snapshot_sha256 == hex2bytes("6f52d44936bef59e543126cbd610a09538f5b5aeb568d616a99750240002b000") - - @test list_info.snapshot_infos[3].time_stamp == DateTime("2022-11-06T17:30:03") - @test list_info.snapshot_infos[3].step_number == 2 - @test list_info.snapshot_infos[3].nthreads == 1 - @test list_info.snapshot_infos[3].julia_versioninfo == "Long Julia Version String" - @test list_info.snapshot_infos[3].rngstate == Xoshiro(0x2007f3c789b9526d, 0x085f596501ec49e6, 0x8048aeb561cb1458, 0xf95c00361c064979) - @test list_info.snapshot_infos[3].snapshot_sha256 == hex2bytes("7c02a201a9f2326c924f961b479ca6570493cb6f15fc173a332c30fa6556c34a") - - @test list_info.final_message == "" -end -@testset "list.txt full run" begin - for ignore_error in (false,true) - list_info, _ = MEDYANSimRunner.parse_list_file("list-examples/list done.txt"; ignore_error) - @test !(list_info.isempty) - @test list_info.job_idx == "2" - @test list_info.input_tree_hash == hex2bytes("f4fde7178433b85c216b15d4678cacfa8650b7289cb2bd8f0ce05c0041924473") - @test list_info.header_sha256 == hex2bytes("a36a3e400d8f3383247c9c58b380b74d9462d3c0484895eaf8a9f55db1aab9aa") - @test length(list_info.snapshot_infos) == 4 - - @test list_info.snapshot_infos[1].time_stamp == DateTime("2022-11-06T17:30:03") - @test list_info.snapshot_infos[1].step_number == 0 - @test list_info.snapshot_infos[1].nthreads == 1 - @test list_info.snapshot_infos[1].julia_versioninfo == "Long Julia Version String" - @test list_info.snapshot_infos[1].rngstate == Xoshiro(0xfff0241072ddab67, 0xc53bc12f4c3f0b4e, 0x56d451780b2dd4ba, 0x50a4aa153d208dd8) - @test list_info.snapshot_infos[1].snapshot_sha256 == hex2bytes("6575c62733b459ec9ee5f7308229a090f1116f0164ab8ef5713e71b9d58dd220") - - @test list_info.snapshot_infos[2].time_stamp == DateTime("2022-11-06T17:30:03") - @test list_info.snapshot_infos[2].step_number == 1 - @test list_info.snapshot_infos[2].nthreads == 1 - @test list_info.snapshot_infos[2].julia_versioninfo == "Long Julia Version String" - @test list_info.snapshot_infos[2].rngstate == Xoshiro(0xfff0241072ddab67, 0xc53bc12f4c3f0b4e, 0x56d451780b2dd4ba, 0x50a4aa153d208dd8) - @test list_info.snapshot_infos[2].snapshot_sha256 == hex2bytes("6f52d44936bef59e543126cbd610a09538f5b5aeb568d616a99750240002b000") - - @test list_info.snapshot_infos[3].time_stamp == DateTime("2022-11-06T17:30:03") - @test list_info.snapshot_infos[3].step_number == 2 - @test list_info.snapshot_infos[3].nthreads == 1 - @test list_info.snapshot_infos[3].julia_versioninfo == "Long Julia Version String" - @test list_info.snapshot_infos[3].rngstate == Xoshiro(0x2007f3c789b9526d, 0x085f596501ec49e6, 0x8048aeb561cb1458, 0xf95c00361c064979) - @test list_info.snapshot_infos[3].snapshot_sha256 == hex2bytes("7c02a201a9f2326c924f961b479ca6570493cb6f15fc173a332c30fa6556c34a") - - @test list_info.snapshot_infos[4].time_stamp == DateTime("2022-11-06T17:30:04") - @test list_info.snapshot_infos[4].step_number == 3 - @test list_info.snapshot_infos[4].nthreads == 1 - @test list_info.snapshot_infos[4].julia_versioninfo == "Long Julia Version String" - @test list_info.snapshot_infos[4].rngstate == Xoshiro(0x0d78c6c5f2400a9c, 0xcf341e2315f17db6, 0x01ceb25a2560ce54, 0x609e89462d6ec715) - @test list_info.snapshot_infos[4].snapshot_sha256 == hex2bytes("4edb8ed71bf2126da7900a58923f96b181a4b4eb9c001da824a9b1a059eb5f9c") - - @test list_info.final_message == "Done" - end -end diff --git a/test/test_normalize_job_idx.jl b/test/test_normalize_job_idx.jl deleted file mode 100644 index e67e8f2..0000000 --- a/test/test_normalize_job_idx.jl +++ /dev/null @@ -1,24 +0,0 @@ -using Test - -using MEDYANSimRunner -using SHA - -@testset "unit tests for normalize_job_idx" begin - get_seed(x) = collect(reinterpret(UInt64, sha256(x))) - @test_throws ArgumentError MEDYANSimRunner.normalize_job_idx("", -1) - @test_throws ArgumentError MEDYANSimRunner.normalize_job_idx("\xff", -1) - @test_throws ArgumentError MEDYANSimRunner.normalize_job_idx("\n", -1) - @test_throws ArgumentError MEDYANSimRunner.normalize_job_idx(",", -1) - @test_throws ArgumentError MEDYANSimRunner.normalize_job_idx("../../1", -1) - @test_throws ArgumentError MEDYANSimRunner.normalize_job_idx("..\\../1", -1) - @test ("1", get_seed("1")) == MEDYANSimRunner.normalize_job_idx("1", -1) - @test ("1/2", get_seed("1/2")) == MEDYANSimRunner.normalize_job_idx("1/2", -1) - @test ("1/2", get_seed("1/2")) == MEDYANSimRunner.normalize_job_idx("1\\2", -1) - @test ("1/2", get_seed("1/2")) == MEDYANSimRunner.normalize_job_idx("1\\2/", -1) - @test ("1/2", get_seed("1/2")) == MEDYANSimRunner.normalize_job_idx("/1\\2/", -1) - @test ("1/2", get_seed("1/2")) == MEDYANSimRunner.normalize_job_idx("//1//2///", -1) - @test_throws ArgumentError MEDYANSimRunner.normalize_job_idx(joinpath(@__DIR__,"foo"), 1) - @test_throws BoundsError MEDYANSimRunner.normalize_job_idx(joinpath(@__DIR__,"examples","good","jobnames.txt"), 10) - @test ("6", get_seed("6")) == MEDYANSimRunner.normalize_job_idx(joinpath(@__DIR__,"examples","good","jobnames.txt"), 1) - @test ("3", get_seed("3")) == MEDYANSimRunner.normalize_job_idx(joinpath(@__DIR__,"examples","good","jobnames.txt"), 2) -end \ No newline at end of file diff --git a/test/test_ref_out.jl b/test/test_ref_out.jl index 43e2c47..e837b55 100644 --- a/test/test_ref_out.jl +++ b/test/test_ref_out.jl @@ -4,52 +4,93 @@ using Test using MEDYANSimRunner +using LoggingExtras +using Logging +module UserCode + include("example/main.jl") +end +@testset "reference output" begin -@testset "good example" begin - test_out = "examples/good/output" - rm(test_out; force=true, recursive=true) - mkpath(test_out) - MEDYANSimRunner.run("examples/good/input/", test_out, "1") - out_diff = sprint(MEDYANSimRunner.diff, "examples/good/output-ref/1", joinpath(test_out,"1")) - if !isempty(out_diff) - println(out_diff) - @test false - end +ref_out = if VERSION >= v"1.10" + joinpath(@__DIR__, "example/output-ref-1_10") +else + joinpath(@__DIR__, "example/output-ref-1_9") end -@testset "good partial example" begin - test_out = "examples/good partial/output" - mkpath(test_out) - cp("examples/good partial/output partial", test_out; force=true) - MEDYANSimRunner.run("examples/good/input/", test_out, "1") - out_diff = sprint(MEDYANSimRunner.diff, "examples/good/output-ref/1", joinpath(test_out,"1")) - if !isempty(out_diff) - println(out_diff) - @test false +warn_only_logger = MinLevelLogger(current_logger(), Logging.Warn); + + +@testset "full run example" begin + for continue_sim in (true,false) + test_out = joinpath(@__DIR__, "example/output/full",continue_sim ? "conti" : "start") + rm(test_out; force=true, recursive=true) + args = ["--out=$test_out","--batch=1"] + continue_sim && push!(args,"--continue") + with_logger(warn_only_logger) do + MEDYANSimRunner.run_sim(args; + UserCode.jobs, + UserCode.setup, + UserCode.save_snapshot, + UserCode.load_snapshot, + UserCode.loop, + UserCode.done, + ) + end + out_diff = sprint(MEDYANSimRunner.print_traj_diff, joinpath(ref_out,"a"), joinpath(test_out,"a")) + if !isempty(out_diff) + println(out_diff) + @test false + end end end -@testset "missing manifest example" begin - test_out = "examples/missing manifest/output" - rm(test_out; force=true, recursive=true) - mkpath(test_out) - MEDYANSimRunner.run("examples/missing manifest/input/", test_out, "1") - # The output should be empty - out_diff = sprint(MEDYANSimRunner.diff, mktempdir(), joinpath(test_out,"1")) - if !isempty(out_diff) - println(out_diff) - @test false +@testset "partial restart example $v" for v in 1:8 + for continue_sim in (true,false) + test_out = joinpath(@__DIR__, "example/output/restart $v",continue_sim ? "conti" : "start") + mkpath(test_out) + cp(ref_out, test_out; force=true) + if v > 1 + rm(joinpath(test_out,"a/traj/footer.json")) + end + if v > 2 + rm(joinpath(test_out,"a/traj/snap11.zarr.zip")) + end + if v > 3 + for i in 10:-1:5 + rm(joinpath(test_out,"a/traj/snap$i.zarr.zip")) + end + end + if v > 4 + for i in 4:-1:2 + rm(joinpath(test_out,"a/traj/snap$i.zarr.zip")) + end + end + if v > 5 + rm(joinpath(test_out,"a/traj/snap1.zarr.zip")) + end + if v > 6 + rm(joinpath(test_out,"a/traj/snap0.zarr.zip")) + end + if v > 7 + rm(joinpath(test_out,"a/traj/header.json")) + end + + args = ["--out=$test_out","--batch=1"] + continue_sim && push!(args,"--continue") + with_logger(warn_only_logger) do + MEDYANSimRunner.run_sim(args; + UserCode.jobs, + UserCode.setup, + UserCode.save_snapshot, + UserCode.load_snapshot, + UserCode.loop, + UserCode.done, + ) + end + out_diff = sprint(MEDYANSimRunner.print_traj_diff, joinpath(ref_out,"a"), joinpath(test_out,"a")) + if !isempty(out_diff) + println(out_diff) + @test false + end end end -@testset "loading timeout example" begin - test_out = "examples/loading timeout/output" - rm(test_out; force=true, recursive=true) - mkpath(test_out) - MEDYANSimRunner.run("examples/loading timeout/input/", test_out, "1"; - startup_timeout=2.0, - ) - out_diff = sprint(MEDYANSimRunner.diff, "examples/loading timeout/output-ref/1", joinpath(test_out,"1")) - if !isempty(out_diff) - println(out_diff) - @test false - end end \ No newline at end of file diff --git a/test/test_timeout.jl b/test/test_timeout.jl deleted file mode 100644 index 112139c..0000000 --- a/test/test_timeout.jl +++ /dev/null @@ -1,92 +0,0 @@ -using Test - -using Distributed - -using MEDYANSimRunner - - -@testset "timeout normal cases" begin - worker = addprocs(1; - topology=:master_worker, - )[1] - state, result = MEDYANSimRunner.run_with_timeout(worker, 1000.0, quote - 1+1 - end) - - @test state === :ok - @test result === 2 - - state, result = MEDYANSimRunner.run_with_timeout(worker, 1.0, quote - sleep(0.1) - 1+1 - end) - - @test state === :ok - @test result === 2 - - state, result = MEDYANSimRunner.run_with_timeout(worker, 0.1, quote - sleep(1.0) - 1+1 - end) - - @test state == :timed_out - @test result === nothing - - @test !(worker in workers()) - - worker = addprocs(1; - topology=:master_worker, - )[1] - - state, result = MEDYANSimRunner.run_with_timeout(worker, 1000.0, quote - 1+1 - end) - - @test state === :ok - @test result === 2 - - state, result = MEDYANSimRunner.run_with_timeout(worker, 1000.0, quote - exit() - end) - - @test state === :worker_exited - @test result === nothing - - @test !(worker in workers()) - - worker = addprocs(1; - topology=:master_worker, - )[1] - - state, result = MEDYANSimRunner.run_with_timeout(worker, 1000.0, quote - error("wow") - end) - - @test state === :errored - @test startswith(result, "wow\nStacktrace:\n [1] error\n") - - state, result = MEDYANSimRunner.run_with_timeout(worker, 1000.0, quote - 1+1 - end) - - @test state === :ok - @test result === 2 - - state, result = MEDYANSimRunner.run_with_timeout(worker, 0.1, quote - function foo() - x = rand() - while x > 0.000001 - x += rand() - end - x - end - foo() - end) - - @test state == :timed_out - @test result === nothing - - @test !(worker in workers()) - - worker in workers() && rmprocs(worker) -end \ No newline at end of file