From 9c869245e88167283e61a5b5bca8b97c042f7376 Mon Sep 17 00:00:00 2001 From: Bradley Meyers Date: Fri, 21 Apr 2023 13:27:00 +0800 Subject: [PATCH] Update workflow readability and data storage paths (#8) * Change data path workflow to match vcstools (#3) * changed base data directories to be USER based, not in shared /astro/mwavcs/vcs * replaced hard-coded paths with those from config, replaced rm with munlink command * removed Galaxy config * removed duplicate parameter assignment, config overrides anyway * updated data path in docs * moved default mwa_search and vcstools versions to config file * updated default software versions for OzStar too * fix fitsdir in search flow * fixed remaining /group reference * remove explicit assignment of global config params * added software version defaults for Shanghai server * Added a link to the ReadTheDocs documentation to the README * Added first pass workflow diagrams to docs folder * Added links to docs README * Attempt to add image to sphinx documentation * Update README.md * Update README.md * Cleanup Nextflow scripts to implement best practices (#7) * Moved all of the params definitions into the nextflow.config and commented them * Rearranged config to load things in the right order * Replaced file inputs with path * Rewrote the beamforming so it is simplier and easier to understand * Made the config more readable and fixed a few bugs * Replaced basedir with vcsdir * Cleaned up the pulsar search module * Got the classifier working but I did install LOTAAS_wrapper.py installed in PulsarFeatureLab * Fixed up the mwa_seach_pipeline and calculated time and memory using channels correctly * Fixed the ipfb mode * Updated the --help to be more accurate * Fixed up the single pulse only search * Started making some very simple testing documentation (may be replaced with unit tests later) * Made the ddplan scripts also calculate and approximate work function * Made the pipeline split the dispersion plan into groups of equal work function size * GroupTuple the search puts by the number of dms so there are not stopping points * Made same changes to single pulse * Updated software layout to prevent instal scriptlation bugs * Started creating better documentation of our dependancies * Began updating the data_processing_pipeline.nf INCOMPLETE * Made some of the config calcs a function and made the presto version a param * Collates the prepfold jobs so they run more efficiently on HPC * Made a few more options to give you more options to add pulsars of different colours and shapes (#10) * Find cand. position bug fix (#9) * Moved all of the params definitions into the nextflow.config and commented them * Rearranged config to load things in the right order * Replaced file inputs with path * Rewrote the beamforming so it is simplier and easier to understand * Made the config more readable and fixed a few bugs * Replaced basedir with vcsdir * Cleaned up the pulsar search module * Got the classifier working but I did install LOTAAS_wrapper.py installed in PulsarFeatureLab * Fixed up the mwa_seach_pipeline and calculated time and memory using channels correctly * Fixed the ipfb mode * Updated the --help to be more accurate * Fixed up the single pulse only search * Started making some very simple testing documentation (may be replaced with unit tests later) * Made the ddplan scripts also calculate and approximate work function * Made the pipeline split the dispersion plan into groups of equal work function size * GroupTuple the search puts by the number of dms so there are not stopping points * Made same changes to single pulse * Updated software layout to prevent instal scriptlation bugs * Started creating better documentation of our dependancies * Began updating the data_processing_pipeline.nf INCOMPLETE * Made some of the config calcs a function and made the presto version a param * Collates the prepfold jobs so they run more efficiently on HPC * Fixed a bug in the splice formatting for single beams * Updated find_candidate_position.nf so that it works with the new format --------- Co-authored-by: Sam McSweeney Co-authored-by: Sam McSweeney Co-authored-by: Nick Swainston --- README.md | 52 +- docs/README.md | 11 + docs/first_pass_workflow.png | Bin 0 -> 173258 bytes docs/first_pass_workflow.pptx | Bin 0 -> 63809 bytes docs/index.rst | 2 + docs/smart_processing.rst | 10 +- docs/test_commands.rst | 35 + {lib/dpp => dpp}/__init__.py | 0 {lib/dpp => dpp}/helper_RM.py | 0 {lib/dpp => dpp}/helper_RVMfit.py | 0 {lib/dpp => dpp}/helper_archive.py | 0 {lib/dpp => dpp}/helper_bestprof.py | 0 {lib/dpp => dpp}/helper_checks.py | 0 {lib/dpp => dpp}/helper_classify.py | 0 {lib/dpp => dpp}/helper_config.py | 0 {lib/dpp => dpp}/helper_database.py | 0 {lib/dpp => dpp}/helper_files.py | 0 {lib/dpp => dpp}/helper_logging.py | 0 {lib/dpp => dpp}/helper_obs_info.py | 0 {lib/dpp => dpp}/helper_prepfold.py | 0 {lib/dpp => dpp}/helper_relaunch.py | 0 {lib/dpp => dpp}/helper_source_info.py | 0 {lib/dpp => dpp}/helper_status.py | 0 {lib/dpp => dpp}/helper_terminate.py | 0 {lib/dpp => dpp}/plotting_toolkit.py | 0 {lib/mwa_search => mwa_search}/__init__.py | 0 .../data/SMART_obs_data.npy | Bin {lib/mwa_search => mwa_search}/data_load.py | 0 .../dispersion_tools.py | 47 +- {lib/mwa_search => mwa_search}/grid_tools.py | 0 {lib/mwa_search => mwa_search}/obs_tools.py | 0 nextflow/beamform.nf | 83 +-- nextflow/beamform_module.nf | 334 +++------ nextflow/benchmark_beamformer.nf | 45 +- nextflow/candidate_TOAs.nf | 44 +- nextflow/classifier_module.nf | 74 +- nextflow/data_processing_pipeline.nf | 64 +- nextflow/dspsr_module.nf | 14 +- nextflow/find_candidate_position.nf | 174 +++-- nextflow/mwa_search_pipeline.nf | 263 ++++--- nextflow/nextflow.config | 517 +++++++++----- nextflow/pdmp.nf | 12 +- nextflow/pulsar_search.nf | 84 +-- nextflow/pulsar_search_module.nf | 647 +++++++++--------- nextflow/sun_signal_search.nf | 44 +- nextflow/test_beamformer.nf | 14 +- nextflow/vcs_download.nf | 36 +- scripts/{mwa_search => }/LOTAAS_wrapper.py | 2 +- scripts/{mwa_search => }/bestgridpos.py | 0 .../{mwa_search => }/cold_storage_mover.py | 0 ...d_clustered_and_known_pulsar_candidates.py | 0 scripts/{mwa_search => }/grid.py | 0 scripts/{mwa_search => }/lfDDplan.py | 24 +- .../observation_processing_pipeline.py | 0 scripts/{dpp => }/opp_status.py | 0 scripts/plotting/plot_obs_pulsar.py | 79 ++- .../{dpp => }/pulsar_processing_pipeline.py | 0 scripts/{dpp => }/pulsars_in_fov.py | 0 scripts/{mwa_search => }/rsync_rm_loop.sh | 0 .../{mwa_search => }/search_launch_loop.sh | 0 setup.py | 92 +-- 61 files changed, 1436 insertions(+), 1367 deletions(-) create mode 100644 docs/README.md create mode 100644 docs/first_pass_workflow.png create mode 100644 docs/first_pass_workflow.pptx create mode 100644 docs/test_commands.rst rename {lib/dpp => dpp}/__init__.py (100%) rename {lib/dpp => dpp}/helper_RM.py (100%) rename {lib/dpp => dpp}/helper_RVMfit.py (100%) rename {lib/dpp => dpp}/helper_archive.py (100%) rename {lib/dpp => dpp}/helper_bestprof.py (100%) rename {lib/dpp => dpp}/helper_checks.py (100%) rename {lib/dpp => dpp}/helper_classify.py (100%) rename {lib/dpp => dpp}/helper_config.py (100%) rename {lib/dpp => dpp}/helper_database.py (100%) rename {lib/dpp => dpp}/helper_files.py (100%) rename {lib/dpp => dpp}/helper_logging.py (100%) rename {lib/dpp => dpp}/helper_obs_info.py (100%) rename {lib/dpp => dpp}/helper_prepfold.py (100%) rename {lib/dpp => dpp}/helper_relaunch.py (100%) rename {lib/dpp => dpp}/helper_source_info.py (100%) rename {lib/dpp => dpp}/helper_status.py (100%) rename {lib/dpp => dpp}/helper_terminate.py (100%) rename {lib/dpp => dpp}/plotting_toolkit.py (100%) rename {lib/mwa_search => mwa_search}/__init__.py (100%) rename {lib/mwa_search => mwa_search}/data/SMART_obs_data.npy (100%) rename {lib/mwa_search => mwa_search}/data_load.py (100%) rename {lib/mwa_search => mwa_search}/dispersion_tools.py (84%) rename {lib/mwa_search => mwa_search}/grid_tools.py (100%) rename {lib/mwa_search => mwa_search}/obs_tools.py (100%) rename scripts/{mwa_search => }/LOTAAS_wrapper.py (99%) rename scripts/{mwa_search => }/bestgridpos.py (100%) rename scripts/{mwa_search => }/cold_storage_mover.py (100%) rename scripts/{mwa_search => }/find_clustered_and_known_pulsar_candidates.py (100%) rename scripts/{mwa_search => }/grid.py (100%) rename scripts/{mwa_search => }/lfDDplan.py (82%) rename scripts/{dpp => }/observation_processing_pipeline.py (100%) rename scripts/{dpp => }/opp_status.py (100%) rename scripts/{dpp => }/pulsar_processing_pipeline.py (100%) rename scripts/{dpp => }/pulsars_in_fov.py (100%) rename scripts/{mwa_search => }/rsync_rm_loop.sh (100%) rename scripts/{mwa_search => }/search_launch_loop.sh (100%) diff --git a/README.md b/README.md index d18f37d5..a7f15d7c 100755 --- a/README.md +++ b/README.md @@ -4,13 +4,28 @@ This repository was written by Nick Swainston to automate pulsar searching using the PRESTO software suite. An explanation of the search procedure can be found on the wiki of the GitHub page. The pipeline uses Nextflow to manage all the required jobs for both beamforming and searching. +## Documentation + +Documentation for `mwa_search` is hosted at [this ReadTheDocs link](https://mwa-search-cira.readthedocs.io/en/latest/). +Source code for this documentation is in the [docs][docs] folder. + ## Prerequisites -Requires the [PRESTO](https://github.com/scottransom/presto) software suite, [Nextflow](https://www.nextflow.io/) and [_vcstools_](https://github.com/CIRA-Pulsars-and-Transients-Group/vcstools). +To run the pipelines contained in the `nextflow` directory requires [Nextflow](https://www.nextflow.io/). +The dependancies of the pipeline are containerised so you will need container software such as Docker installed. If you would like an explanation of the depenancies see the dependancy section below. ## Installing -On Swinburne's Ozstar supercomputer you can load the module using +The repository's scripts can be installed using either: +``` +pip install . +``` +or +``` +python setup.py install. +``` + +On Swinburne's Ozstar supercomputer, the pipeline is already installed so you can load the module using ``` module use /fred/oz125/software/modulefiles module load vcstools/master @@ -24,21 +39,30 @@ module load vcstools module load mwa_search ``` -If you want to install this pipeline on your supercomputer I will likely need to assistant the installation and the writing of your config file. If you would like to try yourself, I recommend installing the _vcstools_ beamformer [docker image](https://hub.docker.com/repository/docker/cirapulsarsandtransients/vcstools) and then installing the python scripts with the setup.py If you would like to attempt it yourself do the following. Make a directory called mwa\_search and then move into it. Then clone the repository and move into the directory it creates. Run the build script using -``` -./build.sh -``` -This will move all the python scripts to a directory called master. Then create a module that does the following. -``` -export PATH=${PATH}:/master -export PYTHONPATH=${PYTHONPATH}:/master -``` -where \ is the directory where you ran the git clone command, and \ is where you would like your search pipeline products (make sure this directory exists). +If you want to install this pipeline on your supercomputer you will need to edit the `nextflow.config` based on your cluster. +To do this, copy one of the `if ( hostname.startsWith("") ) {` sections of the config +and edit to describe your clusters' directories structure and dependancy installation. +I will likely need to assistant the installation and the writing of your config file so feel free to make a GitHub issue to ask for assistance. + + +## Dependancies +The following is all the software we use in the `mwa_search_pipeline.nf`. The following will describe the version of the software we use, the location of Docker images and any changes we have made to the software. + +### PRESTO +[PRESTO](https://github.com/scottransom/presto) pulsar search software suite. +We use [this fork](https://github.com/NickSwainston/presto) which includes out custom `ACCEL_sift.py` script. +We use version [v4.0_7ec3c83](https://hub.docker.com/repository/docker/nickswainston/presto/general) + + +### vcstools +[vcstools](https://github.com/CIRA-Pulsars-and-Transients-Group/vcstools). -You will also need to edit _config.py_ in _vcstools_ to comply with the modules and directory structure of your supercomputer. +I recommend installing the _vcstools_ beamformer [docker image](https://hub.docker.com/repository/docker/cirapulsarsandtransients/vcstools) and then installing the python scripts with the setup.py If you would like to attempt it yourself do the following. ## Developing -If you create a new branch of the git repo then when you use the _build.sh_ script it will make a directory based on your branch name which can be used to test changes to the code without disrupting currently running versions. _mwa\_search\_pipeline.nf_ has an option --mwa_search_version which can use a different module version (which you will have to create) and used to test it, You can then submit a pull request to the GitHub. +If you create a new branch of the git repo then when you use the _build.sh_ script it will make a directory based on your branch name which can be used to test changes to the code without disrupting currently running versions. +_mwa\_search\_pipeline.nf_ has an option --mwa_search_version which can use a different module version (which you will have to create) and used to test it. +You can then submit a pull request to the GitHub. ## Common Use Cases All Nextflow scripts have a --help option to explain all the available arguments. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..06c647e7 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,11 @@ +# Documentation + +Full documentation for `mwa_search` is hosted at [this ReadTheDocs link](https://mwa-search-cira.readthedocs.io/en/latest/). + +## First pass workflow diagram + +Workflow diagram for the first pass survey. +This figure exists as `workflow.png` in the [Overleaf document for the SMART survey description paper](https://www.overleaf.com/5344792699hjhfpkddstxg). +It is available here in both PNG and PPTX formats ([first_pass_workflow.png](first_pass_workflow.png), [first_pass_workflow.pptx](first_pass_workflow.pptx)). + +![first_pass_workflow.png](first_pass_workflow.png) diff --git a/docs/first_pass_workflow.png b/docs/first_pass_workflow.png new file mode 100644 index 0000000000000000000000000000000000000000..378f703ff2b92fd36555fd037d07c1b5e2ad1e47 GIT binary patch literal 173258 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

~fFxke5~FFy`fm9hAwBsDfdUE{%-98r66? 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

V#FB+{dC9s)WcQ5m8Dzm3+sQPh~|lt#JMYoRg+C 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 C>O%Mc literal 0 HcmV?d00001 diff --git a/docs/first_pass_workflow.pptx b/docs/first_pass_workflow.pptx new file mode 100644 index 0000000000000000000000000000000000000000..51b2d49eadd558a1392b357cde7d062f3d0e905d GIT binary patch literal 63809 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 F>0e;VpQ!)< literal 0 HcmV?d00001 diff --git a/docs/index.rst b/docs/index.rst index ab45ecbc..1afc4333 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -17,5 +17,7 @@ Welcome to mwa_search's documentation! mwa_search_scripts plotting_scripts + test_commands + dpp_modules mwa_search_modules \ No newline at end of file diff --git a/docs/smart_processing.rst b/docs/smart_processing.rst index 0fe196fa..61067d5f 100644 --- a/docs/smart_processing.rst +++ b/docs/smart_processing.rst @@ -1,10 +1,14 @@ .. _smart_processing: -SMART Pulsar Search Proccessing +SMART Pulsar Search Processing =============================== The following guide will teach you how to process SMART data for the shallow first pass search. +Overview +-------- + +.. image:: first_pass_workflow.png Choosing an observation ----------------------- @@ -59,7 +63,7 @@ You also need to transfer the calibration solutions to OZStar. This should be do cd /fred/oz125/vcs mkdir -p /cal//rts - rsync garrawarla:/astro/mwavcs/vcs//cal//rts/*{dat,txt} /cal//rts + rsync garrawarla:/astro/mwavcs/${USER}//cal//rts/*{dat,txt} /cal//rts This will download all the calibration solutions and flagged tiles/channels files we need. Once both downloads are complete, update the google sheet so that this observation is marked as "processing" and continue to the next step. @@ -77,4 +81,4 @@ At the same time, you should make another screen and run the following command:: cd /fred/oz125/pulsar_search rsync_rm_loop.sh -This used to transfer the candidates to Prometheus, but since we ran out of room, it deletes all the temporary files after each mwa_search_pipeline.nf batch is done to assure we don't go over our storage limit. \ No newline at end of file +This used to transfer the candidates to Prometheus, but since we ran out of room, it deletes all the temporary files after each mwa_search_pipeline.nf batch is done to assure we don't go over our storage limit. diff --git a/docs/test_commands.rst b/docs/test_commands.rst new file mode 100644 index 00000000..5cf996f1 --- /dev/null +++ b/docs/test_commands.rst @@ -0,0 +1,35 @@ +.. _smart_processing: + +Test Commands +============= + +A bunch of command to run for testing using the first 600 seconds of 1301674968 + + +beamform.nf +----------- + +normal:: + + beamform.nf --obsid 1301674968 --calid 1301739904 --begin 1301674969 --end 1301675568 --pointings 13:11:52.64_-12:28:01.63,14:18:50.28_-39:21:18.51 -w test_work --out_dir test_cands --vcstools_version devel --publish_fits + +summed:: + + beamform.nf --obsid 1301674968 --calid 1301739904 --begin 1301674969 --end 1301675568 --pointings 13:11:52.64_-12:28:01.63,14:18:50.28_-39:21:18.51 -w test_work --out_dir test_cands --vcstools_version devel --publish_fits --summed + +ipfb:: + + beamform.nf --obsid 1301674968 --calid 1301739904 --begin 1301674969 --end 1301675568 --pointings 13:11:52.64_-12:28:01.63,14:18:50.28_-39:21:18.51 -w test_work --out_dir test_cands --vcstools_version devel --publish_fits --ipfb + + +pulsar_search.nf +---------------- +Run with outputs of beamform.nf + +simple periodic:: + + pulsar_search.nf --obsid 1301674968 --calid 1301739904 --fits_file /fred/oz125/vcs/1301674968/pointings/13:11:52.64_-12:28:01.63/*fits -w test_work --out_dir test_cands --vcstools_version devel --dm_min 36 --dm_max 37 + +Just single pulse search:: + + pulsar_search.nf --obsid 1301674968 --calid 1301739904 --fits_file /fred/oz125/vcs/1301674968/pointings/13:11:52.64_-12:28:01.63/*fits -w test_work --out_dir test_cands --vcstools_version devel --dm_min 36 --dm_max 37 --sp \ No newline at end of file diff --git a/lib/dpp/__init__.py b/dpp/__init__.py similarity index 100% rename from lib/dpp/__init__.py rename to dpp/__init__.py diff --git a/lib/dpp/helper_RM.py b/dpp/helper_RM.py similarity index 100% rename from lib/dpp/helper_RM.py rename to dpp/helper_RM.py diff --git a/lib/dpp/helper_RVMfit.py b/dpp/helper_RVMfit.py similarity index 100% rename from lib/dpp/helper_RVMfit.py rename to dpp/helper_RVMfit.py diff --git a/lib/dpp/helper_archive.py b/dpp/helper_archive.py similarity index 100% rename from lib/dpp/helper_archive.py rename to dpp/helper_archive.py diff --git a/lib/dpp/helper_bestprof.py b/dpp/helper_bestprof.py similarity index 100% rename from lib/dpp/helper_bestprof.py rename to dpp/helper_bestprof.py diff --git a/lib/dpp/helper_checks.py b/dpp/helper_checks.py similarity index 100% rename from lib/dpp/helper_checks.py rename to dpp/helper_checks.py diff --git a/lib/dpp/helper_classify.py b/dpp/helper_classify.py similarity index 100% rename from lib/dpp/helper_classify.py rename to dpp/helper_classify.py diff --git a/lib/dpp/helper_config.py b/dpp/helper_config.py similarity index 100% rename from lib/dpp/helper_config.py rename to dpp/helper_config.py diff --git a/lib/dpp/helper_database.py b/dpp/helper_database.py similarity index 100% rename from lib/dpp/helper_database.py rename to dpp/helper_database.py diff --git a/lib/dpp/helper_files.py b/dpp/helper_files.py similarity index 100% rename from lib/dpp/helper_files.py rename to dpp/helper_files.py diff --git a/lib/dpp/helper_logging.py b/dpp/helper_logging.py similarity index 100% rename from lib/dpp/helper_logging.py rename to dpp/helper_logging.py diff --git a/lib/dpp/helper_obs_info.py b/dpp/helper_obs_info.py similarity index 100% rename from lib/dpp/helper_obs_info.py rename to dpp/helper_obs_info.py diff --git a/lib/dpp/helper_prepfold.py b/dpp/helper_prepfold.py similarity index 100% rename from lib/dpp/helper_prepfold.py rename to dpp/helper_prepfold.py diff --git a/lib/dpp/helper_relaunch.py b/dpp/helper_relaunch.py similarity index 100% rename from lib/dpp/helper_relaunch.py rename to dpp/helper_relaunch.py diff --git a/lib/dpp/helper_source_info.py b/dpp/helper_source_info.py similarity index 100% rename from lib/dpp/helper_source_info.py rename to dpp/helper_source_info.py diff --git a/lib/dpp/helper_status.py b/dpp/helper_status.py similarity index 100% rename from lib/dpp/helper_status.py rename to dpp/helper_status.py diff --git a/lib/dpp/helper_terminate.py b/dpp/helper_terminate.py similarity index 100% rename from lib/dpp/helper_terminate.py rename to dpp/helper_terminate.py diff --git a/lib/dpp/plotting_toolkit.py b/dpp/plotting_toolkit.py similarity index 100% rename from lib/dpp/plotting_toolkit.py rename to dpp/plotting_toolkit.py diff --git a/lib/mwa_search/__init__.py b/mwa_search/__init__.py similarity index 100% rename from lib/mwa_search/__init__.py rename to mwa_search/__init__.py diff --git a/lib/mwa_search/data/SMART_obs_data.npy b/mwa_search/data/SMART_obs_data.npy similarity index 100% rename from lib/mwa_search/data/SMART_obs_data.npy rename to mwa_search/data/SMART_obs_data.npy diff --git a/lib/mwa_search/data_load.py b/mwa_search/data_load.py similarity index 100% rename from lib/mwa_search/data_load.py rename to mwa_search/data_load.py diff --git a/lib/mwa_search/dispersion_tools.py b/mwa_search/dispersion_tools.py similarity index 84% rename from lib/mwa_search/dispersion_tools.py rename to mwa_search/dispersion_tools.py index 3cfe88b4..874d73c9 100644 --- a/lib/mwa_search/dispersion_tools.py +++ b/mwa_search/dispersion_tools.py @@ -70,8 +70,17 @@ def calc_nsub(centrefreq, dm): return int(nsub) -def dd_plan(centrefreq, bandwidth, nfreqchan, timeres, lowDM, highDM, - min_DM_step=0.02, max_DM_step=500.0, max_dms_per_job=5000): +def dd_plan( + centrefreq, + bandwidth, + nfreqchan, + timeres, + lowDM, + highDM, + min_DM_step=0.02, + max_DM_step=500.0, + max_dms_per_job=5000, + ): """ Work out the dedisperion plan @@ -146,12 +155,13 @@ def dd_plan(centrefreq, bandwidth, nfreqchan, timeres, lowDM, highDM, #range from last to new D_DM = round(D_DM, 2) nDM_step = int((D_DM - previous_DM) / DM_step) + total_work_factor = nDM_step / downsample if D_DM > lowDM: nsub = calc_nsub(centrefreq, D_DM) if downsample > 16: - DD_plan_array.append([ previous_DM, D_DM, DM_step, nDM_step, timeres, 16, nsub ]) + DD_plan_array.append([ previous_DM, D_DM, DM_step, nDM_step, timeres, 16, nsub, total_work_factor ]) else: - DD_plan_array.append([ previous_DM, D_DM, DM_step, nDM_step, timeres, downsample, nsub ]) + DD_plan_array.append([ previous_DM, D_DM, DM_step, nDM_step, timeres, downsample, nsub, total_work_factor ]) previous_DM = D_DM @@ -163,14 +173,29 @@ def dd_plan(centrefreq, bandwidth, nfreqchan, timeres, lowDM, highDM, new_DD_plan_array = [] for dd_line in DD_plan_array: new_dd_lines = [] - while dd_line[3] > max_dms_per_job: + dm_min, dm_max, dm_step, ndm, timeres, downsamp, nsub, total_work_factor = dd_line + while ndm > max_dms_per_job: # previous_DM, D_DM, DM_step, nDM_step, timeres, downsample, nsub - new_dd_lines.append([dd_line[0], dd_line[0] + dd_line[2] * max_dms_per_job, - dd_line[2], max_dms_per_job, dd_line[4], - dd_line[5], dd_line[6]]) - dd_line = [dd_line[0] + dd_line[2] * max_dms_per_job, dd_line[1], - dd_line[2], dd_line[3] - max_dms_per_job, dd_line[4], - dd_line[5], dd_line[6]] + new_dd_lines.append([ + dm_min, + dm_min + dm_step * max_dms_per_job, + dm_step, + max_dms_per_job, + timeres, + downsamp, + nsub, + total_work_factor + ]) + dd_line = [ + dm_min + dm_step * max_dms_per_job, + dm_max, + dm_step, + ndm - max_dms_per_job, + timeres, + downsamp, + nsub, + total_work_factor + ] new_dd_lines.append(dd_line) for n_line in new_dd_lines: new_DD_plan_array.append(n_line) diff --git a/lib/mwa_search/grid_tools.py b/mwa_search/grid_tools.py similarity index 100% rename from lib/mwa_search/grid_tools.py rename to mwa_search/grid_tools.py diff --git a/lib/mwa_search/obs_tools.py b/mwa_search/obs_tools.py similarity index 100% rename from lib/mwa_search/obs_tools.py rename to mwa_search/obs_tools.py diff --git a/nextflow/beamform.nf b/nextflow/beamform.nf index 2b2b41ff..c9aaf322 100644 --- a/nextflow/beamform.nf +++ b/nextflow/beamform.nf @@ -1,27 +1,6 @@ #!/usr/bin/env nextflow -nextflow.enable.dsl = 2 - -params.obsid = null -params.calid = null -params.pointings = null -params.pointing_file = null - -params.begin = null -params.end = null -params.all = false - -params.summed = false -params.channels = null -params.ipfb = false -params.vcstools_version = 'master' -params.mwa_search_version = 'master' - -params.didir = "${params.scratch_basedir}/${params.obsid}/cal/${params.calid}/rts" -params.publish_fits = false -params.publish_fits_scratch = true - -params.no_combined_check = false +nextflow.enable.dsl=2 params.help = false if ( params.help ) { @@ -29,33 +8,31 @@ if ( params.help ) { |Required argurments: | --obsid Observation ID you want to process [no default] | --calid Observation ID of calibrator you want to process [no default] - | --pointings A comma sepertated list of pointings with the RA and Dec seperated + | --begin First GPS time to process [no default] + | --end Last GPS time to process [no default] + | --all Use entire observation span. Use instead of -b & -e. [default: ${params.all}] + | --publish_fits + | Publish to the fits files to the vcs subdirectory. + | + |Pointing arguments (one is required): + | --pointings A comma-separated list of pointings with the RA and Dec separated | by _ in the format HH:MM:SS_+DD:MM:SS, e.g. | "19:23:48.53_-20:31:52.95,19:23:40.00_-20:31:50.00" [default: None] | --pointing_file - | A file containing pointings with the RA and Dec seperated by _ + | A file containing pointings with the RA and Dec separated by _ | in the format HH:MM:SS_+DD:MM:SS on each line, e.g. | "19:23:48.53_-20:31:52.95\\n19:23:40.00_-20:31:50.00" [default: None] - | --begin First GPS time to process [no default] - | --end Last GPS time to process [no default] - | --all Use entire observation span. Use instead of -b & -e. [default: false] - | --publish_fits - | Publish to the fits directory (/group on Galaxy). Include this - | option. + | + |Beamforming types arguments (optional): + | --summed Sum the Stoke paramters [default: ${params.summed}] + | --incoh Also produce an incoherent beam [default: ${params.incoh}] + | --ipfb Also produce a high time resolution Inverse Polyphase Filter Bank beam + | [default: ${params.ipfb}] + | --offringa Use offringa calibration solution instead of RTS [default: ${params.offringa}] | |Optional arguments: - | --summed Add this flag if you the beamformer output as summed polarisations - | (only Stokes I). This reduces the data size by a factor of 4. - | [default: False] - | --ipfb Perform an the inverse PFB to produce high time resolution beamformed - | vdif files [default: false] - | --publish_fits_scratch - | Publish to the scratch fits directory (/astro on Galaxy). Use this - | instead of --publish_fits_scratch | --vcstools_version - | The vcstools module version to use [default: master] - | --mwa_search_version - | The mwa_search module bersion to use [default: master] + | The vcstools module version to use [default: ${params.vcstools_version}] | --no_combined_check | Don't check if all the combined files are available [default: false] | -w The Nextflow work directory. Delete the directory once the processs @@ -68,16 +45,10 @@ if ( params.pointing_file ) { pointings = Channel .fromPath(params.pointing_file) .splitCsv() - .collect() - .flatten() - .collate( params.max_pointings ) } else if ( params.pointings ) { pointings = Channel .from(params.pointings.split(",")) - .collect() - .flatten() - .collate( params.max_pointings ) } else { println "No pointings given. Either use --pointing_file or --pointings. Exiting" @@ -89,15 +60,17 @@ include { pre_beamform; beamform; beamform_ipfb } from './beamform_module' workflow { pre_beamform() if ( params.ipfb ) { - beamform_ipfb( pre_beamform.out[0],\ - pre_beamform.out[1],\ - pre_beamform.out[2],\ - pointings ) + beamform_ipfb( + pre_beamform.out.utc_beg_end_dur, + pre_beamform.out.channels, + pointings + ) } else { - beamform( pre_beamform.out[0],\ - pre_beamform.out[1],\ - pre_beamform.out[2],\ - pointings ) + beamform( + pre_beamform.out.utc_beg_end_dur, + pre_beamform.out.channels, + pointings + ) } } \ No newline at end of file diff --git a/nextflow/beamform_module.nf b/nextflow/beamform_module.nf index ceba66ea..0ea0ef24 100644 --- a/nextflow/beamform_module.nf +++ b/nextflow/beamform_module.nf @@ -1,76 +1,3 @@ -nextflow.enable.dsl = 2 - - -params.obsid = null -params.pointings = null -params.calid = null - -params.begin = 0 -params.end = 0 -params.all = false - -params.summed = false -params.incoh = false -params.channels = null -params.vcstools_version = 'master' -params.mwa_search_version = 'master' - -params.basedir = '/group/mwavcs/vcs' -params.scratch_basedir = '/astro/mwavcs/vcs' -params.offringa = false -if ( params.offringa ) { - params.didir = "${params.scratch_basedir}/${params.obsid}/cal/${params.calid}/offringa" -} -else { - params.didir = "${params.scratch_basedir}/${params.obsid}/cal/${params.calid}/rts" -} -params.publish_fits = false -params.publish_fits_scratch = false - -params.no_combined_check = false - -//Calculate the max pointings used in the launched jobs -if ( params.pointings ) { - max_job_pointings = params.pointings.split(",").size() - if ( max_job_pointings > params.max_pointings ) { - max_job_pointings = params.max_pointings - } -} -else { - // No input pointings (this happens in beamform_fov_sources.nf) so assuming max - max_job_pointings = params.max_pointings -} - -//Work out total obs time -if ( params.all ) { - // an estimation since there's no easy way to make this work - obs_length = 5400 -} -else { - obs_length = params.end - params.begin + 1 -} - -//Calculate expected number of fits files -n_fits = (int) (obs_length/200) -if ( obs_length % 200 != 0 ) { - n_fits = n_fits + 1 -} - - -//Beamforming ipfb duration calc -mb_ipfb_dur = ( obs_length * (params.bm_read + 3 * (params.bm_cal + params.bm_beam) + params.bm_write) + 200 ) * 1.2 - -//Beamforming duration calc -mb_dur = ( obs_length * (params.bm_read + params.bm_cal + max_job_pointings * (params.bm_beam +params.bm_write)) + 200 ) * 1.2 - -//Required temp SSD mem required for gpu jobs -temp_mem = (int) (0.0012 * obs_length * max_job_pointings + 1) -temp_mem_single = (int) (0.0024 * obs_length + 2) -if ( ! params.summed ) { - temp_mem = temp_mem * 4 - temp_mem_single = temp_mem_single *4 -} - // Set up beamformer output types bf_out = " -p " @@ -84,9 +11,9 @@ if ( params.incoh ) { process beamform_setup { output: - file "${params.obsid}_beg_end.txt" - file "${params.obsid}_channels.txt" - file "${params.obsid}_utc.txt" + path "${params.obsid}_beg_end_dur.txt", emit: beg_end_dur + path "${params.obsid}_channels.txt", emit: channels + path "${params.obsid}_utc.txt", emit: utc """ #!/usr/bin/env python @@ -100,17 +27,15 @@ process beamform_setup { if "${params.all}" == "true": beg, end = obs_max_min(${params.obsid}) else: - beg = $params.begin - end = $params.end - with open("${params.obsid}_beg_end.txt", "w") as outfile: + beg = ${params.begin} + end = ${params.end} + dur = end - beg + 1 + with open("${params.obsid}_beg_end_dur.txt", "w") as outfile: spamwriter = csv.writer(outfile, delimiter=',') - spamwriter.writerow([beg, end]) + spamwriter.writerow([beg, end, dur]) # Find the channels - if "$params.channels" == "null": - channels = get_channels($params.obsid) - else: - channels = [$params.channels] + channels = get_channels(${params.obsid}) # Reorder channels to handle the order switch at 128 channels = np.array(channels, dtype=np.int) hichans = [c for c in channels if c>128] @@ -124,7 +49,7 @@ process beamform_setup { # Ensure the metafits files is there ensure_metafits( - "${params.basedir}/${params.obsid}", + "${params.vcsdir}/${params.obsid}", "${params.obsid}", "${params.obsid}_metafits_ppds.fits", ) @@ -135,10 +60,10 @@ process beamform_setup { spamwriter.writerow([gps_to_utc(beg)]) # Make sure all the required directories are made - mdir("${params.scratch_basedir}/${params.obsid}", "Data") - mdir("${params.scratch_basedir}/${params.obsid}", "Products") - mdir("${params.scratch_basedir}/batch", "Batch") - mdir("${params.scratch_basedir}/${params.obsid}/pointings", "Pointings") + mdir("${params.vcsdir}/${params.obsid}", "Data") + mdir("${params.vcsdir}/${params.obsid}", "Products") + mdir("${params.vcsdir}/batch", "Batch") + mdir("${params.vcsdir}/${params.obsid}/pointings", "Pointings") """ } @@ -147,7 +72,7 @@ process combined_data_check { params.no_combined_check == false input: - tuple val(begin), val(end) + tuple val(begin), val(end), val(dur) """ #!/usr/bin/env python @@ -156,8 +81,8 @@ process combined_data_check { from mwa_search.obs_tools import check_data #Perform data checks - dur = $end-$begin + 1 - check = check_data("$params.obsid", beg=$begin, dur=dur) + dur = ${end} - ${begin} + 1 + check = check_data("${params.obsid}", beg=${begin}, dur=dur) if not check: print("ERROR: Recombined check has failed. Cannot continue.") sys.exit(1) @@ -169,228 +94,163 @@ process combined_data_check { process make_beam { label 'gpu' - //time '2h' - time "${mb_dur*task.attempt}s" + label 'vcsbeam' + + time "${ task.attempt * ( Float.valueOf(dur) * ( params.bm_read + params.bm_cal + points.size() * ( params.bm_beam + params.bm_write ) ) + 200 ) * 1.2 }s" errorStrategy 'retry' maxRetries 2 maxForks params.max_gpu_jobs - if ( "$HOSTNAME".startsWith("farnarkle") ) { - clusterOptions = "--gres=gpu:1 --tmp=${temp_mem}GB" - scratch '$JOBFS' - beforeScript "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" - } - else if ( "$HOSTNAME".startsWith("x86") ) { - clusterOptions = "--gres=gpu:1" - scratch '/ssd' - //container = "file:///${params.containerDir}/vcstools/vcstools_${params.vcstools_version}.sif" - beforeScript "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" - } - else if ( "$HOSTNAME".startsWith("garrawarla") ) { - clusterOptions = "--gres=gpu:1 --tmp=${temp_mem}GB" - scratch '/nvmetmp' - //container = "file:///${params.containerDir}/vcstools/vcstools_${params.vcstools_version}.sif" - beforeScript "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" - } - else if ( "$HOSTNAME".startsWith("galaxy") ) { - beforeScript "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" - } - else { - container = "cirapulsarsandtransients/vcstools:${params.vcstools_version}" - } - input: - each channel_pair - val utc - each point - tuple val(begin), val(end) + tuple val(utc), val(begin), val(end), val(dur) + tuple val(channel_id), val(gpubox), val(points) output: - file "*fits" - + tuple val(channel_id), val(points), path("*fits") """ - if $params.offringa; then + if ${params.offringa}; then DI_file="calibration_solution.bin" - jones_option="-O ${params.didir}/calibration_solution.bin -C ${channel_pair[1].toInteger() - 1}" + jones_option="-O ${params.didir}/calibration_solution.bin -C ${gpubox.toInteger() - 1}" else - jones_option="-J ${params.didir}/DI_JonesMatrices_node${channel_pair[1]}.dat" + jones_option="-J ${params.didir}/DI_JonesMatrices_node${gpubox}.dat" fi - make_beam -o $params.obsid -b $begin -e $end -a 128 -n 128 \ --f ${channel_pair[0]} \${jones_option} \ --d ${params.scratch_basedir}/${params.obsid}/combined -P ${point.join(",").replaceAll(~/\s/,"")} \ --r 10000 -m ${params.scratch_basedir}/${params.obsid}/${params.obsid}_metafits_ppds.fits \ -${bf_out} -t 6000 -F ${params.didir}/flagged_tiles.txt -z $utc + make_beam -o ${params.obsid} -b ${begin} -e ${end} -a 128 -n 128 \ +-f ${channel_id} \${jones_option} \ +-d ${params.vcsdir}/${params.obsid}/combined -P ${points.join(",").replaceAll(~/\s/,"")} \ +-r 10000 -m ${params.vcsdir}/${params.obsid}/${params.obsid}_metafits_ppds.fits \ +${bf_out} -t 6000 -F ${params.didir}/flagged_tiles.txt -z ${utc} mv */*fits . """ } process make_beam_ipfb { - publishDir "${params.basedir}/${params.obsid}/pointings/${point}", mode: 'copy', enabled: params.publish_fits, pattern: "*hdr" - publishDir "${params.basedir}/${params.obsid}/pointings/${point}", mode: 'copy', enabled: params.publish_fits, pattern: "*vdif" - publishDir "${params.scratch_basedir}/${params.obsid}/pointings/${point}", mode: 'copy', enabled: params.publish_fits_scratch, pattern: "*hdr" - publishDir "${params.scratch_basedir}/${params.obsid}/pointings/${point}", mode: 'copy', enabled: params.publish_fits_scratch, pattern: "*vdif" - label 'gpu' - //time '2h' - time "${mb_ipfb_dur*task.attempt}s" + label 'vcsbeam' + publishDir "${params.vcsdir}/${params.obsid}/pointings/${point}", mode: 'copy', enabled: params.publish_fits, pattern: "*hdr" + publishDir "${params.vcsdir}/${params.obsid}/pointings/${point}", mode: 'copy', enabled: params.publish_fits, pattern: "*vdif" + + time "${ task.attempt * ( Float.valueOf(dur) * ( params.bm_read + params.bm_cal * ( params.bm_beam + params.bm_write ) ) + 200 ) * 1.2 }s" errorStrategy 'retry' maxRetries 2 maxForks params.max_gpu_jobs - if ( "$HOSTNAME".startsWith("farnarkle") ) { - clusterOptions = "--gres=gpu:1 --tmp=${temp_mem_single}GB" - scratch '$JOBFS' - beforeScript "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" - } - else if ( "$HOSTNAME".startsWith("x86") ) { - clusterOptions = "--gres=gpu:1" - scratch '/ssd' - container = "file:///${params.containerDir}/vcstools/vcstools_${params.vcstools_version}.sif" - } - else if ( "$HOSTNAME".startsWith("garrawarla") ) { - clusterOptions = "--gres=gpu:1 --tmp=${temp_mem_single}GB" - scratch '/nvmetmp' - //container = "file:///${params.containerDir}/vcstools/vcstools_${params.vcstools_version}.sif" - beforeScript "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" - } - else if ( "$HOSTNAME".startsWith("galaxy") ) { - beforeScript "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" - } - else { - container = "cirapulsarsandtransients/vcstools:${params.vcstools_version}" - } - when: point != " " //Don't run if blank pointing given input: - each channel_pair - val utc - each point - tuple val(begin), val(end) + tuple val(utc), val(begin), val(end), val(dur) + tuple val(channel_id), val(gpubox), val(point) output: - file "*fits" - file "*hdr" - file "*vdif" + tuple val(channel_id), val(point), path("*fits"), emit: fits + tuple val(channel_id), val(point), path("*hdr"), path("*vdif"), emit: vdif """ - if $params.offringa; then + if ${params.offringa}; then DI_file="calibration_solution.bin" - jones_option="-O ${params.didir}/calibration_solution.bin -C ${channel_pair[1].toInteger() - 1}" + jones_option="-O ${params.didir}/calibration_solution.bin -C ${gpubox.toInteger() - 1}" else - jones_option="-J ${params.didir}/DI_JonesMatrices_node${channel_pair[1]}.dat" + jones_option="-J ${params.didir}/DI_JonesMatrices_node${gpubox}.dat" fi - if $params.publish_fits; then - mkdir -p -m 771 ${params.basedir}/${params.obsid}/pointings/${point} - fi - if $params.publish_fits_scratch; then - mkdir -p -m 771 ${params.scratch_basedir}/${params.obsid}/pointings/${point} + if ${params.publish_fits}; then + mkdir -p -m 771 ${params.vcsdir}/${params.obsid}/pointings/${point} fi - make_beam -o $params.obsid -b $begin -e $end -a 128 -n 128 \ --f ${channel_pair[0]} \${jones_option} \ --d ${params.scratch_basedir}/${params.obsid}/combined -P ${point} \ --r 10000 -m ${params.scratch_basedir}/${params.obsid}/${params.obsid}_metafits_ppds.fits \ --p -v -t 6000 -F ${params.didir}/flagged_tiles.txt -z $utc -g 11 - ls * + make_beam -o ${params.obsid} -b ${begin} -e ${end} -a 128 -n 128 \ +-f ${channel_id} \${jones_option} \ +-d ${params.vcsdir}/${params.obsid}/combined -P ${point} \ +-r 10000 -m ${params.vcsdir}/${params.obsid}/${params.obsid}_metafits_ppds.fits \ +-p -v -t 6000 -F ${params.didir}/flagged_tiles.txt -z ${utc} -g 11 mv */*fits . """ } process splice { - publishDir "${params.basedir}/${params.obsid}/pointings/${unspliced[0].baseName.split("_")[2]}_${unspliced[0].baseName.split("_")[3]}", mode: 'copy', enabled: params.publish_fits - publishDir "${params.scratch_basedir}/${params.obsid}/pointings/${unspliced[0].baseName.split("_")[2]}_${unspliced[0].baseName.split("_")[3]}", mode: 'copy', enabled: params.publish_fits_scratch label 'cpu' + label 'vcstools' + + publishDir "${params.vcsdir}/${params.obsid}/pointings/${point}", mode: 'copy', enabled: params.publish_fits time '3h' maxForks 300 errorStrategy 'retry' maxRetries 1 input: - val chan - each file(unspliced) + tuple val(chans), val(point), path(unspliced) output: - file "${params.obsid}*fits" - val "${unspliced[0].baseName.split("_")[2]}_${unspliced[0].baseName.split("_")[3]}" - - if ( "$HOSTNAME".startsWith("farnarkle") ) { - beforeScript "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" - } - else if ( "$HOSTNAME".startsWith("x86") ) { - beforeScript "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" - } - else if ( "$HOSTNAME".startsWith("garrawarla") ) { - //container = "file:///${params.containerDir}/vcstools/vcstools_${params.vcstools_version}.sif" - beforeScript "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" - } - else if ( "$HOSTNAME".startsWith("galaxy") ) { - beforeScript "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" - } - else { - container = "cirapulsarsandtransients/vcstools:${params.vcstools_version}" - } + tuple val(point), path("${params.obsid}*fits") """ - splice_wrapper.py -o ${params.obsid} -c ${chan.join(" ")} + splice_wrapper.py -o ${params.obsid} -c ${chans.join(" ")} """ } workflow pre_beamform { + // Performs metadata calls and data checks main: beamform_setup() - combined_data_check(beamform_setup.out[0].splitCsv()) + // Grab outputs from the CSVs + beg_end_dur = beamform_setup.out.beg_end_dur.splitCsv() + channels = beamform_setup.out.channels.splitCsv() + utc = beamform_setup.out.utc.splitCsv().flatten() + + combined_data_check(beamform_setup.out.beg_end_dur.splitCsv()) emit: - beamform_setup.out[0].splitCsv() - beamform_setup.out[1].splitCsv() - beamform_setup.out[2].splitCsv().flatten() + // Combine all the constant metadata and make it a value channel (with collect) so it will be used for each job + // Format: [ utc, begin(GPS), end(GPS), duration(s) ] + utc_beg_end_dur = utc.concat( beg_end_dur ).collect() + // Channel pair in the format [ channel_id, gpubox_id ] + channels } workflow beamform { + // Beamforms MWA voltage data take: - obs_beg_end + // Metadata in the format [ utc, begin(GPS), end(GPS), duration(s) ] + utc_beg_end_dur + // Channel pair in the format [ channel_id, gpubox_id ] channels - utc + // List of pointings in the format HH:MM:SS_+-DD:MM:SS pointings main: - make_beam( channels,\ - utc,\ - pointings,\ - obs_beg_end ) - splice( channels.map{ it -> it[0] }.collect(),\ - make_beam.out.flatten().map { it -> [it.baseName.split("ch")[0], it ] }.\ - groupTuple( size: 24 ).map { it -> it[1] } ) + // Combine the each channel with each pointing (group) so you make a job for each combination + chan_point = channels.combine( pointings.flatten().collate( params.max_pointings ).map{ [ it ] } ) + make_beam( + utc_beg_end_dur, + chan_point + ) + // Make sure the pointings and fits are in the same order then transpose to "flatten" out multiple pointings then group by the pointing for splicing + splice( make_beam.out.map{ chan, pointings, fits -> [ chan, pointings.sort(), [fits].flatten().findAll{ it != null }.sort() ] }.transpose().groupTuple( by: 1, size: 24 ) ) emit: - make_beam.out.flatten().map{ it -> [it.baseName.split("ch")[0], it ] }.groupTuple().map{ it -> it[1] } - splice.out[0].flatten().map{ it -> [it.baseName.split("ch")[0], it ] }.groupTuple().map{ it -> it[1] } - splice.out[1] - splice.out[0] | flatten() | map { it -> [it.baseName.split("_ch")[0].split("${params.obsid}_")[-1], it ] } | groupTuple() + splice.out // [ pointing, fits_file ] } workflow beamform_ipfb { + // Beamforms MWA voltage data and performs and Inverse Polyphase Filter Bank to increase time resolution take: - obs_beg_end + // Metadata in the format [ utc, begin(GPS), end(GPS), duration(s) ] + utc_beg_end_dur + // Channel pair in the format [ channel_id, gpubox_id ] channels - utc + // List of pointings in the format HH:MM:SS_+-DD:MM:SS pointings main: - make_beam_ipfb( channels,\ - utc,\ - pointings.flatten(),\ - obs_beg_end ) - splice( channels.map{ it -> it[0] }.collect(),\ - make_beam_ipfb.out[0].flatten().map { it -> [it.baseName.split("ch")[0], it ] }.\ - groupTuple( size: 24 ).map { it -> it[1] } ) + // Combine the each channel with each pointing so you make a job for each combination + chan_point = channels.combine( pointings.flatten().map{ [ it ] } ) + make_beam_ipfb( + utc_beg_end_dur, + chan_point + ) + // Group by the pointing for splicing + splice( make_beam_ipfb.out.fits.groupTuple( by: 1, size: 24 ) ) emit: - make_beam_ipfb.out[0].flatten().map{ it -> [it.baseName.split("ch")[0], it ] }.groupTuple().map{ it -> it[1] } - splice.out[0].flatten().map{ it -> [it.baseName.split("ch")[0], it ] }.groupTuple().map{ it -> it[1] } - splice.out[1] - splice.out[0] | flatten() | map { it -> [it.baseName.split("_ch")[0].split("${params.obsid}_")[-1], it ] } | groupTuple() + fits = splice.out // [ pointing, fits_file ] + vdif = make_beam_ipfb.out.vdif // [ channel_id, point, hdr, vdif ] } diff --git a/nextflow/benchmark_beamformer.nf b/nextflow/benchmark_beamformer.nf index a0f462da..d5148026 100644 --- a/nextflow/benchmark_beamformer.nf +++ b/nextflow/benchmark_beamformer.nf @@ -1,22 +1,5 @@ #!/usr/bin/env nextflow -nextflow.enable.dsl = 2 - -params.obsid = null -params.calid = null - -params.begin = null -params.end = null -params.all = false - -params.summed = false -params.channels = null -params.vcstools_version = 'master' -params.mwa_search_version = 'master' - -params.didir = "${params.scratch_basedir}/${params.obsid}/cal/${params.calid}/rts" - -params.no_combined_check = false params.help = false if ( params.help ) { @@ -74,7 +57,7 @@ else { process make_pointings { output: - file "*txt" + path "*txt" """ #!/usr/bin/env python @@ -144,13 +127,13 @@ process make_beam { tuple val(begin), val(end) output: - file "make_beam*txt" + path "make_beam*txt" """ make_beam -o $params.obsid -b $begin -e $end -a 128 -n 128 \ -f ${channel_pair[0]} -J ${params.didir}/DI_JonesMatrices_node${channel_pair[1]}.dat \ --d ${params.scratch_basedir}/${params.obsid}/combined -P ${point.join(",")} \ --r 10000 -m ${params.scratch_basedir}/${params.obsid}/${params.obsid}_metafits_ppds.fits \ +-d ${params.vcsdir}/${params.obsid}/combined -P ${point.join(",")} \ +-r 10000 -m ${params.vcsdir}/${params.obsid}/${params.obsid}_metafits_ppds.fits \ ${bf_out} -t 6000 -z $utc &> make_beam_${channel_pair[1]}_n${point.size()}_output.txt rm */*fits """ @@ -163,7 +146,7 @@ process make_beam_ipfb { errorStrategy 'retry' maxRetries 1 maxForks 12 - + if ( "$HOSTNAME".startsWith("farnarkle") ) { clusterOptions = "--gres=gpu:1 --tmp=${temp_mem_single}GB" scratch '$JOBFS' @@ -194,13 +177,13 @@ process make_beam_ipfb { tuple val(begin), val(end) output: - file "make_beam*txt" - + path "make_beam*txt" + """ make_beam -o $params.obsid -b $begin -e $end -a 128 -n 128 \ -f ${channel_pair[0]} -J ${params.didir}/DI_JonesMatrices_node${channel_pair[1]}.dat \ --d ${params.scratch_basedir}/${params.obsid}/combined -P ${point} \ --r 10000 -m ${params.scratch_basedir}/${params.obsid}/${params.obsid}_metafits_ppds.fits \ +-d ${params.vcsdir}/${params.obsid}/combined -P ${point} \ +-r 10000 -m ${params.vcsdir}/${params.obsid}/${params.obsid}_metafits_ppds.fits \ -p -v -t 6000 -z $utc &> make_beam_${channel_pair[1]}_IPFB_output.txt rm */*fits """ @@ -240,13 +223,13 @@ process make_beam_single { tuple val(begin), val(end) output: - file "make_beam*txt" + path "make_beam*txt" """ make_beam -o $params.obsid -b $begin -e $end -a 128 -n 128 \ -f ${channel_pair[0]} -J ${params.didir}/DI_JonesMatrices_node${channel_pair[1]}.dat \ --d ${params.scratch_basedir}/${params.obsid}/combined -R ${point.split("_")[0]} -D ${point.split("_")[1]} \ --r 10000 -m ${params.scratch_basedir}/${params.obsid}/${params.obsid}_metafits_ppds.fits \ +-d ${params.vcsdir}/${params.obsid}/combined -R ${point.split("_")[0]} -D ${point.split("_")[1]} \ +-r 10000 -m ${params.vcsdir}/${params.obsid}/${params.obsid}_metafits_ppds.fits \ -p -z $utc &> make_beam_${channel_pair[1]}_single-pixel_output.txt rm *fits """ @@ -254,11 +237,11 @@ process make_beam_single { process calc_beamformer_benchmarks { input: - file files + path files output: stdout() - + """ calc_beamformer_benchmarks.py --max_pointing_num ${bench_max_pointings} """ diff --git a/nextflow/candidate_TOAs.nf b/nextflow/candidate_TOAs.nf index 26e2d555..b2f7cf9c 100755 --- a/nextflow/candidate_TOAs.nf +++ b/nextflow/candidate_TOAs.nf @@ -1,12 +1,8 @@ #!/usr/bin/env nextflow -nextflow.enable.dsl = 2 -params.no_combined_check = true include { pre_beamform; beamform } from './beamform_module' -//params.out_dir = "${params.search_dir}/${params.obsid}_toas" -//params.out_dir = "${params.search_dir}/psr2_timing/${params.obsid}_toas" -params.out_dir = "${params.search_dir}/psr2_timing" +params.out_dir = "${params.search_dir}/timing" params.bins = 256 params.period = "" @@ -49,7 +45,7 @@ if ( params.help ) { | [default: 1] | | --eph The ephermis file to fold on the obs. If you don't have one use --period and --dm - | --period The topo period of the pulsar in seconds. + | --period The topo period of the pulsar in seconds. | --dm The dispersion measure of the pulsar. | | --out_dir Where the TOAs will be output @@ -92,13 +88,13 @@ process prepfold_ch { time '2h' input: - file fits_files + path fits_files each chans output: - file "*bestprof" - file fits_files - + path "*bestprof" + path fits_files + if ( "$HOSTNAME".startsWith("farnarkle") ) { beforeScript "module use ${params.presto_module_dir}; module load presto/${params.presto_module}" } @@ -122,10 +118,10 @@ process dspsr_ch { time '2h' input: - file fits_files + path fits_files output: - file "*pTDF" + path "*pTDF" if ( "$HOSTNAME".startsWith("farnarkle") ) { beforeScript "module use ${params.presto_module_dir}; module load dspsr/master" @@ -152,11 +148,11 @@ process dspsr_time { time '12h' input: - file fits_files + path fits_files output: - file "*pTDF" - file "*subint" + path "*pTDF" + path "*subint" if ( "$HOSTNAME".startsWith("farnarkle") ) { beforeScript "module use ${params.presto_module_dir}; module load dspsr/master" @@ -181,13 +177,13 @@ process get_toas { publishDir "${params.out_dir}/${archive.baseName.split("_")[0]}", pattern: "*ps", mode: 'copy' input: - //each file(archive) + //each path(archive) //file std_profile - tuple file(archive), file(std_profile) + tuple path(archive), path(std_profile) output: - file "*tim" - file "*ps" + path "*tim" + path "*ps" if ( "$HOSTNAME".startsWith("farnarkle") ) { beforeScript "module use ${params.presto_module_dir}; module load dspsr/master; module load tempo2" @@ -209,11 +205,11 @@ process combine_obs_toas { input: //file toa_tims //file subints - file toa_tims_and_subints + path toa_tims_and_subints output: - file "*_${params.label}.tim" - file "*.ar" + path "*_${params.label}.tim" + path "*.ar" if ( "$HOSTNAME".startsWith("farnarkle") ) { beforeScript "module use ${params.presto_module_dir}; module load dspsr/master; module load tempo2" @@ -234,10 +230,10 @@ process combine_all_toas { when params.fits_file_dir != "None" input: - file toa_tims + path toa_tims output: - file "*all.tim" + path "*all.tim" if ( "$HOSTNAME".startsWith("farnarkle") ) { beforeScript "module use ${params.presto_module_dir}; module load dspsr/master; module load tempo2" diff --git a/nextflow/classifier_module.nf b/nextflow/classifier_module.nf index c0e2c2ae..fd4afa4c 100644 --- a/nextflow/classifier_module.nf +++ b/nextflow/classifier_module.nf @@ -1,31 +1,18 @@ -nextflow.enable.dsl = 2 -params.publish_all_classifer_cands = true -params.out_dir = "${params.search_dir}/${params.obsid}_candidates" - process feature_extract { label 'cpu' + label 'lofar_feature_lab' + time '1h' errorStrategy 'retry' maxRetries 1 - + input: - file pfd_files + tuple path(pfd), path(bestprof), path(ps), path(png) output: - file "*.arff" - path "*pfd*", includeInputs: true - - if ( "$HOSTNAME".startsWith("farnarkle") ) { - beforeScript "module use $params.module_dir; module load PulsarFeatureLab/V1.3.2" - } - else if ( "$HOSTNAME".startsWith("x86") || "$HOSTNAME".startsWith("garrawarla") || "$HOSTNAME".startsWith("galaxy") ) { - container = "file:///${params.containerDir}/lofar_pulsar_ml/lofar_pulsar_ml.sif" - } - else { - container = "cirapulsarsandtransients/pulsarfeaturelab:V1.3.2" - } + tuple path(pfd), path(bestprof), path(ps), path(png), path("*.arff") """ ls @@ -36,23 +23,13 @@ process feature_extract { } process classify { + label 'lofar_ml' + input: - path fex_out - file pfd_files + tuple path(pfd), path(bestprof), path(ps), path(png), path(fex_out) output: - file "feature_extraction*" - path "*pfd*", includeInputs: true - - if ( "$HOSTNAME".startsWith("farnarkle") ) { - beforeScript "module use $params.module_dir; module load LOTAASClassifier/master" - } - else if ( "$HOSTNAME".startsWith("x86") || "$HOSTNAME".startsWith("garrawarla") || "$HOSTNAME".startsWith("galaxy") ) { - container = "file:///${params.containerDir}/lofar_pulsar_ml/lofar_pulsar_ml.sif" - } - else { - container = "cirapulsarsandtransients/pulsarfeaturelab:V1.3.2" - } + tuple path(pfd), path(bestprof), path(ps), path(png), path(fex_out), path("feature_extraction*") """ REALPATH=`realpath ${fex_out}` @@ -69,22 +46,17 @@ process classify { } process sort_detections { + label 'lofar_feature_lab' + publishDir params.out_dir, mode: 'copy', enabled: params.publish_all_classifer_cands input: - file classifier_files - file pfd_files + tuple path(pfd), path(bestprof), path(ps), path(png), path(fex_out), path(classifier_files) output: - file "positive_detections/*" optional true - file "negative_detections/*" optional true - - if ( "$HOSTNAME".startsWith("x86") || "$HOSTNAME".startsWith("garrawarla") || "$HOSTNAME".startsWith("galaxy") ) { - container = "file:///${params.containerDir}/lofar_pulsar_ml/lofar_pulsar_ml.sif" - } - else if ( ! "$HOSTNAME".startsWith("farnarkle") ) { - container = "nickswainston/lofar_pulsar_ml" - } + path "positive_detections/*", optional: true, emit: positive + path "negative_detections/*", optional: true, emit: negative + """ LOTAAS_wrapper.py if [ -f LOTAAS_positive_detections.txt ]; then @@ -105,14 +77,14 @@ process sort_detections { workflow classifier { take: - pfd_files + presto_candiates main: - feature_extract( pfd_files ) - classify( feature_extract.out[0],\ - feature_extract.out[1] ) - sort_detections( classify.out[0],\ - classify.out[1] )//pfd_files ) + // Collate into groups of 30 candidates + collated_cands = presto_candiates.transpose().collate( 30 ).map{ it.transpose() } + feature_extract( collated_cands ) + classify( feature_extract.out ) + sort_detections( classify.out ) emit: - sort_detections.out[0] - sort_detections.out[1] + positive = sort_detections.out.positive + negative = sort_detections.out.negative } diff --git a/nextflow/data_processing_pipeline.nf b/nextflow/data_processing_pipeline.nf index 0d414c7d..71926bd8 100644 --- a/nextflow/data_processing_pipeline.nf +++ b/nextflow/data_processing_pipeline.nf @@ -1,13 +1,5 @@ #!/usr/bin/env nextflow -nextflow.enable.dsl = 2 - -params.obsid = null -params.calid = null - -params.begin = null -params.end = null -params.all = false params.search_radius = 0.00001 params.fwhm_deg = null @@ -15,18 +7,6 @@ params.only_cand_search = false params.offset = 0.0 params.angle_offset = 0.0 -params.vcstools_version = 'master' -params.mwa_search_version = 'master' - -params.didir = "${params.scratch_basedir}/${params.obsid}/cal/${params.calid}/rts" -params.publish_fits = false -params.publish_fits_scratch = true -params.publish_all_classifer_cands = false - -params.out_dir = "${params.search_dir}/${params.obsid}_candidates" - -params.no_combined_check = false -params.max_gpu_jobs = 24 params.help = false if ( params.help ) { @@ -47,11 +27,7 @@ if ( params.help ) { | Only search for pulsar candidates (no known pulsar processing | [default: False] | --publish_fits - | Publish to the fits directory (/group on Galaxy). Use this instead - | of --publish_fits_scratch - | --publish_fits_scratch - | Publish to the scratch fits directory (/astro on Galaxy). Include - | this option. + | Publish to the fits directory (/astro on Galaxy). | --vcstools_version | The vcstools module version to use [default: master] | --mwa_search_version @@ -78,7 +54,7 @@ process fwhm_calc { val channels output: - file "${params.obsid}_fwhm.txt" + path "${params.obsid}_fwhm.txt" """ #!/usr/bin/env python3 @@ -106,10 +82,10 @@ process find_pointings { val fwhm output: - file "${params.obsid}_fov_sources.csv" + path "${params.obsid}_fov_sources.csv" """ - pulsars_in_fov.py -o $params.obsid -b $begin -e $end --fwhm $fwhm --search_radius ${params.search_radius} \ + pulsars_in_fov.py -o ${params.obsid} -b ${begin} -e ${end} --fwhm ${fwhm} --search_radius ${params.search_radius} \ ${no_known_pulsar_command} --offset ${params.offset} --angle_offset ${params.angle_offset} """ } @@ -120,21 +96,23 @@ include { classifier } from './classifier_module' workflow { pre_beamform() - fwhm_calc( pre_beamform.out[1].map{ it -> it[0] }.collect() ) - find_pointings( pre_beamform.out[0], - fwhm_calc.out.splitCsv().flatten() ) - beamform( pre_beamform.out[0],\ - pre_beamform.out[1],\ - pre_beamform.out[2],\ - //Grab the pointings for slow pulsars and single pulses - find_pointings.out.splitCsv(skip: 1, limit: 1).concat(\ - find_pointings.out.splitCsv(skip: 5, limit: 1),\ - find_pointings.out.splitCsv(skip: 7, limit: 1)).collect().flatten().unique().filter{ it != " " }.collate( params.max_pointings ) ) - beamform_ipfb( pre_beamform.out[0],\ - pre_beamform.out[1],\ - pre_beamform.out[2],\ - //Grab the pointings for slow pulsars and single pulses - find_pointings.out.splitCsv(skip: 3, limit: 1) ) + fwhm_calc( pre_beamform.out.channels.map{ it -> it[0] }.collect() ) + find_pointings( + pre_beamform.out.utc_beg_end_dur.map{ [ it[1], it[2] ] }, + fwhm_calc.out.splitCsv().flatten(), + ) + // beamform( pre_beamform.out[0],\ + // pre_beamform.out[1],\ + // pre_beamform.out[2],\ + // //Grab the pointings for slow pulsars and single pulses + // find_pointings.out.splitCsv(skip: 1, limit: 1).concat(\ + // find_pointings.out.splitCsv(skip: 5, limit: 1),\ + // find_pointings.out.splitCsv(skip: 7, limit: 1)).collect().flatten().unique().filter{ it != " " }.collate( params.max_pointings ) ) + // beamform_ipfb( pre_beamform.out[0],\ + // pre_beamform.out[1],\ + // pre_beamform.out[2],\ + // //Grab the pointings for slow pulsars and single pulses + // find_pointings.out.splitCsv(skip: 3, limit: 1) ) // Perform a search on all candidates (not known pulsars) // if pointing in fits file name is in pulsar search pointing list diff --git a/nextflow/dspsr_module.nf b/nextflow/dspsr_module.nf index af920c89..abb28236 100644 --- a/nextflow/dspsr_module.nf +++ b/nextflow/dspsr_module.nf @@ -1,26 +1,16 @@ nextflow.enable.dsl = 2 -params.obsid = null -params.pointings = null - -params.out_dir = "${params.scratch_basedir}/${params.obsid}/pointings" - -params.bins = 128 -params.period = 0.90004 -params.dm = 23.123 -params.subint = 60 -params.nchan = 48 process pdmp { label 'cpu' time '6h' input: - file fits + path fits val pointings output: - file "*pdmp*" + path "*pdmp*" beforeScript "module use ${params.presto_module_dir}; module load dspsr/master" diff --git a/nextflow/find_candidate_position.nf b/nextflow/find_candidate_position.nf index 6eaaba1e..d0b9cf87 100644 --- a/nextflow/find_candidate_position.nf +++ b/nextflow/find_candidate_position.nf @@ -1,31 +1,9 @@ #!/usr/bin/env nextflow -nextflow.enable.dsl = 2 - -params.obsid = null -params.calid = null -params.pointings = null -params.pointing_file = null -params.begin = 0 -params.end = 0 -params.all = false - params.pointing_grid = null params.fraction = 0.8 params.loops = 1 -params.summed = true -params.channels = null -params.vcstools_version = 'master' -params.mwa_search_version = 'master' - -params.bins = 128 -params.subint = 60 -params.nchan = 48 -params.pulsar = 0 -params.period = 0.90004 -params.dm = 23.123 - params.no_pdmp = false params.fwhm_ra = "None" params.fwhm_dec = "None" @@ -81,8 +59,6 @@ if ( params.help ) { | vdif files [default: false] | --publish_fits | Publish to the fits directory (/group on Galaxy). - | --publish_fits_scratch - | Publish to the scratch fits directory (/astro on Galaxy). | --vcstools_version | The vcstools module version to use [default: master] | --mwa_search_version @@ -108,7 +84,7 @@ else { include { pre_beamform; beamform } from './beamform_module' include { fwhm_calc } from './data_processing_pipeline' -params.didir = "${params.scratch_basedir}/${params.obsid}/cal/${params.calid}/rts" +params.didir = "${params.vcsdir}/${params.obsid}/cal/${params.calid}/rts" params.out_dir = "${params.search_dir}/${params.obsid}_candidate_follow_up" params.final_dir = "${params.search_dir}/psr2_J0024-1932/${params.obsid}" @@ -145,7 +121,7 @@ else { process get_pulsar_ra_dec { output: - file 'pulsar_ra_dec.txt' + path 'pulsar_ra_dec.txt' """ #!/usr/bin/env python3 @@ -173,7 +149,7 @@ process grid { tuple val(pulsar), val(pointings), val(fwhm) output: - file "*txt" + path "*txt" """ grid.py -o $params.obsid -d $fwhm -f $params.fraction -p $pointings -l $params.loops --label ${pulsar} @@ -187,11 +163,11 @@ process prepfold { publishDir params.out_dir, mode: 'copy' input: - tuple val(pointing), file(fits_files), val(pulsar) + tuple val(pointing), path(fits_files), val(pulsar) output: - file "*bestprof" - file "*ps" + path "*bestprof" + path "*ps" if ( "$HOSTNAME".startsWith("farnarkle") ) { beforeScript "module use ${params.presto_module_dir}; module load presto/${params.presto_module}" @@ -204,14 +180,12 @@ process prepfold { } //no mask command currently + script: + eph_command = (params.pulsar=="" ? "-p ${params.period} -dm ${params.dm}" : "-par ${pulsar}.eph") + psrcat_command = (params.pulsar=="" ? "" : "psrcat -e ${pulsar} | grep -v UNITS > ${pulsar}.eph") """ - if [ ${params.pulsar} == 0 ]; then - eph_command="-p ${params.period} -dm ${params.dm}" - else - eph_command="-par ${pulsar}.eph" - psrcat -e ${pulsar} | grep -v UNITS > ${pulsar}.eph - fi - prepfold -ncpus ${task.cpus} -o ${params.obsid}_${pointing}_pos -n ${params.bins} \${eph_command} -noxwin -noclip -nsub 256 -npart 120 \ + ${psrcat_command} + prepfold -ncpus ${task.cpus} -o ${params.obsid}_${pointing}_pos -n ${params.bins} ${eph_command} -noxwin -noclip -nsub 256 -npart 120 \ -dmstep 1 -pstep 1 -pdstep 2 -npfact 1 -ndmfact 1 -runavg ${params.obsid}*.fits """ } @@ -225,15 +199,15 @@ process pdmp { params.no_pdmp == false input: - tuple val(pointings), file(bestprof), file(fits_files) + tuple val(pointings), path(bestprof), path(fits_files) output: - file "*ps" - file "*posn" - file "*ar" + path "*ps" + path "*posn" + path "*ar" if ( "$HOSTNAME".startsWith("farnarkle") ) { - beforeScript "module use ${params.presto_module_dir}; module load dspsr/master" + beforeScript "module use ${params.presto_module_dir}; module load dspsr/45c9d433" } else if ( "$HOSTNAME".startsWith("x86") || "$HOSTNAME".startsWith("garrawarla") || "$HOSTNAME".startsWith("galaxy") ) { container = "file:///${params.containerDir}/dspsr/dspsr.sif" @@ -267,12 +241,12 @@ process bestgridpos { publishDir params.out_dir, mode: 'copy' input: - tuple val(pulsar), file(posn_or_bestprof), val(fwhm), val(orig_pointing) + tuple val(pulsar), path(posn_or_bestprof), val(fwhm), val(orig_pointing) output: - file "*predicted_pos.txt" - file "*png" - file "*orig_best_SN.txt" + path "*predicted_pos.txt" + path "*png" + path "*orig_best_SN.txt" """ if [[ ${params.fwhm_ra} == None || ${params.fwhm_dec} == None ]]; then @@ -286,13 +260,13 @@ process bestgridpos { process format_output { publishDir params.final_dir, mode: 'copy' - echo true + debug true input: - tuple file(orig_best_file), file(posn_or_bestprof) + tuple path(orig_best_file), path(posn_or_bestprof) output: - file "*orig_best_predicted_sn.csv" + path "*orig_best_predicted_sn.csv" """ #!/usr/bin/env python3 @@ -343,7 +317,7 @@ process publish_best_pointing { publishDir params.final_dir, mode: 'copy' input: - file fits + path fits output: path '*', includeInputs: true @@ -356,28 +330,28 @@ process publish_best_pointing { workflow find_pos { take: pointings - pre_beamform_1 - pre_beamform_2 - pre_beamform_3 + utc_beg_end_dur + channels fwhm orig_pointing pulsar_pointings main: - beamform( pre_beamform_1, - pre_beamform_2, - pre_beamform_3, - pointings ) + beamform( + utc_beg_end_dur, + channels, + pointings + ) prepfold( // combine pulsar names with fits files - pulsar_pointings.map{ it -> [ it[1], it[0] ] }.concat(beamform.out[3]).\ + pulsar_pointings.map{ it -> [ it[1], it[0] ] }.concat(beamform.out).\ // group by pointing groupTuple().map{ it -> [ it[0], it[1][1], it[1][0] ] } ) pdmp( // combine bestprof and fits files - prepfold.out[0].map{ it -> [it.baseName.split("_pos")[0].split("${params.obsid}_")[1], it ] }.concat(beamform.out[3]) + prepfold.out[0].map{ it -> [it.baseName.split("_pos")[0].split("${params.obsid}_")[1], it ] }.concat(beamform.out) // group by pointing groupTuple().map{ it -> [ it[0], it[1][0], it[1][1] ] } ) if ( params.no_pdmp ) { - if ( params.pulsar == 0 ) { + if ( params.pulsar == "" ) { bestprof_or_pdmp = prepfold.out[0].flatten().map{ it -> [ "cand", it ] }.groupTuple() } else { @@ -385,7 +359,7 @@ workflow find_pos { } } else { - if ( params.pulsar == 0 ) { + if ( params.pulsar == "" ) { bestprof_or_pdmp = pdmp.out[1].flatten().map{ it -> [ "cand", it ] }.groupTuple() } else { @@ -393,11 +367,11 @@ workflow find_pos { } } bestgridpos( bestprof_or_pdmp.combine(fwhm).combine(orig_pointing.toList()) ) - //tuple val(pulsar), file(posn_or_bestprof), val(fwhm), val(orig_pointing) + //tuple val(pulsar), path(posn_or_bestprof), val(fwhm), val(orig_pointing) emit: bestgridpos.out[0] // label and new pointing bestgridpos.out[2] // orig and best pointing SN file - beamform.out[1] // fits files of first grid + beamform.out // fits files of first grid prepfold.out[0].concat(prepfold.out[1]).flatten().map{ it -> [ it.baseName.split("_pos")[0], it ]} // presto outputs pdmp.out[0].concat(pdmp.out[1], pdmp.out[2]).flatten().map{ it -> [ it.baseName.split("_pos")[0], it ]} // dspsr outputs @@ -405,7 +379,7 @@ workflow find_pos { workflow { pre_beamform() - fwhm_calc( pre_beamform.out[1].map{ it -> it[0] }.collect() ) + fwhm_calc( pre_beamform.out.channels.map{ it -> it[0] }.collect() ) // work out intial pointings if ( params.pointing_grid ) { @@ -421,29 +395,35 @@ workflow { orig_pointing = get_pulsar_ra_dec.out.splitCsv().map{ it -> it[1] }.collect() } - find_pos( pointings, - pre_beamform.out[0], - pre_beamform.out[1], - pre_beamform.out[2], - fwhm_calc.out.splitCsv().flatten(), - orig_pointing, - grid.out.splitCsv() ) - beamform( pre_beamform.out[0], - pre_beamform.out[1], - pre_beamform.out[2], - find_pos.out[0].splitCsv().map{ it -> it[1] }.toSortedList().flatten().collate( params.max_pointings ) ) - prepfold( // combine pulsar names with fits files - find_pos.out[0].splitCsv().map{ it -> [ it[1].replaceAll(~/\s/,""), it[0] ] }.concat(beamform.out[3]).\ - // group by pointing - groupTuple().map{ it -> [ it[0], it[1][1], it[1][0] ] } ) - pdmp( // combine bestprof and fits files - prepfold.out[0].map{ it -> [it.baseName.split("_pos")[0].split("${params.obsid}_")[1], it ] }.concat(beamform.out[3]) - // group by pointing - groupTuple().map{ it -> [ it[0], it[1][0], it[1][1] ] } ) + find_pos( + pointings, + pre_beamform.out.utc_beg_end_dur, + pre_beamform.out.channels, + fwhm_calc.out.splitCsv().flatten(), + orig_pointing, + grid.out.splitCsv() + ) + beamform( + pre_beamform.out.utc_beg_end_dur, + pre_beamform.out.channels, + find_pos.out[0].splitCsv(strip: true).map{ it -> it[1] }.toSortedList().flatten().collate( params.max_pointings ) + ) + prepfold( + // combine pulsar names with fits files + find_pos.out[0].splitCsv(strip: true).map{ it -> [ it[1].replaceAll(~/\s/,""), it[0] ] }.concat(beamform.out).\ + // group by pointing + groupTuple().map{ it -> [ it[0], it[1][1], it[1][0] ] } + ) + pdmp( + // combine bestprof and fits files + prepfold.out[0].map{ it -> [it.baseName.split("_pos")[0].split("${params.obsid}_")[1], it ] }.concat(beamform.out) + // group by pointing + groupTuple().map{ it -> [ it[0], it[1][0], it[1][1] ] } + ) // Work out the best pointing if ( params.no_pdmp ) { - if ( params.pulsar == 0 ) { + if ( params.pulsar == "" ) { bestprof_or_pdmp = prepfold.out[0].flatten().map{ it -> [ "cand", it ] }.groupTuple() } else { @@ -451,7 +431,7 @@ workflow { } } else { - if ( params.pulsar == 0 ) { + if ( params.pulsar == "" ) { bestprof_or_pdmp = pdmp.out[1].flatten().map{ it -> [ "cand", it ] }.groupTuple() } else { @@ -462,14 +442,18 @@ workflow { groupTuple().map{ it -> it[1] } ) // Find the best pointing fits file - publish_best_pointing( // The pointing we want - format_output.out.splitCsv( skip: 1 ).map{ it -> [ ("${params.obsid}_" + it[0]).toString(), it[0] ]}.\ - // All fits files - concat(beamform.out[1].concat(find_pos.out[2]).flatten().map{ it -> [ it.baseName.split("_ch")[0], it ] }, - // Add the presto outputs - find_pos.out[3], prepfold.out[0].concat(prepfold.out[1]).flatten().map{ it -> [ it.baseName.split("_pos")[0], it ]}, - // Add the dspsr outputs - find_pos.out[4], pdmp.out[0].concat(pdmp.out[1], pdmp.out[2]).flatten().map{ it -> [ it.baseName.split("_pos")[0], it ]}).\ - // Filter the pointing - groupTuple( size: tuple_size ).map{ it -> it[1].tail() } ) + publish_best_pointing( + // The pointing we want + format_output.out.splitCsv( skip: 1 ).map{ it -> [ ("${params.obsid}_" + it[0]).toString(), it[0] ]} + .concat( + // All fits files + beamform.out.concat(find_pos.out[2]).map{ it -> [ ("${params.obsid}_" + it[0]).toString(), it[1] ]}, + // Add the presto outputs + find_pos.out[3], prepfold.out[0].concat(prepfold.out[1]).flatten().map{ it -> [ it.baseName.split("_pos")[0], it ]}, + // Add the dspsr outputs + find_pos.out[4], pdmp.out[0].concat(pdmp.out[1], pdmp.out[2]).flatten().map{ it -> [ it.baseName.split("_pos")[0], it ]} + ) + // Filter the pointing + .groupTuple( size: tuple_size ).map{ it -> it[1].tail() } + ) } diff --git a/nextflow/mwa_search_pipeline.nf b/nextflow/mwa_search_pipeline.nf index 5718687a..4026d3f1 100644 --- a/nextflow/mwa_search_pipeline.nf +++ b/nextflow/mwa_search_pipeline.nf @@ -1,57 +1,6 @@ #!/usr/bin/env nextflow -nextflow.enable.dsl = 2 - -params.obsid = null -params.calid = null -params.pointings = null -params.pointing_file = null -params.bestprof_pointings = null - -params.begin = 0 -params.end = 0 -params.all = false - -params.dm_min = 1 -params.dm_max = 250 -params.dm_min_step = 0.02 -params.dm_max_step = 0.5 -params.max_dms_per_job = 5000 - -params.summed = true -params.channels = null -params.vcstools_version = 'master' -params.mwa_search_version = 'master' - -params.didir = "${params.scratch_basedir}/${params.obsid}/cal/${params.calid}/rts" -params.out_dir = "${params.search_dir}/${params.obsid}_candidates" - -params.zmax = 0 - -params.no_combined_check = false - - -if ( params.max_dms_per_job != 5000 ) { - // If using non default max_dms_per_job then use a make the groupTuple size sudo infinite - total_dm_jobs = 10000 -} -// If doing an acceleration search, lower the number of DMs per job so the jobs don't time out -else if ( params.zmax == 0 ) { - // Periodic search defaults - total_dm_jobs = 6 -} -else { - // Accel search defaults - total_dm_jobs = 24 - params.max_dms_per_job = 128 -} - -if ( params.bestprof_pointings ) { - bestprof_files = Channel.fromPath("${params.bestprof_pointings}/*.bestprof").collect() -} -else { - bestprof_files = Channel.from(" ") -} +nextflow.enable.dsl=2 params.help = false if ( params.help ) { @@ -62,7 +11,7 @@ if ( params.help ) { | --calid Observation ID of calibrator you want to process [no default] | --begin First GPS time to process [no default] | --end Last GPS time to process [no default] - | --all Use entire observation span. Use instead of -b & -e. [default: false] + | --all Use entire observation span. Use instead of -b & -e. [default: ${params.all}] | |Pointing arguments (one is required): | --pointings A comma-separated list of pointings with the RA and Dec separated @@ -77,7 +26,14 @@ if ( params.help ) { | follow up. The pipeline will beamform on their pointings, prepfold | on their DM and period, and perform a blind search. [default: None] | - | Dedispersion arguments (optional): + |Beamforming types arguments (optional): + | --summed Sum the Stoke paramters [default: ${params.summed}] + | --incoh Also produce an incoherent beam [default: ${params.incoh}] + | --ipfb Also produce a high time resolution Inverse Polyphase Filter Bank beam + | [default: ${params.ipfb}] + | --offringa Use offringa calibration solution instead of RTS [default: ${params.offringa}] + | + |Dedispersion arguments (optional): | --dm_min Minimum DM to search over [default: ${params.dm_min}] | --dm_max Maximum DM to search over [default: ${params.dm_max}] | --dm_min_step @@ -89,41 +45,73 @@ if ( params.help ) { | Lowering this will reduce memory usage and increase parellelisation. | [default: ${params.max_dms_per_job}] | - |Optional arguments: - | --summed Add this flag if you the beamformer output as summed polarisations - | (only Stokes I). This reduces the data size by a factor of 4. - | [default: False] - | --zmax The acceleration range to search over. If you would like to perform - | an acceleration search I recomend you use 200 and set - | --max_dms_per_job 32 - | [default: 0 (no acceleration search)] + |Pulsar search arguments (optional): + | --sp Perform only a single pulse search [default: ${params.sp }] + | --cand Label given to output files [default: ${params.cand }] + | --nharm Number of harmonics to search [default: ${params.nharm }] + | --min_period + | Min period to search for in sec (ANTF min = 0.0013) + | [default: ${params.min_period }] + | --max_period + | Max period to search for in sec (ANTF max = 23.5) + | [default: ${params.max_period }] + | --zmax Maximum acceleration to search (0 will do a simpler periodic search). + | I recomend you use 200 and set --max_dms_per_job 32 + | [default: ${params.zmax }] + | + |Other arguments (optional): | --out_dir Output directory for the candidates files - | [default: ${params.search_dir}/_candidates] - | --ipfb Perform an the inverse PFB to produce high time resolution beamformed - | vdif files [default: false] + | [default: ${params.out_dir}] | --publish_fits - | Publish to the fits directory (/group on Galaxy). - | --publish_fits_scratch - | Publish to the scratch fits directory (/astro on Galaxy). + | Publish to the fits files to the vcs subdirectory. | --vcstools_version - | The vcstools module version to use [default: master] + | The vcstools module version to use [default: ${params.vcstools_version}] | --mwa_search_version - | The mwa_search module bersion to use [default: master] + | The mwa_search module bersion to use [default: ${params.mwa_search_version}] | --no_combined_check - | Don't check if all the combined files are available [default: false] + | Don't check if all the combined files are available [default: ${params.no_combined_check}] | -w The Nextflow work directory. Delete the directory once the processs | is finished [default: ${workDir}]""".stripMargin() println(help) exit(0) } +// Command line argument parsing + +// if ( params.bestprof_pointings ) { +// bestprof_files = Channel.fromPath("${params.bestprof_pointings}/*.bestprof").collect() +// } +// else { +// bestprof_files = Channel.empty() +// } +bestprof_files = Channel.empty() +if ( params.pointing_file ) { + // Grab the pointings from a pointing file + pointings = Channel + .fromPath(params.pointing_file) + .splitCsv() +} +else if ( params.pointings ) { + // Grab the pointings from the command line + pointings = Channel + .of(params.pointings.split(",")) +} +else if ( params.bestprof_pointings ) { + // Grab the pointings from the bestprof files + bestprof_files = Channel.fromPath("${params.bestprof_pointings}/*.bestprof").collect() +} +else { + println "No pointings given. Either use --pointing_file or --pointings. Exiting" + exit(1) +} + + process bestprof_pointings { input: - val pointings - file bestprof_files + path bestprof_files output: - file "${params.obsid}_DM_pointing.csv" + path "${params.obsid}_DM_pointing.csv" """ #!/usr/bin/env python @@ -133,22 +121,16 @@ process bestprof_pointings { from vcstools.pointing_utils import format_ra_dec dm_pointings = [] - if "${params.bestprof_pointings}" == "null": - pointings = ["${pointings.join('", "')}"] - for p in pointings: - ra, dec = format_ra_dec([[p.split("_")[0], p.split("_")[1]]])[0] - dm_pointings.append(["{}_{}".format(ra, dec), "Blind", "None"]) - else: - bestprof_files = glob.glob("*.bestprof") - for bfile_loc in bestprof_files: - pointing = bfile_loc.split("${params.obsid}_")[-1].split("_DM")[0] - ra, dec = format_ra_dec([[pointing.split("_")[0], pointing.split("_")[1]]])[0] - with open(bfile_loc,"r") as bestprof: - lines = bestprof.readlines() - dm = lines[14][22:-1] - period = lines[15][22:-1] - period, period_uncer = period.split(' +/- ') - dm_pointings.append(["{}_{}".format(ra, dec), "dm_{}".format(dm), period]) + bestprof_files = ["${bestprof_files.join('", "')}"] + for bfile_loc in bestprof_files: + pointing = bfile_loc.split("${params.obsid}_")[-1].split("_DM")[0] + ra, dec = format_ra_dec([[pointing.split("_")[0], pointing.split("_")[1]]])[0] + with open(bfile_loc,"r") as bestprof: + lines = bestprof.readlines() + dm = lines[14][22:-1] + period = lines[15][22:-1] + period, period_uncer = period.split(' +/- ') + dm_pointings.append([f"{ra}_{dec}", f"dm_{dm}_{ra}_{dec}", dm, period]) with open("${params.obsid}_DM_pointing.csv", "w") as outfile: spamwriter = csv.writer(outfile, delimiter=',') @@ -159,6 +141,8 @@ process bestprof_pointings { process follow_up_fold { label 'cpu' + label 'presto' + time "6h" publishDir params.out_dir, mode: 'copy' errorStrategy 'retry' @@ -168,20 +152,11 @@ process follow_up_fold { params.bestprof_pointings != null input: - tuple file(fits_files), val(dm), val(period) + tuple path(fits_files), val(dm), val(period) output: - file "*pfd*" + path "*pfd*" - if ( "$HOSTNAME".startsWith("farnarkle") || "$HOSTNAME".startsWith("galaxy") ) { - beforeScript "module use ${params.presto_module_dir}; module load presto/${params.presto_module}" - } - else if ( "$HOSTNAME".startsWith("x86") || "$HOSTNAME".startsWith("garrawarla") ) { - container = "file:///${params.containerDir}/presto/presto.sif" - } - else { - container = "nickswainston/presto:realfft_docker" - } """ # Set up the prepfold options to match the ML candidate profiler temp_period=${Float.valueOf(period)/1000} @@ -204,47 +179,63 @@ process follow_up_fold { ndmfact=`echo "1 + 1/(\$ddm*\$nbins)" | bc` echo "ndmfact: \$ndmfact ddm: \$ddm" - prepfold -ncpus $task.cpus -o follow_up_${params.obsid}_P${period.replaceAll(~/\s/,"")}_DM${dm} -n \$nbins -dm ${dm} -p \$period -noxwin -noclip -nsub 256 \ + prepfold -ncpus ${task.cpus} -o follow_up_${params.obsid}_P${period.replaceAll(~/\s/,"")}_DM${dm} -n \$nbins -dm ${dm} -p \$period -noxwin -noclip -nsub 256 \ -npart \$ntimechunk -dmstep \$dmstep -pstep 1 -pdstep 2 -npfact \$period_search_n -ndmfact \$ndmfact -runavg *.fits """ } -if ( params.pointing_file ) { - pointings = Channel - .fromPath(params.pointing_file) - .splitCsv() - .collect() -} -else if ( params.pointings ) { - pointings = Channel - .from(params.pointings.split(",")) - .collect() -} -else if ( params.bestprof_pointings ) { - pointings = Channel.from("null") -} -else { - println "No pointings given. Either use --pointing_file or --pointings. Exiting" - exit(1) -} - include { pre_beamform; beamform } from './beamform_module' -include { pulsar_search } from './pulsar_search_module' -include { classifier } from './classifier_module' +include { pulsar_search } from './pulsar_search_module' +include { classifier } from './classifier_module' workflow { - bestprof_pointings( pointings, - bestprof_files ) + // Parse pointing input + if ( params.pointing_file ) { + // Grab the pointings from a pointing file + pointings = Channel.fromPath(params.pointing_file).splitCsv() + } + else if ( params.pointings ) { + // Grab the pointings from the command line + pointings = Channel.of(params.pointings.split(",")) + } + else if ( params.bestprof_pointings ) { + // Grab the pointings from the bestprof files + bestprof_files = Channel.fromPath("${params.bestprof_pointings}/*.bestprof").collect() + // If given bestprof files, extract pointings from them + bestprof_pointings( bestprof_files ) + bestprof_data = bestprof_pointings.out.splitCsv() + // Only grab the pointings + pointings = bestprof_data.map{ point, name, dm, period -> point } + } + else { + println "No pointings given. Either use --pointing_file or --pointings. Exiting" + exit(1) + } + + // Beamform pre_beamform() - beamform( pre_beamform.out[0],\ - pre_beamform.out[1],\ - pre_beamform.out[2],\ - bestprof_pointings.out.splitCsv().map{ it -> it[0] }.flatten().unique().collate( params.max_pointings ) ) - follow_up_fold( beamform.out[1].map{ it -> [ it[0].getBaseName().split("/")[-1].split("_ch")[0], it ] }.cross( - bestprof_pointings.out.splitCsv().map{ it -> ["${params.obsid}_"+it[0], it[1], it[2]]}.map{ it -> [it[0].toString(), [it[1], it[2]]] }).\ - map{ it -> [it[0][1], it[1][1][0].split("_")[-1], it[1][1][1]] } ) - pulsar_search( beamform.out[1].map{ it -> [ it[0].getBaseName().split("/")[-1].split("_ch")[0], it ] }.concat( - bestprof_pointings.out.splitCsv().map{ it -> ["${params.obsid}_"+it[0], it[1]]}).map{ it -> [it[0].toString(), it[1]] }.\ - groupTuple( size: 2 ).map{ it -> [it[1][1]+"_"+it[0], it[1][0]] } ) - classifier( pulsar_search.out[1].flatten().collate( 120 ) ) + beamform( + pre_beamform.out.utc_beg_end_dur, + pre_beamform.out.channels, + pointings + ) + + // If doing follow up also do a fold + if ( params.bestprof_pointings ) { + // Grab name that includes DM so the ddplan will only use nearby DMs + bestprof_data_fits = bestprof_data.concat( beamform.out ).groupTuple() + // Do a follow up fold if bestprofs given + follow_up_fold( + bestprof_data_fits.map{ pointing, name_fits, dm, period -> [ name_fits[1], dm[0], period[0] ] } + ) + name_fits = bestprof_data_fits.map{ pointing, name_fits, dm, period -> name_fits } + } + else { + // Make a name for each fits file + name_fits = beamform.out.map{ pointing, fits -> [ "${params.cand}_${params.obsid}_${pointing}".toString(), fits ] } + } + + // Perform pulsar search + pulsar_search( name_fits ) + classifier( pulsar_search.out ) } diff --git a/nextflow/nextflow.config b/nextflow/nextflow.config index f85e0426..d5f33fe0 100644 --- a/nextflow/nextflow.config +++ b/nextflow/nextflow.config @@ -1,249 +1,454 @@ #!/usr/bin/env nextflow -process { - withLabel: 'gpu|cpu' { - executor = 'slurm' - cpus = 1 - } + +// BEAMFORMING OPTIONS +// ---------------------------------------------------------------------------- +params.obsid = "no_obsid" // MWA observation ID (GPS) +params.calid = "no_calid" // MWA calibration observation ID (GPS) +params.pointings = null // Space seperated pointings in the format HH:MM:SS_DD:MM:SS +params.pointing_file = null // Path to a file containing a pointing on each line in the format HH:MM:SS_DD:MM:SS +params.bestprof_pointings = null // Directory of bestprof files from which pointings will be extracted + +// Observation length options +params.begin = 0 // Beginning of the observation (GPS) +params.end = 0 // End of the observation (GPS) +params.all = false // Use the whole obervation (GPS) + +// Beamforming type options +params.summed = false // Sum the Stoke paramters +params.incoh = false // Also produce an incoherent beam +params.ipfb = false // Also produce a high time resolution Inverse Polyphase Filter Bank beam +params.offringa = false // Use offringa calibration solution instead of RTS +// How much temp memory (MB) needed per second per file +if ( params.summed ) { + params.temp_mem_sec = 1.25 +} +else { + params.temp_mem_sec = 5 } -// This should always be overwritten -params.obsid = "no_obsid" -hostname = "$HOSTNAME" +// PULSAR SEARCH OPTIONS +// ---------------------------------------------------------------------------- +params.cand = "Blind" // Label given to output files +params.sp = false // Perform only a single pulse search +params.publish_all_prepfold = false // Output all prepfold files (redundant if using the calssifier module) -if ( hostname.startsWith("galaxy") ) { - process { - withLabel: 'gpu|cpu|cpu_large_mem|download' { - cpus = 1 - } - withLabel: gpu { - queue = 'gpuq' - executor = 'slurm' - } - withLabel: cpu { - queue = 'workq' - executor = 'slurm-magnus' - clusterOptions = '--account=pawsey0348' - } - withLabel: cpu_large_mem { - queue = 'workq' - executor = 'slurm-magnus' - clusterOptions = '--account=pawsey0348' - } - withLabel: download { - queue = 'copyq' - executor = 'slurm-zeus' - memory = "10 GB" - } - cache = 'lenient' +// Dispersion Measure options +params.dm_min = 1 // Minimum DM +params.dm_max = 250 // Maximum DM +params.dm_min_step = 0.02 // Minimum DM step (lowering increases sensitivity) +params.dm_max_step = 0.5 // Maximum DM step (lowering increases sensitivity) +params.max_dms_per_job = 5000 // Maximum number of DM steps per job. Decrease to make smaller jobs +params.max_work_function = 300 // Maximum total work function per job. Decrease to make smaller jobs +params.max_folds_per_job = 5 // Maximum number prepfolds per job. Decrease to make smaller jobs + +// Defaults for the accelsearch command +params.nharm = 16 // Number of harmonics to search +params.min_period = 0.001 // Min period to search for in sec (ANTF min = 0.0013) +params.max_period = 30 // Max period to search for in sec (ANTF max = 23.5) +params.zmax = 0 // Maximum acceleration to search (0 will do a simpler periodic search) + +// This is a module load command that only OzSTAR needs +params.presto_python_load = "" + + +// FOLLOW UP OPTIONS +// ---------------------------------------------------------------------------- +params.bins = 128 // Number of profile bins +params.subint = 60 // Number of time subintergrations +params.nchan = 48 // Number of frequency subchannels +params.pulsar = "" // Pulsar name +params.period = 0.90004 // Period in seconds +params.dm = 23.123 // Dispersion measure + + +// SOFTWARE VERSION OPTIONS +// ---------------------------------------------------------------------------- +params.vcstools_version = 'master' +params.mwa_search_version = 'master' +params.presto_version = 'v4.0_7ec3c83' + + +// OUTPUT VISUALISATION OPTIONS +// ---------------------------------------------------------------------------- +params.output_vis = false +if ( params.output_vis ) { + dag { + enabled = true + overwrite = true + file = "${params.output_vis}_dag.png" + } + report { + enabled = true + overwrite = true + file = "${params.output_vis}_report.html" + } + timeline { + enabled = true + overwrite = true + file = "${params.output_vis}_timeline.html" } + trace { + enabled = true + overwrite = true + file = "${params.output_vis}_trace.txt" + } +} + + +// Memory and duration calculation fucntions +// ---------------------------------------------------------------------------- +def vcsbeam_temp_mem(dur, npoints) { + // Return memory on SSD/NVME needed fo the VCSbeam files in MB + // dur is in seconds + // npoints is number of pointins which is set as 2.5 for the IPFB to allow enough room + return (int) ( params.temp_mem_sec * Float.valueOf(dur) * npoints + 2000 ) +} +def presto_temp_mem(dur, ndms_job) { + // Return memory on SSD/NVME needed fo the presto search files in MB + // dur is in seconds + // ndms_job is the number of dedispersion jobs + return (int) ( 0.12 * Float.valueOf(dur) * Float.valueOf(ndms_job) ) +} - //executor.queueSize = 90 +// CLUSTER SPECFIC DEFAULTS +// ---------------------------------------------------------------------------- +def hostname = "hostname".execute().text.trim().replace("-", "") +if ( hostname.startsWith("garrawarla") ) { + // Set up for Pawsey's Garrawarla cluster + + // Set up containers process.module = 'singularity/3.7.4' singularity { enabled = true - //cacheDir = '/pawsey/mwa/singularity' - runOptions = '--nv' + runOptions = '--nv -B /nvmetmp' envWhitelist = 'SINGULARITY_BINDPATH, SINGULARITYENV_LD_LIBRARY_PATH' } params.containerDir = '/pawsey/mwa/singularity' - params.module_dir = '/group/mwa/software/modulefiles' - params.presto_module_dir = '/group/mwa/software/modulefiles' - params.presto_module = 'v3.0.1' - params.basedir = '/group/mwavcs/vcs' - params.scratch_basedir = '/astro/mwavcs/vcs' - params.search_dir = '/astro/mwavcs/pulsar_search' - - workDir = "/astro/mwavcs/${USER}/${params.obsid}_work" - - //Beamforming benchmarks - params.bm_read = 1.103 - params.bm_cal = 0.209 - params.bm_beam = 0.256 - params.bm_write = 0.062 + // Default directories + params.module_dir = '/pawsey/mwa/software/python3/modulefiles' + params.presto_module_dir = '/pawsey/mwa/software/python3/modulefiles' + params.presto_module = 'master' + params.vcsdir = "/astro/mwavcs/${USER}" + params.search_dir = "/astro/mwavcs/${USER}/pulsar_search" + workDir = "/astro/mwavcs/${USER}/${params.obsid}_work" - params.max_pointings = 25 - params.max_cpus_per_node = 8 - params.max_gpu_jobs = 120 -} -if ( hostname.startsWith("garrawarla") ) { process { - withLabel: 'gpu|cpu|cpu_large_mem' { + cache = 'lenient' + // Resource set up + withLabel: 'gpu|cpu|download' { cpus = 1 + executor = 'slurm' + memory = "10 GB" } withLabel: gpu { queue = 'gpuq' - executor = 'slurm' - memory = "10 GB" } withLabel: cpu { queue = 'workq' - executor = 'slurm' - memory = "10 GB" } - withLabel: cpu_large_mem { - queue = 'workq' - executor = 'slurm' - memory = "8 GB" - } - withLabel: cpu_any { - queue = 'workq' - executor = 'slurm' - memory = "10 GB" + withLabel: large_mem { + memory = "16 GB" } withLabel: download { queue = 'copyq' executor = 'slurm-zeus' - memory = "10 GB" } - cache = 'lenient' + // Software dependency set up + withLabel: vcsbeam { + if ( params.ipfb ) { + clusterOptions = { "--gres=gpu:1 --tmp=${ vcsbeam_temp_mem(dur, 2.5) }MB" } + } + else { + clusterOptions = { "--gres=gpu:1 --tmp=${ vcsbeam_temp_mem(dur, points.size()) }MB" } + } + scratch = '/nvmetmp' + beforeScript = "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" + } + withLabel: vcstools { + beforeScript = "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" + } + withLabel: presto_search { + container = "file:///${params.containerDir}/presto/presto_${params.presto_version}.sif" + clusterOptions = { "--export=NONE --tmp=${ presto_temp_mem(dur, ndms_job) }MB" } + scratch = '/nvmetmp' + } + withLabel: presto { + container = "file:///${params.containerDir}/presto/presto_${params.presto_version}.sif" + } + withLabel: sps { + container = "file:///${params.containerDir}/sps/sps.sif" + } + withLabel: lofar_ml { + container = "file:///${params.containerDir}/lofar_pulsar_ml/lofar_pulsar_ml.sif" + } + withLabel: lofar_feature_lab { + container = "file:///${params.containerDir}/lofar_pulsar_ml/lofar_pulsar_ml.sif" + } } - - executor.$slurm.queueSize = 1000 - //executor.dumpInterval = '1sec' - //executor.pollInterval = '1sec' executor.submitRateLimit = '100 sec' - - process.module = 'singularity/3.7.4' - singularity { - enabled = true - //cacheDir = '/pawsey/mwa/singularity' - runOptions = '--nv -B /nvmetmp' - envWhitelist = 'SINGULARITY_BINDPATH, SINGULARITYENV_LD_LIBRARY_PATH' - } - params.containerDir = '/pawsey/mwa/singularity' - - params.module_dir = '/pawsey/mwa/software/python3/modulefiles' - params.presto_module_dir = '/pawsey/mwa/software/python3/modulefiles' - params.presto_module = 'master' - params.basedir = '/group/mwavcs/vcs' - params.scratch_basedir = '/astro/mwavcs/vcs' - params.search_dir = '/astro/mwavcs/pulsar_search' - - workDir = "/astro/mwavcs/${USER}/${params.obsid}_work" - - //Beamforming benchmarks + // Beamforming benchmarks //params.bm_read = 0.172 //this read benchmark is not always sufficent //params.bm_read = 3.000 //overkill params.bm_read = 1.000 params.bm_cal = 0.091 params.bm_beam = 0.033 - //params.bm_write = 0.039 params.bm_write = 0.390 - params.max_pointings = 20 - params.max_cpus_per_node = 38 // leaves 2 for the gpuq - params.max_gpu_jobs = 48 + // PRESTO scaling benchmarks (will be adjusted based on observation duration and number of DM steps) + params.search_scale = 5.0 + params.prepfold_scale = 16.0 + + // Job number controls + executor.$slurm.queueSize = 1000 + params.max_pointings = 20 + params.max_gpu_jobs = 48 + params.max_search_jobs = 400 + params.max_cpus_per_node = 38 // leaves 2 for the gpuq } else if ( hostname.startsWith("farnarkle") ) { + // Set up for Swinburnes's Ozstar cluster + + // Set up containers + singularity { + enabled = true + // runOptions = '-B /fred --nv' + runOptions = '-B /fred' + } + params.containerDir = '/fred/oz125/container_images' + + // Default directories + params.module_dir = '/fred/oz125/software/modulefiles' + params.presto_module_dir = '/apps/users/pulsar/skylake/modulefiles' + params.presto_module = 'd6265c2' + params.vcsdir = '/fred/oz125/vcs' + params.search_dir = "/fred/oz125/${USER}/pulsar_search" + workDir = "/fred/oz125/${USER}/${params.obsid}_work" + process { - withLabel: 'gpu|cpu|cpu_large_mem' { + module = 'apptainer/latest' + cache = 'lenient' + // Resource set up + withLabel: 'gpu|cpu' { executor = 'slurm' - cpus = 1 + cpus = 1 } withLabel: gpu { - queue = 'skylake-gpu' + queue = 'skylake-gpu' memory = "25 GB" } withLabel: cpu { - queue = 'skylake' + queue = 'skylake' memory = "3 GB" } - withLabel: cpu_large_mem { - queue = 'skylake' + withLabel: large_mem { memory = "8 GB" } - cache = 'lenient' + // Software dependency set up + withLabel: vcsbeam { + if ( params.ipfb ) { + clusterOptions = { "--gres=gpu:1 --tmp=${ vcsbeam_temp_mem(dur, 2.5) }MB" } + } + else { + clusterOptions = { "--gres=gpu:1 --tmp=${ vcsbeam_temp_mem(dur, points.size()) }MB" } + } + scratch = '$JOBFS' + beforeScript = "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" + } + withLabel: vcstools { + beforeScript = "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" + } + withLabel: presto_search { + beforeScript = "module use ${params.module_dir}; module load presto/min_path" + clusterOptions = { "--export=NONE --tmp=${ presto_temp_mem(dur, ndms_job) }MB" } + scratch = '$JOBFS' + } + withLabel: presto { + container = "file:///${params.containerDir}/presto/presto_${params.presto_version}" + // tmp storage for container image + clusterOptions = { "--tmp=2000MB" } + scratch = '$JOBFS' + } + withLabel: sps { + container = "file:///${params.containerDir}/sps/sps_sandbox" + // tmp storage for container image + clusterOptions = { "--tmp=2000MB" } + scratch = '$JOBFS' + } + withLabel: lofar_ml { + beforeScript = "module use ${params.module_dir}; module load LOTAASClassifier/master" + } + withLabel: lofar_feature_lab { + beforeScript = "module use ${params.module_dir}; module load PulsarFeatureLab/V1.3.2" + } } - - executor.$slurm.queueSize = 1000 - executor.dumpInterval = '10sec' - executor.pollInterval = '10sec' executor.submitRateLimit = '100 sec' + // This is a module load command that only OzSTAR needs + // params.presto_python_load = "module use ${params.presto_module_dir}; module load presto/${params.presto_module}; module load python/2.7.14; module load matplotlib/2.2.2-python-2.7.14" + params.presto_python_load = "module use ${params.presto_module_dir}; module load presto/${params.presto_module}"//"; module load python/2.7.14; module load matplotlib/2.2.2-python-2.7.14" - process.module = 'apptainer/latest' - singularity { - enabled = true - //cacheDir = '/fred/oz125/container_images' - //autoMounts = true - runOptions = '-B /fred --nv' - } - params.containerDir = '/fred/oz125/container_images' - - params.module_dir = '/fred/oz125/software/modulefiles' - params.presto_module_dir = '/apps/users/pulsar/skylake/modulefiles' - params.presto_module = 'd6265c2' - params.basedir = '/fred/oz125/vcs' - params.scratch_basedir = '/fred/oz125/vcs' - params.search_dir = '/fred/oz125/nswainst/pulsar_search' - - workDir = "/fred/oz125/${USER}/${params.obsid}_work" - - //Beamforming benchmarks - params.bm_read = 0.366 + // Beamforming benchmarks + params.bm_read = 1.200 // 0.366 params.bm_cal = 0.115 params.bm_beam = 0.041 params.bm_write = 0.071 - params.max_pointings = 120 - params.max_gpu_jobs = 120 + // PRESTO scaling benchmarks (will be adjusted based on observation duration and number of DM steps) + params.search_scale = 5.0 + params.prepfold_scale = 24.0 + + // Job number controls + executor.$slurm.queueSize = 1000 + params.max_pointings = 120 + params.max_gpu_jobs = 120 + params.max_search_jobs = 800 } else if ( hostname.startsWith("x86") ) { + // Set up for the China SKA Region Centre Prototype's ARM cluster + process { + cache = 'lenient' + // Resource set up withLabel: 'gpu|cpu|cpu_large_mem' { executor = 'slurm' - cpus = 1 + cpus = 1 } withLabel: gpu { - queue = 'all-gpu' + queue = 'all-gpu' memory = "30 GB" } withLabel: cpu { - queue = 'purley-cpu' + queue = 'purley-cpu' memory = "3 GB" } - withLabel: cpu_large_mem { - queue = 'purley-cpu' + withLabel: large_mem { memory = "8 GB" } - cache = 'lenient' + // Software dependency set up + withLabel: vcsbeam { + clusterOptions = "--gres=gpu:1" + scratch = '/ssd' + //container = "file:///${params.containerDir}/vcstools/vcstools_${params.vcstools_version}.sif" + beforeScript = "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" + } + withLabel: vcstools { + beforeScript = "module use ${params.module_dir}; module load vcstools/${params.vcstools_version}" + } + withLabel: presto_search { + container = "file:///${params.containerDir}/presto/presto.sif" + clusterOptions = { "--export=NONE --tmp=${ presto_temp_mem(dur, ndms_job) }MB" } + scratch = '/ssd' + } + withLabel: presto { + container = "file:///${params.containerDir}/presto/presto.sif" + } + withLabel: sps { + container = "nickswainston/sps" + } + withLabel: lofar_ml { + container = "file:///${params.containerDir}/lofar_pulsar_ml/lofar_pulsar_ml.sif" + } + withLabel: lofar_feature_lab { + container = "file:///${params.containerDir}/lofar_pulsar_ml/lofar_pulsar_ml.sif" + } } - - executor.$slurm.queueSize = 1000 - executor.dumpInterval = '10sec' - executor.pollInterval = '10sec' executor.submitRateLimit = '100 sec' + // Set up containers singularity { enabled = true - //cacheDir = '/o9000/MWA/Pulsar/vcs/singularity_images' - //autoMounts = true - //runOptions = '-B /o9000 --nv' runOptions = '-B /o9000' } params.containerDir = '/o9000/MWA/Pulsar/vcs/singularity_images' - //params.module_dir = '/home/nick/modules' - params.module_dir = '/home/app/modulefiles/' + // Default directories + params.module_dir = '/home/app/modulefiles/' params.presto_module_dir = '/home/app/modulefiles/' - params.presto_module = 'cpu-master' - params.basedir = '/o9000/MWA/Pulsar/vcs' - params.scratch_basedir = '/o9000/MWA/Pulsar/vcs' - params.search_dir = '/o9000/MWA/Pulsar/vcs/candidates' - - workDir = "/o9000/MWA/Pulsar/vcs/${params.obsid}/${env.USER}_work" + params.presto_module = 'cpu-master' + params.vcsdir = '/o9000/MWA/Pulsar/vcs' + params.search_dir = '/o9000/MWA/Pulsar/vcs/candidates' + workDir = "/o9000/MWA/Pulsar/vcs/${params.obsid}/${env.USER}_work" - //Beamforming benchmarks + // Beamforming benchmarks //params.bm_read = 0.266 previous benchmark params.bm_read = 0.366*2 params.bm_cal = 0.070*2 params.bm_beam = 0.120*2 params.bm_write = 0.013*2 - params.max_pointings = 120 - params.max_gpu_jobs = 120 + // PRESTO scaling benchmarks (will be adjusted based on observation duration and number of DM steps) + params.search_scale = 10.0 + params.prepfold_scale = 50 + + // Job number controls + executor.$slurm.queueSize = 1000 + params.max_pointings = 120 + params.max_gpu_jobs = 120 + params.max_search_jobs = 800 +} +else { + // No recognised hostname so assuming defaults + + // Resource set up + executor.name = 'local' + executor.queueSize = 8 + + // Software dependency set up + process { + withLabel: vcsbeam { + container = "cirapulsarsandtransients/vcstools:${params.vcstools_version}" + } + withLabel: vcstools { + container = "cirapulsarsandtransients/vcstools:${params.vcstools_version}" + } + withLabel: 'presto_search|presto' { + container = "nickswainston/presto:${params.presto_version}" + } + withLabel: lofar_ml { + container = "nickswainston/lofar_pulsar_ml" + } + withLabel: lofar_feature_lab { + container = "cirapulsarsandtransients/pulsarfeaturelab:V1.3.2" + } + } + + // Set up containers + docker.enabled = true + + // Default directories + params.vcsdir = './' + params.search_dir = './' + + // Beamforming benchmarks + params.bm_read = 0.366*2 + params.bm_cal = 0.070*2 + params.bm_beam = 0.120*2 + params.bm_write = 0.013*2 + + // PRESTO scaling benchmarks (will be adjusted based on observation duration and number of DM steps) + params.search_scale = 10.0 + params.prepfold_scale = 50 + + // Job number controls + params.max_pointings = 10 + params.max_gpu_jobs = 1 + params.max_search_jobs = 16 +} + + +// File and directory optins +params.no_combined_check = false // Don't perform a check of the VCS files +params.publish_fits = false // Publish the fits files to the sub directories +params.publish_all_classifer_cands = true +params.out_dir = "${params.search_dir}/${params.obsid}_candidates" +// Default directory of calibration solutions +if ( params.offringa ) { + params.didir = "${params.vcsdir}/${params.obsid}/cal/${params.calid}/offringa" } +else { + params.didir = "${params.vcsdir}/${params.obsid}/cal/${params.calid}/rts" +} \ No newline at end of file diff --git a/nextflow/pdmp.nf b/nextflow/pdmp.nf index e0648659..56b64e16 100644 --- a/nextflow/pdmp.nf +++ b/nextflow/pdmp.nf @@ -1,24 +1,16 @@ #!/usr/bin/env nextflow -nextflow.enable.dsl = 2 include pdmp_wf from './dspsr_module' -params.obsid = null -params.pointings = null -params.out_dir = "${params.scratch_basedir}/${params.obsid}/pointings" +params.out_dir = "${params.vcsdir}/${params.obsid}/pointings" -params.bins = 128 -params.period = 0.90004 -params.dm = 23.123 -params.subint = 60 -params.nchan = 48 if ( params.pointings ) { pointings = Channel.from(params.pointings) } -fits = Channel.fromPath( "${params.basedir}/${params.obsid}/pointings/${params.pointings}/${params.obsid}*fits" ).collect() +fits = Channel.fromPath( "${params.vcsdir}/${params.obsid}/pointings/${params.pointings}/${params.obsid}*fits" ).collect() workflow { pdmp_wf( fits, diff --git a/nextflow/pulsar_search.nf b/nextflow/pulsar_search.nf index fbe0d3ec..7fe627e5 100644 --- a/nextflow/pulsar_search.nf +++ b/nextflow/pulsar_search.nf @@ -1,25 +1,5 @@ #!/usr/bin/env nextflow -nextflow.enable.dsl = 2 - -params.obsid = null -params.fits_file = null -params.out_dir = "${params.search_dir}/${params.obsid}_candidates" - -params.dm_min = 1 -params.dm_max = 250 -params.dm_min_step = 0.02 -params.dm_max_step = 0.5 -params.max_dms_per_job = 5000 - -params.cand = "Blind" -params.sp = false - -//Defaults for the accelsearch command -params.nharm = 16 // number of harmonics to search -params.min_period = 0.001 // min period to search for in sec (ANTF min = 0.0013) -params.max_period = 30 // max period to search for in sec (ANTF max = 23.5) -params.zmax = 0 // don't do an accelsearch by default params.help = false if ( params.help ) { @@ -27,29 +7,36 @@ if ( params.help ) { | The fits files must be in the format | __ch-_00??.fits |Required argurments: - | --obsid Observation ID you want to process [no default] - | --fits_file The fits file to search [no default] - | --dur Duration of the fits file in seconds [no default] + | --fits_file The fits file to search [no default] | - | Dedispersion arguments (optional): + |Dedispersion arguments (optional): | --dm_min Minimum DM to search over [default: ${params.dm_min}] | --dm_max Maximum DM to search over [default: ${params.dm_max}] | --dm_min_step - | Minimum DM step size (Delta DM) [default: ${params.dm_min_step }] + | Minimum DM step size (Delta DM) [default: ${params.dm_min_step}] | --dm_max_step - | Maximum DM step size (Delta DM) [default: ${params.dm_max_step }] + | Maximum DM step size (Delta DM) [default: ${params.dm_max_step}] | --max_dms_per_job | Maximum number of DM steps a single job will process. | Lowering this will reduce memory usage and increase parellelisation. | [default: ${params.max_dms_per_job}] | + |Pulsar search arguments (optional): + | --sp Perform only a single pulse search [default: ${params.sp }] + | --cand Label given to output files [default: ${params.cand }] + | --nharm Number of harmonics to search [default: ${params.nharm }] + | --min_period + | Min period to search for in sec (ANTF min = 0.0013) + | [default: ${params.min_period }] + | --max_period + | Max period to search for in sec (ANTF max = 23.5) + | [default: ${params.max_period }] + | --zmax Maximum acceleration to search (0 will do a simpler periodic search). + | I recomend you use 200 and set --max_dms_per_job 32 + | [default: ${params.zmax }] + | |Optional arguments: | --cand Candidate name to do a targeted search [default: Blind] - | --sp Perform a single pulse search [default false] - | --zmax The acceleration range to search over. If you would like to perform - | an acceleration search I recomend you use 200 and set - | --max_dms_per_job 32 - | [default: 0 (no acceleration search)] | --out_dir Output directory for the candidates files | [default: ${params.search_dir}/_candidates] | --mwa_search_version @@ -60,13 +47,8 @@ if ( params.help ) { exit(0) } -// Option parsing -if ( params.obsid == null ) { - println("No obsid, please use --obsid. Exiting.") - exit(0) -} if ( params.fits_file ) { - fits_file = Channel.fromPath( "${params.fits_file}", checkIfExists: true ) + fits_file = Channel.fromPath( "${params.fits_file}", checkIfExists: true ).flatten() //nfiles = new File("${params.fits_file}").listFiles().findAll { it.name ==~ /.*fits/ }.size() fits_file.view( it -> "Running search on ${it}" ) } @@ -74,34 +56,16 @@ else { println("No fits file given, please use --fits_file. Exiting.") exit(0) } -if ( params.dur ) { - params.begin = 1 - params.end = params.dur -} -else { - println("No duration given, please use --dur. Exiting.") - exit(0) -} - -// If doing an acceleration search, lower the number of DMs per job so the jobs don't time out -if ( params.zmax == 0 ) { - total_dm_jobs = 6 -} -else { - total_dm_jobs = 24 - params.max_dms_per_job = 128 -} -include {pulsar_search; single_pulse_search} from './pulsar_search_module' -include { classifier } from './classifier_module' +include { pulsar_search; single_pulse_search } from './pulsar_search_module' +include { classifier } from './classifier_module' workflow { if ( params.sp ) { - //single_pulse_search( fits_file.toSortedList().map{ it -> [ params.cand + '_' + it[0].getBaseName().split("/")[-1].split("_ch")[0], it ] }.view() ) - single_pulse_search( fits_file.map{ it -> [ params.cand + '_' + it.getBaseName().split("/")[-1].split("_ch")[0], it ] } ) + single_pulse_search( fits_file.map{ it -> [ params.cand + '_' + it.baseName.split("_ch")[0], it ] } ) } else { - pulsar_search( fits_file.toSortedList().map{ it -> [ params.cand + '_' + it[0].getBaseName().split("/")[-1].split("_ch")[0], it ] } ) - classifier( pulsar_search.out[1].flatten().collate( 120 ) ) + pulsar_search( fits_file.map{ it -> [ params.cand + '_' + it.baseName.split("_ch")[0], it ] } ) + classifier( pulsar_search.out ) } } diff --git a/nextflow/pulsar_search_module.nf b/nextflow/pulsar_search_module.nf index 7ddd3b1d..5e5cef91 100644 --- a/nextflow/pulsar_search_module.nf +++ b/nextflow/pulsar_search_module.nf @@ -1,126 +1,95 @@ -nextflow.enable.dsl = 2 - -params.out_dir = "${params.search_dir}/${params.obsid}_candidates" - -params.vcstools_version = 'master' -params.mwa_search_version = 'master' -params.publish_all_prepfold = false - -params.begin = 0 -params.end = 0 -params.all = false - -params.dm_min = 1 -params.dm_max = 250 -params.dm_min_step = 0.02 -params.dm_max_step = 0.5 -params.max_dms_per_job = 5000 - -//Defaults for the accelsearch command -params.nharm = 16 // number of harmonics to search -params.min_period = 0.001 // min period to search for in sec (ANTF min = 0.0013) -params.max_period = 30 // max period to search for in sec (ANTF max = 23.5) -params.zmax = 0 - -//Some math for the accelsearch command -//convert to freq +// Some math for the accelsearch command +// convert to freq min_freq = 1 / params.max_period max_freq = 1 / params.min_period -//adjust the freq to include the harmonics +// adjust the freq to include the harmonics min_f_harm = min_freq max_f_harm = max_freq * params.nharm // Work out total obs time -if ( params.all ) { - // an estimation since there's no easy way to make this work - obs_length = 4805 -} -else { - obs_length = params.end - params.begin + 1 -} - -// If doing an acceleration search, lower the number of DMs per job so the jobs don't time out -if ( params.zmax == 0 ) { - total_dm_jobs = 6 -} -else { - total_dm_jobs = 24 +// if ( params.all ) { +// // an estimation since there's no easy way to make this work +// obs_length = 4805 +// } +// else { +// obs_length = params.end - params.begin + 1 +// } + + + +def search_time_estimate(dur, ndm) { + // Estimate the duration of a search job in seconds + search_time = params.search_scale * Float.valueOf(dur) * (0.006*Float.valueOf(ndm) + 1) + // Max time is 24 hours for many clusters so always use less than that + if ( search_time < 86400 ) { + return "${search_time}s" + } + else { + return "86400s" + } } -// Work out some estimated job times -if ( "$HOSTNAME".startsWith("farnarkle") ) { - // In seconds - search_dd_fft_acc_dur = obs_length * 5.0 - prepfold_dur = obs_length * 24.0 - presto_python_load = "module use ${params.presto_module_dir}; module load presto/${params.presto_module}; module load python/2.7.14; module load matplotlib/2.2.2-python-2.7.14" -} -else if ( "$HOSTNAME".startsWith("garrawarla") ) { - // In seconds - search_dd_fft_acc_dur = obs_length * 5.0 - prepfold_dur = obs_length * 16.0 - presto_python_load = "" -} -else { - search_dd_fft_acc_dur = 14400 - prepfold_dur = 7200 - presto_python_load = "" -} -if ( params.zmax != 0 ) { - search_dd_fft_acc_dur *= 4 -} +process get_freq_and_dur { + input: + tuple val(name), path(fits_file) -process get_centre_freq { output: - file 'centre_freq.txt' + tuple val(name), path(fits_file), path("freq_dur.csv") """ - #!/usr/bin/env python3 + #!/usr/bin/env python - from vcstools.metadb_utils import get_common_obs_metadata + import os import csv + from astropy.io import fits - centre_freq = get_common_obs_metadata(${params.obsid})[5] + # Read in fits file to read its header + hdul = fits.open(r"${fits_file}".replace("\\\\", "")) + # Grab the centre frequency in MHz + freq = hdul[0].header['OBSFREQ'] + # Calculate the observation duration in seconds + dur = hdul[1].header['NAXIS2'] * hdul[1].header['TBIN'] * hdul[1].header['NSBLK'] - with open("centre_freq.txt", "w") as outfile: + # Export both values as a CSV for easy output + with open("freq_dur.csv", "w") as outfile: spamwriter = csv.writer(outfile, delimiter=',') - spamwriter.writerow([centre_freq]) + spamwriter.writerow([freq, dur]) """ } process ddplan { - label 'ddplan' - input: - tuple val(name), val(centre_freq) + tuple val(name), path(fits_file), val(centre_freq), val(dur) output: - file 'DDplan.txt' + tuple val(name), path(fits_file), val(centre_freq), val(dur), path('DDplan*.txt') """ - #!/usr/bin/env python3 + #!/usr/bin/env python + import csv + import numpy as np from vcstools.catalogue_utils import grab_source_alog from mwa_search.dispersion_tools import dd_plan - import csv - if '$name'.startswith('Blind'): - output = dd_plan(${centre_freq}, 30.72, 3072, 0.1, $params.dm_min, $params.dm_max, - min_DM_step=$params.dm_min_step, max_DM_step=$params.dm_max_step, - max_dms_per_job=$params.max_dms_per_job) + if '${name}'.startswith('Blind'): + output = dd_plan(${centre_freq}, 30.72, 3072, 0.1, ${params.dm_min}, ${params.dm_max}, + min_DM_step=${params.dm_min_step}, max_DM_step=${params.dm_max_step}, + max_dms_per_job=${params.max_dms_per_job}) else: - if '$name'.startswith('dm_'): - dm = float('$name'.split('dm_')[-1].split('_')[0]) - elif '$name'.startswith('FRB'): + if '${name}'.startswith('dm_'): + dm = float('${name}'.split('dm_')[-1].split('_')[0]) + elif '${name}'.startswith('FRB'): dm = grab_source_alog(source_type='FRB', - pulsar_list=['$name'], include_dm=True)[0][-1] + pulsar_list=['${name}'], include_dm=True)[0][-1] else: # Try RRAT first rrat_temp = grab_source_alog(source_type='RRATs', - pulsar_list=['$name'.split("_")[0]], include_dm=True) + pulsar_list=['${name}'.split("_")[0]], include_dm=True) if len(rrat_temp) == 0: #No RRAT so must be pulsar dm = grab_source_alog(source_type='Pulsar', - pulsar_list=['$name'.split("_")[0]], include_dm=True)[0][-1] + pulsar_list=['${name}'.split("_")[0]], include_dm=True)[0][-1] else: dm = rrat_temp[0][-1] dm_min = float(dm) - 2.0 @@ -128,81 +97,131 @@ process ddplan { dm_min = 1.0 dm_max = float(dm) + 2.0 output = dd_plan(${centre_freq}, 30.72, 3072, 0.1, dm_min, dm_max, - min_DM_step=$params.dm_min_step, max_DM_step=$params.dm_max_step, - max_dms_per_job=$params.max_dms_per_job) - with open("DDplan.txt", "w") as outfile: + min_DM_step=${params.dm_min_step}, max_DM_step=${params.dm_max_step}, + max_dms_per_job=${params.max_dms_per_job}) + + # Make a file for each work function batch + work_function_batch = [] + wfi = 0 + wf_sum = 0. + total_dm_steps = int(np.array(output).sum(axis=0)[3]) + for dd_line in output: + dm_min, dm_max, dm_step, ndm, timeres, downsamp, nsub, total_work_factor = dd_line + while total_work_factor > ${params.max_work_function}: + # Ouput a file with just the max_work_function worth of DMs + wf_dm_step = int(${params.max_work_function} * downsamp) + with open(f"DDplan_{wfi:03d}_a{total_dm_steps}_n{wf_dm_step}.txt", "w") as outfile: + spamwriter = csv.writer(outfile, delimiter=',') + spamwriter.writerow([ + dm_min, + dm_min + dm_step * wf_dm_step, + dm_step, + wf_dm_step, + timeres, + downsamp, + nsub, + ${params.max_work_function} + ]) + wfi += 1 + # update dd_line for next part + dm_min = dm_min + dm_step * wf_dm_step + ndm -= wf_dm_step + total_work_factor -= ${params.max_work_function} + + # Append the leftover to the batch list + + # check if the batch list is ready to output + wf_sum += total_work_factor + if wf_sum > ${params.max_work_function}: + # Ouput file with multiple ddplan lines + wf_dm_step = int((wf_sum - ${params.max_work_function}) * downsamp) + local_dm_steps = int(wf_dm_step + np.array(work_function_batch).sum(axis=0)[3]) + with open(f"DDplan_{wfi:03d}_a{total_dm_steps}_n{local_dm_steps}.txt", "w") as outfile: + spamwriter = csv.writer(outfile, delimiter=',') + for out_line in work_function_batch: + spamwriter.writerow(out_line) + # Ouput final new line + spamwriter.writerow([ + dm_min, + dm_min + dm_step * wf_dm_step, + dm_step, + wf_dm_step, + timeres, + downsamp, + nsub, + wf_sum - ${params.max_work_function} + ]) + wfi += 1 + work_function_batch = [] + + # Prepare new plan for next batch + wf_dm_step = int((wf_sum - ${params.max_work_function}) * downsamp) + dm_min = dm_min + dm_step * wf_dm_step + ndm -= wf_dm_step + total_work_factor -= (wf_sum - ${params.max_work_function}) + wf_sum = 0 + + work_function_batch.append([dm_min, dm_max, dm_step, ndm, timeres, downsamp, nsub, total_work_factor]) + + # Write final file + local_dm_steps = int(np.array(work_function_batch).sum(axis=0)[3]) + with open(f"DDplan_{wfi:03d}_a{total_dm_steps}_n{local_dm_steps}.txt", "w") as outfile: spamwriter = csv.writer(outfile, delimiter=',') - for o in output: - spamwriter.writerow(['${name}'] + o) + for out_line in work_function_batch: + spamwriter.writerow(out_line) """ } process search_dd_fft_acc { label 'cpu' - time { search_dd_fft_acc_dur * (0.006*Float.valueOf(dm_values[3]) + 1) < 86400 ? \ - "${search_dd_fft_acc_dur * (0.006*Float.valueOf(dm_values[3]) + 1)}s" : - "86400s"} + label 'presto_search' + + time { search_time_estimate(dur, params.max_work_function) } memory { "${task.attempt * 30} GB"} maxRetries 2 errorStrategy 'retry' - if ( "$HOSTNAME".startsWith("garrawarla") ) { - maxForks 400 - } - else { - maxForks 800 - } + maxForks params.max_search_jobs input: - tuple val(name), val(dm_values), file(fits_files) + tuple val(name), path(fits_files), val(freq), val(dur), val(ndms_job), val(ddplans) output: - tuple val(name), file("*ACCEL_${params.zmax}"), file("*.inf"), file("*.subSpS"), file('*.cand') - //file "*ACCEL_0" optional true - //Will have to change the ACCEL_0 if I do an accelsearch - - if ( "$HOSTNAME".startsWith("farnarkle") ) { - clusterOptions { "--export=NONE --tmp=${ (int) ( 0.12 * obs_length * Float.valueOf(dm_values[3]) / Float.valueOf(dm_values[5]) ) }MB" } - scratch '$JOBFS' - beforeScript "module use ${params.module_dir}; module load presto/min_path" - } - else if ( "$HOSTNAME".startsWith("x86") ) { - //scratch '/ssd' - container = "file:///${params.containerDir}/presto/presto.sif" - } - else if ( "$HOSTNAME".startsWith("galaxy") ) { - container = "file:///${params.containerDir}/presto/presto.sif" - } - else if ( "$HOSTNAME".startsWith("garrawarla") ) { - clusterOptions { "--export=NONE --tmp=${ (int) ( 0.12 * obs_length * Float.valueOf(dm_values[3]) / Float.valueOf(dm_values[5]) ) }MB" } - scratch '/nvmetmp' - container = "file:///${params.containerDir}/presto/presto.sif" - } - else { - container = "nickswainston/presto:realfft_docker" - } - + tuple val(name), path("*ACCEL_${params.zmax}"), path("*.inf"), path("*.singlepulse"), path('*.cand') """ - echo "lowdm highdm dmstep ndms timeres downsamp" - echo ${dm_values} - numout=${(int)(obs_length*10000/Float.valueOf(dm_values[5]))} - if (( \$numout % 2 != 0 )) ; then - numout=\$(expr \$numout + 1) - fi printf "\\n#Dedispersing the time series at \$(date +"%Y-%m-%d_%H:%m:%S") --------------------------------------------\\n" - prepsubband -ncpus $task.cpus -lodm ${dm_values[0]} -dmstep ${dm_values[2]} -numdms ${dm_values[3]} -zerodm -nsub ${dm_values[6]} \ --downsamp ${dm_values[5]} -numout \${numout} -o ${name} ${params.obsid}_*.fits + # Loop over ddplan lines + for ddplan in "${ddplans.join('" "').replace(", ", ",")}"; do + # cut off [ and ] + ddplan=\$( echo "\${ddplan}" | cut -d "[" -f 2 | cut -d "]" -f 1 ) + echo \${ddplan} + # Split into each value + IFS=, read -r dm_min dm_max dm_step ndm timeres downsamp nsub wf <<< \${ddplan} + # Calculate the number of output data points + numout=\$(bc <<< "scale=0; ${dur} * 10000 + \${downsamp}") + numout=\$(printf "%.0f\n" "\${numout}") + if (( \$numout % 2 != 0 )) ; then + numout=\$(expr \$numout + 1) + fi + echo "Performing dedispersion with" + echo " dm_min: \${dm_min}, dm_max: \${dm_max}, dm_step: \${dm_step}" + echo " ndm: \${ndm}, timeres: \${timeres}, downsamp: \${downsamp}, nsub: \${nsub}," + prepsubband -ncpus ${task.cpus} -lodm \${dm_min} -dmstep \${dm_step} -numdms \${ndm} -zerodm -nsub \${nsub} \ +-downsamp \${downsamp} -numout \${numout} -o ${name} *.fits + done + printf "\\n#Performing the FFTs at \$(date +"%Y-%m-%d_%H:%m:%S") -----------------------------------------------------\\n" realfft *dat printf "\\n#Performing the periodic search at \$(date +"%Y-%m-%d_%H:%m:%S") ------------------------------------------\\n" for i in \$(ls *.dat); do # Somtimes this has a 255 error code when data.pow == 0 so ignore it - accelsearch -ncpus $task.cpus -zmax ${params.zmax} -flo $min_f_harm -fhi $max_f_harm -numharm $params.nharm \${i%.dat}.fft || true + accelsearch -ncpus ${task.cpus} -zmax ${params.zmax} -flo ${min_f_harm} -fhi ${max_f_harm} -numharm ${params.nharm} \${i%.dat}.fft || true done - ${presto_python_load} + + printf "\\n#Performing the single pulse search at \$(date +"%Y-%m-%d_%H:%m:%S") ------------------------------------------\\n" + ${params.presto_python_load} single_pulse_search.py -p -m 0.5 -b *.dat - cat *.singlepulse > ${name}_DM${dm_values[0]}-${dm_values[1]}.subSpS printf "\\n#Finished at \$(date +"%Y-%m-%d_%H:%m:%S") ----------------------------------------------------------------\\n" """ } @@ -210,244 +229,250 @@ process search_dd_fft_acc { process accelsift { label 'cpu' + label 'presto' + time '25m' errorStrategy 'retry' maxRetries 1 input: - tuple val(name), file(accel_inf_single_pulse) + tuple val(name), path(accel), path(single_pulse), path(cands) output: - tuple val(name), file("cands_*greped.txt") + tuple val(name), path(accel), path(single_pulse), path(cands), path("cands_*greped.txt") - if ( "$HOSTNAME".startsWith("x86") || "$HOSTNAME".startsWith("garrawarla") ||\ - "$HOSTNAME".startsWith("galaxy") ) { - container = "file:///${params.containerDir}/presto/presto.sif" - } - else if ( "$HOSTNAME".startsWith("farnarkle") ) { - container = "file:///${params.containerDir}/presto/presto_sandbox" - // tmp storage for container image - clusterOptions { "--tmp=1000MB" } - scratch '$JOBFS' - } - else { - container = "nickswainston/presto:realfft_docker" - } - """ + shell: + ''' # Remove incomplete or errored files - for i in \$(ls *0); do - if [ \$(grep " Number of bins in the time series" \$i | wc -l) == 0 ]; then - rm \${i%%_ACCEL_0}* + for i in !{accel}; do + if [ $(grep " Number of bins in the time series" $i | wc -l) == 0 ]; then + rm ${i%%_ACCEL_}* fi done - ACCEL_sift.py --file_name ${name} - if [ -f cands_${name}.txt ]; then - grep ${name} cands_${name}.txt > cands_${name}_greped.txt + ACCEL_sift.py --file_name !{name} + if [ -f cands_!{name}.txt ]; then + # Get candidate lines and replace space with a comma + grep !{name} cands_!{name}.txt | tr -s '[:space:]' | sed -e 's/ /,/g' > cands_!{name}_greped.txt else #No candidates so make an empty file - touch cands_${name}_greped.txt + touch cands_!{name}_greped.txt fi - #cat *.subSpS > ${name}.SpS - """ + ''' } -process single_pulse_searcher { - label 'cpu_large_mem' - time '2h' - publishDir params.out_dir, mode: 'copy' - errorStrategy 'ignore' +process prepfold { + label 'cpu' + label 'presto' + + publishDir params.out_dir, mode: 'copy', enabled: params.publish_all_prepfold + time "${ (int) ( params.prepfold_scale * dur ) }s" + errorStrategy 'retry' + maxRetries 1 input: - tuple val(name), file(sps), file(fits) + tuple val(cand_lines), val(dur), path(cand_infs), path(cand_files), path(fits_files) output: - file "*pdf" optional true - file "*.SpS" + tuple path("*pfd"), path("*bestprof"), path("*ps"), path("*png")//, optional: true) // some PRESTO installs don't make pngs - if ( "$HOSTNAME".startsWith("farnarkle") || "$HOSTNAME".startsWith("x86") ||\ - "$HOSTNAME".startsWith("garrawarla") || "$HOSTNAME".startsWith("galaxy") ) { - container = "file:///${params.containerDir}/sps/sps.sif" - } - else if ( "$HOSTNAME".startsWith("farnarkle") ) { - container = "file:///${params.containerDir}/sps/sps_sandbox" - // tmp storage for container image - clusterOptions { "--tmp=2000MB" } - scratch '$JOBFS' - } - else { - container = "nickswainston/sps" - } + //no mask command currently """ - cat *.subSpS > ${name}.SpS - #-SNR_min 4 -SNR_peak_min 4.5 -DM_cand 1.5 -N_min 3 - single_pulse_searcher.py -fits ${fits} -no_store -plot_name ${name}_sps.pdf ${name}.SpS + # Loop over each candidate + for cand_line in "${cand_lines.join('" "').replace(", ", ",")}"; do + # cut off [ and ] + cand_line=\$( echo "\${cand_line}" | cut -d "[" -f 2 | cut -d "]" -f 1 ) + echo \${cand_line} + # Split into each value + IFS=, read -r name dm c3 c4 nharm period c7 c8 c9 c10 c11 <<< \${cand_line} + fits_name=\${name%_DM*} + fits_name=\${fits_name#*_} + + # Set up the prepfold options to match the ML candidate profiler + period=\$(echo "scale=8; \$period / 1000" | bc | awk '{printf "%.8f", \$0}') + if (( \$(echo "\$period > 0.01" | bc -l) )); then + nbins=100 + ntimechunk=120 + dmstep=1 + period_search_n=1 + else + # bin size is smaller than time resolution so reduce nbins + nbins=50 + ntimechunk=40 + dmstep=3 + period_search_n=2 + fi + + # Work out how many dmfacts to use to search +/- 2 DM + ddm=\$(echo "scale=10;0.000241*138.87^2*\${dmstep} / (1/\$period *\$nbins)" | bc) + ndmfact=\$(echo "1 + 1/(\$ddm*\$nbins)" | bc) + echo "ndmfact: \$ndmfact ddm: \$ddm" + + prepfold -ncpus ${task.cpus} -o \$name -accelfile \${name%:*}.cand -accelcand \${name##*:} \ + -n \$nbins -dm \$dm -noxwin -noclip -nsub 256 -npart \$ntimechunk -dmstep \$dmstep \ + -pstep 1 -pdstep 2 -npfact \$period_search_n -ndmfact \$ndmfact -runavg \${fits_name}*.fits + + done """ } -process prepfold { - publishDir params.out_dir, mode: 'copy', enabled: params.publish_all_prepfold +process search_dd { label 'cpu' - time "${prepfold_dur}s" + label 'presto_search' + + time { search_time_estimate(dur, params.max_work_function) } + memory { "${task.attempt * 30} GB"} + maxRetries 2 errorStrategy 'retry' - maxRetries 1 + maxForks params.max_search_jobs input: - tuple val(cand_line), file(cand_file), file(cand_inf), file(fits_files) + tuple val(name), path(fits_files), val(freq), val(dur), val(ndms_job), val(ddplans) output: - file "*pfd*" + tuple val(name), path("*.inf"), path("*.singlepulse") - if ( "$HOSTNAME".startsWith("farnarkle") || "$HOSTNAME".startsWith("galaxy") ) { - beforeScript "module use ${params.presto_module_dir}; module load presto/${params.presto_module}" - } - else if ( "$HOSTNAME".startsWith("x86") || "$HOSTNAME".startsWith("garrawarla") ) { - container = "file:///${params.containerDir}/presto/presto.sif" - } - else { - container = "nickswainston/presto:realfft_docker" - } - //no mask command currently - //${cand_line.split()[0].substring(0, cand_line.split()[0].lastIndexOf(":")) + '.cand'} """ - echo "${cand_line.split()}" - # Set up the prepfold options to match the ML candidate profiler - temp_period=${Float.valueOf(cand_line.split()[7])/1000} - period=\$(printf "%.8f" \$temp_period) - if (( \$(echo "\$period > 0.01" | bc -l) )); then - nbins=100 - ntimechunk=120 - dmstep=1 - period_search_n=1 - else - # bin size is smaller than time resolution so reduce nbins - nbins=50 - ntimechunk=40 - dmstep=3 - period_search_n=2 - fi - - # Work out how many dmfacts to use to search +/- 2 DM - ddm=`echo "scale=10;0.000241*138.87^2*\${dmstep} / (1/\$period *\$nbins)" | bc` - ndmfact=`echo "1 + 1/(\$ddm*\$nbins)" | bc` - echo "ndmfact: \$ndmfact ddm: \$ddm" + printf "\\n#Dedispersing the time series at \$(date +"%Y-%m-%d_%H:%m:%S") --------------------------------------------\\n" + # Loop over ddplan lines + for ddplan in "${ddplans.join('" "').replace(", ", ",")}"; do + # cut off [ and ] + ddplan=\$( echo "\${ddplan}" | cut -d "[" -f 2 | cut -d "]" -f 1 ) + echo \${ddplan} + # Split into each value + IFS=, read -r dm_min dm_max dm_step ndm timeres downsamp nsub wf <<< \${ddplan} + # Calculate the number of output data points + numout=\$(bc <<< "scale=0; ${dur} * 10000 + \${downsamp}") + numout=\$(printf "%.0f\n" "\${numout}") + if (( \$numout % 2 != 0 )) ; then + numout=\$(expr \$numout + 1) + fi + echo "Performing dedispersion with" + echo " dm_min: \${dm_min}, dm_max: \${dm_max}, dm_step: \${dm_step}" + echo " ndm: \${ndm}, timeres: \${timeres}, downsamp: \${downsamp}, nsub: \${nsub}," + prepsubband -ncpus ${task.cpus} -lodm \${dm_min} -dmstep \${dm_step} -numdms \${ndm} -zerodm -nsub \${nsub} \ +-downsamp \${downsamp} -numout \${numout} -o ${name} *.fits + done - #-p \$period - prepfold -ncpus $task.cpus -o ${cand_line.split()[0]} -n \$nbins -dm ${cand_line.split()[1]} -noxwin -noclip -nsub 256 \ --accelfile ${cand_line.split()[0].substring(0, cand_line.split()[0].lastIndexOf(":"))}.cand -accelcand ${cand_line.split()[0].split(":")[-1]} \ --npart \$ntimechunk -dmstep \$dmstep -pstep 1 -pdstep 2 -npfact \$period_search_n -ndmfact \$ndmfact -runavg *.fits + printf "\\n#Performing the single pulse search at \$(date +"%Y-%m-%d_%H:%m:%S") ------------------------------------------\\n" + ${params.presto_python_load} + single_pulse_search.py -p -m 0.5 -b *.dat + printf "\\n#Finished at \$(date +"%Y-%m-%d_%H:%m:%S") ----------------------------------------------------------------\\n" """ } -process search_dd { +process single_pulse_searcher { label 'cpu' - time { search_dd_fft_acc_dur * (0.006*Float.valueOf(dm_values[3]) + 1) < 86400 ? \ - "${search_dd_fft_acc_dur * (0.006*Float.valueOf(dm_values[3]) + 1)}s" : - "86400s"} - memory { "${task.attempt * 3} GB"} - maxRetries 2 - if ( "$HOSTNAME".startsWith("garrawarla") ) { - maxForks 300 - } - else { - maxForks 800 - } + label 'large_mem' + label 'sps' + + time '2h' + publishDir params.out_dir, mode: 'copy' + errorStrategy 'ignore' input: - tuple val(name), val(dm_values), file(fits_files) + tuple val(name), path(sps), path(fits) output: - tuple val(name), file("*.inf"), file("*.subSpS") - //Will have to change the ACCEL_0 if I do an accelsearch - - if ( "$HOSTNAME".startsWith("farnarkle") ) { - clusterOptions { "--export=NONE --tmp=${ (int) ( 0.08 * obs_length * Float.valueOf(dm_values[3]) / Float.valueOf(dm_values[5]) ) }MB" } - scratch '$JOBFS' - beforeScript "module use ${params.module_dir}; module load presto/min_path" - } - else if ( "$HOSTNAME".startsWith("x86") ) { - scratch '/ssd' - container = "file:///${params.containerDir}/presto/presto.sif" - } - else if ( "$HOSTNAME".startsWith("galaxy") ) { - container = "file:///${params.containerDir}/presto/presto.sif" - } - else if ( "$HOSTNAME".startsWith("garrawarla") ) { - clusterOptions { "--export=NONE --tmp=${ (int) ( 0.08 * obs_length * Float.valueOf(dm_values[3]) / Float.valueOf(dm_values[5]) ) }MB" } - scratch '/nvmetmp' - container = "file:///${params.containerDir}/presto/presto.sif" - } - else { - container = "nickswainston/presto:realfft_docker" - } + path "*pdf", optional: true + path "*.SpS" """ - echo "lowdm highdm dmstep ndms timeres downsamp" - echo ${dm_values} - printf "\\n#Dedispersing the time series at \$(date +"%Y-%m-%d_%H:%m:%S") --------------------------------------------\\n" - prepsubband -ncpus $task.cpus -lodm ${dm_values[0]} -dmstep ${dm_values[2]} -numdms ${dm_values[3]} -zerodm -nsub ${dm_values[6]} \ --downsamp ${dm_values[5]} -numout ${(int)(obs_length*10000/Float.valueOf(dm_values[5]))} -o ${name.replaceAll("\\*","")} ${params.obsid}_*.fits - single_pulse_search.py -p -m 0.5 -b *.dat - cat *.singlepulse > ${name}_DM${dm_values[0]}-${dm_values[1]}.subSpS + cat *.singlepulse > ${name}.SpS + #-SNR_min 4 -SNR_peak_min 4.5 -DM_cand 1.5 -N_min 3 + single_pulse_searcher.py -fits ${fits} -no_store -plot_name ${name}_sps.pdf ${name}.SpS """ } workflow pulsar_search { take: - name_fits_files // [val(candidateName_obsid_pointing), file(fits_files)] + name_fits_files // [val(candidateName_obsid_pointing), path(fits_files)] main: - get_centre_freq() - ddplan( name_fits_files.map{ it -> it[0] }.combine(get_centre_freq.out.splitCsv()) ) - search_dd_fft_acc( // combine the fits files and ddplan with the matching name key (candidateName_obsid_pointing) - ddplan.out.splitCsv().map{ it -> [ it[0], [ it[1], it[2], it[3], it[4], it[5], it[6], it[7] ] ] }.\ - concat(name_fits_files).groupTuple().\ - // Find for each ddplan match that with the fits files and the name key then change the format to [val(name), val(dm_values), file(fits_files)] - map{ it -> [it[1].init(), [[it[0], it[1].last()]]].combinations() }.flatMap().\ - map{ it -> [it[1][0], it[0], it[1][1]]} ) + // Grab meta data from the fits file + get_freq_and_dur( name_fits_files ) // [ name, fits_file, freq, dur ] + + // Grab the meta data out of the CSV + name_fits_freq_dur = get_freq_and_dur.out.map { name, fits, meta -> [ name, fits, meta.splitCsv()[0][0], meta.splitCsv()[0][1] ] } + ddplan( name_fits_freq_dur ) + // ddplan's output format is [ name, fits_file, centrefreq(MHz), duration(s), DDplan_file ] + + // Trasponse to get a DM plan file each row/job then split the csv to get the DDplan + // Also using groupKey so that future groupTuple will have the size of the total number of DMs + // The DDplan file has the name format DDplan_{i}_a{total_dm_steps}_n{local_dm_steps}.txt + search_dd_fft_acc( + ddplan.out.transpose() + .map { name, fits, freq, dur, ddplan -> + [ groupKey(name, ddplan.baseName.split("_n")[0].split("_a")[-1].toInteger() ), fits, freq, dur, ddplan.baseName.split("_n")[-1], ddplan.splitCsv() ] + } + ) + // Output format: [ name, ACCEL_summary, presto_inf, single_pulse, periodic_candidates ] + // Get all the inf, ACCEL and single pulse files and sort them into groups with the same name key - accelsift( search_dd_fft_acc.out.map{ it -> [it[0], [it[1]].flatten().findAll { it != null } + \ - [it[2]].flatten().findAll { it != null }] }.\ - groupTuple( size: total_dm_jobs, remainder: true ).map{ it -> [it[0], it[1].flatten()]} ) - single_pulse_searcher( search_dd_fft_acc.out.map{ it -> [it[0], [it[3]].flatten().findAll { it != null }] }.\ - groupTuple( size: total_dm_jobs, remainder: true ).map{ it -> [it[0], it[1].flatten()]}.\ - // Add fits files - concat(name_fits_files).groupTuple( size: 2 ).map{ it -> [it[0], it[1][0], it[1][1]]} ) - prepfold( name_fits_files.cross( - // Group all the accelsift lines together - accelsift.out.map{ it -> it[1] }.splitCsv().flatten().map{ it -> [it.split()[0].split("_ACCEL")[0], it ] }.cross( - // Group all the .cand and .inf files by their base names - search_dd_fft_acc.out.map{ it -> [it[2]].flatten().findAll { it != null } }. - flatten().map{ it -> [it.baseName.split(".inf")[0], it ] }.concat( - search_dd_fft_acc.out.map{ it -> [it[4]].flatten().findAll { it != null } }. - flatten().map{ it -> [it.baseName.split("_ACCEL")[0], it ] }).groupTuple( size: 2 ) - // match the cand and inf file with each accelsift line and reoraganise - ).map{ it -> [it[0][0].split("_DM")[0], [it[0][1], it[1][1][0], it[1][1][1]]] } - // Match with fits files and eogranise to val(cand_line), file(cand_file), file(cand_inf), file(fits_files) - ).map{ it -> [it[1][1][0], it[1][1][2], it[1][1][1], it[0][1]] } ) + // This uses the groupKey so it should output the channel as soon as it has all the DMs + inf_accel_sp_cand = search_dd_fft_acc.out.transpose( remainder: true ).groupTuple( remainder: true ).map{ key, accel, inf, sp, cands -> [ key.toString(), accel - null, sp, cands - null ] } + accelsift( inf_accel_sp_cand ) + + // For each line of each candidate file and treat it as a candidate + accel_cands = accelsift.out.flatMap{ it[-1].splitCsv() }.map{ it -> [it[0].split("_ACCEL")[0], it ] } + // Grab the inf and cand files for the key (name including DM) + inf_cands = search_dd_fft_acc.out.map{ it[2] }.flatten().map{ [ it.baseName, it ] } // inf files + .concat( search_dd_fft_acc.out.map{ it[4] }.flatten().map{ [ it.baseName.split("_ACCEL")[0], it ] } ) // cand files + .groupTuple( size: 2 ).map{ [ it[0], it[1][0], it[1][1] ] } // grouping and reorganising + // For each accel cand, pair it with its inf and cand files + accel_inf_cands = accel_cands.cross( inf_cands ) + // Reorganise it so it's back to the just name key style + .map{ [ it[0][0].split("_DM")[0], it[0][1], it[1][1], it[1][2] ] } + // Pair them with fits files and metadata so they are ready to fold + cands_for_prepfold = name_fits_freq_dur.cross( accel_inf_cands ) + .map{ [ it[1][1], Float.valueOf(it[0][3]), it[1][2], it[1][3], it[0][1] ] } + // collate by several prepfold jobs together + .collate( params.max_folds_per_job ) + // reformat them to be in lists for each data type + .transpose().collate( 5 ) + .map { cand_lines, durs, cand_infs, cand_files, fits_files -> [ cand_lines, durs.sum(), cand_infs, cand_files, fits_files.unique() ]} + // [ name, fits_files, dur, cand_line, cand_inf, cand_file ] + prepfold( cands_for_prepfold ) + + // Combined the grouped single pulse files with the fits files + single_pulse_searcher( + inf_accel_sp_cand.map{ [ it[0], it[2] ] }.concat( name_fits_files ).groupTuple( size: 2 ).map{ [ it[0], it[1][0], it[1][1] ] } + ) emit: - accelsift.out + // [ pfd, bestprof, ps, png ] prepfold.out } workflow single_pulse_search { take: - name_fits_files + name_fits_files // [val(candidateName_obsid_pointing), path(fits_files)] main: - get_centre_freq() - ddplan( name_fits_files.map{ it -> it[0] }.combine(get_centre_freq.out.splitCsv()) ) - search_dd( // combine the fits files and ddplan witht he matching name key (candidateName_obsid_pointing) - ddplan.out.splitCsv().map{ it -> [ it[0], [ it[1], it[2], it[3], it[4], it[5], it[6], it[7] ] ] }.concat(name_fits_files).groupTuple().\ - // Find for each ddplan match that with the fits files and the name key then change the format to [val(name), val(dm_values), file(fits_files)] - map{ it -> [it[1].init(), [[it[0], it[1].last()]]].combinations() }.flatMap().map{ it -> [it[1][0], it[0], it[1][1]]} ) - single_pulse_searcher( search_dd.out.map{ it -> [it[0], [it[1]].flatten().findAll { it != null } + [it[2]].flatten().findAll { it != null }] }.\ - groupTuple( size: total_dm_jobs, remainder: true).map{ it -> [it[0], it[1].flatten()] }.\ - // Add fits files - concat(name_fits_files).groupTuple( size: 2 ).map{ it -> [it[0], it[1][0], it[1][1]]} ) - // Get all the inf and single pulse files and sort them into groups with the same basename (obsid_pointing) + // Grab meta data from the fits file + get_freq_and_dur( name_fits_files ) // [ name, fits_file, freq, dur ] + + // Grab the meta data out of the CSV + name_fits_freq_dur = get_freq_and_dur.out.map { name, fits, meta -> [ name, fits, meta.splitCsv()[0][0], meta.splitCsv()[0][1] ] } + ddplan( name_fits_freq_dur ) + // ddplan's output format is [ name, fits_file, centrefreq(MHz), duration(s), DDplan_file ] + + // so split the csv to get the DDplan and transpose to make a job for each row of the plan + search_dd( + ddplan.out.transpose() + .map { name, fits, freq, dur, ddplan -> + [ groupKey(name, ddplan.baseName.split("_n")[0].split("_a")[-1].toInteger() ), fits, freq, dur, ddplan.baseName.split("_n")[-1], ddplan.splitCsv() ] + } + ) + // Output format: [ name, presto_inf, single_pulse ] + + // Get all the inf and single pulse files and sort them into groups with the same name key + inf_accel_sp_cand = search_dd.out.transpose( remainder: true ).groupTuple( remainder: true ).map{ key, accel, inf, sp, cands -> [ key.toString(), accel - null, sp, cands - null ] } + // Combined the grouped single pulse files with the fits files + single_pulse_searcher( + inf_accel_sp_cand.map{ [ it[0], it[2] ] }.concat( name_fits_files ).groupTuple().map{ [ it[0], it[1][0], it[1][1] ] } + ) emit: single_pulse_searcher.out[0] single_pulse_searcher.out[1] diff --git a/nextflow/sun_signal_search.nf b/nextflow/sun_signal_search.nf index 56910102..4695ad62 100644 --- a/nextflow/sun_signal_search.nf +++ b/nextflow/sun_signal_search.nf @@ -1,28 +1,5 @@ #!/usr/bin/env nextflow -nextflow.enable.dsl = 2 - -params.obsid = null -params.pointings = null -params.fitsdir = "/group/mwavcs/vcs/${params.obsid}/pointings" -params.fits_files -params.out_dir = "${params.search_dir}/${params.obsid}_candidates" -params.dm_min = 0 -params.dm_max = 0.02 -params.dm_min_step = 0.02 - -params.scratch = false -params.fits_file_dir = false - -params.cand = "Blind" -params.sp = false -params.publish_all_prepfold = true - -//Defaults for the accelsearch command -params.nharm = 16 // number of harmonics to search -params.min_period = 0.001 // min period to search for in sec (ANTF min = 0.0013) -params.max_period = 30 // max period to search for in sec (ANTF max = 23.5) -params.zmax = 0 //Some math for the accelsearch command //convert to freq @@ -52,12 +29,12 @@ else if ( params.fits_files ) { } else { if ( params.scratch ) { - fits_files = Channel.fromPath( "${params.scratch_basedir}/${params.obsid}/dpp_pointings/${params.pointings}/${params.obsid}_*.fits", checkIfExists: true ) - nfiles = new File("${params.scratch_basedir}/${params.obsid}/dpp_pointings/${params.pointings}").listFiles().findAll { it.name ==~ /.*1*fits/ }.size() + fits_files = Channel.fromPath( "${params.vcsdir}/${params.obsid}/dpp_pointings/${params.pointings}/${params.obsid}_*.fits", checkIfExists: true ) + nfiles = new File("${params.vcsdir}/${params.obsid}/dpp_pointings/${params.pointings}").listFiles().findAll { it.name ==~ /.*1*fits/ }.size() } else { - fits_files = Channel.fromPath( "${params.basedir}/${params.obsid}/pointings/${params.pointings}/${params.obsid}_*.fits", checkIfExists: true ) - nfiles = new File("${params.basedir}/${params.obsid}/pointings/${params.pointings}").listFiles().findAll { it.name ==~ /.*1*fits/ }.size() + fits_files = Channel.fromPath( "${params.vcsdir}/${params.obsid}/pointings/${params.pointings}/${params.obsid}_*.fits", checkIfExists: true ) + nfiles = new File("${params.vcsdir}/${params.obsid}/pointings/${params.pointings}").listFiles().findAll { it.name ==~ /.*1*fits/ }.size() } } @@ -79,9 +56,9 @@ if ( params.help ) { | --fits_file_dir | Directory containing the fits files. Use this if the fits files | are not in the default directory : - | ${params.basedir}//pointings/${params.pointings} + | ${params.vcsdir}//pointings/${params.pointings} | --scratch Change the default directory to: - | ${params.scratch_basedir}//pointings/${params.pointings} + | ${params.vcsdir}//pointings/${params.pointings} | --dm_min Minimum DM to search over [default: 1] | --dm_max Maximum DM to search over [default: 250] | --dm_min_step @@ -102,7 +79,7 @@ process search_dd_fft_acc { //Will ignore errors for now because I have no idea why it dies sometimes errorStrategy { task.attempt > 1 ? 'ignore' : 'retry' } maxForks 800 - publishDir "${params.scratch_basedir}/${params.obsid}/pointings/${name.split("${params.obsid}_")[1]}", mode: 'copy' + publishDir "${params.vcsdir}/${params.obsid}/pointings/${name.split("${params.obsid}_")[1]}", mode: 'copy' input: tuple val(name), val(dm_values), file(fits_files) @@ -131,7 +108,7 @@ process search_dd_fft_acc { } //numout=${(int)(obs_length*10000/Float.valueOf(dm_values[5]))} - // -numout \${numout} + // -numout \${numout} """ echo "lowdm highdm dmstep ndms timeres downsamp" echo ${dm_values} @@ -181,7 +158,8 @@ process accelsift { } process single_pulse_searcher { - label 'cpu_large_mem' + label 'cpu' + label 'large_mem' time '2h' stageInMode = 'copy' publishDir params.out_dir, mode: 'copy' @@ -237,4 +215,4 @@ workflow { ).map{ it -> [it[0][0].split("_DM")[0], [it[0][1], it[1][1][0], it[1][1][1]]] } // Match with fits files and eogranise to val(cand_line), file(cand_file), file(cand_inf), file(fits_files) ).map{ it -> [it[1][1][0], it[1][1][2], it[1][1][1], it[0][1]] } ) -} \ No newline at end of file +} diff --git a/nextflow/test_beamformer.nf b/nextflow/test_beamformer.nf index be3a2416..0ada4dc0 100644 --- a/nextflow/test_beamformer.nf +++ b/nextflow/test_beamformer.nf @@ -1,9 +1,7 @@ nextflow.enable.dsl = 2 -params.vcstools_version = 'master' -params.scratch_basedir = '/astro/mwavcs/vcs' params.version_compare = "V2.3_FEE2016" -params.version_compare_base_dir = "${params.scratch_basedir}/beamformer_tests" +params.version_compare_base_dir = "${params.vcsdir}/beamformer_tests" params.version_compare_dir = "${params.version_compare_base_dir}/${params.version_compare}" params.publish_version = false @@ -29,7 +27,7 @@ bf_out = " -p " params.help = false if ( params.help ) { - help = """test_beamformer.nf: A pipeline that will test the beamformer by comparing the binaires of the output psrfits + help = """test_beamformer.nf: A pipeline that will test the beamformer by comparing the binaires of the output psrfits | and vdif files with previous runs and the signal-to-noise of pulsar detections | |Optional arguments: @@ -120,7 +118,7 @@ process make_beam { -d ${params.version_compare_base_dir}/${obsid}/combined -P ${point.join(",")} \ -r 10000 -m ${params.version_compare_base_dir}/${obsid}/${obsid}_metafits_ppds.fits \ ${bf_out} -t 6000 -F ${params.version_compare_base_dir}/${obsid}/cal/${calid}/rts/flagged_tiles.txt -z $utc - + # Label all outputs for i in \$(ls */*); do mv \${i} ${label}_\${i##*/}; done """ @@ -141,7 +139,7 @@ process make_beam_ipfb { else { maxForks 120 } - + if ( "$HOSTNAME".startsWith("farnarkle") ) { clusterOptions = "--gres=gpu:1 --tmp=${temp_mem_single}GB" scratch '$JOBFS' @@ -222,7 +220,7 @@ process compare_repeats { diff no_header_1.fits no_header_3.fits if [ \$? != 0 ]; then fits_diff_success=False; fi if [ \$fits_diff_success == True ]; then echo "PASS: fits files are repeatable 3 times"; fi - + # vdif check vdif_diff_success=True diff ${vdif_1} ${vdif_2} @@ -608,4 +606,4 @@ workflow { tests_1150234552() // J0953+0755 test tests_1260302416() -} \ No newline at end of file +} diff --git a/nextflow/vcs_download.nf b/nextflow/vcs_download.nf index 663b296c..3e0a2171 100644 --- a/nextflow/vcs_download.nf +++ b/nextflow/vcs_download.nf @@ -1,14 +1,6 @@ #!/usr/bin/env nextflow -nextflow.enable.dsl = 2 -params.obsid = 'no_obsid' - -params.begin = null -params.end = null -params.all = false - -params.no_combined_check = true params.ozstar_transfer = false params.increment = 32 @@ -17,12 +9,8 @@ params.untar_jobs = 2 params.keep_tarball = false params.keep_raw = false params.max_jobs = 12 - params.download_dir = null -params.vcstools_version = 'master' -params.mwa_voltage_version = 'master' - if ( params.keep_tarball ) { keep_tarball_command = "-k" } @@ -98,10 +86,10 @@ process check_data_format { from vcstools.general_utils import mdir # Ensure the metafits files is there - meta.ensure_metafits("${params.basedir}/${params.obsid}", "${params.obsid}",\ - "${params.scratch_basedir}/${params.obsid}/${params.obsid}_metafits_ppds.fits") + meta.ensure_metafits("${params.vcsdir}/${params.obsid}", "${params.obsid}",\ + "${params.vcsdir}/${params.obsid}/${params.obsid}_metafits_ppds.fits") - data_dir = '${params.scratch_basedir}/${params.obsid}' + data_dir = '${params.vcsdir}/${params.obsid}' obsinfo = meta.getmeta(service='obs', params={'obs_id':'${params.obsid}'}) comb_del_check = meta.combined_deleted_check(${params.obsid}, begin=${begin}, end=${end}) data_format = obsinfo['dataquality'] @@ -173,15 +161,15 @@ process move_asvo_files { if [ ${data_type} == 16 ]; then # Move ics files if compgen -G ${params.download_dir}/*_ics.dat > /dev/null; then - mv ${params.download_dir}/*_ics.dat ${params.scratch_basedir}/${params.obsid}/combined/ + mv ${params.download_dir}/*_ics.dat ${params.vcsdir}/${params.obsid}/combined/ fi fi # Move metafits files if [ -f ${params.download_dir}/${params.obsid}.metafits ]; then - mv ${params.download_dir}/${params.obsid}.metafits ${params.scratch_basedir}/${params.obsid}/combined/ + mv ${params.download_dir}/${params.obsid}.metafits ${params.vcsdir}/${params.obsid}/combined/ fi if [ -f ${params.download_dir}/${params.obsid}_metafits_ppds.fits ]; then - mv ${params.download_dir}/${params.obsid}_metafits_ppds.fits ${params.scratch_basedir}/${params.obsid}/combined/ + mv ${params.download_dir}/${params.obsid}_metafits_ppds.fits ${params.vcsdir}/${params.obsid}/combined/ fi """ } @@ -212,7 +200,7 @@ process untar { } process recombine { - label 'cpu_large_mem' + label 'cpu' memory {"${begin_time_increment[1] * 5} GB"} time { "${500*params.increment*task.attempt + 900}s" } errorStrategy { task.attempt > 3 ? 'finish' : 'retry' } @@ -234,8 +222,8 @@ process recombine { script: """ - srun --export=all recombine.py -o ${params.obsid} -s ${begin_time_increment[0]} -d ${begin_time_increment[1]} -w ${params.download_dir} -p ${params.scratch_basedir}/${params.obsid} - checks.py -m recombine -o ${params.obsid} -w ${params.scratch_basedir}/${params.obsid}/combined/ -b ${begin_time_increment[0]} -i ${begin_time_increment[1]} + srun --export=all recombine.py -o ${params.obsid} -s ${begin_time_increment[0]} -d ${begin_time_increment[1]} -w ${params.download_dir} -p ${params.vcsdir}/${params.obsid} + checks.py -m recombine -o ${params.obsid} -w ${params.vcsdir}/${params.obsid}/combined/ -b ${begin_time_increment[0]} -i ${begin_time_increment[1]} if ! ${params.keep_raw}; then # Loop over each second and delete raw files for gps in \$(seq ${begin_time_increment[0]} ${begin_time_increment[0] + begin_time_increment[1] - 1}); do @@ -262,9 +250,9 @@ process ozstar_transfer { end=${begin_time_increment[0] + begin_time_increment[1] - 1} echo "obsid: ${params.obsid} start: \${start} end: \${end}" - ls --format single-column /astro/mwavcs/vcs/${params.obsid}/combined/*{${begin_time_increment[0]}..${begin_time_increment[0] + begin_time_increment[1] - 1}}*dat | xargs -n1 basename > temp_file_list.txt - rsync -vhu --files-from=temp_file_list.txt /astro/mwavcs/vcs/${params.obsid}/combined/ ozstar:/fred/oz125/vcs/${params.obsid}/combined - rm /astro/mwavcs/vcs/${params.obsid}/combined/*{${begin_time_increment[0]}..${begin_time_increment[0] + begin_time_increment[1] - 1}}*dat + ls --format single-column ${params.vcsdir}/${params.obsid}/combined/*{${begin_time_increment[0]}..${begin_time_increment[0] + begin_time_increment[1] - 1}}*dat | xargs -n1 basename > temp_file_list.txt + rsync -vhu --files-from=temp_file_list.txt ${params.vcsdir}/${params.obsid}/combined/ ozstar:/fred/oz125/vcs/${params.obsid}/combined + munlink ${params.vcsdir}/${params.obsid}/combined/*{${begin_time_increment[0]}..${begin_time_increment[0] + begin_time_increment[1] - 1}}*dat """ } diff --git a/scripts/mwa_search/LOTAAS_wrapper.py b/scripts/LOTAAS_wrapper.py similarity index 99% rename from scripts/mwa_search/LOTAAS_wrapper.py rename to scripts/LOTAAS_wrapper.py index 3e328d19..2b217591 100644 --- a/scripts/mwa_search/LOTAAS_wrapper.py +++ b/scripts/LOTAAS_wrapper.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python import logging import argparse diff --git a/scripts/mwa_search/bestgridpos.py b/scripts/bestgridpos.py similarity index 100% rename from scripts/mwa_search/bestgridpos.py rename to scripts/bestgridpos.py diff --git a/scripts/mwa_search/cold_storage_mover.py b/scripts/cold_storage_mover.py similarity index 100% rename from scripts/mwa_search/cold_storage_mover.py rename to scripts/cold_storage_mover.py diff --git a/scripts/mwa_search/find_clustered_and_known_pulsar_candidates.py b/scripts/find_clustered_and_known_pulsar_candidates.py similarity index 100% rename from scripts/mwa_search/find_clustered_and_known_pulsar_candidates.py rename to scripts/find_clustered_and_known_pulsar_candidates.py diff --git a/scripts/mwa_search/grid.py b/scripts/grid.py similarity index 100% rename from scripts/mwa_search/grid.py rename to scripts/grid.py diff --git a/scripts/mwa_search/lfDDplan.py b/scripts/lfDDplan.py similarity index 82% rename from scripts/mwa_search/lfDDplan.py rename to scripts/lfDDplan.py index 03ecf9aa..bdfee8f8 100755 --- a/scripts/mwa_search/lfDDplan.py +++ b/scripts/lfDDplan.py @@ -45,15 +45,23 @@ args.centrefreq = centrefreq - DD_plan_array = dd_plan( args.centrefreq, args.bandwidth, args.nfreqchan, args.timeres, args.lowDM, args.highDM, - min_DM_step=args.min_DM_step, max_DM_step=args.max_DM_step, - max_dms_per_job=args.max_dms_per_job) - print(" low DM | high DM | DeltaDM | Nsteps | Downsamp | nsub | Effective time resolution (ms) ") + DD_plan_array = dd_plan( + args.centrefreq, + args.bandwidth, + args.nfreqchan, + args.timeres, + args.lowDM, + args.highDM, + min_DM_step=args.min_DM_step, + max_DM_step=args.max_DM_step, + max_dms_per_job=args.max_dms_per_job + ) + print(" low DM | high DM | DeltaDM | Nsteps | Downsamp | nsub | workfactor | Effective time resolution (ms) ") total_steps = 0 - for d in DD_plan_array: - print("{0:7.1f} | {1:7.1f} | {2:7.2f} | {3:6d} | {4:8d} | {5:4d} | {6:7.3f}".\ - format(d[0], d[1], d[2], d[3], d[5], d[6], d[4])) - total_steps += d[3] + for dd_line in DD_plan_array: + dm_min, dm_max, dm_step, ndm, timeres, downsamp, nsub, total_work_factor = dd_line + print(f"{dm_min:7.1f} | {dm_max:7.1f} | {dm_step:7.2f} | {ndm:6d} | {downsamp:8d} | {nsub:4d} | {total_work_factor:10.2f} | {timeres:7.3f}") + total_steps += ndm print("Total DM steps required: {}".format(total_steps)) if args.plot: diff --git a/scripts/dpp/observation_processing_pipeline.py b/scripts/observation_processing_pipeline.py similarity index 100% rename from scripts/dpp/observation_processing_pipeline.py rename to scripts/observation_processing_pipeline.py diff --git a/scripts/dpp/opp_status.py b/scripts/opp_status.py similarity index 100% rename from scripts/dpp/opp_status.py rename to scripts/opp_status.py diff --git a/scripts/plotting/plot_obs_pulsar.py b/scripts/plotting/plot_obs_pulsar.py index e78fad3a..102a70c7 100755 --- a/scripts/plotting/plot_obs_pulsar.py +++ b/scripts/plotting/plot_obs_pulsar.py @@ -85,12 +85,20 @@ def deg_to_plotmap(ra, dec, ra_offset=False, square=False): add_group = parser.add_argument_group('Extra Plot Layers Options') add_group.add_argument('--pulsar', type=str, nargs='+', help='A list of pulsar to mark on the plot.') + add_group.add_argument('--pulsar_other', type=str, nargs='+', + help='A list of pulsar to mark on the plot in a different colour.') + add_group.add_argument('--pulsar_other_other', type=str, nargs='+', + help='A list of pulsar to mark on the plot in a different colour.') add_group.add_argument('--pulsar_detected', action='store_true', help='Plots all pulsars detected by the MWA and uploaded to the pulsar database.') add_group.add_argument('--pulsar_all', action='store_true', help='Plots all known pulsars.') add_group.add_argument('--pulsar_discovered', action='store_true', help='Plots all pulsars discovered with the MWA.') + add_group.add_argument('--pulsar_rediscovered', action='store_true', + help='Plots all pulsars rediscovered with the MWA.') + add_group.add_argument('--pulsar_unpublished', action='store_true', + help='Plots all pulsars unpublished discoveries with the MWA.') add_group.add_argument('--fill', action='store_true', help='Shades the area the MWA can view.') add_group.add_argument('--shade', type=str, nargs='+', @@ -587,21 +595,82 @@ def deg_to_plotmap(ra, dec, ra_offset=False, square=False): ra_map, dec_map = deg_to_plotmap(ra_temp, dec_temp, ra_offset=args.ra_offset, square=args.square) ra_PCAT.append(ra_map) dec_PCAT.append(dec_map) - ax.scatter(ra_PCAT, dec_PCAT, s=5, color ='purple', zorder=100) + ax.scatter(ra_PCAT, dec_PCAT, s=5, color ='red', zorder=100) + + if args.pulsar_other: + #add some pulsars + ra_PCAT = [] + dec_PCAT = [] + print("{} input pulsars".format(len(args.pulsar_other))) + raw_pulsar_list = list(dict.fromkeys(args.pulsar_other)) + print("{} distinct pulsars".format(len(raw_pulsar_list))) + pulsar_list = get_psrcat_ra_dec(pulsar_list=raw_pulsar_list) + for pulsar in pulsar_list: + ra_temp, dec_temp = sex2deg(pulsar[1], pulsar[2]) + ra_map, dec_map = deg_to_plotmap(ra_temp, dec_temp, ra_offset=args.ra_offset, square=args.square) + ra_PCAT.append(ra_map) + dec_PCAT.append(dec_map) + ax.scatter(ra_PCAT, dec_PCAT, s=5, color ='darkblue', zorder=100) + + if args.pulsar_other_other: + #add some pulsars + ra_PCAT = [] + dec_PCAT = [] + print("{} input pulsars".format(len(args.pulsar_other_other))) + raw_pulsar_list = list(dict.fromkeys(args.pulsar_other_other)) + print("{} distinct pulsars".format(len(raw_pulsar_list))) + pulsar_list = get_psrcat_ra_dec(pulsar_list=raw_pulsar_list) + for pulsar in pulsar_list: + ra_temp, dec_temp = sex2deg(pulsar[1], pulsar[2]) + ra_map, dec_map = deg_to_plotmap(ra_temp, dec_temp, ra_offset=args.ra_offset, square=args.square) + ra_PCAT.append(ra_map) + dec_PCAT.append(dec_map) + ax.scatter(ra_PCAT, dec_PCAT, s=5, color ='cornflowerblue', zorder=100) if args.pulsar_discovered: #add some pulsars ra_PCAT = [] dec_PCAT = [] - pulsar_list = [["J0036-1033", "00:36:14.58", "-10:33:16.40"], - ["J0026-1955", "00:26:36.49", "-19:55:54.87"], - ["J1002-2044", "10:02:39.26", "-20:44:41.42"]] + pulsar_list = [ + ["J0036-1033", "00:36:14.58", "-10:33:16.40"], + ["J0026-1955", "00:26:36.49", "-19:55:54.87"], # technically a rediscovery + ["J1002-2044", "10:02:39.26", "-20:44:41.42"], + #["J0451-3421", "04:51", "-34:21"], + ] + for pulsar in pulsar_list: + ra_temp, dec_temp = sex2deg(pulsar[1], pulsar[2]) + ra_map, dec_map = deg_to_plotmap(ra_temp, dec_temp, ra_offset=args.ra_offset, square=args.square) + ra_PCAT.append(ra_map) + dec_PCAT.append(dec_map) + ax.scatter(ra_PCAT, dec_PCAT, s=50, color='black', marker="*", zorder=120) + + if args.pulsar_rediscovered: + #add some pulsars + ra_PCAT = [] + dec_PCAT = [] + pulsar_list = [ + ["J1357-2530", "13:57:24.40", "-25:30:39.00"], + ] + for pulsar in pulsar_list: + ra_temp, dec_temp = sex2deg(pulsar[1], pulsar[2]) + ra_map, dec_map = deg_to_plotmap(ra_temp, dec_temp, ra_offset=args.ra_offset, square=args.square) + ra_PCAT.append(ra_map) + dec_PCAT.append(dec_map) + ax.scatter(ra_PCAT, dec_PCAT, s=50, color='grey', marker="*", zorder=120) + + if args.pulsar_unpublished: + #add some pulsars + ra_PCAT = [] + dec_PCAT = [] + pulsar_list = [ + ["J0452-3418", "04:52:10.2", "-34:18:52.9"], + ] for pulsar in pulsar_list: ra_temp, dec_temp = sex2deg(pulsar[1], pulsar[2]) ra_map, dec_map = deg_to_plotmap(ra_temp, dec_temp, ra_offset=args.ra_offset, square=args.square) ra_PCAT.append(ra_map) dec_PCAT.append(dec_map) - ax.scatter(ra_PCAT, dec_PCAT, s=10, color ='r', zorder=120) + ax.scatter(ra_PCAT, dec_PCAT, s=50, color='black', facecolors="None", marker="*", zorder=120) plt.xlabel("Right Ascension") plt.ylabel("Declination") diff --git a/scripts/dpp/pulsar_processing_pipeline.py b/scripts/pulsar_processing_pipeline.py similarity index 100% rename from scripts/dpp/pulsar_processing_pipeline.py rename to scripts/pulsar_processing_pipeline.py diff --git a/scripts/dpp/pulsars_in_fov.py b/scripts/pulsars_in_fov.py similarity index 100% rename from scripts/dpp/pulsars_in_fov.py rename to scripts/pulsars_in_fov.py diff --git a/scripts/mwa_search/rsync_rm_loop.sh b/scripts/rsync_rm_loop.sh similarity index 100% rename from scripts/mwa_search/rsync_rm_loop.sh rename to scripts/rsync_rm_loop.sh diff --git a/scripts/mwa_search/search_launch_loop.sh b/scripts/search_launch_loop.sh similarity index 100% rename from scripts/mwa_search/search_launch_loop.sh rename to scripts/search_launch_loop.sh diff --git a/setup.py b/setup.py index 0b72217c..22d1c9cc 100644 --- a/setup.py +++ b/setup.py @@ -47,44 +47,58 @@ def get_git_version(): with open('version.py', 'a') as the_file: the_file.write('__version__ = "{}"\n'.format(mwa_search_version)) -setup(name="mwa_search", - version=mwa_search_version, - description="Scripts used to search for pulsars with the Murchison Widefield Array's Voltage Capture System data", - url="https://github.com/NickSwainston/mwa_search", - #long_description=read('README.md'), - python_requires='>=3.6', - packages=['dpp', 'mwa_search'], - package_dir={'dpp': 'lib/dpp', - 'mwa_search': 'lib/mwa_search'}, - package_data={'mwa_search':['data/*.npy']}, - install_requires=reqs, - scripts=['version.py', 'scripts/calc_beamformer_benchmarks.py', - # mwa_search - 'scripts/mwa_search/cold_storage_mover.py', - 'scripts/mwa_search/grid.py', 'scripts/mwa_search/lfDDplan.py', - 'scripts/mwa_search/LOTAAS_wrapper.py', - 'scripts/mwa_search/search_launch_loop.sh', 'scripts/mwa_search/rsync_rm_loop.sh', - 'scripts/mwa_search/bestgridpos.py', 'scripts/mwa_search/find_clustered_and_known_pulsar_candidates.py', - # dpp - 'scripts/dpp/pulsars_in_fov.py', - 'scripts/dpp/observation_processing_pipeline.py', - 'scripts/dpp/opp_status.py', - # plotting - 'scripts/plotting/plot_obs_pulsar.py', - 'scripts/plotting/position_sn_heatmap_fwhm.py', - # nextflow - 'nextflow/beamform.nf', 'nextflow/beamform_module.nf', - 'nextflow/pulsar_search.nf', 'nextflow/pulsar_search_module.nf', - 'nextflow/classifier.nf', 'nextflow/classifier_module.nf', - 'nextflow/nextflow.config', 'nextflow/data_processing_pipeline.nf', - 'nextflow/candidate_TOAs.nf', 'nextflow/find_candidate_position.nf', - 'nextflow/pdmp.nf', 'nextflow/dspsr_module.nf', - 'nextflow/pulsar_search.nf', 'nextflow/pulsar_search_module.nf', - 'nextflow/mwa_search_pipeline.nf', 'nextflow/benchmark_beamformer.nf', - 'nextflow/vcs_download.nf'], - #data_files=[('AegeanTools', [os.path.join(data_dir, 'MOC.fits')]) ], - setup_requires=['pytest-runner'], - tests_require=['pytest'] # , 'nose'] - ) +setup( + name="mwa_search", + version=mwa_search_version, + description="Scripts used to search for pulsars with the Murchison Widefield Array's Voltage Capture System data", + url="https://github.com/NickSwainston/mwa_search", + #long_description=read('README.md'), + python_requires='>=3.6', + packages=['dpp', 'mwa_search'], + package_dir={ + 'dpp': 'dpp', + 'mwa_search': 'mwa_search', + }, + package_data={'mwa_search':['data/*.npy']}, + install_requires=reqs, + scripts=[ + 'version.py', + 'scripts/calc_beamformer_benchmarks.py', + # mwa_search + 'scripts/cold_storage_mover.py', + 'scripts/grid.py', + 'scripts/lfDDplan.py', + 'scripts/LOTAAS_wrapper.py', + 'scripts/search_launch_loop.sh', + 'scripts/rsync_rm_loop.sh', + 'scripts/bestgridpos.py', + 'scripts/find_clustered_and_known_pulsar_candidates.py', + # dpp + 'scripts/pulsars_in_fov.py', + 'scripts/observation_processing_pipeline.py', + 'scripts/opp_status.py', + # plotting + 'scripts/plotting/plot_obs_pulsar.py', + 'scripts/plotting/position_sn_heatmap_fwhm.py', + # nextflow + 'nextflow/mwa_search_pipeline.nf', + 'nextflow/beamform.nf', + 'nextflow/beamform_module.nf', + 'nextflow/pulsar_search.nf', + 'nextflow/pulsar_search_module.nf', + 'nextflow/classifier.nf', + 'nextflow/classifier_module.nf', + 'nextflow/nextflow.config', + 'nextflow/data_processing_pipeline.nf', + 'nextflow/candidate_TOAs.nf', + 'nextflow/find_candidate_position.nf', + 'nextflow/pdmp.nf', + 'nextflow/dspsr_module.nf', + 'nextflow/benchmark_beamformer.nf', + 'nextflow/vcs_download.nf', + ], + setup_requires=['pytest-runner'], + tests_require=['pytest'] +) os.remove('version.py')