From 5e8e1ad52a1789298ee12c062c5e54445c0165fc Mon Sep 17 00:00:00 2001 From: Maximilian Mordig Date: Sat, 2 Mar 2024 13:32:52 +0100 Subject: [PATCH] updated --- .github/workflows/build_docker.yml | 6 +- .vscode/launch.json | 48 +- .vscode/tasks.json | 28 + DeveloperNotes.md | 11 +- pyproject.toml | 5 +- simulator_example.png | Bin 72535 -> 91023 bytes .../seqsum_tools/coverage_tracker.py | 12 +- .../seqsum_tools/seqsum_plotting.py | 129 +- .../shared_utils/debugging_helpers.py | 6 +- .../shared_utils/logging_utils.py | 14 +- .../shared_utils/nanosim_parsing.py | 9 +- .../shared_utils/tee_stdouterr.py | 2 +- .../shared_utils/thread_helpers.py | 2 + src/simreaduntil/shared_utils/timing.py | 2 +- src/simreaduntil/shared_utils/utils.py | 205 +++- src/simreaduntil/simulator/README.md | 4 +- src/simreaduntil/simulator/channel.py | 326 ++--- src/simreaduntil/simulator/channel_element.py | 187 +-- src/simreaduntil/simulator/channel_stats.py | 4 +- .../constant_gaps_until_blocked.py | 3 + .../gap_sampler_per_window_until_blocked.py | 4 +- .../simulator/gap_sampling/gap_sampling.py | 2 + .../inactive_active_gaps_replication.py | 2 + .../rolling_window_gap_sampler.py | 4 +- .../simulator/protos/ont_device.proto | 10 +- .../protos_generated/ont_device_pb2.py | 50 +- .../protos_generated/ont_device_pb2.pyi | 44 +- .../protos_generated/ont_device_pb2_grpc.py | 12 +- src/simreaduntil/simulator/readpool.py | 270 ++++- src/simreaduntil/simulator/readswriter.py | 73 +- .../simulator/simfasta_to_seqsum.py | 99 +- src/simreaduntil/simulator/simulator.py | 192 +-- .../simulator/simulator_client.py | 20 +- .../simulator/simulator_params.py | 22 +- .../simulator/simulator_server.py | 6 +- src/simreaduntil/simulator/utils.py | 12 +- .../cli_usecase/simulator_client_cli.py | 51 +- .../cli_usecase/simulator_server_cli.py | 89 +- .../usecase_helpers/readfish_plotting.py | 160 ++- .../usecase_helpers/readfish_wrappers.py | 16 +- .../simulator_with_readfish.py | 161 ++- src/simreaduntil/usecase_helpers/utils.py | 42 +- tests/shared_utils/test_nanosim_parsing.py | 15 +- tests/shared_utils/test_timing.py | 2 +- tests/shared_utils/test_utils.py | 141 ++- tests/simulator/test_channel.py | 56 +- tests/simulator/test_channel_element.py | 159 ++- tests/simulator/test_readpool.py | 97 +- tests/simulator/test_readswriter.py | 49 +- tests/simulator/test_sim_params.py | 6 +- tests/simulator/test_simfasta_to_seqsum.py | 60 +- tests/simulator/test_simulator.py | 126 +- tests/simulator/test_simulator_client.py | 4 +- tests/simulator/test_simulator_server.py | 11 +- tests/simulator/test_utils.py | 7 +- .../data/run_dir/configs/config.toml | 6 +- .../run_dir/configs/readfish_enrich_chr1.toml | 2 +- .../test_run_simulator_with_readfish.py | 3 + usecases/README.md | 7 +- usecases/analyze_readfish_outputs.ipynb | 1046 ++++++++++------- usecases/compare_replication_methods.ipynb | 140 ++- usecases/compute_absolute_enrichment.ipynb | 447 +++++++ .../sampler_per_window/config.toml | 5 +- .../readfish_enrich_chr20.toml | 2 +- .../accelerations/config_accel1/README.md | 1 + .../accelerations/config_accel1/config.toml | 29 + .../readfish_enrich_chr2021.toml | 21 + .../accelerations/config_accel10/README.md | 1 + .../accelerations/config_accel10/config.toml | 29 + .../readfish_enrich_chr2021.toml | 21 + .../accelerations/config_accel3/README.md | 1 + .../accelerations/config_accel3/config.toml | 29 + .../readfish_enrich_chr2021.toml | 21 + .../accelerations/config_accel5/README.md | 1 + .../accelerations/config_accel5/config.toml | 29 + .../readfish_enrich_chr2021.toml | 21 + .../accelerations/config_accel7.5/README.md | 1 + .../accelerations/config_accel7.5/config.toml | 29 + .../readfish_enrich_chr2021.toml | 21 + .../config_readfishexp/config.toml | 30 + .../readfish_enrich_chr1620.toml | 21 + .../config.toml | 30 + .../readfish_enrich_chr1to8.toml | 21 + .../config.toml | 30 + .../readfish_enrich_chr9to14.toml | 21 + .../config_readfishexp_control/config.toml | 30 + .../readfish_enrich_chr1620.toml | 21 + .../config_readfishexp_fakemapper/config.toml | 30 + .../readfish_enrich_chr1620.toml | 21 + .../config.toml | 30 + .../readfish_enrich_chr1620.toml | 21 + .../config_readfishexp_realreads/config.toml | 38 + .../readfish_enrich_per_quadrant.toml | 60 + .../sampler_per_window/config.toml | 8 +- .../readfish_enrich_chr2021.toml | 2 +- .../sampler_per_window/config.toml | 2 +- .../readfish_enrich_chr2021.toml | 2 +- usecases/create_nanosim_reads.ipynb | 89 +- usecases/enrich_usecase.py | 116 +- usecases/enrich_usecase_submission.sh | 43 +- usecases/gen_example_sim_plot.py | 48 + usecases/generate_nanosim_reads.sh | 98 ++ usecases/install_usecase_deps.sh | 8 +- usecases/replicate_run.py | 4 +- usecases/replicate_run_submission.sh | 3 +- 105 files changed, 4385 insertions(+), 1432 deletions(-) create mode 100644 .vscode/tasks.json create mode 100644 usecases/compute_absolute_enrichment.ipynb create mode 100644 usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel1/README.md create mode 100644 usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel1/config.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel1/readfish_enrich_chr2021.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel10/README.md create mode 100644 usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel10/config.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel10/readfish_enrich_chr2021.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel3/README.md create mode 100644 usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel3/config.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel3/readfish_enrich_chr2021.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel5/README.md create mode 100644 usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel5/config.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel5/readfish_enrich_chr2021.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel7.5/README.md create mode 100644 usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel7.5/config.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel7.5/readfish_enrich_chr2021.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp/config.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp/readfish_enrich_chr1620.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_chr1to8_fakemapper/config.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_chr1to8_fakemapper/readfish_enrich_chr1to8.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_chr9to14_fakemapper/config.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_chr9to14_fakemapper/readfish_enrich_chr9to14.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_control/config.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_control/readfish_enrich_chr1620.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_fakemapper/config.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_fakemapper/readfish_enrich_chr1620.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_fakemapper_control/config.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_fakemapper_control/readfish_enrich_chr1620.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_realreads/config.toml create mode 100644 usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_realreads/readfish_enrich_per_quadrant.toml create mode 100644 usecases/gen_example_sim_plot.py create mode 100755 usecases/generate_nanosim_reads.sh diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml index bbe909d..ea3ee60 100644 --- a/.github/workflows/build_docker.yml +++ b/.github/workflows/build_docker.yml @@ -33,7 +33,7 @@ jobs: packages: write contents: read runs-on: ubuntu-latest - timeout-minutes: 45 + timeout-minutes: 60 steps: - name: Checkout @@ -83,6 +83,7 @@ jobs: - name: Test docker image in python3.10 uses: addnab/docker-run-action@v3 + timeout-minutes: 20 with: image: ${{ env.TEST_TAG }} shell: /bin/bash @@ -97,6 +98,7 @@ jobs: - name: Push docker image uses: docker/build-push-action@v4 + timeout-minutes: 20 id: Push with: context: . @@ -108,6 +110,7 @@ jobs: - name: Run full usecase (in Docker container) uses: addnab/docker-run-action@v3 + timeout-minutes: 20 with: image: ${{ env.TEST_TAG }} shell: /bin/bash @@ -133,6 +136,7 @@ jobs: - name: Archive figures uses: actions/upload-artifact@v3 + timeout-minutes: 10 with: name: usecase-figures path: figures.tar.gz diff --git a/.vscode/launch.json b/.vscode/launch.json index d3b30d6..3992111 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,23 +15,61 @@ { "name": "Python: Current File", - "type": "python", + "type": "debugpy", "request": "launch", - "program": "${file}", + + // "program": "${file}", + + "program": "/home/mmordig/ont_project_all/ont_project/usecases/enrich_usecase.py", + "cwd": "/home/mmordig/ont_project_all/ont_project/runs/enrich_usecase/full_genome_run_sampler_per_window", + "console": "integratedTerminal", // "justMyCode": true "justMyCode": false // to debug external library code }, { "name": "Python: Attach to python process", - "type": "python", + "type": "debugpy", "request": "attach", - "processId": "${command:pickProcess}", + "processId": "${command:pickProcess}", // ctrl+Z, fg to get pid; requires "echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope" on Linux, see https://code.visualstudio.com/docs/python/debugging, may need to launch program from within vscode + // "logToFile": true, // in case it fails "justMyCode": false - } + }, // { // // "host": "compute-biomed-01", // "" // } + + { + "name": "Python: enrich usecase", + "type": "debugpy", + "request": "launch", + "python": "/home/mmordig/miniforge3/envs/nanosim/bin/python", + + // (cd /home/mmordig/ont_project_all/ont_project/runs/enrich_usecase/chr202122_run && python ~/ont_project_all/ont_project/usecases/enrich_usecase.py) + + "program": "/home/mmordig/ont_project_all/ont_project/usecases/enrich_usecase.py", + // "cwd": "/home/mmordig/ont_project_all/ont_project/runs/enrich_usecase/chr202122_run", + "cwd": "/home/mmordig/ont_project_all/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_realreads", + + "console": "integratedTerminal", + // "justMyCode": true + "justMyCode": false // to debug external library code + }, + + { + "name": "Python: debug nanosim", + "type": "debugpy", + "request": "launch", + + "python": "/home/mmordig/miniforge3/envs/nanosim/bin/python", + "program": "external/ont_nanosim/src/simulator.py", + "args": ["genome", "--model_prefix", "runs/nanosim_models/human_NA12878_DNA_FAB49712_guppy/training", "--ref_g", "runs/data/random_genome.fasta", "-dna_type", "linear", "-med", "15000", "-max", "20000", "-min", "400", "-sd", "6.9", "--output", "runs/data/nanosim_reads/human_genome_med15000/reads_seed3", "--number", "100000", "--seed", "3", "--strandness", "0.5", "--basecaller", "guppy", "--aligned_rate", "100%", "--num_threads", "1", "--no_flanking", "--no_error_profile"], + "cwd": "/home/mmordig/ont_project_all/ont_project/", + + "console": "integratedTerminal", + // "justMyCode": true + "justMyCode": false // to debug external library code + }, ] } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..79ea931 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "label": "rsync to mpi", + "command": [ + // "rsync -avzh --exclude /ont_project/runs --exclude /ont_project/usecase_data.tar.gz --exclude /ont_project/.tox/ --exclude /ont_project/.git/ --exclude /ont_project/external/ont_nanosim --progress --delete ~/ont_project_all/ont_project mpi:/home/mmordig/ont_project_all &&", + // also syncing nanosim + "rsync -avzh --exclude /ont_project/runs --exclude /ont_project/usecase_data.tar.gz --exclude /ont_project/.tox/ --exclude /ont_project/.git/ --progress --delete ~/ont_project_all/ont_project mpi:/home/mmordig/ont_project_all &&", + + // sync to biomed + // "rsync -avzh --exclude /ont_project/runs --exclude /ont_project/usecase_data.tar.gz --exclude /ont_project/.tox/ --exclude /ont_project/.git/ --exclude /ont_project/external/ont_nanosim --progress --delete ~/ont_project_all/ont_project biomed:/cluster/home/mmordig/ont_project_all &&", + // // sync figures back from biomed + // "rsync -avzh --progress --include='*/' --include '**/figures/*.png' --include '**/configs/*' --include '**/pickled_figures/*.dill' --exclude '*' --delete biomed:/cluster/work/grlab/projects/mmordig/selseq_runs/ ~/ont_project_all/figures_biomed_cluster &&", + + "echo Current time: $(date)" + ], + "problemMatcher": [], + // in keybindings.json + // { + // "key": "cmd+m cmd+p", + // "command": "workbench.action.tasks.runTask", + // "args": "rsync to mpi" + // } + } + ] +} \ No newline at end of file diff --git a/DeveloperNotes.md b/DeveloperNotes.md index 0f05f21..95ef8e4 100644 --- a/DeveloperNotes.md +++ b/DeveloperNotes.md @@ -8,7 +8,13 @@ This is only applicable if you want to develop the package. After changing the package entrypoints, you have to reinstall the package with ```{bash} +# test it with `python -c "import ru"`. pip uninstall -y simreaduntil; pip install -e './[test,readfish,dev]' + +# need to reinstall readfish to use our modified version +# Hatch does not support installing dependencies like readfish in editable mode, so we install it manually with "-e". +# ReadFish imports its own files with `ru.*`, so it assumes that the ReadFish directory is in the `PYTHONPATH`. +pip uninstall -y readfish; pip install -e ./external/ont_readfish ``` This is also necessary when modifying the readfish dependency because it cannot easily be installed in editable mode with hatch: https://(github.com/pypa/hatch/issues/588). @@ -38,11 +44,6 @@ python -m pytest --cov=. tests/simulator/gap_sampling/test_gap_sampling.py::test pydoctor "./src/simreaduntil" # can also put one file to just compile it -# manually install ReadFish -# ReadFish imports its own files with `ru.*`, so it assumes that the ReadFish directory is in the `PYTHONPATH`. -pip install -e ./external/ont_readfish -# test it with `python -c "import ru"`. - git submodule add [] git config --add oh-my-zsh.hide-dirty 1 # otherwise cd into NanoSim directory is slow diff --git a/pyproject.toml b/pyproject.toml index 724d1a7..b8c0276 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,6 +70,9 @@ readfish = [ "read-until @ git+https://github.com/nanoporetech/read_until_api@v3.4.1", "readfish @ {root:uri}/external/ont_readfish", ] +rawsignal = [ + "pyslow5", +] [project.scripts] plot_seqsum = "simreaduntil.seqsum_tools.seqsum_plotting:main" @@ -91,7 +94,7 @@ usecase_make_html_report = "simreaduntil.usecase_helpers.cli_usecase.make_html_r [tool.hatch.version] source = "vcs" -fallback-version = "unknown_version" +fallback-version = "0.0.0.7999" # dummy version to recognize it [tool.hatch.metadata] # to install from git diff --git a/simulator_example.png b/simulator_example.png index 7478122559e4c1fccefdd16c1db4dc6dc4a40ca5..254ae222caf2f54086d841d50d39babf0d4a9fd4 100644 GIT binary patch literal 91023 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 literal 72535 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 sQ^|o8fi&SP$?;2`v+ifqHTu2 Optional[Tuple[Any, int, int]]: read_id: a NanoSim read id Returns: - Tuple (chrom, ref_start, ref_len) with respect to forward strand; None + Tuple (chrom, ref_start, ref_len) with respect to forward strand; None if could not be mapped """ raise NotImplementedError() @@ -255,6 +255,9 @@ def get_fraction_cov_atleast(self, threshold, chroms: Optional[List]=None) -> fl """ if chroms is None: chroms = list(self.coverage_per_chrom.keys()) + if len(chroms) == 0: + logger.warning("get_fraction_cov_atleast called with empty chroms, returning 1.0") + return 1.0 return sum((self.coverage_per_chrom[chrom] >= threshold).sum(dtype=np.uint64) for chrom in chroms) / sum(len(self.coverage_per_chrom[chrom]) for chrom in chroms) def get_chrom_lens(self) -> Dict[str, int]: @@ -503,6 +506,9 @@ def get_fraction_cov_atleast(self, threshold, chroms: Optional[List]=None) -> fl """ if chroms is None: chroms = list(self.coverage_per_chrom.keys()) + if len(chroms) == 0: + logger.warning("get_fraction_cov_atleast called with empty chroms, returning 1.0") + return 1.0 # last block can be shorter, so we also have to weight it differently return sum(((self._avg_cov_per_block(chrom) >= threshold) * self._block_sizes(chrom)).sum(dtype=np.uint64) for chrom in chroms) / sum(self.chrom_lens[chrom] for chrom in chroms) @@ -548,9 +554,13 @@ def plot_state(self, plot_type, target_coverage=None, **kwargs): class NanoSimCoverageTracker(CovTrackerClass): """ Track coverage by parsing the location from the NanoSim read ids + + NanoSim unaligned reads do not map. """ def get_chrom_start_len(self, read_id): nanosim_id = NanoSimId.from_str(read_id) + if nanosim_id.read_type == "unaligned": + return None return (nanosim_id.chrom, nanosim_id.ref_pos, nanosim_id.ref_len) class PafCoverageTracker(CovTrackerClass): diff --git a/src/simreaduntil/seqsum_tools/seqsum_plotting.py b/src/simreaduntil/seqsum_tools/seqsum_plotting.py index 8d61b4d..d4ac77b 100644 --- a/src/simreaduntil/seqsum_tools/seqsum_plotting.py +++ b/src/simreaduntil/seqsum_tools/seqsum_plotting.py @@ -139,7 +139,7 @@ def seqsum_add_cols_for_plotting_selseq_performance(seqsum_df, group_column=None seqsum_df["end_reason"] = "unknown" seqsum_df["end_reason"] = seqsum_df["end_reason"].astype("category") - seqsum_df["is_user_rejection"] = seqsum_df["end_reason"] == "data_service_unblock_mux_change" # user rejections only, rejections due to mux scan may also happen + seqsum_df["is_user_rejection"] = seqsum_df["end_reason"] == "data_service_unblock_mux_change" # user rejections only, rejections due to mux scan or simulation end may also happen seqsum_df["is_full_read"] = seqsum_df["end_reason"] == "signal_positive" # number of full reads seqsum_df["end_time"] = seqsum_df["start_time"] + seqsum_df["duration"] @@ -167,7 +167,9 @@ def seqsum_add_cols_for_plotting_selseq_performance(seqsum_df, group_column=None seqsum_df[f"cum_nb_never_requested_per_{group_column}"] = seqsum_df.groupby(group_column, observed=True)["never_requested"].cumsum() if "nb_ref_bps_full" in seqsum_df.columns: - assert all(~seqsum_df["is_user_rejection"] | seqsum_df["nb_rejectedbps"] > 0) # user rejected => nb_rejectedbps > 0 + pass + # not always true when using real data, e.g. if read was already over when rejected but still logging as rejected + # assert all(~seqsum_df["is_user_rejection"] | seqsum_df["nb_rejectedbps"] > 0) # user rejected => nb_rejectedbps > 0 return seqsum_df @@ -446,7 +448,7 @@ def create_plot(ax, normalize): return fig -def plot_channel_occupation_fraction_over_time(seqsum_df, timepoints=None, mux_scan_interval=None, save_dir=None): +def plot_channel_occupation_over_time(seqsum_df, timepoints=None, mux_scan_interval=None, save_dir=None): """ Plot channel occupation (active percentage) over time @@ -498,7 +500,7 @@ def plot_channel_occupation_fraction_over_time(seqsum_df, timepoints=None, mux_s make_tight_layout(fig) if save_dir is not None: - save_fig_and_pickle(fig, save_dir / f"channel_occupation_fraction_over_time.{FIGURE_EXT}") + save_fig_and_pickle(fig, save_dir / f"channel_occupation_over_time.{FIGURE_EXT}") return ax @@ -787,6 +789,29 @@ def plot_read_end_reason_hist(seqsum_df, save_dir=None): return ax +def plot_read_length_by_end_reason(seqsum_df, save_dir=None, end_reasons=None): + """ + Plot histogram of read length for different end reasons (fully read, stop_receiving, rejected, never_requested) + """ + fig, ax = plt.subplots() + if end_reasons is None: + df = seqsum_df + else: + df = seqsum_df[seqsum_df["end_reason"].isin(end_reasons)] + df["end_reason"] = df["end_reason"].cat.remove_unused_categories() # raises a SettingWithCopyWarning warning, not really clear why + if len(df) > 0: + # seaborn: error when no data + sns.histplot(df, x="sequence_length_template", hue="end_reason", multiple="dodge", ax=ax) + make_tight_layout(fig) + + if save_dir is not None: + base_filename = "read_length_by_end_reason" + if end_reasons is not None: + base_filename += "_" + "_".join(end_reasons) + save_fig_and_pickle(fig, save_dir / f"{base_filename}.{FIGURE_EXT}") + + return ax + def plot_processed_seqsum(seqsum_df, save_dir: Optional[Path]=None, group_column=None, close_figures: Optional[bool]=None): """ Plot a bunch of stuff from the processed seqsum_df, subsampling when sensible @@ -806,7 +831,7 @@ def plot_processed_seqsum(seqsum_df, save_dir: Optional[Path]=None, group_column def close_fig(fig): if close_figures: plt.close(fig) - + # # compute instantaneous rates # # take difference (x[i+step] - x[i-step]) while keeping array size the same by extending the array on both sides by the step # take_diff = lambda x, step=3: np.concatenate((x[step:] - x[:-step], [np.NaN]*step)) @@ -846,14 +871,18 @@ def sample_group(group): # require full seqsum_df fig = plot_number_channels_per_group_over_time(seqsum_df, save_dir=save_dir, group_column=group_column); logger.debug("Created 1 plot"); close_fig(fig) - ax = plot_channel_occupation_fraction_over_time(seqsum_df, save_dir=save_dir); logger.debug("Created 1 plot"); close_fig(ax.figure) + ax = plot_channel_occupation_over_time(seqsum_df, save_dir=save_dir); logger.debug("Created 1 plot"); close_fig(ax.figure) ax = plot_channels_over_time(seqsum_df, save_dir=save_dir); logger.debug("Created 1 plot"); close_fig(ax.figure) if "mux" in seqsum_df.columns and (seqsum_df["mux"].nunique() > 1): ax = plot_mux_over_time(seqsum_df, save_dir=save_dir); logger.debug("Created 1 plot"); close_fig(ax.figure) fig, _ = plot_read_stats_by_channel_hists(seqsum_df, save_dir=save_dir); logger.debug("Created 1 plot"); close_fig(fig) ax = plot_fraction_states_per_channel(seqsum_df, save_dir=save_dir); logger.debug("Created 1 plot"); close_fig(ax.figure) ax = plot_read_end_reason_hist(seqsum_df, save_dir=save_dir); logger.debug("Created 1 plot"); close_fig(ax.figure) - + + # ax = plot_read_length_by_end_reason(seqsum_df, save_dir=save_dir); logger.debug("Created 1 plot"); close_fig(ax.figure) + ax = plot_read_length_by_end_reason(seqsum_df, save_dir=save_dir, end_reasons=["signal_positive"]); logger.debug("Created 1 plot"); close_fig(ax.figure) + ax = plot_read_length_by_end_reason(seqsum_df, save_dir=save_dir, end_reasons=["data_service_unblock_mux_change"]); logger.debug("Created 1 plot"); close_fig(ax.figure) + def plot_coverage_per_group(cov_df, cov_thresholds=[1, 2, 3, 4, 5, 6], save_dir: Optional[Path]=None, group_column="group", close_figures=None): """Plot fraction covered per group for each coverage, then per coverage for each group""" if close_figures is None: @@ -888,43 +917,24 @@ def close_fig(fig): save_fig_and_pickle(fig, save_dir / f"fraction_covered_{group}.{FIGURE_EXT}") logger.debug("Created 1 plot"); close_fig(ax.figure) -def create_plots_for_seqsum(seqsum_df, nrows=None, group_to_units: Dict[str, List[Any]]=None, group_column=None, - ref_genome_path=None, paf_file=None, cov_thresholds=[1, 2, 3, 4, 5, 6], cov_every=1, - save_dir=None, close_figures=None): - """ - Create plots for a sequencing summary file - - Args: - seqsum_df: path to sequencing summary file, or dataframe - nrows: only read the first nrows reads - group_to_units: dictionary {group_name: units} where units form a subset of the unique values in group_column; if None, groups have size 1 - group_column: column in sequencing summary file to group by; if "all", use one group called "all"; if None, use GROUP_COLUMN - - ref_genome_path: path to reference genome; if None, don't plot coverage - paf_file: path to PAF file to map reads to unit; if None, unit is the chromosome extracted from NanoSim read id - cov_thresholds: coverage thresholds to plot - cov_every: coverage is calculated every cov_every reads - - save_dir: directory to save plots to, if None, plots are not saved - close_figures: close figures after saving, if None, close figures if save_dir is not None - - Returns: - seqsum_df, cov_df - """ +# for doc, see create_plots_for_seqsum +def preprocess_seqsum_df_for_plotting(seqsum_df, nrows=None, group_to_units=None, group_column=None, paf_file=None): group_column = group_column or GROUP_COLUMN + chrom_column = group_column # column to use for coverage or to compute groups - if save_dir is not None: - save_dir.mkdir(exist_ok=True) - if not isinstance(seqsum_df, pd.DataFrame): logger.debug(f"Reading {nrows if nrows is not None else 'all'} reads from sequencing summary file '{seqsum_df}'") seqsum_df_filename = seqsum_df - seqsum_df = pd.read_csv(seqsum_df_filename, sep="\t", nrows=nrows) + try: + seqsum_df = pd.read_csv(seqsum_df_filename, sep="\t", nrows=nrows) + except pd.errors.EmptyDataError: + logger.warning(f"Empty sequencing summary file '{seqsum_df}'") + seqsum_df = pd.DataFrame() # empty, will exit below logger.debug(f"Done reading sequencing summary file '{seqsum_df_filename}'") if len(seqsum_df) == 0: - logger.warning(f"Empty sequencing summary file '{seqsum_df}'") - return seqsum_df, None + logger.warning(f"Empty sequencing summary") + return seqsum_df, None, chrom_column logger.info(f"Sorting and cleaning seqsummary file of shape {seqsum_df.shape}") seqsum_df = sort_and_clean_seqsum_df(seqsum_df) @@ -941,18 +951,17 @@ def create_plots_for_seqsum(seqsum_df, nrows=None, group_to_units: Dict[str, Lis logger.info(f"Adding group column from NanoSim read id") add_group_and_reflen_from_nanosim_id(seqsum_df, group_column=group_column) - chrom_column = group_column if group_to_units is not None: # create column to group by, e.g. several chromosomes in one group - units_in_group = set.union(*[set(units) for units in group_to_units.values()]) + all_units = set.union(*[set(units) for units in group_to_units.values()]) observed_units = set(seqsum_df[group_column].unique()) - if not units_in_group.issubset(observed_units): - logger.warning(f"No reads were observed from the following groups: {units_in_group - observed_units}") - other_group = observed_units - units_in_group + if not all_units.issubset(observed_units): + logger.warning(f"No reads were observed from the following groups: {all_units - observed_units}") + other_group = observed_units - all_units if len(other_group) > 0: assert "other" not in group_to_units group_to_units["other"] = other_group - logger.info(f"Plotting according to groups {group_to_units}") + logger.info(f"Splitting according to groups {group_to_units}") group_column = "group" assert group_column not in seqsum_df.columns, f"New column '{group_column}' already in sequencing summary df with columns {seqsum_df.columns}" @@ -962,6 +971,38 @@ def create_plots_for_seqsum(seqsum_df, nrows=None, group_to_units: Dict[str, Lis logger.info("Adding extra columns for plotting") seqsum_df = seqsum_add_cols_for_plotting_selseq_performance(seqsum_df, group_column=group_column) + return seqsum_df, group_column, chrom_column + +def create_plots_for_seqsum(seqsum_df, nrows=None, group_to_units: Dict[str, List[Any]]=None, group_column=None, + ref_genome_path=None, paf_file=None, cov_thresholds=[1, 2, 3, 4, 5, 6], cov_every=1, + save_dir=None, close_figures=None): + """ + Create plots for a sequencing summary file + + Args: + seqsum_df: path to sequencing summary file, or dataframe + nrows: only read the first nrows reads + group_to_units: dictionary {group_name: units} where units form a subset of the unique values in group_column; + if None, groups have size 1; each read should belong to exactly one group + group_column: column in sequencing summary file to group by; if "all", use one group called "all"; if None, use GROUP_COLUMN + + ref_genome_path: path to reference genome; if None, don't plot coverage + paf_file: path to PAF file to map reads to unit; if None, unit is the chromosome extracted from NanoSim read id + cov_thresholds: coverage thresholds to plot + cov_every: coverage is calculated every cov_every reads + + save_dir: directory to save plots to, if None, plots are not saved + close_figures: close figures after saving, if None, close figures if save_dir is not None + + Returns: + seqsum_df, cov_df + """ + + if save_dir is not None: + save_dir.mkdir(exist_ok=True) + + seqsum_df, group_column, chrom_column = preprocess_seqsum_df_for_plotting(seqsum_df, nrows=nrows, group_to_units=group_to_units, group_column=group_column, paf_file=paf_file) + logger.debug("Creating plots for seqsum...") plot_processed_seqsum(seqsum_df, group_column=group_column, save_dir=save_dir, close_figures=close_figures) logger.debug("Done creating plots for seqsum...") @@ -996,7 +1037,7 @@ def main(): """ CLI entrypoint to create plots from a sequencing summary file """ - add_comprehensive_stream_handler_to_logger(None, logging.DEBUG) + add_comprehensive_stream_handler_to_logger(logger, logging.DEBUG) if is_test_mode(): args = argparse.Namespace() @@ -1027,6 +1068,8 @@ def main(): else: group_units = {"targets": args.targets.split(",")} create_plots_for_seqsum(seqsum_df=args.seqsummary_filename, nrows=args.nrows, group_to_units=group_units, ref_genome_path=args.ref_genome_path, paf_file=args.paf_file, cov_thresholds=cov_thresholds, cov_every=args.cov_every, save_dir=args.save_dir) + # # todo: enable above + # create_plots_for_seqsum(seqsum_df=args.seqsummary_filename, nrows=args.nrows, group_column="all", ref_genome_path=args.ref_genome_path, paf_file=args.paf_file, cov_thresholds=cov_thresholds, cov_every=args.cov_every, save_dir=args.save_dir) logger.debug("Done with plotting script") diff --git a/src/simreaduntil/shared_utils/debugging_helpers.py b/src/simreaduntil/shared_utils/debugging_helpers.py index f4773ec..9db4b80 100644 --- a/src/simreaduntil/shared_utils/debugging_helpers.py +++ b/src/simreaduntil/shared_utils/debugging_helpers.py @@ -40,14 +40,14 @@ def warn_debugging(): def helper(): # also print, in case logging is disabled [print("#"*80) for _ in range(5)] - print("Running in test mode") + print("Running in debug mode") [print("#"*80) for _ in range(5)] [logger.info("#"*80) for _ in range(5)] - logger.info("Running in test mode", stacklevel=2) + logger.info("Running in debug mode", stacklevel=2) [logger.info("#"*80) for _ in range(5)] helper() - # also print when program terminates + # also print when program terminates, but only once global __WARN_DEBUGGING_REGISTERED if not __WARN_DEBUGGING_REGISTERED: import atexit diff --git a/src/simreaduntil/shared_utils/logging_utils.py b/src/simreaduntil/shared_utils/logging_utils.py index ad55785..f19de31 100644 --- a/src/simreaduntil/shared_utils/logging_utils.py +++ b/src/simreaduntil/shared_utils/logging_utils.py @@ -66,6 +66,13 @@ def custom_emit(record): END_WITH_CARRIAGE_RETURN = {"extra": {"end": "\r"}} """use together with make_handler_support_end to print to logger with a carriage return (move to beginning of line, overwriting content)""" +def logging_output_formatter(handler): + """configures handler to use a specific formatter""" + formatter = logging.Formatter("%(asctime)s - %(message)s --- %(filename)s:%(lineno)d (%(funcName)s) %(levelname)s ##") + # "--- vscode://%(pathname)s:%(lineno)d - %(levelname)s" + make_handler_support_end(handler) + handler.setFormatter(formatter) + _STREAM_HANDLER_ATTR_NAME = "COMPREHENSIVE_STREAM_HANDLER" def add_comprehensive_stream_handler_to_logger(logger: Union[str, logging.Logger, None]=None, level=logging.NOTSET): """ @@ -91,14 +98,11 @@ def add_comprehensive_stream_handler_to_logger(logger: Union[str, logging.Logger return False handler = logging.StreamHandler() # outputs to sys.stderr - + logging_output_formatter(handler) handler.setLevel(level) setattr(handler, _STREAM_HANDLER_ATTR_NAME, True) - formatter = logging.Formatter("%(asctime)s - %(message)s --- %(filename)s:%(lineno)d (%(funcName)s) %(levelname)s ##") - # "--- vscode://%(pathname)s:%(lineno)d - %(levelname)s" - make_handler_support_end(handler) - handler.setFormatter(formatter) + logger.addHandler(handler) logging.captureWarnings(True) diff --git a/src/simreaduntil/shared_utils/nanosim_parsing.py b/src/simreaduntil/shared_utils/nanosim_parsing.py index dcb1265..8630b44 100644 --- a/src/simreaduntil/shared_utils/nanosim_parsing.py +++ b/src/simreaduntil/shared_utils/nanosim_parsing.py @@ -12,7 +12,7 @@ class NanoSimId: Integer-like strings will be converted. """ def __init__(self, chrom, ref_pos, read_nb, direction, ref_len, head_len=0, tail_len=0, read_type="aligned"): - assert read_type in ["aligned", "perfect"] + assert read_type in ["aligned", "unaligned", "perfect"] assert direction in ["R", "F"] ref_pos = int(ref_pos) ref_len = int(ref_len) @@ -20,8 +20,8 @@ def __init__(self, chrom, ref_pos, read_nb, direction, ref_len, head_len=0, tail head_len = int(head_len) tail_len = int(tail_len) # if read_type == "perfect": - assert head_len == 0 - assert tail_len == 0 + # assert head_len == 0 + # assert tail_len == 0 self.chrom = chrom self.ref_pos = ref_pos @@ -42,11 +42,14 @@ def from_str(read_id: str): E.g. chr2_920875_perfect_proc0:1_F_0_8346_0 chr2_649870_aligned_proc0:2_F_0_10399_0 + chr-18_12681_aligned_proc3:68_F_5_13231_10 + genome1-chr-6_236227_unaligned_proc5:16_R_0_16119_0 This is of the form {genome}-{chromosome}_{ref_pos}_{read_type}_{read_nb}_{strand}_{head_len}_{segment_lengths}_{tail_len} The ref_pos is 0-based and the read spans [ref_pos:ref_pos+ref_len] on the forward strand, independent of the the direction which is F or R (forward, reverse). We assume here that the head and tail flanking lengths are 0. read_nb: proc{process_nb}:{read_nb_for_process} + head and tail len are with respect to read on forward strand, so when the read is reversed, the read starts with the tail length Note (not relevant here): for chimeric reads, '{genome-chromosome}_{position}' and 'segment_lengths' are joined by ";" etc. """ diff --git a/src/simreaduntil/shared_utils/tee_stdouterr.py b/src/simreaduntil/shared_utils/tee_stdouterr.py index 164750e..90542c5 100644 --- a/src/simreaduntil/shared_utils/tee_stdouterr.py +++ b/src/simreaduntil/shared_utils/tee_stdouterr.py @@ -13,7 +13,7 @@ class TeeStdouterr: We use the logging module to ensure thread-safety when writing to it. Do not use this logger or its children for anything else. - Unlike common implementations seen on the Internet, this implementation correctly logs stdout and stderr to the respective + Unlike common implementations seen on the Internet, this implementation logs stdout and stderr to the respective streams (instead of merging them into one stream). Args: diff --git a/src/simreaduntil/shared_utils/thread_helpers.py b/src/simreaduntil/shared_utils/thread_helpers.py index 5350e00..7d3c30f 100644 --- a/src/simreaduntil/shared_utils/thread_helpers.py +++ b/src/simreaduntil/shared_utils/thread_helpers.py @@ -54,6 +54,8 @@ def inner_wrapper(self, *args, **kwargs): return inner_wrapper return wrapper +# todo: remove + class MakeThreadSafe: """ Inheriting from this class makes instance method and instance attribute access thread-safe. diff --git a/src/simreaduntil/shared_utils/timing.py b/src/simreaduntil/shared_utils/timing.py index 57865ba..6a38430 100644 --- a/src/simreaduntil/shared_utils/timing.py +++ b/src/simreaduntil/shared_utils/timing.py @@ -62,7 +62,7 @@ def elapsed_time_last_reset(self): _time_offset = time.time_ns() - time.perf_counter_ns() # time when this module is loaded, minus offset def cur_ns_time(): """ - Get monotonic current time with nanosecond precision + Get monotonic current time with nanosecond precision, in seconds, includes time during sleep Notes: time.time_ns() is not monotonic, so when waking up the machine again, the time might decrease by 1 second or so, so it is not monotonic diff --git a/src/simreaduntil/shared_utils/utils.py b/src/simreaduntil/shared_utils/utils.py index dc8b3cc..16a5f4b 100644 --- a/src/simreaduntil/shared_utils/utils.py +++ b/src/simreaduntil/shared_utils/utils.py @@ -2,18 +2,25 @@ General utility functions """ +from contextlib import contextmanager +import contextlib import copy import functools import gzip import os from pathlib import Path +import queue import shutil import subprocess +import sys +import threading +import time from typing import Any, Dict, Iterable, List import dill import tqdm from simreaduntil.shared_utils.logging_utils import setup_logger_simple +from simreaduntil.shared_utils.timing import cur_ns_time logger = setup_logger_simple(__name__) @@ -220,4 +227,200 @@ def _cycle_list_deep(lst): import copy while True: for x in lst: - yield copy.deepcopy(x) \ No newline at end of file + yield copy.deepcopy(x) + + +@contextmanager +def set_signal_handler(signal_type, handler): + """ + Set a signal handler temporarily + + This function can only be called from the main thread of the main interpreter. + + This is better than using KeyboardInterrupt because this immediately breaks out of the code, possibly leaving the code in an inconsistent + state, e.g. for the simulator updating channels when Ctrl-C is pressed causes it to immediately stop. It is better to set a flag to stop + it. Since the signal handler acquires the Pytho GIL, make sure the code in the signal handler can run to completion because all other threads are + stopped as well due to the Python GIL, so no locked mutexes should be required. + + See the tests for an example. + + Args: + signal_type: e.g. signal.SIGINT for KeyboardInterrupt + handler: function to handle the signal taking two arguments, should not mess with any state + """ + import signal + old_handler = signal.getsignal(signal_type) + signal.signal(signal_type, handler) + yield + signal.signal(signal_type, old_handler) + + +@contextmanager +def tee_stdouterr_to_file(filename_base, mode="a"): + """ + Try to use a file handler with the logger rather than this function! + + Tee the entire output of a Python program to a file and the console. + + # assign to an object since otherwise it will get destroyed and the file handler will become invalid! + obj = tee_stdouterr_to_file("test11", mode="w") + obj.__enter__() + # or use with a "with" statement + + Args: + filename_base: base of the filename to write to, ".out" and ".err" will be appended, directory containing the file must exist + mode: mode to open file in, "a" for append, "w" for overwrite + """ + + """ + File object that writes to multiple file objects at once, e.g. for teeing + """ + class MultipleFilesWriter: + def __init__(self, *files): + self.files = files + + def write(self, text): + for file in self.files: + file.write(text) + + def flush(self): + for file in self.files: + file.flush() + + # to debug this function in case of exception, remove the redirection of stdout + with open(str(filename_base) + ".out", mode=mode) as out_file: + with open(str(filename_base) + ".err", mode=mode) as err_file: + old_stdout = sys.stdout + old_stderr = sys.stderr + with contextlib.redirect_stdout(MultipleFilesWriter(old_stdout, out_file)): + with contextlib.redirect_stderr(MultipleFilesWriter(old_stderr, err_file)): + yield + + +""" +Class storing a value + +Useful for passing values by reference to a function, since int, float etc are immutable, so modifications to them are not visible outside the function +""" +class MutableValue: + def __init__(self, value=None): + self.value = value +# def __repr__(self): +# return f"MutableValue({self.value})" +# def __str__(self): +# return f"MutableValue({self.value})" +# def __eq__(self, other): +# return self.value == other.value +# def __hash__(self): +# return hash(self.value) + +def record_gen_waiting_time(gen, waiting_time: MutableValue): + """ + Record how much time is spent waiting for new elements in the generator, only counting the times when requesting an element and until getting it + + The function also works if the generator is stopped early. + + Args: + gen: generator to wrap + waiting_time: MutableValue to store the total waiting time in seconds, only includes the time until the generator is destroyed + + Yields: values from gen + """ + elapsed_time = 0 + try: + t_before = cur_ns_time() + for x in gen: + elapsed_time += cur_ns_time() - t_before + yield x + t_before = cur_ns_time() + finally: + # to make sure this is executed even if the generator is stopped early + waiting_time.value = elapsed_time + +def record_gen_fcn_waiting_time(gen_fcn, gen, waiting_time: MutableValue): + """ + Record how much time the function gen_fcn(gen) takes processing elements coming from generator gen, excluding the time waiting for elements from gen + + Example: + See tests + + Args: + gen_fcn: function taking a generator and returning a generator + gen: generator to wrap + waiting_time: MutableValue to store the total waiting time in seconds of gen_fcn itself only, only includes the time until the generator is destroyed + """ + waiting_time.value = 0 + time_inner_gen = MutableValue() + try: + yield from record_gen_waiting_time(gen_fcn(record_gen_waiting_time(gen, time_inner_gen)), waiting_time) + finally: + waiting_time.value -= time_inner_gen.value + + +""" +Queue with interruptible get and put methods when it is stopped, returned a QueueStoppedException +""" +class StoppableQueue(queue.Queue): + class QueueStoppedException(Exception): + pass + # workaround since otherwise Empty not found + Empty = queue.Empty + Full = queue.Full + + def __init__(self, maxsize=0): + super().__init__(maxsize) + # Create an event that can be used to signal the queue to stop its operations. + self.stop_event = threading.Event() + self.POLL_INTERVAL = 0.1 + + """ + Put an item on the queue + + raises an QueueStoppedException if the stop event is set + + Args: + item: the item to put on the queue + block: if True, repeatedly try to put item (repeating if queue is full); if False, immediately raise queue.Full if full + timeout: only applies when block=True + """ + def put(self, item, block=True, timeout=None): + start_time = time.time() + while not self.stop_event.is_set(): + try: + super().put(item, block, timeout=self.POLL_INTERVAL) + return + except queue.Full: + if (not block) or (timeout is not None and time.time() - start_time > timeout): + raise # queue full exception + if self.stop_event.is_set(): + raise self.QueueStoppedException() + + """ + Get an item from the queue + + raises an QueueStoppedException if the stop event is set + + Args: + block: if True, repeatedly try to get item (repeating if queue is empty); if False, immediately raise queue.Empty if empty + timeout: only applies when block=True + + Returns: + item from the queue, or exception if None (queue empty or stop event set or timeout expired) + """ + def get(self, block=True, timeout=None): + start_time = time.time() + while not self.stop_event.is_set(): + try: + item = super().get(block, timeout=self.POLL_INTERVAL) + return item + except queue.Empty: + if (not block) or (timeout is not None and time.time() - start_time > timeout): + raise # queue empty exception + if self.stop_event.is_set(): + raise self.QueueStoppedException() + + """ + Convenience method to set the stop event. + """ + def stop(self): + self.stop_event.set() \ No newline at end of file diff --git a/src/simreaduntil/simulator/README.md b/src/simreaduntil/simulator/README.md index d51f4bc..6c5dd93 100644 --- a/src/simreaduntil/simulator/README.md +++ b/src/simreaduntil/simulator/README.md @@ -41,8 +41,8 @@ mkdir protos_generated python -m grpc_tools.protoc -Iprotos/ --python_out=protos_generated/ --pyi_out=protos_generated/ --grpc_python_out=protos_generated/ protos/ont_device.proto && \ sed -i -E "s%import (.*)_pb2 as%import simreaduntil.simulator.protos_generated.\1_pb2 as%g" protos_generated/ont_device_pb2_grpc.py -# todo: check -cd src && python -m grpc_tools.protoc -Isimreaduntil/simulator/protos/ --python_out=simreaduntil/simulator/protos_generated/ --pyi_out=simreaduntil/simulator/protos_generated/ --grpc_python_out=simreaduntil/simulator/protos_generated/ simreaduntil/simulator/protos/ont_device.proto +# not working: +# cd src && python -m grpc_tools.protoc -Isimreaduntil/simulator/protos/ --python_out=simreaduntil/simulator/protos_generated/ --pyi_out=simreaduntil/simulator/protos_generated/ --grpc_python_out=simreaduntil/simulator/protos_generated/ simreaduntil/simulator/protos/ont_device.proto ``` diff --git a/src/simreaduntil/simulator/channel.py b/src/simreaduntil/simulator/channel.py index 3e5bece..1b11c34 100644 --- a/src/simreaduntil/simulator/channel.py +++ b/src/simreaduntil/simulator/channel.py @@ -4,19 +4,20 @@ #%% from __future__ import annotations -import enum # for referring in type hints of a class's method to class itself, Python 3.7, otherwise use strings; see https://stackoverflow.com/questions/55320236/does-python-evaluate-type-hinting-of-a-forward-reference +import contextlib +import enum +from threading import Lock # for referring in type hints of a class's method to class itself, Python 3.7, otherwise use strings; see https://stackoverflow.com/questions/55320236/does-python-evaluate-type-hinting-of-a-forward-reference from typing import Iterable, List, Union, Tuple, Any from matplotlib.layout_engine import TightLayoutEngine import numpy as np from simreaduntil.shared_utils.logging_utils import setup_logger_simple -from simreaduntil.shared_utils.thread_helpers import MakeThreadSafe from simreaduntil.simulator.channel_stats import ChannelStats from simreaduntil.simulator.gap_sampling.gap_sampling import GapSampler from simreaduntil.simulator.simulator_params import SimParams from simreaduntil.simulator.utils import in_interval -from simreaduntil.simulator.readpool import NoReadLeft, ReadPool +from simreaduntil.simulator.readpool import NoReadLeftException, ReadPool from simreaduntil.simulator.readswriter import ReadsWriter from simreaduntil.simulator.channel_element import ChannelBroken, ChannelElement, ShortGap, MuxScan, NoReadLeftGap, UnblockDelay, ChunkedRead, LongGap, ReadEndReason @@ -67,7 +68,7 @@ class UnblockResponse(int, enum.Enum): def to_str(self): return {UnblockResponse.MISSED: "missed", UnblockResponse.UNBLOCKED: "unblocked"}[self] -class Channel(MakeThreadSafe): +class Channel: """ Simulate the reads from a flow cell pore (channel) @@ -79,14 +80,16 @@ class Channel(MakeThreadSafe): The channel can be reused by calling .start() again after a .stop(). This will however not reset the states of ReadPool and ReadsWriter. - The class is thread-safe, at most one thread at a time can call its methods simultaneously. - Methods: - chan.start(t) # Start the channel at time t, channel now active - chan.forward(t) # Forward the channel to time t - - chan.get_new_chunks() # get new chunks of read-in-progress, concatenation of all chunks + - chan.get_new_samples() # get new chunks of read-in-progress, concatenation of all chunks - chan.stop() # stop the channel, write current read until current time (last call of chan.forward(t)) After this, the channel is clean and .start(t) can be called again + + A mutex protects start, stop, forward, run_mux_scan, stop_receiving, unblock. + get_new_samples() can be called in parallel without a mutex, but modifies the stats, so they should not + be accessed/written at the same time. Arguments: name: channel name @@ -112,6 +115,8 @@ def __init__(self, name: str, read_pool: ReadPool, reads_writer: ReadsWriter, si self.save_elems = False self.stats = None self.cur_elem : Union[ChannelElement, None] = None + + self._cur_elem_mutex = Lock() def __repr__(self): return f"Channel({self.name}, cur_elem={self.cur_elem}, stats={self.stats})" @@ -126,11 +131,12 @@ def start(self, t_start): if self.is_running: raise ChannelAlreadyRunningException() - self.t_start = t_start - self.t = t_start - self.finished_elems = [] - self.stats = ChannelStats(n_channels=1) - self.run_mux_scan(t_duration=0, _starting_up=True) + with self._cur_elem_mutex: + self.t_start = t_start + self.t = t_start + self.finished_elems = [] + self.stats = ChannelStats(n_channels=1) + self.run_mux_scan(t_duration=0, _starting_up=True) # sets self.cur_elem def stop(self): """ @@ -147,14 +153,15 @@ def stop(self): if not self.is_running: raise ChannelNotRunningException() - if isinstance(self.cur_elem, ChunkedRead): - # reject current read - self._write_read(end_reason=ReadEndReason.SIM_STOPPED) - else: - self.cur_elem.t_end = self.t - - self._finish_element_in_stats() - self.cur_elem = None + with self._cur_elem_mutex: + if isinstance(self.cur_elem, ChunkedRead): + # reject current read + self._write_read(self.cur_elem, end_reason=ReadEndReason.SIM_STOPPED) + else: + self.cur_elem.t_end = self.t + + self._finish_elem_in_stats(self.cur_elem) + self.cur_elem = None @property def is_running(self): @@ -169,24 +176,24 @@ def is_idle(self): """ return isinstance(self.cur_elem, (NoReadLeftGap, ChannelBroken)) - def _move_to_next_element(self): + def _move_to_next_elem(self, last_elem): """ Helper function for .forward() to choose the next element in the channel + Writes the current read if last_elem is a read - Also starts the element + Not thread-safe """ - last_elem = self.cur_elem t_start = last_elem.t_end - # get a new read, otherwise NoReadLeft + # get a new read, otherwise NoReadLeftGap def get_new_read(): try: # new read new_read_id, new_read_seq = self.read_pool.get_new_read(channel=self.name) return ChunkedRead(new_read_id, new_read_seq, t_start, t_delay=self.sim_params.gap_samplers[self.name].sample_read_start_delay(channel_stats=self.stats, random_state=self.sim_params.random_state), - read_speed=self.sim_params.bp_per_second, chunk_size=self.sim_params.chunk_size) - except NoReadLeft: + read_speed=self.sim_params.bp_per_second, min_chunk_size=self.sim_params.min_chunk_size) + except NoReadLeftException: # insert infinite gap return NoReadLeftGap(t_start) @@ -202,27 +209,26 @@ def get_new_gap(): if isinstance(last_elem, MuxScan): if last_elem.elem_to_restore is None: - self.cur_elem = get_new_gap() + new_elem = get_new_gap() else: # restore old element which is a long gap assert isinstance(last_elem.elem_to_restore, LongGap) - next_elem = last_elem.elem_to_restore - next_elem.t_start = t_start - self.cur_elem = next_elem + new_elem = last_elem.elem_to_restore + new_elem.t_start = t_start elif isinstance(last_elem, ChunkedRead): - self._write_read(end_reason=None) - self.cur_elem = get_new_gap() + self._write_read(last_elem, end_reason=None) + new_elem = get_new_gap() elif isinstance(last_elem, UnblockDelay): - self.cur_elem = get_new_gap() + new_elem = get_new_gap() elif isinstance(last_elem, ShortGap): - self.cur_elem = get_new_read() + new_elem = get_new_read() elif isinstance(last_elem, LongGap): self.sim_params.gap_samplers[self.name].mark_long_gap_end(channel_stats=self.stats) - self.cur_elem = get_new_read() + new_elem = get_new_read() else: assert not isinstance(last_elem, (NoReadLeftGap, ChannelBroken)), "NoReadLeftGap has infinite length (sink state), so no next state" raise ValueError(f"unknown channel element type: {type(last_elem).__name__}") - self._start_cur_element_in_stats() + return new_elem def forward(self, t, delta=False): """ @@ -238,25 +244,27 @@ def forward(self, t, delta=False): Raises: ChannelNotRunningException: if channel is not running """ - if not self.is_running: - raise ChannelNotRunningException() - assert self.is_running, "need to call .start(t) first" - if delta: - t += self.t - assert t >= self.t, "can only forward time, not go backwards" - - while self.cur_elem.has_finished_by(t): - self._update_cur_elem_in_stats(self.t, self.cur_elem.t_end) - self.t = self.cur_elem.t_end - self._finish_element_in_stats() # takes current self.t into account + with self._cur_elem_mutex: + if not self.is_running: + raise ChannelNotRunningException() + assert self.is_running, "need to call .start(t) first" + if delta: + t += self.t + assert t >= self.t, "can only forward time, not go backwards" - # important to update stats before so the gap sampling takes the updated values into account - self._move_to_next_element() - - self._update_cur_elem_in_stats(self.t, t) - self.t = t - - self.stats.check_consistent() + while self.cur_elem.has_finished_by(t): + self._update_elem_in_stats(self.cur_elem, self.t, self.cur_elem.t_end) + self.t = self.cur_elem.t_end + self._finish_elem_in_stats(self.cur_elem) # takes current self.t into account + + # important to update stats before so the gap sampling takes the updated values into account + self.cur_elem = self._move_to_next_elem(self.cur_elem) + self._start_elem_in_stats(self.cur_elem) + + self._update_elem_in_stats(self.cur_elem, self.t, t) + self.t = t + + self.stats.check_consistent() ###################### functions that terminate the current element in the channel and replace it by another one ##################### @@ -274,7 +282,7 @@ def run_mux_scan(self, t_duration: float, _starting_up: bool=False) -> bool: Args: t_duration: duration of mux scan starting from current time - _starting_up: for internal use, used when the channel is started + _starting_up: for internal use, used when the channel is started, mutex should already be held Returns: whether a read was rejected @@ -285,40 +293,41 @@ def run_mux_scan(self, t_duration: float, _starting_up: bool=False) -> bool: if not self.is_running and not _starting_up: raise ChannelNotRunningException() - assert t_duration >= 0 - elem_to_restore = None - read_was_rejected = False - - if isinstance(self.cur_elem, ChunkedRead): - # stop active read immediately - self._write_read(end_reason=ReadEndReason.MUX_SCAN_STARTED) - read_was_rejected = True - elif isinstance(self.cur_elem, (UnblockDelay, ShortGap)): - # end immediately - self.cur_elem.t_end = self.t - elif isinstance(self.cur_elem, LongGap): - # split gap into two at t_split, i.e. set self to [t_start, t_split] and return a new [t_split, t_end] - # have mux scan refer to it - elem_to_restore = self.cur_elem.split(self.t) - elif isinstance(self.cur_elem, MuxScan): - # modify t_end of current mux scan, same element to restore - self.cur_elem.t_end = self.t + t_duration - return False # don't add MuxScan again - elif isinstance(self.cur_elem, (NoReadLeftGap, ChannelBroken)): - # don't do anything - return False - else: - # beginning of channel, no element yet - assert self.cur_elem is None and _starting_up, f"unknown element type {type(self.cur_elem).__name__}" + with self._cur_elem_mutex if not _starting_up else contextlib.nullcontext(): # starting up -> mutex already held + assert t_duration >= 0 + elem_to_restore = None + read_was_rejected = False - # cur_elem is None when called right after start - if self.cur_elem is not None: - self._finish_element_in_stats() - - self.cur_elem = MuxScan(self.t, t_duration=t_duration, elem_to_restore=elem_to_restore) - self._start_cur_element_in_stats() - - return read_was_rejected + if isinstance(self.cur_elem, ChunkedRead): + # stop active read immediately + self._write_read(self.cur_elem, end_reason=ReadEndReason.MUX_SCAN_STARTED) + read_was_rejected = True + elif isinstance(self.cur_elem, (UnblockDelay, ShortGap)): + # end immediately + self.cur_elem.t_end = self.t + elif isinstance(self.cur_elem, LongGap): + # split gap into two at t_split, i.e. set self to [t_start, t_split] and return a new [t_split, t_end] + # have mux scan refer to it + elem_to_restore = self.cur_elem.split(self.t) + elif isinstance(self.cur_elem, MuxScan): + # modify t_end of current mux scan, same element to restore + self.cur_elem.t_end = self.t + t_duration + return False # don't add MuxScan again + elif isinstance(self.cur_elem, (NoReadLeftGap, ChannelBroken)): + # don't do anything + return False + else: + # beginning of channel, no element yet + assert self.cur_elem is None and _starting_up, f"unknown element type {type(self.cur_elem).__name__}" + + # cur_elem is None when called right after start + if self.cur_elem is not None: + self._finish_elem_in_stats(self.cur_elem) + + self.cur_elem = MuxScan(self.t, t_duration=t_duration, elem_to_restore=elem_to_restore) + self._start_elem_in_stats(self.cur_elem) + + return read_was_rejected def has_active_mux_scan(self) -> bool: return isinstance(self.cur_elem, MuxScan) @@ -337,22 +346,24 @@ def unblock(self, unblock_duration=None, end_reason=ReadEndReason.UNBLOCKED, rea Returns: UnblockResponse """ - # add unblock delay - if not self._write_read(end_reason=end_reason, read_id=read_id): - # read was not finished - return UnblockResponse.MISSED - - self._finish_element_in_stats() - - if unblock_duration is None: - unblock_duration = self.sim_params.default_unblock_duration - assert isinstance(unblock_duration, (int, float)) - - self.cur_elem = UnblockDelay(self.t, unblock_duration, self.cur_elem) - self._start_cur_element_in_stats() - return UnblockResponse.UNBLOCKED + with self._cur_elem_mutex: + cur_elem = self.cur_elem # for thread-safety + # add unblock delay + if not self._write_read(self.cur_elem, end_reason=end_reason, read_id=read_id): + # read was missed + return UnblockResponse.MISSED + + self._finish_elem_in_stats(cur_elem) + + if unblock_duration is None: + unblock_duration = self.sim_params.default_unblock_duration + assert isinstance(unblock_duration, (int, float)) + + self.cur_elem = UnblockDelay(self.t, unblock_duration, cur_elem) + self._start_elem_in_stats(self.cur_elem) # pass in new element! + return UnblockResponse.UNBLOCKED - def _write_read(self, end_reason, read_id=None) -> bool: + def _write_read(self, elem, end_reason, read_id=None) -> bool: """ Finish the current read right now (possibly early) by writing it (without changing stats!) @@ -363,13 +374,13 @@ def _write_read(self, end_reason, read_id=None) -> bool: Returns: whether read was missed or not """ - if not isinstance(self.cur_elem, ChunkedRead) or (read_id is not None and self.cur_elem.full_read_id != read_id): + if not isinstance(elem, ChunkedRead) or (read_id is not None and elem.full_read_id != read_id): # read no longer the current read self.stats.reads.number_rejected_missed += 1 return False # write read up to current time t only (not necessarily full read) - seq_record = self.cur_elem.finish(self.t, end_reason=end_reason) + seq_record = elem.finish(self.t, end_reason=end_reason) seq_record.description += f" ch={self.name}" if DONT_WRITE_ZERO_LENGTH_READS and len(seq_record.seq) == 0: logger.debug(f"Read with id '{seq_record.id}' had length 0, not writing it") @@ -386,6 +397,8 @@ def stop_receiving(self, read_id=None) -> StoppedReceivingResponse: Stop receiving chunks from current read with read_id. If the channel is not running, it is logged as a missed action. + + This method should not be called in parallel from several threads (but can be called along with other methods). Args: read_id: read id of read to unblock; if None, current read @@ -393,94 +406,105 @@ def stop_receiving(self, read_id=None) -> StoppedReceivingResponse: Returns: True if read was stopped, False if read was not found (no longer current read) """ - if not isinstance(self.cur_elem, ChunkedRead) or (read_id is not None and self.cur_elem.full_read_id != read_id): - # read no longer the current read - self.stats.reads.number_stop_receiving_missed += 1 - return StoppedReceivingResponse.MISSED - - if self.cur_elem.stop_receiving(): - # only count if read was not already stopped - assert self.stats.reads.cur_number == 1 - self.stats.reads.cur_number_stop_receiving += 1 - return StoppedReceivingResponse.STOPPED_RECEIVING - else: - return StoppedReceivingResponse.ALREADY_STOPPED_RECEIVING + with self._cur_elem_mutex: # for updating stats + cur_elem = self.cur_elem # for thread-safety + if not isinstance(cur_elem, ChunkedRead) or (read_id is not None and cur_elem.full_read_id != read_id): + # read no longer the current read + self.stats.reads.number_stop_receiving_missed += 1 # only method writing to this field + return StoppedReceivingResponse.MISSED + + if cur_elem.stop_receiving(): + # only count if read was not already stopped + assert self.stats.reads.cur_number == 1 + self.stats.reads.cur_number_stop_receiving += 1 # only method writing to this field + return StoppedReceivingResponse.STOPPED_RECEIVING + else: + return StoppedReceivingResponse.ALREADY_STOPPED_RECEIVING - def get_new_chunks(self): + def get_new_samples(self): """ - Get concatenation of new chunks of the current read. + Get new samples of the current read. - If the read was not set to stop receiving, no new chunks are returned. + If the read was set to stop receiving, no new samples are returned. Returns: - Tuple of (concatenated_new_chunks, read_id, estimated_ref_len_so_far) - If the read was set to stop_receiving, concatenated_new_chunks is "" + Tuple of (samples, read_id, estimated_ref_len_so_far) + If the read was set to stop_receiving, samples is "" If no read is active (e.g. read gap, not running), it returns ("", None, None) """ - if not isinstance(self.cur_elem, ChunkedRead): + + # we are not acquiring a lock because this method will be called in parallel to "forward" + cur_elem = self.cur_elem # for thread-safety + if not isinstance(cur_elem, ChunkedRead): # also works if channel is not running return ("", None, None) - chunks, read_id, estimated_ref_len_so_far = self.cur_elem.get_new_chunks(self.t) - self.stats.reads.number_bps_requested += len(chunks) + chunks, read_id, estimated_ref_len_so_far = cur_elem.get_new_samples(self.t) + self.stats.reads.number_bps_requested += len(chunks) # only method writing to this field, todo: writing racing condition return (chunks, read_id, estimated_ref_len_so_far) ##################### Channel statistics ##################### + # They all take elem as an argument of elem to make clear what they are modifying. + # They also modify the stats, so a lock should be acquired. - def _get_cur_elem_in_stats(self): + def _get_stats_for_elem(self, elem): """ Returns object to modify in stats given current element """ - if isinstance(self.cur_elem, ShortGap): + if isinstance(elem, ShortGap): return self.stats.short_gaps - elif isinstance(self.cur_elem, LongGap): + elif isinstance(elem, LongGap): return self.stats.long_gaps - elif isinstance(self.cur_elem, ChannelBroken): + elif isinstance(elem, ChannelBroken): return self.stats.channel_broken - elif isinstance(self.cur_elem, MuxScan): + elif isinstance(elem, MuxScan): return self.stats.mux_scans - elif isinstance(self.cur_elem, UnblockDelay): + elif isinstance(elem, UnblockDelay): return self.stats.unblock_delays - elif isinstance(self.cur_elem, ChunkedRead): + elif isinstance(elem, ChunkedRead): return self.stats.reads else: - assert isinstance(self.cur_elem, NoReadLeftGap), f"Encountered unknown element of class {self.cur_elem.__class__}" + assert isinstance(elem, NoReadLeftGap), f"Encountered unknown element of class {elem.__class__}" return self.stats.no_reads_left - def _start_cur_element_in_stats(self): + def _start_elem_in_stats(self, elem): """ Start current element in stats """ - self._get_cur_elem_in_stats().start() + self._get_stats_for_elem(elem).start() - def _update_cur_elem_in_stats(self, t_from, t_to): + def _update_elem_in_stats(self, elem, t_from, t_to): """ Add time to current element in stats - + Args: t_from, t_to: time interval [t_from, t_to] to add for current element """ kwargs = {} - if isinstance(self.cur_elem, ChunkedRead): - kwargs["nb_new_bps"] = self.cur_elem.nb_basepairs(t_to) - self.cur_elem.nb_basepairs(t_from) + if isinstance(elem, ChunkedRead): + kwargs["nb_new_bps"] = elem.actual_seq_length(t_to) - elem.actual_seq_length(t_from) # not thread-safe - self._get_cur_elem_in_stats().add_time(t_to - t_from, **kwargs) + self._get_stats_for_elem(elem).add_time(t_to - t_from, **kwargs) - def _finish_element_in_stats(self): + def _finish_elem_in_stats(self, elem): """ Finish current element (possibly prematurely) in stats """ kwargs = {} - if isinstance(self.cur_elem, ChunkedRead): - kwargs["nb_bps_rejected"] = self.cur_elem.nb_basepairs_full() - self.cur_elem.nb_basepairs(self.t) - kwargs["stopped_receiving"] = self.cur_elem.stopped_receiving - self._get_cur_elem_in_stats().finish(**kwargs) + if isinstance(elem, ChunkedRead): + kwargs["nb_bps_rejected"] = elem.full_seq_length() - elem.actual_seq_length(self.t) + kwargs["stopped_receiving"] = elem.stopped_receiving + self._get_stats_for_elem(elem).finish(**kwargs) if self.save_elems: - self.finished_elems.append(self.cur_elem) + self.finished_elems.append(elem) def plot(self, *args, **kwargs): - """Plot channels, only plots elements recorded while save_elems was set to True""" + """ + Plot channels, only plots elements recorded while save_elems was set to True + + Not thread-safe + """ return plot_channels([self], *args, **kwargs) #%% @@ -535,12 +559,12 @@ def plot_channels(channels: List[Channel], time_interval=None, ax=None, **plot_a elif isinstance(elem, MuxScan): color = "purple" offset = -0.05 - elif isinstance(elem, NoReadLeft): + elif isinstance(elem, NoReadLeftGap): color = "grey" offset = 0.02 elif isinstance(elem, ChannelBroken): - color = "darkgrey" - offset = 0.02 + color = "blue" + offset = 0.01 else: raise TypeError(elem) t_end = elem.t_end @@ -575,7 +599,7 @@ def plot_channels(channels: List[Channel], time_interval=None, ax=None, **plot_a Line2D(existing_point, existing_point, color='red', lw=2, label='long gap'), Line2D(existing_point, existing_point, color='purple', lw=2, label='mux scan'), Line2D(existing_point, existing_point, color='grey', lw=2, label='no read left'), - Line2D(existing_point, existing_point, color='darkgrey', lw=2, label='broken channel'), + Line2D(existing_point, existing_point, color='blue', lw=2, label='broken channel'), Line2D(existing_point, existing_point, color='black', lw=2, label='current time', linestyle="dotted"), ] ax.legend(handles=legend_elements, loc='center right') diff --git a/src/simreaduntil/simulator/channel_element.py b/src/simreaduntil/simulator/channel_element.py index 62cb96f..39a3451 100644 --- a/src/simreaduntil/simulator/channel_element.py +++ b/src/simreaduntil/simulator/channel_element.py @@ -6,6 +6,7 @@ import enum from numbers import Number from functools import cached_property +from threading import Lock from typing import Any, List, Optional, Tuple, Union from Bio import SeqIO from Bio.Seq import Seq @@ -170,7 +171,7 @@ def estimate_ref_len(orig_ref_len, orig_seq_len, new_seq_len): # StrEnum does not exist yet in Python3.8, see PythonDoc for IntEnum for this recipe # allows printing as "field" instead of "class.field", where class is a class derived from enum class ReadEndReason(str, enum.Enum): - """Reason why a read ended""" + """Reason why a read ended, stop receiving is not part of it""" # read ended prematurely SIM_STOPPED = "sim_stopped_unblocked" # simulation was stopped while read was still in-progress @@ -191,7 +192,7 @@ class ReadTags(str, enum.Enum): """ Tags to attach to a read, multiple are possible! """ - RU_NEVER_REQUESTED = "never_requested" # never requested by ReadUntil + RU_NEVER_REQUESTED = "never_requested" # never returned data (may have been requested, but data length below chunk size) RU_STOPPED_RECEIVING = "stopped_receiving" # read was set to stop_receiving class ChunkedRead(ChannelElement): @@ -201,8 +202,8 @@ class ChunkedRead(ChannelElement): Chunks can be received from the read, it can be ended prematurely with .finish(), the sequence record or sequence summary record can be retrieved. If the read has a NanoSim read id, its id is modified to reflect the estimated reference length if it is ended prematurely. - A read with n basepairs starts at t_start + t_delay, goes to time n*dt and the ith basepair (i>=1) is read after time i*dt, where dt=1/bp_per_second. - Basepairs are returned in chunks of size chunk_size. + A read with n basepairs/signals starts at t_start + t_delay, goes to time n*dt and the ith basepair (i>=1) is read after time i*dt, where dt=1/bp_per_second. + Basepairs/signals are returned in chunks of size at least min_chunk_size. t_start, t_delay, t_end, t_duration should not be modified once this class was instantiated. A read can be terminated early by calling .finish_now(). @@ -210,16 +211,16 @@ class ChunkedRead(ChannelElement): read_id: id of read seq: read sequence t_start: time at which the read starts - read_speed: speed at which the read is read, in basepairs per second, defaults to SIM_PARAMS.bp_per_second - chunk_size: size of chunks that .get_new_chunks() returns, defaults to SIM_PARAMS.chunk_size - t_delay: delay before read starts (template_start - read_start), 0 basepairs are read during this delay, end time is shifted accordingly + read_speed: speed at which the read is read, in basepairs/signals per second, defaults to SIM_PARAMS.bp_per_second + min_chunk_size: minimum size of chunks that .get_new_chunks() returns, defaults to SIM_PARAMS.min_chunk_size + t_delay: delay before read starts (template_start - read_start), 0 basepairs/signals are read during this delay, end time is shifted accordingly """ - def __init__(self, read_id: str, seq: str, t_start: Number, read_speed: Number=None, chunk_size: Number=None, t_delay:float = 0): + def __init__(self, read_id: str, seq: str, t_start: Number, read_speed: Number=None, min_chunk_size: Number=None, t_delay: float = 0): # copy params in case they change while the read is in-progress assert read_speed > 0 - assert chunk_size > 0 + assert min_chunk_size > 0 self._read_speed = read_speed - self._chunk_size = chunk_size + self._min_chunk_size = min_chunk_size super().__init__(t_start, len(seq) / self._read_speed + t_delay) self.full_read_id = read_id @@ -228,6 +229,7 @@ def __init__(self, read_id: str, seq: str, t_start: Number, read_speed: Number=N assert t_delay >= 0 self._t_delay = t_delay + # self._ref_len: length of sequence on reference sequence, seq can be shorter/longer due to indels, constant if NanoSimId.is_valid(read_id): # whether the read id is from NanoSim -> read id will be changed when read is terminated early self._nanosim_read_id = NanoSimId.from_str(read_id) @@ -238,65 +240,77 @@ def __init__(self, read_id: str, seq: str, t_start: Number, read_speed: Number=N self._ref_len = len(self._full_seq) self.stopped_receiving = False # whether to receive chunks from the read - self._next_chunk_idx = 0 # start idx of next chunks to return - self.end_reason = None # action used to finish read + self._num_samples_returned = 0 # number of samples returned so far + self.end_reason : Optional[ReadEndReason] = None # action used to finish read (excluding stop receiving!) + + # lock to ensure get_new_samples is not called in parallel, other methods reading from self._num_samples_returned + # can only assume the value is valid, but it may have changed when accessing it again, see + # https://docs.python.org/3/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe + # this applies to self.end_reason, self._num_samples_returned, self.stopped_receiving + self._get_new_samples_lock = Lock() def __repr__(self): - return f"ChunkedRead '{self.full_read_id}': '{self._full_seq}' between [{self.t_start}, {self.t_end}], num chunks returned: {self._next_chunk_idx}, end_reason: {self.end_reason}" + return f"ChunkedRead '{self.full_read_id}': '{self._full_seq}' between [{self.t_start}, {self.t_end}], num samples returned: {self._num_samples_returned}, end_reason: {self.end_reason}" def __eq__(self, other) -> bool: assert isinstance(other, ChunkedRead) return self.t_start == other.t_start and self.t_duration == other.t_duration \ and self._t_delay == other._t_delay \ and self.full_read_id == other.full_read_id and self._full_seq == other._full_seq and self._read_speed == other._read_speed \ - and self._chunk_size == other._chunk_size and self.stopped_receiving == other.stopped_receiving \ - and self._next_chunk_idx == other._next_chunk_idx and self.end_reason == other.end_reason - - @property - def _nb_chunks(self): - """Number of chunks the read is divided into""" - return (len(self._full_seq) + self._chunk_size - 1) // self._chunk_size # round up + and self._min_chunk_size == other._min_chunk_size and self.stopped_receiving == other.stopped_receiving \ + and self._num_samples_returned == other._num_samples_returned and self.end_reason == other.end_reason def full_duration(self) -> Number: return self._t_delay + len(self._full_seq) / self._read_speed + def full_seq_length(self): + """number of basepairs/signals of full read""" + return len(self._full_seq) + + def num_samples_returned(self): + """Number of basepairs/signals that were returned so far (if stopped receiving or if not getting chunks)""" + return self._num_samples_returned + @property - def _has_received_chunks(self): - """Whether at least one non-empty chunk was returned (via get_new_chunks)""" - return self._next_chunk_idx > 0 + def _has_received_data(self): + """Whether at least one new sample was returned (via get_new_samples)""" + return self._num_samples_returned > 0 - def all_chunks_consumed(self) -> bool: - """Whether all chunks have been consumed, i.e. read has finished""" - return self._next_chunk_idx >= self._nb_chunks + def all_samples_consumed(self) -> bool: + """Whether read has been fully read (so also has finished)""" + return self._num_samples_returned >= self.full_seq_length() - @cached_property # lazy - def _chunk_end_positions(self): + def _estimate_ref_len(self, read_seq_length): """ - End positions of the chunks (cumulative chunk lengths), exclusive + Estimate reference length given number of basepairs/signals read, not exact due to indels - cached because they may not be needed if the simulation passes over the read (because time forwarded a lot). - """ - cum_lens = [(i+1)*self._chunk_size for i in range(self._nb_chunks)] - cum_lens[-1] = len(self._full_seq) - return cum_lens - - def _get_chunks(self, fr: int, to: int): - """Get concatenated chunks [from, to)""" - return self._full_seq[fr * self._chunk_size:to * self._chunk_size] - - def _estimate_ref_len(self, nb_bps_read): - """ - Estimate reference length given number of basepairs read, not exact due to indels + If it is a NanoSim read, it also removes the head or tail length Requires correct estimation of ref length of full read (which is available for NanoSim eads) """ # round rather than round down (with int()) - assert nb_bps_read <= len(self._full_seq) - return estimate_ref_len(orig_ref_len=self._ref_len, orig_seq_len=len(self._full_seq), new_seq_len=nb_bps_read) + full_len = self.full_seq_length() + assert read_seq_length <= full_len + if self._nanosim_read_id: + # remove head and tail length + head_len, tail_len = self._nanosim_read_id.head_len, self._nanosim_read_id.tail_len + assert head_len + tail_len <= full_len + if self._nanosim_read_id.direction == "R": + # swap + head_len, tail_len = tail_len, head_len + full_len_no_flanking = full_len - (head_len + tail_len) + # constrain (read_seq_length - head_len) to interval [0, full_len_no_flanking] + read_seq_length = min( + max(0, read_seq_length - head_len), + full_len_no_flanking + ) + else: + full_len_no_flanking = full_len + return estimate_ref_len(orig_ref_len=self._ref_len, orig_seq_len=full_len_no_flanking, new_seq_len=read_seq_length) - def nb_basepairs(self, t: Number): + def actual_seq_length(self, t: Number): """ - Number of basepairs of read up to time t, first basepair emitted at time t_start + Number of basepairs/signals of read up to time t, first basepair emitted at time t_start If .has_finished_by(t) returns True and the full read was read or .finish() not yet called, it is guaranteed to return the full length of the @@ -305,22 +319,14 @@ def nb_basepairs(self, t: Number): real_start = self.t_start + self._t_delay if t < real_start: return 0 - if (self.has_finished_by(t) and self.end_reason in [None, ReadEndReason.READ_ENDED_NORMALLY]): - # special case due to floating point problem with addition + if self.has_finished_by(t) and self.end_reason in [None, ReadEndReason.READ_ENDED_NORMALLY]: + # special case due to floating point precision loss with addition, when read ended normally, make sure to return full length when t>=t_end return len(self._full_seq) return min( len(self._full_seq), int((min(t, self.t_end) - real_start) * self._read_speed) # round down ) - def nb_basepairs_full(self): - """number of basepairs of full read""" - return len(self._full_seq) - - def nb_basepairs_returned(self): - """Number of basepairs not yet returned (if stopped receiving or if not getting chunks)""" - return min(len(self._full_seq), self._next_chunk_idx * self._chunk_size) - def finish(self, t=None, end_reason: Optional[ReadEndReason]=None) -> SeqIO.SeqRecord: """ Finish read by time t, updating t_end @@ -329,18 +335,17 @@ def finish(self, t=None, end_reason: Optional[ReadEndReason]=None) -> SeqIO.SeqR Arguments: t: time when read ends, read is ended prematurely if less than full read end; full read if None or t exceeds full read end - end_reason: action that caused read to be written to file + end_reason: action that caused read to finish, must be 'valid' if t is not None and before end of full read Returns: SeqRecord of read that can be written to fasta file """ - assert self.end_reason is None, f"already ended read with action {self.end_reason}" + assert self.end_reason is None, f"trying to end with {end_reason}, but already ended read with action {self.end_reason}" if t is not None: if not self.has_finished_by(t): assert end_reason in [ReadEndReason.UNBLOCKED, ReadEndReason.MUX_SCAN_STARTED, ReadEndReason.SIM_STOPPED], "end reason must be set" - nb_bps_returned = self.nb_basepairs_returned() - assert t >= self.t_start + self._t_delay * (nb_bps_returned > 0) + nb_bps_returned / self._read_speed, "cannot finish earlier than last returned chunk" + assert t >= self.t_start + self._t_delay * (self._num_samples_returned > 0) + self._num_samples_returned / self._read_speed, "cannot finish earlier than last returned chunk" self.t_end = min(self.t_end, t) # t_end contains end time of read now @@ -366,10 +371,10 @@ def get_seq_record(self): if self.end_reason == ReadEndReason.READ_ENDED_NORMALLY: seq = self._full_seq else: - seq = self._full_seq[:self.nb_basepairs(self.t_end)] + seq = self._full_seq[:self.actual_seq_length(self.t_end)] if self._nanosim_read_id is not None and (len(seq) < len(self._full_seq)): # adapt reference length, as read was stopped early - actual_ref_len = self._estimate_ref_len(self.nb_basepairs(self.t_end)) + actual_ref_len = self._estimate_ref_len(self.actual_seq_length(self.t_end)) self._nanosim_read_id.change_ref_len(actual_ref_len) # if this method is called again, the read id will not change again because the ref len is the same adapted_read_id = str(self._nanosim_read_id) @@ -377,13 +382,17 @@ def get_seq_record(self): read_tags = [] if self.stopped_receiving: read_tags.append(ReadTags.RU_STOPPED_RECEIVING) - if not self._has_received_chunks: + if not self._has_received_data: read_tags.append(ReadTags.RU_NEVER_REQUESTED) # append full sequence length (in case read was unblocked) - description = f"full_seqlen={self.nb_basepairs_full()} t_start={self.t_start} t_end={self.t_end} t_delay={self._t_delay} ended={self.end_reason} tags={','.join(read_tags)} full_read_id={self.full_read_id}" - return SeqIO.SeqRecord(Seq(seq), id=adapted_read_id, description=description) + description = f"full_seqlen={self.full_seq_length()} t_start={self.t_start} t_end={self.t_end} t_delay={self._t_delay} ended={self.end_reason} tags={','.join(read_tags)} full_read_id={self.full_read_id}" + return SeqIO.SeqRecord(Seq(seq if self.is_nucleotide_seq(seq) else ""), id=adapted_read_id, description=description) + @staticmethod + def is_nucleotide_seq(seq): + """Whether the read is a nucleotide sequence""" + return isinstance(seq, str) SEQ_SUMMARY_HEADER = ["read_id", "channel", "mux", "start_time", "duration", "passes_filtering", "template_start", "template_duration", "sequence_length_template", "end_reason"] def get_seq_summary_record(self) -> Optional[List[str]]: @@ -391,19 +400,19 @@ def get_seq_summary_record(self) -> Optional[List[str]]: Get list of entries for sequence summary file, see SEQ_SUMMARY_HEADER for field names Returns: - list of string entries, or None if read has no basepairs (e.g. due to delay) + list of string entries, or None if read has no basepairs/signals (e.g. due to delay) """ # read_id, channel, mux, start_time, duration, passes_filtering, template_start, template_duration, sequence_length_template, end_reason mux = 1 passes_filtering = "True" template_duration = self.t_duration - self._t_delay - nb_bps_read = self.nb_basepairs(self.t_end) - if nb_bps_read <= 0: + read_seq_length = self.actual_seq_length(self.t_end) + if read_seq_length <= 0: return None return [ self.full_read_id, self.channel, mux, self.t_start, self.t_duration, passes_filtering, self.t_start + self._t_delay, template_duration, - nb_bps_read, self.end_reason + read_seq_length, self.end_reason ] def stop_receiving(self, value=True) -> bool: @@ -422,29 +431,33 @@ def stop_receiving(self, value=True) -> bool: self.stopped_receiving = value return True - def get_new_chunks(self, t: Number) -> Tuple[str, str, Optional[int]]: + def get_new_samples(self, t: Number) -> Tuple[str, str, Optional[int]]: """ - Get new read chunks up to time <= t, only new data since last call to this function + Get new read samples up to time <= t, only new data since last call to this function + + Implicitly forwards to time t - Implicitly forwards to time t (choosing chunk index of chunk containing t) + Still works when other methods are called in parallel (e.g. finish, stop_receiving), + though be careful about interpreting the results if it is finished before the time t provided here Returns: - (all chunks concatenated, read_id, estimated_ref_len_so_far). + (samples, read_id, estimated_ref_len_so_far). - Empty chunks "" if stop_receiving is True - estimated_ref_len_so_far is the estimated number of basepairs covered by chunks returned so far - """ - assert self.end_reason is None, f"already ended read with action {self.end_reason}" - - if self.stopped_receiving: - return "", self.full_read_id, self._estimate_ref_len(self.nb_basepairs_returned()) - - # choose chunk index of chunk containing t - next_chunk_idx = np.searchsorted(self._chunk_end_positions, v=self.nb_basepairs(t), side='right') # index such that a[i-1] <= v < a[i] - old_next_chunk_idx = self._next_chunk_idx - self._next_chunk_idx = next_chunk_idx - estimated_ref_len_so_far = self._estimate_ref_len(self.nb_basepairs_returned()) # takes into account _next_chunk_idx - return self._get_chunks(old_next_chunk_idx, self._next_chunk_idx), self.full_read_id, estimated_ref_len_so_far + Empty samples "" if stop_receiving is True + estimated_ref_len_so_far is the estimated number of basepairs/signals covered by samples returned so far + """ + if self.stopped_receiving or self.end_reason is not None: # may be set in parallel + return "", self.full_read_id, self._estimate_ref_len(self._num_samples_returned) + + with self._get_new_samples_lock: + old_num_samples_returned = self._num_samples_returned + new_num_samples_returned = self.actual_seq_length(t) # depends on self.end_reason which may be set in parallel, this is okay + if new_num_samples_returned < old_num_samples_returned + self._min_chunk_size: + return "", self.full_read_id, self._estimate_ref_len(self._num_samples_returned) + + self._num_samples_returned = new_num_samples_returned + estimated_ref_len_so_far = self._estimate_ref_len(new_num_samples_returned) # takes into account _next_chunk_idx + return self._full_seq[old_num_samples_returned:new_num_samples_returned], self.full_read_id, estimated_ref_len_so_far class ReadDescriptionParser: diff --git a/src/simreaduntil/simulator/channel_stats.py b/src/simreaduntil/simulator/channel_stats.py index 3410590..a9e8fa2 100644 --- a/src/simreaduntil/simulator/channel_stats.py +++ b/src/simreaduntil/simulator/channel_stats.py @@ -367,6 +367,7 @@ def channel_stats_to_df(channel_stats: List[ChannelStats]): df = pd.DataFrame( [( + channel, channel.short_gaps.finished_number, channel.short_gaps.time_spent, channel.long_gaps.finished_number, channel.long_gaps.time_spent, channel.unblock_delays.finished_number, channel.unblock_delays.time_spent, @@ -377,6 +378,7 @@ def channel_stats_to_df(channel_stats: List[ChannelStats]): channel.reads.fin_number_rejected, channel.reads.number_rejected_missed, channel.reads.number_stop_receiving_missed, channel.no_reads_left.finished_number, channel.no_reads_left.time_spent ) for channel in channel_stats], columns=[ + 'channel', 'short_gaps_finished_number', 'short_gaps_time_spent', 'long_gaps_finished_number', 'long_gaps_time_spent', 'unblock_delays_finished_number', 'unblock_delays_time_spent', 'mux_scans_finished_number', 'mux_scans_time_spent', 'channel_broken_finished_number', 'channel_broken_time_spent', 'reads_finished_number', 'reads_time_spent', 'reads_number_bps_requested', 'reads_number_bps_read', 'reads_number_bps_rejected', @@ -389,7 +391,7 @@ def channel_stats_to_df(channel_stats: List[ChannelStats]): return df def plot_read_stats_per_channel(df, save_dir=None): - """Plot stats about reads per channel""" + """Plot stats about reads per channel, df coming from channel_stats_to_df""" df_reads = df[[col for col in df.columns if col.startswith("reads_")]].copy() df_reads["reads_number_bps_notrequested"] = df_reads["reads_number_bps_read"] - df_reads["reads_number_bps_requested"] # silently read, but never requested by ReadUntil diff --git a/src/simreaduntil/simulator/gap_sampling/constant_gaps_until_blocked.py b/src/simreaduntil/simulator/gap_sampling/constant_gaps_until_blocked.py index 567f5bb..da02153 100644 --- a/src/simreaduntil/simulator/gap_sampling/constant_gaps_until_blocked.py +++ b/src/simreaduntil/simulator/gap_sampling/constant_gaps_until_blocked.py @@ -18,6 +18,9 @@ class ConstantGapsUntilBlocked(GapSampler): """ Chooses short and long gaps of constant length, where a long gap is chosen with some probability, until the channel is blocked. + + Called constant_gaps in the paper + Args: short_gap_length: length of short gaps long_gap_length: length of long gaps diff --git a/src/simreaduntil/simulator/gap_sampling/gap_sampler_per_window_until_blocked.py b/src/simreaduntil/simulator/gap_sampling/gap_sampler_per_window_until_blocked.py index 21c1a79..b5db4e5 100644 --- a/src/simreaduntil/simulator/gap_sampling/gap_sampler_per_window_until_blocked.py +++ b/src/simreaduntil/simulator/gap_sampling/gap_sampler_per_window_until_blocked.py @@ -44,6 +44,8 @@ class GapSamplerPerWindowUntilBlocked(GapSampler): """ Gap sampler that has separate sample distributions per time window and eventually blocks. + Called window_all_channels in the paper + Args: short_gaps_per_window: array of short gaps for each time window long_gaps_per_window: array of long gaps for each time window @@ -101,7 +103,7 @@ def from_seqsum_df(cls, seqsum_df, read_delay=None, time_and_aggregation_windows read_delay: delay between read starting and first bp being read; if None, compute median read delay time_and_aggregation_windows: tuple of array of time windows (window_start, window_end) and array of data aggregation windows (window_start, window_end); - if None, use windows with 50% overlap, i.e. [t, t+4] window with data from [t-2, t+6] + if None, use 4h windows with 50% overlap, i.e. [t, t+4] window with data from [t-2, t+6] Returns: function to create a gap sampler, so it is flexible with respect to the number of channels diff --git a/src/simreaduntil/simulator/gap_sampling/gap_sampling.py b/src/simreaduntil/simulator/gap_sampling/gap_sampling.py index 502fbf4..3b343e1 100644 --- a/src/simreaduntil/simulator/gap_sampling/gap_sampling.py +++ b/src/simreaduntil/simulator/gap_sampling/gap_sampling.py @@ -107,6 +107,8 @@ class RandomGapSampler(GapSampler): Gap sampler with random gap lengths, for testing mostly Channel breaks with some probability + + Called random_gaps in the paper """ def __init__(self, prob_long_gap=0.5) -> None: super().__init__() diff --git a/src/simreaduntil/simulator/gap_sampling/inactive_active_gaps_replication.py b/src/simreaduntil/simulator/gap_sampling/inactive_active_gaps_replication.py index f35986d..985d728 100644 --- a/src/simreaduntil/simulator/gap_sampling/inactive_active_gaps_replication.py +++ b/src/simreaduntil/simulator/gap_sampling/inactive_active_gaps_replication.py @@ -213,6 +213,8 @@ class SingleChannelInactiveActiveReplicator(GapSampler): Whenever an inactive period finishes, you must call mark_long_gap_end. This moves to the next (active) period. Within an active period, the short gaps are recycled from the observed short gaps within the active period. It ignores the time spent in mux scans. + + Called gap_replication in the paper """ def __init__(self, inactive_active_periods_tracker: ChannelInactiveActivePeriodsTracker, read_delay) -> None: super().__init__() diff --git a/src/simreaduntil/simulator/gap_sampling/rolling_window_gap_sampler.py b/src/simreaduntil/simulator/gap_sampling/rolling_window_gap_sampler.py index f1bb184..59a6690 100644 --- a/src/simreaduntil/simulator/gap_sampling/rolling_window_gap_sampler.py +++ b/src/simreaduntil/simulator/gap_sampling/rolling_window_gap_sampler.py @@ -16,7 +16,6 @@ class RollingWindowGapSampler(GapSampler): """ - Rolling window gap sampler. Takes into account the whole gaps, so it mixes channels. Args: gaps_df: pd.DataFrame with columns "gap_start", "gap_end", "gap_duration", "gap_type" (long or short) @@ -56,6 +55,7 @@ def modify_args(*, gaps_df, **kwargs): @classmethod def from_seqsum_df(cls, seqsum_df, read_delay=None, long_gap_threshold=None, window_width=None): + """mixes gaps from all channels""" if read_delay is None: read_delay = compute_median_read_delay(seqsum_df) @@ -145,6 +145,8 @@ class RollingWindowGapSamplerPerChannel: For each channel, it samples the corresponding channel in the originak dataset + Called rolling_window_per_channel in the paper + """ def __init__(self): pass diff --git a/src/simreaduntil/simulator/protos/ont_device.proto b/src/simreaduntil/simulator/protos/ont_device.proto index 85d2cef..5ea795a 100644 --- a/src/simreaduntil/simulator/protos/ont_device.proto +++ b/src/simreaduntil/simulator/protos/ont_device.proto @@ -15,7 +15,7 @@ service ONTDevice { rpc GetMKRunDir(EmptyRequest) returns (MKRunDirResponse) {} // request actions to perform on channels - rpc PerformActions(ReadActionsRequest) returns (ActionResultImmediateResponse) {} + rpc PerformActions(ReadActionsRequest) returns (EmptyResponse) {} // get new chunks rpc GetBasecalledChunks(BasecalledChunksRequest) returns (stream BasecalledReadChunkResponse) {} // get action results (only those that were not yet received) @@ -25,7 +25,7 @@ service ONTDevice { rpc StartSim(StartRequest) returns (BoolResponse) {} // stop simulation, returns whether it succeeded (i.e. if simulation was running) rpc StopSim(EmptyRequest) returns (BoolResponse) {} - rpc RunMuxScan(RunMuxScanRequest) returns (MuxScanStartedInfo) {} + rpc RunMuxScan(RunMuxScanRequest) returns (RunMuxScanResponse) {} // whether simulation is running rpc IsRunning(EmptyRequest) returns (BoolResponse) {} @@ -69,10 +69,6 @@ message ReadActionsRequest { repeated Action actions = 1; } -message ActionResultImmediateResponse { - repeated bool succeeded = 1; -} - message ActionResultsRequest { bool clear = 1; // whether to clear the action results after getting them } @@ -100,7 +96,7 @@ message RunMuxScanRequest { double t_duration = 1; } -message MuxScanStartedInfo { +message RunMuxScanResponse { uint32 nb_reads_rejected = 1; } diff --git a/src/simreaduntil/simulator/protos_generated/ont_device_pb2.py b/src/simreaduntil/simulator/protos_generated/ont_device_pb2.py index 46bc1dc..b064db8 100644 --- a/src/simreaduntil/simulator/protos_generated/ont_device_pb2.py +++ b/src/simreaduntil/simulator/protos_generated/ont_device_pb2.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: ont_device.proto +# Protobuf Python Version: 4.25.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -13,13 +14,12 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10ont_device.proto\x12\tontdevice\"\x0e\n\x0c\x45mptyRequest\"\x0f\n\rEmptyResponse\"\'\n\x12ServerInfoResponse\x12\x11\n\tunique_id\x18\x01 \x01(\t\"&\n\x10MKRunDirResponse\x12\x12\n\nmk_run_dir\x18\x01 \x01(\t\"\xe2\x02\n\x12ReadActionsRequest\x12\x35\n\x07\x61\x63tions\x18\x01 \x03(\x0b\x32$.ontdevice.ReadActionsRequest.Action\x1a\x94\x02\n\x06\x41\x63tion\x12\x0f\n\x07\x63hannel\x18\x01 \x01(\r\x12\x0f\n\x07read_id\x18\x02 \x01(\t\x12\x45\n\x07unblock\x18\x03 \x01(\x0b\x32\x32.ontdevice.ReadActionsRequest.Action.UnblockActionH\x00\x12U\n\x11stop_further_data\x18\x04 \x01(\x0b\x32\x38.ontdevice.ReadActionsRequest.Action.StopReceivingActionH\x00\x1a\x15\n\x13StopReceivingAction\x1a)\n\rUnblockAction\x12\x18\n\x10unblock_duration\x18\x01 \x01(\x01\x42\x08\n\x06\x61\x63tion\"2\n\x1d\x41\x63tionResultImmediateResponse\x12\x11\n\tsucceeded\x18\x01 \x03(\x08\"%\n\x14\x41\x63tionResultsRequest\x12\r\n\x05\x63lear\x18\x01 \x01(\x08\"k\n\x14\x41\x63tionResultResponse\x12\x0f\n\x07read_id\x18\x01 \x01(\t\x12\x0c\n\x04time\x18\x02 \x01(\x01\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\r\x12\x13\n\x0b\x61\x63tion_type\x18\x04 \x01(\r\x12\x0e\n\x06result\x18\x05 \x01(\r\"r\n\x0cStartRequest\x12\x1b\n\x13\x61\x63\x63\x65leration_factor\x18\x01 \x01(\x01\x12\x15\n\rupdate_method\x18\x02 \x01(\t\x12\x14\n\x0clog_interval\x18\x03 \x01(\r\x12\x18\n\x10stop_if_no_reads\x18\x04 \x01(\x08\"\'\n\x11RunMuxScanRequest\x12\x12\n\nt_duration\x18\x01 \x01(\x01\"/\n\x12MuxScanStartedInfo\x12\x19\n\x11nb_reads_rejected\x18\x01 \x01(\r\"\xad\x01\n\x17\x42\x61secalledChunksRequest\x12\x17\n\nbatch_size\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x42\n\x08\x63hannels\x18\x03 \x01(\x0b\x32+.ontdevice.BasecalledChunksRequest.ChannelsH\x01\x88\x01\x01\x1a\x19\n\x08\x43hannels\x12\r\n\x05value\x18\x02 \x03(\rB\r\n\x0b_batch_sizeB\x0b\n\t_channels\"\x83\x01\n\x1b\x42\x61secalledReadChunkResponse\x12\x0f\n\x07\x63hannel\x18\x01 \x01(\r\x12\x0f\n\x07read_id\x18\x02 \x01(\t\x12\x0b\n\x03seq\x18\x03 \x01(\t\x12\x13\n\x0bquality_seq\x18\x04 \x01(\t\x12 \n\x18\x65stimated_ref_len_so_far\x18\x05 \x01(\r\"\x1d\n\x0c\x42oolResponse\x12\r\n\x05value\x18\x01 \x01(\x08\"6\n\x12\x44\x65viceInfoResponse\x12\x0c\n\x04info\x18\x01 \x01(\t\x12\x12\n\nn_channels\x18\x02 \x01(\r2\x93\x06\n\tONTDevice\x12I\n\rGetServerInfo\x12\x17.ontdevice.EmptyRequest\x1a\x1d.ontdevice.ServerInfoResponse\"\x00\x12\x45\n\x0bGetMKRunDir\x12\x17.ontdevice.EmptyRequest\x1a\x1b.ontdevice.MKRunDirResponse\"\x00\x12[\n\x0ePerformActions\x12\x1d.ontdevice.ReadActionsRequest\x1a(.ontdevice.ActionResultImmediateResponse\"\x00\x12\x65\n\x13GetBasecalledChunks\x12\".ontdevice.BasecalledChunksRequest\x1a&.ontdevice.BasecalledReadChunkResponse\"\x00\x30\x01\x12X\n\x10GetActionResults\x12\x1f.ontdevice.ActionResultsRequest\x1a\x1f.ontdevice.ActionResultResponse\"\x00\x30\x01\x12>\n\x08StartSim\x12\x17.ontdevice.StartRequest\x1a\x17.ontdevice.BoolResponse\"\x00\x12=\n\x07StopSim\x12\x17.ontdevice.EmptyRequest\x1a\x17.ontdevice.BoolResponse\"\x00\x12K\n\nRunMuxScan\x12\x1c.ontdevice.RunMuxScanRequest\x1a\x1d.ontdevice.MuxScanStartedInfo\"\x00\x12?\n\tIsRunning\x12\x17.ontdevice.EmptyRequest\x1a\x17.ontdevice.BoolResponse\"\x00\x12I\n\rGetDeviceInfo\x12\x17.ontdevice.EmptyRequest\x1a\x1d.ontdevice.DeviceInfoResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10ont_device.proto\x12\tontdevice\"\x0e\n\x0c\x45mptyRequest\"\x0f\n\rEmptyResponse\"\'\n\x12ServerInfoResponse\x12\x11\n\tunique_id\x18\x01 \x01(\t\"&\n\x10MKRunDirResponse\x12\x12\n\nmk_run_dir\x18\x01 \x01(\t\"\xe2\x02\n\x12ReadActionsRequest\x12\x35\n\x07\x61\x63tions\x18\x01 \x03(\x0b\x32$.ontdevice.ReadActionsRequest.Action\x1a\x94\x02\n\x06\x41\x63tion\x12\x0f\n\x07\x63hannel\x18\x01 \x01(\r\x12\x0f\n\x07read_id\x18\x02 \x01(\t\x12\x45\n\x07unblock\x18\x03 \x01(\x0b\x32\x32.ontdevice.ReadActionsRequest.Action.UnblockActionH\x00\x12U\n\x11stop_further_data\x18\x04 \x01(\x0b\x32\x38.ontdevice.ReadActionsRequest.Action.StopReceivingActionH\x00\x1a\x15\n\x13StopReceivingAction\x1a)\n\rUnblockAction\x12\x18\n\x10unblock_duration\x18\x01 \x01(\x01\x42\x08\n\x06\x61\x63tion\"%\n\x14\x41\x63tionResultsRequest\x12\r\n\x05\x63lear\x18\x01 \x01(\x08\"k\n\x14\x41\x63tionResultResponse\x12\x0f\n\x07read_id\x18\x01 \x01(\t\x12\x0c\n\x04time\x18\x02 \x01(\x01\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\r\x12\x13\n\x0b\x61\x63tion_type\x18\x04 \x01(\r\x12\x0e\n\x06result\x18\x05 \x01(\r\"r\n\x0cStartRequest\x12\x1b\n\x13\x61\x63\x63\x65leration_factor\x18\x01 \x01(\x01\x12\x15\n\rupdate_method\x18\x02 \x01(\t\x12\x14\n\x0clog_interval\x18\x03 \x01(\r\x12\x18\n\x10stop_if_no_reads\x18\x04 \x01(\x08\"\'\n\x11RunMuxScanRequest\x12\x12\n\nt_duration\x18\x01 \x01(\x01\"/\n\x12RunMuxScanResponse\x12\x19\n\x11nb_reads_rejected\x18\x01 \x01(\r\"\xad\x01\n\x17\x42\x61secalledChunksRequest\x12\x17\n\nbatch_size\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x42\n\x08\x63hannels\x18\x03 \x01(\x0b\x32+.ontdevice.BasecalledChunksRequest.ChannelsH\x01\x88\x01\x01\x1a\x19\n\x08\x43hannels\x12\r\n\x05value\x18\x02 \x03(\rB\r\n\x0b_batch_sizeB\x0b\n\t_channels\"\x83\x01\n\x1b\x42\x61secalledReadChunkResponse\x12\x0f\n\x07\x63hannel\x18\x01 \x01(\r\x12\x0f\n\x07read_id\x18\x02 \x01(\t\x12\x0b\n\x03seq\x18\x03 \x01(\t\x12\x13\n\x0bquality_seq\x18\x04 \x01(\t\x12 \n\x18\x65stimated_ref_len_so_far\x18\x05 \x01(\r\"\x1d\n\x0c\x42oolResponse\x12\r\n\x05value\x18\x01 \x01(\x08\"6\n\x12\x44\x65viceInfoResponse\x12\x0c\n\x04info\x18\x01 \x01(\t\x12\x12\n\nn_channels\x18\x02 \x01(\r2\x83\x06\n\tONTDevice\x12I\n\rGetServerInfo\x12\x17.ontdevice.EmptyRequest\x1a\x1d.ontdevice.ServerInfoResponse\"\x00\x12\x45\n\x0bGetMKRunDir\x12\x17.ontdevice.EmptyRequest\x1a\x1b.ontdevice.MKRunDirResponse\"\x00\x12K\n\x0ePerformActions\x12\x1d.ontdevice.ReadActionsRequest\x1a\x18.ontdevice.EmptyResponse\"\x00\x12\x65\n\x13GetBasecalledChunks\x12\".ontdevice.BasecalledChunksRequest\x1a&.ontdevice.BasecalledReadChunkResponse\"\x00\x30\x01\x12X\n\x10GetActionResults\x12\x1f.ontdevice.ActionResultsRequest\x1a\x1f.ontdevice.ActionResultResponse\"\x00\x30\x01\x12>\n\x08StartSim\x12\x17.ontdevice.StartRequest\x1a\x17.ontdevice.BoolResponse\"\x00\x12=\n\x07StopSim\x12\x17.ontdevice.EmptyRequest\x1a\x17.ontdevice.BoolResponse\"\x00\x12K\n\nRunMuxScan\x12\x1c.ontdevice.RunMuxScanRequest\x1a\x1d.ontdevice.RunMuxScanResponse\"\x00\x12?\n\tIsRunning\x12\x17.ontdevice.EmptyRequest\x1a\x17.ontdevice.BoolResponse\"\x00\x12I\n\rGetDeviceInfo\x12\x17.ontdevice.EmptyRequest\x1a\x1d.ontdevice.DeviceInfoResponse\"\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ont_device_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _globals['_EMPTYREQUEST']._serialized_start=31 _globals['_EMPTYREQUEST']._serialized_end=45 @@ -37,28 +37,26 @@ _globals['_READACTIONSREQUEST_ACTION_STOPRECEIVINGACTION']._serialized_end=447 _globals['_READACTIONSREQUEST_ACTION_UNBLOCKACTION']._serialized_start=449 _globals['_READACTIONSREQUEST_ACTION_UNBLOCKACTION']._serialized_end=490 - _globals['_ACTIONRESULTIMMEDIATERESPONSE']._serialized_start=502 - _globals['_ACTIONRESULTIMMEDIATERESPONSE']._serialized_end=552 - _globals['_ACTIONRESULTSREQUEST']._serialized_start=554 - _globals['_ACTIONRESULTSREQUEST']._serialized_end=591 - _globals['_ACTIONRESULTRESPONSE']._serialized_start=593 - _globals['_ACTIONRESULTRESPONSE']._serialized_end=700 - _globals['_STARTREQUEST']._serialized_start=702 - _globals['_STARTREQUEST']._serialized_end=816 - _globals['_RUNMUXSCANREQUEST']._serialized_start=818 - _globals['_RUNMUXSCANREQUEST']._serialized_end=857 - _globals['_MUXSCANSTARTEDINFO']._serialized_start=859 - _globals['_MUXSCANSTARTEDINFO']._serialized_end=906 - _globals['_BASECALLEDCHUNKSREQUEST']._serialized_start=909 - _globals['_BASECALLEDCHUNKSREQUEST']._serialized_end=1082 - _globals['_BASECALLEDCHUNKSREQUEST_CHANNELS']._serialized_start=1029 - _globals['_BASECALLEDCHUNKSREQUEST_CHANNELS']._serialized_end=1054 - _globals['_BASECALLEDREADCHUNKRESPONSE']._serialized_start=1085 - _globals['_BASECALLEDREADCHUNKRESPONSE']._serialized_end=1216 - _globals['_BOOLRESPONSE']._serialized_start=1218 - _globals['_BOOLRESPONSE']._serialized_end=1247 - _globals['_DEVICEINFORESPONSE']._serialized_start=1249 - _globals['_DEVICEINFORESPONSE']._serialized_end=1303 - _globals['_ONTDEVICE']._serialized_start=1306 - _globals['_ONTDEVICE']._serialized_end=2093 + _globals['_ACTIONRESULTSREQUEST']._serialized_start=502 + _globals['_ACTIONRESULTSREQUEST']._serialized_end=539 + _globals['_ACTIONRESULTRESPONSE']._serialized_start=541 + _globals['_ACTIONRESULTRESPONSE']._serialized_end=648 + _globals['_STARTREQUEST']._serialized_start=650 + _globals['_STARTREQUEST']._serialized_end=764 + _globals['_RUNMUXSCANREQUEST']._serialized_start=766 + _globals['_RUNMUXSCANREQUEST']._serialized_end=805 + _globals['_RUNMUXSCANRESPONSE']._serialized_start=807 + _globals['_RUNMUXSCANRESPONSE']._serialized_end=854 + _globals['_BASECALLEDCHUNKSREQUEST']._serialized_start=857 + _globals['_BASECALLEDCHUNKSREQUEST']._serialized_end=1030 + _globals['_BASECALLEDCHUNKSREQUEST_CHANNELS']._serialized_start=977 + _globals['_BASECALLEDCHUNKSREQUEST_CHANNELS']._serialized_end=1002 + _globals['_BASECALLEDREADCHUNKRESPONSE']._serialized_start=1033 + _globals['_BASECALLEDREADCHUNKRESPONSE']._serialized_end=1164 + _globals['_BOOLRESPONSE']._serialized_start=1166 + _globals['_BOOLRESPONSE']._serialized_end=1195 + _globals['_DEVICEINFORESPONSE']._serialized_start=1197 + _globals['_DEVICEINFORESPONSE']._serialized_end=1251 + _globals['_ONTDEVICE']._serialized_start=1254 + _globals['_ONTDEVICE']._serialized_end=2025 # @@protoc_insertion_point(module_scope) diff --git a/src/simreaduntil/simulator/protos_generated/ont_device_pb2.pyi b/src/simreaduntil/simulator/protos_generated/ont_device_pb2.pyi index 9cb0b91..d424ba1 100644 --- a/src/simreaduntil/simulator/protos_generated/ont_device_pb2.pyi +++ b/src/simreaduntil/simulator/protos_generated/ont_device_pb2.pyi @@ -6,34 +6,34 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor class EmptyRequest(_message.Message): - __slots__ = [] + __slots__ = () def __init__(self) -> None: ... class EmptyResponse(_message.Message): - __slots__ = [] + __slots__ = () def __init__(self) -> None: ... class ServerInfoResponse(_message.Message): - __slots__ = ["unique_id"] + __slots__ = ("unique_id",) UNIQUE_ID_FIELD_NUMBER: _ClassVar[int] unique_id: str def __init__(self, unique_id: _Optional[str] = ...) -> None: ... class MKRunDirResponse(_message.Message): - __slots__ = ["mk_run_dir"] + __slots__ = ("mk_run_dir",) MK_RUN_DIR_FIELD_NUMBER: _ClassVar[int] mk_run_dir: str def __init__(self, mk_run_dir: _Optional[str] = ...) -> None: ... class ReadActionsRequest(_message.Message): - __slots__ = ["actions"] + __slots__ = ("actions",) class Action(_message.Message): - __slots__ = ["channel", "read_id", "unblock", "stop_further_data"] + __slots__ = ("channel", "read_id", "unblock", "stop_further_data") class StopReceivingAction(_message.Message): - __slots__ = [] + __slots__ = () def __init__(self) -> None: ... class UnblockAction(_message.Message): - __slots__ = ["unblock_duration"] + __slots__ = ("unblock_duration",) UNBLOCK_DURATION_FIELD_NUMBER: _ClassVar[int] unblock_duration: float def __init__(self, unblock_duration: _Optional[float] = ...) -> None: ... @@ -50,20 +50,14 @@ class ReadActionsRequest(_message.Message): actions: _containers.RepeatedCompositeFieldContainer[ReadActionsRequest.Action] def __init__(self, actions: _Optional[_Iterable[_Union[ReadActionsRequest.Action, _Mapping]]] = ...) -> None: ... -class ActionResultImmediateResponse(_message.Message): - __slots__ = ["succeeded"] - SUCCEEDED_FIELD_NUMBER: _ClassVar[int] - succeeded: _containers.RepeatedScalarFieldContainer[bool] - def __init__(self, succeeded: _Optional[_Iterable[bool]] = ...) -> None: ... - class ActionResultsRequest(_message.Message): - __slots__ = ["clear"] + __slots__ = ("clear",) CLEAR_FIELD_NUMBER: _ClassVar[int] clear: bool def __init__(self, clear: bool = ...) -> None: ... class ActionResultResponse(_message.Message): - __slots__ = ["read_id", "time", "channel", "action_type", "result"] + __slots__ = ("read_id", "time", "channel", "action_type", "result") READ_ID_FIELD_NUMBER: _ClassVar[int] TIME_FIELD_NUMBER: _ClassVar[int] CHANNEL_FIELD_NUMBER: _ClassVar[int] @@ -77,7 +71,7 @@ class ActionResultResponse(_message.Message): def __init__(self, read_id: _Optional[str] = ..., time: _Optional[float] = ..., channel: _Optional[int] = ..., action_type: _Optional[int] = ..., result: _Optional[int] = ...) -> None: ... class StartRequest(_message.Message): - __slots__ = ["acceleration_factor", "update_method", "log_interval", "stop_if_no_reads"] + __slots__ = ("acceleration_factor", "update_method", "log_interval", "stop_if_no_reads") ACCELERATION_FACTOR_FIELD_NUMBER: _ClassVar[int] UPDATE_METHOD_FIELD_NUMBER: _ClassVar[int] LOG_INTERVAL_FIELD_NUMBER: _ClassVar[int] @@ -89,21 +83,21 @@ class StartRequest(_message.Message): def __init__(self, acceleration_factor: _Optional[float] = ..., update_method: _Optional[str] = ..., log_interval: _Optional[int] = ..., stop_if_no_reads: bool = ...) -> None: ... class RunMuxScanRequest(_message.Message): - __slots__ = ["t_duration"] + __slots__ = ("t_duration",) T_DURATION_FIELD_NUMBER: _ClassVar[int] t_duration: float def __init__(self, t_duration: _Optional[float] = ...) -> None: ... -class MuxScanStartedInfo(_message.Message): - __slots__ = ["nb_reads_rejected"] +class RunMuxScanResponse(_message.Message): + __slots__ = ("nb_reads_rejected",) NB_READS_REJECTED_FIELD_NUMBER: _ClassVar[int] nb_reads_rejected: int def __init__(self, nb_reads_rejected: _Optional[int] = ...) -> None: ... class BasecalledChunksRequest(_message.Message): - __slots__ = ["batch_size", "channels"] + __slots__ = ("batch_size", "channels") class Channels(_message.Message): - __slots__ = ["value"] + __slots__ = ("value",) VALUE_FIELD_NUMBER: _ClassVar[int] value: _containers.RepeatedScalarFieldContainer[int] def __init__(self, value: _Optional[_Iterable[int]] = ...) -> None: ... @@ -114,7 +108,7 @@ class BasecalledChunksRequest(_message.Message): def __init__(self, batch_size: _Optional[int] = ..., channels: _Optional[_Union[BasecalledChunksRequest.Channels, _Mapping]] = ...) -> None: ... class BasecalledReadChunkResponse(_message.Message): - __slots__ = ["channel", "read_id", "seq", "quality_seq", "estimated_ref_len_so_far"] + __slots__ = ("channel", "read_id", "seq", "quality_seq", "estimated_ref_len_so_far") CHANNEL_FIELD_NUMBER: _ClassVar[int] READ_ID_FIELD_NUMBER: _ClassVar[int] SEQ_FIELD_NUMBER: _ClassVar[int] @@ -128,13 +122,13 @@ class BasecalledReadChunkResponse(_message.Message): def __init__(self, channel: _Optional[int] = ..., read_id: _Optional[str] = ..., seq: _Optional[str] = ..., quality_seq: _Optional[str] = ..., estimated_ref_len_so_far: _Optional[int] = ...) -> None: ... class BoolResponse(_message.Message): - __slots__ = ["value"] + __slots__ = ("value",) VALUE_FIELD_NUMBER: _ClassVar[int] value: bool def __init__(self, value: bool = ...) -> None: ... class DeviceInfoResponse(_message.Message): - __slots__ = ["info", "n_channels"] + __slots__ = ("info", "n_channels") INFO_FIELD_NUMBER: _ClassVar[int] N_CHANNELS_FIELD_NUMBER: _ClassVar[int] info: str diff --git a/src/simreaduntil/simulator/protos_generated/ont_device_pb2_grpc.py b/src/simreaduntil/simulator/protos_generated/ont_device_pb2_grpc.py index 063cff1..8933100 100644 --- a/src/simreaduntil/simulator/protos_generated/ont_device_pb2_grpc.py +++ b/src/simreaduntil/simulator/protos_generated/ont_device_pb2_grpc.py @@ -29,7 +29,7 @@ def __init__(self, channel): self.PerformActions = channel.unary_unary( '/ontdevice.ONTDevice/PerformActions', request_serializer=ont__device__pb2.ReadActionsRequest.SerializeToString, - response_deserializer=ont__device__pb2.ActionResultImmediateResponse.FromString, + response_deserializer=ont__device__pb2.EmptyResponse.FromString, ) self.GetBasecalledChunks = channel.unary_stream( '/ontdevice.ONTDevice/GetBasecalledChunks', @@ -54,7 +54,7 @@ def __init__(self, channel): self.RunMuxScan = channel.unary_unary( '/ontdevice.ONTDevice/RunMuxScan', request_serializer=ont__device__pb2.RunMuxScanRequest.SerializeToString, - response_deserializer=ont__device__pb2.MuxScanStartedInfo.FromString, + response_deserializer=ont__device__pb2.RunMuxScanResponse.FromString, ) self.IsRunning = channel.unary_unary( '/ontdevice.ONTDevice/IsRunning', @@ -157,7 +157,7 @@ def add_ONTDeviceServicer_to_server(servicer, server): 'PerformActions': grpc.unary_unary_rpc_method_handler( servicer.PerformActions, request_deserializer=ont__device__pb2.ReadActionsRequest.FromString, - response_serializer=ont__device__pb2.ActionResultImmediateResponse.SerializeToString, + response_serializer=ont__device__pb2.EmptyResponse.SerializeToString, ), 'GetBasecalledChunks': grpc.unary_stream_rpc_method_handler( servicer.GetBasecalledChunks, @@ -182,7 +182,7 @@ def add_ONTDeviceServicer_to_server(servicer, server): 'RunMuxScan': grpc.unary_unary_rpc_method_handler( servicer.RunMuxScan, request_deserializer=ont__device__pb2.RunMuxScanRequest.FromString, - response_serializer=ont__device__pb2.MuxScanStartedInfo.SerializeToString, + response_serializer=ont__device__pb2.RunMuxScanResponse.SerializeToString, ), 'IsRunning': grpc.unary_unary_rpc_method_handler( servicer.IsRunning, @@ -253,7 +253,7 @@ def PerformActions(request, metadata=None): return grpc.experimental.unary_unary(request, target, '/ontdevice.ONTDevice/PerformActions', ont__device__pb2.ReadActionsRequest.SerializeToString, - ont__device__pb2.ActionResultImmediateResponse.FromString, + ont__device__pb2.EmptyResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @@ -338,7 +338,7 @@ def RunMuxScan(request, metadata=None): return grpc.experimental.unary_unary(request, target, '/ontdevice.ONTDevice/RunMuxScan', ont__device__pb2.RunMuxScanRequest.SerializeToString, - ont__device__pb2.MuxScanStartedInfo.FromString, + ont__device__pb2.RunMuxScanResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/src/simreaduntil/simulator/readpool.py b/src/simreaduntil/simulator/readpool.py index 9fbbb27..6292786 100644 --- a/src/simreaduntil/simulator/readpool.py +++ b/src/simreaduntil/simulator/readpool.py @@ -2,12 +2,21 @@ Read pool that returns reads when requested, e.g., from a generator or a file """ +import contextlib +from pathlib import Path +from queue import Empty +import queue import threading -from typing import Optional, Dict, Any, Generator +from typing import Optional, Dict, Any, Generator, Tuple import numpy as np import pysam +from simreaduntil.shared_utils.logging_utils import setup_logger_simple +from simreaduntil.shared_utils.thread_helpers import ThreadWithResultsAndExceptions -from simreaduntil.shared_utils.utils import force_eval_generator_function, get_some_value_from_dict, is_empty_file +from simreaduntil.shared_utils.utils import StoppableQueue, force_eval_generator_function, get_some_value_from_dict, is_empty_file + +logger = setup_logger_simple(__name__) +"""module logger""" @force_eval_generator_function def reads_from_file_gen(fasta_file, shuffle_rand_state: Optional[np.random.Generator]=None): @@ -29,7 +38,8 @@ def reads_from_file_gen(fasta_file, shuffle_rand_state: Optional[np.random.Gener for id in ref_names: yield (id, fasta.fetch(id)) -class NoReadLeft(Exception): +# todo9: put into readpool +class NoReadLeftException(Exception): """ When no read is left in the read pool """ @@ -38,51 +48,72 @@ class NoReadLeft(Exception): class ReadPool: """ Read pool from which reads can be obtained + + Do not forget to call finish() when done. + + Args: + reads_per_channel: whether reads are channel-specific """ - def __init__(self): + def __init__(self, reads_per_channel): self.lock = threading.Lock() self.definitely_empty = False # whether the readpool is definitely empty (if False, we don't know) self.nb_reads_returned = 0 + self.reads_per_channel = reads_per_channel - def get_new_read(self, channel=None) -> str: + def get_new_read(self, channel=None) -> Tuple[str, Any]: """ Get new read (thread-safe) - Once the function returns NoReadLeft for a channel, it will always return NoReadLeft for this channel (also for channel=None). + Once the function returns NoReadLeftException for a channel, it will always return NoReadLeftException for this channel (also for channel=None). Args: channel: channel for which to get read """ with self.lock: - res = self._get_new_read(channel=channel) + res = self._get_new_read(channel=channel) if self.reads_per_channel else self._get_new_read() self.nb_reads_returned += 1 return res - def _get_new_read(self, channel=None) -> str: + def _get_new_read(self, channel=None) -> Tuple[str, Any]: """ Get new read (not thread-safe), overwrite in subclasses Args: - channel: channel for which to get read + channel: channel for which to get read, only provided if self.reads_per_channel is True + + Returns: + tuple (read, read signal) """ raise NotImplementedError() + + """ + Stop the read pool + + For example, if it is threaded, stop the thread + """ + def finish(self): + pass + def __enter__(self): + return self + def __exit__(self, exc_type, exc_value, traceback): + self.finish() class ReadPoolFromIterable(ReadPool): """ Read pool that requests reads from generator """ def __init__(self, reads_iterable): - super().__init__() + super().__init__(reads_per_channel=False) self.reads_iterable = reads_iterable - def _get_new_read(self, channel=None) -> str: + def _get_new_read(self) -> Tuple[str, Any]: # note: generators are not thread-safe !! try: return next(self.reads_iterable) except StopIteration as e: self.definitely_empty = True - raise NoReadLeft from e # exception chaining + raise NoReadLeftException from e # exception chaining # support pickling def __getstate__(self): @@ -98,15 +129,15 @@ class ReadPoolFromIterablePerChannel(ReadPool): Read pool that requests reads from channel-specific generator """ def __init__(self, reads_iterable_per_channel: Dict[Any, Generator[str, None, None]]): - super().__init__() + super().__init__(reads_per_channel=True) self.reads_iterable_per_channel = reads_iterable_per_channel - def _get_new_read(self, channel=None) -> str: + def _get_new_read(self, channel) -> Tuple[str, Any]: try: return next(self.reads_iterable_per_channel[channel]) except StopIteration as e: - raise NoReadLeft from e # exception chaining + raise NoReadLeftException from e # exception chaining def __repr__(self): return f"ReadPoolFromIterable({list(self.reads_iterable_per_channel.keys())})" @@ -122,15 +153,212 @@ def __getstate__(self): class ReadPoolFromFile(ReadPoolFromIterable): """ - Keep track of reads_file + Read pool that reads from a file or directory """ - def __init__(self, reads_file, shuffle_rand_state: Optional[np.random.Generator]=None): - super().__init__(reads_from_file_gen(reads_file, shuffle_rand_state=shuffle_rand_state)) + def __init__(self, reads_file_or_dir, shuffle_rand_state: Optional[np.random.Generator]=None): + reads_file_or_dir = Path(reads_file_or_dir) + def read_gen(): + for filename in (reads_file_or_dir.glob("**/*.fasta") if reads_file_or_dir.is_dir() else [reads_file_or_dir]): + logger.info(f"Starting to read file '{filename}'") + yield from reads_from_file_gen(filename, shuffle_rand_state=shuffle_rand_state) + super().__init__(read_gen()) self.shuffled = shuffle_rand_state is not None - self.reads_file = reads_file + self.reads_file_or_dir = reads_file_or_dir + + """ + Check if the read pool can open the file/directory + """ + @staticmethod + def can_handle(file: Path) -> bool: + file = Path(file) + return ( + (file.is_dir() and any(file.glob("**/*.fasta"))) or + (file.is_file() and (file.suffix == ".fasta" or file.suffix == ".fasta.gz")) + ) def __repr__(self): - return f"ReadPool(file = {self.reads_file}, shuffled = {self.shuffled})" + return f"ReadPool(file = {self.reads_file_or_dir}, shuffled = {self.shuffled})" + +""" +Threaded ReadPool that wraps another ReadPool and reads from it in another thread using a queue + +Note: Using a rng with ThreadedPoolWrapper is not thread-safe if rng is accessed from multiple threads +""" +class ThreadedReadPoolWrapper(ReadPool): + def __init__(self, read_pool: ReadPool, queue_size: int): + super().__init__(reads_per_channel=read_pool.reads_per_channel) + self._read_pool = read_pool + assert queue_size > 0 # otherwise will read all reads at once + self._reads_queue = StoppableQueue(queue_size) + self._reader_thread = ThreadWithResultsAndExceptions(target=self._fill_queue, name="ThreadedReadPoolWrapper") + self._reader_thread.start() + self.definitely_empty = False + + """ + Check if the read pool can open the file/directory + """ + def can_handle(self, *args, **kwargs) -> bool: + return self._read_pool.can_handle(*args, **kwargs) - \ No newline at end of file + def _fill_queue(self): + try: + while True: + read = self._read_pool.get_new_read() + self._reads_queue.put(read) + except (StoppableQueue.QueueStoppedException, NoReadLeftException): + pass + + try: + self._reads_queue.put(None) # allows get_new_read() to detect it will never return a read again + except StoppableQueue.QueueStoppedException: + # was terminated in between + pass + + logger.info("Finished read queue filler thread") + + def __repr__(self) -> str: + return f"ThreadedReadPool({self._read_pool}, queue_size: {self._reads_queue.maxsize})" + + # protected by lock/mutex + def _get_new_read(self) -> Tuple[str, Any]: + if self.definitely_empty: + raise NoReadLeftException + + try: + read = self._reads_queue.get() + except StoppableQueue.QueueStoppedException: + read = None + + if read is None: + self.definitely_empty = True + raise NoReadLeftException + return read + + def finish(self): + self._reads_queue.stop() + self._reader_thread.join() + self._reader_thread.raise_if_error() + self._read_pool.finish() + +def get_slow5_reads_gen(filename): + """generator returning reads in a slow5 file""" + import pyslow5 + with contextlib.closing(pyslow5.Open(str(filename), "r")) as fh: + for read in fh.seq_reads(): + yield (read["read_id"], read["signal"]) + +# """ +# Read Slow5 files in another thread and put the read data into a queue. + +# # todo: remove read_buffer, rather use ThreadedReadPoolWrapper +# # todo: subclass ReadPool +# # todo: overwrite _get_new_read +# # todo: number of threads for reading slow5 + +# Args: +# s5_dir: directory containing slow5 files +# read_buffer: number of reads to buffer in queue +# """ +# class Slow5ReadPool(ReadPool): +# def __init__(self, s5_dir, read_buffer) -> None: +# super().__init__(reads_per_channel=False) + +# raise ("NotYetImplementedError") + +# import pyslow5 # todo: add as dependency +# from pathlib import Path +# from queue import Queue +# import threading + +# s5_dir = Path(s5_dir) +# assert s5_dir.is_dir() +# self.s5_files = list(s5_dir.glob("*.[sb]low5")) +# self.queue = Queue(read_buffer) # threadsafe +# self.cur_file_idx = -1 +# self._queue_filler_thread = threading.Thread(target=self._fill_queue) +# self._queue_filler_thread.start() +# # self._queue_filler_thread = multiprocessing.Process(target=self._fill_queue) + +# """ +# Check if the read pool can open the file/directory +# """ +# @staticmethod +# def can_handle(dir: Path) -> bool: +# dir = Path(dir) +# return dir.is_dir() and any(dir.glob("**/*.[sb]low5")) + +# def _open_next_file(self): +# self.cur_file_idx += 1 +# if self.cur_file_idx >= len(self.s5_files): +# return False +# logger.info(f"Switching to file {self.cur_file_idx} of {len(self.s5_files)} with name {self.s5_files[self.cur_file_idx]}") +# self.cur_read_gen = get_slow5_reads_gen(str(self.s5_files[self.cur_file_idx])) +# return True + +# def _fill_queue(self): +# logger.info("Started queue filler thread") +# while self._open_next_file(): +# for read in self.cur_read_gen: +# # logger.debug(f"Putting read {read[0]} into queue") +# self.queue.put(read) +# self.queue.put(None) # sentinel + +# def get_new_read(self) -> Tuple[str, np.ndarray]: +# # overwriting get_new_read directly rather than _get_new_read because queue already threadsafe +# # res = self.queue.get() +# # try getting an element immediately, otherwise print a warning +# try: +# res = self.queue.get_nowait() +# except Empty: +# logger.warning("Slow5 read queue empty, waiting until available") +# res = self.queue.get() +# if res is None: +# self.definitely_empty = True +# raise NoReadLeftException() +# return res + +# # def stop_reading(self): +# # self.queue.put(None) +# # self._queue_filler_thread.join(), raise_if_error + +# # def reads_gen(self): +# # while True: +# # res = self.queue.get() +# # if res is None: +# # break +# # yield res + +# s5_dir = "/home/mmordig/rawhash_project/rawhash2/test/data/d2_ecoli_r94/slow5_files" +# reader = Slow5ReadPool(s5_dir, 2) + +# import time +# import tqdm + +# num_signals = 0 +# num_reads = 0 +# # read_id, read_signal = reader.get_new_read() +# start_time = time.time() +# for (read_id, read_signal) in tqdm.tqdm(reader.reads_gen()): +# num_reads += 1 +# num_signals += len(read_signal) +# if num_reads > 10000: +# break + +# elapsed_time = time.time() - start_time +# num_signals / elapsed_time +# n_channels = 512 +# min_throughput = n_channels * 4000 +# num_signals / elapsed_time / min_throughput + + +# end_time_per_channel = np.array([time.time()] * n_channels) +# for (read_id, read_signal) in tqdm.tqdm(reader.reads_gen()): +# min_i = np.argmin(end_time_per_channel) +# time_sleep = end_time_per_channel[min_i] - time.time() +# if time_sleep >= 0: +# time.sleep(time_sleep) +# # else: +# # print(f"Warning: missed deadline {time_sleep}") +# end_time_per_channel[min_i] = time.time() + len(read_signal) / 4000 +# # xxx = read_signal.sum() \ No newline at end of file diff --git a/src/simreaduntil/simulator/readswriter.py b/src/simreaduntil/simulator/readswriter.py index abbe17a..32ae0b5 100644 --- a/src/simreaduntil/simulator/readswriter.py +++ b/src/simreaduntil/simulator/readswriter.py @@ -6,6 +6,7 @@ import logging import os from pathlib import Path +import queue import shutil import sys import tempfile @@ -13,6 +14,7 @@ import threading from Bio import SeqIO from Bio.Seq import Seq +from simreaduntil.shared_utils.thread_helpers import ThreadWithResultsAndExceptions from simreaduntil.shared_utils.utils import is_empty_dir, setup_logger_simple logger = setup_logger_simple(__name__) @@ -45,12 +47,74 @@ def flush(self): def _flush(self): """thread-safe helper method for flush""" raise NotImplementedError + + def finish(self): + self.flush() + + def __enter__(self): + return self + def __exit__(self, exc_type, exc_value, traceback): + self.finish() +""" +Wrapper arounds ReadsWriter that writes reads in a separate thread +""" +class ThreadedReadsWriterWrapper(ReadsWriter): + def __init__(self, reads_writer: ReadsWriter): + super().__init__() + self._reads_writer = reads_writer + self._reads_queue = queue.Queue() + self._writer_thread = ThreadWithResultsAndExceptions(target=self._write_received_reads, name="ThreadedReadsWriterWrapper") + self._writer_thread.start() + + def __repr__(self): + return f"ThreadedReadsWriterWrapper(reads_writer={self._reads_writer})" + + def _write_received_reads(self): + while True: + read = self._reads_queue.get() + if read is None: + break + self._reads_writer.write_read(read) + self.flush() + + def write_read(self, read: SeqIO.SeqRecord): + self._reads_queue.put(read) + + def _flush(self): + self._reads_writer.flush() + + def finish(self): + self._reads_queue.put(None) + self._writer_thread.join() + self._writer_thread.raise_if_error() + self._reads_writer.finish() + +""" +Combines several ReadsWriters into one, calling them sequentially +""" +class CompoundReadsWriter(ReadsWriter): + def __init__(self, reads_writers): + super().__init__() + self.reads_writers = reads_writers + + def __repr__(self): + return f"CompoundReadsWriter(reads_writers={self.reads_writers})" + + def write_read(self, read: SeqIO.SeqRecord): + [rw.write_read(read) for rw in self.reads_writers] + + def flush(self): + [rw.flush() for rw in self.reads_writers] + + def finish(self): + [rw.finish() for rw in self.reads_writers] + class SingleFileReadsWriter(ReadsWriter): """ Write reads to one file (by default stdout), appending reads to the file as they are written (possibly with buffering) - When pickling the file, the file is flushed. When reloading it, the filehandler is not restored and must be set directly. + When pickling this class, the file is flushed. When reloading it, the filehandler is not restored and must be set directly. This class is useful for debugging by writing to sys.stdout. Args: @@ -85,6 +149,12 @@ def __getstate__(self): state["fh"] = None return state + def finish(self): + if self.fh not in [sys.stdout, sys.stderr]: + self.fh.close() + else: + self.fh.flush() + class RotatingFileReadsWriter(ReadsWriter): """ Write reads to a file, creating a new file whenever a maximum of reads is reached. @@ -196,7 +266,6 @@ def __init__(self): super().__init__() self.reads = [] - self.output_dir = None # for compatibility with the ONTSimulator def __repr__(self) -> str: return f"ArrayReadsWriter(nb_reads={len(self.reads)})" diff --git a/src/simreaduntil/simulator/simfasta_to_seqsum.py b/src/simreaduntil/simulator/simfasta_to_seqsum.py index b95d894..c25fd68 100644 --- a/src/simreaduntil/simulator/simfasta_to_seqsum.py +++ b/src/simreaduntil/simulator/simfasta_to_seqsum.py @@ -7,6 +7,8 @@ import argparse import os from pathlib import Path +import sys +import typing import warnings from Bio import SeqIO import numpy as np @@ -19,6 +21,7 @@ from simreaduntil.shared_utils.utils import print_args from simreaduntil.simulator.channel_element import ReadDescriptionParser, ReadTags, end_reason_to_ont_map +from simreaduntil.simulator.readswriter import ReadsWriter logger = setup_logger_simple(__name__) """module logger""" @@ -27,6 +30,78 @@ SEQ_SUMMARY_HEADER = ["read_id", "channel", "mux", "start_time", "duration", "passes_filtering", "template_start", "template_duration", "sequence_length_template", "end_reason"] + _extra_fields """Fields in the sequencing summary file""" +"""Write the sequencing summary header""" +def write_seqsum_header(seqsummary_file): + seqsummary_file.write("\t".join(SEQ_SUMMARY_HEADER) + os.linesep) + +""" +Writes a single sequence record to a sequencing summary file + +Args: + record: sequence record + seqsummary_file: file to write to + read_id: if None, will be parsed from description (by splitting on the first whitespace) + Typically, when SeqIO.SeqRecord is constructed in the code, read_id must be set because the description does not contain the read id + If it is read from a file with SeqIO.parse, the read_id is also in the description +""" +def write_seqsum_record_line(record: SeqIO.SeqRecord, seqsummary_file: typing.IO, read_id: str = None): + if (read_id is None) or record.description.startswith(record.id): + # id is in description + read_id, description = record.description.split(" ", maxsplit=1) + else: + description = record.description + + parsed_desc = ReadDescriptionParser(description) + if NanoSimId.is_valid(parsed_desc.full_read_id): + full_len = NanoSimId.from_str(parsed_desc.full_read_id).ref_len # length if read had not been rejected + else: + full_len = np.NaN + + t_duration = parsed_desc.t_end - parsed_desc.t_start + template_duration = t_duration - parsed_desc.t_delay + if len(record.seq) == 0: + logger.info(f"Found read '{read_id}' that stopped after time {t_duration} before its actual content would have started (at {parsed_desc.t_delay}), skipping") # due to adapters, barcodes + return + channel = parsed_desc.ch + + mux = 1 + passes_filtering = True + print("\t".join(map(str, + [read_id, channel, mux, parsed_desc.t_start, t_duration, passes_filtering, parsed_desc.t_start + parsed_desc.t_delay, + template_duration, len(record.seq), end_reason_to_ont_map[parsed_desc.ended], full_len, ReadTags.RU_STOPPED_RECEIVING in parsed_desc.tags, ReadTags.RU_NEVER_REQUESTED in parsed_desc.tags] + )), file=seqsummary_file) + +class SequencingSummaryWriter(ReadsWriter): + """ + Write the sequencing summary to a single file on-the-fly. + + Args: + reads_out_fh: filehandler to write to + """ + def __init__(self, reads_out_fh=sys.stdout): + super().__init__() + + self.fh = reads_out_fh + + write_seqsum_header(self.fh) + + # Flush reads, e.g. write outstanding reads to file by flushing the file handler + def _flush(self): + self.fh.flush() + + def __repr__(self): + return f"SequencingSummaryWriter(filename='{self.fh.name}')" + + # write read to file + def _write_read(self, read: SeqIO.SeqRecord): + write_seqsum_record_line(read, self.fh, read_id=read.id) + + def finish(self): + if self.fh not in [sys.stdout, sys.stderr]: + self.fh.close() + else: + self.fh.flush() + def convert_simfasta_to_seqsum(reads_fasta, seqsummary_filename, mode="w", tqdm_outer=False): """ Convert FASTA generated by simulator to a sequencing summary file @@ -44,7 +119,7 @@ def convert_simfasta_to_seqsum(reads_fasta, seqsummary_filename, mode="w", tqdm_ assert mode in ["a", "w"] with open(seqsummary_filename, mode=mode) as seqsummary_file: if mode == "w": - seqsummary_file.write("\t".join(SEQ_SUMMARY_HEADER) + os.linesep) + write_seqsum_header(seqsummary_file) nb_seqs = get_nb_fasta_seqs(reads_fasta) if nb_seqs == 0: @@ -52,27 +127,7 @@ def convert_simfasta_to_seqsum(reads_fasta, seqsummary_filename, mode="w", tqdm_ # set leave=False since progress bar is otherwise not properly erased for record in tqdm.tqdm(SeqIO.parse(reads_fasta, "fasta"), desc="Reading fasta line", leave=not tqdm_outer, total=nb_seqs): - read_id, description = record.description.split(" ", maxsplit=1) - parsed_desc = ReadDescriptionParser(description) - if NanoSimId.is_valid(parsed_desc.full_read_id): - full_len = NanoSimId.from_str(parsed_desc.full_read_id).ref_len # length if read had not been rejected - else: - full_len = np.NaN - - t_duration = parsed_desc.t_end - parsed_desc.t_start - template_duration = t_duration - parsed_desc.t_delay - if len(record.seq) == 0: - logger.info(f"Found read '{read_id}' that stopped after time {t_duration} before its actual content would have started (at {parsed_desc.t_delay}), skipping") # due to adapters, barcodes - continue - channel = parsed_desc.ch - - mux = 1 - passes_filtering = True - print("\t".join(map(str, - [read_id, channel, mux, parsed_desc.t_start, t_duration, passes_filtering, parsed_desc.t_start + parsed_desc.t_delay, - template_duration, len(record.seq), end_reason_to_ont_map[parsed_desc.ended], full_len, ReadTags.RU_STOPPED_RECEIVING in parsed_desc.tags, ReadTags.RU_NEVER_REQUESTED in parsed_desc.tags] - )), file=seqsummary_file) - + write_seqsum_record_line(record, seqsummary_file) # print(record) # break diff --git a/src/simreaduntil/simulator/simulator.py b/src/simreaduntil/simulator/simulator.py index 82d0294..627331e 100644 --- a/src/simreaduntil/simulator/simulator.py +++ b/src/simreaduntil/simulator/simulator.py @@ -8,6 +8,7 @@ import enum import itertools from pathlib import Path +import queue from textwrap import dedent, indent import threading import time @@ -133,7 +134,7 @@ def is_running(self) -> bool: """ raise NotImplementedError() - def get_basecalled_read_chunks(self, batch_size=None, channel_subset=None) -> List[Any]: + def get_basecalled_read_chunks(self, batch_size=None, channel_subset=None) -> Generator[Any, None, None]: """ Get available read chunks from the selected channels, from at most 'batch_size' channels @@ -141,7 +142,7 @@ def get_basecalled_read_chunks(self, batch_size=None, channel_subset=None) -> Li """ raise NotImplementedError() - def get_action_results(self, **kwargs) -> List[Tuple[Any, float, int, str, Any]]: + def get_action_results(self, **kwargs) -> Generator[Tuple[Any, float, int, str, Any], Any, Any]: """ Get new results of actions that were performed with unblock and stop_receiving (mux scans etc not included) @@ -268,8 +269,9 @@ class ONTSimulator(ReadUntilDevice): reads_writer: reads writer, ideally of type RotatingFileReadsWriter with attribute .output_dir (used in .mk_run_dir attribute) sim_params: simulation parameters, can be modified during the simulation channel_status_filename: where to write combined channel status at regular intervals + output_dir: output dir returned by self.mk_run_dir, this is where files will be put """ - def __init__(self, read_pool: ReadPool, reads_writer: RotatingFileReadsWriter, sim_params: SimParams, channel_status_filename: Optional[Union[str, Path]]=None): + def __init__(self, read_pool: ReadPool, reads_writer: RotatingFileReadsWriter, sim_params: SimParams, channel_status_filename: Optional[Union[str, Path]]=None, output_dir=""): logger.debug(f"Creating ONT device simulator") self._read_pool = read_pool @@ -278,6 +280,7 @@ def __init__(self, read_pool: ReadPool, reads_writer: RotatingFileReadsWriter, s self._channels: List[Channel] = [Channel(channel_name, read_pool, reads_writer, sim_params=sim_params) for channel_name in sim_params.gap_samplers.keys()] self._channel_status_filename = channel_status_filename + self._output_dir = output_dir # thread that forwards simulation at regular time intervals, may not be alive, so call .is_running() to check if simulation is currently running self._forward_sim_thread: Optional[ThreadWithResultsAndExceptions] = None @@ -287,14 +290,12 @@ def __init__(self, read_pool: ReadPool, reads_writer: RotatingFileReadsWriter, s self.lock_sim_state = threading.RLock() # lock to hold running state of simulation fixed self.sim_state = SimulationRunningState.Stopped + self.action_queue = queue.Queue() # queue for actions to perform on the simulator to avoid lock contention, do it at the end of each forward self._action_results = [] @property def mk_run_dir(self) -> Union[Path, str]: - try: - return self._reads_writer.output_dir - except AttributeError: - return "" + return self._output_dir def device_info(self, sim_params=True, channel_states=False) -> str: """ @@ -336,7 +337,7 @@ def get_channel_stats(self, combined=False) -> Union[ChannelStats, List[ChannelS if combined: return combine_stats((channel.stats for channel in self._channels)) else: - return [channel.stats for channel in self._channels] + return [channel.stats for channel in self._channels] def _forward_channels(self, t, delta=False, show_progress=False): """ @@ -367,7 +368,7 @@ def _stop_channels(self): for channel in self._channels: assert channel.is_running channel.stop() - self._reads_writer.flush() + self._reads_writer.finish() def _all_channels_finished(self) -> bool: """Whether no reads are left and all channels have finished""" @@ -400,7 +401,7 @@ def start(self, *args, **kwargs): # don't set as daemon because reads in-progress need to be written to a file self._forward_sim_thread = ThreadWithResultsAndExceptions( - target=self._forward_sim_loop, name=new_thread_name(), args=args, kwargs=kwargs + target=self._forward_sim_loop, name=new_thread_name("simforw-{}"), args=args, kwargs=kwargs ) logger.info("Starting the simulation") @@ -434,6 +435,7 @@ def _forward_sim_loop(self, acceleration_factor=1.0, update_method="realtime", l self.sim_state = SimulationRunningState.Running logger.debug("Simulator forward thread started...") + logger.info(f"Device info: {self.device_info()}") assert acceleration_factor > 0, f"invalid acceleration_factor {acceleration_factor}" if self._channel_status_filename is not None: @@ -479,6 +481,7 @@ def _log(): logger.debug(f"Forwarding to time {t_sim}") t_real_last_forward = cur_ns_time() self._forward_channels(t_sim) + self._process_actions() if (log_interval != -1) and (i % log_interval == 0): _log() @@ -502,7 +505,7 @@ def _compute_delta_t_sim(self): Returns: Length of one chunk in seconds (without acceleration) """ - return self.sim_params.chunk_size / self.sim_params.bp_per_second + return self.sim_params.min_chunk_size / self.sim_params.bp_per_second def stop(self, _join_thread=True): """ @@ -515,6 +518,7 @@ def stop(self, _join_thread=True): Returns: Whether the simulation was stopped (i.e. it was running and not in the process of being stopped) + The simulation has not necessarily stopped when this method returns False, it only started the stopping process. """ logger.info("Stop request received, stopping simulation...") @@ -527,6 +531,7 @@ def stop(self, _join_thread=True): if _join_thread: self._forward_sim_thread.join() # block, try hard for .cancel() on stream + self._forward_sim_thread.raise_if_error() assert not self._forward_sim_thread.is_alive() self._forward_sim_thread = None @@ -552,7 +557,7 @@ def is_running(self): ############## chunk related methods ############## - def get_basecalled_read_chunks(self, batch_size=None, channel_subset=None) -> List[Tuple[Any]]: + def get_basecalled_read_chunks(self, batch_size=None, channel_subset=None) -> Generator[Tuple[Any], None, None]: """ It permutes the channels and gets at most 'batch_size' from them. Channels with no new chunks are filtered out. @@ -577,9 +582,9 @@ def get_basecalled_read_chunks(self, batch_size=None, channel_subset=None) -> Li for channel in self.sim_params.random_state.permutation(channel_subset): if nb_chunks >= batch_size: break - chunks, read_id, estimated_ref_len_so_far = self._channels[channel-1].get_new_chunks() # if simulation was already stopped (in between), just returns "" + chunks, read_id, estimated_ref_len_so_far = self._channels[channel-1].get_new_samples() # if simulation was already stopped (in between), just returns "" # ignore if no new chunks (e.g. if channel does not have a read currently) - if chunks != "": + if len(chunks) > 0: # chunks is either str or array of raw signals nb_chunks += 1 yield (channel, read_id, chunks, "noquality", estimated_ref_len_so_far) @@ -593,7 +598,7 @@ def get_raw_chunks(self, *args, **kwargs): for (channel, read_id, chunks, quality, estimated_ref_len_so_far) in self.get_basecalled_read_chunks(*args, **kwargs): yield (channel, read_id, self.sim_params.pore_model.to_raw(chunks), quality, estimated_ref_len_so_far) - def get_action_results(self, clear=True) -> List[Tuple[Any, float, int, str, Any]]: + def get_action_results(self, clear=True) -> Generator[Tuple[Any, float, int, str, Any], Any, Any]: """ Get action results of actions performed on simulator @@ -613,27 +618,47 @@ def get_action_results(self, clear=True) -> List[Tuple[Any, float, int, str, Any else: return self._action_results.copy() - def unblock_read(self, read_channel, read_id, unblock_duration=None) -> Optional[bool]: + def unblock_read(self, read_channel, read_id, unblock_duration=None): + self.action_queue.put((ActionType.Unblock, (read_channel, read_id, unblock_duration))) + + def stop_receiving_read(self, read_channel, read_id): + self.action_queue.put((ActionType.StopReceiving, (read_channel, read_id))) + + # process actions asynchronously after calling forward since otherwise, there is a lot of lock contention which + # means we cannot run at acceleration factor 10 + def _process_actions(self): + while True: + try: + action_type, args = self.action_queue.get_nowait() + except queue.Empty: + return + if action_type == ActionType.Unblock: + self._unblock_read(*args) + else: + assert action_type == ActionType.StopReceiving + self._stop_receiving_read(*args) + + def _unblock_read(self, read_channel, read_id, unblock_duration=None) -> Optional[bool]: """Unblock read""" self._check_channels_available([read_channel]) action_res = self._channels[read_channel-1].unblock(unblock_duration=unblock_duration, read_id=read_id) self._action_results.append((read_id, self._channels[read_channel-1].t, read_channel, ActionType.Unblock, action_res)) - logger.info(f"Unblocking read {read_id} on channel {read_channel}, result: {action_res.to_str()}") + # logger.info(f"Unblocking read {read_id} on channel {read_channel}, result: {action_res.to_str()}") return action_res - def stop_receiving_read(self, read_channel, read_id) -> Optional[StoppedReceivingResponse]: + def _stop_receiving_read(self, read_channel, read_id) -> Optional[StoppedReceivingResponse]: """Stop receiving from read""" self._check_channels_available([read_channel]) action_res = self._channels[read_channel-1].stop_receiving(read_id=read_id) self._action_results.append((read_id, self._channels[read_channel-1].t, read_channel, ActionType.StopReceiving, action_res)) - logger.info(f"Stopping receiving from read {read_id} on channel {read_channel}, result: {action_res.to_str()}") + # logger.info(f"Stopping receiving from read {read_id} on channel {read_channel}, result: {action_res.to_str()}") return action_res - def run_mux_scan(self, t_duration: float) -> int: + def run_mux_scan(self, t_duration: float, is_sync=False) -> int: """Pass in duration on each channel rather than end time because the channel may already have been forwarded in-between""" with self.lock_sim_state: # the lock ensures that channels are not forwarded - if self.sim_state != SimulationRunningState.Running: + if (not is_sync) and self.sim_state != SimulationRunningState.Running: logger.warning("Simulation not (or no longer) running, mux scan ignored") return 0 if self._channels[0].has_active_mux_scan(): @@ -653,9 +678,12 @@ def _check_not_async_mode(self): def sync_forward(self, t, delta=False, show_progress=False): """ - Forward all channels to time t + Process actions and forward all channels to time t + + Using (t=0, delta=True) means actions are processed """ self._check_not_async_mode() + self._process_actions() return self._forward_channels(t, delta=delta, show_progress=show_progress) def sync_start(self, t=None): @@ -728,7 +756,7 @@ def convert_action_results_to_df(action_results): action_results_df = pd.DataFrame.from_records(action_results, columns=["read_id", "time", "channel", "action_type", "success"]) return action_results_df -def simulator_stats_to_disk(simulators, output_dir=None): +def write_simulator_stats(simulators, output_dir=None): """ Dump action results (success or missed) and channel statistics to the run dir @@ -788,9 +816,9 @@ def plot_nb_actions_per_read(action_results_df, save_dir=None): reads_with_multiple_actions = nb_actions_per_read[nb_actions_per_read > 1].index.values reads_with_contradicting_actions = nb_unique_actions_per_read[nb_unique_actions_per_read > 1].index.values if len(reads_with_multiple_actions) > 0: - logger.warning(f"There are {len(reads_with_multiple_actions)} reads with multiple actions: {reads_with_multiple_actions}") + logger.warning(f"There are {len(reads_with_multiple_actions)} reads with multiple actions (possible same actions): {reads_with_multiple_actions}") if len(reads_with_contradicting_actions) > 0: - logger.warning(f"There are {len(reads_with_contradicting_actions)} reads with contradicting actions: {reads_with_contradicting_actions}") + logger.warning(f"There are {len(reads_with_contradicting_actions)} reads with contradicting actions (e.g. stop_receiving and unblock): {reads_with_contradicting_actions}") fig, (ax1, ax2, ax3) = plt.subplots(ncols=3, figsize=(15, 4)) @@ -854,58 +882,58 @@ def plot_action_success_rate(action_results_df, save_dir=None): return fig -class ReadUntilClientFromDevice(ReadUntilDevice): - """ - ReadUntilClient with ReadUntil actions operating on a batch of reads - - Named ReadUntilClientFromDevice to avoid nameclash with ReadUntilClient - - start, stop, device_info not implemented. They should be directly called on the device. - """ - def __init__(self, device : ReadUntilDevice): - self._device = device - - def __repr__(self): - res = "ReadUntilClientFromDevice of the following device:\n" - res += indent(repr(self._device), " ") - return res - - @property - def n_channels(self) -> int: - """ - Number of channels - """ - return len(self._device.n_channels) - - @property - def is_running(self) -> bool: - """ - Whether the device is sequencing - """ - return self._device.is_running - - @property - def mk_run_dir(self): - return self._device.mk_run_dir - - def get_basecalled_read_chunks(self, batch_size=None, channel_subset=None): - """ - Yield basecalled chunks from channels - - Args: - batch_size: maximum number of channels to get reads from - channel_subset: restrict to these channels (if provided) - - Yields: - basecalled chunks from channels in the form (chan_key, read_id, chunk, quality, estimated ref len of all chunks returned so far for this read) - """ - yield from self._device.get_basecalled_read_chunks(batch_size, channel_subset) - - def unblock_read(self, read_channel, read_id, unblock_duration=None) -> bool: - return self._device.unblock_read(read_channel, read_id=read_id, unblock_duration=unblock_duration) - - def stop_receiving_read(self, read_channel, read_id) -> StoppedReceivingResponse: - return self._device.stop_receiving_read(read_channel, read_id=read_id) +# class ReadUntilClientFromDevice(ReadUntilDevice): +# """ +# ReadUntilClient with ReadUntil actions operating on a batch of reads + +# Named ReadUntilClientFromDevice to avoid nameclash with ReadUntilClient + +# start, stop, device_info not implemented. They should be directly called on the device. +# """ +# def __init__(self, device : ReadUntilDevice): +# self._device = device + +# def __repr__(self): +# res = "ReadUntilClientFromDevice of the following device:\n" +# res += indent(repr(self._device), " ") +# return res + +# @property +# def n_channels(self) -> int: +# """ +# Number of channels +# """ +# return len(self._device.n_channels) + +# @property +# def is_running(self) -> bool: +# """ +# Whether the device is sequencing +# """ +# return self._device.is_running + +# @property +# def mk_run_dir(self): +# return self._device.mk_run_dir + +# def get_basecalled_read_chunks(self, batch_size=None, channel_subset=None): +# """ +# Yield basecalled chunks from channels + +# Args: +# batch_size: maximum number of channels to get reads from +# channel_subset: restrict to these channels (if provided) + +# Yields: +# basecalled chunks from channels in the form (chan_key, read_id, chunk, quality, estimated ref len of all chunks returned so far for this read) +# """ +# yield from self._device.get_basecalled_read_chunks(batch_size, channel_subset) + +# def unblock_read(self, read_channel, read_id, unblock_duration=None) -> bool: +# return self._device.unblock_read(read_channel, read_id=read_id, unblock_duration=unblock_duration) + +# def stop_receiving_read(self, read_channel, read_id) -> StoppedReceivingResponse: +# return self._device.stop_receiving_read(read_channel, read_id=read_id) def stop_simulation_after_time_thread(simulator: ONTSimulator, t: float): """ @@ -951,7 +979,7 @@ def run_periodic_mux_scan_thread(simulator: ONTSimulator, period: float, scan_du warnings.warn(f"Period between mux scans may be so short that mux scans happen the whole time: scan_duration={scan_duration:.2f}s, period={period:.2f}s") def _run_periodic_mux_scan(): - logger.info(f"Running periodic mux scan every {period:.2f}s (sim time), acceleration factor {acceleration_factor:.2f}") + logger.info(f"Running periodic mux scan every {period:.2f}s (sim time) with duration {scan_duration:.2f}s, acceleration factor {acceleration_factor:.2f}") i = 1 time_start = cur_ns_time() while True: @@ -1036,6 +1064,7 @@ def run_simulator_from_sampler_per_channel( read_pool=read_pool, reads_writer=reads_writer, sim_params=sim_params, + output_dir=reads_writer.output_dir, ) simulator.sync_start(0) @@ -1118,6 +1147,7 @@ def forward_channels(idx, sim_params, read_durations_per_channel, random_state): read_pool=read_pool, reads_writer=reads_writer, sim_params=sim_params, + output_dir="", ) simulator.sync_start(0) @@ -1185,7 +1215,7 @@ def parse_line(line): return df def plot_simulator_delay_over_time(df, n_delays=200, save_dir=None): - """Plot simulator delay over time""" + """Plot simulator delay over time for the loop updating the channels""" # df = df.sample(min(len(df), 200)) # restrict to the largest rather than sample @@ -1194,13 +1224,13 @@ def plot_simulator_delay_over_time(df, n_delays=200, save_dir=None): fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(11, 4)) sns.lineplot(df, x="time", y="delay", ax=ax1) - ax1.set_xlabel("Time (of iteration) (s)") + ax1.set_xlabel("Real time (of iteration) (s)") ax1.set_ylabel("Delay (s)") sns.lineplot(df, x="iteration", y="delay", ax=ax2) ax2.set_xlabel("Iteration") ax2.set_ylabel("Delay (s)") - fig.suptitle(f"Simulator delays (largest {n_delays})") + fig.suptitle(f"Simulator loop delays (largest {n_delays})") make_tight_layout(fig) if save_dir is not None: diff --git a/src/simreaduntil/simulator/simulator_client.py b/src/simreaduntil/simulator/simulator_client.py index 4e93eac..5d61e6b 100644 --- a/src/simreaduntil/simulator/simulator_client.py +++ b/src/simreaduntil/simulator/simulator_client.py @@ -3,7 +3,7 @@ """ import grpc -from typing import Any, List, Optional, Tuple +from typing import Any, Generator, List, Optional, Tuple from simreaduntil.simulator.channel import StoppedReceivingResponse, UnblockResponse from simreaduntil.simulator.simulator import ActionType @@ -133,11 +133,11 @@ def get_basecalled_read_chunks(self, batch_size=None, channel_subset=None): for chunk in self._stub.GetBasecalledChunks(ont_device_pb2.BasecalledChunksRequest(batch_size=batch_size, channels=channels)): yield (chunk.channel, chunk.read_id, chunk.seq, chunk.quality_seq, chunk.estimated_ref_len_so_far) - def get_action_results(self, clear=True) -> List[Tuple[Any, float, int, str, Any]]: + def get_action_results(self, clear=True) -> Generator[Tuple[Any, float, int, str, Any], Any, Any]: """ Get action results """ - for action_response in self._stub.GetActionResults(ont_device_pb2.ActionResultsRequest(clear=clear)).actions: + for action_response in self._stub.GetActionResults(ont_device_pb2.ActionResultsRequest(clear=clear)): action_type = ActionType(action_response.action_type) action_result = (StoppedReceivingResponse if action_type == ActionType.StopReceiving else UnblockResponse)(action_response.result) yield (action_response.read_id, action_response.time, action_response.channel, action_type, action_result) @@ -150,7 +150,7 @@ def unblock_read(self, read_channel, read_id, unblock_duration=None): # return self._stub.PerformActions(ont_device_pb2.ReadActionsRequest(actions=[ # ont_device_pb2.ReadActionsRequest.Action(channel=read_channel, read_id=read_id, unblock=ont_device_pb2.ReadActionsRequest.Action.UnblockAction(unblock_duration=unblock_duration if unblock_duration is not None else -1)) # ])).succeeded[0] - return self.unblock_read_batch([(read_channel, read_id)], unblock_duration=unblock_duration)[0] + self.unblock_read_batch([(read_channel, read_id)], unblock_duration=unblock_duration) def stop_receiving_read(self, read_channel, read_id): """ @@ -160,7 +160,7 @@ def stop_receiving_read(self, read_channel, read_id): # return self._stub.PerformActions(ont_device_pb2.ReadActionsRequest(actions=[ # ont_device_pb2.ReadActionsRequest.Action(channel=read_channel, read_id=read_id, stop_further_data=ont_device_pb2.ReadActionsRequest.Action.StopReceivingAction()), # ])).succeeded[0] - return self.stop_receiving_read_batch([(read_channel, read_id)])[0] + self.stop_receiving_read_batch([(read_channel, read_id)]) # batch methods def unblock_read_batch(self, channel_and_ids, unblock_duration=None): @@ -168,18 +168,20 @@ def unblock_read_batch(self, channel_and_ids, unblock_duration=None): Unblock a batch of reads on channel; returns whether the actions were performed (not performed if the read was already over) """ self._check_connected() - return self._stub.PerformActions(ont_device_pb2.ReadActionsRequest(actions=[ + self._stub.PerformActions(ont_device_pb2.ReadActionsRequest(actions=[ ont_device_pb2.ReadActionsRequest.Action(channel=read_channel, read_id=read_id, unblock=ont_device_pb2.ReadActionsRequest.Action.UnblockAction(unblock_duration=unblock_duration if unblock_duration is not None else -1)) - for (read_channel, read_id) in channel_and_ids])).succeeded + for (read_channel, read_id) in channel_and_ids + ])) def stop_receiving_read_batch(self, channel_and_ids): """ Stop receiving a batch of reads on channel; returns whether the actions were performed (not performed if the read was already over) """ self._check_connected() - return self._stub.PerformActions(ont_device_pb2.ReadActionsRequest(actions=[ + self._stub.PerformActions(ont_device_pb2.ReadActionsRequest(actions=[ ont_device_pb2.ReadActionsRequest.Action(channel=read_channel, read_id=read_id, stop_further_data=ont_device_pb2.ReadActionsRequest.Action.StopReceivingAction()) - for (read_channel, read_id) in channel_and_ids])).succeeded + for (read_channel, read_id) in channel_and_ids + ])) @property def mk_run_dir(self): diff --git a/src/simreaduntil/simulator/simulator_params.py b/src/simreaduntil/simulator/simulator_params.py index fe15193..d665399 100644 --- a/src/simreaduntil/simulator/simulator_params.py +++ b/src/simreaduntil/simulator/simulator_params.py @@ -1,5 +1,5 @@ """ -Manage simulation parameters such as bps_per_second, chunk_size, gap_samplers +Manage simulation parameters such as bps_per_second, min_chunk_size, gap_samplers """ from simreaduntil.simulator.gap_sampling.gap_sampling import GapSampler @@ -17,21 +17,21 @@ class SimParams: gap_samplers: gap samplers for each channel bp_per_second: basepairs per second going through the pore (per channel) default_unblock_duration: extra delay to reject a read / unblock a pore, in seconds - chunk_size: chunk size for selective sequencing ReadUntil (size of chunks when sending data; chunks are concatenated; last chunk has shorter size) + min_chunk_size: minimum chunk size for selective sequencing ReadUntil seed: seed for random number generator, same state set on all channels """ - def __init__(self, gap_samplers: Dict[str, GapSampler], bp_per_second=450, default_unblock_duration=0.1, chunk_size=200, pore_model: Optional[PoreModel]=None, seed: Union[int, np.random.Generator]=0): - self.set(gap_samplers=gap_samplers, bp_per_second=bp_per_second, default_unblock_duration=default_unblock_duration, chunk_size=chunk_size, seed=seed, pore_model=pore_model) + def __init__(self, gap_samplers: Dict[str, GapSampler], bp_per_second=450, default_unblock_duration=0.1, min_chunk_size=200, pore_model: Optional[PoreModel]=None, seed: Union[int, np.random.Generator]=0): + self.set(gap_samplers=gap_samplers, bp_per_second=bp_per_second, default_unblock_duration=default_unblock_duration, min_chunk_size=min_chunk_size, seed=seed, pore_model=pore_model) def restrict_to_channels(self, channels, rand_state): """Subset SimParams to some channels""" - return SimParams(gap_samplers={channel: self.gap_samplers[channel] for channel in channels}, bp_per_second=self.bp_per_second, default_unblock_duration=self.default_unblock_duration, chunk_size=self.chunk_size, seed=rand_state) + return SimParams(gap_samplers={channel: self.gap_samplers[channel] for channel in channels}, bp_per_second=self.bp_per_second, default_unblock_duration=self.default_unblock_duration, min_chunk_size=self.min_chunk_size, seed=rand_state) def __repr__(self) -> str: # repr(random_state) is not very informative (does not show seed, so we store it separately and display it here) - return f"""SimParams(bp_per_second={self.bp_per_second}, default_unblock_duration={self.default_unblock_duration}, chunk_size={self.chunk_size}, initial_seed={self._initial_seed}, n_channels={len(self.gap_samplers)})""" + return f"""SimParams(bp_per_second={self.bp_per_second}, default_unblock_duration={self.default_unblock_duration}, min_chunk_size={self.min_chunk_size}, initial_seed={self._initial_seed}, n_channels={len(self.gap_samplers)})""" - def set(self, *, gap_samplers: Dict[str, GapSampler]=None, bp_per_second=None, default_unblock_duration=None, chunk_size=None, pore_model=None, seed=None): + def set(self, *, gap_samplers: Dict[str, GapSampler]=None, bp_per_second=None, default_unblock_duration=None, min_chunk_size=None, pore_model=None, seed=None): """ Set parameters, None values are ignored """ @@ -44,8 +44,8 @@ def set(self, *, gap_samplers: Dict[str, GapSampler]=None, bp_per_second=None, d self.bp_per_second = bp_per_second if default_unblock_duration is not None: self.default_unblock_duration = default_unblock_duration - if chunk_size is not None: - self.chunk_size = chunk_size + if min_chunk_size is not None: + self.min_chunk_size = min_chunk_size if pore_model is not None: self.pore_model = pore_model if seed is not None: @@ -66,8 +66,8 @@ def _check_sim_params(self): assert isinstance(self.bp_per_second, (int, float)) assert self.bp_per_second > 0 - assert isinstance(self.chunk_size, int) - assert self.chunk_size > 0 + assert isinstance(self.min_chunk_size, int) + assert self.min_chunk_size > 0 assert isinstance(self.default_unblock_duration, (int, float)) assert self.default_unblock_duration >= 0 diff --git a/src/simreaduntil/simulator/simulator_server.py b/src/simreaduntil/simulator/simulator_server.py index 764b979..1900524 100644 --- a/src/simreaduntil/simulator/simulator_server.py +++ b/src/simreaduntil/simulator/simulator_server.py @@ -102,7 +102,7 @@ def PerformActions(self, request, context): res.append(self.device.unblock_read(channel, read_id=read_id, unblock_duration=unblock_duration)) else: res.append(self.device.stop_receiving_read(channel, read_id=read_id)) #todo2: current conversion from enum 0,1,2 to bool is not ideal - return ont_device_pb2.ActionResultImmediateResponse(succeeded=res) + return ont_device_pb2.EmptyResponse() @print_gen_exceptions def GetActionResults(self, request, context): @@ -134,8 +134,8 @@ def StopSim(self, request, context): @print_nongen_exceptions def RunMuxScan(self, request, context): - assert request.HasField("t_duration"), "t_duration must be set" - return ont_device_pb2.MuxScanStartedInfo(value=self.device.run_mux_scan(t_duration=request.t_duration)) + # assert request.HasField("t_duration"), "t_duration must be set" # implicitly set + return ont_device_pb2.RunMuxScanResponse(nb_reads_rejected=self.device.run_mux_scan(t_duration=request.t_duration)) @print_nongen_exceptions # whether simulation is running diff --git a/src/simreaduntil/simulator/utils.py b/src/simreaduntil/simulator/utils.py index a259671..b752bad 100644 --- a/src/simreaduntil/simulator/utils.py +++ b/src/simreaduntil/simulator/utils.py @@ -32,9 +32,9 @@ def in_interval(x, interval): # pylint: disable=invalid-name _counter = _count() -def new_thread_name(template_str="ont-sim-{}"): +def new_thread_name(template_str="thread-{}"): """ - Helper to generate new thread names + Helper to generate new thread names, thread name is unlikely to exist because we use a counter Args: template_str: string with one placeholder for the counter @@ -53,3 +53,11 @@ def set_package_log_level(log_level=logging.INFO): with temp_logging_level(logging.getLogger("ru"), log_level): with temp_logging_level(logging.getLogger("simreaduntil"), log_level): yield + + # sys.stdout = Tee(old_stdout, out_file) + # sys.stderr = Tee(old_stderr, err_file) + # yield + # sys.stdout = old_stdout + # sys.stderr = old_stderr + + \ No newline at end of file diff --git a/src/simreaduntil/usecase_helpers/cli_usecase/simulator_client_cli.py b/src/simreaduntil/usecase_helpers/cli_usecase/simulator_client_cli.py index 6af3603..8f3479b 100644 --- a/src/simreaduntil/usecase_helpers/cli_usecase/simulator_client_cli.py +++ b/src/simreaduntil/usecase_helpers/cli_usecase/simulator_client_cli.py @@ -1,6 +1,7 @@ import argparse import logging +import signal import time import grpc @@ -11,6 +12,7 @@ from simreaduntil.shared_utils.utils import print_args from simreaduntil.simulator.simulator_client import DeviceGRPCClient +from simreaduntil.shared_utils.utils import set_signal_handler logger = setup_logger_simple(__name__) """module logger""" @@ -49,35 +51,36 @@ def main(): num_batches = 0 num_chunks = 0 - try: - with logging_redirect_tqdm(): - while True: - num_batches += 1 - for (channel, read_id, seq, quality_seq, estimated_ref_len_so_far) in tqdm(client.get_basecalled_read_chunks(), desc=f"Processing chunks in batch {num_batches}"): - num_chunks += 1 - logger.debug(f"Read chunk: channel={channel}, read_id={read_id}, seq={seq[:20]}..., quality_seq={quality_seq}, estimated_ref_len_so_far={estimated_ref_len_so_far}") - u = rng.uniform() - if u < 0.2: - logger.debug(f"Rejecting read '{read_id}'") - client.unblock_read(channel, read_id) - elif u < 0.4: - logger.debug(f"Stop receiving read '{read_id}'") - client.stop_receiving_read(channel, read_id) - else: - # no action - pass - # time.sleep(0.05) - time.sleep(0.2) # throttle - except KeyboardInterrupt: - pass - except grpc.RpcError as e: - logger.error(f"Caught gRPC error: {e}") - finally: + def stop_client(*args, **kwargs): try: if client.stop(): logger.info("Stopped simulation") except grpc.RpcError as e: pass + + with set_signal_handler(signal_type=signal.SIGINT, handler=stop_client): # catch keyboard interrupt (Ctrl+C) + try: + with logging_redirect_tqdm(): + while client.is_running: + num_batches += 1 + for (channel, read_id, seq, quality_seq, estimated_ref_len_so_far) in tqdm(client.get_basecalled_read_chunks(), desc=f"Processing chunks in batch {num_batches}"): + num_chunks += 1 + logger.debug(f"Read chunk: channel={channel}, read_id={read_id}, seq={seq[:20]}..., quality_seq={quality_seq}, estimated_ref_len_so_far={estimated_ref_len_so_far}") + u = rng.uniform() + if u < 0.2: + logger.debug(f"Rejecting read '{read_id}'") + client.unblock_read(channel, read_id) + elif u < 0.4: + logger.debug(f"Stop receiving read '{read_id}'") + client.stop_receiving_read(channel, read_id) + else: + # no action + pass + # time.sleep(0.05) + time.sleep(0.2) # throttle + except grpc.RpcError as e: + logger.error(f"Caught gRPC error: {e}") + logger.info(f"Done. Received {num_chunks} chunks from {num_batches} batches") diff --git a/src/simreaduntil/usecase_helpers/cli_usecase/simulator_server_cli.py b/src/simreaduntil/usecase_helpers/cli_usecase/simulator_server_cli.py index 5497408..5152fc4 100644 --- a/src/simreaduntil/usecase_helpers/cli_usecase/simulator_server_cli.py +++ b/src/simreaduntil/usecase_helpers/cli_usecase/simulator_server_cli.py @@ -15,12 +15,13 @@ from simreaduntil.shared_utils.utils import print_args from simreaduntil.simulator.gap_sampling.constant_gaps_until_blocked import ConstantGapsUntilBlocked from simreaduntil.simulator.gap_sampling.rolling_window_gap_sampler import RollingWindowGapSamplerPerChannel -from simreaduntil.simulator.readpool import ReadPoolFromFile -from simreaduntil.simulator.readswriter import RotatingFileReadsWriter -from simreaduntil.simulator.simfasta_to_seqsum import convert_simfasta_dir_to_seqsum -from simreaduntil.simulator.simulator import ONTSimulator, simulator_stats_to_disk +from simreaduntil.simulator.readpool import ReadPoolFromFile, ThreadedReadPoolWrapper +from simreaduntil.simulator.readswriter import CompoundReadsWriter, RotatingFileReadsWriter +from simreaduntil.simulator.simfasta_to_seqsum import SequencingSummaryWriter, convert_simfasta_dir_to_seqsum +from simreaduntil.simulator.simulator import ONTSimulator, write_simulator_stats from simreaduntil.simulator.simulator_params import SimParams from simreaduntil.simulator.simulator_server import launchable_device_grpc_server, manage_grpc_server +from simreaduntil.shared_utils.utils import set_signal_handler logger = setup_logger_simple(__name__) """module logger""" @@ -30,10 +31,10 @@ def parse_args(args=None): parser.add_argument("reads_file", type=Path, help="Path to the reads, e.g., generated by NanoSim or perfect reads") parser.add_argument("run_dir", type=Path, help="Run dir, must not exist", default="example_run") # todo: add pore model, extract sim params from an existing run: ConstantGapsUntilBlocked.from_seqsum_df, RollingWindowGapSamplerPerChannel.from_seqsum_df - parser.add_argument("--num_channels", type=int, help="Number of channels", default=512) + parser.add_argument("--n_channels", type=int, help="Number of channels", default=512) # parser.add_argument("--run_time", type=float, help="Time to run for (s)", default=2 ** 63 / 10 ** 9) # maximum value handled by time.sleep parser.add_argument("--acceleration_factor", type=float, help="Speedup factor for simulation", default=1.0) - parser.add_argument("--chunk_size", type=int, help="Number of basepairs per chunk (API returns a multiple of chunk_size per channel)", default=200) + parser.add_argument("--min_chunk_size", type=int, help="Number of basepairs per chunk (API returns a multiple of min_chunk_size per channel)", default=200) parser.add_argument("--bp_per_second", type=int, help="Pore speed (number of basepairs per second (per channel))", default=450) parser.add_argument("--seed", type=int, help="Random seed", default=None) parser.add_argument("--unblock_duration", type=float, help="Duration to unblock a read (s)", default=0.1) @@ -65,14 +66,14 @@ def main(): reads_file = args.reads_file assert reads_file.exists(), f"reads_file '{reads_file}' does not exist" run_dir = args.run_dir - num_channels = args.num_channels - assert num_channels > 0, f"num_channels {num_channels} must be > 0" + n_channels = args.n_channels + assert n_channels > 0, f"n_channels {n_channels} must be > 0" # run_time = args.run_time # assert run_time > 0, f"run_time {run_time} must be > 0" acceleration_factor = args.acceleration_factor assert acceleration_factor > 0, f"acceleration_factor {acceleration_factor} must be > 0" - chunk_size = args.chunk_size - assert chunk_size > 0, f"chunk_size {chunk_size} must be > 0" + min_chunk_size = args.min_chunk_size + assert min_chunk_size > 0, f"min_chunk_size {min_chunk_size} must be > 0" bp_per_second = args.bp_per_second assert bp_per_second > 0, f"bp_per_second {bp_per_second} must be > 0" seed = args.seed @@ -98,43 +99,49 @@ def main(): logger.info("Reading in reads. pysam index creation may take some time.") read_pool = ReadPoolFromFile(reads_file=reads_file) + read_pool = ThreadedReadPoolWrapper(read_pool, queue_size=2*n_channels) mk_run_dir = run_dir / "reads" logger.info(f"Writing basecalled reads to directory '{mk_run_dir}'") reads_writer = RotatingFileReadsWriter(mk_run_dir, "reads_", max_reads_per_file=4000) + seqsum_writer = SequencingSummaryWriter(open(run_dir / "live_sequencing_summary.txt", "w")) + reads_writer = CompoundReadsWriter([reads_writer, seqsum_writer]) - gap_samplers = {f"chan{channel}": ConstantGapsUntilBlocked(short_gap_length=0.4, long_gap_length=10, prob_long_gap=0.05, time_until_blocked=np.inf, read_delay=0.05) for channel in range(num_channels)} + gap_samplers = {f"chan{channel}": ConstantGapsUntilBlocked(short_gap_length=0.4, long_gap_length=10, prob_long_gap=0.05, time_until_blocked=np.inf, read_delay=0.05) for channel in range(n_channels)} logger.info("Using constant gap samplers") - sim_params = SimParams(gap_samplers=gap_samplers, bp_per_second=bp_per_second, default_unblock_duration=unblock_duration, chunk_size=chunk_size, seed=np.random.default_rng(seed)) - - simulator = ONTSimulator( - read_pool=read_pool, - reads_writer=reads_writer, - sim_params=sim_params, - ) - - ####### Starting the gRPC server ####### - port, server, unique_id = launchable_device_grpc_server(simulator, port=port) - assert port != 0, f"port {port} already in use" - - logger.info(f"Starting gRPC server on port {port}") - with manage_grpc_server(server): - logger.info("Started gRPC server") - try: - if not dont_start: - logger.info("Starting the simulation") - simulator.start(acceleration_factor=acceleration_factor, log_interval=100) - else: - logger.info("Not starting the simulation, must be started manually (via a gRPC call), or remove the --dont_start flag") - signal.pause() # wait for keyboard interrupt - except KeyboardInterrupt: - pass - finally: - if simulator.stop(): - logger.info("Stopped simulation") - - simulator_stats_to_disk([simulator], output_dir=run_dir) - + sim_params = SimParams(gap_samplers=gap_samplers, bp_per_second=bp_per_second, default_unblock_duration=unblock_duration, min_chunk_size=min_chunk_size, seed=np.random.default_rng(seed)) + + with reads_writer, read_pool: + simulator = ONTSimulator( + read_pool=read_pool, + reads_writer=reads_writer, + sim_params=sim_params, + output_dir=run_dir, + ) + + ####### Starting the gRPC server ####### + port, server, unique_id = launchable_device_grpc_server(simulator, port=port) + assert port != 0, f"port {port} already in use" + + logger.info(f"Starting gRPC server on port {port}") + with manage_grpc_server(server): + logger.info("Started gRPC server") + + def stop_server(*args, **kwargs): + if simulator.stop(): + logger.info("Stopped simulation") + + with set_signal_handler(signal_type=signal.SIGINT, handler=stop_server): # catch keyboard interrupt (Ctrl+C) + if not dont_start: + logger.info("Starting the simulation") + simulator.start(acceleration_factor=acceleration_factor, log_interval=100) + else: + logger.info("Not starting the simulation, must be started manually (via a gRPC call), or remove the --dont_start flag") + signal.pause() # wait for keyboard interrupt, only for Linux, alternatively run a while loop with time.sleep(1) + + write_simulator_stats([simulator], output_dir=simulator.mk_run_dir) + + # todo: possibly remove since the same as live sequencing summary seqsum_filename = run_dir / "sequencing_summary.txt" logger.info(f"Writing sequencing summary file '{seqsum_filename}'") convert_simfasta_dir_to_seqsum(reads_writer.output_dir, seqsummary_filename=seqsum_filename) diff --git a/src/simreaduntil/usecase_helpers/readfish_plotting.py b/src/simreaduntil/usecase_helpers/readfish_plotting.py index 1496a15..199c316 100644 --- a/src/simreaduntil/usecase_helpers/readfish_plotting.py +++ b/src/simreaduntil/usecase_helpers/readfish_plotting.py @@ -42,15 +42,19 @@ def parse_line(line): return df -def plot_extra_basecalling_delay_per_iter(df, save_dir=None): +def plot_extra_basecalling_delay_per_iter(df, save_dir=None, n_points=200): """ Plots the extra basecalling delay per iteration. - One iteration is a called to get_basecalled_read_chunks() which logs the total time spent due to basecalling. + Sort by avg_extra_wait_time, then take the n_points largest. + + One iteration is a call to get_basecalled_read_chunks() which logs the total time spent due to basecalling. The basecalling starts right when the function is called, so if the processing of the basecalled data takes longer, there is no extra delay due to basecalling. """ - df = df.sample(min(len(df), 200)) + # df = df.sample(min(len(df), 200)) + # restrict to the largest rather than sample + df = df.sort_values("avg_extra_wait_time", ascending=False).iloc[:n_points] fig, (ax1, ax2, ax3) = plt.subplots(ncols=3, figsize=(15, 4)) # to show all in one plot @@ -61,24 +65,80 @@ def plot_extra_basecalling_delay_per_iter(df, save_dir=None): # twin2.spines.right.set_position(("axes", 1.2)) sns.lineplot(df, x="time", y="extra_wait_time", ax=ax1) - ax1.set_xlabel("Time (of iteration) (s)") + ax1.set_xlabel("Real time (of iteration) (s)") ax1.set_ylabel("Extra wait time (whole iteration) (s)") ax1.set_title("Extra waiting time (whole iteration)") sns.lineplot(df, x="time", y="avg_extra_wait_time", ax=ax2) - ax2.set_xlabel("Time (of iteration) (s)") + ax2.set_xlabel("Real time (of iteration) (s)") ax2.set_ylabel("Average waiting time per basepair (s)") ax2.set_title("Average extra waiting time") sns.lineplot(df, x="time", y="nb_basepairs", ax=ax3) - ax3.set_xlabel("Time (of iteration) (s)") + ax3.set_xlabel("Real time (of iteration) (s)") ax3.set_ylabel("Number of called basepairs at iteration") ax3.set_title("Number of called basepairs at iteration") - fig.suptitle("Extra delay due to basecalling (delaying ReadFish)") + fig.suptitle(f"Extra delay due to basecalling (delaying ReadFish), largest {n_points})") # wrt avg_extra_wait_time + + make_tight_layout(fig) + if save_dir is not None: + save_fig_and_pickle(fig, save_dir / f"readfish_extra_basecall_delay.{FIGURE_EXT}") + + return fig + +def plot_chunk_waiting_time(df, save_dir=None, n_points=200): + """ + Plots the time spent waiting for chunks from the device per iteration and time. + + Sorts by waiting_time, then takes the n_points largest. + """ + # df = df.sample(min(len(df), 200)) + # restrict to the largest rather than sample + df["iteration"] = df.index + 1 + df = df.sort_values("waiting_time", ascending=False).iloc[:n_points] + + fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(11, 4)) + + sns.lineplot(df, x="time", y="waiting_time", ax=ax1) + ax1.set_xlabel("Real time (of iteration) (s)") + ax1.set_ylabel("Time waiting for chunks (s)") + sns.lineplot(df, x="iteration", y="waiting_time", ax=ax2) + ax2.set_xlabel("ReadFish iteration") + ax2.set_ylabel("Time waiting for chunks (s)") + + fig.suptitle(f"Time waiting for chunks (largest {n_points})") make_tight_layout(fig) if save_dir is not None: - save_fig_and_pickle(fig, save_dir / f"extra_basecall_delay.{FIGURE_EXT}") + save_fig_and_pickle(fig, save_dir / f"readfish_chunks_waiting_time.{FIGURE_EXT}") + + return fig + +def plot_chunk_mapping_time(df, save_dir=None, n_points=200): + """ + Plots the time spent mapping chunks from the device per iteration and time. + Sorts by mapping_time, then takes the n_points largest. + """ + # df = df.sample(min(len(df), 200)) + # restrict to the largest rather than sample + df["iteration"] = df.index + 1 + df = df.sort_values("mapping_time", ascending=False).iloc[:n_points] + + fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(11, 4)) + + sns.lineplot(df, x="time", y="mapping_time", ax=ax1) + ax1.set_xlabel("Real time (of iteration) (s)") + ax1.set_ylabel("Time mapping chunks (s)") + sns.lineplot(df, x="iteration", y="mapping_time", ax=ax2) + ax2.set_xlabel("ReadFish iteration") + ax2.set_ylabel("Time mapping chunks (s)") + + fig.suptitle(f"Time mapping chunks (largest {n_points})") + + make_tight_layout(fig) + if save_dir is not None: + save_fig_and_pickle(fig, save_dir / f"readfish_chunks_mapping_time.{FIGURE_EXT}") + return fig def get_processing_time_per_read_over_time_df(log_filename): @@ -121,16 +181,16 @@ def plot_readfish_processing_time(df, save_dir=None): # twin2.spines.right.set_position(("axes", 1.2)) sns.lineplot(df, x="time", y="elapsed_time", ax=ax1) - ax1.set_xlabel("Time (of iteration) (s)") + ax1.set_xlabel("Real time (of iteration) (s)") ax1.set_ylabel("Elapsed time (whole iteration) (s)") sns.lineplot(df, x="time", y="elapsed_time_per_read", ax=ax2) - ax2.set_xlabel("Time (of iteration) (s)") + ax2.set_xlabel("Real time (of iteration) (s)") ax2.set_ylabel("Average elapsed time per read (s)") sns.lineplot(df, x="time", y="nb_reads", ax=ax3) - ax3.set_xlabel("Time (of iteration) (s)") + ax3.set_xlabel("Real time (of iteration) (s)") ax3.set_ylabel("Number of reads at iteration") - fig.suptitle("ReadFish processing time") + fig.suptitle("ReadFish processing time (sampled)") make_tight_layout(fig) if save_dir is not None: @@ -161,18 +221,67 @@ def parse_line(line): return df -def plot_throttle_over_time(df, save_dir=None): +def get_chunk_wait_time_over_time_df(log_filename): + """cumulative time waiting for chunks from the device per iteration""" + MARKER = "ReadFish time waiting for chunks: " + def parse_line(line): + # return time of log entry, throttle + log_time, remaining = line.split(" - ", maxsplit=1) + # convert log_time to time + log_time = datetime.datetime.strptime(log_time, "%Y-%m-%d %H:%M:%S,%f") + remaining = remaining.split(" --- ", maxsplit=1)[0] + remaining = remaining.split(MARKER)[1] + return log_time, float(remaining[:-1]) + + # line = "2023-12-16 11:51:53,349 - ReadFish time waiting for chunks: 0.01086s --- ru_gen.py:395 (simple_analysis) INFO ##" + # parse_line(line) + + with open(log_filename) as f: + info = [parse_line(line) for line in f if MARKER in line] + # info = list(itertools.islice((parse_line(line) for line in f if MARKER in line), 100)) + df = pd.DataFrame.from_records(info, columns=["time", "waiting_time"]) + if len(df) > 0: + df["time"] = (df["time"] - df["time"].iloc[0]).dt.total_seconds() + + return df + +def get_chunk_mapping_time_over_time_df(log_filename): + """cumulative time mapping chunks from the device per iteration""" + MARKER = "ReadFish mapping time for chunks: " + def parse_line(line): + # return time of log entry, throttle + log_time, remaining = line.split(" - ", maxsplit=1) + # convert log_time to time + log_time = datetime.datetime.strptime(log_time, "%Y-%m-%d %H:%M:%S,%f") + remaining = remaining.split(" --- ", maxsplit=1)[0] + remaining = remaining.split(MARKER)[1] + return log_time, float(remaining[:-1]) + + # line = "2023-12-16 11:51:53,349 - ReadFish mapping time for chunks: 0.01086s --- ru_gen.py:395 (simple_analysis) INFO ##" + # parse_line(line) + + with open(log_filename) as f: + info = [parse_line(line) for line in f if MARKER in line] + # info = list(itertools.islice((parse_line(line) for line in f if MARKER in line), 100)) + df = pd.DataFrame.from_records(info, columns=["time", "mapping_time"]) + if len(df) > 0: + df["time"] = (df["time"] - df["time"].iloc[0]).dt.total_seconds() + + return df + +def plot_throttle_over_time(df, save_dir=None, n_points=200): """Plot ReadFish throttle over time""" - df = df.sample(min(len(df), 200)) + # df = df.sample(min(len(df), 200)) + df = df.sort_values("throttle", ascending=False).iloc[:n_points] df.sort_values("time", inplace=True) fig, ax = plt.subplots() ax.plot(df["time"], df["throttle"]) sns.lineplot(df, x="time", y="throttle", ax=ax) - ax.set_xlabel("Time (s)") + ax.set_xlabel("Real time (s)") ax.set_ylabel("Throttle") - ax.set_title("Throttle over time") + ax.set_title(f"ReadFish Throttle over time (largest {n_points}, negative means too slow)") make_tight_layout(fig) if save_dir is not None: @@ -182,8 +291,12 @@ def plot_throttle_over_time(df, save_dir=None): if __name__ == "__main__": - log_filename = "/Volumes/mmordig/joblogs/job-13931078-0.err" - + log_filename = "/home/mmordig/ont_project_all/ont_project/runs/enrich_usecase/full_genome_run_sampler_per_window/log.txt" + # plt.get_backend() + # import matplotlib + # print(matplotlib.rcsetup.all_backends) + # plt.switch_backend('TkAgg') + proc_df = get_processing_time_per_read_over_time_df(log_filename) plot_readfish_processing_time(proc_df) @@ -192,4 +305,15 @@ def plot_throttle_over_time(df, save_dir=None): basecall_delay_df = get_extra_basecall_delay_over_time_df(log_filename) plot_extra_basecalling_delay_per_iter(basecall_delay_df) + + chunk_waiting_time_df = get_chunk_wait_time_over_time_df(log_filename) + plot_chunk_waiting_time(chunk_waiting_time_df) + + chunk_mapping_time_df = get_chunk_mapping_time_over_time_df(log_filename) + plot_chunk_mapping_time(chunk_mapping_time_df) + + # # also need X11 forwarding and XQuartz running on MacOS X, linux text mode is sufficient (graphical mode not required) + # import matplotlib.pyplot as plt + # plt.show() + \ No newline at end of file diff --git a/src/simreaduntil/usecase_helpers/readfish_wrappers.py b/src/simreaduntil/usecase_helpers/readfish_wrappers.py index 8622b07..2d5d437 100644 --- a/src/simreaduntil/usecase_helpers/readfish_wrappers.py +++ b/src/simreaduntil/usecase_helpers/readfish_wrappers.py @@ -127,7 +127,7 @@ def basecall_minknow(self, reads: Iterable[Tuple[int, ReadWrapper]], signal_dtyp (channel, read_number), read_id, sequence, sequence_length, quality """ - time_start = time.perf_counter_ns() # in nanoseconds, only offsets are correct + time_start = cur_ns_time() total_wait_time = 0 nb_called_bps = 0 for (channel, read_info) in reads: @@ -136,7 +136,7 @@ def basecall_minknow(self, reads: Iterable[Tuple[int, ReadWrapper]], signal_dtyp # to imitate the guppy basecaller which runs in parallel, we do not delay each time something is requested, but rather since the function was called nb_called_bps += len(read_info.seq) if self.time_per_bp > 0: - wait_time = nb_called_bps * self.time_per_bp - (time.perf_counter_ns() - time_start)/1_000_000_000 + wait_time = nb_called_bps * self.time_per_bp - (cur_ns_time() - time_start) if wait_time > 0: time.sleep(wait_time) total_wait_time += wait_time @@ -203,13 +203,15 @@ def __init__(self, index): @staticmethod def _map_seq(read_id, seq_len): - parsed = NanoSimId.from_str(read_id) + parsed_id = NanoSimId.from_str(read_id) - return NanoSimMapper.Alignment( + if parsed_id.read_type == "unaligned": + return [] + return [NanoSimMapper.Alignment( query_name=read_id, query_len=seq_len, query_start=0, query_end=seq_len, - target_strand=1 if parsed.direction == "F" else -1, target_name=parsed.chrom, target_len="*", target_start=parsed.ref_pos, target_end=parsed.ref_len, + target_strand=1 if parsed_id.direction == "F" else -1, target_name=parsed_id.chrom, target_len="*", target_start=parsed_id.ref_pos, target_end=parsed_id.ref_len, num_matches=seq_len, alignment_block_length=seq_len, mapping_quality=255 - ) + )] def map_reads_2(self, calls): """Align reads against a reference @@ -222,7 +224,7 @@ def map_reads_2(self, calls): """ for read_info, read_id, seq, seq_len, quality in calls: assert len(seq) == seq_len - yield read_info, read_id, seq_len, [self._map_seq(read_id, seq_len)] + yield read_info, read_id, seq_len, self._map_seq(read_id, seq_len) @contextmanager def replace_ru_mapper(replace): diff --git a/src/simreaduntil/usecase_helpers/simulator_with_readfish.py b/src/simreaduntil/usecase_helpers/simulator_with_readfish.py index cc75ed2..63bd33c 100644 --- a/src/simreaduntil/usecase_helpers/simulator_with_readfish.py +++ b/src/simreaduntil/usecase_helpers/simulator_with_readfish.py @@ -6,6 +6,8 @@ from contextlib import contextmanager import logging from pathlib import Path +import signal +import time import numpy as np import pysam from simreaduntil.shared_utils.debugging_helpers import is_test_mode @@ -13,12 +15,12 @@ from simreaduntil.shared_utils.logging_utils import add_comprehensive_stream_handler_to_logger, setup_logger_simple from simreaduntil.shared_utils.timing import cur_ns_time -from simreaduntil.shared_utils.utils import delete_dir_if_exists, dill_dump, dill_load, print_args +from simreaduntil.shared_utils.utils import delete_dir_if_exists, dill_dump, dill_load, print_args, set_signal_handler from simreaduntil.simulator.gap_sampling.constant_gaps_until_blocked import ConstantGapsUntilBlocked -from simreaduntil.simulator.readpool import ReadPoolFromFile, ReadPoolFromIterable -from simreaduntil.simulator.readswriter import ArrayReadsWriter, RotatingFileReadsWriter, SingleFileReadsWriter -from simreaduntil.simulator.simfasta_to_seqsum import convert_simfasta_dir_to_seqsum -from simreaduntil.simulator.simulator import ONTSimulator, convert_action_results_to_df, run_periodic_mux_scan_thread, simulator_stats_to_disk, stop_simulation_after_time_thread +from simreaduntil.simulator.readpool import ReadPoolFromFile, ReadPoolFromIterable, ThreadedReadPoolWrapper +from simreaduntil.simulator.readswriter import ArrayReadsWriter, CompoundReadsWriter, RotatingFileReadsWriter, SingleFileReadsWriter, ThreadedReadsWriterWrapper +from simreaduntil.simulator.simfasta_to_seqsum import SequencingSummaryWriter, convert_simfasta_dir_to_seqsum +from simreaduntil.simulator.simulator import ONTSimulator, convert_action_results_to_df, run_periodic_mux_scan_thread, write_simulator_stats, stop_simulation_after_time_thread from simreaduntil.simulator.simulator_client import DeviceGRPCClient from simreaduntil.simulator.simulator_params import SimParams from simreaduntil.simulator.simulator_server import launchable_device_grpc_server, manage_grpc_server @@ -48,30 +50,36 @@ def compute_nonselective_coverage(ref_genome_path, reads_file): """Compute coverage if all reads are played back without any selective sequencing, i.e. full reads""" ref_length = sum(get_ref_lengths(ref_genome_path).values()) - total_reads_length = sum(get_ref_lengths(reads_file).values()) + reads_file = Path(reads_file) + if reads_file.is_dir(): + total_reads_length = sum(sum(get_ref_lengths(x).values()) for x in reads_file.glob("*.fasta")) + else: + total_reads_length = sum(get_ref_lengths(reads_file).values()) return total_reads_length / ref_length -def get_reads_writer(run_dir: Path, rotating: bool): +def get_reads_writer(run_dir: Path, rotating_writeout: bool): # reads_writer = ArrayReadsWriter() # for debugging mostly mk_run_dir = run_dir / "reads" delete_dir_if_exists(mk_run_dir) mk_run_dir.mkdir() - if rotating: + if rotating_writeout: reads_writer = RotatingFileReadsWriter(mk_run_dir, "reads_", max_reads_per_file=4000) else: reads_writer = SingleFileReadsWriter(open(mk_run_dir / "reads.fasta", "w")) - reads_writer.output_dir = mk_run_dir + seqsum_writer = SequencingSummaryWriter(open(run_dir / "live_sequencing_summary.txt", "w")) + reads_writer = CompoundReadsWriter([reads_writer, seqsum_writer]) + reads_writer = ThreadedReadsWriterWrapper(reads_writer) return reads_writer def get_sim_params(sim_params_file, n_channels) -> SimParams: if sim_params_file is None: # take realistic params, otherwise ReadFish mapper (minimap2) will still not map after 12 (small) chunks sim_params = SimParams( - gap_samplers={f"{i+1}": ConstantGapsUntilBlocked(short_gap_length=0.4, long_gap_length=10.1, prob_long_gap=0, time_until_blocked=np.inf, read_delay=0) for i in range(n_channels)}, - bp_per_second=450, chunk_size=200, default_unblock_duration=0.1, seed=0, + gap_samplers={f"ch{i+1}": ConstantGapsUntilBlocked(short_gap_length=0.4, long_gap_length=10.1, prob_long_gap=0, time_until_blocked=np.inf, read_delay=0.1) for i in range(n_channels)}, + bp_per_second=450, min_chunk_size=200, default_unblock_duration=0.1, seed=0, ) else: logger.info(f"Loading simparams from '{sim_params_file}'") @@ -79,17 +87,29 @@ def get_sim_params(sim_params_file, n_channels) -> SimParams: if n_channels != sim_params.n_channels: logger.warning(f"Using sim_params.n_channels={sim_params.n_channels} instead of {n_channels} because it was saved in the sim_params_file") + assert sorted(list(sim_params.gap_samplers.keys())) == {f"ch{i+1}" for i in range(n_channels)} # assumed by downstream plotting scripts + return sim_params -def get_read_pool(reads_file, ref_genome_path): +def get_read_pool(reads_file_type, reads_file, ref_genome_path, n_channels, reads_len_range=None): """Get read pool either from reads_file or perfect reads from ref_fasta""" - if reads_file is None: + + if reads_file_type == "generate": # read_pool = ReadPoolFromIterable(random_nanosim_reads_gen(random_state=np.random.default_rng(3), length_range=(10, 50))) logger.info(f"Generating perfect reads without NanoSim using ref genome '{ref_genome_path}'") - read_pool = ReadPoolFromIterable(perfect_reads_gen(ref_genome_path, read_lens_range=(5_000, 10_000), random_state=np.random.default_rng(1))) + assert reads_len_range is not None + read_pool = ReadPoolFromIterable(perfect_reads_gen(ref_genome_path, read_lens_range=reads_len_range, random_state=np.random.default_rng(1))) + elif reads_file_type == "fasta": + logger.info("Reading in FASTA reads. pysam index creation may take some time.") + # read_pool = ReadPoolFromFile(reads_file=reads_file) + # read_pool = ReadPoolFromFile(reads_file_or_dir=reads_file) + read_pool = ReadPoolFromFile(reads_file_or_dir=reads_file, shuffle_rand_state=np.random.default_rng(3)) # use a different rng since not thread-safe! else: - logger.info("Reading in reads. pysam index creation may take some time.") - read_pool = ReadPoolFromFile(reads_file=reads_file) + logger.info("Creating slow5 read pool") + raise "slow5 currently unavailable" + # read_pool = Slow5ReadPool(reads_file, read_buffer=2*n_channels) # todo: remove read_buffer + + read_pool = ThreadedReadPoolWrapper(read_pool, queue_size=2*n_channels) return read_pool @contextmanager @@ -105,6 +125,35 @@ def wrap_simulator_in_grpc(simulator, use_grpc): # don't do anything yield simulator +""" +Auto-detect the reads file type + +Args: + reads_file (Path): path to the reads file or directory, possibly None + +Returns: + If reads file is None, "generate: + Else If reads file ends with fasta or is a directory containing fastas, "fasta" + Else If reads file ends with slow5/blow5 or is a directory containing slow5/blow5, "slow5" + Else raise ValueError +""" +def get_reads_file_type(reads_file: Path): + if reads_file is None: + return "generate" # generate reads + elif ReadPoolFromFile.can_handle(reads_file): + return "fasta" + # elif Slow5ReadPool.can_handle(reads_file): + # return "slow5" + else: + raise ValueError(f"Cannot determine reads file type for file '{reads_file}'") + + # if reads_file is None: + # return "generate" # generate reads + # elif reads_file.endswith(".fasta"): + # return "fasta" + # else: + # return "slow5" + def main(toml_file): """ Run ReadFish with the simulator @@ -132,9 +181,12 @@ def main(toml_file): realtime_run_duration = float(config["run_duration"]) / acceleration_factor reads_file = config.get("reads_file", None) + reads_len_range = config.get("reads_len_range", None) + if reads_len_range is not None: + assert isinstance(reads_len_range, list) ref_genome_path = config.get("ref_genome_path", None) sim_params_file = config.get("sim_params_file", None) - rotating = config.get("rotating", True) + rotating_writeout = config.get("rotating_writeout", True) use_grpc = config.get("use_grpc", False) if "mux_scan_period" in config: mux_scan_period = float(config["mux_scan_period"]) @@ -147,55 +199,75 @@ def main(toml_file): # readfish params readfish_config_file = Path(config["readfish_config_file"]) if "readfish_config_file" in config else None readfish_method = config.get("readfish_method", "targeted_seq") - assert readfish_method in ["unblock_all", "targeted_seq"] + assert readfish_method in ["control", "unblock_all", "targeted_seq"] logger.info(f"Running ReadFish with method '{readfish_method}'") #################################################### #################### SET UP RUN #################### #################################################### - if (ref_genome_path is not None) and (reads_file is not None): - logger.info(f"You will reach average coverage {compute_nonselective_coverage(ref_genome_path, reads_file=reads_file)}") + reads_file_type = config.get("reads_file_type", get_reads_file_type(reads_file)) + logger.info(f"Using reads file type '{reads_file_type}'") + if (ref_genome_path is not None) and reads_file_type == "fasta": + logger.info(f"(Without selseq,) You will reach average coverage {compute_nonselective_coverage(ref_genome_path, reads_file=reads_file)}") if run_dir.exists(): logger.warning(f"Run dir '{run_dir}' already exists") run_dir.mkdir(exist_ok=True) - reads_writer = get_reads_writer(run_dir, rotating=rotating) - read_pool = get_read_pool(reads_file, ref_genome_path=ref_genome_path) sim_params = get_sim_params(sim_params_file=sim_params_file, n_channels=int(config["n_channels"])) + reads_writer = get_reads_writer(run_dir, rotating_writeout=rotating_writeout) + read_pool = get_read_pool(reads_file_type=reads_file_type, reads_file=reads_file, ref_genome_path=ref_genome_path, n_channels=sim_params.n_channels, reads_len_range=reads_len_range) + # if isinstance(read_pool, Slow5ReadPool): + # logger.info("Detected slow5 reads, so adapting chunk size and bp_per_second") + # sim_params.bp_per_second = int(sim_params.bp_per_second * 4000/450) # todo: rename bp_per_second + # sim_params.min_chunk_size = int(sim_params.min_chunk_size * 4000/450) # When the simulation is accelerated, we decrease the batch size so that ReadFish can send out actions faster. # Higher acceleration factor means that reads will finish faster. # The throttle is decreased because there are less reads per batch and the acceleration is going faster. original_batch_size = int(config.get("readfish_batch_size", sim_params.n_channels)) - readfish_batch_size = max(1, round(original_batch_size / acceleration_factor)) - # readfish_batch_size = original_batch_size + # readfish_batch_size = max(1, round(original_batch_size / acceleration_factor)) + readfish_batch_size = original_batch_size readfish_throttle = float(config.get("readfish_throttle", 0.1)) * (readfish_batch_size / original_batch_size) / acceleration_factor logger.info(f"Running ReadFish with batch size {readfish_batch_size} and throttle {readfish_throttle}s") - simulator = ONTSimulator( + simulator : ONTSimulator = ONTSimulator( read_pool=read_pool, reads_writer=reads_writer, sim_params=sim_params, + output_dir=run_dir, ) def start_sim(): # start sim and mux scan thread logger.info(f"Starting the simulation with {simulator.n_channels} channels") simulator.start(acceleration_factor=acceleration_factor) + logger.info(f"Started the simulation") if mux_scan_period is not None: mux_scan_thread = run_periodic_mux_scan_thread(simulator, period=mux_scan_period, scan_duration=mux_scan_duration, acceleration_factor=acceleration_factor) mux_scan_thread.start() else: mux_scan_thread = None return mux_scan_thread - - try: - with wrap_simulator_in_grpc(simulator, use_grpc=use_grpc): - if readfish_method == "unblock_all": + + orig_simulator = simulator + with wrap_simulator_in_grpc(orig_simulator, use_grpc=use_grpc) as simulator: + # use a thread to stop it because simulator.stop() may never be able to acquire the mutex in the signal handler because the mutex may still be held by simulator.forward() or similar when the signal handler is running + with set_signal_handler(signal_type=signal.SIGINT, handler=lambda *args, **kwargs: stop_simulation_after_time_thread(simulator, t=0).start()): + if readfish_method == "control": + mux_scan_thread = start_sim() + + stop_thread = stop_simulation_after_time_thread(simulator, t=realtime_run_duration) + stop_thread.start() + + elif readfish_method == "unblock_all": mux_scan_thread = start_sim() - unblock_all(ReadUntilClientWrapper(simulator), duration=realtime_run_duration, batch_size=readfish_batch_size, throttle=readfish_throttle, unblock_duration=0.1) + stop_thread = stop_simulation_after_time_thread(simulator, t=realtime_run_duration) + stop_thread.start() + + unblock_all(ReadUntilClientWrapper(simulator), duration=realtime_run_duration, batch_size=readfish_batch_size, throttle=readfish_throttle, unblock_duration=0.1) + elif readfish_method == "targeted_seq": chunk_logger = get_chunk_logger(run_dir / "chunk_log.txt") paf_logger = get_paf_logger(run_dir / "mapping.paf") @@ -218,36 +290,37 @@ def start_sim(): stop_thread = stop_simulation_after_time_thread(simulator, t=realtime_run_duration) stop_thread.start() + targeted_seq(ReadUntilClientWrapper(simulator), batch_size=readfish_batch_size, throttle=readfish_throttle, unblock_duration=0.1, chunk_logger=chunk_logger, paf_logger=paf_logger, live_toml_path=live_toml, flowcell_size=simulator.n_channels, dry_run=False, run_info=run_info, conditions=conditions, mapper=mapper, caller=dummy_caller) - stop_thread.raise_if_error() + stop_thread.raise_if_error() + while simulator.is_running: + # stop call() returns False early, when another stop call is already running + time.sleep(0.5) - if mux_scan_thread is not None: - mux_scan_thread.raise_if_error() - - except KeyboardInterrupt: - pass - finally: - simulator.stop() - logger.info("Stopped simulation") + if mux_scan_thread is not None: + mux_scan_thread.raise_if_error() + simulator = orig_simulator # restore + # go outside grpc connection + write_simulator_stats([simulator], output_dir=simulator.mk_run_dir) - simulator_stats_to_disk([simulator], output_dir=run_dir) + read_pool.finish() seqsum_filename = None if isinstance(reads_writer, ArrayReadsWriter): - reads_writer.reads + # reads_writer.reads logger.info(reads_writer.extended_repr()) else: - assert isinstance(reads_writer, (SingleFileReadsWriter, RotatingFileReadsWriter)) - logger.info(reads_writer) + # logger.info(reads_writer) + # todo: seqsummary file written on the fly, remove write_seqsum_file arg, if write_seqsum_file: seqsum_filename = run_dir / "sequencing_summary.txt" logger.info(f"Writing sequencing summary file '{seqsum_filename}'") - convert_simfasta_dir_to_seqsum(reads_writer.output_dir, seqsummary_filename=seqsum_filename) + convert_simfasta_dir_to_seqsum(run_dir / "reads", seqsummary_filename=seqsum_filename) logger.info("Wrote sequencing summary file") logger.info("Done with simulation") diff --git a/src/simreaduntil/usecase_helpers/utils.py b/src/simreaduntil/usecase_helpers/utils.py index 70a40f8..9c712c4 100644 --- a/src/simreaduntil/usecase_helpers/utils.py +++ b/src/simreaduntil/usecase_helpers/utils.py @@ -39,7 +39,7 @@ from simreaduntil.simulator.simulator import get_simulator_delay_over_time_df, plot_sim_actions, plot_simulator_delay_over_time from simreaduntil.simulator.simulator_params import SimParams from simreaduntil.simulator.utils import set_package_log_level -from simreaduntil.usecase_helpers.readfish_plotting import get_extra_basecall_delay_over_time_df, get_processing_time_per_read_over_time_df, get_throttle_over_time_df, plot_extra_basecalling_delay_per_iter, plot_readfish_processing_time, plot_throttle_over_time +from simreaduntil.usecase_helpers.readfish_plotting import get_chunk_mapping_time_over_time_df, get_chunk_wait_time_over_time_df, get_extra_basecall_delay_over_time_df, get_processing_time_per_read_over_time_df, get_throttle_over_time_df, plot_chunk_mapping_time, plot_chunk_waiting_time, plot_extra_basecalling_delay_per_iter, plot_readfish_processing_time, plot_throttle_over_time logger = setup_logger_simple(__name__) """module logger""" @@ -74,7 +74,7 @@ def random_nanosim_reads_gen(random_state=np.random.default_rng(2), length_range # to load the FASTA file when the function is called rather than when the first read is requested (which may delay the simulation if an index has to be built first) @force_eval_generator_function -def perfect_reads_gen(fasta_filename: Path, read_lens_range, random_state=np.random.default_rng(1), nanosim_read_id=True): +def perfect_reads_gen(fasta_filename: Path, read_lens_range: tuple[int], random_state=np.random.default_rng(1), nanosim_read_id=True): """ Generate perfect reads that align to the reference genome @@ -367,7 +367,7 @@ def create_simparams_if_inexistent(sim_params_filename, seqsum_param_extr_file, random_state = np.random.default_rng(1) # todo2: one random state, also in on_sim sim_params = SimParams( gap_samplers={f"ch{i+1}": gap_sampler_maker(random_state=random_state)[1] for i in range(n_channels)}, - bp_per_second=compute_median_pore_speed(seqsum_df), chunk_size=200, default_unblock_duration=0.1, seed=0, + bp_per_second=compute_median_pore_speed(seqsum_df), min_chunk_size=200, default_unblock_duration=0.1, seed=0, ) logger.debug(f"Saving sim_params to file '{sim_params_filename}'") @@ -454,6 +454,8 @@ def create_figures(seqsum, run_dir, figure_dir=None, delete_existing_figure_dir= def plot_log_file_metrics(log_filename, save_dir=None): """Parse log file and plot metrics""" + logger.info(f"Plotting metrics from log file '{log_filename}' and saving to '{save_dir}'") + df = get_simulator_delay_over_time_df(log_filename) fig = plot_simulator_delay_over_time(df, save_dir=save_dir); logger.debug("Created 1 plot"); plt.close(fig) @@ -466,8 +468,14 @@ def plot_log_file_metrics(log_filename, save_dir=None): basecall_delay_df = get_extra_basecall_delay_over_time_df(log_filename) fig = plot_extra_basecalling_delay_per_iter(basecall_delay_df, save_dir=save_dir); logger.debug("Created 1 plot"); plt.close(fig) + chunk_waiting_time_df = get_chunk_wait_time_over_time_df(log_filename) + fig = plot_chunk_waiting_time(chunk_waiting_time_df, save_dir=save_dir); logger.debug("Created 1 plot"); plt.close(fig) + + chunk_mapping_time_df = get_chunk_mapping_time_over_time_df(log_filename) + fig = plot_chunk_mapping_time(chunk_mapping_time_df, save_dir=save_dir); logger.debug("Created 1 plot"); plt.close(fig) + def extract_errfile_from_condor_jobad(jobad_filename): - """Extract the file where stderr is redriected to from a condor jobad file""" + """Extract the file where stderr is redirected to from a condor jobad file""" # find first match of "Err = " with open(jobad_filename, "r") as f: for line in f: @@ -475,16 +483,16 @@ def extract_errfile_from_condor_jobad(jobad_filename): return line[6:].strip()[1:-1] # temporary hack to get rid of quotes; could use htcondor's pythonbindings instead: python package classad return None -def plot_condor_log_file_metrics(save_dir=None): - """Parse log filename from condor job add, then plot metrics from log""" - jobad_filename = os.environ.get("_CONDOR_JOB_AD", None) - if jobad_filename is not None: - # parse log filename from condor job ad, then process it - log_filename = extract_errfile_from_condor_jobad(jobad_filename) - if log_filename is not None: - logger.info(f"Plotting metrics from condor log file '{log_filename}'") - plot_log_file_metrics(log_filename, save_dir=save_dir) - else: - logger.warning(f"Did not find log file in condor job ad: {jobad_filename}") - else: - logger.warning("Did not find condor job ad environment variable '_CONDOR_JOB_AD', cannot plot metrics") \ No newline at end of file +# def plot_condor_log_file_metrics(save_dir=None): +# """Parse log filename from condor job add, then plot metrics from log""" +# jobad_filename = os.environ.get("_CONDOR_JOB_AD", None) +# if jobad_filename is not None: +# # parse log filename from condor job ad, then process it +# log_filename = extract_errfile_from_condor_jobad(jobad_filename) +# if log_filename is not None: +# logger.info(f"Plotting metrics from condor log file '{log_filename}'") +# plot_log_file_metrics(log_filename, save_dir=save_dir) +# else: +# logger.warning(f"Did not find log file in condor job ad: {jobad_filename}") +# else: +# logger.warning("Did not find condor job ad environment variable '_CONDOR_JOB_AD', cannot plot metrics") \ No newline at end of file diff --git a/tests/shared_utils/test_nanosim_parsing.py b/tests/shared_utils/test_nanosim_parsing.py index 9298940..5dae6ee 100644 --- a/tests/shared_utils/test_nanosim_parsing.py +++ b/tests/shared_utils/test_nanosim_parsing.py @@ -11,10 +11,10 @@ def test_parsing(): nanosim_id = NanoSimId.from_str(nanosim_id_str) assert nanosim_id.chrom == "Human-chr11-NC-000011" assert nanosim_id.ref_pos == 76599 - assert nanosim_id.ref_len == 9967 - assert nanosim_id.direction == "F" assert nanosim_id.read_nb == "proc0:0" + assert nanosim_id.direction == "F" assert nanosim_id.head_len == 0 + assert nanosim_id.ref_len == 9967 assert nanosim_id.tail_len == 0 assert nanosim_id.read_type == "aligned" assert str(nanosim_id) == nanosim_id_str @@ -34,6 +34,17 @@ def test_parsing(): # reverse strand (R): 76599 + 9967 - 1001 = 85565 assert str(NanoSimId.from_str("Human-chr11-NC-000011_76599_aligned_proc0:0_R_0_9967_0").change_ref_len(1001)) == "Human-chr11-NC-000011_85565_aligned_proc0:0m_R_0_1001_0" # adds m to read_nb +def test_parsing_unaligned(): + nanosim_id = NanoSimId.from_str("genome1-chr-6_236227_unaligned_proc5:16_R_0_16119_0") + assert nanosim_id.chrom == "genome1-chr-6" + assert nanosim_id.ref_pos == 236227 + assert nanosim_id.read_type == "unaligned" + assert nanosim_id.read_nb == "proc5:16" + assert nanosim_id.direction == "R" + assert nanosim_id.head_len == 0 + assert nanosim_id.ref_len == 16119 + assert nanosim_id.tail_len == 0 + def test_normalize_seq_name(): assert normalize_seq_name("chr1 extra_info more-info hello") == "chr1-extra-info-more-info-hello" assert normalize_seq_name("chr1.aa extra_info more-info hello") == "chr1" diff --git a/tests/shared_utils/test_timing.py b/tests/shared_utils/test_timing.py index bcf7a68..0935857 100644 --- a/tests/shared_utils/test_timing.py +++ b/tests/shared_utils/test_timing.py @@ -48,7 +48,7 @@ def test_step_and_total_elapsed_timer(): assert in_interval(time_since_last_call, (0.4, 0.42)) assert in_interval(time_since_last_reset, (0.4, 0.42)) -def test_cur_time_ns(): +def test_cur_ns_time(): # less than 1 microsecond deviation # print(cur_ns_time()) time_difference = time.time_ns()/1_000_000_000 - cur_ns_time() diff --git a/tests/shared_utils/test_utils.py b/tests/shared_utils/test_utils.py index ae73bd7..cb3697d 100644 --- a/tests/shared_utils/test_utils.py +++ b/tests/shared_utils/test_utils.py @@ -1,7 +1,11 @@ +import signal import subprocess +import sys +import threading import time import pytest -from simreaduntil.shared_utils.utils import dill_dump, dill_load, force_eval_generator, force_eval_generator_function, get_file_content, is_sorted, num_lines_in_file, print_cmd_and_run, subset_dict, tqdm_with_name, get_some_value_from_dict +from simreaduntil.shared_utils.utils import MutableValue, StoppableQueue, dill_dump, dill_load, force_eval_generator, force_eval_generator_function, get_file_content, is_sorted, num_lines_in_file, print_cmd_and_run, record_gen_fcn_waiting_time, set_signal_handler, subset_dict, tee_stdouterr_to_file, tqdm_with_name, get_some_value_from_dict +from simreaduntil.shared_utils.utils import record_gen_waiting_time def test_is_sorted(): import numpy as np @@ -104,3 +108,138 @@ def compute_data(): yield i for i in tqdm_with_name((i, i) for i in compute_data()): time.sleep(0.1) + +def test_set_signal_handler(): + print_method = lambda *args, **kwargs: None + # print_method = print + + sleep_time = 0.05 + class StoppableGen: + def __init__(self): + self.stop = False + self.state = 0 + self.invalid_state = False # to signal when state is invalid, to ensure we do not exit anywhere in the code + + def gen_numbers(self, max_val): + while self.state < max_val: + self.invalid_state = False + if self.stop: + print_method("Checked stop condition, stopping") + break + self.invalid_state = True + yield self.state + prev_state = self.state + print_method("Sleep") + time.sleep(sleep_time) + print_method("after sleep") + self.state = prev_state + 1 + self.invalid_state = False + + def on_keyboard_interrupt(signum, frame): + print_method("Keyboard interrupt, setting stop") + gen.stop = True + + def raise_sigint(): + time.sleep(5*sleep_time) + signal.raise_signal(signal.SIGINT) + threading.Thread(target=raise_sigint).start() + + gen = StoppableGen() + with set_signal_handler(signal.SIGINT, on_keyboard_interrupt): + for x in gen.gen_numbers(100): + print_method(x) + assert not gen.invalid_state + print_method("Continuing") + gen.stop = False + print_method(list(gen.gen_numbers(gen.state + 5))) + assert not gen.invalid_state + +def test_tee_stdouterr_to_file(tmp_path): + with tee_stdouterr_to_file(tmp_path / "teeing", mode="w"): + for i in range(5): + print(f"out{i}") + print(f"err{i}", file=sys.stderr) + # time.sleep(0.1) + assert (tmp_path / "teeing.out").read_text() == "".join(f"out{i}\n" for i in range(5)) + assert (tmp_path / "teeing.err").read_text() == "".join(f"err{i}\n" for i in range(5)) + +def test_record_gen_waiting_time(): + def produce_data(): + for i in range(5): + time.sleep(0.1) + yield i + time.sleep(0.5) + + wait_time = MutableValue() + for x in record_gen_waiting_time(produce_data(), wait_time): + print(x) + if x >= 3: + # break early to check this is handled as well when the generator is not exhausted + break + # print(wait_time.value) + assert 0.4 - 0.05 <= wait_time.value <= 0.4 + 0.05 + +def test_record_gen_fcn_waiting_time(): + def produce_data(): + for i in range(5): + time.sleep(0.1) + yield i + time.sleep(0.5) + + def transform_data(gen): + for x in gen: + time.sleep(0.2) + yield x + + transform_time = MutableValue() + for x in record_gen_fcn_waiting_time(transform_data, produce_data(), transform_time): + if x >= 3: + # break early to check this is handled as well when the generator is not exhausted + break + assert 0.2*4-0.05 <= transform_time.value <= 0.2*4+0.05 + + # as opposed to measuring the total time + wait_time = MutableValue() + for x in record_gen_waiting_time(transform_data(produce_data()), wait_time): + print(x) + if x >= 3: + # break early to check this is handled as well when the generator is not exhausted + break + # print(wait_time.value) + assert (0.2+0.1)*4-0.05 <= wait_time.value <= (0.2+0.1)*4+0.05 + +def put_item_onto_queue(queue, i, when): + # print(f"Putting {i} onto queue at {when}") + def f(): + queue.put(i) + print(f"Put item '{i}' onto queue after {when}s") + threading.Timer(when, f).start() + +def test_StoppableQueue_NonBlockingGet(): + queue = StoppableQueue(2) + queue.put(1) + + # non-blocking get + assert queue.get(block=False) == 1 + put_item_onto_queue(queue, 2, 0.1) + # item not yet on queue + with pytest.raises(queue.Empty): + queue.get(block=False) + # block until item on queue + assert queue.get() == 2 + +def test_StoppableQueue_FullQueueStopped(): + queue = StoppableQueue(2) + queue.put(3) + queue.put(4) + # stop queue, so next get() will raise exception + queue.stop() + with pytest.raises(StoppableQueue.QueueStoppedException): + queue.get() + +def test_StoppableQueue_EmptyQueueStopped(): + queue = StoppableQueue(2) + # empty queue that is stopped after some time + threading.Timer(0.1, queue.stop).start() + with pytest.raises(StoppableQueue.QueueStoppedException): + queue.get() \ No newline at end of file diff --git a/tests/simulator/test_channel.py b/tests/simulator/test_channel.py index 8edcd8b..cdd2641 100644 --- a/tests/simulator/test_channel.py +++ b/tests/simulator/test_channel.py @@ -21,7 +21,7 @@ def sim_params() -> SimParams: return SimParams( gap_samplers={"channel1": ConstantGapsUntilBlocked(short_gap_length=0.4, long_gap_length=10.1, prob_long_gap=0, time_until_blocked=np.inf, read_delay=0)}, - bp_per_second=10, chunk_size=4, default_unblock_duration=1.4, seed=0, + bp_per_second=10, min_chunk_size=4, default_unblock_duration=1.4, seed=0, ) @pytest.fixture() @@ -88,15 +88,16 @@ def test_channel_stats(sim_params, channel_stats: ChannelStats): chan.start(t_start) chan.forward(t_start + 1.1 + eps) + # initial read gap of 0.4 channel_stats.reads.start_and_add_time(0.7 + eps, nb_new_bps=7) assert chan.stats == channel_stats - chan.get_new_chunks() - channel_stats.reads.number_bps_requested = 4 + chan.get_new_samples() + channel_stats.reads.number_bps_requested = 7 assert chan.stats == channel_stats chan.stop_receiving() - chan.get_new_chunks() # no chunks + chan.get_new_samples() # no chunks channel_stats.reads.cur_number_stop_receiving = 1 assert chan.stats == channel_stats @@ -104,10 +105,16 @@ def test_channel_stats(sim_params, channel_stats: ChannelStats): channel_stats.reads.cur_number_stop_receiving = 1 assert chan.stats == channel_stats + chan.forward(t_start + 1.3 + eps) + chan.get_new_samples() # no chunks since less than min chunk size + channel_stats.reads.add_time(0.2, nb_new_bps=2) + assert chan.stats == channel_stats + chan.forward(t_start + 1.7 + eps) - channel_stats.reads.add_time(0.6, nb_new_bps=6) + channel_stats.reads.add_time(0.4, nb_new_bps=4) assert chan.stats == channel_stats + # read ends and gap starts chan.forward(t_start + 1.9 + eps) channel_stats.reads.add_time_and_finish(0.1, nb_new_bps=1, stopped_receiving=True) channel_stats.short_gaps.start_and_add_time(0.1) @@ -176,13 +183,13 @@ def test_stopreceiving_then_unblock(sim_params, channel_stats: ChannelStats): chan.start(t_start) chan.forward(t_start + 1.0 + eps) - chan.get_new_chunks() + chan.get_new_samples() assert chan.stop_receiving() == StoppedReceivingResponse.STOPPED_RECEIVING assert chan.stop_receiving() == StoppedReceivingResponse.ALREADY_STOPPED_RECEIVING channel_stats.reads.start_and_add_time(0.6 + eps, nb_new_bps=6) channel_stats.reads.cur_number_stop_receiving += 1 - channel_stats.reads.number_bps_requested += 4 + channel_stats.reads.number_bps_requested += 6 assert chan.stats == channel_stats assert chan.unblock(0.3) @@ -237,7 +244,7 @@ def test_channel_restart(sim_params, channel_stats: ChannelStats): assert channel_stats.n_channels_running == 0 assert chan.stats == channel_stats -def test_get_new_chunks(sim_params): +def test_get_new_samples(sim_params): # reads of length 14, 10, 10, 10 read_pool = ReadPoolFromIterable(gen_from_list((("read1", "AAAAGGGGCCCCTT"), ("read2", "TTTTAAAACC"), ("read3", "TTTTAAAACC"), ("read4", "TTTTAAAACC")))) reads_writer = ArrayReadsWriter() @@ -247,13 +254,13 @@ def test_get_new_chunks(sim_params): chan.start(-0.4) chan.forward(0.3 + eps) assert [x[:2] for x in reads_writer.reads] == [] - assert chan.get_new_chunks()[:2] == ("", "read1") + assert chan.get_new_samples()[:2] == ("", "read1") chan.forward(0.4 + eps) - assert chan.get_new_chunks()[:2] == ("AAAA", "read1") - assert chan.get_new_chunks()[:2] == ("", "read1") # already returned chunks + assert chan.get_new_samples()[:2] == ("AAAA", "read1") + assert chan.get_new_samples()[:2] == ("", "read1") # already returned chunks assert [x[:2] for x in reads_writer.reads] == [] chan.forward(1.3 + eps) - assert chan.get_new_chunks()[:2] == ("GGGGCCCC", "read1") + assert chan.get_new_samples()[:2] == ("GGGGCCCCT", "read1") assert [x[:2] for x in reads_writer.reads] == [] chan.forward(1.4 + eps) assert [x[:2] for x in reads_writer.reads] == [("read1", "AAAAGGGGCCCCTT")] @@ -262,13 +269,14 @@ def test_get_new_chunks(sim_params): chan.forward(0.4 + 2.4 + eps) # 14+10 assert [x[:2] for x in reads_writer.reads] == [("read1", "AAAAGGGGCCCCTT"), ('read2', 'TTTTAAAACC')] - assert chan.get_new_chunks()[:2] == ("", None) + assert chan.get_new_samples()[:2] == ("", None) chan.forward(2*0.4 + 3.1 + eps) # 14+10+7 - assert chan.get_new_chunks()[:2] == ("TTTT", "read3") + assert chan.get_new_samples()[:2] == ("TTTTAAA", "read3") chan.stop() - chan.get_new_chunks() # no exception + chan.get_new_samples() # no exception + # todo: remove # ax = plot_channels([chan], time_interval=[0.9, 6.2], figsize=(6, 2)) # ax.figure.show() @@ -279,16 +287,18 @@ def test_read_stop_receiving(sim_params): chan.start(2-0.4) chan.forward(2.6 + eps) - assert chan.get_new_chunks()[:2] == ("AAAA", "read1") + assert chan.get_new_samples()[:2] == ("AAAAGG", "read1") assert chan.stop_receiving() == StoppedReceivingResponse.STOPPED_RECEIVING assert chan.stop_receiving() == StoppedReceivingResponse.ALREADY_STOPPED_RECEIVING - assert chan.get_new_chunks()[:2] == ("", "read1") + assert chan.get_new_samples()[:2] == ("", "read1") + chan.forward(2.9 + eps) + assert chan.get_new_samples()[:2] == ("", "read1") assert chan.stop_receiving(read_id="inexistent") == StoppedReceivingResponse.MISSED chan.forward(3.5 + eps) assert chan.stop_receiving() == StoppedReceivingResponse.MISSED - assert chan.get_new_chunks()[:2] == ("", None), "in a gap" + assert chan.get_new_samples()[:2] == ("", None), "in a gap" def test_mux_scan(sim_params, channel_stats): read_pool = ReadPoolFromIterable(gen_from_list((("read1", "AAAAGGGGCCCCTT"), ("read2", "TTTTAAAACC")))) @@ -338,7 +348,7 @@ def test_poreblockage_continues_after_mux_scan(): sim_params = SimParams( gap_samplers={"channel1": ConstantGapsUntilBlocked(short_gap_length=0.4, long_gap_length=10.4, prob_long_gap=0.9, time_until_blocked=np.inf, read_delay=0)}, - bp_per_second=10, chunk_size=4, default_unblock_duration=1.4, seed=0, + bp_per_second=10, min_chunk_size=4, default_unblock_duration=1.4, seed=0, ) read_pool = ReadPoolFromIterable(gen_from_list((("read1", "AAAAGGGGCCCCTT"), ("read2", "TTTTAAAACC")))) reads_writer = ArrayReadsWriter() @@ -386,7 +396,7 @@ def test_plotting(sim_params): # print(chan.cur_elem) pass else: - chunks = chan.get_new_chunks()[0] + chunks = chan.get_new_samples()[0] # if len(chunks) > 0: # print(f"{delta_t}: {chunks}") channels.append(chan) @@ -408,7 +418,7 @@ def test_channel_normal_operation(sim_params): chan.start(t_start) for t in (t_start + 1e-8 + np.arange(0, 3, 0.05)): chan.forward(t) - chunks = chan.get_new_chunks()[0] + chunks = chan.get_new_samples()[0] if len(chunks) > 0: print(f"{t}: {chunks}")#, end=", ") @@ -451,7 +461,7 @@ def perform_random_channel_ops(chan: Channel, random_state: np.random.Generator, chan.forward(t_duration + 0.01, delta=True) if random_state.uniform() < 0.2: - chan.get_new_chunks() + chan.get_new_samples() # finish simulation nb_actions["sim_stopped_unblock"] += int(isinstance(chan.cur_elem, ChunkedRead)) @@ -495,7 +505,7 @@ def test_random_operations(sim_params, channel_write_zero_length_reads): # make all elements roughly the same length, reads have length 8-17 -> take about 1.2s sim_params = SimParams( gap_samplers={"channel1": ConstantGapsUntilBlocked(short_gap_length=1.2, long_gap_length=1.2, prob_long_gap=0.15, time_until_blocked=np.inf, read_delay=0)}, - bp_per_second=10, chunk_size=4, default_unblock_duration=1.2, seed=0, + bp_per_second=10, min_chunk_size=4, default_unblock_duration=1.2, seed=0, ) plotted_once = False diff --git a/tests/simulator/test_channel_element.py b/tests/simulator/test_channel_element.py index 7359904..44bef28 100644 --- a/tests/simulator/test_channel_element.py +++ b/tests/simulator/test_channel_element.py @@ -7,26 +7,22 @@ from simreaduntil.simulator.channel_element import ShortGap, ChunkedRead as _ChunkedRead, NoReadLeftGap, LongGap, ReadDescriptionParser, ReadEndReason, end_reason_to_ont_map -ChunkedRead = lambda *args, **kwargs: _ChunkedRead(*args, **kwargs, read_speed=10, chunk_size=4) +ChunkedRead = lambda *args, **kwargs: _ChunkedRead(*args, **kwargs, read_speed=10, min_chunk_size=4) eps = 1e-8 # small delay (to avoid issues with rounding errors when geting chunks up to time <= t) def test_readended_map(): assert end_reason_to_ont_map[ReadEndReason.UNBLOCKED.value] == "data_service_unblock_mux_change" -def test_nb_basepairs(): +def test_actual_seq_length(): # test basic functions chunked_read = ChunkedRead("read1", "111122223333444455", 10.1) assert chunked_read._nanosim_read_id is None - assert chunked_read._nb_chunks == 5 - assert chunked_read._chunk_end_positions == [4, 8, 12, 16, 18] - assert chunked_read._get_chunks(2, 4) == "33334444" - assert chunked_read._get_chunks(4, 10) == "55" - assert chunked_read.nb_basepairs(10.1+eps) == 0 - assert chunked_read.nb_basepairs(10.18+eps) == 0 - assert chunked_read.nb_basepairs(10.2+eps) == 1 - assert chunked_read.nb_basepairs(10.35+eps) == 2 - assert chunked_read.nb_basepairs(13) == len(chunked_read._full_seq) + assert chunked_read.actual_seq_length(10.1+eps) == 0 + assert chunked_read.actual_seq_length(10.18+eps) == 0 + assert chunked_read.actual_seq_length(10.2+eps) == 1 + assert chunked_read.actual_seq_length(10.35+eps) == 2 + assert chunked_read.actual_seq_length(13) == len(chunked_read._full_seq) assert chunked_read.t_end == approx(10.1 + 1.8) assert chunked_read.full_duration() == approx(1.8) @@ -37,8 +33,8 @@ def test_nb_basepairs(): chunked_read.finish(10.9+eps, end_reason=ReadEndReason.UNBLOCKED) assert chunked_read.t_end == 10.9+eps - assert chunked_read.nb_basepairs(10.9+eps) == 8 - assert chunked_read.nb_basepairs_full() == 18 + assert chunked_read.actual_seq_length(10.9+eps) == 8 + assert chunked_read.full_seq_length() == 18 nanosim_id = "chr11_77_aligned_proc0:0_F_0_36_0" chunked_read = ChunkedRead(nanosim_id, "111122223333444455", 10.1) @@ -48,68 +44,101 @@ def test_nb_basepairs(): # issue previously due to floating point chunked_read = ChunkedRead("read1", "111122223", 28.4) t_end = 29.299999999999997 - chunked_read.has_finished_by(t_end) - assert chunked_read.nb_basepairs(t_end) == 9 + assert chunked_read.has_finished_by(t_end) + assert chunked_read.actual_seq_length(t_end) == 9 -def test_chunks(): - # check get_new_chunks - chunked_read = ChunkedRead("read1", "111122223333444455", 10.1) - assert not chunked_read.all_chunks_consumed() +def test_get_samples(): + # check get_new_samples + chunked_read = ChunkedRead("read1", "111112222222222333333", 10.1) + assert not chunked_read.all_samples_consumed() # 1 bp emitted every 0.1 seconds, add small tolerance if it is just on the edge - assert chunked_read.get_new_chunks(8.9) == ("", "read1", 0) - assert chunked_read.nb_basepairs_returned() == 0 - assert chunked_read.get_new_chunks(10.1+0.3+eps) == ("", "read1", 0) - assert chunked_read.get_new_chunks(10.1+0.4+eps) == ("1111", "read1", 4) - assert chunked_read.nb_basepairs_returned() == 4 - assert chunked_read.get_new_chunks(10.1+0.4+eps) == ("", "read1", 4), "no new chunks since last time" - assert not chunked_read.all_chunks_consumed() - assert chunked_read.get_new_chunks(10.1+0.4+eps) == ("", "read1", 4), "no new chunks since last time" - assert chunked_read.get_new_chunks(10.1+0.4+1+eps) == ("22223333", "read1", 12) - assert chunked_read.nb_basepairs_returned() == 12 - assert chunked_read.get_new_chunks(10.1+0.4+1.+0.7+eps) == ("444455", "read1", 18) - assert chunked_read.get_new_chunks(130.2) == ("", "read1", 18) - assert chunked_read.nb_basepairs_returned() == 18 - assert chunked_read.all_chunks_consumed() + assert chunked_read.get_new_samples(8.9) == ("", "read1", 0) + assert chunked_read.num_samples_returned() == 0 + assert chunked_read.get_new_samples(10.1+0.3+eps) == ("", "read1", 0) + assert chunked_read.num_samples_returned() == 0 + assert chunked_read.get_new_samples(10.1+0.5+eps) == ("11111", "read1", 5) + assert chunked_read.num_samples_returned() == 5 + assert chunked_read.get_new_samples(10.1+0.5+eps) == ("", "read1", 5), "no new chunks since last time" + assert not chunked_read.all_samples_consumed() + assert chunked_read.get_new_samples(10.1+0.5+eps) == ("", "read1", 5), "no new chunks since last time" + assert chunked_read.get_new_samples(10.1+0.5+1+eps) == ("2222222222", "read1", 15) + assert chunked_read.num_samples_returned() == 15 + assert chunked_read.get_new_samples(10.1+0.5+1.+0.7+eps) == ("333333", "read1", 21) + assert chunked_read.get_new_samples(130.2) == ("", "read1", 21) + assert chunked_read.num_samples_returned() == 21 + assert chunked_read.all_samples_consumed() # test with NanoSim-like read, ref_len = 36, start position = 77 # 36/18 = 2 nanosim_read_id = "chr11_77_aligned_proc0:0_F_0_36_0" chunked_read = ChunkedRead(nanosim_read_id, "111122223333444455", 10.1) - assert chunked_read.get_new_chunks(10.1+0.9+eps) == ("11112222", nanosim_read_id, 16) - assert chunked_read.get_new_chunks(10.1+3.9+eps) == ("3333444455", nanosim_read_id, 36) + assert chunked_read.get_new_samples(10.1+0.9+eps) == ("111122223", nanosim_read_id, 2 * 9) + assert chunked_read.get_new_samples(10.1+3.9+eps) == ("333444455", nanosim_read_id, 2 * 18) # test stop_receiving chunked_read = ChunkedRead("read1", "111122223333444455", 10.1) chunked_read.stop_receiving() - assert chunked_read.get_new_chunks(10.1+0.9+eps) == ("", "read1", 0) + assert chunked_read.get_new_samples(10.1+0.9+eps) == ("", "read1", 0) chunked_read = ChunkedRead("read1", "111122223333444455", 10.1) - assert chunked_read.get_new_chunks(10.1+0.9+eps) == ("11112222", "read1", 8) + assert chunked_read.get_new_samples(10.1+0.9+eps) == ("111122223", "read1", 9) chunked_read.stop_receiving() - assert chunked_read.get_new_chunks(10.1+1.4+eps) == ("", "read1", 8) + assert chunked_read.get_new_samples(10.1+1.4+eps) == ("", "read1", 9) + +def test__estimate_ref_len(): + head_len = 4 + tail_len = 6 + ref_len = 16 + # seq_len central part: 8 + + direction = "F" + nanosim_read_id = f"chr11_77_aligned_proc0:0_{direction}_{head_len}_{ref_len}_{tail_len}" + chunked_read = ChunkedRead(nanosim_read_id, "111122222222333333", 10.1) + estimated_ref_length = (7 - head_len) * (16/8) + assert chunked_read._estimate_ref_len(7) == estimated_ref_length + assert chunked_read.get_new_samples(10.1+0.7+eps) == ("1111222", nanosim_read_id, estimated_ref_length) + + # same with reverse + direction = "R" + nanosim_read_id = f"chr11_77_aligned_proc0:0_{direction}_{head_len}_{ref_len}_{tail_len}" + chunked_read = ChunkedRead(nanosim_read_id, "111111222222223333", 10.1) + estimated_ref_length = (7 - tail_len) * (16/8) + assert chunked_read._estimate_ref_len(7) == estimated_ref_length + assert chunked_read.get_new_samples(10.1+0.7+eps) == ("1111112", nanosim_read_id, estimated_ref_length) + + # test when in head or in tail + direction = "R" + nanosim_read_id = f"chr11_77_aligned_proc0:0_{direction}_{head_len}_{ref_len}_{tail_len}" + chunked_read = ChunkedRead(nanosim_read_id, "111111222222223333", 10.1) + assert chunked_read._estimate_ref_len(5) == 0 + assert chunked_read.get_new_samples(10.1+0.5+eps) == ("11111", nanosim_read_id, 0) + + direction = "R" + nanosim_read_id = f"chr11_77_aligned_proc0:0_{direction}_{head_len}_{ref_len}_{tail_len}" + chunked_read = ChunkedRead(nanosim_read_id, "111111222222223333", 10.1) + assert chunked_read._estimate_ref_len(15) == 16 # 15 >= 6 + 8 + assert chunked_read.get_new_samples(10.1+1.5+eps) == ("111111222222223", nanosim_read_id, 16) def test_chunks_with_delay(): # extra delay before actual read starts chunked_read = ChunkedRead("read1", "111122223333444455", 10.1, t_delay=2.1) - assert chunked_read._nb_chunks == 5 - assert chunked_read._chunk_end_positions == [4, 8, 12, 16, 18] - assert chunked_read.nb_basepairs(10.4) == 0 - assert chunked_read.nb_basepairs(10.1 + 2.15) == 0 - assert chunked_read.nb_basepairs(10.1 + 2.25) == 1 - assert chunked_read.nb_basepairs(10.1 + 3.25) == 11 + assert chunked_read.actual_seq_length(10.4) == 0 + assert chunked_read.actual_seq_length(10.1 + 2.15) == 0 + assert chunked_read.actual_seq_length(10.1 + 2.25) == 1 + assert chunked_read.actual_seq_length(10.1 + 3.25) == 11 assert chunked_read.t_end == approx(10.1 + 2.1 + 1.8) assert chunked_read.full_duration() == approx(1.8 + 2.1) - assert chunked_read.get_new_chunks(8.9) == ("", "read1", 0) - assert chunked_read.get_new_chunks(10.5) == ("", "read1", 0) - assert chunked_read.get_new_chunks(10.1 + 2.3) == ("", "read1", 0) - assert chunked_read.get_new_chunks(10.1+2.1+0.4+eps) == ("1111", "read1", 4) - assert chunked_read.get_new_chunks(10.1+5+eps) == ("22223333444455", "read1", 18) + assert chunked_read.get_new_samples(8.9) == ("", "read1", 0) + assert chunked_read.get_new_samples(10.5) == ("", "read1", 0) + assert chunked_read.get_new_samples(10.1 + 2.3) == ("", "read1", 0) + assert chunked_read.get_new_samples(10.1+2.1+0.4+eps) == ("1111", "read1", 4) + assert chunked_read.get_new_samples(10.1+5+eps) == ("22223333444455", "read1", 18) # test finish nanosim_read_id = "chr11_77_aligned_proc0:0_F_0_36_0" chunked_read = ChunkedRead(nanosim_read_id, "111122223333444455", 0.2, t_delay=2.1) - assert chunked_read.get_new_chunks(0.2+2.1+0.9+eps) == ("11112222", nanosim_read_id, 16) # 2 * 8 = 16 + assert chunked_read.get_new_samples(0.2+2.1+0.9+eps) == ("111122223", nanosim_read_id, 18) # 2 * 9 = 18 with pytest.raises(AssertionError, match="finish earlier"): chunked_read.finish(0.2+2.1+0.5+eps, end_reason=ReadEndReason.UNBLOCKED) @@ -126,7 +155,7 @@ def test_read_finish(): # get chunks, but no action chunked_read = ChunkedRead(nanosim_read_id, "111122223333444455", 0.2) - chunked_read.get_new_chunks(0.9) + chunked_read.get_new_samples(0.9) seq_record = chunked_read.finish() check_equal_seq_records(seq_record, SeqIO.SeqRecord(Seq("111122223333444455"), id=nanosim_read_id, description=f"full_seqlen=18 t_start=0.2 t_end={0.2+1.8} t_delay={0} ended=read_ended_normally tags= full_read_id={nanosim_read_id}")) assert chunked_read.end_reason == ReadEndReason.READ_ENDED_NORMALLY @@ -136,12 +165,12 @@ def test_read_finish(): # negative start time, read finished chunked_read = ChunkedRead(nanosim_read_id, "111122223333444455", -0.5) - chunked_read.get_new_chunks(0.9) + chunked_read.get_new_samples(0.9) chunked_read.finish(-0.5+1.9, end_reason=ReadEndReason.READ_ENDED_NORMALLY) # get chunks, then stop receiving chunked_read = ChunkedRead(nanosim_read_id, "111122223333444455", 0.2) - chunked_read.get_new_chunks(0.9) + chunked_read.get_new_samples(0.9) chunked_read.stop_receiving() seq_record = chunked_read.finish() check_equal_seq_records(seq_record, chunked_read.get_seq_record()) @@ -164,9 +193,9 @@ def test_read_finish(): # stopped receiving, rejected afterwards chunked_read = ChunkedRead(nanosim_read_id, "111122223333444455", 0.2) - chunked_read.get_new_chunks(0.9) + chunked_read.get_new_samples(0.9) chunked_read.stop_receiving() - chunked_read.get_new_chunks(1.1) + chunked_read.get_new_samples(1.1) seq_record = chunked_read.finish(1.2+eps, end_reason=ReadEndReason.UNBLOCKED) check_equal_seq_records(seq_record, chunked_read.get_seq_record()) check_equal_seq_records(seq_record, SeqIO.SeqRecord(Seq("1111222233"), id="chr11_77_aligned_proc0:0m_F_0_20_0", description=f"full_seqlen=18 t_start=0.2 t_end={1.2+eps} t_delay={0} ended=user_unblocked tags=stopped_receiving full_read_id={nanosim_read_id}")) # 10*2 = 20 @@ -174,14 +203,14 @@ def test_read_finish(): # sim stopped chunked_read = ChunkedRead(nanosim_read_id, "111122223333444455", 0.2) - chunked_read.get_new_chunks(0.9) + chunked_read.get_new_samples(0.9) seq_record = chunked_read.finish(1.2+eps, end_reason=ReadEndReason.SIM_STOPPED) check_equal_seq_records(seq_record, SeqIO.SeqRecord(Seq("1111222233"), id="chr11_77_aligned_proc0:0m_F_0_20_0", description=f"full_seqlen=18 t_start=0.2 t_end={1.2+eps} t_delay={0} ended=sim_stopped_unblocked tags= full_read_id={nanosim_read_id}")) # 10*2 = 20 assert chunked_read.end_reason == ReadEndReason.SIM_STOPPED # mux scan started chunked_read = ChunkedRead(nanosim_read_id, "111122223333444455", 0.2) - chunked_read.get_new_chunks(0.9) + chunked_read.get_new_samples(0.9) seq_record = chunked_read.finish(1.2+eps, end_reason=ReadEndReason.MUX_SCAN_STARTED) check_equal_seq_records(seq_record, SeqIO.SeqRecord(Seq("1111222233"), id="chr11_77_aligned_proc0:0m_F_0_20_0", description=f"full_seqlen=18 t_start=0.2 t_end={1.2+eps} t_delay={0} ended=mux_scan_unblocked tags= full_read_id={nanosim_read_id}")) # 10*2 = 20 assert chunked_read.end_reason == ReadEndReason.MUX_SCAN_STARTED @@ -189,25 +218,25 @@ def test_read_finish(): # terminate early without end reason with pytest.raises(AssertionError, match="end reason"): chunked_read = ChunkedRead(nanosim_read_id, "111122223333444455", 0.2) - chunked_read.get_new_chunks(0.9) + chunked_read.get_new_samples(0.9) chunked_read.finish(1.1) # need to indicate end reason # cannot finish earlier than last chunk received with pytest.raises(AssertionError, match="finish earlier"): chunked_read = ChunkedRead(nanosim_read_id, "111122223333444455", 0.2) - chunked_read.get_new_chunks(0.9) + chunked_read.get_new_samples(0.9) chunked_read.finish(0.5, end_reason=ReadEndReason.UNBLOCKED) - # can finish at 0.6 since last chunk returned then + # can finish at 0.4 since no chunks returned yet chunked_read = ChunkedRead(nanosim_read_id, "111122223333444455", 0.2) - chunked_read.get_new_chunks(0.9) - seq_record = chunked_read.finish(0.6+eps, end_reason=ReadEndReason.UNBLOCKED) - check_equal_seq_records(seq_record, SeqIO.SeqRecord(Seq("1111"), id="chr11_77_aligned_proc0:0m_F_0_8_0", description=f"full_seqlen=18 t_start=0.2 t_end={0.6+eps} t_delay={0} ended=user_unblocked tags= full_read_id={nanosim_read_id}")) # 4*2 = 8 + chunked_read.get_new_samples(0.5) + seq_record = chunked_read.finish(0.4+eps, end_reason=ReadEndReason.UNBLOCKED) + check_equal_seq_records(seq_record, SeqIO.SeqRecord(Seq("11"), id="chr11_77_aligned_proc0:0m_F_0_4_0", description=f"full_seqlen=18 t_start=0.2 t_end={0.4+eps} t_delay={0} ended=user_unblocked tags=never_requested full_read_id={nanosim_read_id}")) # 2*2 = 4 assert chunked_read.end_reason == ReadEndReason.UNBLOCKED # unblock read chunked_read = ChunkedRead(nanosim_read_id, "111122223333444455", 0.2) - chunked_read.get_new_chunks(0.9) + chunked_read.get_new_samples(0.9) seq_record = chunked_read.finish(1.2+eps, end_reason=ReadEndReason.UNBLOCKED) check_equal_seq_records(seq_record, SeqIO.SeqRecord(Seq("1111222233"), id="chr11_77_aligned_proc0:0m_F_0_20_0", description=f"full_seqlen=18 t_start=0.2 t_end={1.2+eps} t_delay={0} ended=user_unblocked tags= full_read_id={nanosim_read_id}")) # 10*2 = 20 assert chunked_read.end_reason == ReadEndReason.UNBLOCKED @@ -216,7 +245,7 @@ def test_read_finish(): # starting at position 77 and ref length 36, 36/18=2 nanosim_read_id = "chr11_77_aligned_proc0:0_R_0_36_0" chunked_read = ChunkedRead(nanosim_read_id, "111122223333444455", 0.2) - chunked_read.get_new_chunks(0.9) + chunked_read.get_new_samples(0.9) seq_record = chunked_read.finish(1.2+eps, end_reason=ReadEndReason.UNBLOCKED) check_equal_seq_records(seq_record, SeqIO.SeqRecord(Seq("1111222233"), id="chr11_93_aligned_proc0:0m_R_0_20_0", description=f"full_seqlen=18 t_start=0.2 t_end={1.2+eps} t_delay={0} ended=user_unblocked tags= full_read_id={nanosim_read_id}")) # 77 + 36 - 10*2 = 93 assert chunked_read.end_reason == ReadEndReason.UNBLOCKED diff --git a/tests/simulator/test_readpool.py b/tests/simulator/test_readpool.py index a9f8c88..501aa92 100644 --- a/tests/simulator/test_readpool.py +++ b/tests/simulator/test_readpool.py @@ -1,24 +1,32 @@ +from pathlib import Path +from unittest.mock import MagicMock import pytest import numpy as np from Bio import SeqIO from Bio.Seq import Seq from simreaduntil.shared_utils.dna import get_random_DNA_seq -from simreaduntil.simulator.readpool import ReadPoolFromIterable, ReadPoolFromIterablePerChannel, ReadPoolFromFile, reads_from_file_gen, NoReadLeft +from simreaduntil.simulator.readpool import ReadPoolFromIterable, ReadPoolFromIterablePerChannel, ReadPoolFromFile, ThreadedReadPoolWrapper, reads_from_file_gen, NoReadLeftException gen_from_list = pytest.helpers.gen_from_list @pytest.fixture -def dummy_reads_fasta(tmp_path): +def dummy_reads_fastas(tmp_path): """ Creates a dummy fasta file with reads """ - filename = tmp_path / "test111.fasta" - with open(filename, "w") as f: + filename1 = tmp_path / "test1.fasta" + with open(filename1, "w") as f: SeqIO.write(SeqIO.SeqRecord(Seq("AAACCTGG"), id="read1"), f, "fasta") SeqIO.write(SeqIO.SeqRecord(Seq("CCCCCGGTT"), id="read2"), f, "fasta") - return filename + + filename2 = tmp_path / "test2.fasta" + with open(filename2, "w") as f: + SeqIO.write(SeqIO.SeqRecord(Seq("AAACCTGG"), id="read3"), f, "fasta") + SeqIO.write(SeqIO.SeqRecord(Seq("CCCCCGGTT"), id="read4"), f, "fasta") + + return filename1, filename2 def test_ReadPoolFromIterable(): @@ -29,9 +37,11 @@ def test_ReadPoolFromIterable(): assert read_pool.nb_reads_returned == 1 assert read_pool.get_new_read() == ("read2", "AAACCTGGTTAGG") assert read_pool.nb_reads_returned == 2 - with pytest.raises(NoReadLeft): + with pytest.raises(NoReadLeftException): read_pool.get_new_read() assert read_pool.nb_reads_returned == 2 + + read_pool.finish() def test_ReadPoolFromIterablePerChannel(): random_state = np.random.default_rng(2) @@ -45,34 +55,89 @@ def test_ReadPoolFromIterablePerChannel(): assert len(read_pool.get_new_read(2)) == 8 assert len(read_pool.get_new_read(1)) == 3 assert len(read_pool.get_new_read(1)) == 5 - with pytest.raises(NoReadLeft): + with pytest.raises(NoReadLeftException): read_pool.get_new_read(1) # do twice - with pytest.raises(NoReadLeft): + with pytest.raises(NoReadLeftException): read_pool.get_new_read(1) assert len(read_pool.get_new_read(2)) == 9 - with pytest.raises(NoReadLeft): + with pytest.raises(NoReadLeftException): read_pool.get_new_read(2) -def test_reads_from_file_gen(dummy_reads_fasta): + read_pool.finish() + +def test_reads_from_file_gen(dummy_reads_fastas): # no shuffle - assert list(reads_from_file_gen(dummy_reads_fasta)) == [('read1', 'AAACCTGG'), ('read2', 'CCCCCGGTT')] + assert list(reads_from_file_gen(dummy_reads_fastas[0])) == [('read1', 'AAACCTGG'), ('read2', 'CCCCCGGTT')] random_state = np.random.default_rng(2) - assert list(reads_from_file_gen(dummy_reads_fasta, shuffle_rand_state=random_state)) == [('read1', 'AAACCTGG'), ('read2', 'CCCCCGGTT')] + assert list(reads_from_file_gen(dummy_reads_fastas[0], shuffle_rand_state=random_state)) == [('read1', 'AAACCTGG'), ('read2', 'CCCCCGGTT')] random_state = np.random.default_rng(5) - assert list(reads_from_file_gen(dummy_reads_fasta, shuffle_rand_state=random_state)) == [('read2', 'CCCCCGGTT'), ('read1', 'AAACCTGG')] + assert list(reads_from_file_gen(dummy_reads_fastas[0], shuffle_rand_state=random_state)) == [('read2', 'CCCCCGGTT'), ('read1', 'AAACCTGG')] -def test_ReadPoolFromFile(dummy_reads_fasta): - read_pool = ReadPoolFromFile(dummy_reads_fasta) +def test_ReadPoolFromFile(dummy_reads_fastas): + read_pool = ReadPoolFromFile(dummy_reads_fastas[0]) assert not read_pool.definitely_empty print(read_pool) assert read_pool.get_new_read() == ("read1", "AAACCTGG") assert read_pool.get_new_read() == ("read2", "CCCCCGGTT") - with pytest.raises(NoReadLeft): + with pytest.raises(NoReadLeftException): read_pool.get_new_read() assert read_pool.definitely_empty +def test_ReadPoolFromFileThreaded(dummy_reads_fastas): + reads_dir = Path(dummy_reads_fastas[0]).parent + + for (file_obj, num_reads) in [(dummy_reads_fastas[0], 2), (reads_dir, 4)]: + + assert ReadPoolFromFile.can_handle(file_obj) + + read_pool = ThreadedReadPoolWrapper(ReadPoolFromFile(file_obj), queue_size=3) + repr(read_pool) + + [read_pool.get_new_read() for _ in range(num_reads)] + with pytest.raises(NoReadLeftException): + read_pool.get_new_read() + with pytest.raises(NoReadLeftException): + read_pool.get_new_read() + + assert read_pool.definitely_empty + + read_pool.finish() + +def test_ReadPoolFromFileThreaded_NonEmpty(dummy_reads_fastas): + reads_dir = Path(dummy_reads_fastas[0]).parent + read_pool = ThreadedReadPoolWrapper(ReadPoolFromFile(reads_dir), queue_size=2) + read_pool.get_new_read() + + read_pool.finish() # joins the thread + # there should still be one read left, check that the read pool thread terminates properly + +def pyslow5_is_available(): + try: + import pyslow5 + return True + except ImportError: + return False + +# @pytest.mark.skipif(not pyslow5_is_available(), reason="pyslow5 is not installed") +# def test_Slow5ReadPool(mocker, tmp_path): +# import tempfile +# mock = MagicMock() # cannot use Mock() because it doesn't have __iter__ +# mocker.patch("simreaduntil.simulator.readpool.get_slow5_reads_gen", return_value=mock) +# mock.__iter__.return_value = gen_from_list((("read1", [1, 2, 3]), ("read2", [4, 5]))) +# slow5_dir = tmp_path / "slow5_dummy" +# slow5_dir.mkdir() +# (slow5_dir / "test.slow5").touch() # so it iterates over one file + +# assert Slow5ReadPool.can_handle(slow5_dir) + +# read_pool = Slow5ReadPool(slow5_dir, 1) +# assert read_pool.get_new_read() == ("read1", [1, 2, 3]) +# assert read_pool.get_new_read() == ("read2", [4, 5]) +# with pytest.raises(NoReadLeftException): +# read_pool.get_new_read() +# assert read_pool.definitely_empty \ No newline at end of file diff --git a/tests/simulator/test_readswriter.py b/tests/simulator/test_readswriter.py index 60c8ee7..10a8808 100644 --- a/tests/simulator/test_readswriter.py +++ b/tests/simulator/test_readswriter.py @@ -3,35 +3,54 @@ import shutil import dill from simreaduntil.shared_utils.utils import get_file_content -from simreaduntil.simulator.readswriter import ArrayReadsWriter, RotatingFileReadsWriter, SingleFileReadsWriter +from simreaduntil.simulator.readswriter import ArrayReadsWriter, CompoundReadsWriter, RotatingFileReadsWriter, SingleFileReadsWriter, ThreadedReadsWriterWrapper from Bio import SeqIO from Bio.Seq import Seq def test_SingleFileReadsWriter(tmp_path): - filename = tmp_path / "reads1.txt" - with open(filename, "w") as fh: - reads_writer = SingleFileReadsWriter(fh, prefix="Pref:") - reads_writer.write_read(SeqIO.SeqRecord(Seq("AACCGTT"), id="read1")) - reads_writer.write_read(SeqIO.SeqRecord(Seq("GGGGCCAA"), id="read2")) - print(reads_writer) - expected_content = ">Pref:read1 \nAACCGTT\n>Pref:read2 \nGGGGCCAA\n" - assert get_file_content(filename) == expected_content - obj = dill.loads(dill.dumps(reads_writer)) - assert obj.fh is None - # check file not overwritten - assert get_file_content(filename) == expected_content - + for (writer_wrapper, test_picklable) in [(lambda x: x, True), (ThreadedReadsWriterWrapper, False)]: + filename = tmp_path / "reads1.txt" + with open(filename, "w") as fh: + reads_writer = SingleFileReadsWriter(fh, prefix="Pref:") + reads_writer = writer_wrapper(reads_writer) + reads_writer.write_read(SeqIO.SeqRecord(Seq("AACCGTT"), id="read1")) + reads_writer.write_read(SeqIO.SeqRecord(Seq("GGGGCCAA"), id="read2")) + print(reads_writer) + + reads_writer.finish() + + expected_content = ">Pref:read1 \nAACCGTT\n>Pref:read2 \nGGGGCCAA\n" + assert get_file_content(filename) == expected_content + if test_picklable: + obj = dill.loads(dill.dumps(reads_writer)) + assert obj.fh is None + # check file not overwritten + assert get_file_content(filename) == expected_content + + def test_ArrayReadsWriter(): reads_writer = ArrayReadsWriter() reads_writer.write_read(SeqIO.SeqRecord(Seq("AACCGTT"), id="read1")) reads_writer.write_read(SeqIO.SeqRecord(Seq("GGGGCCAA"), id="read2")) - reads_writer.reads, [('read1', Seq('AACCGTT')), ('read2', Seq('GGGGCCAA'))] + assert reads_writer.reads == [("read1", Seq("AACCGTT"), ""), ("read2", Seq("GGGGCCAA"), "")] str(reads_writer) str(reads_writer.extended_repr()) dill.loads(dill.dumps(reads_writer)) +def test_CompoundReadsWriter(): + reads_writer1 = ArrayReadsWriter() + reads_writer2 = ArrayReadsWriter() + with CompoundReadsWriter([reads_writer1, reads_writer2]) as reads_writer: + reads_writer.write_read(SeqIO.SeqRecord(Seq("AACCGTT"), id="read1")) + reads_writer.write_read(SeqIO.SeqRecord(Seq("GGGGCCAA"), id="read2")) + + repr(reads_writer) + + assert reads_writer1.reads == [("read1", Seq("AACCGTT"), ""), ("read2", Seq("GGGGCCAA"), "")] + assert reads_writer2.reads == [("read1", Seq("AACCGTT"), ""), ("read2", Seq("GGGGCCAA"), "")] + def test_RotatingFileReadsWriter(tmp_path): def nb_files_in_dir(path): return sum(1 for _ in Path(path).iterdir()) diff --git a/tests/simulator/test_sim_params.py b/tests/simulator/test_sim_params.py index bd11a30..eaa2846 100644 --- a/tests/simulator/test_sim_params.py +++ b/tests/simulator/test_sim_params.py @@ -7,14 +7,14 @@ def test_sim_params(): sim_params = SimParams( gap_samplers={f"channel_{i}": ConstantGapsUntilBlocked(short_gap_length=1.2, long_gap_length=1.2, prob_long_gap=0.15, time_until_blocked=np.inf, read_delay=0) for i in range(2)}, - bp_per_second=10, chunk_size=4, default_unblock_duration=1.2, seed=0, + bp_per_second=10, min_chunk_size=4, default_unblock_duration=1.2, seed=0, ) # set to some random values - sim_params.set(bp_per_second=1000, default_unblock_duration=0.2, chunk_size=100, seed=2) + sim_params.set(bp_per_second=1000, default_unblock_duration=0.2, min_chunk_size=100, seed=2) assert sim_params.bp_per_second == 1000 assert sim_params.default_unblock_duration == 0.2 - assert sim_params.chunk_size == 100 + assert sim_params.min_chunk_size == 100 assert sim_params._initial_seed == 2 assert sim_params.gap_samplers["channel_0"].short_gap_length == 1.2 diff --git a/tests/simulator/test_simfasta_to_seqsum.py b/tests/simulator/test_simfasta_to_seqsum.py index 67e951f..4e725a8 100644 --- a/tests/simulator/test_simfasta_to_seqsum.py +++ b/tests/simulator/test_simfasta_to_seqsum.py @@ -1,7 +1,65 @@ +from pathlib import Path +from textwrap import dedent import pandas as pd from simreaduntil.seqsum_tools.seqsum_preprocessing import sort_and_clean_seqsum_df -from simreaduntil.simulator.simfasta_to_seqsum import convert_simfasta_dir_to_seqsum, convert_simfasta_to_seqsum +from simreaduntil.simulator.channel_element import ChunkedRead +from simreaduntil.simulator.simfasta_to_seqsum import SequencingSummaryWriter, convert_simfasta_dir_to_seqsum, convert_simfasta_to_seqsum, write_seqsum_header, write_seqsum_record_line +from Bio import SeqIO +from Bio.Seq import Seq +def get_dummy_record(): + # >chr20_36784526_aligned_proc0:16m_R_0_2226_0 full_seqlen=13552 t_start=0.1752480000000105 t_end=6.115326881408691 t_delay=0.09375 ended=user_unblocked tags= full_read_id=chr20_36772816_aligned_proc0:16_R_0_13936_0 ch=ch138 + # GTGCAATTTATACTCATGGCCAGTGTACAGTGACTCATGCCTGTACCCCACTTTAGGAGA + description = "chr20_36784526_aligned_proc0:16m_R_0_2226_0 full_seqlen=13552 t_start=0.1752480000000105 t_end=6.115326881408691 t_delay=0.09375 ended=user_unblocked tags= full_read_id=chr20_36772816_aligned_proc0:16_R_0_13936_0 ch=ch138" + read_id = description.split(" ")[0] + seq = "GTGCAATTTATACTCATGGCCAGTGTACAGTGACTCATGCCTGTACCCCACTTTAGGAGA" + return SeqIO.SeqRecord(id=read_id, description=description, seq=Seq(seq)) + +# to match with get_dummy_record +expected_seqsum_file_content=dedent(f"""\ +read_id\tchannel\tmux\tstart_time\tduration\tpasses_filtering\ttemplate_start\ttemplate_duration\tsequence_length_template\tend_reason\tnb_ref_bps_full\tstopped_receiving\tnever_requested +chr20_36784526_aligned_proc0:16m_R_0_2226_0\tch138\t1\t0.1752480000000105\t5.940078881408681\tTrue\t0.2689980000000105\t5.846328881408681\t{len(get_dummy_record().seq)}\tdata_service_unblock_mux_change\t13936\tFalse\tFalse +""") + +def test_write_seqsum_record_line(tmp_path): + sequencing_summary_filename = tmp_path / "seqsummary_filename_simple1.txt" + with open(sequencing_summary_filename, mode="w") as seqsummary_fh: + write_seqsum_header(seqsummary_fh) + + write_seqsum_record_line(get_dummy_record(), seqsummary_fh) + + assert sequencing_summary_filename.read_text() == expected_seqsum_file_content + +def test_SequencingSummaryWriter(tmp_path): + sequencing_summary_filename = tmp_path / "seqsummary_filename_simple2.txt" + with open(sequencing_summary_filename, mode="w") as seqsummary_fh: + with SequencingSummaryWriter(seqsummary_fh) as seqsum_writer: + seqsum_writer.write_read(get_dummy_record()) + + assert sequencing_summary_filename.read_text() == expected_seqsum_file_content + +def test_seqsum_line_with_chunked_read(tmp_path): + chunked_read = ChunkedRead("read1", "111112222222222333333", 10.1, read_speed=10, min_chunk_size=4) + chunked_read.get_new_samples(10.1+0.5) + chunked_read.stop_receiving() + seq_record = chunked_read.finish() + seq_record.description += f" ch=ch1" # added by channel on top + + sequencing_summary_filename = tmp_path / "seqsummary_filename_simple3.txt" + with open(sequencing_summary_filename, mode="w") as seqsummary_fh: + write_seqsum_record_line(seq_record, seqsummary_fh, read_id=seq_record.id) + + # test with SequencingSummaryWriter + sequencing_summary_filename = tmp_path / "seqsummary_filename_simple4.txt" + with open(sequencing_summary_filename, mode="w") as seqsummary_fh: + with SequencingSummaryWriter(seqsummary_fh) as seqsum_writer: + seqsum_writer.write_read(seq_record) + + expected_filecontent = dedent("""\ + read_id\tchannel\tmux\tstart_time\tduration\tpasses_filtering\ttemplate_start\ttemplate_duration\tsequence_length_template\tend_reason\tnb_ref_bps_full\tstopped_receiving\tnever_requested + read1\tch1\t1\t10.1\t2.0999999999999996\tTrue\t10.1\t2.0999999999999996\t21\tsignal_positive\tnan\tTrue\tFalse + """) + assert Path(sequencing_summary_filename).read_text() == expected_filecontent def test_convert_simfasta_to_seqsum(shared_datadir, tmp_path): number_of_lines_in_file = lambda filename: sum(1 for _ in open(filename)) diff --git a/tests/simulator/test_simulator.py b/tests/simulator/test_simulator.py index 213ea8b..492e76e 100644 --- a/tests/simulator/test_simulator.py +++ b/tests/simulator/test_simulator.py @@ -1,5 +1,3 @@ -import itertools -import logging from typing import Dict import pytest from pytest import approx @@ -12,12 +10,11 @@ from simreaduntil.shared_utils.timing import cur_ns_time from simreaduntil.simulator import channel from simreaduntil.simulator.channel import ChannelAlreadyRunningException, StoppedReceivingResponse, UnblockResponse -from simreaduntil.simulator.channel_element import ChunkedRead from simreaduntil.simulator.gap_sampling.constant_gaps_until_blocked import ConstantGapsUntilBlocked from simreaduntil.simulator.pore_model import PoreModel from simreaduntil.simulator.readpool import ReadPoolFromIterable from simreaduntil.simulator.readswriter import ArrayReadsWriter -from simreaduntil.simulator.simulator import ActionType, InexistentChannelsException, ONTSimulator, ReadUntilClientFromDevice, ReadUntilDevice, assign_read_durations_to_channels, convert_action_results_to_df, plot_sim_actions, run_periodic_mux_scan_thread, stop_simulation_after_time_thread +from simreaduntil.simulator.simulator import ActionType, InexistentChannelsException, ONTSimulator, assign_read_durations_to_channels, convert_action_results_to_df, plot_sim_actions, run_periodic_mux_scan_thread, stop_simulation_after_time_thread from simreaduntil.simulator.simulator_params import SimParams from simreaduntil.simulator.utils import in_interval from simreaduntil.usecase_helpers.utils import random_reads_gen @@ -32,7 +29,7 @@ def sim_params() -> SimParams: # make it fast enough so something actually happens (without making tests last too long) return SimParams( gap_samplers={f"channel_{i}": ConstantGapsUntilBlocked(short_gap_length=0.04, long_gap_length=0.05, prob_long_gap=0.02, time_until_blocked=np.inf, read_delay=0) for i in range(2)}, - bp_per_second=100, chunk_size=20, default_unblock_duration=0.02, seed=0, + bp_per_second=100, min_chunk_size=20, default_unblock_duration=0.02, seed=0, ) @pytest.fixture @@ -40,7 +37,8 @@ def simulator(sim_params) -> ONTSimulator: return ONTSimulator( read_pool=ReadPoolFromIterable(random_reads_gen(random_state=np.random.default_rng(3), length_range=(10, 50))), reads_writer=ArrayReadsWriter(), - sim_params=sim_params + sim_params=sim_params, + output_dir="", ) def test_start_stop(simulator): @@ -105,14 +103,15 @@ def test_get_basecalled_chunks(): sim_params = SimParams( gap_samplers={f"channel_{i}": ConstantGapsUntilBlocked(short_gap_length=0.4, long_gap_length=0.5, prob_long_gap=0, time_until_blocked=np.inf, read_delay=0) for i in range(2)}, - bp_per_second=10, chunk_size=4, default_unblock_duration=0.2, seed=0, + bp_per_second=10, min_chunk_size=4, default_unblock_duration=0.2, seed=0, ) - read_pool = ReadPoolFromIterable(gen_from_list((("read1", "AAAAGGGGC"), ("read2", "TTTTAC"), ("read3", "TTTTAAAACCCAAACTTTACCA"), ("read4", "TCTTAAAACCTTA")))) + read_pool = ReadPoolFromIterable(gen_from_list((("read1", "AAAAAGGGGC"), ("read2", "TTTTAC"), ("read3", "TTTTAAAACCCAAACTTTACCA"), ("read4", "TCTTAAAACCTTA")))) simulator = ONTSimulator( read_pool=read_pool, reads_writer=ArrayReadsWriter(), - sim_params = sim_params + sim_params = sim_params, + output_dir="", ) simulator.save_elems = True eps = 1e-5 @@ -120,30 +119,33 @@ def test_get_basecalled_chunks(): simulator.sync_start(0) simulator.sync_forward(0.9) chunks = list(simulator.get_basecalled_read_chunks()) - assert sorted(chunks) == sorted([(1, 'read1', 'AAAA', 'noquality', 4), (2, 'read2', 'TTTT', 'noquality', 4)]) + assert sorted(chunks) == sorted([(1, 'read1', 'AAAAA', 'noquality', 5), (2, 'read2', 'TTTTA', 'noquality', 5)]) chunks = list(simulator.get_basecalled_read_chunks()) assert len(chunks) == 0 simulator.sync_forward(1.2+eps) chunks = list(simulator.get_basecalled_read_chunks()) - assert chunks == [(1, 'read1', 'GGGG', 'noquality', 8)] + assert chunks == [] # below min chunk size + simulator.sync_forward(1.3+eps) + chunks = list(simulator.get_basecalled_read_chunks()) + assert chunks == [(1, 'read1', 'GGGG', 'noquality', 9)] - simulator.sync_forward(1.4+eps) # to force channel 1 to get read3 - simulator.sync_forward(2.2+eps) + simulator.sync_forward(1.5+eps) # to force channel 2 to get read3, channel 1 not yet because gap has not finished + simulator.sync_forward(2.3+eps) with pytest.raises(InexistentChannelsException): # channels are 1-based list(simulator.get_basecalled_read_chunks(channel_subset=[0])) chunks = list(simulator.get_basecalled_read_chunks(channel_subset=[1])) - assert chunks == [(1, 'read4', 'TCTT', 'noquality', 4)] + assert chunks == [(1, 'read4', 'TCTTA', 'noquality', 5)] chunks = list(simulator.get_basecalled_read_chunks(channel_subset=[2])) - assert chunks == [(2, 'read3', 'TTTTAAAA', 'noquality', 8)] + assert chunks == [(2, 'read3', 'TTTTAAAAC', 'noquality', 9)] simulator.sync_forward(2.7+eps) chunks = list(simulator.get_basecalled_read_chunks(batch_size=1)) assert len(chunks) == 1 - # simulator.plot_channels(); import matplotlib.pyplot as plt; plt.show() + # ax = simulator.plot_channels(); import matplotlib.pyplot as plt; plt.show() simulator.sync_stop() @@ -159,14 +161,15 @@ def test_get_raw_chunks(shared_datadir): sim_params = SimParams( gap_samplers={f"channel_{i}": ConstantGapsUntilBlocked(short_gap_length=0.4, long_gap_length=0.5, prob_long_gap=0, time_until_blocked=np.inf, read_delay=0) for i in range(2)}, - bp_per_second=10, chunk_size=4, default_unblock_duration=0.2, seed=0, pore_model=PoreModel(pore_filename, signals_per_bp=signals_per_bp) + bp_per_second=10, min_chunk_size=4, default_unblock_duration=0.2, seed=0, pore_model=PoreModel(pore_filename, signals_per_bp=signals_per_bp) ) read_pool = ReadPoolFromIterable(gen_from_list((("read1", seq), ("read2", "TTTTAC")))) simulator = ONTSimulator( read_pool=read_pool, reads_writer=ArrayReadsWriter(), - sim_params = sim_params + sim_params = sim_params, + output_dir="", ) simulator.sync_start(0) @@ -174,7 +177,7 @@ def test_get_raw_chunks(shared_datadir): simulator.sync_forward(0.4+0.9+eps) chunks = list(simulator.get_raw_chunks(channel_subset=[1])) # get 2 chunks for channel 1 raw_signal = chunks[0][2] - assert len(raw_signal) == (2*4 - k + 1) * signals_per_bp + assert len(raw_signal) == (9 - k + 1) * signals_per_bp def test_synchronous_sim_is_deterministic(sim_params, channel_write_zero_length_reads): # test that the synchronous simulator produces the same results when run twice, only for "constant" update method @@ -188,7 +191,8 @@ def run_sim(seed): simulator = ONTSimulator( read_pool=ReadPoolFromIterable(random_reads_gen(random_state=np.random.default_rng(3), length_range=(10, 50))), reads_writer=reads_writer, - sim_params=sim_params + sim_params=sim_params, + output_dir="", ) simulator.save_elems = True @@ -217,7 +221,7 @@ def test_random_ops_synchronous(simulator, async_mode, channel_write_zero_length sim_params = SimParams( gap_samplers={f"channel_{i}": ConstantGapsUntilBlocked(short_gap_length=1.2, long_gap_length=1.2, prob_long_gap=0.35, time_until_blocked=200, read_delay=0) for i in range(2)}, # gap_samplers={f"channel_{i}": ConstantGapsUntilBlocked(short_gap_length=1.2, long_gap_length=5.2, prob_long_gap=0.25, time_until_blocked=200, read_delay=0) for i in range(2)}, - bp_per_second=10, chunk_size=4, default_unblock_duration=1.2, seed=0, + bp_per_second=10, min_chunk_size=4, default_unblock_duration=1.2, seed=0, ) # apply random operations, check that reads are correct @@ -230,7 +234,8 @@ def get_read_and_save_id(): simulator = ONTSimulator( read_pool=ReadPoolFromIterable(get_read_and_save_id()), reads_writer=ArrayReadsWriter(), - sim_params=sim_params + sim_params=sim_params, + output_dir="", ) simulator.save_elems = True @@ -261,11 +266,14 @@ def get_read_and_save_id(): assert stats.time_active + stats.no_reads_left.time_spent + stats.channel_broken.time_spent == approx(simulator._channels[0].t - (0 if async_mode else t_start)) def test_realtime(channel_write_zero_length_reads): + # todo: this does not test real time, instead need to check that the simulator forward loop is never delayed, + # need to look at test results and look for "Simulation cannot keep up, delay" messages + # test that the simulator can run in real time (i.e. end time is at least 0.95 * real time) # can be used to determine the optimal acceleration factor that does not cause too much delay sim_params = SimParams( gap_samplers={f"channel_{i}": ConstantGapsUntilBlocked(short_gap_length=0.4, long_gap_length=1.3, prob_long_gap=0.1, time_until_blocked=np.inf, read_delay=0) for i in range(512)}, - bp_per_second=450, chunk_size=200, default_unblock_duration=1.4, seed=0, + bp_per_second=450, min_chunk_size=200, default_unblock_duration=1.4, seed=0, ) # pre-generate reads to make sure that this is not responsible for the delay @@ -276,7 +284,8 @@ def test_realtime(channel_write_zero_length_reads): # read_pool=ReadPoolFromIterable(reads_gen), read_pool=ReadPoolFromIterable(random_reads_gen(random_state=np.random.default_rng(3), length_range=(500, 5000))), reads_writer=ArrayReadsWriter(), - sim_params=sim_params + sim_params=sim_params, + output_dir="", ) acceleration_factor = 5 # depends on computer load @@ -317,59 +326,61 @@ def test_run_periodic_mux_scan_thread(simulator): assert simulator.get_channel_stats()[0].mux_scans.finished_number == 5 assert simulator.get_channel_stats()[1].mux_scans.finished_number == 5 -def test_readuntil_fromdevice(): - # tests the readuntil client +# def test_readuntil_fromdevice(): +# # tests the readuntil client - sim_params = SimParams( - gap_samplers={f"channel_{i}": ConstantGapsUntilBlocked(short_gap_length=0.4, long_gap_length=0.5, prob_long_gap=0, time_until_blocked=np.inf, read_delay=0) for i in range(2)}, - bp_per_second=10, chunk_size=4, default_unblock_duration=0.2, seed=0, - ) +# sim_params = SimParams( +# gap_samplers={f"channel_{i}": ConstantGapsUntilBlocked(short_gap_length=0.4, long_gap_length=0.5, prob_long_gap=0, time_until_blocked=np.inf, read_delay=0) for i in range(2)}, +# bp_per_second=10, min_chunk_size=4, default_unblock_duration=0.2, seed=0, +# ) - read_pool = ReadPoolFromIterable(gen_from_list((("read1", "AAAAGGGGC"), ("read2", "TTTTACCTTACC"), ("read3", "TTTTAAAACCCAAACTTTACCA"), ("read4", "TCTTAAAACCTTA")))) - simulator = ONTSimulator( - read_pool=read_pool, - reads_writer=ArrayReadsWriter(), - sim_params=sim_params - ) - simulator.save_elems = True - eps = 1e-5 +# read_pool = ReadPoolFromIterable(gen_from_list((("read1", "AAAAGGGGC"), ("read2", "TTTTACCTTACC"), ("read3", "TTTTAAAACCCAAACTTTACCA"), ("read4", "TCTTAAAACCTTA")))) +# simulator = ONTSimulator( +# read_pool=read_pool, +# reads_writer=ArrayReadsWriter(), +# sim_params=sim_params, +# output_dir="", +# ) +# simulator.save_elems = True +# eps = 1e-5 - ru_client = ReadUntilClientFromDevice(simulator) +# ru_client = ReadUntilClientFromDevice(simulator) - simulator.sync_start(-0.4) - simulator.sync_forward(0.5) +# simulator.sync_start(-0.4) +# simulator.sync_forward(0.5) - chunks = list(ru_client.get_basecalled_read_chunks()) - assert len(chunks) == 2 +# chunks = list(ru_client.get_basecalled_read_chunks()) +# assert len(chunks) == 2 - with pytest.raises(InexistentChannelsException): - ru_client.stop_receiving_batch([(0, "read1")]) - responses = ru_client.stop_receiving_batch([(1, "read1"), (2, "inexistent")]) - assert responses == [StoppedReceivingResponse.STOPPED_RECEIVING, StoppedReceivingResponse.MISSED] - assert ru_client.stop_receiving_batch([(1, "read1")]) == [StoppedReceivingResponse.ALREADY_STOPPED_RECEIVING] +# with pytest.raises(InexistentChannelsException): +# ru_client.stop_receiving_batch([(0, "read1")]) +# responses = ru_client.stop_receiving_batch([(1, "read1"), (2, "inexistent")]) +# assert responses == [StoppedReceivingResponse.STOPPED_RECEIVING, StoppedReceivingResponse.MISSED] +# assert ru_client.stop_receiving_batch([(1, "read1")]) == [StoppedReceivingResponse.ALREADY_STOPPED_RECEIVING] - simulator.sync_forward(0.8+eps) +# simulator.sync_forward(0.8+eps) - chunks = list(ru_client.get_basecalled_read_chunks()) - assert len(chunks) == 1 +# chunks = list(ru_client.get_basecalled_read_chunks()) +# assert len(chunks) == 1 - assert ru_client.stop_receiving_batch([(2, "read2")]) == [StoppedReceivingResponse.STOPPED_RECEIVING] +# assert ru_client.stop_receiving_batch([(2, "read2")]) == [StoppedReceivingResponse.STOPPED_RECEIVING] - assert ru_client.unblock_read_batch([(1, "read1"), (2, "read2"), (1, "inexistent")]) == [True, True, False] +# assert ru_client.unblock_read_batch([(1, "read1"), (2, "read2"), (1, "inexistent")]) == [True, True, False] - simulator.sync_stop() +# simulator.sync_stop() def test_get_action_results(): sim_params = SimParams( gap_samplers={f"channel_{i}": ConstantGapsUntilBlocked(short_gap_length=0.4, long_gap_length=0.5, prob_long_gap=0, time_until_blocked=np.inf, read_delay=0) for i in range(2)}, - bp_per_second=10, chunk_size=4, default_unblock_duration=0.2, seed=0, + bp_per_second=10, min_chunk_size=4, default_unblock_duration=0.2, seed=0, ) read_pool = ReadPoolFromIterable(gen_from_list((("read1", "AAAAGGGGC"), ("read2", "TTTTAC"), ("read3", "TTTTAAAACCCAAACTTTACCA"), ("read4", "TCTTAAAACCTTA")))) simulator = ONTSimulator( read_pool=read_pool, reads_writer=ArrayReadsWriter(), - sim_params = sim_params + sim_params=sim_params, + output_dir="", ) simulator.save_elems = True @@ -381,6 +392,8 @@ def test_get_action_results(): ("read1", 0.5, 1, ActionType.StopReceiving, StoppedReceivingResponse.STOPPED_RECEIVING), ("inexistent", 0.5, 2, ActionType.StopReceiving, StoppedReceivingResponse.MISSED) ] + assert simulator.get_action_results(clear=False) == [] + simulator.sync_forward(t=0, delta=True) # process actions assert simulator.get_action_results(clear=False) == exp_action_results plot_sim_actions(convert_action_results_to_df(exp_action_results), close_figures=True) @@ -390,6 +403,7 @@ def test_get_action_results(): simulator.sync_forward(1.3+1e-4) simulator.unblock_read(2, "read3") simulator.unblock_read(2, "inexistent") + simulator.sync_forward(t=0, delta=True) # process actions assert simulator.get_action_results() == [ ("read3", 1.3+1e-4, 2, ActionType.Unblock, UnblockResponse.UNBLOCKED), ("inexistent", 1.3+1e-4, 2, ActionType.Unblock, UnblockResponse.MISSED) diff --git a/tests/simulator/test_simulator_client.py b/tests/simulator/test_simulator_client.py index 33ab036..4fdefdf 100644 --- a/tests/simulator/test_simulator_client.py +++ b/tests/simulator/test_simulator_client.py @@ -17,7 +17,6 @@ def test_grpc_client(channel_write_zero_length_reads): reads_writer = ArrayReadsWriter() - reads_writer.output_dir = "dummy_dir" # patch attribute sim_params = SimParams( gap_samplers={f"channel_{i}": ConstantGapsUntilBlocked(short_gap_length=0.4, long_gap_length=10.1, prob_long_gap=0, time_until_blocked=np.inf, read_delay=0) for i in range(2)}, @@ -26,6 +25,7 @@ def test_grpc_client(channel_write_zero_length_reads): read_pool=ReadPoolFromIterable(random_reads_gen(random_state=np.random.default_rng(3), length_range=(10, 50))), reads_writer=reads_writer, sim_params=sim_params, + output_dir="", ) port, server, unique_id = launchable_device_grpc_server(simulator) @@ -40,7 +40,7 @@ def test_grpc_client(channel_write_zero_length_reads): assert client.unique_id == unique_id, f"mismatching unique_ids, probably connected to an existing server: {client.unique_id} != {unique_id}" - assert str(client.mk_run_dir) == "dummy_dir" + assert str(client.mk_run_dir) == "" assert not client.is_running assert client.start(acceleration_factor=acceleration_factor) diff --git a/tests/simulator/test_simulator_server.py b/tests/simulator/test_simulator_server.py index e68a926..b02d602 100644 --- a/tests/simulator/test_simulator_server.py +++ b/tests/simulator/test_simulator_server.py @@ -85,7 +85,8 @@ def test_launchable_device_grpc_server(): simulator = ONTSimulator( read_pool=ReadPoolFromIterable(random_reads_gen(random_state=np.random.default_rng(3), length_range=(10, 50))), reads_writer=ArrayReadsWriter(), - sim_params=sim_params + sim_params=sim_params, + output_dir="", ) # import os; os.environ["GRPC_VERBOSITY"] = "DEBUG"; os.environ["GRPC_TRACE"] = "http" @@ -106,13 +107,13 @@ def test_launchable_device_grpc_server(): assert stub.StartSim(ont_device_pb2.StartRequest(acceleration_factor=2, update_method="realtime", log_interval=10)).value # unblocking inexistent read - assert not stub.PerformActions(ont_device_pb2.ReadActionsRequest(actions=[ + stub.PerformActions(ont_device_pb2.ReadActionsRequest(actions=[ ont_device_pb2.ReadActionsRequest.Action(channel=2, read_id="inexistent", unblock=ont_device_pb2.ReadActionsRequest.Action.UnblockAction(unblock_duration=0.2)) - ])).succeeded[0] + ])) - assert not stub.PerformActions(ont_device_pb2.ReadActionsRequest(actions=[ + stub.PerformActions(ont_device_pb2.ReadActionsRequest(actions=[ ont_device_pb2.ReadActionsRequest.Action(channel=1, read_id="inexistent", stop_further_data=ont_device_pb2.ReadActionsRequest.Action.StopReceivingAction()), - ])).succeeded[0] + ])) assert stub.StopSim(ont_device_pb2.EmptyRequest()).value diff --git a/tests/simulator/test_utils.py b/tests/simulator/test_utils.py index 9309de2..5d836bf 100644 --- a/tests/simulator/test_utils.py +++ b/tests/simulator/test_utils.py @@ -1,7 +1,12 @@ +import signal +import sys +import threading +import time from matplotlib import pyplot as plt import pytest from simreaduntil.shared_utils.plotting import make_tight_layout +from simreaduntil.shared_utils.utils import set_signal_handler, tee_stdouterr_to_file from simreaduntil.simulator.utils import format_percentage, in_interval, new_thread_name @@ -22,4 +27,4 @@ def test_format_percentage(): def test_make_tight_layout(): fig, ax = plt.subplots() ax.plot([0, 1], [0, 1]) - make_tight_layout(fig) \ No newline at end of file + make_tight_layout(fig) diff --git a/tests/usecase_helpers/data/run_dir/configs/config.toml b/tests/usecase_helpers/data/run_dir/configs/config.toml index 8306102..b40b264 100644 --- a/tests/usecase_helpers/data/run_dir/configs/config.toml +++ b/tests/usecase_helpers/data/run_dir/configs/config.toml @@ -1,5 +1,5 @@ run_dir = "simulator_run" # where reads, logs, pafs etc. will be written to -n_channels = 200 +n_channels = 20 # n_channels = 4 acceleration_factor = 10 run_duration = 100.0 @@ -9,9 +9,11 @@ run_duration = 100.0 ################################################# # reads_file = "nanosim_reads/perfect_reads_seed1_aligned_reads.fasta" +# reads_file = "/home/mmordig/rawhash_project/rawhash2/test/data/d2_ecoli_r94/small_slow5_files" +reads_len_range = [5_000, 10_000] ref_genome_path = "data/chm13v2.0_normalized1000000firsttwo.fa.gz" # sim_params_file = "sim_params.dill" -rotating = true +rotating_writeout = true mux_scan_period = 50 # seconds, accounting for acceleration mux_scan_duration = 10 # seconds diff --git a/tests/usecase_helpers/data/run_dir/configs/readfish_enrich_chr1.toml b/tests/usecase_helpers/data/run_dir/configs/readfish_enrich_chr1.toml index d4b471b..9b2cd13 100644 --- a/tests/usecase_helpers/data/run_dir/configs/readfish_enrich_chr1.toml +++ b/tests/usecase_helpers/data/run_dir/configs/readfish_enrich_chr1.toml @@ -15,6 +15,6 @@ targets = ["chr1"] single_on = "stop_receiving" multi_on = "stop_receiving" single_off = "unblock" -multi_off = "unblock" +multi_off = "proceed" no_seq = "proceed" # unclear what it is, does not seem to be used no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected \ No newline at end of file diff --git a/tests/usecase_helpers/test_run_simulator_with_readfish.py b/tests/usecase_helpers/test_run_simulator_with_readfish.py index efaa470..138d1f1 100644 --- a/tests/usecase_helpers/test_run_simulator_with_readfish.py +++ b/tests/usecase_helpers/test_run_simulator_with_readfish.py @@ -34,6 +34,9 @@ def test_simulator_with_readfish(shared_datadir, tmp_path): assert Path("simulator_run/reads").exists() assert Path("simulator_run/sequencing_summary.txt").exists() + assert Path("simulator_run/live_sequencing_summary.txt").exists() + + assert Path("simulator_run/sequencing_summary.txt").read_text() == Path("simulator_run/live_sequencing_summary.txt").read_text() action_results_df = pd.read_csv("simulator_run/action_results.csv", sep="\t") plot_sim_actions(action_results_df, close_figures=True) diff --git a/usecases/README.md b/usecases/README.md index 3fee734..b59d8ec 100644 --- a/usecases/README.md +++ b/usecases/README.md @@ -75,7 +75,7 @@ cd .. # install ReadFish git submodule update --init --depth 1 external/ont_readfish source ~/ont_project_all/ont_project_venv/bin/activate -pip install -e './[readfish]' # -e for dev version +pip uninstall -y readfish; pip install './[readfish]'; pip show readfish # optional: install NanoSim and minimap2, but the usecase also works without # git submodule update --init --depth 1 external/ont_nanosim @@ -100,8 +100,9 @@ If the read ids are NanoSim ids with ground-truth alignment information, `minima Files: - `enrich_usecase.py`: end-to-end script that runs an enrichment with ReadFish connected to the simulator, see the instructions in that file - `enrich_usecase_submission.sh`: condor submission script, can also be run locally +- `compute_absolute_enrichment.ipynb`: compute the absolute enrichment for the enrich usecase by comparing to control - `install_usecase_deps.sh`: to install `minimap2` and `NanoSim` (optional), launch it from the repo root -- `create_nanosim_reads.ipynb`: notebook to create NanoSim reads that can be fed into the simulator by modifying the config file +- `generate_nanosim_reads.sh`: script to create NanoSim reads that can be fed into the simulator ## Parameter Extraction from an Existing Run @@ -129,7 +130,7 @@ When running several configurations in parallel and some cache files do not exis These files are for our own reference and may not work for you out of the box: - `analyze_readfish_outputs.py`: to check whether ReadFish is mapping reads correctly by parsing the ground-truth from the read id -- `plot_existing_seqsum.py`: to plot an existing sequencing summary file, e.g., from a real run +- `plot_existing_seqsum.py`: to plot an existing sequencing summary file, e.g., from a real run; probably needs to be adapted to your setting - `remove_mux_scans.ipynb`: notebook showing how mux scans are removed (you don't need to run this, this is done automatically in the usecases) - `prepare_small_refgenome.py`: to create a small reference genome for the usecase - `results_preparation.md`: commands to create the results in the paper diff --git a/usecases/analyze_readfish_outputs.ipynb b/usecases/analyze_readfish_outputs.ipynb index 44b43c5..7b26b50 100644 --- a/usecases/analyze_readfish_outputs.ipynb +++ b/usecases/analyze_readfish_outputs.ipynb @@ -11,7 +11,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -23,10 +23,22 @@ "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", + "from pathlib import Path\n", "\n", "from simreaduntil.shared_utils.nanosim_parsing import NanoSimId\n" ] }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "# run_dir = Path(\"runs/enrich_usecase/full_run_sampler_per_window/simulator_run/\")\n", + "run_dir = Path(\"/is/cluster-test/fast/mmordig/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_realreads/simulator_run/\")\n", + "# run_dir = Path(\"/home/mmordig/ont_project_all/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_realreads_withflanking/simulator_run\")" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -36,7 +48,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -74,98 +86,99 @@ " \n", " \n", " 0\n", - " chr1_818237_aligned_2_R_0_5720_0\n", - " 600\n", + " chr1_26454210_aligned_proc0:64_F_0_1535_0\n", + " 263\n", " 0\n", - " 600\n", - " -\n", + " 263\n", + " +\n", " chr1\n", - " 1000000\n", - " 823357\n", - " 823957\n", + " 248387328\n", + " 26454210\n", + " 26454469\n", " \n", " \n", " 1\n", - " chr1_763753_aligned_21_R_0_8206_0\n", - " 800\n", - " 0\n", - " 800\n", - " -\n", - " chr1\n", - " 1000000\n", - " 771159\n", - " 771959\n", + " chr8_35080996_aligned_proc0:45_F_0_11151_0\n", + " 323\n", + " 3\n", + " 323\n", + " +\n", + " chr8\n", + " 146259331\n", + " 35081000\n", + " 35081331\n", " \n", " \n", " 2\n", - " chr1_541945_aligned_25_R_0_5739_0\n", - " 600\n", + " chr5_119262799_aligned_proc0:51_F_0_7992_0\n", + " 265\n", " 0\n", - " 600\n", - " -\n", - " chr1\n", - " 1000000\n", - " 547084\n", - " 547684\n", + " 265\n", + " +\n", + " chr5\n", + " 182045439\n", + " 119262799\n", + " 119263072\n", " \n", " \n", " 3\n", - " chr1_737931_aligned_46_F_0_8456_0\n", - " 800\n", + " chr2_26169279_aligned_proc0:26_F_0_10295_0\n", + " 343\n", " 0\n", - " 800\n", + " 334\n", " +\n", - " chr1\n", - " 1000000\n", - " 737931\n", - " 738731\n", + " chr2\n", + " 242696752\n", + " 26169279\n", + " 26169617\n", " \n", " \n", " 4\n", - " chr1_826073_aligned_166_R_0_6358_0\n", - " 600\n", + " chr3_117116174_aligned_proc0:36_F_0_7402_0\n", + " 342\n", " 0\n", - " 600\n", - " -\n", - " chr1\n", - " 1000000\n", - " 831831\n", - " 832431\n", + " 342\n", + " +\n", + " chr3\n", + " 201105948\n", + " 117116174\n", + " 117116526\n", " \n", " \n", "\n", "" ], "text/plain": [ - " read_id read_length read_start read_end \\\n", - "0 chr1_818237_aligned_2_R_0_5720_0 600 0 600 \n", - "1 chr1_763753_aligned_21_R_0_8206_0 800 0 800 \n", - "2 chr1_541945_aligned_25_R_0_5739_0 600 0 600 \n", - "3 chr1_737931_aligned_46_F_0_8456_0 800 0 800 \n", - "4 chr1_826073_aligned_166_R_0_6358_0 600 0 600 \n", + " read_id read_length read_start \\\n", + "0 chr1_26454210_aligned_proc0:64_F_0_1535_0 263 0 \n", + "1 chr8_35080996_aligned_proc0:45_F_0_11151_0 323 3 \n", + "2 chr5_119262799_aligned_proc0:51_F_0_7992_0 265 0 \n", + "3 chr2_26169279_aligned_proc0:26_F_0_10295_0 343 0 \n", + "4 chr3_117116174_aligned_proc0:36_F_0_7402_0 342 0 \n", "\n", - " strand contig_name contig_length contig_start contig_end \n", - "0 - chr1 1000000 823357 823957 \n", - "1 - chr1 1000000 771159 771959 \n", - "2 - chr1 1000000 547084 547684 \n", - "3 + chr1 1000000 737931 738731 \n", - "4 - chr1 1000000 831831 832431 " + " read_end strand contig_name contig_length contig_start contig_end \n", + "0 263 + chr1 248387328 26454210 26454469 \n", + "1 323 + chr8 146259331 35081000 35081331 \n", + "2 265 + chr5 182045439 119262799 119263072 \n", + "3 334 + chr2 242696752 26169279 26169617 \n", + "4 342 + chr3 201105948 117116174 117116526 " ] }, - "execution_count": 3, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "mapping_paf_file = \"runs/enrich_usecase/full_run_sampler_per_window/simulator_run/mapping.paf\"\n", + "mapping_paf_file = run_dir / \"mapping.paf\"\n", + "\n", "df = pd.read_csv(mapping_paf_file, sep=\"\\t\", header=None, usecols=[0, 1, 2, 3, 4, 5, 6, 7, 8], names=[\"read_id\", \"read_length\", \"read_start\", \"read_end\", \"strand\", \"contig_name\", \"contig_length\", \"contig_start\", \"contig_end\"])\n", "df.head()" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -205,72 +218,72 @@ " \n", " \n", " 0\n", - " chr1_818237_aligned_2_R_0_5720_0\n", - " 600\n", + " chr1_26454210_aligned_proc0:64_F_0_1535_0\n", + " 263\n", " 0\n", - " 600\n", - " -\n", + " 263\n", + " +\n", " chr1\n", - " 1000000\n", - " 823357\n", - " 823957\n", + " 248387328\n", + " 26454210\n", + " 26454469\n", " chr1\n", " True\n", " \n", " \n", " 1\n", - " chr1_763753_aligned_21_R_0_8206_0\n", - " 800\n", - " 0\n", - " 800\n", - " -\n", - " chr1\n", - " 1000000\n", - " 771159\n", - " 771959\n", - " chr1\n", + " chr8_35080996_aligned_proc0:45_F_0_11151_0\n", + " 323\n", + " 3\n", + " 323\n", + " +\n", + " chr8\n", + " 146259331\n", + " 35081000\n", + " 35081331\n", + " chr8\n", " True\n", " \n", " \n", " 2\n", - " chr1_541945_aligned_25_R_0_5739_0\n", - " 600\n", + " chr5_119262799_aligned_proc0:51_F_0_7992_0\n", + " 265\n", " 0\n", - " 600\n", - " -\n", - " chr1\n", - " 1000000\n", - " 547084\n", - " 547684\n", - " chr1\n", + " 265\n", + " +\n", + " chr5\n", + " 182045439\n", + " 119262799\n", + " 119263072\n", + " chr5\n", " True\n", " \n", " \n", " 3\n", - " chr1_737931_aligned_46_F_0_8456_0\n", - " 800\n", + " chr2_26169279_aligned_proc0:26_F_0_10295_0\n", + " 343\n", " 0\n", - " 800\n", + " 334\n", " +\n", - " chr1\n", - " 1000000\n", - " 737931\n", - " 738731\n", - " chr1\n", + " chr2\n", + " 242696752\n", + " 26169279\n", + " 26169617\n", + " chr2\n", " True\n", " \n", " \n", " 4\n", - " chr1_826073_aligned_166_R_0_6358_0\n", - " 600\n", + " chr3_117116174_aligned_proc0:36_F_0_7402_0\n", + " 342\n", " 0\n", - " 600\n", - " -\n", - " chr1\n", - " 1000000\n", - " 831831\n", - " 832431\n", - " chr1\n", + " 342\n", + " +\n", + " chr3\n", + " 201105948\n", + " 117116174\n", + " 117116526\n", + " chr3\n", " True\n", " \n", " \n", @@ -278,19 +291,19 @@ "" ], "text/plain": [ - " read_id read_length read_start read_end \\\n", - "0 chr1_818237_aligned_2_R_0_5720_0 600 0 600 \n", - "1 chr1_763753_aligned_21_R_0_8206_0 800 0 800 \n", - "2 chr1_541945_aligned_25_R_0_5739_0 600 0 600 \n", - "3 chr1_737931_aligned_46_F_0_8456_0 800 0 800 \n", - "4 chr1_826073_aligned_166_R_0_6358_0 600 0 600 \n", + " read_id read_length read_start \\\n", + "0 chr1_26454210_aligned_proc0:64_F_0_1535_0 263 0 \n", + "1 chr8_35080996_aligned_proc0:45_F_0_11151_0 323 3 \n", + "2 chr5_119262799_aligned_proc0:51_F_0_7992_0 265 0 \n", + "3 chr2_26169279_aligned_proc0:26_F_0_10295_0 343 0 \n", + "4 chr3_117116174_aligned_proc0:36_F_0_7402_0 342 0 \n", "\n", - " strand contig_name contig_length contig_start contig_end chrom \\\n", - "0 - chr1 1000000 823357 823957 chr1 \n", - "1 - chr1 1000000 771159 771959 chr1 \n", - "2 - chr1 1000000 547084 547684 chr1 \n", - "3 + chr1 1000000 737931 738731 chr1 \n", - "4 - chr1 1000000 831831 832431 chr1 \n", + " read_end strand contig_name contig_length contig_start contig_end chrom \\\n", + "0 263 + chr1 248387328 26454210 26454469 chr1 \n", + "1 323 + chr8 146259331 35081000 35081331 chr8 \n", + "2 265 + chr5 182045439 119262799 119263072 chr5 \n", + "3 334 + chr2 242696752 26169279 26169617 chr2 \n", + "4 342 + chr3 201105948 117116174 117116526 chr3 \n", "\n", " mapping_correct \n", "0 True \n", @@ -300,7 +313,7 @@ "4 True " ] }, - "execution_count": 4, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -313,16 +326,16 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "0.999574612897737" + "0.8156957759568824" ] }, - "execution_count": 5, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -334,7 +347,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -343,13 +356,13 @@ "Text(0, 0.5, 'Cumulative mapping correct')" ] }, - "execution_count": 6, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -359,12 +372,13 @@ } ], "source": [ + "# can be used to see if the mapping fails initially (for small chunk indices) pointing to the mapper being overwhelmed by the sheer number of channels\n", "df[\"chunk_idx\"] = np.arange(len(df))\n", "df[\"cum_mapping_correct\"] = df[\"mapping_correct\"].cumsum()\n", "fig, ax = plt.subplots()\n", "sns.lineplot(data=df, x=\"chunk_idx\", y=\"cum_mapping_correct\", ax=ax)\n", "ax.set_xlabel(\"Chunk index\")\n", - "ax.set_ylabel(\"Cumulative mapping correct\")" + "ax.set_ylabel(\"Cumulative number of correct mappings\")" ] }, { @@ -378,7 +392,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -422,135 +436,135 @@ " \n", " \n", " 0\n", - " 2\n", + " 12\n", " 1\n", - " chr1_818237_aligned_2_R_0_5720_0\n", - " 15\n", - " chr1_818237_aligned_2_R_0_5720_0\n", - " 600\n", + " chr4_79091622_aligned_proc0:17_R_0_10859_0\n", + " 126\n", + " chr4_79091622_aligned_proc0:17_R_0_10859_0\n", + " 319\n", " 1\n", - " single_on\n", - " stop_receiving\n", - " enrich_chr_1\n", + " control\n", + " True\n", + " control\n", " False\n", " False\n", " 0.000000\n", - " 0.000218\n", + " 0.000864\n", " 0.000000\n", " \n", " \n", " 1\n", + " 12\n", " 2\n", - " 2\n", - " chr1_763753_aligned_21_R_0_8206_0\n", - " 78\n", - " chr1_763753_aligned_21_R_0_8206_0\n", - " 800\n", + " chr1_51922780_aligned_proc0:2_R_0_35088_0\n", + " 15\n", + " chr1_51922780_aligned_proc0:2_R_0_35088_0\n", + " 246\n", " 1\n", - " single_on\n", - " stop_receiving\n", - " enrich_chr_1\n", + " control\n", + " True\n", + " control\n", " False\n", " False\n", - " 0.008041\n", - " 0.008245\n", - " 0.008028\n", + " 0.001536\n", + " 0.001548\n", + " 0.000683\n", " \n", " \n", " 2\n", - " 2\n", + " 12\n", " 3\n", - " chr1_541945_aligned_25_R_0_5739_0\n", - " 86\n", - " chr1_541945_aligned_25_R_0_5739_0\n", - " 600\n", + " chr1_26454210_aligned_proc0:64_F_0_1535_0\n", + " 482\n", + " chr1_26454210_aligned_proc0:64_F_0_1535_0\n", + " 263\n", " 1\n", - " single_on\n", - " stop_receiving\n", - " enrich_chr_1\n", + " single_off\n", + " unblock\n", + " enrich_chr_16_20\n", " False\n", " False\n", - " 0.050249\n", - " 0.050441\n", - " 0.050222\n", + " 0.001865\n", + " 0.004453\n", + " 0.003588\n", " \n", " \n", " 3\n", - " 2\n", + " 12\n", " 4\n", - " chr1_737931_aligned_46_F_0_8456_0\n", - " 191\n", - " chr1_737931_aligned_46_F_0_8456_0\n", - " 800\n", + " chr8_35080996_aligned_proc0:45_F_0_11151_0\n", + " 314\n", + " chr8_35080996_aligned_proc0:45_F_0_11151_0\n", + " 323\n", " 1\n", - " single_on\n", - " stop_receiving\n", - " enrich_chr_1\n", + " single_off\n", + " unblock\n", + " enrich_chr_9_14\n", " False\n", " False\n", - " 0.282108\n", - " 0.282311\n", - " 0.282090\n", + " 0.004892\n", + " 0.005007\n", + " 0.004142\n", " \n", " \n", " 4\n", - " 2\n", + " 12\n", " 5\n", - " chr1_826073_aligned_166_R_0_6358_0\n", - " 165\n", - " chr1_826073_aligned_166_R_0_6358_0\n", - " 600\n", + " chr5_119262799_aligned_proc0:51_F_0_7992_0\n", + " 375\n", + " chr5_119262799_aligned_proc0:51_F_0_7992_0\n", + " 265\n", " 1\n", - " single_on\n", - " stop_receiving\n", - " enrich_chr_1\n", + " single_off\n", + " unblock\n", + " enrich_chr_9_14\n", " False\n", " False\n", - " 0.332250\n", - " 0.332455\n", - " 0.332234\n", + " 0.005365\n", + " 0.005459\n", + " 0.004594\n", " \n", " \n", "\n", "" ], "text/plain": [ - " client_iteration read_in_loop read_id \\\n", - "0 2 1 chr1_818237_aligned_2_R_0_5720_0 \n", - "1 2 2 chr1_763753_aligned_21_R_0_8206_0 \n", - "2 2 3 chr1_541945_aligned_25_R_0_5739_0 \n", - "3 2 4 chr1_737931_aligned_46_F_0_8456_0 \n", - "4 2 5 chr1_826073_aligned_166_R_0_6358_0 \n", + " client_iteration read_in_loop read_id \\\n", + "0 12 1 chr4_79091622_aligned_proc0:17_R_0_10859_0 \n", + "1 12 2 chr1_51922780_aligned_proc0:2_R_0_35088_0 \n", + "2 12 3 chr1_26454210_aligned_proc0:64_F_0_1535_0 \n", + "3 12 4 chr8_35080996_aligned_proc0:45_F_0_11151_0 \n", + "4 12 5 chr5_119262799_aligned_proc0:51_F_0_7992_0 \n", "\n", - " channel read_number seq_len counter mode \\\n", - "0 15 chr1_818237_aligned_2_R_0_5720_0 600 1 single_on \n", - "1 78 chr1_763753_aligned_21_R_0_8206_0 800 1 single_on \n", - "2 86 chr1_541945_aligned_25_R_0_5739_0 600 1 single_on \n", - "3 191 chr1_737931_aligned_46_F_0_8456_0 800 1 single_on \n", - "4 165 chr1_826073_aligned_166_R_0_6358_0 600 1 single_on \n", + " channel read_number seq_len counter \\\n", + "0 126 chr4_79091622_aligned_proc0:17_R_0_10859_0 319 1 \n", + "1 15 chr1_51922780_aligned_proc0:2_R_0_35088_0 246 1 \n", + "2 482 chr1_26454210_aligned_proc0:64_F_0_1535_0 263 1 \n", + "3 314 chr8_35080996_aligned_proc0:45_F_0_11151_0 323 1 \n", + "4 375 chr5_119262799_aligned_proc0:51_F_0_7992_0 265 1 \n", "\n", - " decision condition min_threshold count_threshold \\\n", - "0 stop_receiving enrich_chr_1 False False \n", - "1 stop_receiving enrich_chr_1 False False \n", - "2 stop_receiving enrich_chr_1 False False \n", - "3 stop_receiving enrich_chr_1 False False \n", - "4 stop_receiving enrich_chr_1 False False \n", + " mode decision condition min_threshold count_threshold \\\n", + "0 control True control False False \n", + "1 control True control False False \n", + "2 single_off unblock enrich_chr_16_20 False False \n", + "3 single_off unblock enrich_chr_9_14 False False \n", + "4 single_off unblock enrich_chr_9_14 False False \n", "\n", " start_analysis end_analysis timestamp \n", - "0 0.000000 0.000218 0.000000 \n", - "1 0.008041 0.008245 0.008028 \n", - "2 0.050249 0.050441 0.050222 \n", - "3 0.282108 0.282311 0.282090 \n", - "4 0.332250 0.332455 0.332234 " + "0 0.000000 0.000864 0.000000 \n", + "1 0.001536 0.001548 0.000683 \n", + "2 0.001865 0.004453 0.003588 \n", + "3 0.004892 0.005007 0.004142 \n", + "4 0.005365 0.005459 0.004594 " ] }, - "execution_count": 12, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "chunk_log = \"runs/enrich_usecase/full_run_sampler_per_window/simulator_run/chunk_log.txt\"\n", + "chunk_log = run_dir / \"chunk_log.txt\"\n", "chunk_df = pd.read_csv(chunk_log, sep=\"\\t\")\n", "first_time = chunk_df[\"start_analysis\"].min()\n", "chunk_df[\"start_analysis\"] -= first_time\n", @@ -563,9 +577,160 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 21, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following reads have contradicting decisions: ['chr10_46560475_aligned_proc1:8557_R_0_10144_0'\n", + " 'chr10_51022679_aligned_proc0:15129_R_0_4742_0'\n", + " 'chr10_57355202_aligned_proc1:2861_R_0_24476_0'\n", + " 'chr10_95690626_aligned_proc0:22338_F_0_13966_0'\n", + " 'chr11_13489989_aligned_proc1:5601_F_0_3642_0'\n", + " 'chr11_3443736_aligned_proc0:17762_F_0_1555_0'\n", + " 'chr11_65986646_aligned_proc0:44259_R_0_8924_0'\n", + " 'chr11_67699121_aligned_proc1:32911_F_0_18565_0'\n", + " 'chr11_67773251_aligned_proc0:46991_F_0_1263_0'\n", + " 'chr11_79041662_aligned_proc1:11941_R_0_30699_0'\n", + " 'chr11_83978157_aligned_proc0:45408_F_0_988_0'\n", + " 'chr11_93901830_aligned_proc0:1066_R_0_30342_0'\n", + " 'chr12_100988723_aligned_proc1:9500_R_0_6199_0'\n", + " 'chr12_102809304_aligned_proc0:43933_R_0_9229_0'\n", + " 'chr12_18717659_aligned_proc1:22380_F_0_4309_0'\n", + " 'chr12_35213329_aligned_proc1:37488_F_0_10051_0'\n", + " 'chr12_50869782_aligned_proc1:10980_F_0_7198_0'\n", + " 'chr12_69368901_aligned_proc0:18483_R_0_7777_0'\n", + " 'chr13_48384073_aligned_proc1:42729_F_0_9275_0'\n", + " 'chr13_89341620_aligned_proc1:35990_R_0_7831_0'\n", + " 'chr14_2593612_aligned_proc0:31067_R_0_4084_0'\n", + " 'chr14_39765478_aligned_proc1:3662_R_0_6925_0'\n", + " 'chr14_7252598_aligned_proc1:4960_R_0_18754_0'\n", + " 'chr14_8281639_aligned_proc0:46007_R_0_14063_0'\n", + " 'chr14_8492702_aligned_proc1:16602_R_0_6974_0'\n", + " 'chr15_19557707_aligned_proc1:37344_F_0_3725_0'\n", + " 'chr15_25967950_aligned_proc1:46938_R_0_14443_0'\n", + " 'chr15_26063686_aligned_proc1:47323_F_0_3605_0'\n", + " 'chr15_26993023_aligned_proc0:37907_F_0_12110_0'\n", + " 'chr15_38387169_aligned_proc1:33753_F_0_13189_0'\n", + " 'chr15_3870910_aligned_proc0:26542_R_0_3762_0'\n", + " 'chr15_44915189_aligned_proc0:48803_F_0_10344_0'\n", + " 'chr16_52149344_aligned_proc0:25461_R_0_7199_0'\n", + " 'chr16_71069423_aligned_proc0:6274_F_0_871_0'\n", + " 'chr16_76408721_aligned_proc0:37110_R_0_20934_0'\n", + " 'chr17_16177646_aligned_proc1:32996_R_0_957_0'\n", + " 'chr18_14193416_aligned_proc1:34833_R_0_13101_0'\n", + " 'chr18_17587817_aligned_proc0:10739_F_0_7250_0'\n", + " 'chr18_2990210_aligned_proc0:23579_F_0_1180_0'\n", + " 'chr19_18144550_aligned_proc0:30723_R_0_22472_0'\n", + " 'chr19_25360455_aligned_proc1:18421_R_0_7774_0'\n", + " 'chr19_26242398_aligned_proc1:18836_F_0_9834_0'\n", + " 'chr19_39464877_aligned_proc1:34998_R_0_14918_0'\n", + " 'chr1_124032594_aligned_proc1:39712_R_0_4410_0'\n", + " 'chr1_128420334_aligned_proc1:8631_F_0_19655_0'\n", + " 'chr1_128653974_aligned_proc0:44830_R_0_883_0'\n", + " 'chr1_135842843_aligned_proc1:19491_F_0_12556_0'\n", + " 'chr1_162080599_aligned_proc1:2487_R_0_15422_0'\n", + " 'chr1_96214307_aligned_proc0:39567_R_0_15558_0'\n", + " 'chr1_9968920_aligned_proc1:37847_F_0_7081_0'\n", + " 'chr20_15197783_aligned_proc1:46479_F_0_13466_0'\n", + " 'chr20_25158745_aligned_proc1:34813_R_0_12951_0'\n", + " 'chr20_26089332_aligned_proc1:33096_F_0_4777_0'\n", + " 'chr20_30217908_aligned_proc0:18726_R_0_11330_0'\n", + " 'chr20_30418603_aligned_proc0:2936_F_0_5432_0'\n", + " 'chr20_30594460_aligned_proc0:44865_F_0_6238_0'\n", + " 'chr20_31623226_aligned_proc0:42643_R_0_7972_0'\n", + " 'chr20_35870999_aligned_proc0:38245_R_0_12794_0'\n", + " 'chr20_40792048_aligned_proc0:33537_F_0_2262_0'\n", + " 'chr20_61317321_aligned_proc1:49070_F_0_7010_0'\n", + " 'chr21_11161655_aligned_proc1:17388_R_0_7114_0'\n", + " 'chr21_23612599_aligned_proc0:21285_R_0_12620_0'\n", + " 'chr21_41489503_aligned_proc1:26980_R_0_17814_0'\n", + " 'chr21_5248173_aligned_proc0:30336_F_0_7183_0'\n", + " 'chr21_6169884_aligned_proc1:29955_R_0_6273_0'\n", + " 'chr22_11310596_aligned_proc0:21221_F_0_9981_0'\n", + " 'chr22_16107138_aligned_proc0:13091_F_0_3196_0'\n", + " 'chr22_21681969_aligned_proc0:45221_F_0_12802_0'\n", + " 'chr22_25111818_aligned_proc0:20384_R_0_4431_0'\n", + " 'chr22_31832726_aligned_proc1:29276_R_0_20265_0'\n", + " 'chr22_31867035_aligned_proc1:34519_R_0_952_0'\n", + " 'chr22_5446207_aligned_proc0:24705_R_0_6289_0'\n", + " 'chr2_140751766_aligned_proc1:28348_F_0_10596_0'\n", + " 'chr2_146853335_aligned_proc1:2200_F_0_8993_0'\n", + " 'chr2_168253672_aligned_proc1:30454_R_0_2784_0'\n", + " 'chr2_190400513_aligned_proc1:6854_R_0_12491_0'\n", + " 'chr2_198206518_aligned_proc0:41877_R_0_7323_0'\n", + " 'chr2_54474365_aligned_proc0:43024_R_0_8114_0'\n", + " 'chr2_74800789_aligned_proc0:7329_F_0_3593_0'\n", + " 'chr3_101089418_aligned_proc0:35024_R_0_11684_0'\n", + " 'chr3_132923394_aligned_proc1:24600_F_0_947_0'\n", + " 'chr3_143371346_aligned_proc1:43167_R_0_1204_0'\n", + " 'chr3_173897725_aligned_proc0:49640_R_0_5091_0'\n", + " 'chr4_108001666_aligned_proc0:41302_R_0_1142_0'\n", + " 'chr4_129670423_aligned_proc1:12925_R_0_12606_0'\n", + " 'chr4_158225635_aligned_proc0:34109_R_0_26976_0'\n", + " 'chr4_161631671_aligned_proc0:19883_F_0_1768_0'\n", + " 'chr4_162001002_aligned_proc1:17077_R_0_13868_0'\n", + " 'chr4_16635_aligned_proc0:1491_F_0_5297_0'\n", + " 'chr4_193069225_aligned_proc1:30482_R_0_3536_0'\n", + " 'chr4_193469036_aligned_proc0:42397_R_0_13000_0'\n", + " 'chr4_32721384_aligned_proc0:12985_R_0_12866_0'\n", + " 'chr4_46251507_aligned_proc0:8453_R_0_11417_0'\n", + " 'chr4_52459859_aligned_proc1:49498_F_0_7552_0'\n", + " 'chr4_57204653_aligned_proc1:25149_R_0_11764_0'\n", + " 'chr4_67388664_aligned_proc1:13243_F_0_12873_0'\n", + " 'chr4_9583325_aligned_proc1:14902_F_0_630_0'\n", + " 'chr5_146003101_aligned_proc0:14935_R_0_944_0'\n", + " 'chr5_42989255_aligned_proc1:23663_F_0_10686_0'\n", + " 'chr5_56545386_aligned_proc1:46505_F_0_11313_0'\n", + " 'chr5_79513949_aligned_proc0:26723_R_0_6100_0'\n", + " 'chr6_19388917_aligned_proc0:5578_R_0_9180_0'\n", + " 'chr6_75878370_aligned_proc0:4691_R_0_720_0'\n", + " 'chr7_116477133_aligned_proc1:44199_R_0_1709_0'\n", + " 'chr7_151809288_aligned_proc0:6781_R_0_14919_0'\n", + " 'chr7_58029291_aligned_proc0:29200_F_0_20787_0'\n", + " 'chr7_58099971_aligned_proc1:44551_F_0_7750_0'\n", + " 'chr7_66727152_aligned_proc1:40767_R_0_3805_0'\n", + " 'chr8_99847749_aligned_proc0:760_R_0_16778_0'\n", + " 'chr9_105972060_aligned_proc1:42632_F_0_9583_0'\n", + " 'chr9_129852539_aligned_proc1:8824_F_0_3056_0'\n", + " 'chr9_30102964_aligned_proc1:398_R_0_23344_0'\n", + " 'chr9_33661732_aligned_proc0:45082_R_0_18589_0'\n", + " 'chr9_40309392_aligned_proc1:49611_F_0_19854_0'\n", + " 'chr9_40454857_aligned_proc0:33702_F_0_1068_0'\n", + " 'chr9_42868514_aligned_proc0:31014_F_0_6221_0'\n", + " 'chr9_45813582_aligned_proc1:36286_F_0_10326_0'\n", + " 'chr9_75329071_aligned_proc1:30586_F_0_11208_0'\n", + " 'chr9_77335760_aligned_proc1:5947_F_0_2326_0'\n", + " 'chrX_104593026_aligned_proc0:45220_R_0_22565_0'\n", + " 'chrX_110913597_aligned_proc1:5015_R_0_3464_0'\n", + " 'chrX_111331_aligned_proc1:34711_F_0_10062_0'\n", + " 'chrX_113112332_aligned_proc1:515_R_0_1292_0'\n", + " 'chrX_115736885_aligned_proc0:4644_F_0_18129_0'\n", + " 'chrX_119653213_aligned_proc0:21547_R_0_12532_0'\n", + " 'chrX_124386630_aligned_proc0:19718_R_0_23355_0'\n", + " 'chrX_129152314_aligned_proc1:7330_R_0_8222_0'\n", + " 'chrX_140736248_aligned_proc1:24214_F_0_1410_0'\n", + " 'chrX_153106503_aligned_proc0:45685_F_0_1234_0'\n", + " 'chrX_153558546_aligned_proc0:32738_F_0_5713_0'\n", + " 'chrX_20729273_aligned_proc0:26535_R_0_11241_0'\n", + " 'chrX_26075894_aligned_proc1:45825_R_0_3721_0'\n", + " 'chrX_5037558_aligned_proc1:3909_F_0_19167_0'\n", + " 'chrX_61668685_aligned_proc1:18226_R_0_12795_0'\n", + " 'chrX_63309112_aligned_proc0:49759_F_0_15355_0'\n", + " 'chrX_64775308_aligned_proc1:35335_F_0_23726_0'\n", + " 'chrX_6488382_aligned_proc0:6136_R_0_3175_0'\n", + " 'chrX_65679376_aligned_proc0:42394_F_0_2851_0'\n", + " 'chrY_11353602_aligned_proc0:6949_F_0_22804_0'\n", + " 'chrY_24179543_aligned_proc1:8323_F_0_3643_0'\n", + " 'chrY_3830730_aligned_proc1:37676_R_0_6535_0'\n", + " 'chrY_40962526_aligned_proc1:6606_F_0_24037_0'\n", + " 'chrY_52876394_aligned_proc0:8058_F_0_13033_0'\n", + " 'chrY_8103282_aligned_proc0:36625_R_0_11099_0']\n" + ] + } + ], "source": [ "# check whether some reads have contradicting decisions (except for \"proceed\")\n", "df1 = chunk_df[chunk_df[\"decision\"] != \"proceed\"]\n", @@ -577,7 +742,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -625,119 +790,119 @@ " \n", " \n", " \n", - " 4555\n", - " chr1_818237_aligned_2_R_0_5720_0\n", - " 2\n", + " 1524713\n", + " chr4_79091622_aligned_proc0:17_R_0_10859_0\n", + " 12\n", " 1\n", - " 15\n", - " chr1_818237_aligned_2_R_0_5720_0\n", - " 600\n", + " 126\n", + " chr4_79091622_aligned_proc0:17_R_0_10859_0\n", + " 319\n", " 1\n", - " single_on\n", - " stop_receiving\n", - " enrich_chr_1\n", + " control\n", + " True\n", + " control\n", " False\n", " False\n", " 0.000000\n", - " 0.000218\n", + " 0.000864\n", " 0.000000\n", - " chr1\n", + " chr4\n", + " False\n", " False\n", - " True\n", " 0\n", - " 1\n", + " 0\n", " \n", " \n", - " 4196\n", - " chr1_763753_aligned_21_R_0_8206_0\n", + " 912219\n", + " chr1_51922780_aligned_proc0:2_R_0_35088_0\n", + " 12\n", " 2\n", - " 2\n", - " 78\n", - " chr1_763753_aligned_21_R_0_8206_0\n", - " 800\n", + " 15\n", + " chr1_51922780_aligned_proc0:2_R_0_35088_0\n", + " 246\n", " 1\n", - " single_on\n", - " stop_receiving\n", - " enrich_chr_1\n", + " control\n", + " True\n", + " control\n", " False\n", " False\n", - " 0.008041\n", - " 0.008245\n", - " 0.008028\n", + " 0.001536\n", + " 0.001548\n", + " 0.000683\n", " chr1\n", " False\n", - " True\n", + " False\n", + " 0\n", " 0\n", - " 2\n", " \n", " \n", - " 2810\n", - " chr1_541945_aligned_25_R_0_5739_0\n", - " 2\n", + " 891103\n", + " chr1_26454210_aligned_proc0:64_F_0_1535_0\n", + " 12\n", " 3\n", - " 86\n", - " chr1_541945_aligned_25_R_0_5739_0\n", - " 600\n", + " 482\n", + " chr1_26454210_aligned_proc0:64_F_0_1535_0\n", + " 263\n", " 1\n", - " single_on\n", - " stop_receiving\n", - " enrich_chr_1\n", + " single_off\n", + " unblock\n", + " enrich_chr_16_20\n", " False\n", " False\n", - " 0.050249\n", - " 0.050441\n", - " 0.050222\n", + " 0.001865\n", + " 0.004453\n", + " 0.003588\n", " chr1\n", - " False\n", " True\n", + " False\n", + " 1\n", " 0\n", - " 3\n", " \n", " \n", - " 4014\n", - " chr1_737931_aligned_46_F_0_8456_0\n", - " 2\n", + " 1976384\n", + " chr8_35080996_aligned_proc0:45_F_0_11151_0\n", + " 12\n", " 4\n", - " 191\n", - " chr1_737931_aligned_46_F_0_8456_0\n", - " 800\n", + " 314\n", + " chr8_35080996_aligned_proc0:45_F_0_11151_0\n", + " 323\n", " 1\n", - " single_on\n", - " stop_receiving\n", - " enrich_chr_1\n", - " False\n", + " single_off\n", + " unblock\n", + " enrich_chr_9_14\n", " False\n", - " 0.282108\n", - " 0.282311\n", - " 0.282090\n", - " chr1\n", " False\n", + " 0.004892\n", + " 0.005007\n", + " 0.004142\n", + " chr8\n", " True\n", + " False\n", + " 1\n", " 0\n", - " 4\n", " \n", " \n", - " 4608\n", - " chr1_826073_aligned_166_R_0_6358_0\n", - " 2\n", + " 1557842\n", + " chr5_119262799_aligned_proc0:51_F_0_7992_0\n", + " 12\n", " 5\n", - " 165\n", - " chr1_826073_aligned_166_R_0_6358_0\n", - " 600\n", + " 375\n", + " chr5_119262799_aligned_proc0:51_F_0_7992_0\n", + " 265\n", " 1\n", - " single_on\n", - " stop_receiving\n", - " enrich_chr_1\n", - " False\n", + " single_off\n", + " unblock\n", + " enrich_chr_9_14\n", " False\n", - " 0.332250\n", - " 0.332455\n", - " 0.332234\n", - " chr1\n", " False\n", + " 0.005365\n", + " 0.005459\n", + " 0.004594\n", + " chr5\n", " True\n", + " False\n", + " 1\n", " 0\n", - " 5\n", " \n", " \n", " ...\n", @@ -763,119 +928,119 @@ " ...\n", " \n", " \n", - " 10904\n", - " chr2_948429_aligned_11335_R_0_7314_0\n", - " 258\n", - " 57\n", - " 331\n", - " chr2_948429_aligned_11335_R_0_7314_0\n", - " 5800\n", - " 1\n", - " single_off\n", - " unblock\n", - " enrich_chr_1\n", - " False\n", - " False\n", - " 53.131931\n", - " 53.132076\n", - " 53.131196\n", - " chr2\n", - " True\n", - " False\n", - " 4938\n", - " 3\n", - " \n", - " \n", - " 6312\n", - " chr2_208090_aligned_11673_F_0_5703_0\n", - " 258\n", - " 58\n", - " 476\n", - " chr2_208090_aligned_11673_F_0_5703_0\n", + " 2115686\n", + " chr9_68107336_aligned_proc0:9822_R_0_6730_0\n", + " 2449\n", + " 22\n", + " 254\n", + " chr9_68107336_aligned_proc0:9822_R_0_6730_0\n", " 200\n", - " 1\n", - " single_off\n", - " unblock\n", - " enrich_chr_1\n", - " False\n", + " 12\n", + " exceeded_max_chunks_unblocked\n", + " exceeded_max_chunks_unblocked\n", + " enrich_chr_1_8\n", " False\n", - " 53.132271\n", - " 53.132340\n", - " 53.131459\n", - " chr2\n", " True\n", + " 128.434243\n", + " 128.434250\n", + " 128.433385\n", + " chr9\n", " False\n", - " 4939\n", - " 3\n", + " False\n", + " 263\n", + " 133\n", " \n", " \n", - " 9514\n", - " chr2_72326_aligned_11545_R_0_6764_0\n", - " 258\n", - " 59\n", - " 461\n", - " chr2_72326_aligned_11545_R_0_6764_0\n", - " 2000\n", + " 282185\n", + " chr12_82202404_aligned_proc0:10025_R_0_11764_0\n", + " 2449\n", + " 24\n", + " 269\n", + " chr12_82202404_aligned_proc0:10025_R_0_11764_0\n", + " 342\n", " 1\n", - " single_off\n", - " unblock\n", - " enrich_chr_1\n", + " single_on\n", + " stop_receiving\n", + " enrich_chr_9_14\n", " False\n", " False\n", - " 53.133177\n", - " 53.133263\n", - " 53.132383\n", - " chr2\n", - " True\n", + " 128.434833\n", + " 128.434892\n", + " 128.434027\n", + " chr12\n", " False\n", - " 4940\n", - " 3\n", + " True\n", + " 262\n", + " 131\n", " \n", " \n", - " 1240\n", - " chr1_294523_aligned_11554_F_0_9141_0\n", - " 258\n", - " 60\n", - " 144\n", - " chr1_294523_aligned_11554_F_0_9141_0\n", - " 1800\n", + " 321565\n", + " chr13_28038874_aligned_proc0:10008_R_0_1007_0\n", + " 2449\n", + " 25\n", + " 352\n", + " chr13_28038874_aligned_proc0:10008_R_0_1007_0\n", + " 384\n", " 1\n", " single_on\n", " stop_receiving\n", - " enrich_chr_1\n", + " enrich_chr_9_14\n", " False\n", " False\n", - " 53.134323\n", - " 53.134421\n", - " 53.133541\n", - " chr1\n", + " 128.435300\n", + " 128.435361\n", + " 128.434495\n", + " chr13\n", " False\n", " True\n", - " 0\n", - " 5056\n", + " 204\n", + " 106\n", " \n", " \n", - " 9906\n", - " chr2_79073_aligned_11329_F_0_9603_0\n", - " 258\n", - " 61\n", - " 416\n", - " chr2_79073_aligned_11329_F_0_9603_0\n", - " 5800\n", + " 1604331\n", + " chr5_175553425_aligned_proc0:10034_F_0_7706_0\n", + " 2449\n", + " 26\n", + " 464\n", + " chr5_175553425_aligned_proc0:10034_F_0_7706_0\n", + " 225\n", " 1\n", " single_off\n", " unblock\n", - " enrich_chr_1\n", + " enrich_chr_16_20\n", " False\n", " False\n", - " 53.136868\n", - " 53.137215\n", - " 53.136335\n", - " chr2\n", + " 128.435569\n", + " 128.435622\n", + " 128.434756\n", + " chr5\n", " True\n", " False\n", - " 4941\n", - " 3\n", + " 372\n", + " 126\n", + " \n", + " \n", + " 2063327\n", + " chr9_139979310_aligned_proc0:10028_R_0_6990_0\n", + " 2449\n", + " 27\n", + " 300\n", + " chr9_139979310_aligned_proc0:10028_R_0_6990_0\n", + " 354\n", + " 1\n", + " single_on\n", + " stop_receiving\n", + " enrich_chr_9_14\n", + " False\n", + " False\n", + " 128.435911\n", + " 128.435969\n", + " 128.435104\n", + " chr9\n", + " False\n", + " True\n", + " 263\n", + " 134\n", " \n", " \n", "\n", @@ -883,88 +1048,101 @@ "" ], "text/plain": [ - " read_id client_iteration read_in_loop \\\n", - "4555 chr1_818237_aligned_2_R_0_5720_0 2 1 \n", - "4196 chr1_763753_aligned_21_R_0_8206_0 2 2 \n", - "2810 chr1_541945_aligned_25_R_0_5739_0 2 3 \n", - "4014 chr1_737931_aligned_46_F_0_8456_0 2 4 \n", - "4608 chr1_826073_aligned_166_R_0_6358_0 2 5 \n", - "... ... ... ... \n", - "10904 chr2_948429_aligned_11335_R_0_7314_0 258 57 \n", - "6312 chr2_208090_aligned_11673_F_0_5703_0 258 58 \n", - "9514 chr2_72326_aligned_11545_R_0_6764_0 258 59 \n", - "1240 chr1_294523_aligned_11554_F_0_9141_0 258 60 \n", - "9906 chr2_79073_aligned_11329_F_0_9603_0 258 61 \n", + " read_id client_iteration \\\n", + "1524713 chr4_79091622_aligned_proc0:17_R_0_10859_0 12 \n", + "912219 chr1_51922780_aligned_proc0:2_R_0_35088_0 12 \n", + "891103 chr1_26454210_aligned_proc0:64_F_0_1535_0 12 \n", + "1976384 chr8_35080996_aligned_proc0:45_F_0_11151_0 12 \n", + "1557842 chr5_119262799_aligned_proc0:51_F_0_7992_0 12 \n", + "... ... ... \n", + "2115686 chr9_68107336_aligned_proc0:9822_R_0_6730_0 2449 \n", + "282185 chr12_82202404_aligned_proc0:10025_R_0_11764_0 2449 \n", + "321565 chr13_28038874_aligned_proc0:10008_R_0_1007_0 2449 \n", + "1604331 chr5_175553425_aligned_proc0:10034_F_0_7706_0 2449 \n", + "2063327 chr9_139979310_aligned_proc0:10028_R_0_6990_0 2449 \n", "\n", - " channel read_number seq_len counter \\\n", - "4555 15 chr1_818237_aligned_2_R_0_5720_0 600 1 \n", - "4196 78 chr1_763753_aligned_21_R_0_8206_0 800 1 \n", - "2810 86 chr1_541945_aligned_25_R_0_5739_0 600 1 \n", - "4014 191 chr1_737931_aligned_46_F_0_8456_0 800 1 \n", - "4608 165 chr1_826073_aligned_166_R_0_6358_0 600 1 \n", - "... ... ... ... ... \n", - "10904 331 chr2_948429_aligned_11335_R_0_7314_0 5800 1 \n", - "6312 476 chr2_208090_aligned_11673_F_0_5703_0 200 1 \n", - "9514 461 chr2_72326_aligned_11545_R_0_6764_0 2000 1 \n", - "1240 144 chr1_294523_aligned_11554_F_0_9141_0 1800 1 \n", - "9906 416 chr2_79073_aligned_11329_F_0_9603_0 5800 1 \n", + " read_in_loop channel \\\n", + "1524713 1 126 \n", + "912219 2 15 \n", + "891103 3 482 \n", + "1976384 4 314 \n", + "1557842 5 375 \n", + "... ... ... \n", + "2115686 22 254 \n", + "282185 24 269 \n", + "321565 25 352 \n", + "1604331 26 464 \n", + "2063327 27 300 \n", "\n", - " mode decision condition min_threshold \\\n", - "4555 single_on stop_receiving enrich_chr_1 False \n", - "4196 single_on stop_receiving enrich_chr_1 False \n", - "2810 single_on stop_receiving enrich_chr_1 False \n", - "4014 single_on stop_receiving enrich_chr_1 False \n", - "4608 single_on stop_receiving enrich_chr_1 False \n", - "... ... ... ... ... \n", - "10904 single_off unblock enrich_chr_1 False \n", - "6312 single_off unblock enrich_chr_1 False \n", - "9514 single_off unblock enrich_chr_1 False \n", - "1240 single_on stop_receiving enrich_chr_1 False \n", - "9906 single_off unblock enrich_chr_1 False \n", + " read_number seq_len counter \\\n", + "1524713 chr4_79091622_aligned_proc0:17_R_0_10859_0 319 1 \n", + "912219 chr1_51922780_aligned_proc0:2_R_0_35088_0 246 1 \n", + "891103 chr1_26454210_aligned_proc0:64_F_0_1535_0 263 1 \n", + "1976384 chr8_35080996_aligned_proc0:45_F_0_11151_0 323 1 \n", + "1557842 chr5_119262799_aligned_proc0:51_F_0_7992_0 265 1 \n", + "... ... ... ... \n", + "2115686 chr9_68107336_aligned_proc0:9822_R_0_6730_0 200 12 \n", + "282185 chr12_82202404_aligned_proc0:10025_R_0_11764_0 342 1 \n", + "321565 chr13_28038874_aligned_proc0:10008_R_0_1007_0 384 1 \n", + "1604331 chr5_175553425_aligned_proc0:10034_F_0_7706_0 225 1 \n", + "2063327 chr9_139979310_aligned_proc0:10028_R_0_6990_0 354 1 \n", "\n", - " count_threshold start_analysis end_analysis timestamp chrom \\\n", - "4555 False 0.000000 0.000218 0.000000 chr1 \n", - "4196 False 0.008041 0.008245 0.008028 chr1 \n", - "2810 False 0.050249 0.050441 0.050222 chr1 \n", - "4014 False 0.282108 0.282311 0.282090 chr1 \n", - "4608 False 0.332250 0.332455 0.332234 chr1 \n", - "... ... ... ... ... ... \n", - "10904 False 53.131931 53.132076 53.131196 chr2 \n", - "6312 False 53.132271 53.132340 53.131459 chr2 \n", - "9514 False 53.133177 53.133263 53.132383 chr2 \n", - "1240 False 53.134323 53.134421 53.133541 chr1 \n", - "9906 False 53.136868 53.137215 53.136335 chr2 \n", + " mode decision \\\n", + "1524713 control True \n", + "912219 control True \n", + "891103 single_off unblock \n", + "1976384 single_off unblock \n", + "1557842 single_off unblock \n", + "... ... ... \n", + "2115686 exceeded_max_chunks_unblocked exceeded_max_chunks_unblocked \n", + "282185 single_on stop_receiving \n", + "321565 single_on stop_receiving \n", + "1604331 single_off unblock \n", + "2063327 single_on stop_receiving \n", "\n", - " is_rejection is_stopreceiving cum_nb_rejections_per_chrom \\\n", - "4555 False True 0 \n", - "4196 False True 0 \n", - "2810 False True 0 \n", - "4014 False True 0 \n", - "4608 False True 0 \n", - "... ... ... ... \n", - "10904 True False 4938 \n", - "6312 True False 4939 \n", - "9514 True False 4940 \n", - "1240 False True 0 \n", - "9906 True False 4941 \n", + " condition min_threshold count_threshold start_analysis \\\n", + "1524713 control False False 0.000000 \n", + "912219 control False False 0.001536 \n", + "891103 enrich_chr_16_20 False False 0.001865 \n", + "1976384 enrich_chr_9_14 False False 0.004892 \n", + "1557842 enrich_chr_9_14 False False 0.005365 \n", + "... ... ... ... ... \n", + "2115686 enrich_chr_1_8 False True 128.434243 \n", + "282185 enrich_chr_9_14 False False 128.434833 \n", + "321565 enrich_chr_9_14 False False 128.435300 \n", + "1604331 enrich_chr_16_20 False False 128.435569 \n", + "2063327 enrich_chr_9_14 False False 128.435911 \n", "\n", - " cum_nb_stopreceiving_per_chrom \n", - "4555 1 \n", - "4196 2 \n", - "2810 3 \n", - "4014 4 \n", - "4608 5 \n", - "... ... \n", - "10904 3 \n", - "6312 3 \n", - "9514 3 \n", - "1240 5056 \n", - "9906 3 \n", + " end_analysis timestamp chrom is_rejection is_stopreceiving \\\n", + "1524713 0.000864 0.000000 chr4 False False \n", + "912219 0.001548 0.000683 chr1 False False \n", + "891103 0.004453 0.003588 chr1 True False \n", + "1976384 0.005007 0.004142 chr8 True False \n", + "1557842 0.005459 0.004594 chr5 True False \n", + "... ... ... ... ... ... \n", + "2115686 128.434250 128.433385 chr9 False False \n", + "282185 128.434892 128.434027 chr12 False True \n", + "321565 128.435361 128.434495 chr13 False True \n", + "1604331 128.435622 128.434756 chr5 True False \n", + "2063327 128.435969 128.435104 chr9 False True \n", + "\n", + " cum_nb_rejections_per_chrom cum_nb_stopreceiving_per_chrom \n", + "1524713 0 0 \n", + "912219 0 0 \n", + "891103 1 0 \n", + "1976384 1 0 \n", + "1557842 1 0 \n", + "... ... ... \n", + "2115686 263 133 \n", + "282185 262 131 \n", + "321565 204 106 \n", + "1604331 372 126 \n", + "2063327 263 134 \n", "\n", "[10000 rows x 20 columns]" ] }, - "execution_count": 14, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -979,12 +1157,12 @@ "chunk_df[\"is_stopreceiving\"] = chunk_df[\"decision\"].apply(lambda x: x == \"stop_receiving\")\n", "chunk_df[\"cum_nb_rejections_per_chrom\"] = chunk_df.groupby(\"chrom\", observed=True)[\"is_rejection\"].cumsum()\n", "chunk_df[\"cum_nb_stopreceiving_per_chrom\"] = chunk_df.groupby(\"chrom\", observed=True)[\"is_stopreceiving\"].cumsum()\n", - "chunk_df.head(10000)" + "chunk_df.head(10)" ] }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -993,13 +1171,13 @@ "Text(0.5, 1.0, 'Cumulative number of stopreceiving per chromosome')" ] }, - "execution_count": 21, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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lJXfhyI5QtZNfCgi2c1ff/1lvPTSS0aJEiUMV1dXw8fHx6hXr54xc+ZMm+mv4+LijBdffNHw8/MzfHx8jC5duhjR0dH3nW48eYrnZL169TK8vLxS7L9Ro0ZGhQoVbMqOHz9uNG3a1HBzczMCAwONd955x1i3bl2apn7/9NNPjVKlShlubm5G2bJljQULFlhjutvhw4eNhg0bGh4eHgZgnWr5flPPG4ZhPPfccwZgNG3a9L7n85tvvjHq169veHl5GV5eXkbZsmWNAQMGGEeOHLnvewzj/ucttXiy6lokT//81VdfGSNGjDACAgIMDw8Po02bNtYp7++2Z88e49lnnzXy589vuLm5GcWLFze6dOlibNiw4aExPcjx48eNTp06Gf7+/oa7u7tRq1YtY+XKlSnaYefU7/eb2vz48eNGz549jaCgIMPFxcUoXLiw0bZtW2P58uXWNqktRWAYaTsHhmEYp06dMnr27GkULFjQcHNzM0qWLGkMGDDAiI+Pt7b573//a5QsWdJwdna22de9U78bhmFERUUZffr0MQoUKGC4uroalSpVSrE8wIOO++575++//zYGDBhglC1b1vDy8jL8/PyM2rVrG0uXLn3AWbVIvreOHz9uNG/e3PD09DQCAwON0aNH20zTn+yTTz4xqlevbnh4eBg+Pj5GpUqVjLfffts4f/68tU3x4sWNNm3aPHTfd9u2bZvRrFkzw8fHx/Dy8jIqV65szJw5M0Wc97r3u+Jh98r69euNevXqGR4eHoavr6/Rrl074+DBg6lu81G+E9NyfZcvX240b97cCAgIMFxdXY1ixYoZL7/8snHhwgWbdmn5PCXf38uWLbMpT/4+unfZgvsdY3q/D0VyG5NhZGDfuoiISC63YcMGmjZtys8//2wzjfbjrnfv3ixfvjzVRylFRB5XGrMlIiJih+QxXfdO2y0iInIvjdkSERFJgxs3bvDll18yY8YMihQpopnWRETkodSzJSIikgaXLl3itddew8PDI1Mm6BARkdxHY7ZEREREREQygf4sJyIiIiIikgmUbImIiIiIiGQCTZCRBmazmfPnz+Pj44PJZHJ0OCIiIiIi4iCGYXDt2jWCg4MfOn5XyVYanD9/nqJFizo6DBERERERySbOnDlDkSJFHthGyVYa+Pj4AJYT6uvr6+BoRERERETEUWJjYylatKg1R3gQ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twQnu/9GIiIiIiIZxJ7cwO4xW927d+fcuXOMGzeOwMDATJsgIzIykosXL9K0aVNrmZ+fH7Vr1yYiIoJu3boRERGBv7+/NdECaNq0KU5OTvz6668888wzRERE0LBhQ2uiBdCiRQsmTpzIlStXyJs3b4p9x8fHEx8fb30dGxubKccokqqbV+HH4fDHV5bXHnmh32bIW8KBQYmIiIiIvexOtnbs2EFERARVqlTJjHisLl68CEBgYKBNeWBgoLXu4sWLBAQE2NTnyZOHfPny2bQJCQlJsY3kutSSrfHjxzNmzJiMORCRtLp6Bv73KkRuvVMWXA06L4S8xR0WloiIiIikj93PI5UtW5abN29mRizZxogRI4iJibH+nDlzxtEhSW53fBPMqnkn0cpbArp8Dv02KdESERERyaHs7tmaMGECb775JmPHjqVSpUopxmxl1JimoKAgAKKioihUqJC1PCoqiqpVq1rbREdH27wvMTGRy5cvW98fFBREVFSUTZvk18lt7uXm5oabm1uGHIfIA8VdhmW9IXKL5bWzK3T4GMq1gzy6B0VERERyMrt7tlq2bElERARNmjQhICCAvHnzkjdvXvz9/VN9JC+9QkJCCAoKYsOGDday2NhYfv31V8LDwwEIDw/n6tWr7Nq1y9pm48aNmM1mateubW2zdetWbt++bW2zbt06ypQpk6Hxitjt76OwoNWdRCu0Cby2Gyp1UqIlIiIikgvY3bO1adOmDNv59evXOXbsmPV1ZGQke/fuJV++fBQrVozBgwfzwQcfUKpUKUJCQhg5ciTBwcHWGQvLlStHy5Yteemll5g7dy63b99m4MCBdOvWjeDgYAB69OjBmDFjePHFFxk2bBj79+9nxowZTJs2LcOOQ8Qu16Jg5WA4stryOo8HdPsSwpo4NCwRERERyVh2T/2ekTZv3sxTTz2VorxXr14sXLgQwzAYPXo0n3zyCVevXqV+/frMmTOH0qXvLN56+fJlBg4cyA8//ICTkxMdO3bko48+wtvb29pm3759DBgwgJ07d1KgQAFee+01hg0bluY4NfW7ZIi4y7BrAWybDvGxYHKCUi2g6WgIKOfo6EREREQkDezJDexOtrZu3frA+oYNG9qzuRxByZY8sktHYHEXuHLS8tq/GHReBIWrOTQsEREREbFPpq6z9eSTT6You3utraSkJHs3KZI7XdgH26bChT/g8ok75a0mQ7We4OLuuNhEREREJNPZnWxduXLF5vXt27fZs2cPI0eOZOzYsRkWmEiOZRiwdzGsGQYJ1+6UhzaGlhOgYBnHxSYiIiIiWcbuZMvPzy9FWbNmzXB1deWNN96wmRlQ5LHz11pY+QbEnrW8zhcKDd60JFq+hR78XhERERHJVexOtu4nMDCQI0eOZNTmRHIGw4Crp+HcLji8EvZ/CxjglAfqvQ71h4Cbj6OjFBEREREHsDvZ2rdvn81rwzC4cOECEyZMsC42LJKrXY+GQz/A0XVwdifE/W1bX/JJ6LQAPPM5JDwRERERyR7sTraqVq2KyWTi3kkM69Spw//93/9lWGAi2c7ZXbBpLBzfYFvu5AKBFaBIDSjTyrI48V2TxoiIiIjI48nuZCsyMtLmtZOTEwULFsTdXTOrSS5lToJf5sD6MWC+bSkrUAYKVYFaL0FQZc0sKCIiIiIp2J1sFS9ePDPiEMl+Em7Age/gl7kQ9aelrEhNaD0Fgqs6NDQRERERyf6c0tpw48aNlC9fntjY2BR1MTExVKhQgZ9//jlDgxNxiKtn4Nt+MLEE/G+AJdFy84Un34EX1irREhEREZE0SXPP1vTp03nppZdSXSXZz8+Pl19+malTp9KgQYMMDVAkS8Scgz8WWya+uPDHnXKvAMujgjVeAK8CjotPRERERHKcNCdbf/zxBxMnTrxvffPmzZkyZUqGBCWSZWLOwpaJsPsz2/Li9aDRMCjZyDFxiYiIiEiOl+ZkKyoqChcXl/tvKE8eLl26lCFBiWS6xATLeKzVQyH+/z8aG1AeKnW2/PgXdWx8IiIiIpLjpTnZKly4MPv37ycsLCzV+n379lGoUKEMC0wkQ5iTLAsOn9gM16Pg9k3LOlnn99xZH8s7CFqMhQrPglOahzGKiIiIiDxQmpOt1q1bM3LkSFq2bJlimvebN28yevRo2rZtm+EBitgt5hz8tQZOboPjm+DW1dTbeRaAaj0tjwtq6nYRERERyWAm497Vie8jKiqKatWq4ezszMCBAylTpgwAhw8fZvbs2SQlJbF7924CAwMzNWBHiI2Nxc/Pj5iYmFQnCJFsIOk2nP4FdnwEx9aDYb5T5+4PoU9BgdLg4gEuXpC/JIQ0Auf7PxorIiIiInIve3KDNPdsBQYGsmPHDvr378+IESNIztFMJhMtWrRg9uzZuTLRkmwu7rJl7NXWyXDtwp3ywjUgrAmENYXgauBs95JyIiIiIiKPxK7fQIsXL87q1au5cuUKx44dwzAMSpUqRd68eTMrPpGUEuLg1A44+B38+Q0k3rxTV7olPPUOFKriuPhERERERLAz2UqWN29eatasmdGxiDzYxf3w5zLYuxhuRN8pL1Aayj0NdQeChxJ/EREREcke9GyVZH9nfoN1o+H0jjtl3oEQ0hAqd4XQJppFUERERESyHSVbkv0YhmV69nO/W3qyDqwA/v88LsHVoMIzUOsly2QXIiIiIiLZlJItyV7O/g7rRsGp7bbllTpD45GQt7hj4hIRERERsZOSLXGsG3/DXz/C4VUQfRCunLxTlzfEMh4r/FUo+aSjIhQRERERSRclW5K1EhPgzC9w+YRlweHDq8B827ZN1eeg4VuQL8QxMYqIiIiIZAAlW5I14i7Dvq8t62HF/WNb51sEqnaHIjWhUFXw0XptIiIiIpLzKdmSzHXjH8sYrH1LwJx4p7xEAwgob3k8sEwrMJkcFqKIiIiISGZQsiUZyzAgar+lF+vMTri4D27HWeoCysMTz1smu/Au6Ng4RUREREQymZItyRhJifDHYvjtE7j4p21dQAVoMRZCn3JMbCIiIiIiDqBkS9LPbIZbV+HEJtgyGS4dspQ7u0Hh6pb1sEIaWmYU1KLDIiIiIvKYUbIlaWcYEH0I9n8Dx9ZB9GFIirdtU+EZaDEOfIMdE6OIiIiISDahZEsezpwEpyNgzTDLeKx7Jc8mWLkbFAjL+vhERERERLIhJVuSUmI8RB2Aq6fgr7Vw8Hu4feNOffF6lh6s0MbgVwTyuDkuVhERERGRbErJllhEHbTMIHj2dzi3CxJv2tbn8YBKHS2LDect4ZAQRURERERyEiVbjyPDsCRUJzbBP8fh3G74+4htG2dXywLDAeWgVHMo01qTXIiIiIiI2EHJ1uPEMCyTW2ydcmfmwLuFNIJidSwzCZZ8CvK4Zn2MIiIiIiK5xGOVbM2ePZvJkydz8eJFqlSpwsyZM6lVq5ajw8p8hgGntsOWiRC51VLm7AZlW0PBspA/zDJFu3eAY+MUEREREclFHptk6+uvv+aNN95g7ty51K5dm+nTp9OiRQuOHDlCQEAuTTKunILfP4UjP971mKAJar8CDd4E74IODU9EREREJDczGYZhODqIrFC7dm1q1qzJrFmzADCbzRQtWpTXXnuN4cOHP/C9sbGx+Pn5ERMTg6+vb1aEe1+3k8wkJJpTViQlYIo9jyn2DM4nt5Bn31c4Xb9orTbyuJNUqhUJDYZh5C+VhRGLiIiIiDw6tzxO5HF2/BwC9uQGj0XPVkJCArt27WLEiBHWMicnJ5o2bUpERESK9vHx8cTH31msNzY2NkviTIslO88wckXKta42uL5JqNOFFOWHzEX5IqkZ39+qy7U9nrDnBHAiCyIVEREREck4c56rRutKhRwdhl0ei2Tr77//JikpicDAQJvywMBADh8+nKL9+PHjGTNmTFaFlyEuGPkobPzNOaMA54387DZKszqpFkeMYo4OTURERETksfRYJFv2GjFiBG+88Yb1dWxsLEWLFnVgRHd0r1mUztWLpKxIqA8uXhQ2mSgM1ARezurgREREREQyiUs2eITQXo9FslWgQAGcnZ2JioqyKY+KiiIoKChFezc3N9zc3LIqPLvkcXYij3MqFS5+WR6LiIiIiIjcX85LD9PB1dWV6tWrs2HDBmuZ2Wxmw4YNhIeHOzAyERERERHJrR6Lni2AN954g169elGjRg1q1arF9OnTuXHjBn369HF0aCIiIiIikgs9NslW165duXTpEqNGjeLixYtUrVqVH3/8McWkGSIiIiIiIhnhsVln61Fkp3W2RERERETEcezJDR6LMVsiIiIiIiJZTcmWiIiIiIhIJlCyJSIiIiIikgmUbImIiIiIiGQCJVsiIiIiIiKZ4LGZ+v1RJE/YGBsb6+BIRERERETEkZJzgrRM6q5kKw2uXbsGQNGiRR0ciYiIiIiIZAfXrl3Dz8/vgW20zlYamM1mzp8/j4+PDyaTydHhEBsbS9GiRTlz5ozW/coldE1zJ13X3EnXNffRNc2ddF1zp+xwXQ3D4Nq1awQHB+Pk9OBRWerZSgMnJyeKFCni6DBS8PX11ZdHLqNrmjvpuuZOuq65j65p7qTrmjs5+ro+rEcrmSbIEBERERERyQRKtkRERERERDKBkq0cyM3NjdGjR+Pm5uboUCSD6JrmTrquuZOua+6ja5o76brmTjntumqCDBERERERkUygni0REREREZFMoGRLREREREQkEyjZEhERERERyQRKtkRERERERDKBki0REREREZFMoGRLREREREQkEyjZEhERERERyQRKtkRERERERDKBki0REREREZFMoGRLREREREQkEyjZEhERERERyQRKtkRERERERDKBki0REREREZFMoGRLREREREQkEyjZEhERERERyQRKtkRERERERDKBki0REREREZFMoGRLREREREQkEyjZEhERERERyQRKtkRERERERDKBki0REREREZFMoGRLREREREQkEyjZEhERERERyQRKtkRERERERDKBki0REREREZFMoGRLREREREQkEyjZEhERERERyQRKtkTEIXr37k2JEiUydJsLFy7EZDJx8uTJDN1uTrR582ZMJhPLly93dChpEhUVRadOncifPz8mk4np06c7OqQcy2Qy8d5779n9vvfeew+TyZTxAeUiTz75JBUrVnR0GCKSgyjZEsnBjh8/zssvv0zJkiVxd3fH19eXevXqMWPGDG7evOno8DLNuHHjWLFihaPDkAw0ZMgQ1q5dy4gRI/j8889p2bJlurd18OBB3nvvPSXdIiLicHkcHYCIpM+qVavo3Lkzbm5u9OzZk4oVK5KQkMC2bdt46623OHDgAJ988omjw8wU48aNo1OnTnTo0MGm/Pnnn6dbt264ubk5JjBJt40bN9K+fXuGDh36yNs6ePAgY8aM4cknn8zw3tOc4ObNm+TJY///3v/9738zfPjwTIhIROTxpWRLJAeKjIykW7duFC9enI0bN1KoUCFr3YABAzh27BirVq1yYISO4ezsjLOzs6PDeKzcuHEDLy+vR95OdHQ0/v7+jx5QNnDr1i1cXV1xcnLMwyPu7u7pel+ePHnSlaTlFGazmYSEhHSfn0fl6PtCRBxDn3iRHGjSpElcv36dTz/91CbRShYWFsbrr78OwMmTJzGZTCxcuDBFu3vHdiSP2fjrr7/417/+hZ+fHwULFmTkyJEYhsGZM2do3749vr6+BAUF8eGHH9ps735jppLHD23evPmBxzVlyhTq1q1L/vz58fDwoHr16inGHJlMJm7cuMGiRYswmUyYTCZ69+6d6v7btm1LyZIlU91XeHg4NWrUsCn74osvqF69Oh4eHuTLl49u3bpx5syZB8YMd87bsWPH6N27N/7+/vj5+dGnTx/i4uKs7bLyWiRLSkrinXfeISgoCC8vL55++ulUj+nXX3+lZcuW+Pn54enpSaNGjdi+fXuqx3nw4EF69OhB3rx5qV+//gPPzYkTJ+jcuTP58uXD09OT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iAjCw8Mf2Da5R+Le2dbu/St+RniUfX3zzTe4u7uzdu1am3WyFixYkKKtPT1dXl5etG3blmXLljF16lS+/vprGjRoYPO4U2hoKIZhEBISkqa/9KdXVl6LZMk9LMkMw+DYsWNUrlwZwPp4mK+vL02bNs3QfRcvXpwjR46kKD98+LC1Pj1cXV1p164d7dq1w2w28+qrrzJv3jxGjhxJWFjYfe+P5P0dOXLE5nGohIQEIiMjUxz/+fPnU0xt/9dffwE8dP2utJ7XggUL4uvr+9A/noSGhnL9+vV0X6OuXbuyaNEiNmzYwKFDhzAMI02PEKZFQEAA7u7uHDt2LEVdamX3k5bzHRoayh9//EGTJk0eqcf7XsnXa//+/Xb1xtnr7nsw+dHHZEeOHEn3Z+J++0rr58/Ly4uuXbvStWtXEhISePbZZxk7diwjRoygYMGCeHp63ndbTk5OKZLc9AoNDWX9+vXUq1fPoX/UEclIGrMlkgO9/fbbeHl50bdvX6KiolLUHz9+3DoVtq+vLwUKFEgxpmPOnDkZHlfyLyx37yspKSlNiys7OztjMplsenlOnjzJihUrUrT18vJKkbA8SNeuXTl//jzz58/njz/+SPFL5rPPPouzszNjxoxJ8ZdhwzAy7FHJrLwWyT777DObR06XL1/OhQsXrGNpqlevTmhoKFOmTOH69esp3n/p0qV077t169b89ttvREREWMtu3LjBJ598QokSJdI0luZe914LJycna+KY/Ohp8i/r994jTZs2xdXVlY8++sjmOn/66afExMTQpk0bm/aJiYnMmzfP+johIYF58+ZRsGBBqlev/sA403penZyc6NChAz/88AO///57inbJcXbp0oWIiAjWrl2bos3Vq1dJTEx8YDxNmzYlX758fP3113z99dfUqlUrzY/YPYyzszNNmzZlxYoVnD9/3lp+7Ngx1qxZk+btpOV8d+nShXPnzvHf//43xftv3rzJjRs30nUMzZs3x8fHh/Hjx3Pr1i2buozswatRowYBAQHMnTvX5lHpNWvWcOjQoRT34KNI6+fv3s+Uq6sr5cuXxzAMbt++jbOzM82bN+d///ufzbIeUVFRLF68mPr166f7Mel7denShaSkJP7zn/+kqEtMTLTre18ku1DPlkgOFBoayuLFi+natSvlypWjZ8+eVKxYkYSEBHbs2MGyZcusa0+B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/uOPP2LevHmIj4/XtFlbW9co5OjLuHHjYGpqivXr19f4Oe3bt8drr72GSZMm6bEyIiLSN95GSERERs3FxUXzIZfLIRKJtNqsra2r3Ub47LPPYsqUKZg2bRrs7OygUCjw1Vdfobi4GK+//jpsbGzg5+eH3bt3a71WTEwM+vfvD2traygUCowaNQo3b958aG0qlQq//PILBg4cqNW+du1a+Pv7w9zcHAqFAsOGDdPqHzhwILZu3frkXxwiIjIohi0iImqUvv32Wzg6OuLUqVOYMmUKJk6ciBdffBGdO3fG2bNn0bdvX4waNQolJSUAgPz8fPTs2ROtW7fGmTNnsGfPHmRnZ+Oll1566GtcvHgRBQUFaNeunabtzJkzmDp1Kj788EPEx8djz5496N69u9bzOnTogFOnTqG8vFw/b56IiJ4Khi0iImqUQkND8f7778Pf3x9z5syBubk5HB0dMX78ePj7+2PevHm4desWLl68CABYs2YNWrdujUWLFiEgIACtW7fG119/jUOHDuHq1asPfI3U1FSYmJjA2dlZ05aWlgYrKysMGDAATZs2RevWrTF16lSt57m5uaGiokLrFkkiIjI+DFtERNQotWzZUvO5iYkJHBwcEBISomlTKBQAgJycHAB3Fro4dOiQZg6YtbU1AgICAABJSUkPfI3S0lJIpVKtRTz69OmDpk2bwsfHB6NGjcKmTZs0V8/usrCwAIBq7UREZFwYtoiIqFEyMzPTeiwSibTa7gYktVoNACgqKsLAgQNx/vx5rY+EhIRqtwHe5ejoiJKSElRUVGjabGxscPbsWWzZsgWurq6YN28eQkNDkZ+frxmTl5cHAHBycqqT90pERIbBsEVERFQDbdq0QWxsLLy8vODn56f1YWVl9cDntGrVCgAQFxen1W5qaorevXtj6dKluHjxIlJSUnDw4EFNf0xMDNzd3eHo6Ki390NERPrHsEVERFQDkyZNQl5eHkaMGIHTp08jKSkJe/fuxeuvvw6VSvXA5zg5OaFNmzb4+++/NW07duzA6tWrcf78eaSmpuK7776DWq1G8+bNNWP++usv9O3bV+/viYiI9Ithi4iIqAbc3Nxw7NgxqFQq9O3bFyEhIZg2bRpsbW0hFj/8v9Nx48Zh06ZNmse2trb47bff0LNnTwQGBmL9+vXYsmULWrRoAQAoKyvD77//jvHjx+v9PRERkX5xU2MiIiI9Ki0tRfPmzfHjjz8iLCzssePXrVuHbdu2Yd++fU+hOiIi0ide2SIiItIjCwsLfPfdd4/c/PheZmZm+Oyzz/RcFRERPQ28skVERERERKQHvLJFRERERESkBwxbREREREREesCwRUREREREpAcMW0RERERERHrAsEVERERERKQHDFtERERERER6wLBFRERERESkBwxbREREREREesCwRUREREREpAf/B9+n+8QVY0oiAAAAAElFTkSuQmCC", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA2wAAAOmCAYAAACXHxaDAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd5xU5aH/8c850/vsbN9lYRdYeg0qUiwJKvbevbGL5tqvXkUNGk2MsbcktuSKiTFGzY33F5PYUGMNUREL0haWZXuf3uc8vz9mdthhF9iFhQV83rzmNTPnnDnnOVOW+c7TFCGEQJIkSZIkSZIkSdrrqMNdAEmSJEmSJEmSJKl/MrBJkiRJkiRJkiTtpWRgkyRJkiRJkiRJ2kvJwCZJkiRJkiRJkrSXkoFNkiRJkiRJkiRpLyUDmyRJkiRJkiRJ0l5KBjZJkiRJkiRJkqS9lAxskiRJkiRJkiRJeykZ2CRJkiRJkiRJkvZSMrBJkvSddeGFF1JZWTmk+1y6dCmKorBp06Yh3e++6L333kNRFF555ZXhLsqAtLa2cvrpp5Ofn4+iKDzyyCO77Vg9z8177723246xLYcffjiHH374Hj/u/uTCCy/EbrcPdzEkSfqOkIFNkqRdsmHDBi6//HJGjx6N2WzG6XQyb948Hn30USKRyHAXb7f5+c9/zquvvjrcxZCG0PXXX88bb7zBLbfcwu9//3uOPvro4S7STvv222/5yU9+In84kCRJ2g/oh7sAkiTtu/72t79xxhlnYDKZOP/885kyZQrxeJwPP/yQ//7v/2bVqlU8/fTTw13M3eLnP/85p59+OieffHLO8h/+8IecffbZmEym4SmYtNPeeecdTjrpJG688cbdfqxDDz2USCSC0WjcLfv/9ttvufPOOzn88MP71CK/+eabu+WYkiRJ0u4hA5skSTultraWs88+m1GjRvHOO+9QWlqaXXfllVdSU1PD3/72t2Es4fDQ6XTodLrhLsZ3SigUwmaz7fJ+2tracLvdO/XYZDKJpmkDDmCqqmI2m3fqWLtqd4XEfYUQgmg0isViGZbjD/a9IkmSJJtESpK0U+677z6CwSC//e1vc8Jaj7Fjx3LttdcCsGnTJhRFYenSpX22UxSFn/zkJ9n7P/nJT1AUhXXr1vEf//EfuFwuCgsLWbJkCUII6uvrOemkk3A6nZSUlPDggw/m7G9bfcgG2mfogQceYO7cueTn52OxWJg1a1afPliKohAKhXjuuedQFAVFUbjwwgv7Pf7xxx/P6NGj+z3WnDlzOOCAA3KWPf/888yaNQuLxYLH4+Hss8+mvr5+u2WGLc9bTU0NF154IW63G5fLxUUXXUQ4HM5utydfix6pVIpbb72VkpISbDYbJ554Yr/ntHz5co4++mhcLhdWq5XDDjuMjz76qN/z/Pbbbzn33HPJy8tj/vz5231uNm7cyBlnnIHH48FqtXLwwQfn/JjQ85oJIfjVr36VfU23pec5fOCBB3jkkUcYM2YMJpOJb7/9FoA1a9Zw+umn4/F4MJvNHHDAAfy///f/cvaxrffjQJ4DgMbGRi655BLKysowmUxUVVXxox/9iHg8ztKlSznjjDMA+P73v589n55j9deHra2tjUsuuYTi4mLMZjPTp0/nueee2+Z5P/3009nzPvDAA/n0009ztm1paeGiiy5ixIgRmEwmSktLOemkk3bYRLOnb9jGjRtZuHAhNpuNsrIy7rrrLoQQOdtqmsYjjzzC5MmTMZvNFBcXc/nll9Pd3Z2zXWVlJccffzxvvPEGBxxwABaLhaeeemq75Vi+fDnHHnsseXl52Gw2pk2bxqOPPtpnu8bGRk4++WTsdjuFhYXceOONpFKpfp+z/t4r77zzDocccgg2mw23281JJ53E6tWrc44xFJ/Dgby+AC+++CKzZs3C4XDgdDqZOnVqn/Pe0ecJtry/X3rpJe68807Ky8txOBycfvrp+Hw+YrEY1113HUVFRdjtdi666CJisVif8uzs30NJ2t/IGjZJknbKX//6V0aPHs3cuXN3y/7POussJk6cyC9+8Qv+9re/8bOf/QyPx8NTTz3FD37wA+69917+8Ic/cOONN3LggQdy6KGHDslxH330UU488UTOO+884vE4L774ImeccQavvfYaxx13HAC///3vufTSSznooINYtGgRAGPGjNnmeZx//vl8+umnHHjggdnldXV1/Otf/+L+++/PLrv77rtZsmQJZ555Jpdeeint7e08/vjjHHrooXzxxRcDqv0588wzqaqq4p577mHFihX85je/oaioiHvvvXenn5NdfS3uvvtuFEXh5ptvpq2tjUceeYQjjjiClStXZms53nnnHY455hhmzZrFHXfcgaqqPPvss/zgBz/ggw8+4KCDDsrZ5xlnnEF1dTU///nP+3yR7621tZW5c+cSDoe55ppryM/P57nnnuPEE0/klVde4ZRTTuHQQw/l97//PT/84Q858sgjOf/88wf0vDz77LNEo1EWLVqEyWTC4/GwatUq5s2bR3l5OYsXL8Zms/HSSy9x8skn8+c//5lTTjllm/sb6HPQ1NTEQQcdhNfrZdGiRUyYMIHGxkZeeeUVwuEwhx56KNdccw2PPfYYt956KxMnTgTIXm8tEolw+OGHU1NTw1VXXUVVVRUvv/wyF154IV6vN/vDS48XXniBQCDA5ZdfjqIo3HfffZx66qls3LgRg8EAwGmnncaqVau4+uqrqayspK2tjbfeeovNmzfvcKCfVCrF0UcfzcEHH8x9993H66+/zh133EEymeSuu+7Kbnf55ZezdOlSLrroIq655hpqa2v55S9/yRdffMFHH32ULQvA2rVrOeecc7j88su57LLLGD9+/DaP/9Zbb3H88cdTWlrKtddeS0lJCatXr+a1117LeS5SqRQLFy5k9uzZPPDAA7z99ts8+OCDjBkzhh/96Ec5++zvvfL2229zzDHHMHr0aH7yk58QiUR4/PHHmTdvHitWrOjzPO3s53Cgr+9bb73FOeecw4IFC7J/L1avXs1HH32U3WYgn6fe7rnnHiwWC4sXL6ampobHH38cg8GAqqp0d3fzk5/8hH/9618sXbqUqqoqbr/99uxjh+LvoSTtN4QkSdIg+Xw+AYiTTjppQNvX1tYKQDz77LN91gHijjvuyN6/4447BCAWLVqUXZZMJsWIESOEoijiF7/4RXZ5d3e3sFgs4oILLsgue/bZZwUgamtrc47z7rvvCkC8++672WUXXHCBGDVqVM524XA45348HhdTpkwRP/jBD3KW22y2nONu6/g+n0+YTCZxww035Gx33333CUVRRF1dnRBCiE2bNgmdTifuvvvunO2+/vprodfr+yzfWs/zdvHFF+csP+WUU0R+fn72/p58LXqe8/LycuH3+7PLX3rpJQGIRx99VAghhKZporq6WixcuFBompbdLhwOi6qqKnHkkUf2KdM555yz3eejx3XXXScA8cEHH2SXBQIBUVVVJSorK0Uqlco5/yuvvHKH++x5Dp1Op2hra8tZt2DBAjF16lQRjUazyzRNE3PnzhXV1dXZZVu/HwfzHJx//vlCVVXx6aef9ilbz2NffvnlPu/3Hocddpg47LDDsvcfeeQRAYjnn38+uywej4s5c+YIu92efe16zjs/P190dXVlt/2///s/AYi//vWvQoj0ewEQ999//zafw2254IILBCCuvvrqnHM67rjjhNFoFO3t7UIIIT744AMBiD/84Q85j3/99df7LB81apQAxOuvv77D4yeTSVFVVSVGjRoluru7c9b1fl16ynnXXXflbDNz5kwxa9as7P3tvVdmzJghioqKRGdnZ3bZl19+KVRVFeeff3522a5+Dgf6+l577bXC6XSKZDK5zednoJ+nnvf3lClTRDwez257zjnnCEVRxDHHHJOz3zlz5uT8Ld7Vv4eStL+RTSIlSRo0v98PgMPh2G3HuPTSS7O3dTodBxxwAEIILrnkkuxyt9vN+PHj2bhx45Adt3e/lu7ubnw+H4cccggrVqzYqf05nU6OOeYYXnrppZyaoD/96U8cfPDBjBw5EoD//d//RdM0zjzzTDo6OrKXkpISqqureffddwd0vCuuuCLn/iGHHEJnZ2f2NdsZu/panH/++TnvldNPP53S0lL+/ve/A7By5UrWr1/PueeeS2dnZ/bcQ6EQCxYs4P3330fTtO2e57b8/e9/56CDDsppNmm321m0aBGbNm3KNk3bGaeddhqFhYXZ+11dXbzzzjuceeaZBAKB7Hl0dnaycOFC1q9fT2NjY7/7GuhzoGkar776KieccEKf5rTAdptybsvf//53SkpKOOecc7LLDAYD11xzDcFgkH/+858525911lnk5eVl7x9yyCEA2dfeYrFgNBp57733+jRPHKirrroqe1tRFK666iri8Thvv/02AC+//DIul4sjjzwy5/Mya9Ys7HZ7n89LVVUVCxcu3OFxv/jiC2pra7nuuuv61OD099z293nr7zOw9XulubmZlStXcuGFF+LxeLLLp02bxpFHHpn9bPS2s5/Dgb6+brebUCjEW2+91efYvfc1mM/T+eefn1PTOXv2bIQQXHzxxTnbzZ49m/r6epLJJDB0fw8laX8hm0RKkjRoTqcTgEAgsNuO0RNkerhcLsxmMwUFBX2Wd3Z2DtlxX3vtNX72s5+xcuXKnD4VO/NFuMdZZ53Fq6++yieffMLcuXPZsGEDn3/+ec48X+vXr0cIQXV1db/76P2lZ3u2ft56vlh3d3dnX7fB2tXXYutzUhSFsWPHZvsz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VY3YDEXYk3ZsQR22jgSWeM98ZjG2NGtU08Pjl4/qFdBGoThce/LUpO8wTdN44IEHePrpp6mvr6e4uJjLL7+c8847D4CNGzdy/fXXs3z5cqqrq3nyySeZM2cOAEuXLuW6667jd7/7HYsXL2bdunXU1NRQWVlJKpXivPPO48477+SDDz7A6/UO41luMejAtmLFCh5++GEAXnnlFYqLi/niiy/485//zO233y4DmyRJkiRJ0jAQWopwT5+z9tUEOlYPIJzZsHYL7D6BJW7aarJpE1hsKEWlqD39zMorUctGolhte+7EpD1GCAHxbU9UvtsYDYOqib3lllt45plnePjhh5k/fz7Nzc2sWbMmu/62227jgQceoLq6mttuu41zzjmHmpoa9Jk+kuFwmHvvvZff/OY35OfnU1RUBMBdd91FUVERl1xyCR988MHQnuMuGHRgC4fDOBwOAN58801OPfVUVFXl4IMPpq6ubsgLKEmSJEmSJOXaqXCWsGH1ath99A1nipIe/GPEKNSyUSglI9Jzm7ny9uBZScMuniB2yyN7/LCme64D08DmzgsEAjz66KP88pe/5IILLgBgzJgxzJ8/n02bNgFw4403ctxxxwFw5513MnnyZGpqapgwYQIAiUSCX//610yfPj273w8//JDf/va3rFy5csjOa6gMOrCNHTuWV199lVNOOYU33niD66+/HoC2tjacTueQF1CSJEmSJOm7TGipdLPGjtXZgBbsWEsqGemzbTqcFWKP27B1a9j8CpZEP+GspDzdlHFEJWp5JUpZBYrJvAfPSpJ2zurVq4nFYixYsGCb20ybNi17u7S0FEhnlZ7AZjQac7YJBAL88Ic/5JlnnqGgoGA3lXznDTqw3X777Zx77rlcf/31LFiwINse9M0332TmzJlDXkBJkiRJkqTvip55zvytXw0onNkNhdhiNmzebYQzVUUpLUcdUZkdDEQpq0AxmvbgWUn7DKMhXds1DMcdKIvFssNtDIYt++tpaqlpWs4+ejfB3LBhA5s2beKEE07ILuvZXq/Xs3btWsaMGTPgMg61QQe2008/PdtWtHc14oIFCzjllFOGtHCSJEmSJEn7M6GlCHauo7vpM3zNX+Br/ZpEpKvPdqrOhN1Uii1px+YV6dEa+4QzHUpZTzirTF+XjkAxDKypmSQpijLgponDpbq6GovFwrJly7j0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j07QU+lZSK33PObnrmB+7krM+rPj9ykrCp2hCAd9gaxAa/IHSU8jznJMRuo8ThbnuFnidbPA48JhPP3pgFJ4mHR/E+n+RqThNtLDbUhDbSip6SN+orMgk85Yi76wDn1eNTpvuZbKqKGBerMjlYqSSkVIpiIkkyESySDJZJBEMkQiESCRDJJKhkkkQ6RSYeIJf3YslZ6cBm0yFlI378tEogaS6dk7uWaFkigiCiKCoENAUIVWVkiNi6qjjR/5mAmyLPPAAw/wwx/+kK6uLgoKCviHf/gHbr31VgC6uvq4885/YevWrdTW1vLoo4+ydu1aAH7605/yhS98gZ///OfcfffdNDU10dLSwqWXXjrpHJ///Of52c9+xhtvvHHuCbbvf//7tLa2UlBQQGVl5RTTkZ07d87Z5IqKili4cOGksQULFvC73/0OgMLCQgAGBgYoKhq3jh4YGGD58uXZfQYHJ38hp9NpRkdHs68vLCxkYGBg0j5jz4+3z9j26bjnnnsmpVEGg0HKysqO/aY1NDTOKqaLqOkUHcU+L0X+XHRWtyrU1l52TpiJpFMxets309H8NMP927PjBpOLsnmbKK+9Dk/uwimvSyVjtO54ikPtz9GqHCI1UaSl7dRZV1K/+H2U1ayf+RduOkm0by/B1lcItr1B0t81abvBno+9Yi2OijU4KtaiM88+ihZO+Dkw+DYHB9+mYXjblBRHvWigOmcZ83PV+rOanGWY9GfH73E4FuegL8CBCdGzcGpqio/baGBhjpuFOS4WelzUuBzkWUyn1URDkdOqTX5/oyrQBppIDTShREanf4GoR+cpQV8wH0PxQlWcFdYhWt2nbc4aGmcCRVHGhVQiSCIZJJEYE11jyzDJZHCCIAuroisVztRknRwGvRWD0Y7NUoqoM2AwWDAZTRnBpMuIr4wAmySmjnx+5ox67rnnHn70ox/xve99j/Xr19PX15f1nQC49957eeCBB6itreXee+/llltuoam5CVGnR5JlotEo999/P4899hher3dKNp6iKLz00ks0NjZy//33n+63N4VZC7YbbrjhFExjetatW0djY+OksaamJioq1H48VVVVFBYWsnnz5qxACwaDbN26lU9/+tMArF27Fr/fz44dO1i5ciUAL730ErIss3r16uw+9957L6lUKitAX3jhBerq6rKOlGvXrmXz5s184Qtf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4z5zDhsREREREdE1igkbERERERHRNYoJGxERERER0TWKCRsREREREdE1igkbERERERHRNYoJGxERERER0TWKCRsREREREdE1igkbERERERHRNYoJGxERERER0TWKCRsREREREf3j5ebmQpIkZGRkNHYobsWEjYiIiIiIrmuZmZm4//770aZNG0iShEWLFlWrY7VakZiYiNDQUOh0OoSFheGll16CEKJeY1PV69mJiIiIiIiuUVarFZIkoby8HG3btsXw4cPx9NNP11g3JSUFb775JtasWYPo6Gj88ssvGDduHHx9fTF16tR6i5E9bERERERE9I9hs9mQmpqK8PBwaDQatGrVCnPnzpWPHz16FP369YNer0dMTAx27dolH1u9ejX8/PywceNGREVFQaPR4Pjx47j55pvx6quvYuTIkdBoNDW2u3PnTgwdOhSDBw9GmzZt8MADD2DgwIH4+eef6/V62cNGREREREQQQsBgtTZ4u1qlEpIkOV0/Pj4eK1euxMKFCxEbG4tTp07h4MGD8vGEhASkpaUhIiICCQkJiIuLQ3Z2NlQqe+pTXl6OlJQUrFq1Ck2bNkVAQIBT7fbu3RtvvfUW/vzzT9x444349ddfsWPHDixYsMC1C3YREzYiIiIiIoLBasVt/+/zBm/3u6H3QadyLi0pKSnB4sWLsWTJEowdOxYAEBYWhtjYWOTm5gIApk+fjsGDBwMAkpOTER0djezsbERGRgIAzGYzli1bhpiYGJfinDVrFoqLixEZGQmlUgmr1Yq5c+di9OjRLp3HVRwSSURERERE/whZWVkwGo3o379/rXU6deokPw8ODgYAFBQUyGVqtdqhjrPWrVuHDz/8EGvXrsW+ffuwZs0apKWlYc2aNS6fyxXsYSMiIiIiImiVSnw39L5GaddZOp3usnU8PDzk55VDLW02m8M5XBmCWem5557DrFmzMHLkSABAx44dcezYMcybN0/u7asP7GEjon+0Rx55BG3atHHrOVevXg1JkuShFdez7777DpIk4ZNPPmnsUJxy+vRpPPDAA2jatGmtyzKTcyRJwosvvujy+1588cUr+ofQ9eS2225Dhw4dGjsMomokSYJOpWrwhyt/MyIiIqDT6ZCenl6Pd6Jm5eXlUCgc0yelUumQDNYH9rAREY4cOYLU1FRs27YNeXl5UKvV6NixIx588EFMmjTJqf+b9U/0yiuvICoqCsOGDWvsUMhNnn76aXz55Zd44YUXEBQUhG7dul3xuQ4cOIB169bVy/8UICKiK6PVajFz5kzMmDEDarUaffr0wZkzZ5CZmVnnMMnLMZlMOHDggPz85MmTyMjIgJeXF8LDwwEAQ4YMwdy5c9GqVStER0dj//79WLBgAR599FG3XFttmLARXee++OILDB8+HBqNBmPGjEGHDh1gMpmwY8cOPPfcc8jMzMRbb73V2GHWi1deeQUPPPBAtYTt4YcfrnNZX7p2ffPNNxg6dCimT59+1ec6cOAAkpOTcdttt12XCVtFRYW8oporZs+ejVmzZtVDREREdomJiVCpVEhKSkJeXh6Cg4MxefLkqzpnXl4eunTpIr9OS0tDWloa+vbti++++w4A8MYbbyAxMRFPPPEECgoKEBISgsceewxJSUlX1fblMGEjuo7l5ORg5MiRaN26Nb755ht5Yi4APPnkk8jOzsYXX3zRiBE2DqVSCaUL4+np6pWVlcHT0/Oqz1NQUAA/P7+rD+gaYDAYoFarqw2/aSharfaK3qdSqa4o0funsNlsMJlMV3x/rlZjfy+IrgUKhQIJCQlISEiodkwI4fDaz8/PoeyRRx7BI488Uu19bdq0qfbeS3l7e2PRokUNPtyev+1E17HU1FSUlpbi7bffdkjWKoWHh+Opp54CAOTm5kKSJKxevbpavUvnulTOYfnzzz/x0EMPwdfXF82bN0diYiKEEDhx4gSGDh0KHx8fBAUF4bXXXnM4X21zyCrnU1X+n67apKWloXfv3mjatCl0Oh26du1abQ6WJEkoKyvDmjVrIEkSJEmS/4Bf2v4999yDtm3b1thWr169qg27++CDD9C1a1fodDo0adIEI0eOxIkTJ+qMGfj7vmVnZ+ORRx6Bn58ffH19MW7cOJSXl8v1GvKzqGS1WvH8888jKCgInp6euPfee2u8pt27d+POO++Er68v9Ho9+vbtix9//LHG6zxw4ABGjRoFf39/xMbG1nlvjh49iuHDh6NJkybQ6/Xo2bOnw/9MqPzMhBBYunSp/JnW5aOPPkLXrl3h7e0NHx8fdOzYEYsXL5bPN3z4cABAv3795PNV/e4tW7YM0dHR0Gg0CAkJwZNPPonCwkKHNirnKu3duxe9e/eGTqdDaGgoli9f7lCv8rv90UcfYfbs2WjRogX0ej2Ki4udvq8AcPLkSYwfPx4hISHQaDQIDQ3F448/DpPJJNcpLCzEtGnT0LJlS2g0GoSHhyMlJaXaHIyq36VPPvkEkiTh+++/r9bmihUrIEkS/vjjDwA1z2GTJAlTpkzBhg0b0KFDB2g0GkRHR2Pr1q3Vzvfdd9+hW7du0Gq1CAsLw4oVK5yeF+fs/QYAo9GIF154Qd54t2XLlpgxYwaMRmONsX/44Yfy511T3FVt2bIFffv2lb9bN998M9auXVut3oEDB+TNfVu0aIHU1NRq96Ku78X69evlvzXNmjXDQw89hJMnTzqc45FHHoGXlxeOHz+Oe+65B15eXmjRogWWLl0KAPj9999x++23w9PTE61bt64xzsv9/lV64403EB0dDb1eD39/f3Tr1q3a+fbv34+77roLPj4+8PLyQv/+/fHTTz851Kn8fd6xYwemTp2K5s2bw8/PD4899hhMJhMKCwsxZswY+Pv7w9/fHzNmzKj2D22bzYZFixYhOjoaWq0WgYGBeOyxx3DhwoXaPjaia5cgoutWixYtRNu2bZ2qm5OTIwCId999t9oxAOKFF16QX7/wwgsCgOjcubOIi4sTy5YtE4MHDxYAxIIFC0S7du3E448/LpYtWyb69OkjAIjvv/9efv+7774rAIicnByHdr799lsBQHz77bdy2dixY0Xr1q0d6t1www3iiSeeEEuWLBELFiwQ3bt3FwDEpk2b5Drvv/++0Gg04pZbbhHvv/++eP/998XOnTtrbP+9994TAMTPP//s0E5ubq4AIF599VW57OWXXxaSJIkRI0aIZcuWieTkZNGsWTPRpk0bceHChTrvceV969Kli/jPf/4jli1bJiZMmCAAiBkzZsj1GvKzqLznHTt2FJ06dRILFiwQs2bNElqtVtx4442ivLxcrpueni7UarXo1auXeO2118TChQtFp06dhFqtFrt3764WU1RUlBg6dKhYtmyZWLp0aa33JT8/XwQGBgpvb2+RkJAgFixYIGJiYoRCoRCfffaZEEKII0eOiPfff18AEHfccYf8mdbmq6++EgBE//79xdKlS8XSpUvFlClTxPDhw+XzTZ06VQAQzz//vHy+/Px8h2sYMGCAeOONN8SUKVOEUqkUN998szCZTHI7ffv2FSEhISIgIEBMmTJFvP766yI2NlYAEG+//Xa1+xwVFSU6d+4sFixYIObN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O5zn/PnzGD16NHx8fODn54fx48ejtLTUoc5vv/2GW265BVqtFi1btkRqamq1eNavX4/IyEhotVp07NgRmzdvduv1EhERERERVeXyKpE5OTlo2rRpfcRSTUpKCt58800sWbIEWVlZSElJQWpqKt544w25TmpqKl5//XUsX74cu3fvhqenJwYNGuTQAzh69GhkZmZi27Zt2LRpE7Zv345JkybJx4uLizFw4EC0bt0ae/fuxauvvooXX3wRb731llxn586diIuLw/jx47F//34MGzYMw4YNwx9//NEg94KIiIiIiK4/V7Rx9vfff4+0tDRkZWUBAKKiovDcc8/hlltucWtw99xzDwIDA/H222/LZffffz90Oh0++OADCCEQEhKCZ599FtOnTwcAFBUVITAwEKtXr8bIkSORlZWFqKgo7NmzB926dQMAbN26FXfffTf++usvhISE4M0330RCQgLy8/OhVqsBALNmzcKGDRtw8OBBAMCIESNQVlaGTZs2ybH07NkTnTt3xvLly526Hm6cTURERETXAm6c3TAaZePsDz74AAMGDIBer8fUqVPlBUj69++PtWvXun4VdejduzfS09Px559/AgB+/fVX7NixA3fddRcAe29ffn4+BgwYIL/H19cXPXr0wK5duwAAu3btgp+fn5ysAcCAAQOgUCiwe/duuc6tt94qJ2sAMGjQIBw6dAgXLlyQ61Rtp7JOZTs1MRqNKC4udngQEREREZH75ebmQpIkZGRkNHYobuXUoiNVzZ07F6mpqXj66aflsqlTp2LBggV46aWXMGrUKLcFN2vWLBQXFyMyMhJKpRJWqxVz587F6NGjAQD5+fkA7HPsqgoMDJSP5efnIyAgwOG4SqVCkyZNHOqEhoZWO0flMX9/f+Tn59fZTk3mzZuH5ORkVy+biIiIiIga0OrVqzFu3DiHMo1Gc00stOhyD9vRo0cxZMiQauX33nsvcnJy3BJUpXXr1uHDDz/E2rVrsW/fPqxZswZpaWlYs2aNW9upL/Hx8SgqKpIfJ06caOyQiIiIiIjoIqvVCpvNBgDw8fHBqVOn5MexY8caOTo7lxO2li1bIj09vVr5119/jZYtW7olqErPPfccZs2ahZEjR6Jjx454+OGH8fTTT2PevHkAgKCgIADA6dOnHd53+vRp+VhQUBAKCgocjlssFpw/f96hTk3nqNpG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j0dpaalDnd9++w233HILtFotWrZsidTU1GqxrF+/HpGRkdBqtejYsSM2b95cPxdNRERERESEKxgSOX36dAwePBhhYWGIioqCh4eHw/HPPvvMbcFduHABffr0Qb9+/bBlyxY0b94chw8fhr+/v1wnNTUVr7/+OtasWYPQ0FAkJiZi0KBBOHDggLzXwejRo3Hq1Cls27YNZrMZ48aNw6RJk7B27VoA9j0QBg4ciAEDBmD58uX4/fff8eijj8LPzw+TJk0CAOzcuRNxcXGYN28e7rnnHqxduxbDhg3Dvn370KFDB7ddMxERERERUSWnNs6uasqUKVi1ahX69etXbdERAHj33XfdFtysWbPw448/4ocffqjxuBACISEhePbZZzF9+nQAQFFREQIDA7F69WqMHDkSWVlZiIqKwp49e9CtWzcAwNatW3H33Xfjr7/+QkhICN58800kJCQgPz8farVabnvDhg04ePAgAGDEiBEoKyvDpk2b5PZ79uyJzp07Y/ny5U5dDzfOJiIiIqJrATfObhju2Djb5SGRa9aswaeffootW7Zg9erVePfddx0e7rRx40Z069YNw4cPR0BAALp06YKVK1fKx3NycpCfn48BAwbIZb6+vujRowd27doFANi1axf8/PzkZA0ABgwYAIVCgd27d8t1br31VjlZA4BBgwbh0KFDuHDhglynajuVdSrbISIiIiKixpObmwtJkpCRkdHYobiVywlbkyZNEBYWVh+xVHP06FG8+eabiIiIwJdffonHH38cU6dOxZo1awAA+fn5AIDAwECH9wUGBsrH8vPzERAQ4HBcpVKhSZMmDnVqOkfVNmqrU3m8JkajEcXFxQ4PIiIiIiK6tnz22Wfo1q0b/Pz84Onpic6dO+P9999v7LAAXEHC9uKLL+KFF15AeXl5fcTjwGaz4aabbsIrr7yCLl26YNKkSZg4caLTQxAb27x58+Dr6ys/WrZs2dghERERERHRRVarFTabDU2aNEFCQgJ27dqF3377DePGjcO4cePw5ZdfNnaIridsr7/+OrZs2YLAwEB07NgRN910k8PDnYKDgxEVFeVQ1r59exw/fhwAEBQUBAA4ffq0Q53Tp0/Lx4KCglBQUOBw3GKx4Pz58w51ajpH1TZqq1N5vCbx8fEoKiqSHydOnLj8RRMRERERUa1sNhtSU1MRHh4OjUaDVq1a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wQLceuutCAsLQ3p6Oj777LNrYn0OJmxERERERASLxYD3PurT4O2OGfkjPDx0TtXNysqC0WhE//79a63TqVMn+XlwcDAAoKCgQE7Y1Gq1Qx0AWLx4MSZOnIjIyEhIkoSwsDCMGzcO77zzjquX43YcEklERERERP8IOt3lE7uq245V9txVXThRp9NV69Fr3rw5NmzYgLKyMhw7dgwHDx6El5cX2rZt66bIr9wV9bAdOXIE7777Lo4cOYLFixcjICAAW7ZsQatWrRAdHe3uGImIiIiIqJ6pVFqMGfljo7TrrIiICOh0OqSnp2PChAluj0Wr1aJFixYwm8349NNP8eCDD7q9DVe5nLB9//33uOuuu9CnTx9s374dc+fORUBAAH799Ve8/fbb+OSTT+ojTiIiIiIiqkeSJDk9NLGxaLVazJw5EzNmzIBarUafPn1w5swZZGZm1jlM8nJ2796NkydPonPnzjh58iRefPFF2Gw2zJgxw43RXxmXh0TOmjULL7/8MrZt2wa1Wi2X33777fjpp5/cGhwREREREVFViYmJePbZZ5GUlIT27dtjxIgRKCgouKpzGgwGzJ49G1FRUbjvvvvQokUL7NixA35+fu4J+ipIQgjhyhu8vLzw+++/IzQ0FN7e3vj111/Rtm1b5ObmIjIyEgZDw68s809RXFwMX19fFBUVwcfHp7HDISIiIqLrlMFgQE5ODkJDQ6stZ0/uU9d9djY3cLmHzc/PD6dOnapWvn//frRo0cLV0xEREREREVEtXE7YRo4ciZkzZyI/Px+SJMFms+HHH3/E9OnTMWbMmPqIkYiIiIiI6LrkcsL2yiuvIDIyEi1btkRpaSmioqJw6623onfv3pg9e3Z9xEhERERERHRdcnmVSLVajZUrVyIxMRF//PEHSktL0aVLF0RERNRHfERERERERNctlxO2HTt2IDY2Fq1atUKrVq3qIyYiIiIiIiLCFQyJvP322xEaGornn38eBw4cqI+YiIiIiIiICFeQsOXl5eHZZ5/F999/jw4dOqBz58549dVX8ddff9VHfERERERERNctlxO2Zs2aYcqUKfjxxx9x5MgRDB8+HGvWrEGbNm1w++2310eMsvnz50OSJEybNk0uMxgMePLJJ9G0aVN4eXnh/vvvx+nTpx3ed/z4cQwePBh6vR4BAQF47rnnYLFYHOp89913uOmm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li9ffsBrhmHIhg0buOGGG/I8KSWXXHJJjyA8EhjBZjAYDAaDwWAwGPCTmDf/y//vqF/327/8/1C0nUXVrdVqfOpTn+Izn/kMV1xxBQCnnHIKF110EVu2bAHgD//wD7nssssAuPXWWzn77LPZtGkTZ555JgBRFHHPPfewfv36Rfdx7969JEnC2NhYT/7Y2FiPWDwSGJfI45S77747X9n+ggsu4Ic//OGx7pLhAHznO9/hl37pl1i1ahVCCP7pn/6pp1xrzc0338zKlSspFotccsklPPvssz11Jicnufzyy+nv72dwcJB3v/vd1Ov1njqPP/44b3zjGykUCqxZs4aPfexjc/ryD//wD5x55pkUCgVe9apX8bWvfe2w36+hw2233cZ5551HX18fy5cv51d+5Vd45plneur4vs9VV13FyMgIlUqFX//1X2f37t09dbZt28Zll11GqVRi+fLl/NEf/RFx3PtL6oMPPshrXvMaPM/j1FNP5b777pvTH/P9cfT5y7/8S84555x8oeMLL7yQf/3Xf83Lzfs/frj99tsRQvQEQzDv/+XNn/zJnyCE6NnawgMO3/uv1WpHXHgcDp566imCIODiiy9esM4555yTp1euXAnQM8/Mdd2eOi96tOG44/7779eu6+rPf/7zeuPGjfo973mPHhwc1Lt37z7WXTPsh6997Wv6xhtv1P/4j/+oAf3lL3+5p/z222/XAwMD+p/+6Z/0j3/8Y/3Lv/zLet26dbrVauV1fv7nf16vX79ef//739f/9//+X33qqafqt7/97Xn5zMyMHhsb05dffrl+4okn9N/93d/pYrGo/+qv/iqv873vfU9blqU/9rGP6SeffFJ/+MMf1o7j6J/85CdH/Bkcr1x66aX6C1/4gn7iiSf0Y489pn/hF35Bn3jiibper+d1rrzySr1mzRr9wAMP6Icffli//vWv1z/zMz+Tl8dxrF/5ylfqSy65RD/66KP6a1/7mh4dHdU33HBDXuf555/XpVJJX3vttfrJJ5/Un/70p7VlWfrrX/96Xsd8fxwb/uVf/kV/9atf1T/96U/1M888o//4j/9YO46jn3jiCa21ef/HCz/84Q/1SSedpM855xz9/ve/P8837//lzS233KLPPvtsvWvXrnybmJjIyw/2/X/0ox/VTz75pG61Wtr3fb1hwwa9detWva9a1Vt37tDf/cEP9K59e3UzCnUzCvWOPbv19374Q719fJfeV63qpzZt0g89/CM902zmdQ52U0ot+nk8/vjjGtDPP//8nLLNmzdrQD/66KN53tTUlAb0t771La211l/4whf0wMDAfq+xdu1afdddd/XkBUGgLcuaM/76nd/5Hf3Lv/zLC7bVarXy53ywGMF2HHL++efrq666Kj9OkkSvWrVK33bbbcewV4alMFuwKaX0ihUr9Mc//vE8b3p6Wnuep//u7/5Oa631k08+qQH9ox/9KK/zr//6r1oIoXfs2KG11vqee+7RQ0NDOgiCvM51112nzzjjjPz4v/7X/6ovu+yynv5ccMEF+vd///cP6z0aFmbPnj0a0N/+9re11um7dhxH/8M//ENe56mnntKAfuihh7TWqeCXUurx8fG8zl/+5V/q/v7+/H1/6EMf0meffXbPtd72trfpSy+9ND823x8vHoaGhvTnPvc58/6PE2q1mj7ttNP0N77xDf3mN785F2zm/b/8ueWWW/T69evnLTuU93/22WfrjRs36larpbdv357/ANRm06ZN+plnnsmPn3zySb1169b8WCmlH3vsMb1z587DcZuLptVq6WKxqD/72c/OKTuSgk3r9G/g6quvzo+TJNGrV6/e79/A4RBsxiXyOKM9YfKSSy7J847WhEnDkWPz5s2Mj4/3vNeBgQEuuOCC/L0+9NBDDA4O8rrXvS6vc8kllyCl5Ac/+EFe501velPPYpqXXnopzzzzDFNTU3md7uu065jPz9FjZmYGgOHhYQA2bNhAFEU97+XMM8/kxBNP7Hn/r3rVq3p87y+99FKq1SobN27M6+zv3ZrvjxcHSZJw//3302g0uPDCC837P0646qqruOyyy+a8I/P+jw+effZZVq1axcknn8zll1/Otm3bgEN7//V6nSiKAKjX6/T19fVcc2BggEajAaRRGRuNRk8dIQT9/f15naNFoVDguuuu40Mf+hBf/OIXee655/j+97/P//gf/+OQ2g3DkMcee4zHHnuMMAzZsWMHjz32GJs2bcrrXHvttXz2s5/lf/7P/8lTTz3Fe9/7XhqNBu9617sO9bb2iwk6cpxxLCdMGo4c4+PjAPO+13bZ+Pj4nChItm0zPDzcU2fdunVz2miXDQ0NMT4+vt/rGI4sSik+8IEP8IY3vIFXvvKVQPpuXNdlcHCwp+7s9z/fe2uX7a9OtVql1WoxNTVlvj+OIT/5yU+48MIL8X2fSqXCl7/8Zc466ywee+wx8/5f5tx///088sgj/OhHP5pTZv7+X/5ccMEF3HfffZxxxhns2rWLW2+9lTe+8Y088cQTh/z+kyQB0iAcjtMb9MNxnHyNsvZ8t/nq+L5/2O51sdx0003Yts3NN9/Mzp07WblyJVdeeeUhtblz505e/epX58d33nknd955J29+85t58MEHAXjb297GxMQEN998M+Pj45x77rl8/etfn/N8DzdGsBkMBsNLiKuuuoonnniC7373u8e6K4ajzBlnnMFjjz3GzMwM//t//2+uuOIKvv3tbx/rbhmOMNu3b+f9738/3/jGNygUCse6O4ZjwFvf+tY8fc4553DBBRewdu1avvSlL1EsFo9hz44dUkpuvPFGbrzxxjllWuue48HBwZ68d77znbzzne+cc95JJ50059z5uPrqq7n66quX3ulDwLhEHmeMjo5iWdac6EG7d+9ecFV3w4uf9rvb33tdsWJFT4QkgDiOmZyc7KkzXxvd11iojvn8HHmuvvpqvvKVr/Ctb32LE044Ic9fsWIFYRgyPT3dU3/2+z/Yd9vf30+xWDTfH8cY13U59dRTee1rX8ttt93G+vXr+dSnPmXe/8ucDRs2sGfPHl7zmtdg2za2bfPtb3+bv/iLv8C2bcbGxsz7P84YHBzk9NNPZ9OmTYf8929ZFpBaytrukW2iKMKyLKSU+fpl89WZbXUzHH6MYDvOcF2X1772tTzwwAN5nlKKBx54gAsvvPAY9sxwKKxbt44VK1b0vNdqtcoPfvCD/L1eeOGFTE9Ps2HDhrzON7/5TZRSXHDBBXmd73znOz1fyN/4xjc444wzGBoayut0X6ddx3x+jhxaa66++mq+/OUv881vfnOO2+prX/taHMfpeS/PPPMM27Zt63n/P/nJT3pE+ze+8Q36+/s566yz8jr7e7fm++PFhVKKIAjM+3+Zc/HFF/OTn/wkn1vz2GOP8brXvY7LL788T5v3f3xRr9d57rnnWLly5SH9/VcqlVxsVSoVarVaz3Wq1SrlchlILVrlcrmnjta6p47hCHLQ4UoML1nuv/9+7Xmevu+++/STTz6p/9t/+296cHCwJ3qQ4cVHrVbTjz76qH700Uc1oD/xiU/oRx99NI/YdPvtt+vBwUH9z//8z/rxxx/X/+W//Jd5w/q/+tWv1j/4wQ/0d7/7XX3aaaf1hPWfnp7WY2Nj+h3veId+4okn9P33369LpdKcsP62bes777xTP/XUU/qWW24xYf2PMO9973v1wMCAfvDBB3vCOjebzbzOlVdeqU888UT9zW9+Uz/88MP6wgsv1BdeeGFe3g7r/Ja3vEU/9thj+utf/7petmzZvGG9/+iP/kg/9dRT+u677543rLf5/jj6XH/99frb3/623rx5s3788cf19ddfr4UQ+t///d+11ub9H290R4nU2rz/lzsf/OAH9YMPPqg3b96sv/e97+lLLrlEj46O6j179mitD/79zxfWf/v27brZbOrdu3frH/3oR3p6ejpvZ9++ffrhhx/WExMTutls6i1btuhHHnlEh2F41J/JSwkT1t9w0Hz605/WJ554onZdV59//vn6+9///rHukuEAfOtb39LAnO2KK67QWqfhdW+66SY9NjamPc/TF198cU84Xq3TL9u3v/3tulKp6P7+fv2ud71L12q1njo//vGP9UUXXaQ9z9OrV6/Wt99++5y+fOlLX9Knn366dl1Xn3322fqrX/3qEbtvg573vQP6C1/4Ql6n1WrpP/iDP9BDQ0O6VCrpX/3VX9W7du3qaWfLli36rW99qy4Wi3p0dFR/8IMf1FEU9dT51re+pc8991ztuq4++eSTe67Rxnx/HH1+93d/V69du1a7rquXLVumL7744lysaW3e//HGbMFm3v/Lm7e97W165cqV2nVdvXr1av22t71Nb9q0KS8/2Pdfq9V6hES1WtUbN27UDz/8sH788cd71nprs3v3bv3jH/9YP/zww/rJJ5+cM4YwzOVwCDah9SJm1xkMBoPBYDAYDIaXDb7vs3nzZtatW2cC2hxBDsdzNnPYDAaDwWAwGAwGg+FFihFsBoPBYDAYDAaDwfAixQg2g8FgMBgMBoPBYHiRYgSbwWAwGAwGg8FgMLxIMYLNYDAYDP9fe/cfFVd953/8dWWYH4Q0JChhUkUIEBEaJtjENoImSMrqUlf7jbGhaomW7qbaumkXoSyCRosFMjZBOeAWNGhrmlprLMfTGg2Gtm6piWanNoREKYxsQ1LStVHWkR+Zme8f2UzF/IKEH0PyfJwz51zu5z2fz/uOf728n3sDAMCU53a7ZRiGXC7XZLcypghsAAAAAM5rbW1tWr58uWJjY2UYhjZs2HBcjdfrVWlpqeLi4mSz2RQfH6+HHnpI4/3SfdO4zg4AAAAAQcrr9cowDHk8Hs2dO1crVqzQt7/97RPWVlZWqq6uTk899ZRSUlL0xhtv6I477tCMGTN0zz33jFuP3GEDAAAAMGX4fD5VVVUpISFBFotFMTExKi8vD4x3dnYqMzNTYWFhcjgcam1tDYw1NjYqIiJCTU1NSk5OlsViUXd3txYtWqR169Zp5cqVslgsJ1z3d7/7nW688Ubl5OQoNjZWN998s7Kzs7Vjx45xvV4CGwAAI7Bq1SrddNNNk7b+7bffrocffnhEtStXrtQjjzwyzh0BONf4/X59dOTIhH9Gu6WwuLhYFRUVKi0t1Z49e7Rp0ybNnj07MF5SUqKCggK5XC7NmzdPubm5OnLkSGDc4/GosrJSDQ0NamtrU1RU1IjWveqqq9Tc3Ky3335bkvSHP/xBr732mq6//vpR9T9abIkEAJz3DMM45fj999+v6urqcX9O4WT+8Ic/6Je//KXq6upGVH/ffffpmmuuUX5+vmbMmDHO3QE4V/R7vVr6iy0Tvm7LjV+SzTSyWNLX16fq6mrV1NQoLy9PkhQfH6+MjAy53W5JUkFBgXJyciRJa9euVUpKijo6OpSUlCRJGhoaUm1trRwOx6j6/O53v6sPPvhASUlJCgkJkdfrVXl5uW699dZRzTNaBDYAwHnvwIEDgeOf/vSnKisr0759+wLnwsPDFR4ePhmtSZIee+wxrVixYsQ9fOYzn1F8fLx+/OMf6+677x7n7gBg4rS3t2tgYEBZWVknrUlNTQ0c2+12SVJvb28gsJnN5mE1I/Xss8/qmWee0aZNm5SSkiKXy6U1a9Zozpw5gfA4HghsAIDzXnR0dOB4xowZMgxj2Dnp6JbIw4cP64UXXpAkLV26VPPnz1dISIieeuopmc1mfe9739NXvvIVffOb39Rzzz2n2bNn67HHHhu2XWb37t2699579dvf/lbTpk1Tdna21q9frwsvvPCEvXm9Xj333HN65plnhp2vra3V+vXr9d///d+aMWOGrr76aj333HOB8RtuuEGbN28msAEYMWtIiFpu/NKkrDtSNpvttDWhoaGB42M7KHw+37A5Trez4kTuvfdeffe739XKlSslSfPnz9e7776r73//++Ma2HiGDQCAM/TUU0/pwgsv1I4dO/Stb31L3/jGN7RixQpdddVV2rVrl7Kzs3X77bfL4/FIkg4fPqxrr71WaWlpeuONN/TSSy/pL3/5i2655ZaTrvHWW2/p/fff18KFCwPn3njjDd1zzz168MEHtW/fPr300ku65pprhn3vyiuv1I4dOzQwMDA+Fw/gnGMYhmwm04R/RhOeEhMTZbPZ1NzcPI6/xIl5PB5dcMHw+BQSEjIsDI4H7rABAHCGHA6H7rvvPkl/fwj+wgsv1Ne//nVJUllZmerq6vTWW2/p85//vGpqapSWljbs5SFPPvmkLrnkEr399tuaN2/ecWu8++67CgkJGfZQfHd3t6ZNm6YvfvGLmj59ui699FKlpaUN+96cOXM0ODiogwcP6tJLLx2PyweACWe1WlVUVKTCwkKZzWalp6fr0KFDamtrO+U2ydMZHBzUnj17Asf79++Xy+VSeHi4EhISJB3duVBeXq6YmBilpKTov/7rv/SDH/xAd95555hc28kQ2AAAOEMffwYiJCREkZGRmj9/fuDcsbeW9fb2Sjr68pDt27ef8Fm0P/3pTycMbB999JEsFsuw/wP9hS98QZdeeqnmzp2r6667Ttddd52+9KUvKSwsLFBzbNvQsbt7AHCuKC0tlclkUllZmXp6emS327V69eqzmrOnp2fY//hyOp1yOp1asmSJWlpaJB19nri0tFR33XWXent7NWfOHP3Lv/yLysrKzmrt0yGwAQBwhj7+nIR0dDvRqZ6d+N///V/dcMMNqqysPG6uYw/Gf9KFF14oj8ejwcFBmc1mSdL06dO1a9cutbS06OWXX1ZZWZkeeOAB7dy5UxEREZKk9957T5J00UUXnd1FAkCQueCCC1RSUqKSkpLjxj75Nt+IiIhh51atWqVVq1Yd973Y2NjTvgl4+vTp2rBhgzZs2HBGfZ8pnmEDAGCCXHHFFWpra1NsbKwSEhKGfaZNm3bC7yxYsECSAlt1jjGZTFq2bJmqqqr01ltvye1269VXXw2M7969WxdffPFJX2YCAJgaCGwAAEyQu+++W++9955yc3O1c+dO/elPf9LWrVt1xx13yOv1nvA7F110ka644gq99tprgXMvvviiHn30UblcLr377rt6+umn5fP5dNlllwVqfvvb3yo7O3vcrwkAML4IbAAATJA5c+boP//zP+X1epWdna358+drzZo1ioiIOO7NYx+Xn58/7LX+ERERev7553Xttdfq8ssv1+OPP66f/OQnSklJkST19/frhRdeCLz8BAAwdRn+023WBAAAk+qjjz7SZZddpp/+9KdavHjxaevr6uq0ZcsWvfzyyxPQHYCpqL+/X11dXYqLi5PVap3sds5ZY/E7c4cNAIAgZ7PZ9PTTT+uvf/3riOpDQ0P12GOPjXNXAICJwFsiAQCYApYuXTri2vz8/PFrBAAwobjDBgAAAABBisAGAAAAAEGKwAYAAAAAQYrABgAAAGDKc7vdMgxDLpdrslsZUwQ2AAAAAOe1pUuXyjCM4z45OTmT3RpviQQAAABwfvJ6vTIMQ88//7wGBwcD5//nf/5HDodDK1asmMTujuIOGwAAAIApw+fzqaqqSgkJCbJYLIqJiVF5eXlgvLOzU5mZmQoLC5PD4VBra2tgrLGxUREREWpqalJycrIsFou6u7s1a9YsRUdHBz6vvPKKwsLCgiKwcYcNAAAAgPx+v/q93glf1xoSIsMwRlxfXFys+vp6rV+/XhkZGTpw4ID27t0bGC8pKZHT6VRiYqJKSkqUm5urjo4OmUxHo4/H41FlZaUaGhoUGRmpqKio49Z44okntHLlSk2bNu3sL/AsEdgAAAAAqN/rVeYL2yZ83e03LZPNNLJY0tfXp+rqatXU1CgvL0+SFB8fr4yMDLndbklSQUFB4NmztWvXKiUlRR0dHUpKSpIkDQ0Nqba2Vg6H44Rr7NixQ7t379YTTzxxllc2NtgSCQAAAGBKaG9v18DAgLKysk5ak5qaGji22+2SpN7e3sA5s9k8rOaTnnjiCc2fP19XXnnlGHR89rjDBgAAAEDWkBBtv2nZpKw7Ujab7bQ1oaGhgeNjWy19Pt+wOU62BfPDDz/U5s2b9eCDD464p/FGYAMAAAAgwzBGvDVxsiQmJspms6m5uVn5+fljPv/PfvYzDQwM6Lbbbhvzuc9UcP8XAQAAAID/Y7VaVVRUpMLCQpnNZqWnp+vQoUNqa2s75TbJkXriiSd00003KTIycgy6HRsENgAAAABTRmlpqUwmk8rKytTT0yO73a7Vq1ef9bz79u3Ta6+9ppdffnkMuhw7ht/v9092EwAAAAAmTn9/v7q6uhQXFyer1TrZ7ZyzxuJ35i2RAAAAABCkCGwAAAAAEKQIbAAAAAAQpAhsAAAAABCkCGwAAAAAEKQIbAAAAAAQpAhsAAAAABCkCGwAAAAAEKQIbAAAAAAQpAhsAAAAAKY8t9stwzDkcrkmu5UxRWADAAAAcF5ra2vT8uXLFRsbK8MwtGHDhuNqvv/972vRokWaPn26oqKidNNNN2nfvn3j3huBDQAAAMB5yev1yufzyePxaO7cuaqoqFB0dPQJa3/961/r7rvv1u9//3u98sorGhoaUnZ2tj788MNx7ZHABgAAAGDK8Pl8qqqqUkJCgiwWi2JiYlReXh4Y7+zsVGZmpsLCwuRwONTa2hoYa2xsVEREhJqampScnCyLxaLu7m4tWrRI69at08qVK2WxWE647ksvvaRVq1YpJSVFDodDjY2N6u7u1ptvvjmu12sa19kBAAAATAl+v1/9Xt+Er2sNuUCGYYy4vri4WPX19Vq/fr0yMjJ04MAB7d27NzBeUlIip9OpxMRElZSUKDc3Vx0dHTKZjkYfj8ejyspKNTQ0KDIyUlFRUWfU9/vvvy9JmjVr1hl9f6QIbAAAAADU7/Xp2udfn/B1X/1/n5PNFDKi2r6+PlVXV6umpkZ5eXmSpPj4eGVkZMjtdkuSCgoKlJOTI0lau3atUlJS1NHRoaSkJEnS0NCQamtr5XA4zrhnn8+nNWvWKD09XZ/5zGfOeJ6RYEskAAAAgCmhvb1dAwMDysrKOmlNampq4Nhut0uSent7A+fMZvOwmjNx9913a/fu3dq8efNZzTMS3GEDAAAAIGvIBXr1/31uUtYdKZvNdtqa0NDQwPGxrZY+39+3etpstlFtwfykb37zm3rxxRf1m9/8RhdffPEZzzNSBDYAAAAAMgxjxFsTJ0tiYqJsNpuam5uVn58/oWv7/X5961vf0pYtW9TS0qK4uLgJWZfABgAAAGBKsFqtKioqUmFhocxms9LT03Xo0CG1tbWdcpvk6QwODmrPnj2B4/3798vlcik8PFwJCQmSjm6D3LRpk37xi19o+vTpOnjwoCRpxowZI7rzd6YIbAAAAACmjNLSUplMJpWVlamnp0d2u12rV68+qzl7enqUlpYW+NvpdMrpdGrJkiVqaWmRJNXV1UmSli5dOuy7Gzdu1KpVq85q/VMx/H6/f9xmBwAAABB0+vv71dXVpbi4OFmt1slu55w1Fr8zb4kEAAAAgCBFYAMAAACAIEVgAwAAAIAgRWADAAAAgCBFYAMAAACAIEVgAwAAAIAgRWADAAAAgCBFYAMAAACAIEVgAwAAAIAgRWADAAAAMOW53W4ZhiGXyzXZrYwpAhsAAACA81pbW5uWL1+u2NhYGYahDRs2HFdzbOyTn7vvvntceyOwAQAAADgveb1e+Xw+eTwezZ07VxUVFYqOjj5h7c6dO3XgwIHA55VXXpEkrVixYlx7JLABAAAAmDJ8Pp+qqqqUkJAgi8WimJgYlZeXB8Y7OzuVmZmpsLAwORwOtba2BsYaGxsVERGhpqYmJScny2KxqLu7W4sWLdK6deu0cuVKWSyWE6570UUXKTo6OvB58cUXFR8fryVLlozr9ZrGdXYAAAAAU4Lf71e/1z/h61pDjm4tHKni4mLV19dr/fr1ysjI0IEDB7R3797AeElJiZxOpxITE1VSUqLc3Fx1dHTIZDoafTwejyorK9XQ0KDIyEhFRUWNuufBwUH9+Mc/1ne+851R9X4mCGwAAAAA1O/1a9nPOiZ83W0rEmQzjSz09PX1qbq6WjU1NcrLy5MkxcfHKyMjQ263W5JUUFCgnJwcSdLatWuVkpKijo4OJSUlSZKGhoZUW1srh8Nxxj2/8MILOnz4sFatWnXGc4wUWyIBAAAATAnt7e0aGBhQVlbWSWtSU1MDx3a7XZLU29sbOGc2m4fVnIknnnhC119/vebMmXNW84wEd9gAAAAAyBpiaNuKhElZd6RsNttpa0JDQwPHx7Yr+ny+YXOczTbGd999V9u2bdPzzz9/xnOMBoENAAAAgAzDGPHWxMmSmJgom82m5uZm5efnT0oPGzduVFRUVGDb5XgjsAEAAACYEqxWq4qKilRYWCiz2az09HQdOnRIbW1tp9wmeTqDg4Pas2dP4Hj//v1yuVwKDw9XQsLf7zr6fD5t3LhReXl5gZeYjDcCGwAAAIApo7S0VCaTSWVlZerp6ZHdbtfq1avPas6enh6lpaUF/nY6nXI6nVqyZIlaWloC57dt26bu7m7deeedZ7XeaBh+v3/i390JAAAAYNL09/erq6tLcXFxslqtk93OOWssfmfeEgkAAAAAQYrABgAAAABBisAGAAAAAEGKwAYAAAAAQYrABgAAAABBisAGAAAAAEGKwAYAAAAAQYrABgAAAABBisAGAAAAAEGKwAYAAABgynO73TIMQy6Xa7JbGVMENgAAAADntba2Ni1fvlyxsbEyDEMbNmw4rqaurk6pqan61Kc+pU996lNavHixfvWrX417bwQ2AAAAAOclr9crn88nj8ejuXPnqqKiQtHR0Sesvfjii1VRUaE333xTb7zxhq699lrdeOONamtrG9ceCWwAAAAApgyfz6eqqiolJCTIYrEoJiZG5eXlgfHOzk5lZmYqLCxMDodDra2tgbHGxkZFRESoqalJycnJslgs6u7u1qJFi7Ru3TqtXLlSFovlhOvecMMN+sd//EclJiZq3rx5Ki8vV3h4uH7/+9+P6/WaxnV2AAAAAFOC3+/XwJGJX9dikgzDGHF9cXGx6uvrtX79emVkZOjAgQPau3dvYLykpEROp1OJiYkqKSlRbm6uOjo6ZDIdjT4ej0eVlZVqaGhQZGSkoqKiRt2z1+vVz372M3344YdavHjxqL8/GgQ2AAAAABo4Ii3f5JnwdX/+lTBZQ0dW29fXp+rqatXU1CgvL0+SFB8fr4yMDLndbklSQUGBcnJyJElr165VSkqKOjo6lJSUJEkaGhpSbW2tHA7HqHv94x//qMWLF6u/v1/h4eHasmWLkpOTRz3PaLAlEgAAAMCU0N7eroGBAWVlZZ20JjU1NXBst9slSb29vYFzZrN5WM1oXHbZZXK5XHr99df1jW98Q3l5edqzZ88ZzTVS3GEDAAAAIIvp6N2uyVh3pGw222lrQkP/frvu2FZLn883bI7RbMH8OLPZrISEBEnSZz/7We3cuVPV1dX6j//4jzOabyQIbAAAAABkGMaItyZOlsTERNlsNjU3Nys/P3+y25HP59PAwMC4rkFgAwAAADAlWK1WFRUVqbCwUGazWenp6Tp06JDa2tpOuU3ydAYHBwNbGwcHB7V//365XC6Fh4cH7qgVFxfr+uuvV0xMjPr6+rRp0ya1tLRo69atY3JtJ0NgAwAAADBllJaWymQyqaysTD09PbLb7Vq9evVZzdnT06O0tLTA306nU06nU0uWLFFLS4uko8/BffWrX9WBAwc0Y8YMpaamauvWrfrCF75wVmufjuH3+/3jugIAAACAoNLf36+uri7FxcXJarVOdjvnrLH4nXlLJAAAAAAEKQIbAAAAAAQpAhsAAAAABCkCGwAAAAAEKQIbAAAAAAQpAhsAAAAABCkCGwAAAAAEKQIbAAAAAAQpAhsAAAAABCkCGwAAAIApz+12yzAMuVyuyW5lTBHYAAAAAJzX2tratHz5csXGxsowDG3YsOGEdfv379dtt92myMhI2Ww2zZ8/X2+88ca49kZgAwAAAHBe8nq98vl88ng8mjt3rioqKhQdHX3C2r/97W9KT09XaGiofvWrX2nPnj165JFHNHPmzHHtkcAGAAAAYMrw+XyqqqpSQkKCLBaLYmJiVF5eHhjv7OxUZmamwsLC5HA41NraGhhrbGxURESEmpqalJycLIvFou7ubi1atEjr1q3TypUrZbFYTrhuZWWlLrnkEm3cuFFXXnml4uLilJ2drfj4+HG9XgIbAAAAAPn9fg0OTfzH7/ePqs/i4mJVVFSotLRUe/bs0aZNmzR79uzAeElJiQoKCuRyuTRv3jzl5ubqyJEjgXGPx6PKyko1NDSora1NUVFRI1q3qalJCxcu1IoVKxQVFaW0tDTV19ePqvczYRr3FQAAAAAEvaEj0g/qPRO+7ne+HiZz6Mhq+/r6VF1drZqaGuXl5UmS4uPjlZGRIbfbLUkqKChQTk6OJGnt2rVKSUlRR0eHkpKSJElDQ0Oqra2Vw+EYVZ+dnZ2qq6vTd77zHf37v/+7du7cqXvuuUdmsznQy3ggsAEAAACYEtrb2zUwMKCsrKyT1qSmpgaO7Xa7JKm3tzcQ2Mxm87CakfL5fFq4cKEefvhhSVJaWpp2796txx9/nMAGAAAAYHyFmo7e7ZqMdUfKZrOdfr7Qv9+uMwxD0tGw9fE5jp0fDbvdruTk5GHnLr/8cv385z8f9VyjQWADAAAAIMMwRrw1cbIkJibKZrOpublZ+fn5E7p2enq69u3bN+zc22+/rUsvvXRc1yWwAQAAAJgSrFarioqKVFhYKLPZrPT0dB06dEhtbW2n3CZ5OoODg9qzZ0/geP/+/XK5XAoPD1dCQoIk6dvf/rauuuoqPfzww7rlllu0Y8cO/fCHP9QPf/jDMbm2kyGwAQAAAJgySktLZTKZVFZWpp6eHtntdq1evfqs5uzp6VFaWlrgb6fTKafTqSVLlqilpUWStGjRIm3ZskXFxcV68MEHFRcXpw0bNujWW289q7VPx/CP9j2aAAAAAKa0/v5+dXV1KS4uTlardbLbOWeNxe/Mv8MGAAAAAEGKwAYAAAAAQYrABgAAAABBisAGAAAAAEGKwAYAAAAAQYrABgAAAABBisAGAAAAAEGKwAYAAAAAQYrABgAAAABBisAGAAAAYMpzu90yDEMul2uyWxlTBDYAAAAA57X6+npdffXVmjlzpmbOnKlly5Zpx44dw2qef/55ZWdnKzIyckKDIYENAAAAwHnJ6/XK5/OppaVFubm52r59u1pbW3XJJZcoOztb+/fvD9R++OGHysjIUGVl5YT2SGADAAAAMGX4fD5VVVUpISFBFotFMTExKi8vD4x3dnYqMzNTYWFhcjgcam1tDYw1NjYqIiJCTU1NSk5OlsViUXd3t5555hndddddWrBggZKSktTQ0CCfz6fm5ubAd2+//XaVlZVp2bJlE3q9pgldDQAAAEBQ8vv98g5N/LohoZJhGCOuLy4uVn19vdavX6+MjAwdOHBAe/fuDYyXlJTI6XQqMTFRJSUlys3NVUdHh0ymo9HH4/GosrJSDQ0NioyMVFRU1HFreDweDQ0NadasWWd/gWeJwAYAAABA3iHpFxs8E77ujWvCZDKPrLavr0/V1dWqqalRXl6eJCk+Pl4ZGRlyu92SpIKCAuXk5EiS1q5dq5SUFHV0dCgpKUmSNDQ0pNraWjkcjpOuU1RUpDlz5kz43bQTYUskAAAAgCmhvb1dAwMDysrKOmlNampq4Nhut0uSent7A+fMZvOwmk+qqKjQ5s2btWXLFlmt1jHo+uxwhw0AAACAQkKP3u2ajHVHymaznbYmNPTvEx7baunz+YbNcbItmE6nUxUVFdq2bdspQ91EIrABAAAAkGEYI96aOFkSExNls9nU3Nys/Pz8MZ27qqpK5eXl2rp1qxYuXDimc58NAhsAAACAKcFqtaqoqEiFhYUym81KT0/XoUOH1NbWdsptkqdTWVmpsrIybdq0SbGxsTp48KAkKTw8XOHh4ZKk9957T93d3erp6ZEk7du3T5IUHR2t6Ojos7yyk+MZNgAAAABTRmlpqf7t3/5NZWVluvzyy/XlL3952DNqZ6Kurk6Dg4O6+eabZbfbAx+n0xmoaWpqUlpaWuCFJitXrlRaWpoef/zxs1r7dAy/3+8f1xUAAAAABJX+/n51dXUpLi4uKF6sca4ai9+ZO2wAAAAAEKQIbAAAAAAQpAhsAAAAABCkCGwAAAAAEKQIbAAAAAAQpAhsAAAAABCkCGwAAAAAEKQIbAAAAAAQpAhsAAAAABCkCGwAAAAApjy32y3DMORyuSa7lTFFYAMAAABwXmtra9Py5csVGxsrwzC0YcOG42r6+vq0Zs0aXXrppbLZbLrqqqu0c+fOce+NwAYAAADgvOT1euXz+eTxeDR37lxVVFQoOjr6hLX5+fl65ZVX9KMf/Uh//OMflZ2drWXLlmn//v3j2iOBDQAAAMCU4fP5VFVVpYSEBFksFsXExKi8vDww3tnZqczMTIWFhcnhcKi1tTUw1tjYqIiICDU1NSk5OVkWi0Xd3d1atGiR1q1bp5UrV8pisRy35kcffaSf//znqqqq0jXXXKOEhAQ98MADSkhIUF1d3bher2lcZwcAAAAwJfj9fvmGJn7dC0IlwzBGXF9cXKz6+nqtX79eGRkZOnDggPbu3RsYLykpkdPpVGJiokpKSpSbm6uOjg6ZTEejj8fjUWVlpRoaGhQZGamoqKjTrnnkyBF5vV5ZrdZh5202m1577bUR934mCGwAAAAA5BuSXGs9E77ugvvDFGIeWW1fX5+qq6tVU1OjvLw8SVJ8fLwyMjLkdrslSQUFBcrJyZEkrV27VikpKero6FBSUpIkaWhoSLW1tXI4HCPucfr06Vq8eLEeeughXX755Zo9e7Z+8pOfqLW1VQkJCSO/2DPAlkgAAAAAU0J7e7sGBgaUlZV10prU1NTAsd1ulyT19vYGzpnN5mE1I/WjH/1Ifr9fn/70p2WxWPToo48qNzdXF1wwvpGKO2wAAAAAdEHo0btdk7HuSNlsttPWhIb+fcJjWy19Pt+wOUazBfOY+Ph4/frXv9aHH36oDz74QHa7XV/+8pc1d+7cUc81GtxhAwAAACDDMBRinvjPaMJTYmKibDabmpubx/GXOLVp06bJbrfrb3/7m7Zu3aobb7xxXNfjDhsAAACAKcFqtaqoqEiFhYUym81KT0/XoUOH1NbWdsptkqczODioPXv2BI73798vl8ul8PDwwDNqW7duld/v12WXXaaOjg7de++9SkpK0h133DEm13YyBDYAAAAAU0ZpaalMJpPKysrU09Mju92u1atXn9WcPT09SktLC/ztdDrldDq1ZMkStbS0SJLef/99FRcX689//rNmzZql5cuXq7y8fNgWzPFg+P1+/7iuAAAAACCo9Pf3q6urS3Fxcce9qh5jZyx+Z55hAwAAAIAgRWADAAAAgCBFYAMAAACAIEVgAwAAAIAgRWADAAAAgCBFYAMAAACAIEVgAwAAAIAgRWADAAAAgCBFYAMAAACAIEVgAwAAADDlud1uGYYhl8s12a2MKQIbAAAAgPNafX29rr76as2cOVMzZ87UsmXLtGPHjmE1fr9fZWVlstvtstlsWrZsmd55551x743ABgAAAOC85PV65fP51NLSotzcXG3fvl2tra265JJLlJ2drf379wdqq6qq9Oijj+rxxx/X66+/rmnTpukf/uEf1N/fP649EtgAAAAATBk+n09VVVVKSEiQxWJRTEyMysvLA+OdnZ3KzMxUWFiYHA6HWltbA2ONjY2KiIhQU1OTkpOTZbFY1N3drWeeeUZ33XWXFixYoKSkJDU0NMjn86m5uVnS0btrGzZs0H333acbb7xRqampevrpp9XT06MXXnhhXK+XwAYAAABAfr9f/oFJ+Pj9o+qzuLhYFRUVKi0t1Z49e7Rp0ybNnj07MF5SUqKCggK5XC7NmzdPubm5OnLkSGDc4/GosrJSDQ0NamtrU1RU1HFreDweDQ0NadasWZKkrq4uHTx4UMuWLQvUzJgxQ5/73OeGBcLxYBrX2QEAAABMDYPS4W94JnzZiLowyTKy2r6+PlVXV6umpkZ5eXmSpPj4eGVkZMjtdkuSCgoKlJOTI0lau3atUlJS1NHRoaSkJEnS0NCQamtr5XA4TrpOUVGR5syZEwhoBw8elKRhwfDY38fGxgt32AAAAABMCe3t7RoYGFBWVtZJa1JTUwPHdrtdktTb2xs4Zzabh9V8UkVFhTZv3qwtW7bIarWOQddnhztsAAAAACTz/93tmoR1R8pms522JjQ0NHBsGIako8+9fXyOY+c/yel0qqKiQtu2bRsW6qKjoyVJf/nLXwIh8NjfCxYsGPkFnAHusAEAAACQYRgyLJPwOUl4OpHExETZbLbAy0DGUlVVlR566CG99NJLWrhw4bCxuLg4RUdHD1v3gw8+0Ouvv67FixePeS8fxx02AAAAAFOC1WpVUVGRCgsLZTablZ6erkOHDqmtre2U2yRPp7KyUmVlZdq0aZNiY2MDz6WFh4crPDxchmFozZo1+t73vqfExETFxcWptLRUc+bM0U033TRGV3diBDYAAAAAU0ZpaalMJpPKysrU09Mju92u1atXn9WcdXV1Ghwc1M033zzs/P33368HHnhAklRYWKgPP/xQ//zP/6zDhw8rIyNDL7300rg/52b4R/seTQAAAABTWn9/v7q6uhQXFxcUL9Y4V43F78wzbAAAAAAQpAhsAAAAABCkCGwAAAAAEKQIbAAAAAAQpAhsAAAAABCkCGwAAAAAEKQIbAAAAAAQpAhsAAAAABCkCGwAAAAAEKQIbAAAAACmPLfbLcMw5HK5JruVMUVgAwAAAHBeq6+v19VXX62ZM2dq5syZWrZsmXbs2BEYHxoaUlFRkebPn69p06Zpzpw5+upXv6qenp5x743ABgAAAOC85PV65fP51NLSotzcXG3fvl2tra265JJLlJ2drf3790uSPB6Pdu3apdLSUu3atUvPP/+89u3bp3/6p38a9x4JbAAAAACmDJ/Pp6qqKiUkJMhisSgmJkbl5eWB8c7OTmVmZiosLEwOh0Otra2BscbGRkVERKipqUnJycmyWCzq7u7WM888o7vuuksLFixQUlKSGhoa5PP51NzcLEmaMWOGXnnlFd1yyy267LLL9PnPf141NTV688031d3dPa7XaxrX2QEAAABMCX6/Xxr0T/zCZkOGYYy4vLi4WPX19Vq/fr0yMjJ04MAB7d27NzBeUlIip9OpxMRElZSUKDc3Vx0dHTKZjkYfj8ejyspKNTQ0KDIyUlFRUcet4fF4NDQ0pFmzZp20j/fff1+GYSgiImLk13oGCGwAAAAApEG/PrrrTxO+rK02XrKMLLD19fWpurpaNTU1ysvLkyTFx8crIyNDbrdbklRQUKCcnBxJ0tq1a5WSkqKOjg4lJSVJOvo8Wm1trRwOx0nXKSoq0pw5c7Rs2bITjvf396uoqEi5ubn61Kc+NdJLPSNsiQQAAAAwJbS3t2tgYEBZWVknrUlNTQ0c2+12SVJvb2/gnNlsHlbzSRUVFdq8ebO2bNkiq9V63PjQ0JBuueUW+f1+1dXVnclljAp32AAAAABIZuPo3a5JWHekbDbbaWtCQ0MDx8e2Wvp8vmFznGwLptPpVEVFhbZt23bCUHcsrL377rt69dVXx/3umkRgAwAAAKD/Czcj3Jo4WRITE2Wz2dTc3Kz8/Pwxnbuqqkrl5eXaunWrFi5ceNz4sbD2zjvvaPv27YqMjBzT9U+GwAYAAABgSrBarSoqKlJhYaHMZrPS09N16NAhtbW1nXKb5OlUVlaqrKxMmzZtUmxsrA4ePChJCg8PV3h4uIaGhnTzzTdr165devHFF+X1egM1s2bNktlsHpPrOxECGwAAAIApo7S0VCaTSWVlZerp6ZHdbtfq1avPas66ujoNDg7q5ptvHnb+/vvv1wMPPKD9+/erqalJkrRgwYJhNdu3b9fSpUvPav1TMfx+/yS8uxMAAADAZOnv71dXV5fi4uJO+GINjI2x+J15SyQAAAAABCkCGwAAAAAEKQIbAAAAAAQpAhsAAAAABCkCGwAAAAAEKQIbAAAAAAQpAhsAAAAABCkCGwAAAAAEKQIbAAAAAAQpAhsAAACAKc/tdsswDLlcrsluZUwR2AAAAACct772ta9p/vz5GhwcHHb+l7/8pcxms3bt2jVJnR1FYAMAAABwXvJ6vVq/fr36+vp0//33B84fPnxYX//611VaWqorrrhiEjsksAEAAACYQnw+n6qqqpSQkCCLxaKYmBiVl5cHxjs7O5WZmamwsDA5HA61trYGxhobGxUREaGmpiYlJyfLYrHovffe08aNG/XII4/o9ddflyStWbNGn/70p1VcXDzh1/dJpsluAAAAAMDk8/v90qBv4hc2XyDDMEZcXlxcrPr6eq1fv14ZGRk6cOCA9u7dGxgvKSmR0+lUYmKiSkpKlJubq46ODplMR6OPx+NRZWWlGhoaFBkZqaioKMXGxuquu+5SXl6eHnroIT377LPatWtX4DuTyfD7/f7JbgIAAADAxOnv71dXV5fi4uJktVolSf4Br/rXvDbhvVg3ZMiwhIyotq+vTxdddJFqamqUn58/bMztdisuLk4NDQ362te+Jknas2ePUlJS1N7erqSkJDU2NuqOO+6Qy+WSw+EY9v2PPvpIaWlpeuedd/TII49ozZo1Z31tJ/qdR4stkQAAAACmhPb2dg0MDCgrK+ukNampqYFju90uSert7Q2cM5vNw2qOsdlsKigoUFhYmP71X/91DLs+O5N/jw8AAADA5DNfIOuGjElZd6RsNttpa0JDQwPHx7Za+nx/3+pps9lOugXTZDIpJCRkVFs0xxuBDQAAAMDRkDLCrYmTJTExUTabTc3NzcdtiTxXEdgAAAAATAlWq1VFRUUqLCyU2WxWenq6Dh06pLa2tlNuk5zKCGwAAAAApozS0lKZTCaVlZWpp6dHdrtdq1evnuy2xg1viQQAAADOM2Px9kKcHm+JBAAAAIBzGIENAAAAAIIUgQ0AAAAAghSBDQAAAACCFIENAAAAAIIUgQ0AAAAAghSBDQAAAACCFIENAAAAAIIUgQ0AAADAlOd2u2UYhlwu12S3MqYIbAAAAADOaw888IAMw9B111133Ni6detkGIaWLl068Y2JwAYAAADgPOX1euXz+SRJdrtd27dv15///OdhNU8++aRiYmImoz1JBDYAAAAAU4jP51NVVZUSEhJksVgUExOj8vLywHhnZ6cyMzMVFhYmh8Oh1tbWwFhjY6MiIiLU1NSk5ORkWSwWdXd3S5KioqKUnZ2tp556KlD/u9/9Tn/961+Vk5MzcRf4CQQ2AAAAAFNGcXGxKioqVFpaqj179mjTpk2aPXt2YLykpEQFBQVyuVyaN2+ecnNzdeTIkcC4x+NRZWWlGhoa1NbWpqioqMDYnXfeqcbGxsDfTz75pG699VaZzeYJubYTMU3aygAAAACCht/vlwa9E7+wOUSGYYyotK+vT9XV1aqpqVFeXp4kKT4+XhkZGXK73ZKkgoKCwB2xtWvXKiUlRR0dHUpKSpIkDQ0Nqba2Vg6H47j5v/jFL2r16tX6zW9+o89+9rN69tln9dprr+nJJ58cgws9MwQ2AAAAANKgV/0Fz034slbnzZJlZLGkvb1dAwMDysrKOmlNampq4Nhut0uSent7A4HNbDYPq/m40NBQ3Xbbbdq4caM6Ozs1b968k9ZOFAIbAAAAgCnBZrOdtiY0NDRwfOzO3bEXixyb41R39O6880597nOf0+7du3XnnXeeRbdjg8AGAAAAQDKHHL3bNQnrjlRiYqJsNpuam5uVn58/Lu2kpKQoJSVFb731lr7yla+MyxqjQWADAAAAcPSu0wi3Jk4Wq9WqoqIiFRYWymw2Kz09XYcOHVJbW9spt0mO1quvvqqhoSFFRESM2ZxnKrj/iwAAAADAx5SWlspkMqmsrEw9PT2y2+1avXr1mK4xbdq0MZ3vbBh+v98/2U0AAAAAmDj9/f3q6upSXFycrFbrZLdzzhqL35l/hw0AAAAAghSBDQAAAACCFIENAAAAAIIUgQ0AAAAAghSBDQAAAACCFIENAAAAOE/xwvjxNRa/L4ENAAAAOM+EhoZKkjwezyR3cm479vse+73PBP9wNgAAAHCeCQkJUUREhHp7eyVJYWFhMgxjkrs6d/j9fnk8HvX29ioiIkIhISFnPBf/cDYAAABwHvL7/Tp48KAOHz482a2csyIiIhQdHX1WYZjABgAAAJzHvF6vhoaGJruNc05oaOhZ3Vk7hsAGAAAAAEGKl44AAAAAQJAisAEAAABAkCKwAQAAAECQIrABAAAAQJAisAEAAABAkCKwAQAAAECQIrABAAAAQJD6/942dRa3S0qYAAAAAElFTkSuQmCC", "text/plain": [ "
" ] diff --git a/usecases/compare_replication_methods.ipynb b/usecases/compare_replication_methods.ipynb index c765837..db492ad 100644 --- a/usecases/compare_replication_methods.ipynb +++ b/usecases/compare_replication_methods.ipynb @@ -31,7 +31,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -53,7 +53,137 @@ " ax.autoscale()\n", " make_tight_layout(ax.figure)\n", "\n", - " return fig\n", + " return fig\n" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 102, + "metadata": {}, + "outputs": [], + "source": [ + "# # merging the plots\n", + "# fig = figs[\"1\"]\n", + "# fig\n", + "\n", + "# for (name, fig) in figs.items():\n", + "# print(name)\n", + "# ax = fig.axes[0]\n", + "# data = ax.get_children()[0].get_offsets().data.T\n", + "# plt.plot(*data, ls=\"None\", marker=\".\", markersize=1)\n", + " \n", + "# # test for merging two plots\n", + "\n", + "# n_points = 50\n", + "# xvals = np.arange(n_points)\n", + "# df1 = pd.DataFrame({\"x\": xvals, \"y\": np.random.randn(n_points), \"group\": np.random.choice([\"a\", \"b\"], n_points)})\n", + "# sns.lmplot(df1, x=\"x\", y=\"y\", hue=\"group\", scatter_kws={\"s\": 1})\n", + "# fig1 = plt.gcf()\n", + "\n", + "# xvals = np.arange(n_points) + 0.5\n", + "# df2 = pd.DataFrame({\"x\": xvals, \"y\": np.random.randn(n_points) * 20, \"group\": np.random.choice([\"a\", \"b\"], n_points)})\n", + "# sns.lmplot(df2, x=\"x\", y=\"y\", hue=\"group\", scatter_kws={\"s\": 1})\n", + "# fig2 = plt.gcf()\n", + "\n", + "# figs = {\"1\": fig1, \"2\": fig2}\n", + "\n", + "# for (name, fig) in figs.items():\n", + "# print(name)\n", + "# ax = fig.axes[0]\n", + "# data = ax.get_children()[0].get_offsets().data.T\n", + "# plt.plot(*data, ls=\"None\", marker=\".\", markersize=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 0.98, 'Read length of rejected reads over time for different acceleration factors')" + ] + }, + "execution_count": 107, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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5UomERAREZHZtFqtHNTVqlXL1sWpUlxcXAAASUlJ8Pf3L1ezrMXJEytWrIBGozFanpycjLS0tDIXhIiIiOyXvk+dqdmr6P7zUt6+hxYHdsOHD8c333xjtHzTpk0YPnx4uQpDRERE9o3Nr6ZZ63mxOLA7dOiQyanDHnroIRw6dMgqhSIiIiIiy1kc2OXm5ppsilWr1cjOzrZKoYiIiIjIchYHdh06dMCnn35qtHz16tVo166dVQpFREREVFXs3r0bAwYMQHBwMCRJwtatW21dpGJZPEDxu+++i6ioKJw8eRI9e/YEAOzcuROHDx/G9u3brV5AIiIiIlvKzMxEq1atMGbMGAwePNjWxSmRxYFdly5dcODAASxevBibNm2Ci4sLWrZsibVr16Jhw4YVUUYiIiIim+nbty/69u1r62KYxeLADgBat26Nr776ytplISIiohpECIG0HON++5XF09nR7rJ0yxTYXb16FevWrcO///6LFStWwN/fH7/99hvCwsLQrFkza5eRiIiI7FBajgat5tmuG9fJub3g5aK02fkrgsXJE3///TdatGiBQ4cO4fvvv0dGRgYA4OTJk5g7d67VC0hERERE5rE4sHvttdfw7rvvYseOHQbzxT7yyCM4ePCgVQtHREREROazuCn29OnTJvvX+fv7486dO1YpFBEREdk/T2dHnJzby6bntzcWX5G3tzfi4+MRERFhsPz48eOoU6eO1QpGRERE9k2SpGrRxy0jIwNXrlyRH8fExODEiRPw9fVFWFiYDUtmrExzxc6cORMJCQmQJAk6nQ779u3DjBkz8Oyzz1ZEGYmIiIhs5siRI2jTpg3atGkDAJg+fTratGmDOXPm2LhkxiyusVuwYAEmTZqE0NBQaLVaREZGQqvVYsSIEXjzzTcrooxERERENvPQQw9BCGHrYpjF4sDOyckJn332GWbPno0zZ84gIyMDbdq04eDERERERDZW5l6DYWFhVa5dmYiIiKgmMyuwmz59Ot555x24ublh+vTpJW7r7u6OZs2a4cknn4SDg4NVCklEREREpTMrsDt+/DjUarX8d0lyc3OxcuVK/Prrr9iwYUP5S0hEREREZjErsNu1a5fJv4tz5MgR9OzZs+ylIiIiIiKLWTzciV5eXh4uXrwIjcZ48t6WLVviiy++KPUYu3fvxoABAxAcHAxJkrB161aD9UII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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "# base_dir = Path(\"/Volumes/mmordig/ont_project/runs/run_replication/runs/run_replication\")\n", + "base_dir = Path(\"/home/mmordig/ont_project_all/ont_project/runs/enrich_usecase/\")\n", + "figure_dirs = {\n", + " \"1\": base_dir / \"results_accel1/simulator_run/figures/pickled_figures\",\n", + " \"3\": base_dir / \"results_accel3/simulator_run/figures/pickled_figures\",\n", + " \"5\": base_dir / \"results_accel5/simulator_run/figures/pickled_figures\",\n", + " \"7.5\": base_dir / \"results_accel7.5/simulator_run/figures/pickled_figures\",\n", + " \"10\": base_dir / \"results_accel10/simulator_run/figures/pickled_figures\",\n", + "}\n", + "\n", + "figure_basename = \"read_length_rejected.dill\"\n", + "\n", + "figs = {name: dill_load(figure_dir / figure_basename) for name, figure_dir in figure_dirs.items()}\n", + "[plt.close(fig) for fig in figs.values()]\n", + "named_axes = {name: fig.axes[0] for name, fig in figs.items()}\n", + "\n", + "# # plot number rather than fraction of active channels\n", + "# for original_ax in named_axes.values():\n", + "# # parse title of the form:\n", + "# # f\"Fraction of active channels over time ({n_channels} active channels)\"\n", + "# n_channels = int(original_ax.get_title().split(\"(\")[-1].split(\" \")[0])\n", + "# line = original_ax.lines[0]\n", + "# print(n_channels)\n", + "# line.set_ydata(np.array(line.get_ydata()) / 100 * n_channels)\n", + "# # ax.autoscale()\n", + "\n", + "fig = merge_axes_into_one(named_axes)\n", + "ax = fig.axes[0]\n", + "\n", + "fig.suptitle(\"Read length of rejected reads over time for different acceleration factors\")\n", + "# ax.set_ylabel(\"Number of reading channels\")\n", + "# ax.set_title(f\"Number of reading channels over time\") # number of active channels varies between sequencing runs\n", + "# fig.savefig(base_dir / \"combined_channel_occupation_over_time.png\", dpi=300, bbox_inches=\"tight\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "\n", "# base_dir = Path(\"/Volumes/mmordig/ont_project/runs/run_replication/runs/run_replication\")\n", "base_dir = Path(\"/Users/maximilianmordig/ont_project_all/figures_cluster/runs/run_replication\")\n", @@ -98,7 +228,7 @@ } ], "source": [ - "figure_basename = \"channel_occupation_fraction_over_time.dill\"\n", + "figure_basename = \"channel_occupation_over_time.dill\"\n", "\n", "figs = {name: dill_load(figure_dir / figure_basename) for name, figure_dir in figure_dirs.items()}\n", "[plt.close(fig) for fig in figs.values()]\n", @@ -119,7 +249,7 @@ "\n", "ax.set_ylabel(\"Number of reading channels\")\n", "ax.set_title(f\"Number of reading channels over time\") # number of active channels varies between sequencing runs\n", - "fig.savefig(base_dir / \"combined_channel_occupation_fraction_over_time.png\", dpi=300, bbox_inches=\"tight\")" + "fig.savefig(base_dir / \"combined_channel_occupation_over_time.png\", dpi=300, bbox_inches=\"tight\")" ] }, { @@ -229,7 +359,7 @@ "cp \"${base_dir}sampler_per_window/simulator_run/figures/read_stats_by_channel.png\" \"${target_base_dir}read_stats_by_channel_sampler_per_window.png\"\n", "cp \"${base_dir}constant_gaps/simulator_run/figures/read_stats_by_channel.png\" \"${target_base_dir}read_stats_by_channel_constantgaps.png\"\n", "\n", - "cp \"${base_dir}/combined_channel_occupation_fraction_over_time.png\" \"${target_base_dir}\"\n", + "cp \"${base_dir}/combined_channel_occupation_over_time.png\" \"${target_base_dir}\"\n", "cp \"${base_dir}/combined_cum_nb_reads_per_all.png\" \"${target_base_dir}\"\n", "cp \"${base_dir}/combined_cum_nb_seq_bps_per_all.png\" \"${target_base_dir}\"\n", "\n", diff --git a/usecases/compute_absolute_enrichment.ipynb b/usecases/compute_absolute_enrichment.ipynb new file mode 100644 index 0000000..19a0d24 --- /dev/null +++ b/usecases/compute_absolute_enrichment.ipynb @@ -0,0 +1,447 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "import numpy as np\n", + "import pandas as pd\n", + "from pathlib import Path\n", + "import logging\n", + "import toml\n", + "import copy\n", + "\n", + "from simreaduntil.shared_utils.logging_utils import add_comprehensive_stream_handler_to_logger, setup_logger_simple\n", + "from simreaduntil.seqsum_tools.seqsum_plotting import preprocess_seqsum_df_for_plotting\n", + "\n", + "\n", + "add_comprehensive_stream_handler_to_logger(None)\n", + "logging.getLogger(__name__).setLevel(logging.DEBUG)\n", + "logging.getLogger(\"simreaduntil\").setLevel(logging.DEBUG)\n", + "\n", + "logger = logging\n", + "\n", + "# logging.getLogger(None).setLevel(logging.ERROR)\n", + "# logging.getLogger(\"simreaduntil\").setLevel(logging.ERROR)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "# run_dir = Path(\"/home/mmordig/ont_project_all/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_realreads_withflanking/simulator_run/\")\n", + "# run_dir = Path(\"/is/cluster-test/fast/mmordig/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_realreads/simulator_run/\")\n", + "# run_dir = Path(\"/is/cluster-test/fast/mmordig/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_realreads_realmapper_accel5/simulator_run/\")\n", + "# run_dir = Path(\"/is/cluster-test/fast/mmordig/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_realreads_fakemapper_accel10/simulator_run/\")\n", + "# run_dir = Path(\"/is/cluster-test/fast/mmordig/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_realreads_realmapper_withunaligned_constantgapsampler_accel5/simulator_run/\")\n", + "# run_dir = Path(\"/is/cluster-test/fast/mmordig/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_realreads_realmapper_withunaligned_accel10/simulator_run/\")\n", + "# run_dir = Path(\"/is/cluster-test/fast/mmordig/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_realreads_realmapper_withunaligned_accel5/simulator_run/\")\n", + "# run_dir = Path(\"/is/cluster-test/fast/mmordig/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_realreads_realmapper_withunaligned_accel2/simulator_run/\")\n", + "# run_dir = Path(\"/is/cluster-test/fast/mmordig/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_realreads_realmapper_withunaligned_accel3_longer/simulator_run/\")\n", + "run_dir = Path(\"/is/cluster-test/fast/mmordig/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_realreads_realmapper_withunaligned_accel5_longer/simulator_run/\")\n", + "\n", + "seqsum_filename = run_dir / \"sequencing_summary.txt\"\n", + "sim_config = {\"readfish_config_file\": run_dir / \"..\" / \"configs/readfish_enrich_per_quadrant.toml\" }\n", + "\n", + "readfish_conditions = [v for v in toml.load(sim_config[\"readfish_config_file\"])[\"conditions\"].values() if isinstance(v, dict)]\n", + "channel_assignments_toml = run_dir / \"channels.toml\"\n", + "channel_assignments_per_cond = toml.load(channel_assignments_toml)\n", + "channels_per_condition = {condition_dict[\"name\"]: condition_dict[\"channels\"] for condition_dict in channel_assignments_per_cond[\"conditions\"].values()}\n", + "\n", + "logger.debug(f\"Reading sequencing summary file '{seqsum_filename}'\")\n", + "full_seqsum_df = pd.read_csv(seqsum_filename, sep=\"\\t\")#, nrows=100) # todo\n", + "logger.debug(f\"Done reading sequencing summary file '{seqsum_filename}'\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "# partial_seqsum_df.columns\n", + "# full_seqsum_df[\"channel\"].unique()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-03-01 10:33:16,409 - Sorting and cleaning seqsummary file of shape (184996, 13) --- seqsum_plotting.py:939 (preprocess_seqsum_df_for_plotting) INFO ##\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Processing condition control\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-03-01 10:33:16,677 - Adding previous gap duration to seqsummary --- seqsum_plotting.py:941 (preprocess_seqsum_df_for_plotting) INFO ##\n", + "2024-03-01 10:33:16,835 - Adding group column from NanoSim read id --- seqsum_plotting.py:951 (preprocess_seqsum_df_for_plotting) INFO ##\n", + "2024-03-01 10:33:17,431 - Splitting according to groups {'enrich_chr_1_8': ['chr1', 'chr2', 'chr3', 'chr4', 'chr5', 'chr6', 'chr7', 'chr8'], 'enrich_chr_9_14': ['chr9', 'chr10', 'chr11', 'chr12', 'chr13', 'chr14'], 'enrich_chr_16_20': ['chr16', 'chr17', 'chr18', 'chr19', 'chr20'], 'other': {'chr21', 'chrY', 'chr15', 'chr22', 'chrX'}} --- seqsum_plotting.py:964 (preprocess_seqsum_df_for_plotting) INFO ##\n", + "2024-03-01 10:33:17,453 - Adding extra columns for plotting --- seqsum_plotting.py:971 (preprocess_seqsum_df_for_plotting) INFO ##\n", + "2024-03-01 10:33:17,543 - /tmp/ipykernel_1011868/1748468773.py:16: FutureWarning: The default of observed=False is deprecated and will be changed to True in a future version of pandas. Pass observed=False to retain current behavior or observed=True to adopt the future default and silence this warning.\n", + " num_sequenced_bps_per_group = dict(partial_seqsum_df.groupby(\"group\")[\"cum_nb_seq_bps_per_group\"].max())\n", + " --- warnings.py:109 (_showwarnmsg) WARNING ##\n", + "2024-03-01 10:33:17,651 - Sorting and cleaning seqsummary file of shape (355109, 13) --- seqsum_plotting.py:939 (preprocess_seqsum_df_for_plotting) INFO ##\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Processing condition enrich_chr_1_8\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-03-01 10:33:18,095 - Adding previous gap duration to seqsummary --- seqsum_plotting.py:941 (preprocess_seqsum_df_for_plotting) INFO ##\n", + "2024-03-01 10:33:18,389 - Adding group column from NanoSim read id --- seqsum_plotting.py:951 (preprocess_seqsum_df_for_plotting) INFO ##\n", + "2024-03-01 10:33:19,486 - Splitting according to groups {'enrich_chr_1_8': ['chr1', 'chr2', 'chr3', 'chr4', 'chr5', 'chr6', 'chr7', 'chr8'], 'enrich_chr_9_14': ['chr9', 'chr10', 'chr11', 'chr12', 'chr13', 'chr14'], 'enrich_chr_16_20': ['chr16', 'chr17', 'chr18', 'chr19', 'chr20'], 'other': {'chr21', 'chrY', 'chr15', 'chr22', 'chrX'}} --- seqsum_plotting.py:964 (preprocess_seqsum_df_for_plotting) INFO ##\n", + "2024-03-01 10:33:19,527 - Adding extra columns for plotting --- seqsum_plotting.py:971 (preprocess_seqsum_df_for_plotting) INFO ##\n", + "2024-03-01 10:33:19,697 - /tmp/ipykernel_1011868/1748468773.py:16: FutureWarning: The default of observed=False is deprecated and will be changed to True in a future version of pandas. Pass observed=False to retain current behavior or observed=True to adopt the future default and silence this warning.\n", + " num_sequenced_bps_per_group = dict(partial_seqsum_df.groupby(\"group\")[\"cum_nb_seq_bps_per_group\"].max())\n", + " --- warnings.py:109 (_showwarnmsg) WARNING ##\n", + "2024-03-01 10:33:19,821 - Sorting and cleaning seqsummary file of shape (526237, 13) --- seqsum_plotting.py:939 (preprocess_seqsum_df_for_plotting) INFO ##\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Processing condition enrich_chr_9_14\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-03-01 10:33:20,519 - Adding previous gap duration to seqsummary --- seqsum_plotting.py:941 (preprocess_seqsum_df_for_plotting) INFO ##\n", + "2024-03-01 10:33:20,981 - Adding group column from NanoSim read id --- seqsum_plotting.py:951 (preprocess_seqsum_df_for_plotting) INFO ##\n", + "2024-03-01 10:33:22,599 - Splitting according to groups {'enrich_chr_1_8': ['chr1', 'chr2', 'chr3', 'chr4', 'chr5', 'chr6', 'chr7', 'chr8'], 'enrich_chr_9_14': ['chr9', 'chr10', 'chr11', 'chr12', 'chr13', 'chr14'], 'enrich_chr_16_20': ['chr16', 'chr17', 'chr18', 'chr19', 'chr20'], 'other': {'chr21', 'chrY', 'chr15', 'chr22', 'chrX'}} --- seqsum_plotting.py:964 (preprocess_seqsum_df_for_plotting) INFO ##\n", + "2024-03-01 10:33:22,658 - Adding extra columns for plotting --- seqsum_plotting.py:971 (preprocess_seqsum_df_for_plotting) INFO ##\n", + "2024-03-01 10:33:22,903 - /tmp/ipykernel_1011868/1748468773.py:16: FutureWarning: The default of observed=False is deprecated and will be changed to True in a future version of pandas. Pass observed=False to retain current behavior or observed=True to adopt the future default and silence this warning.\n", + " num_sequenced_bps_per_group = dict(partial_seqsum_df.groupby(\"group\")[\"cum_nb_seq_bps_per_group\"].max())\n", + " --- warnings.py:109 (_showwarnmsg) WARNING ##\n", + "2024-03-01 10:33:23,035 - Sorting and cleaning seqsummary file of shape (700614, 13) --- seqsum_plotting.py:939 (preprocess_seqsum_df_for_plotting) INFO ##\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Processing condition enrich_chr_16_20\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-03-01 10:33:23,805 - Adding previous gap duration to seqsummary --- seqsum_plotting.py:941 (preprocess_seqsum_df_for_plotting) INFO ##\n", + "2024-03-01 10:33:24,419 - Adding group column from NanoSim read id --- seqsum_plotting.py:951 (preprocess_seqsum_df_for_plotting) INFO ##\n", + "2024-03-01 10:33:26,596 - Splitting according to groups {'enrich_chr_1_8': ['chr1', 'chr2', 'chr3', 'chr4', 'chr5', 'chr6', 'chr7', 'chr8'], 'enrich_chr_9_14': ['chr9', 'chr10', 'chr11', 'chr12', 'chr13', 'chr14'], 'enrich_chr_16_20': ['chr16', 'chr17', 'chr18', 'chr19', 'chr20'], 'other': {'chr21', 'chrY', 'chr15', 'chrM', 'chr22', 'chrX'}} --- seqsum_plotting.py:964 (preprocess_seqsum_df_for_plotting) INFO ##\n", + "2024-03-01 10:33:26,675 - Adding extra columns for plotting --- seqsum_plotting.py:971 (preprocess_seqsum_df_for_plotting) INFO ##\n", + "2024-03-01 10:33:27,002 - /tmp/ipykernel_1011868/1748468773.py:16: FutureWarning: The default of observed=False is deprecated and will be changed to True in a future version of pandas. Pass observed=False to retain current behavior or observed=True to adopt the future default and silence this warning.\n", + " num_sequenced_bps_per_group = dict(partial_seqsum_df.groupby(\"group\")[\"cum_nb_seq_bps_per_group\"].max())\n", + " --- warnings.py:109 (_showwarnmsg) WARNING ##\n" + ] + } + ], + "source": [ + "\n", + "\n", + "num_sequenced_bps_per_group_per_condition = {}\n", + "\n", + "# targets of conditions are disjoint, so we can group by each of them for each condition (a condition is a selseq strategy applied to a subset of channels)\n", + "group_to_units = {cond[\"name\"]: cond[\"targets\"] for cond in readfish_conditions if cond[\"name\"] != \"control\"}\n", + "\n", + "for condition in readfish_conditions:\n", + " condition_name = condition[\"name\"]\n", + " print(f\"Processing condition {condition_name}\")\n", + " subchannels = channels_per_condition[condition_name]\n", + " \n", + " partial_seqsum_df = full_seqsum_df[full_seqsum_df[\"channel\"].isin([f\"ch{i}\" for i in subchannels])]\n", + " # partial_seqsum_df = full_seqsum_df[full_seqsum_df[\"channel\"].isin([i for i in subchannels])]\n", + " \n", + " partial_seqsum_df, group_column, chrom_column = preprocess_seqsum_df_for_plotting(partial_seqsum_df, group_to_units=copy.deepcopy(group_to_units))\n", + " \n", + " num_sequenced_bps_per_group = dict(partial_seqsum_df.groupby(\"group\")[\"cum_nb_seq_bps_per_group\"].max())\n", + " num_sequenced_bps_per_group_per_condition[condition_name] = num_sequenced_bps_per_group\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "# when having one condition/target per simulation run\n", + "\n", + "# group_to_units = {\n", + "# 'enrich_chr_1_8': ['chr1', 'chr2', 'chr3', 'chr4', 'chr5', 'chr6', 'chr7', 'chr8'],\n", + "# 'enrich_chr_9_14': ['chr9', 'chr10', 'chr11', 'chr12', 'chr13', 'chr14'],\n", + "# 'enrich_chr_16_20': ['chr16', 'chr17', 'chr18', 'chr19', 'chr20'],\n", + "# }\n", + "# seqsum_filenames_per_cond = {\n", + "# \"control\": Path(\"/is/cluster-test/fast/mmordig/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_fakemapper_control/simulator_run/sequencing_summary.txt\"),\n", + "# \"enrich_chr_1_8\": Path(\"/is/cluster-test/fast/mmordig/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_fakemapper_chr1to8/simulator_run/sequencing_summary.txt\"),\n", + "# \"enrich_chr_9_14\": Path(\"/is/cluster-test/fast/mmordig/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_fakemapper_chr9to14/simulator_run/sequencing_summary.txt\"),\n", + "# \"enrich_chr_16_20\": Path(\"/is/cluster-test/fast/mmordig/ont_project/runs/enrich_usecase/readfish_exp/results_readfishexp_fakemapper_chr16to20/simulator_run/sequencing_summary.txt\"),\n", + "# }\n", + "\n", + "# assert(all(x.exists() for x in seqsum_filenames_per_cond.values()))\n", + "\n", + "# num_sequenced_bps_per_group_per_condition = {}\n", + "# for (condition_name, seqsum_filename) in seqsum_filenames_per_cond.items():\n", + "# partial_seqsum_df, group_column = preprocess_seqsum_df_for_plotting(seqsum_filename, group_to_units=copy.deepcopy(group_to_units))\n", + " \n", + "# num_sequenced_bps_per_group = dict(partial_seqsum_df.groupby(\"group\")[\"cum_nb_seq_bps_per_group\"].max())\n", + "# num_sequenced_bps_per_group_per_condition[condition_name] = num_sequenced_bps_per_group" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "control : total: 2.67E+09 (1.0), enrich_chr_16_20: 3.35E+08, enrich_chr_1_8: 1.32E+09, enrich_chr_9_14: 6.60E+08, other: 3.54E+08\n", + "enrich_chr_1_8 : total: 2.12E+09 (1.26), enrich_chr_16_20: 6.32E+07, enrich_chr_1_8: 1.84E+09, enrich_chr_9_14: 1.33E+08, other: 8.11E+07\n", + "enrich_chr_9_14 : total: 1.94E+09 (1.38), enrich_chr_16_20: 9.16E+07, enrich_chr_1_8: 3.60E+08, enrich_chr_9_14: 1.35E+09, other: 1.36E+08\n", + "enrich_chr_16_20 : total: 1.75E+09 (1.53), enrich_chr_16_20: 9.05E+08, enrich_chr_1_8: 4.61E+08, enrich_chr_9_14: 2.40E+08, other: 1.43E+08\n" + ] + } + ], + "source": [ + "for (condition_name, num_sequenced_bps_per_group) in num_sequenced_bps_per_group_per_condition.items():\n", + " total_bps = sum(num_sequenced_bps_per_group.values())\n", + " throughput_reduction = sum(num_sequenced_bps_per_group_per_condition[\"control\"].values()) / sum(num_sequenced_bps_per_group.values())\n", + " \n", + " bps_per_target_str = \", \".join(f\"{group}: {num_bps:.2E}\" for (group, num_bps) in num_sequenced_bps_per_group.items())\n", + " print(f\"{condition_name:20}: total: {total_bps:.2E} ({throughput_reduction:.3}), {bps_per_target_str}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Absolute enrichment of target in condition enrich_chr_1_8 : 1.4\n", + "Relative enrichment of target in condition enrich_chr_1_8 : 1.75\n", + "Absolute enrichment of target in condition enrich_chr_9_14 : 2.04\n", + "Relative enrichment of target in condition enrich_chr_9_14 : 2.82\n", + "Absolute enrichment of target in condition enrich_chr_16_20 : 2.7\n", + "Relative enrichment of target in condition enrich_chr_16_20 : 4.14\n" + ] + } + ], + "source": [ + "from simreaduntil.simulator.gap_sampling.gap_sampler_per_window_until_blocked import dict_without_items\n", + "\n", + "absolute_enrichment = {\n", + " condition_name: seq_bps_per_target[condition_name] / num_sequenced_bps_per_group_per_condition[\"control\"][condition_name]\n", + " for (condition_name, seq_bps_per_target) in dict_without_items(num_sequenced_bps_per_group_per_condition, [\"control\"]).items()\n", + "}\n", + "\n", + "# relative composition when no selective sequencing is happening\n", + "composition_noselseq = {\n", + " \"enrich_chr_1_8\": 0.496,\n", + " \"enrich_chr_9_14\": 0.247,\n", + " \"enrich_chr_16_20\": 0.125\n", + "}\n", + "relative_enrichment = {\n", + " condition_name: (seq_bps_per_target[condition_name] / sum(seq_bps_per_target.values())) / composition_noselseq[condition_name]\n", + " for (condition_name, seq_bps_per_target) in dict_without_items(num_sequenced_bps_per_group_per_condition, [\"control\"]).items()\n", + "}\n", + "\n", + "for (condition_name, enrichment) in absolute_enrichment.items():\n", + " print(f\"Absolute enrichment of target in condition {condition_name:20}: {enrichment:.3}\")\n", + " print(f\"Relative enrichment of target in condition {condition_name:20}: {relative_enrichment[condition_name]:.3}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```\n", + "# aligned, fake mapper, accel10 (results_readfishexp_realreads_fakemapper_accel10)\n", + "control : total: 2.72E+09 (1.0 ), enrich_chr_16_20: 3.36E+08, enrich_chr_1_8: 1.35E+09, enrich_chr_9_14: 6.74E+08, other: 3.59E+08\n", + "enrich_chr_1_8 : total: 2.28E+09 (1.19), enrich_chr_16_20: 2.09E+07, enrich_chr_1_8: 2.20E+09, enrich_chr_9_14: 4.03E+07, other: 2.19E+07\n", + "enrich_chr_9_14 : total: 1.99E+09 (1.36), enrich_chr_16_20: 3.27E+07, enrich_chr_1_8: 1.31E+08, enrich_chr_9_14: 1.79E+09, other: 3.49E+07\n", + "enrich_chr_16_20 : total: 1.69E+09 (1.61), enrich_chr_16_20: 1.35E+09, enrich_chr_1_8: 1.92E+08, enrich_chr_9_14: 9.56E+07, other: 5.17E+07\n", + "Absolute enrichment of target in condition enrich_chr_1_8 : 1.63\n", + "Relative enrichment of target in condition enrich_chr_1_8 : 1.94\n", + "Absolute enrichment of target in condition enrich_chr_9_14 : 2.66\n", + "Relative enrichment of target in condition enrich_chr_9_14 : 3.65\n", + "Absolute enrichment of target in condition enrich_chr_16_20 : 4.0\n", + "Relative enrichment of target in condition enrich_chr_16_20 : 6.39\n", + "\n", + "# aligned, realmapper, accel5 (results_readfishexp_realreads_realmapper_accel5)\n", + "control : total: 2.72E+09 (1.0 ), enrich_chr_16_20: 3.38E+08, enrich_chr_1_8: 1.35E+09, enrich_chr_9_14: 6.70E+08, other: 3.65E+08\n", + "enrich_chr_1_8 : total: 2.31E+09 (1.18), enrich_chr_16_20: 3.01E+07, enrich_chr_1_8: 2.16E+09, enrich_chr_9_14: 6.96E+07, other: 4.90E+07\n", + "enrich_chr_9_14 : total: 2.03E+09 (1.34), enrich_chr_16_20: 4.51E+07, enrich_chr_1_8: 1.71E+08, enrich_chr_9_14: 1.72E+09, other: 1.01E+08\n", + "enrich_chr_16_20 : total: 1.73E+09 (1.58), enrich_chr_16_20: 1.28E+09, enrich_chr_1_8: 2.27E+08, enrich_chr_9_14: 1.31E+08, other: 9.46E+07\n", + "Absolute enrichment of target in condition enrich_chr_1_8 : 1.59\n", + "Relative enrichment of target in condition enrich_chr_1_8 : 1.89\n", + "Absolute enrichment of target in condition enrich_chr_9_14 : 2.56\n", + "Relative enrichment of target in condition enrich_chr_9_14 : 3.42\n", + "Absolute enrichment of target in condition enrich_chr_16_20 : 3.78\n", + "Relative enrichment of target in condition enrich_chr_16_20 : 5.9\n", + "\n", + "# with unaligned, realmapper accel10 (results_readfishexp_realreads_realmapper_withunaligned_accel10)\n", + "control : total: 2.67E+09 (1.0), enrich_chr_16_20: 3.32E+08, enrich_chr_1_8: 1.33E+09, enrich_chr_9_14: 6.57E+08, other: 3.55E+08\n", + "enrich_chr_1_8 : total: 2.32E+09 (1.15), enrich_chr_16_20: 2.26E+08, enrich_chr_1_8: 1.40E+09, enrich_chr_9_14: 4.52E+08, other: 2.44E+08\n", + "enrich_chr_9_14 : total: 2.31E+09 (1.16), enrich_chr_16_20: 2.40E+08, enrich_chr_1_8: 9.53E+08, enrich_chr_9_14: 8.48E+08, other: 2.68E+08\n", + "enrich_chr_16_20 : total: 2.27E+09 (1.18), enrich_chr_16_20: 4.90E+08, enrich_chr_1_8: 1.00E+09, enrich_chr_9_14: 5.00E+08, other: 2.74E+08\n", + "Absolute enrichment of target in condition enrich_chr_1_8 : 1.06\n", + "Relative enrichment of target in condition enrich_chr_1_8 : 1.22\n", + "Absolute enrichment of target in condition enrich_chr_9_14 : 1.29\n", + "Relative enrichment of target in condition enrich_chr_9_14 : 1.49\n", + "Absolute enrichment of target in condition enrich_chr_16_20 : 1.48\n", + "Relative enrichment of target in condition enrich_chr_16_20 : 1.73\n", + "\n", + "# with unaligned, realmapper accel2 (results_readfishexp_realreads_realmapper_withunaligned_accel2)\n", + "control : total: 2.67E+09 (1.0), enrich_chr_16_20: 3.34E+08, enrich_chr_1_8: 1.32E+09, enrich_chr_9_14: 6.57E+08, other: 3.52E+08\n", + "enrich_chr_1_8 : total: 2.15E+09 (1.24), enrich_chr_16_20: 6.55E+07, enrich_chr_1_8: 1.86E+09, enrich_chr_9_14: 1.37E+08, other: 8.44E+07\n", + "enrich_chr_9_14 : total: 1.92E+09 (1.39), enrich_chr_16_20: 9.18E+07, enrich_chr_1_8: 3.61E+08, enrich_chr_9_14: 1.34E+09, other: 1.33E+08\n", + "enrich_chr_16_20 : total: 1.74E+09 (1.54), enrich_chr_16_20: 8.91E+08, enrich_chr_1_8: 4.61E+08, enrich_chr_9_14: 2.40E+08, other: 1.44E+08\n", + "Absolute enrichment of target in condition enrich_chr_1_8 : 1.41\n", + "Relative enrichment of target in condition enrich_chr_1_8 : 1.75\n", + "Absolute enrichment of target in condition enrich_chr_9_14 : 2.03\n", + "Relative enrichment of target in condition enrich_chr_9_14 : 2.81\n", + "Absolute enrichment of target in condition enrich_chr_16_20 : 2.67\n", + "Relative enrichment of target in condition enrich_chr_16_20 : 4.11\n", + "\n", + "# with unaligned, realmapper, accel3 (results_readfishexp_realreads_realmapper_withunaligned_accel3_longer)\n", + "control : total: 2.65E+09 (1.0), enrich_chr_16_20: 3.28E+08, enrich_chr_1_8: 1.32E+09, enrich_chr_9_14: 6.47E+08, other: 3.49E+08\n", + "enrich_chr_1_8 : total: 2.13E+09 (1.24), enrich_chr_16_20: 6.44E+07, enrich_chr_1_8: 1.85E+09, enrich_chr_9_14: 1.33E+08, other: 8.19E+07\n", + "enrich_chr_9_14 : total: 1.92E+09 (1.38), enrich_chr_16_20: 9.07E+07, enrich_chr_1_8: 3.55E+08, enrich_chr_9_14: 1.34E+09, other: 1.33E+08\n", + "enrich_chr_16_20 : total: 1.73E+09 (1.53), enrich_chr_16_20: 8.92E+08, enrich_chr_1_8: 4.56E+08, enrich_chr_9_14: 2.39E+08, other: 1.42E+08\n", + "Absolute enrichment of target in condition enrich_chr_1_8 : 1.4\n", + "Relative enrichment of target in condition enrich_chr_1_8 : 1.75\n", + "Absolute enrichment of target in condition enrich_chr_9_14 : 2.07\n", + "Relative enrichment of target in condition enrich_chr_9_14 : 2.82\n", + "Absolute enrichment of target in condition enrich_chr_16_20 : 2.72\n", + "Relative enrichment of target in condition enrich_chr_16_20 : 4.13\n", + "\n", + "# with unaligned, realmapper, accel5 (results_readfishexp_realreads_realmapper_withunaligned_accel5_longer)\n", + "control : total: 2.67E+09 (1.0), enrich_chr_16_20: 3.35E+08, enrich_chr_1_8: 1.32E+09, enrich_chr_9_14: 6.60E+08, other: 3.54E+08\n", + "enrich_chr_1_8 : total: 2.12E+09 (1.26), enrich_chr_16_20: 6.32E+07, enrich_chr_1_8: 1.84E+09, enrich_chr_9_14: 1.33E+08, other: 8.11E+07\n", + "enrich_chr_9_14 : total: 1.94E+09 (1.38), enrich_chr_16_20: 9.16E+07, enrich_chr_1_8: 3.60E+08, enrich_chr_9_14: 1.35E+09, other: 1.36E+08\n", + "enrich_chr_16_20 : total: 1.75E+09 (1.53), enrich_chr_16_20: 9.05E+08, enrich_chr_1_8: 4.61E+08, enrich_chr_9_14: 2.40E+08, other: 1.43E+08\n", + "Absolute enrichment of target in condition enrich_chr_1_8 : 1.4\n", + "Relative enrichment of target in condition enrich_chr_1_8 : 1.75\n", + "Absolute enrichment of target in condition enrich_chr_9_14 : 2.04\n", + "Relative enrichment of target in condition enrich_chr_9_14 : 2.82\n", + "Absolute enrichment of target in condition enrich_chr_16_20 : 2.7\n", + "Relative enrichment of target in condition enrich_chr_16_20 : 4.14\n", + "\n", + "\n", + "## unused \n", + "\n", + "# short: with unaligned, realmapper accel5 (results_readfishexp_realreads_realmapper_withunaligned_accel5)\n", + "control : total: 1.82E+09 (1.0), enrich_chr_16_20: 2.23E+08, enrich_chr_1_8: 9.07E+08, enrich_chr_9_14: 4.47E+08, other: 2.39E+08\n", + "enrich_chr_1_8 : total: 1.54E+09 (1.18), enrich_chr_16_20: 4.62E+07, enrich_chr_1_8: 1.34E+09, enrich_chr_9_14: 9.68E+07, other: 5.85E+07\n", + "enrich_chr_9_14 : total: 1.36E+09 (1.33), enrich_chr_16_20: 6.51E+07, enrich_chr_1_8: 2.55E+08, enrich_chr_9_14: 9.48E+08, other: 9.54E+07\n", + "enrich_chr_16_20 : total: 1.27E+09 (1.43), enrich_chr_16_20: 6.53E+08, enrich_chr_1_8: 3.34E+08, enrich_chr_9_14: 1.74E+08, other: 1.05E+08\n", + "Absolute enrichment of target in condition enrich_chr_1_8 : 1.47\n", + "Relative enrichment of target in condition enrich_chr_1_8 : 1.75\n", + "Absolute enrichment of target in condition enrich_chr_9_14 : 2.12\n", + "Relative enrichment of target in condition enrich_chr_9_14 : 2.82\n", + "Absolute enrichment of target in condition enrich_chr_16_20 : 2.93\n", + "Relative enrichment of target in condition enrich_chr_16_20 : 4.13\n", + "\n", + "# for constant gap sampler (results_readfishexp_realreads_realmapper_withunaligned_constantgapsampler_accel5)\n", + "control : total: 2.27E+09 (1.0), enrich_chr_16_20: 2.82E+08, enrich_chr_1_8: 1.12E+09, enrich_chr_9_14: 5.63E+08, other: 3.00E+08\n", + "enrich_chr_1_8 : total: 2.21E+09 (1.03), enrich_chr_16_20: 6.70E+07, enrich_chr_1_8: 1.92E+09, enrich_chr_9_14: 1.40E+08, other: 8.49E+07\n", + "enrich_chr_9_14 : total: 2.15E+09 (1.05), enrich_chr_16_20: 1.03E+08, enrich_chr_1_8: 4.05E+08, enrich_chr_9_14: 1.49E+09, other: 1.51E+08\n", + "enrich_chr_16_20 : total: 2.08E+09 (1.09), enrich_chr_16_20: 1.07E+09, enrich_chr_1_8: 5.56E+08, enrich_chr_9_14: 2.88E+08, other: 1.72E+08\n", + "Absolute enrichment of target in condition enrich_chr_1_8 : 1.71\n", + "Relative enrichment of target in condition enrich_chr_1_8 : 1.75\n", + "Absolute enrichment of target in condition enrich_chr_9_14 : 2.65\n", + "Relative enrichment of target in condition enrich_chr_9_14 : 2.81\n", + "Absolute enrichment of target in condition enrich_chr_16_20 : 3.78\n", + "Relative enrichment of target in condition enrich_chr_16_20 : 4.1\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# yield-corrected\n", + "1.49 * 1.23/1.24,\\\n", + "2.2 * 1.44/1.89, \\\n", + "3.04 * 1.7/2.84" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# yield-corrected\n", + "# 1.59 * 1.15/1.24, \\\n", + "# 2.38 * 1.32/1.89, \\\n", + "# 3.23 * 1.56/2.84" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "ont_project_venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/usecases/configs/enrich_usecase/chr202122_run/sampler_per_window/config.toml b/usecases/configs/enrich_usecase/chr202122_run/sampler_per_window/config.toml index bd1024a..f50cd15 100644 --- a/usecases/configs/enrich_usecase/chr202122_run/sampler_per_window/config.toml +++ b/usecases/configs/enrich_usecase/chr202122_run/sampler_per_window/config.toml @@ -9,10 +9,11 @@ run_duration = 3600.0 ################################################# # reads_file = "nanosim_reads/perfect_reads_seed1_aligned_reads.fasta" +reads_len_range = [12000, 16000] ref_genome_path = "data/chm13v2.0_normalized3chroms.fa.gz" sim_params_file = "sim_params.dill" -rotating = true -mux_scan_period = 240 # 90 minutes +rotating_writeout = true +mux_scan_period = 240 # seconds mux_scan_duration = 40 # seconds use_grpc = true diff --git a/usecases/configs/enrich_usecase/chr202122_run/sampler_per_window/readfish_enrich_chr20.toml b/usecases/configs/enrich_usecase/chr202122_run/sampler_per_window/readfish_enrich_chr20.toml index 1ce1482..3909139 100644 --- a/usecases/configs/enrich_usecase/chr202122_run/sampler_per_window/readfish_enrich_chr20.toml +++ b/usecases/configs/enrich_usecase/chr202122_run/sampler_per_window/readfish_enrich_chr20.toml @@ -15,6 +15,6 @@ targets = ["chr20"] single_on = "stop_receiving" multi_on = "stop_receiving" single_off = "unblock" -multi_off = "unblock" +multi_off = "proceed" no_seq = "proceed" # unclear what it is, does not seem to be used no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel1/README.md b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel1/README.md new file mode 100644 index 0000000..f5391ad --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel1/README.md @@ -0,0 +1 @@ +Used to enrich chr20, 21 from the human genome \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel1/config.toml b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel1/config.toml new file mode 100644 index 0000000..d921479 --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel1/config.toml @@ -0,0 +1,29 @@ +run_dir = "simulator_run" # where reads, logs, pafs etc. will be written to +n_channels = 512 +acceleration_factor = 1 +run_duration = 2000 + +################################################# +# Optional arguments +################################################# + +# reads_file = "nanosim_reads/perfect_reads_seed1_aligned_reads.fasta" +ref_genome_path = "data/chm13v2.0_normalized.fa.gz" +sim_params_file = "sim_params.dill" +rotating_writeout = true +# mux_scan_period = 5400 # 90 minutes +# mux_scan_duration = 100 # seconds + +# readfish params +readfish_config_file = "configs/readfish_enrich_chr2021.toml" +# readfish_method = "unblock_all" +readfish_method = "targeted_seq" +# readfish_method = "control" + +################################################# +# Parameter extraction arguments +################################################# +seqsum_param_extr_file = "data/20190809_zymo_seqsum.txt" +n_channels_full = 512 +# gap_sampler_type = "sampler_per_window" +gap_sampler_type = "sampler_per_rolling_window_channel" \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel1/readfish_enrich_chr2021.toml b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel1/readfish_enrich_chr2021.toml new file mode 100644 index 0000000..727125a --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel1/readfish_enrich_chr2021.toml @@ -0,0 +1,21 @@ +[caller_settings] +config_name = "ignored" +host = "ignored" +port = 9999 + +[conditions] +# reference = "data/chm13v2.0_normalized.mmi" +reference = "fake_mapper" + +[conditions.0] +name = "enrich_chr_20_21" +control = false +min_chunks = 0 # no decision made whenever <= min_chunks have been received from a read +max_chunks = 12 +targets = ["chr20", "chr21"] +single_on = "stop_receiving" +multi_on = "stop_receiving" +single_off = "unblock" +multi_off = "proceed" +no_seq = "proceed" # unclear what it is, does not seem to be used +no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel10/README.md b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel10/README.md new file mode 100644 index 0000000..f5391ad --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel10/README.md @@ -0,0 +1 @@ +Used to enrich chr20, 21 from the human genome \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel10/config.toml b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel10/config.toml new file mode 100644 index 0000000..4a1220d --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel10/config.toml @@ -0,0 +1,29 @@ +run_dir = "simulator_run" # where reads, logs, pafs etc. will be written to +n_channels = 512 +acceleration_factor = 10 +run_duration = 20000 + +################################################# +# Optional arguments +################################################# + +# reads_file = "nanosim_reads/perfect_reads_seed1_aligned_reads.fasta" +ref_genome_path = "data/chm13v2.0_normalized.fa.gz" +sim_params_file = "sim_params.dill" +rotating_writeout = true +# mux_scan_period = 5400 # 90 minutes +# mux_scan_duration = 100 # seconds + +# readfish params +readfish_config_file = "configs/readfish_enrich_chr2021.toml" +# readfish_method = "unblock_all" +readfish_method = "targeted_seq" +# readfish_method = "control" + +################################################# +# Parameter extraction arguments +################################################# +seqsum_param_extr_file = "data/20190809_zymo_seqsum.txt" +n_channels_full = 512 +# gap_sampler_type = "sampler_per_window" +gap_sampler_type = "sampler_per_rolling_window_channel" \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel10/readfish_enrich_chr2021.toml b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel10/readfish_enrich_chr2021.toml new file mode 100644 index 0000000..727125a --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel10/readfish_enrich_chr2021.toml @@ -0,0 +1,21 @@ +[caller_settings] +config_name = "ignored" +host = "ignored" +port = 9999 + +[conditions] +# reference = "data/chm13v2.0_normalized.mmi" +reference = "fake_mapper" + +[conditions.0] +name = "enrich_chr_20_21" +control = false +min_chunks = 0 # no decision made whenever <= min_chunks have been received from a read +max_chunks = 12 +targets = ["chr20", "chr21"] +single_on = "stop_receiving" +multi_on = "stop_receiving" +single_off = "unblock" +multi_off = "proceed" +no_seq = "proceed" # unclear what it is, does not seem to be used +no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel3/README.md b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel3/README.md new file mode 100644 index 0000000..f5391ad --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel3/README.md @@ -0,0 +1 @@ +Used to enrich chr20, 21 from the human genome \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel3/config.toml b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel3/config.toml new file mode 100644 index 0000000..4183490 --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel3/config.toml @@ -0,0 +1,29 @@ +run_dir = "simulator_run" # where reads, logs, pafs etc. will be written to +n_channels = 512 +acceleration_factor = 3 +run_duration = 10000 + +################################################# +# Optional arguments +################################################# + +# reads_file = "nanosim_reads/perfect_reads_seed1_aligned_reads.fasta" +ref_genome_path = "data/chm13v2.0_normalized.fa.gz" +sim_params_file = "sim_params.dill" +rotating_writeout = true +# mux_scan_period = 5400 # 90 minutes +# mux_scan_duration = 100 # seconds + +# readfish params +readfish_config_file = "configs/readfish_enrich_chr2021.toml" +# readfish_method = "unblock_all" +readfish_method = "targeted_seq" +# readfish_method = "control" + +################################################# +# Parameter extraction arguments +################################################# +seqsum_param_extr_file = "data/20190809_zymo_seqsum.txt" +n_channels_full = 512 +# gap_sampler_type = "sampler_per_window" +gap_sampler_type = "sampler_per_rolling_window_channel" \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel3/readfish_enrich_chr2021.toml b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel3/readfish_enrich_chr2021.toml new file mode 100644 index 0000000..727125a --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel3/readfish_enrich_chr2021.toml @@ -0,0 +1,21 @@ +[caller_settings] +config_name = "ignored" +host = "ignored" +port = 9999 + +[conditions] +# reference = "data/chm13v2.0_normalized.mmi" +reference = "fake_mapper" + +[conditions.0] +name = "enrich_chr_20_21" +control = false +min_chunks = 0 # no decision made whenever <= min_chunks have been received from a read +max_chunks = 12 +targets = ["chr20", "chr21"] +single_on = "stop_receiving" +multi_on = "stop_receiving" +single_off = "unblock" +multi_off = "proceed" +no_seq = "proceed" # unclear what it is, does not seem to be used +no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel5/README.md b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel5/README.md new file mode 100644 index 0000000..f5391ad --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel5/README.md @@ -0,0 +1 @@ +Used to enrich chr20, 21 from the human genome \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel5/config.toml b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel5/config.toml new file mode 100644 index 0000000..effacef --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel5/config.toml @@ -0,0 +1,29 @@ +run_dir = "simulator_run" # where reads, logs, pafs etc. will be written to +n_channels = 512 +acceleration_factor = 5 +run_duration = 10000 + +################################################# +# Optional arguments +################################################# + +# reads_file = "nanosim_reads/perfect_reads_seed1_aligned_reads.fasta" +ref_genome_path = "data/chm13v2.0_normalized.fa.gz" +sim_params_file = "sim_params.dill" +rotating_writeout = true +# mux_scan_period = 5400 # 90 minutes +# mux_scan_duration = 100 # seconds + +# readfish params +readfish_config_file = "configs/readfish_enrich_chr2021.toml" +# readfish_method = "unblock_all" +readfish_method = "targeted_seq" +# readfish_method = "control" + +################################################# +# Parameter extraction arguments +################################################# +seqsum_param_extr_file = "data/20190809_zymo_seqsum.txt" +n_channels_full = 512 +# gap_sampler_type = "sampler_per_window" +gap_sampler_type = "sampler_per_rolling_window_channel" \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel5/readfish_enrich_chr2021.toml b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel5/readfish_enrich_chr2021.toml new file mode 100644 index 0000000..727125a --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel5/readfish_enrich_chr2021.toml @@ -0,0 +1,21 @@ +[caller_settings] +config_name = "ignored" +host = "ignored" +port = 9999 + +[conditions] +# reference = "data/chm13v2.0_normalized.mmi" +reference = "fake_mapper" + +[conditions.0] +name = "enrich_chr_20_21" +control = false +min_chunks = 0 # no decision made whenever <= min_chunks have been received from a read +max_chunks = 12 +targets = ["chr20", "chr21"] +single_on = "stop_receiving" +multi_on = "stop_receiving" +single_off = "unblock" +multi_off = "proceed" +no_seq = "proceed" # unclear what it is, does not seem to be used +no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel7.5/README.md b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel7.5/README.md new file mode 100644 index 0000000..f5391ad --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel7.5/README.md @@ -0,0 +1 @@ +Used to enrich chr20, 21 from the human genome \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel7.5/config.toml b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel7.5/config.toml new file mode 100644 index 0000000..ed36234 --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel7.5/config.toml @@ -0,0 +1,29 @@ +run_dir = "simulator_run" # where reads, logs, pafs etc. will be written to +n_channels = 512 +acceleration_factor = 7.5 +run_duration = 2000 + +################################################# +# Optional arguments +################################################# + +# reads_file = "nanosim_reads/perfect_reads_seed1_aligned_reads.fasta" +ref_genome_path = "data/chm13v2.0_normalized.fa.gz" +sim_params_file = "sim_params.dill" +rotating_writeout = true +# mux_scan_period = 5400 # 90 minutes +# mux_scan_duration = 100 # seconds + +# readfish params +readfish_config_file = "configs/readfish_enrich_chr2021.toml" +# readfish_method = "unblock_all" +readfish_method = "targeted_seq" +# readfish_method = "control" + +################################################# +# Parameter extraction arguments +################################################# +seqsum_param_extr_file = "data/20190809_zymo_seqsum.txt" +n_channels_full = 512 +# gap_sampler_type = "sampler_per_window" +gap_sampler_type = "sampler_per_rolling_window_channel" \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel7.5/readfish_enrich_chr2021.toml b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel7.5/readfish_enrich_chr2021.toml new file mode 100644 index 0000000..727125a --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/accelerations/config_accel7.5/readfish_enrich_chr2021.toml @@ -0,0 +1,21 @@ +[caller_settings] +config_name = "ignored" +host = "ignored" +port = 9999 + +[conditions] +# reference = "data/chm13v2.0_normalized.mmi" +reference = "fake_mapper" + +[conditions.0] +name = "enrich_chr_20_21" +control = false +min_chunks = 0 # no decision made whenever <= min_chunks have been received from a read +max_chunks = 12 +targets = ["chr20", "chr21"] +single_on = "stop_receiving" +multi_on = "stop_receiving" +single_off = "unblock" +multi_off = "proceed" +no_seq = "proceed" # unclear what it is, does not seem to be used +no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp/config.toml b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp/config.toml new file mode 100644 index 0000000..87c581a --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp/config.toml @@ -0,0 +1,30 @@ +run_dir = "simulator_run" # where reads, logs, pafs etc. will be written to +n_channels = 512 +acceleration_factor = 2 +run_duration = 120000 + +################################################# +# Optional arguments +################################################# + +# reads_file = "nanosim_reads/perfect_reads_seed1_aligned_reads.fasta" +reads_len_range = [12000, 16000] +ref_genome_path = "data/chm13v2.0_normalized.fa.gz" +sim_params_file = "sim_params.dill" +rotating_writeout = true +# mux_scan_period = 5400 # 90 minutes +# mux_scan_duration = 100 # seconds + +# readfish params +readfish_config_file = "configs/readfish_enrich_chr1620.toml" +# readfish_method = "unblock_all" +readfish_method = "targeted_seq" +# readfish_method = "control" + +################################################# +# Parameter extraction arguments +################################################# +seqsum_param_extr_file = "data/20190809_zymo_seqsum.txt" +n_channels_full = 512 +# gap_sampler_type = "sampler_per_window" +gap_sampler_type = "sampler_per_rolling_window_channel" \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp/readfish_enrich_chr1620.toml b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp/readfish_enrich_chr1620.toml new file mode 100644 index 0000000..5703403 --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp/readfish_enrich_chr1620.toml @@ -0,0 +1,21 @@ +[caller_settings] +config_name = "ignored" +host = "ignored" +port = 9999 + +[conditions] +reference = "data/chm13v2.0_normalized.mmi" +# reference = "fake_mapper" + +[conditions.0] +name = "enrich_chr_16_20" +control = false +min_chunks = 0 # no decision made whenever <= min_chunks have been received from a read +max_chunks = 12 +targets = ["chr16", "chr17", "chr18", "chr19", "chr20"] +single_on = "stop_receiving" +multi_on = "stop_receiving" +single_off = "unblock" +multi_off = "proceed" +no_seq = "proceed" # unclear what it is, does not seem to be used +no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_chr1to8_fakemapper/config.toml b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_chr1to8_fakemapper/config.toml new file mode 100644 index 0000000..532e4c0 --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_chr1to8_fakemapper/config.toml @@ -0,0 +1,30 @@ +run_dir = "simulator_run" # where reads, logs, pafs etc. will be written to +n_channels = 512 +acceleration_factor = 10 +run_duration = 120000 + +################################################# +# Optional arguments +################################################# + +# reads_file = "nanosim_reads/perfect_reads_seed1_aligned_reads.fasta" +reads_len_range = [12000, 16000] +ref_genome_path = "data/chm13v2.0_normalized.fa.gz" +sim_params_file = "sim_params.dill" +rotating_writeout = true +# mux_scan_period = 5400 # 90 minutes +# mux_scan_duration = 100 # seconds + +# readfish params +readfish_config_file = "configs/readfish_enrich_chr1to8.toml" +# readfish_method = "unblock_all" +readfish_method = "targeted_seq" +# readfish_method = "control" + +################################################# +# Parameter extraction arguments +################################################# +seqsum_param_extr_file = "data/20190809_zymo_seqsum.txt" +n_channels_full = 512 +# gap_sampler_type = "sampler_per_window" +gap_sampler_type = "sampler_per_rolling_window_channel" \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_chr1to8_fakemapper/readfish_enrich_chr1to8.toml b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_chr1to8_fakemapper/readfish_enrich_chr1to8.toml new file mode 100644 index 0000000..e875fd7 --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_chr1to8_fakemapper/readfish_enrich_chr1to8.toml @@ -0,0 +1,21 @@ +[caller_settings] +config_name = "ignored" +host = "ignored" +port = 9999 + +[conditions] +# reference = "data/chm13v2.0_normalized.mmi" +reference = "fake_mapper" + +[conditions.0] +name = "enrich_chr_1_8" +control = false +min_chunks = 0 # no decision made whenever <= min_chunks have been received from a read +max_chunks = 12 +targets = ["chr1", "chr2", "chr3", "chr4", "chr5", "chr6", "chr7", "chr8"] +single_on = "stop_receiving" +multi_on = "stop_receiving" +single_off = "unblock" +multi_off = "proceed" +no_seq = "proceed" # unclear what it is, does not seem to be used +no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_chr9to14_fakemapper/config.toml b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_chr9to14_fakemapper/config.toml new file mode 100644 index 0000000..5e257fb --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_chr9to14_fakemapper/config.toml @@ -0,0 +1,30 @@ +run_dir = "simulator_run" # where reads, logs, pafs etc. will be written to +n_channels = 512 +acceleration_factor = 10 +run_duration = 120000 + +################################################# +# Optional arguments +################################################# + +# reads_file = "nanosim_reads/perfect_reads_seed1_aligned_reads.fasta" +reads_len_range = [12000, 16000] +ref_genome_path = "data/chm13v2.0_normalized.fa.gz" +sim_params_file = "sim_params.dill" +rotating_writeout = true +# mux_scan_period = 5400 # 90 minutes +# mux_scan_duration = 100 # seconds + +# readfish params +readfish_config_file = "configs/readfish_enrich_chr9to14.toml" +# readfish_method = "unblock_all" +readfish_method = "targeted_seq" +# readfish_method = "control" + +################################################# +# Parameter extraction arguments +################################################# +seqsum_param_extr_file = "data/20190809_zymo_seqsum.txt" +n_channels_full = 512 +# gap_sampler_type = "sampler_per_window" +gap_sampler_type = "sampler_per_rolling_window_channel" \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_chr9to14_fakemapper/readfish_enrich_chr9to14.toml b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_chr9to14_fakemapper/readfish_enrich_chr9to14.toml new file mode 100644 index 0000000..b05315c --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_chr9to14_fakemapper/readfish_enrich_chr9to14.toml @@ -0,0 +1,21 @@ +[caller_settings] +config_name = "ignored" +host = "ignored" +port = 9999 + +[conditions] +# reference = "data/chm13v2.0_normalized.mmi" +reference = "fake_mapper" + +[conditions.0] +name = "enrich_chr_9_14" +control = false +min_chunks = 0 # no decision made whenever <= min_chunks have been received from a read +max_chunks = 12 +targets = ["chr9", "chr10", "chr11", "chr12", "chr13", "chr14"] +single_on = "stop_receiving" +multi_on = "stop_receiving" +single_off = "unblock" +multi_off = "proceed" +no_seq = "proceed" # unclear what it is, does not seem to be used +no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_control/config.toml b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_control/config.toml new file mode 100644 index 0000000..799aea1 --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_control/config.toml @@ -0,0 +1,30 @@ +run_dir = "simulator_run" # where reads, logs, pafs etc. will be written to +n_channels = 512 +acceleration_factor = 2 +run_duration = 120000 + +################################################# +# Optional arguments +################################################# + +# reads_file = "nanosim_reads/perfect_reads_seed1_aligned_reads.fasta" +reads_len_range = [12000, 16000] +ref_genome_path = "data/chm13v2.0_normalized.fa.gz" +sim_params_file = "sim_params.dill" +rotating_writeout = true +# mux_scan_period = 5400 # 90 minutes +# mux_scan_duration = 100 # seconds + +# readfish params +readfish_config_file = "configs/readfish_enrich_chr1620.toml" +# readfish_method = "unblock_all" +# readfish_method = "targeted_seq" +readfish_method = "control" + +################################################# +# Parameter extraction arguments +################################################# +seqsum_param_extr_file = "data/20190809_zymo_seqsum.txt" +n_channels_full = 512 +# gap_sampler_type = "sampler_per_window" +gap_sampler_type = "sampler_per_rolling_window_channel" \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_control/readfish_enrich_chr1620.toml b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_control/readfish_enrich_chr1620.toml new file mode 100644 index 0000000..5703403 --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_control/readfish_enrich_chr1620.toml @@ -0,0 +1,21 @@ +[caller_settings] +config_name = "ignored" +host = "ignored" +port = 9999 + +[conditions] +reference = "data/chm13v2.0_normalized.mmi" +# reference = "fake_mapper" + +[conditions.0] +name = "enrich_chr_16_20" +control = false +min_chunks = 0 # no decision made whenever <= min_chunks have been received from a read +max_chunks = 12 +targets = ["chr16", "chr17", "chr18", "chr19", "chr20"] +single_on = "stop_receiving" +multi_on = "stop_receiving" +single_off = "unblock" +multi_off = "proceed" +no_seq = "proceed" # unclear what it is, does not seem to be used +no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_fakemapper/config.toml b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_fakemapper/config.toml new file mode 100644 index 0000000..a73bdbe --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_fakemapper/config.toml @@ -0,0 +1,30 @@ +run_dir = "simulator_run" # where reads, logs, pafs etc. will be written to +n_channels = 512 +acceleration_factor = 10 +run_duration = 120000 + +################################################# +# Optional arguments +################################################# + +# reads_file = "nanosim_reads/perfect_reads_seed1_aligned_reads.fasta" +reads_len_range = [12000, 16000] +ref_genome_path = "data/chm13v2.0_normalized.fa.gz" +sim_params_file = "sim_params.dill" +rotating_writeout = true +# mux_scan_period = 5400 # 90 minutes +# mux_scan_duration = 100 # seconds + +# readfish params +readfish_config_file = "configs/readfish_enrich_chr1620.toml" +# readfish_method = "unblock_all" +readfish_method = "targeted_seq" +# readfish_method = "control" + +################################################# +# Parameter extraction arguments +################################################# +seqsum_param_extr_file = "data/20190809_zymo_seqsum.txt" +n_channels_full = 512 +# gap_sampler_type = "sampler_per_window" +gap_sampler_type = "sampler_per_rolling_window_channel" \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_fakemapper/readfish_enrich_chr1620.toml b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_fakemapper/readfish_enrich_chr1620.toml new file mode 100644 index 0000000..1001f7a --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_fakemapper/readfish_enrich_chr1620.toml @@ -0,0 +1,21 @@ +[caller_settings] +config_name = "ignored" +host = "ignored" +port = 9999 + +[conditions] +# reference = "data/chm13v2.0_normalized.mmi" +reference = "fake_mapper" + +[conditions.0] +name = "enrich_chr_16_20" +control = false +min_chunks = 0 # no decision made whenever <= min_chunks have been received from a read +max_chunks = 12 +targets = ["chr16", "chr17", "chr18", "chr19", "chr20"] +single_on = "stop_receiving" +multi_on = "stop_receiving" +single_off = "unblock" +multi_off = "proceed" +no_seq = "proceed" # unclear what it is, does not seem to be used +no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_fakemapper_control/config.toml b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_fakemapper_control/config.toml new file mode 100644 index 0000000..7b7cc26 --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_fakemapper_control/config.toml @@ -0,0 +1,30 @@ +run_dir = "simulator_run" # where reads, logs, pafs etc. will be written to +n_channels = 512 +acceleration_factor = 10 +run_duration = 120000 + +################################################# +# Optional arguments +################################################# + +# reads_file = "nanosim_reads/perfect_reads_seed1_aligned_reads.fasta" +reads_len_range = [12000, 16000] +ref_genome_path = "data/chm13v2.0_normalized.fa.gz" +sim_params_file = "sim_params.dill" +rotating_writeout = true +# mux_scan_period = 5400 # 90 minutes +# mux_scan_duration = 100 # seconds + +# readfish params +readfish_config_file = "configs/readfish_enrich_chr1620.toml" +# readfish_method = "unblock_all" +# readfish_method = "targeted_seq" +readfish_method = "control" + +################################################# +# Parameter extraction arguments +################################################# +seqsum_param_extr_file = "data/20190809_zymo_seqsum.txt" +n_channels_full = 512 +# gap_sampler_type = "sampler_per_window" +gap_sampler_type = "sampler_per_rolling_window_channel" \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_fakemapper_control/readfish_enrich_chr1620.toml b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_fakemapper_control/readfish_enrich_chr1620.toml new file mode 100644 index 0000000..1001f7a --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_fakemapper_control/readfish_enrich_chr1620.toml @@ -0,0 +1,21 @@ +[caller_settings] +config_name = "ignored" +host = "ignored" +port = 9999 + +[conditions] +# reference = "data/chm13v2.0_normalized.mmi" +reference = "fake_mapper" + +[conditions.0] +name = "enrich_chr_16_20" +control = false +min_chunks = 0 # no decision made whenever <= min_chunks have been received from a read +max_chunks = 12 +targets = ["chr16", "chr17", "chr18", "chr19", "chr20"] +single_on = "stop_receiving" +multi_on = "stop_receiving" +single_off = "unblock" +multi_off = "proceed" +no_seq = "proceed" # unclear what it is, does not seem to be used +no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_realreads/config.toml b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_realreads/config.toml new file mode 100644 index 0000000..64d3652 --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_realreads/config.toml @@ -0,0 +1,38 @@ +run_dir = "simulator_run" # where reads, logs, pafs etc. will be written to +n_channels = 512 +acceleration_factor = 5 +run_duration = 108000 +# acceleration_factor = 5 +# run_duration = 120000 +# run_duration = 72000 +# run_duration = 720 # todo +# run_duration = 200 # todo + +################################################# +# Optional arguments +################################################# + +# reads_file = "nanosim_reads/perfect_reads_seed1_aligned_reads.fasta" +reads_file = "data/nanosim_reads/human_genome_med15000_alignedrate2" +# reads_file = "data/nanosim_reads/human_genome_med15000" +# reads_file = "data/nanosim_reads/human_genome_few" #todo +# reads_len_range = [12000, 16000] +ref_genome_path = "data/chm13v2.0_normalized.fa.gz" +sim_params_file = "sim_params.dill" # todo +rotating_writeout = true +# mux_scan_period = 5400 # 90 minutes +# mux_scan_duration = 100 # seconds + +# readfish params +readfish_config_file = "configs/readfish_enrich_per_quadrant.toml" +# readfish_method = "unblock_all" +readfish_method = "targeted_seq" +# readfish_method = "control" + +################################################# +# Parameter extraction arguments +################################################# +seqsum_param_extr_file = "data/20190809_zymo_seqsum.txt" +n_channels_full = 512 +# gap_sampler_type = "sampler_per_window" +gap_sampler_type = "sampler_per_rolling_window_channel" \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_realreads/readfish_enrich_per_quadrant.toml b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_realreads/readfish_enrich_per_quadrant.toml new file mode 100644 index 0000000..2fa0382 --- /dev/null +++ b/usecases/configs/enrich_usecase/full_genome_run/readfish_exp/config_readfishexp_realreads/readfish_enrich_per_quadrant.toml @@ -0,0 +1,60 @@ +[caller_settings] +config_name = "ignored" +host = "ignored" +port = 9999 + +[conditions] +reference = "data/chm13v2.0_normalized.mmi" +# reference = "fake_mapper" + +[conditions.0] +name = "control" +control = true +min_chunks = 0 # no decision made whenever <= min_chunks have been received from a read +max_chunks = 12 +targets = [] +single_on = "stop_receiving" +multi_on = "stop_receiving" +single_off = "unblock" +multi_off = "proceed" +no_seq = "proceed" # unclear what it is, does not seem to be used +no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected + +[conditions.1] +name = "enrich_chr_1_8" +control = false +min_chunks = 0 # no decision made whenever <= min_chunks have been received from a read +max_chunks = 12 +targets = ["chr1", "chr2", "chr3", "chr4", "chr5", "chr6", "chr7", "chr8"] +single_on = "stop_receiving" +multi_on = "stop_receiving" +single_off = "unblock" +multi_off = "proceed" +no_seq = "proceed" # unclear what it is, does not seem to be used +no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected + +[conditions.2] +name = "enrich_chr_9_14" +control = false +min_chunks = 0 # no decision made whenever <= min_chunks have been received from a read +max_chunks = 12 +targets = ["chr9", "chr10", "chr11", "chr12", "chr13", "chr14"] +single_on = "stop_receiving" +multi_on = "stop_receiving" +single_off = "unblock" +multi_off = "proceed" +no_seq = "proceed" # unclear what it is, does not seem to be used +no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected + +[conditions.3] +name = "enrich_chr_16_20" +control = false +min_chunks = 0 # no decision made whenever <= min_chunks have been received from a read +max_chunks = 12 +targets = ["chr16", "chr17", "chr18", "chr19", "chr20"] +single_on = "stop_receiving" +multi_on = "stop_receiving" +single_off = "unblock" +multi_off = "proceed" +no_seq = "proceed" # unclear what it is, does not seem to be used +no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/full_genome_run/sampler_per_window/config.toml b/usecases/configs/enrich_usecase/full_genome_run/sampler_per_window/config.toml index 5026a1a..edbb315 100644 --- a/usecases/configs/enrich_usecase/full_genome_run/sampler_per_window/config.toml +++ b/usecases/configs/enrich_usecase/full_genome_run/sampler_per_window/config.toml @@ -1,8 +1,9 @@ run_dir = "simulator_run" # where reads, logs, pafs etc. will be written to n_channels = 512 acceleration_factor = 10 -# run_duration = 200 -run_duration = 15000 +# acceleration_factor = 1 +# run_duration = 100 +run_duration = 2000 # run_duration = 86400.0 ################################################# @@ -12,7 +13,7 @@ run_duration = 15000 # reads_file = "nanosim_reads/perfect_reads_seed1_aligned_reads.fasta" ref_genome_path = "data/chm13v2.0_normalized.fa.gz" sim_params_file = "sim_params.dill" -rotating = true +rotating_writeout = true # mux_scan_period = 5400 # 90 minutes # mux_scan_duration = 100 # seconds @@ -20,6 +21,7 @@ rotating = true readfish_config_file = "configs/readfish_enrich_chr2021.toml" # readfish_method = "unblock_all" readfish_method = "targeted_seq" +# readfish_method = "control" ################################################# # Parameter extraction arguments diff --git a/usecases/configs/enrich_usecase/full_genome_run/sampler_per_window/readfish_enrich_chr2021.toml b/usecases/configs/enrich_usecase/full_genome_run/sampler_per_window/readfish_enrich_chr2021.toml index cc0fcf5..727125a 100644 --- a/usecases/configs/enrich_usecase/full_genome_run/sampler_per_window/readfish_enrich_chr2021.toml +++ b/usecases/configs/enrich_usecase/full_genome_run/sampler_per_window/readfish_enrich_chr2021.toml @@ -16,6 +16,6 @@ targets = ["chr20", "chr21"] single_on = "stop_receiving" multi_on = "stop_receiving" single_off = "unblock" -multi_off = "unblock" +multi_off = "proceed" no_seq = "proceed" # unclear what it is, does not seem to be used no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected \ No newline at end of file diff --git a/usecases/configs/enrich_usecase/test_config/sampler_per_window/config.toml b/usecases/configs/enrich_usecase/test_config/sampler_per_window/config.toml index b9d0a0a..15a4fe3 100644 --- a/usecases/configs/enrich_usecase/test_config/sampler_per_window/config.toml +++ b/usecases/configs/enrich_usecase/test_config/sampler_per_window/config.toml @@ -10,7 +10,7 @@ run_duration = 200 # reads_file = "nanosim_reads/perfect_reads_seed1_aligned_reads.fasta" ref_genome_path = "data/chm13v2.0_normalized.fa.gz" sim_params_file = "sim_params.dill" -rotating = true +rotating_writeout = true # mux_scan_period = 5400 # 90 minutes # mux_scan_duration = 100 # seconds diff --git a/usecases/configs/enrich_usecase/test_config/sampler_per_window/readfish_enrich_chr2021.toml b/usecases/configs/enrich_usecase/test_config/sampler_per_window/readfish_enrich_chr2021.toml index cc0fcf5..727125a 100644 --- a/usecases/configs/enrich_usecase/test_config/sampler_per_window/readfish_enrich_chr2021.toml +++ b/usecases/configs/enrich_usecase/test_config/sampler_per_window/readfish_enrich_chr2021.toml @@ -16,6 +16,6 @@ targets = ["chr20", "chr21"] single_on = "stop_receiving" multi_on = "stop_receiving" single_off = "unblock" -multi_off = "unblock" +multi_off = "proceed" no_seq = "proceed" # unclear what it is, does not seem to be used no_map = "proceed" # if no_map happens after mux_chunks were received, the read is rejected \ No newline at end of file diff --git a/usecases/create_nanosim_reads.ipynb b/usecases/create_nanosim_reads.ipynb index 76b6480..9e77699 100644 --- a/usecases/create_nanosim_reads.ipynb +++ b/usecases/create_nanosim_reads.ipynb @@ -7,21 +7,25 @@ "## Generate reads from the reference\n", "\n", "We generate reads with NanoSim.\n", - "We first extract the NanoSim read error model to some directory. This is only necessary once." + "We first extract the NanoSim read error model to some directory. This is only necessary once.\n", + "\n", + "**Rather take a look at `generate_nanosim_reads.sh`.**" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 2, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "The autoreload extension is already loaded. To reload it, use:\n", - " %reload_ext autoreload\n" - ] + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ @@ -37,66 +41,62 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "n_procs = 4\n", - "perfect = True\n", - "use_slurm = True\n", + "perfect = False\n", + "use_slurm = False\n", "on_cluster = False\n", "nanosim_dir = Path(\"external/ont_nanosim/\")\n", "nanosim_model_dir = Path(\"runs/nanosim_models\")\n", "nanosim_model_prefix = nanosim_model_dir / \"human_NA12878_DNA_FAB49712_guppy/training\"\n", "reads_output_dir = \"runs/enrich_usecase/nanosim_reads\"\n", - "ref_genome_path = \"runs/enrich_usecase/data/chm13v2.0_normalized1000000firsttwo.fa.gz\"\n", + "# ref_genome_path = \"runs/enrich_usecase/data/chm13v2.0_normalized1000000firsttwo.fa.gz\"\n", + "ref_genome_path = \"runs/enrich_usecase/data/chm13v2.0_normalized.fa.gz\"\n", "\n", "assert nanosim_dir.exists(), \"move to the repo root repository\"" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "x human_NA12878_DNA_FAB49712_guppy/\n", - "x human_NA12878_DNA_FAB49712_guppy/training_unaligned_length.pkl\n", - "x human_NA12878_DNA_FAB49712_guppy/training_reads_alignment_rate\n", - "x human_NA12878_DNA_FAB49712_guppy/training_model_profile\n", - "x human_NA12878_DNA_FAB49712_guppy/training_aligned_region.pkl\n", - "x human_NA12878_DNA_FAB49712_guppy/training_first_match.hist\n", - "x human_NA12878_DNA_FAB49712_guppy/training_strandness_rate\n", - "x human_NA12878_DNA_FAB49712_guppy/training_gap_length.pkl\n", - "x human_NA12878_DNA_FAB49712_guppy/training_error_markov_model\n", - "x human_NA12878_DNA_FAB49712_guppy/training_aligned_reads.pkl\n", - "x human_NA12878_DNA_FAB49712_guppy/training_chimeric_info\n", - "x human_NA12878_DNA_FAB49712_guppy/training_ht_ratio.pkl\n", - "x human_NA12878_DNA_FAB49712_guppy/training_match_markov_model\n", - "x human_NA12878_DNA_FAB49712_guppy/training_ht_length.pkl\n", - "x human_NA12878_DNA_FAB49712_guppy/training_error_rate.tsv\n" + "mkdir: cannot create directory ‘runs/nanosim_models’: File exists\n", + "tar -xvzf external/ont_nanosim/pre-trained_models/human_NA12878_DNA_FAB49712_guppy.tar.gz -C runs/nanosim_models\n" ] } ], "source": [ "# only necessary once\n", - "!mkdir runs/nanosim_models\n", - "!tar -xvzf external/ont_nanosim/pre-trained_models/human_NA12878_DNA_FAB49712_guppy.tar.gz -C \"{nanosim_model_dir}\"" + "!mkdir {nanosim_model_dir}\n", + "# !tar -xvzf external/ont_nanosim/pre-trained_models/human_NA12878_DNA_FAB49712_guppy.tar.gz -C \"{nanosim_model_dir}\"\n", + "!echo tar -xvzf external/ont_nanosim/pre-trained_models/human_NA12878_DNA_FAB49712_guppy.tar.gz -C \"{nanosim_model_dir}\"" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "2023-07-27 20:20:19,776 - Dry run, so not executing the command:\n", + "2024-02-26 14:20:44,568 - Dry run, so not executing the command:\n", "#!/usr/bin/bash\n", "seed=1\n", "conda run -n nanosim python -c \"import HTSeq; print(HTSeq.__version__)\"\n", @@ -105,19 +105,19 @@ "conda run -n nanosim \\\n", " python \"external/ont_nanosim/src/simulator.py\" genome \\\n", " --model_prefix \"runs/nanosim_models/human_NA12878_DNA_FAB49712_guppy/training\" \\\n", - " --ref_g \"runs/enrich_usecase/data/chm13v2.0_normalized1000000firsttwo.fa.gz\" \\\n", + " --ref_g \"runs/enrich_usecase/data/chm13v2.0_normalized.fa.gz\" \\\n", " -dna_type linear \\\n", - " --output \"runs/enrich_usecase/nanosim_reads/perfect_reads_seed$seed\" \\\n", - " --number 10 \\\n", + " --output \"runs/enrich_usecase/nanosim_reads/reads_seed$seed\" \\\n", + " --number 1000 \\\n", " --seed \"$seed\" \\\n", " --strandness 0.5 \\\n", " --basecaller guppy \\\n", " --aligned_rate \"100%\" \\\n", " --num_threads \"4\" \\\n", - " --perfect \\\n", + " \\\n", " --no_error_profile \\\n", " --no_flanking\n", - " #; exit --- 999482901.py:12 () WARNING ##\n" + " #; exit --- 2829796575.py:15 () WARNING ##\n" ] } ], @@ -125,12 +125,15 @@ "from simreaduntil.shared_utils.utils import print_cmd_and_run\n", "from simreaduntil.usecase_helpers.utils import get_gen_nanosim_reads_cmd\n", "\n", - "if on_cluster:\n", - " n_reads_per_sim = 1_000_000\n", - "else: \n", - " # n_reads_per_sim = 160_000\n", - " n_reads_per_sim = 10\n", - " use_slurm = False\n", + "# if on_cluster:\n", + "# n_reads_per_sim = 1_000_000\n", + "# else: \n", + "# # n_reads_per_sim = 160_000\n", + "# n_reads_per_sim = 10\n", + "# use_slurm = False\n", + "\n", + "n_reads_per_sim = 1_000\n", + "# n_reads_per_sim = 100_000\n", " \n", "nanosim_command = get_gen_nanosim_reads_cmd(nanosim_dir, nanosim_model_prefix, ref_genome_path, reads_dir=reads_output_dir, n_reads_per_sim=n_reads_per_sim, perfect=perfect, use_slurm=use_slurm)\n", "print_cmd_and_run(nanosim_command, dry=True)\n", diff --git a/usecases/enrich_usecase.py b/usecases/enrich_usecase.py index 29c1fc1..1c743f5 100644 --- a/usecases/enrich_usecase.py +++ b/usecases/enrich_usecase.py @@ -2,6 +2,7 @@ Combines SimReadUntil with ReadFish This script shows how to combine the SimReadUntil with ReadFish. +It creates the output in the current directory. It first learns a gap sampler from an existing run and saves it. Then, it runs the simulator in combination with ReadFish. on perfect reads generated from a reference genome, or from a reads file (if the config is adapted, e.g. NanoSim reads). @@ -41,30 +42,66 @@ import sys import warnings import numpy as np +import pandas as pd import toml from simreaduntil.shared_utils.debugging_helpers import is_test_mode -from simreaduntil.shared_utils.logging_utils import add_comprehensive_stream_handler_to_logger, print_logging_levels, setup_logger_simple +from simreaduntil.shared_utils.logging_utils import add_comprehensive_stream_handler_to_logger, logging_output_formatter, print_logging_levels, setup_logger_simple from simreaduntil.shared_utils.plotting import filter_seaborn_warnings -from simreaduntil.shared_utils.tee_stdouterr import TeeStdouterr -from simreaduntil.shared_utils.utils import delete_dir_if_exists, dill_dump, dill_load, print_cmd_and_run +# from simreaduntil.shared_utils.tee_stdouterr import TeeStdouterr +from simreaduntil.shared_utils.utils import delete_dir_if_exists, dill_dump, dill_load, print_cmd_and_run, tee_stdouterr_to_file from simreaduntil.simulator.utils import set_package_log_level from simreaduntil.usecase_helpers import simulator_with_readfish -from simreaduntil.usecase_helpers.utils import create_simparams_if_inexistent, get_gap_sampler_method, plot_condor_log_file_metrics +from simreaduntil.usecase_helpers.utils import create_simparams_if_inexistent, get_gap_sampler_method, plot_log_file_metrics from simreaduntil.usecase_helpers.utils import create_figures logger = setup_logger_simple(__name__) add_comprehensive_stream_handler_to_logger(None) set_package_log_level(logging.INFO).__enter__() -print_logging_levels() logging.getLogger(__name__).setLevel(logging.DEBUG) # logging.getLogger().setLevel(logging.DEBUG) # warnings from everywhere, not desired +file_handler = logging.FileHandler("log.txt", mode="a") # append in case we are just running the plotting part of the script +logging_output_formatter(file_handler) +logging.getLogger(None).addHandler(file_handler) +print_logging_levels() # import warnings # warnings.filterwarnings("error") filter_seaborn_warnings() +def create_minimap_index_if_inexistent(): + if sim_config["readfish_method"] != "unblock_all": + readfish_config = toml.load(sim_config["readfish_config_file"]) + mmi_filename = readfish_config["conditions"]["reference"] + if mmi_filename == "fake_mapper": + logger.info(f"Skipping minimap2 index creation, using fake wrapper") + return + + mmi_filename = Path(mmi_filename) + if mmi_filename.exists(): + logger.info(f"Minimap2 index '{mmi_filename}' already exists, skipping minimap2 index creation") + else: + logger.debug(f"Creating minimap2 index at location '{mmi_filename}' for ReadFish from reference genome '{ref_genome_path}'") + assert ref_genome_path is not None + print_cmd_and_run(f"""minimap2 -d {mmi_filename} {ref_genome_path}""") + else: + logger.debug("Skipping minimap2 index (not needed)") + +def run_readfish_simulation(): + logger.debug(f"#################################################################") + logger.debug(f"#################################################################") + logger.debug(f"Running the simulation from config file '{sim_config_file}' with ReadFish config file '{sim_config['readfish_config_file']}'") + delete_dir_if_exists(run_dir, ask=ask_dir_deletion) + # with set_package_log_level(logging.INFO): + # print_logging_levels() + seqsum_file = simulator_with_readfish.main(sim_config_file) + assert Path(seqsum_file).exists() + logger.debug(f"#################################################################") + logger.debug(f"#################################################################") + + return seqsum_file + ################################ ## PARAMS ################################ @@ -86,10 +123,8 @@ ################################ sim_config = toml.load(sim_config_file) -run_dir = Path(sim_config["run_dir"]) -# TeeStdouterr(run_dir / "stdouterr.txt").redirect() logger.debug(f"Read in simulation config file '{sim_config_file}'") - +run_dir = Path(sim_config["run_dir"]) ref_genome_path = sim_config.get("ref_genome_path", None) sim_params_filename = Path(sim_config["sim_params_file"]) if "sim_params_file" in sim_config else None seqsum_param_extr_file = Path(sim_config["seqsum_param_extr_file"]) if "seqsum_param_extr_file" in sim_config else None @@ -103,38 +138,6 @@ logger.info(f"""Loading ReadFish config file with content:\n{Path(sim_config["readfish_config_file"]).read_text()}""") logger.info("#"*80) -def create_minimap_index_if_inexistent(): - if sim_config["readfish_method"] != "unblock_all": - readfish_config = toml.load(sim_config["readfish_config_file"]) - mmi_filename = readfish_config["conditions"]["reference"] - if mmi_filename == "fake_mapper": - logger.info(f"Skipping minimap2 index creation, using fake wrapper") - return - - mmi_filename = Path(mmi_filename) - if mmi_filename.exists(): - logger.info(f"Minimap2 index '{mmi_filename}' already exists, skipping minimap2 index creation") - else: - logger.debug(f"Creating minimap2 index at location '{mmi_filename}' for ReadFish from reference genome '{ref_genome_path}'") - assert ref_genome_path is not None - print_cmd_and_run(f"""minimap2 -d {mmi_filename} {ref_genome_path}""") - else: - logger.debug("Skipping minimap2 index (not needed)") - -def run_readfish_simulation(): - logger.debug(f"#################################################################") - logger.debug(f"#################################################################") - logger.debug(f"Running the simulation from config file '{sim_config_file}' with ReadFish config file '{sim_config['readfish_config_file']}'") - delete_dir_if_exists(run_dir, ask=ask_dir_deletion) - # with set_package_log_level(logging.INFO): - # print_logging_levels() - seqsum_file = simulator_with_readfish.main(sim_config_file) - assert Path(seqsum_file).exists() - logger.debug(f"#################################################################") - logger.debug(f"#################################################################") - - return seqsum_file - # comment out as needed create_minimap_index_if_inexistent() # comment this out if you want to use minimap2 to align to a reference if sim_params_filename is None: @@ -149,11 +152,32 @@ def run_readfish_simulation(): delete_dir_if_exists(figure_dir, ask=ask_dir_deletion) figure_dir.mkdir(exist_ok=True) -plot_condor_log_file_metrics(figure_dir) -create_figures( - seqsum_filename, run_dir=run_dir, figure_dir=figure_dir, - ref_genome_path=ref_genome_path, cov_thresholds=[1, 2, 3, 4], - group_to_units={"target": toml.load(sim_config["readfish_config_file"])["conditions"]["0"]["targets"]}, -) +file_handler.flush() # logger writes to stderr +plot_log_file_metrics(file_handler.baseFilename, save_dir=figure_dir) + +readfish_conditions = [v for v in toml.load(sim_config["readfish_config_file"])["conditions"].values() if isinstance(v, dict)] +channel_assignments_toml = run_dir / "channels.toml" +channel_assignments_per_cond = toml.load(channel_assignments_toml) +channels_per_condition = {condition_dict["name"]: condition_dict["channels"] for condition_dict in channel_assignments_per_cond["conditions"].values()} + +logger.debug(f"Reading sequencing summary file '{seqsum_filename}'") +full_seqsum_df = pd.read_csv(seqsum_filename, sep="\t")#, nrows=100) # todo +logger.debug(f"Done reading sequencing summary file '{seqsum_filename}'") + +for condition in readfish_conditions: + condition_name = condition["name"] + subchannels = channels_per_condition[condition_name] + logger.info(f"Creating figures for condition '{condition_name}' with subchannels {subchannels}") + + partial_seqsum_df = full_seqsum_df[full_seqsum_df["channel"].isin([f"ch{i}" for i in subchannels])] + create_figures( + partial_seqsum_df, run_dir=run_dir, figure_dir=figure_dir / ("condition_" + condition_name), + ref_genome_path=ref_genome_path, cov_thresholds=[1, 2, 3, 4], + group_to_units={"target": condition["targets"]}, + ) + + logger.info(f"Done creating figures for condition '{condition_name}'") + + # break # todo logger.debug(f"Done with usecase script") \ No newline at end of file diff --git a/usecases/enrich_usecase_submission.sh b/usecases/enrich_usecase_submission.sh index 84e13f3..89611ed 100755 --- a/usecases/enrich_usecase_submission.sh +++ b/usecases/enrich_usecase_submission.sh @@ -1,15 +1,28 @@ #!/usr/bin/env bash -# it seems 2 CPUs are fine based on condor log average resource usage -##CONDOR request_cpus=2 +# it seems 2 CPUs are fine based on condor log average resource usage, simforward,muxscan,stopthread +##CONDOR request_cpus=4 # takes about 8GB of memory -##CONDOR request_memory=32000 +##CONDOR request_memory=64000 ##CONDOR request_disk=100G +##CONDOR +JobBatchName = "ont_enrich_usecase" ##CONDOR log = /home/mmordig/joblogs/job-$(ClusterId)-$(ProcId).log ##CONDOR output = /home/mmordig/joblogs/job-$(ClusterId)-$(ProcId).out ##CONDOR error = /home/mmordig/joblogs/job-$(ClusterId)-$(ProcId).err +#SBATCH --job-name=enrich_usecase-%j +#SBATCH --error=/cluster/home/mmordig/joblogs/job-%j.err +#SBATCH --output=/cluster/home/mmordig/joblogs/job-%j.out +#SBATCH --mem=16G +#SBATCH --cpus-per-task=4 +## #SBATCH --time=00:10:00 +#SBATCH --time=2:00:00 +# not avail #SBATCH --tmp=10G +#SBATCH --partition=compute + + # launch_condor_job 20 --- ~/ont_project_all/ont_project/usecases/enrich_usecase_submission.sh +# sbatch ~/ont_project_all/ont_project/usecases/enrich_usecase_submission.sh echo "Content of job ad file $_CONDOR_JOB_AD:"; cat "$_CONDOR_JOB_AD" echo "Starting job with args: " "$@" @@ -19,25 +32,31 @@ source ~/.bashrc cd ~/ont_project_all/ont_project/ source ~/ont_project_all/ont_project_venv/bin/activate +set -ex export PATH=~/ont_project_all/tools/bin:$PATH && which minimap2 -set -ex +output_dir=${1:-full_genome_run_sampler_per_window} +config_rel_dir=${2:-sampler_per_window} cd runs/enrich_usecase -rm -rf full_genome_run_sampler_per_window -mkdir full_genome_run_sampler_per_window -cd full_genome_run_sampler_per_window -ln -s ../data . -ln -s ../configs/full_genome_run/sampler_per_window configs +rm -rf "$output_dir" +mkdir -p "$output_dir" +ln -s "$(pwd)"/data "$output_dir" +cp -rL configs/full_genome_run/"${config_rel_dir}" "$output_dir"/configs # -L: expand symlinks +cd "$output_dir" +pwd python ~/ont_project_all/ont_project/usecases/enrich_usecase.py # symlink job output files to directory # parse stderr from job ad file # Err = "/home/mmordig/joblogs/job-13951206-0.err" if [ -n "$_CONDOR_JOB_AD" ]; then - grep -oP '(?<=Err = ").*(?=")' "$_CONDOR_JOB_AD" | xargs -I {} ln -s {} . - grep -oP '(?<=Out = ").*(?=")' "$_CONDOR_JOB_AD" | xargs -I {} ln -s {} . - grep -oP '(?<=Log = ").*(?=")' "$_CONDOR_JOB_AD" | xargs -I {} ln -s {} . + grep -oP '(?<=Err = ").*(?=")' "$_CONDOR_JOB_AD" | xargs -I {} cp {} . + grep -oP '(?<=Out = ").*(?=")' "$_CONDOR_JOB_AD" | xargs -I {} cp {} . + grep -oP '(?<=Log = ").*(?=")' "$_CONDOR_JOB_AD" | xargs -I {} cp {} . + # grep -oP '(?<=Err = ").*(?=")' "$_CONDOR_JOB_AD" | xargs -I {} ln -s {} . + # grep -oP '(?<=Out = ").*(?=")' "$_CONDOR_JOB_AD" | xargs -I {} ln -s {} . + # grep -oP '(?<=Log = ").*(?=")' "$_CONDOR_JOB_AD" | xargs -I {} ln -s {} . fi echo "Done with job, pwd $(pwd)" diff --git a/usecases/gen_example_sim_plot.py b/usecases/gen_example_sim_plot.py new file mode 100644 index 0000000..56d2734 --- /dev/null +++ b/usecases/gen_example_sim_plot.py @@ -0,0 +1,48 @@ +""" +Generate an example plot for the simulator with 2 channels (included in the paper) +""" + +import numpy as np +from simreaduntil.simulator.gap_sampling.constant_gaps_until_blocked import ConstantGapsUntilBlocked +from simreaduntil.simulator.readpool import ReadPoolFromIterable +from simreaduntil.simulator.readswriter import ArrayReadsWriter +from simreaduntil.simulator.simulator import ONTSimulator +from simreaduntil.simulator.simulator_params import SimParams + +sim_params = SimParams( + gap_samplers={f"channel_{i}": ConstantGapsUntilBlocked(short_gap_length=0.4, long_gap_length=1.5, prob_long_gap=0.5, time_until_blocked=8.1, read_delay=0) for i in range(2)}, + bp_per_second=10, min_chunk_size=4, default_unblock_duration=0.8, seed=0, +) + +rng = np.random.default_rng(0) +def reads_gen(): + for i in range(8): + l = rng.integers(1, 3)*10 + yield f"read{i}", "A" * l + +read_pool = ReadPoolFromIterable(reads_gen()) +simulator = ONTSimulator( + read_pool=read_pool, + reads_writer=ArrayReadsWriter(), + sim_params = sim_params, + output_dir="", +) +simulator.save_elems = True + +simulator.sync_start(0) +simulator.sync_forward(2) +simulator._channels[1].unblock() +simulator.sync_forward(5.5) +# simulator._channels[0].cur_elem. +simulator._channels[0].unblock() +simulator.sync_forward(8) +simulator.run_mux_scan(2, is_sync=True) +simulator.sync_forward(13) + +ax = simulator.plot_channels()#; import matplotlib.pyplot as plt; plt.show() +ax.figure.tight_layout() +ax.set_ylim([-0.2, 1.2]) +ax.autoscale() + +simulator.sync_stop() +ax.figure.savefig("simulator_example.png", dpi=300) \ No newline at end of file diff --git a/usecases/generate_nanosim_reads.sh b/usecases/generate_nanosim_reads.sh new file mode 100755 index 0000000..fff3beb --- /dev/null +++ b/usecases/generate_nanosim_reads.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash + +# note: aligned and unaligned reads are not shuffled here because they are shuffled by the ReadPoolFromFile +# more efficient to run with 2 cpus and run many in parallel + +##CONDOR request_cpus=2 +##CONDOR request_memory=6000 +##CONDOR request_disk=2G +##CONDOR +JobBatchName = "ont_enrich_usecase" +##CONDOR log = /home/mmordig/joblogs/job-$(ClusterId)-$(ProcId).log +##CONDOR output = /home/mmordig/joblogs/job-$(ClusterId)-$(ProcId).out +##CONDOR error = /home/mmordig/joblogs/job-$(ClusterId)-$(ProcId).err +# seems to be broken/filesystem very slow +##CONDOR Requirements = (Machine != "g110.internal.cluster.is.localnet") + +# bash ~/ont_project_all/ont_project/usecases/generate_nanosim_reads.sh 1000 3 +# conda activate nanosim +# sbatch ~/ont_project_all/ont_project/usecases/generate_nanosim_reads.sh 100000 3 +# or use ##CONDOR queue 100 together with $(Item) +# for seed in range(1, 100+1): +# print(f"sbatch ~/ont_project_all/ont_project/usecases/generate_nanosim_reads.sh 100000 {seed}") +# +# initially +# mkdir -p runs/nanosim_models +# echo tar -xvzf external/ont_nanosim/pre-trained_models/human_NA12878_DNA_FAB49712_guppy.tar.gz -C "runs/nanosim_models" + +# set -x +# source ~/.bashrc +# conda hell: conda not found, not sure why, so hardcoding python executable from conda env + +# for python, samtools; conda activate not really working (need to copy entire env) +export PATH=/home/mmordig/tools/mambaforge/envs/nanosim/bin:$PATH +# export PATH=/home/mmordig/miniforge3/envs/nanosim/bin:$PATH + +set -eux + +cd ~/ont_project_all/ont_project/ + +num_reads=$1 +seed=$2 + +# output_dir=runs/data/nanosim_reads/human_genome_med15000 +output_dir=runs/data/nanosim_reads/human_genome_med15000_alignedrate2 +# output_dir=runs/data/nanosim_reads/human_genome +# output_dir=runs/data/nanosim_reads/human_genome_with_flanking +mkdir -p "$output_dir" +num_procs=$(nproc) +# ((num_procs--)) # 1 manager process, not really needed +# num_procs=1 #todo + +# genome=runs/data/random_genome.fasta # see below for how to generate +genome="runs/enrich_usecase/data/chm13v2.0_normalized.fa.gz" +# aligned_rate="100%" +aligned_rate="2" + +echo "nanosim read generation: generating ${num_reads} using seed $seed using $num_procs threads from genome '$genome' with aligned_rate '$aligned_rate' into output_dir '$output_dir'" + +echo "Generating reads" +# conda slow to run, instead use "conda activate nanosim" once and then launch the script several times +# conda run -n nanosim python \ +rm "$output_dir/reads_seed$seed"* || true +# in NanoSim, replaced by uniform distribution now because median length was unreliable, sd=6.9=ln(1000) is the std of the lognormal (lognormal = distribution whose log is normally distributed with stddeviation std) +python \ + "external/ont_nanosim/src/simulator.py" genome \ + --model_prefix "runs/nanosim_models/human_NA12878_DNA_FAB49712_guppy/training" \ + --ref_g "$genome" \ + -dna_type linear \ + -med 15000 -max 20000 -min 400 -sd 2000 \ + --output "$output_dir/reads_seed$seed" \ + --number "${num_reads}" \ + --seed "$seed" \ + --strandness 0.5 \ + --basecaller guppy \ + --aligned_rate "$aligned_rate" \ + --num_threads "$num_procs" \ + --no_flanking \ + --no_error_profile + +echo "Merging files" +# merge 'reads_seed1_aligned_reads.fasta', 'reads_seed1_unaligned_reads.fasta' into 'reads_seed1_merged_reads.fasta' +files=$(ls "$output_dir/reads_seed${seed}_"*) +merged_file="$output_dir/reads_seed${seed}_merged_reads.fasta" +# note: aligned and unaligned reads are not shuffled here because they are shuffled by the ReadPoolFromFile +cat $files > "$merged_file" +rm $files + +echo "Generating .fai file" +samtools faidx "$merged_file" + +echo "nanosim read generation: done with seed $seed" + + +# # generate small fake genome and write to file using pysam +# from simreaduntil.shared_utils.dna import get_random_DNA_seq +# from Bio import SeqIO +# from Bio.Seq import Seq +# with open("runs/data/random_genome.fasta", "w") as fasta: +# SeqIO.write((SeqIO.SeqRecord(id=f"fakechr_{i}", seq=Seq(get_random_DNA_seq(1_000_000))) for i in range(20)), fasta, "fasta") \ No newline at end of file diff --git a/usecases/install_usecase_deps.sh b/usecases/install_usecase_deps.sh index 47fe9ca..ec053af 100755 --- a/usecases/install_usecase_deps.sh +++ b/usecases/install_usecase_deps.sh @@ -16,6 +16,9 @@ trap on_exit ERR tools_dir=~/ont_project_all/tools conda_or_mamba="conda" # very slow +# if mamba is available, use mamba +which mamba && conda_or_mamba="mamba" +echo "Using $conda_or_mamba for conda environment creation" usage() { echo "Usage: $0 [-h] [-e ] [-t ]" @@ -50,10 +53,6 @@ echo "The current base directory to install minimap to is: $tools_dir" # echo "Updated path to: $tools_dir" # fi -# if mamba is available, use mamba -which mamba && conda_or_mamba="mamba" -echo "Using $conda_or_mamba for conda environment creation" - # check we are in the right directory by checking for a directory "external" [ -d "external" ] || (echo "Error: not in the right directory. Run this script from the ont_project root directory containing the external directory"; exit 1) @@ -94,6 +93,7 @@ echo "Installed minimap2 to location: $(which minimap2)" echo "Make sure to add this to your PATH variable, e.g." echo "export \"PATH=$tools_dir/bin:\$PATH\"" +# exit 0 #################################################### # install NanoSim conda env diff --git a/usecases/replicate_run.py b/usecases/replicate_run.py index d174b88..a8eeed1 100644 --- a/usecases/replicate_run.py +++ b/usecases/replicate_run.py @@ -32,7 +32,7 @@ from simreaduntil.shared_utils.utils import delete_dir_if_exists, dill_dump, dill_load, num_lines_in_file, subset_dict from simreaduntil.simulator.gap_sampling.inactive_active_gaps_replication import get_read_durations_per_channel from simreaduntil.simulator.simfasta_to_seqsum import convert_simfasta_dir_to_seqsum, convert_simfasta_to_seqsum -from simreaduntil.simulator.simulator import assign_read_durations_to_channels, run_simulator_from_sampler_per_channel, run_simulator_from_sampler_per_channel_parallel, simulator_stats_to_disk +from simreaduntil.simulator.simulator import assign_read_durations_to_channels, run_simulator_from_sampler_per_channel, run_simulator_from_sampler_per_channel_parallel, write_simulator_stats from simreaduntil.simulator.simulator_params import SimParams from simreaduntil.simulator.utils import set_package_log_level from simreaduntil.usecase_helpers.utils import get_cleaned_seqsum_filename, create_figures, create_simparams_if_inexistent, get_gap_sampler_method, remove_mux_scans_and_clean_if_inexistent @@ -116,7 +116,7 @@ def run_simulator(seqsum_filename): logger.debug(f"#################################################################") logger.info("Saving simulator statistics") - simulator_stats_to_disk([simulator for (simulator, _) in simulators_and_read_filenames], output_dir=run_dir) + write_simulator_stats([simulator for (simulator, _) in simulators_and_read_filenames], output_dir=run_dir) logger.info(f"Writing sequencing summary file '{seqsum_filename}'") convert_simfasta_dir_to_seqsum(reads_dir, seqsummary_filename=seqsum_filename) logger.info("Wrote sequencing summary file") diff --git a/usecases/replicate_run_submission.sh b/usecases/replicate_run_submission.sh index 29479dd..9a0e07b 100755 --- a/usecases/replicate_run_submission.sh +++ b/usecases/replicate_run_submission.sh @@ -4,6 +4,7 @@ # takes about 8GB of memory ##CONDOR request_memory=32G ##CONDOR request_disk=100G +##CONDOR +JobBatchName = "ont_replicate_run" ##CONDOR log = /home/mmordig/joblogs/job-$(ClusterId)-$(ProcId).log ##CONDOR output = /home/mmordig/joblogs/job-$(ClusterId)-$(ProcId).out ##CONDOR error = /home/mmordig/joblogs/job-$(ClusterId)-$(ProcId).err @@ -25,9 +26,9 @@ source ~/.bashrc cd ~/ont_project_all/ont_project/ source ~/ont_project_all/ont_project_venv/bin/activate +set -ex export PATH=~/ont_project_all/tools/bin:$PATH && which minimap2 -set -ex cd runs/run_replication method=$1