From 60e46d71e09e142ee53cf51573f15ab1d547d5a2 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson <49216024+bgunnar5@users.noreply.github.com> Date: Wed, 14 Feb 2024 13:05:34 -0800 Subject: [PATCH] Version/1.12.0 (#466) * fix default worker bug with all steps * version bump and requirements fix * Bugfix/filename-special-vars (#425) * fix file naming bug * fix filename bug with variable as study name * add tests for the file name special vars changes * modify changelog * implement Luc's suggestions * remove replace line * Create dependabot-changelog-updater.yml * testing outputs of modifying changelog * delete dependabot-changelog-updater * feature/pdf-docs (#427) * first attempt at adding pdf * fixing build error * modify changelog to show docs changes * fix errors Luc found in the build logs * trying out removal of latex * reverting latex changes back * uncommenting the latex_elements settings * adding epub to see if latex will build * adding a latex engine variable to conf * fix naming error with latex_engine * attempting to add a logo to the pdf build * testing an override to the searchtools file * revert back to not using searchtools override * update changelog * bugfix/openfoam_singularity_issues (#426) * fix openfoam_singularity issues * update requirements and descriptions for openfoam examples * bugfix/output-path-substitution (#430) * fix bug with output_path and variable substitution * add tests for cli substitutions * bugfix/scheduler-permission-error (#436) * Release/1.10.2 (#437) * bump version to 1.10.2 * bump version in CHANGELOG * resolve develop to main merge issues (#439) * fix default worker bug with all steps * version bump and requirements fix * dependabot/certifi-requests-pygments (#441) * Bump certifi from 2022.12.7 to 2023.7.22 in /docs Bumps [certifi](https://github.com/certifi/python-certifi) from 2022.12.7 to 2023.7.22. - [Commits](https://github.com/certifi/python-certifi/compare/2022.12.07...2023.07.22) --- updated-dependencies: - dependency-name: certifi dependency-type: direct:production ... Signed-off-by: dependabot[bot] * add all dependabot changes and update CHANGELOG --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * bugfix/server-pip-redis-conf (#443) * add *.conf to the MANIFEST file so pip will grab the redis.conf file * add note explaining how to fix a hanging merlin server start * modify CHANGELOG * add second export option to docs and fix typo * bump to version 1.10.3 (#444) * bugfix/sphinx-5.3.0-requirement (#446) * Version/1.10.3 (#445) * fix default worker bug with all steps * version bump and requirements fix * Bugfix/filename-special-vars (#425) * fix file naming bug * fix filename bug with variable as study name * add tests for the file name special vars changes * modify changelog * implement Luc's suggestions * remove replace line * Create dependabot-changelog-updater.yml * testing outputs of modifying changelog * delete dependabot-changelog-updater * feature/pdf-docs (#427) * first attempt at adding pdf * fixing build error * modify changelog to show docs changes * fix errors Luc found in the build logs * trying out removal of latex * reverting latex changes back * uncommenting the latex_elements settings * adding epub to see if latex will build * adding a latex engine variable to conf * fix naming error with latex_engine * attempting to add a logo to the pdf build * testing an override to the searchtools file * revert back to not using searchtools override * update changelog * bugfix/openfoam_singularity_issues (#426) * fix openfoam_singularity issues * update requirements and descriptions for openfoam examples * bugfix/output-path-substitution (#430) * fix bug with output_path and variable substitution * add tests for cli substitutions * bugfix/scheduler-permission-error (#436) * Release/1.10.2 (#437) * bump version to 1.10.2 * bump version in CHANGELOG * resolve develop to main merge issues (#439) * fix default worker bug with all steps * version bump and requirements fix * dependabot/certifi-requests-pygments (#441) * Bump certifi from 2022.12.7 to 2023.7.22 in /docs Bumps [certifi](https://github.com/certifi/python-certifi) from 2022.12.7 to 2023.7.22. - [Commits](https://github.com/certifi/python-certifi/compare/2022.12.07...2023.07.22) --- updated-dependencies: - dependency-name: certifi dependency-type: direct:production ... Signed-off-by: dependabot[bot] * add all dependabot changes and update CHANGELOG --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * bugfix/server-pip-redis-conf (#443) * add *.conf to the MANIFEST file so pip will grab the redis.conf file * add note explaining how to fix a hanging merlin server start * modify CHANGELOG * add second export option to docs and fix typo * bump to version 1.10.3 (#444) --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * change hardcoded sphinx requirement * update CHANGELOG --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feature/vlauncher (#447) * fix file naming error for iterative workflows * fixed small bug with new filepath naming * add VLAUNCHER functionality * add docs for VLAUNCHER and modify changelog * re-word docs and fix table format * add a test for vlauncher * run fix-style and add a test for vlauncher * Add the find_vlaunch_var and setup_vlaunch functions. The numeric value of the shell variables may not be defined until run time, so replace with variable strings instead of values. Consolidate the commands into one function. * Add variable set for (t)csh. * Run fix-style * make step settings the defaults and ignore commented lines * add some additional tests * remove regex library import --------- Co-authored-by: Joseph M. Koning * release/1.11.0 (#448) * bugfix/skewed-sample-hierarchy (#450) * add patch for skewed sample hierarchy/additional samples * update changelog * catch narrower range of exceptions * bugfix/lsf-gpu-typo (#453) * fix typo in batch.py that causes a bug * change print statements to log statements * release/1.11.1 (#454) * Add Pytest Fixtures to Test Suite (#456) * begin work on integration refactor; create fixtures and initial tests * update CHANGELOG and run fix-style * add pytest fixtures and README explaining them * add tests to demonstrate how to use the fixtures * move/rename some files and modify integration's README * add password change to redis.pass file * fix lint issues * modify redis pwd for test server to be constant for each test * fix lint issue only caught on github ci * Bugfix for WEAVE CI (#457) * begin work on integration refactor; create fixtures and initial tests * update CHANGELOG and run fix-style * add pytest fixtures and README explaining them * add tests to demonstrate how to use the fixtures * move/rename some files and modify integration's README * add password change to redis.pass file * fix lint issues * modify redis pwd for test server to be constant for each test * fix lint issue only caught on github ci * add fix for merlin server startup * update CHANGELOG * bugfix/monitor-shutdown (#452) * add celery query to see if workers still processing tasks * fix merlin status when using redis as broker * fix consumer count bug and run fix-style * fix linter issues * update changelog * update docs for monitor * remove unused exception I previously added * first attempt at using pytest fixtures for monitor tests * (partially) fix launch_workers fixture so it can be used in multiple classes * fix linter issues and typo on pytest decorator * update black's python version and fix style issue * remove print statements from celeryadapter.py * workers manager is now allowed to be used as a context manager * add one thing to changelog and remove print statement * Add the missing restart keyword to the specification docs. (#459) * docs/conversion-to-mkdocs (#460) * remove a merge conflict statement that was missed * add base requirements for mkdocs * set up configuration for API docs * start work on porting user guide to mkdocs * add custom styling and contact page * begin work on porting tutorial to mkdocs * add new examples page * move old sphinx docs to their own folder (*delete later*) * modify some admonitions to be success * modify hello examples page and port step 3 of tutorial to mkdocs * fix typo in hello example * finish porting step 4 of tutorial to mkdocs * port part 5 of the tutorial to mkdocs * copy faq and contributing from old docs * port step 6 of tutorial to mkdocs * remove unused prereq * port step 7 of tutorial to mkdocs * add more detailed instructions on contributing * move venv page into installation and add spack instructions too * add configuration docs * add content to user guide landing page * port celery page to mkdocs * rearrange configuration pages to add in merlin server configuration instructions * port command line page to mkdocs * finish new landing page * change size of merlin logo * port variables page to mkdocs * fix broken links to configuration page * port FAQ to mkdocs * fix incorrect requirement name * update CHANGELOG * attempt to get docs to build through readthedocs * port docker page to mkdocs * port contributing guide to mkdocs * add new 'running studies' page * add path changes to images * add a page on how to interpret study output * add page on the spec file * remove old sphinx docs that are no longer needed * added README to docs and updated CHANGELOG * fix copyright and hello_samples tree * rearrange images/stylesheets and statements that use them * add suggestions from Luc and Joe * add tcsh instructions for venv activation * add Charle's suggestions for the landing page * change tcsh mentions to csh * openfoam tutorial modifications (#463) * feature/revamped status (#464) * feature/new-status (#442) * add backend functionality for merlin status * add frontend functionality for merlin status * add tests for merlin status * run fix-style and remove import of deprecated function * update CHANGELOG * add more logging statements, make better use of glob * run fix-style * clean up test files a bit * fix test suite after step_name_map mod * add avg/std dev run time calculations to status * modify status tests to accommodate new avg/std dev calculations * fix linter issues * fix lint issue and add test for avg/std dev calc * feature/detailed-status (#451) * Version/1.11.0 (#449) * fix default worker bug with all steps * version bump and requirements fix * Bugfix/filename-special-vars (#425) * fix file naming bug * fix filename bug with variable as study name * add tests for the file name special vars changes * modify changelog * implement Luc's suggestions * remove replace line * Create dependabot-changelog-updater.yml * testing outputs of modifying changelog * delete dependabot-changelog-updater * feature/pdf-docs (#427) * first attempt at adding pdf * fixing build error * modify changelog to show docs changes * fix errors Luc found in the build logs * trying out removal of latex * reverting latex changes back * uncommenting the latex_elements settings * adding epub to see if latex will build * adding a latex engine variable to conf * fix naming error with latex_engine * attempting to add a logo to the pdf build * testing an override to the searchtools file * revert back to not using searchtools override * update changelog * bugfix/openfoam_singularity_issues (#426) * fix openfoam_singularity issues * update requirements and descriptions for openfoam examples * bugfix/output-path-substitution (#430) * fix bug with output_path and variable substitution * add tests for cli substitutions * bugfix/scheduler-permission-error (#436) * Release/1.10.2 (#437) * bump version to 1.10.2 * bump version in CHANGELOG * resolve develop to main merge issues (#439) * fix default worker bug with all steps * version bump and requirements fix * dependabot/certifi-requests-pygments (#441) * Bump certifi from 2022.12.7 to 2023.7.22 in /docs Bumps [certifi](https://github.com/certifi/python-certifi) from 2022.12.7 to 2023.7.22. - [Commits](https://github.com/certifi/python-certifi/compare/2022.12.07...2023.07.22) --- updated-dependencies: - dependency-name: certifi dependency-type: direct:production ... Signed-off-by: dependabot[bot] * add all dependabot changes and update CHANGELOG --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * bugfix/server-pip-redis-conf (#443) * add *.conf to the MANIFEST file so pip will grab the redis.conf file * add note explaining how to fix a hanging merlin server start * modify CHANGELOG * add second export option to docs and fix typo * bump to version 1.10.3 (#444) * bugfix/sphinx-5.3.0-requirement (#446) * Version/1.10.3 (#445) * fix default worker bug with all steps * version bump and requirements fix * Bugfix/filename-special-vars (#425) * fix file naming bug * fix filename bug with variable as study name * add tests for the file name special vars changes * modify changelog * implement Luc's suggestions * remove replace line * Create dependabot-changelog-updater.yml * testing outputs of modifying changelog * delete dependabot-changelog-updater * feature/pdf-docs (#427) * first attempt at adding pdf * fixing build error * modify changelog to show docs changes * fix errors Luc found in the build logs * trying out removal of latex * reverting latex changes back * uncommenting the latex_elements settings * adding epub to see if latex will build * adding a latex engine variable to conf * fix naming error with latex_engine * attempting to add a logo to the pdf build * testing an override to the searchtools file * revert back to not using searchtools override * update changelog * bugfix/openfoam_singularity_issues (#426) * fix openfoam_singularity issues * update requirements and descriptions for openfoam examples * bugfix/output-path-substitution (#430) * fix bug with output_path and variable substitution * add tests for cli substitutions * bugfix/scheduler-permission-error (#436) * Release/1.10.2 (#437) * bump version to 1.10.2 * bump version in CHANGELOG * resolve develop to main merge issues (#439) * fix default worker bug with all steps * version bump and requirements fix * dependabot/certifi-requests-pygments (#441) * Bump certifi from 2022.12.7 to 2023.7.22 in /docs Bumps [certifi](https://github.com/certifi/python-certifi) from 2022.12.7 to 2023.7.22. - [Commits](https://github.com/certifi/python-certifi/compare/2022.12.07...2023.07.22) --- updated-dependencies: - dependency-name: certifi dependency-type: direct:production ... Signed-off-by: dependabot[bot] * add all dependabot changes and update CHANGELOG --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * bugfix/server-pip-redis-conf (#443) * add *.conf to the MANIFEST file so pip will grab the redis.conf file * add note explaining how to fix a hanging merlin server start * modify CHANGELOG * add second export option to docs and fix typo * bump to version 1.10.3 (#444) --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * change hardcoded sphinx requirement * update CHANGELOG --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feature/vlauncher (#447) * fix file naming error for iterative workflows * fixed small bug with new filepath naming * add VLAUNCHER functionality * add docs for VLAUNCHER and modify changelog * re-word docs and fix table format * add a test for vlauncher * run fix-style and add a test for vlauncher * Add the find_vlaunch_var and setup_vlaunch functions. The numeric value of the shell variables may not be defined until run time, so replace with variable strings instead of values. Consolidate the commands into one function. * Add variable set for (t)csh. * Run fix-style * make step settings the defaults and ignore commented lines * add some additional tests * remove regex library import --------- Co-authored-by: Joseph M. Koning * release/1.11.0 (#448) * bugfix/skewed-sample-hierarchy (#450) * add patch for skewed sample hierarchy/additional samples * update changelog * catch narrower range of exceptions --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Joseph M. Koning * add functionality for the detailed-status command * add tests for detailed-status * fix linter issues * update changelog * general cleanup and add log statements * slightly modify two tests * default status renderer now uses json status format * remove inaccurate comment * bugfix/lsf-gpu-typo (#453) * fix typo in batch.py that causes a bug * change print statements to log statements * release/1.11.1 (#454) * Add Pytest Fixtures to Test Suite (#456) * begin work on integration refactor; create fixtures and initial tests * update CHANGELOG and run fix-style * add pytest fixtures and README explaining them * add tests to demonstrate how to use the fixtures * move/rename some files and modify integration's README * add password change to redis.pass file * fix lint issues * modify redis pwd for test server to be constant for each test * fix lint issue only caught on github ci * Bugfix for WEAVE CI (#457) * begin work on integration refactor; create fixtures and initial tests * update CHANGELOG and run fix-style * add pytest fixtures and README explaining them * add tests to demonstrate how to use the fixtures * move/rename some files and modify integration's README * add password change to redis.pass file * fix lint issues * modify redis pwd for test server to be constant for each test * fix lint issue only caught on github ci * add fix for merlin server startup * update CHANGELOG * bugfix/monitor-shutdown (#452) * add celery query to see if workers still processing tasks * fix merlin status when using redis as broker * fix consumer count bug and run fix-style * fix linter issues * update changelog * update docs for monitor * remove unused exception I previously added * first attempt at using pytest fixtures for monitor tests * (partially) fix launch_workers fixture so it can be used in multiple classes * fix linter issues and typo on pytest decorator * update black's python version and fix style issue * remove print statements from celeryadapter.py * workers manager is now allowed to be used as a context manager * add one thing to changelog and remove print statement * Add the missing restart keyword to the specification docs. (#459) * add Jeremy's suggestion to change vars option to output-path * remove unnecessary lines from CHANGELOG --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Joseph M. Koning Co-authored-by: Joe Koning * feature/queue info (#461) * remove a merge conflict statement that was missed * add queue-info functionality * add tests for queue-info * update CHANGELOG * add try/except for forceful termination of test workers * change github workflow to use py38 with black instead of py36 * run fix-style with py 3.12 and fix a typo in a test * add filetype check for dump option * add banner print statement * docs/revamped status (#462) * fix broken image link in README * add new commands to the command line page * add monitoring docs layout and complete status cmds page * fix bug with dumping queue-info to files * add docs for queue-info * add documentation for 'query-workers' * add reference to new query-workers docs and split a paragraph * fix small bug with --steps option of monitor * add documentation for monitor command * update CHANGELOG * fix dump-csv image for queue-info --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Joseph M. Koning Co-authored-by: Joe Koning * release/1.12.0 (#465) * remove a merge conflict statement that was missed * bump version to 1.12.0 --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Joseph M. Koning Co-authored-by: Joe Koning Co-authored-by: Jane Herriman --- .github/workflows/push-pr_workflow.yml | 8 +- .readthedocs.yaml | 6 +- CHANGELOG.md | 77 + Makefile | 16 +- README.md | 2 +- docs/Makefile | 31 - docs/README.md | 44 + docs/api_reference/index.md | 5 + docs/assets/images/hello-samples-tree.png | Bin 0 -> 124951 bytes .../basic-merlin-info-workspace.png | Bin 0 -> 28175 bytes .../basic-step-workspace.png | Bin 0 -> 27852 bytes .../merlin-info-with-samples.png | Bin 0 -> 48968 bytes .../modified-hierarchy-structure.png | Bin 0 -> 57958 bytes .../two-level-sample-hierarchy.png | Bin 0 -> 57043 bytes .../workspace-with-params-and-samples.png | Bin 0 -> 39547 bytes .../workspace-with-params.png | Bin 0 -> 97183 bytes .../workspace-with-samples.png | Bin 0 -> 37567 bytes docs/{ => assets}/images/merlin_arch.png | Bin .../images/merlin_banner.png} | Bin docs/assets/images/merlin_banner_white.png | Bin 0 -> 61782 bytes docs/{ => assets}/images/merlin_icon.png | Bin .../monitor-flowchart.png | Bin 0 -> 49390 bytes .../status-step-2-incomplete.png | Bin 0 -> 45627 bytes .../monitor_for_allocation/status-success.png | Bin 0 -> 47186 bytes .../monitor_for_allocation/steps-demo.png | Bin 0 -> 47711 bytes .../queues_and_workers/active-queues.png | Bin 0 -> 19662 bytes .../queues_and_workers/connected-workers.png | Bin 0 -> 28087 bytes .../queues_and_workers/dump-csv.png | Bin 0 -> 40059 bytes .../queues_and_workers/dump-json.png | Bin 0 -> 43697 bytes .../queues_and_workers/no-active-queues.png | Bin 0 -> 20105 bytes .../no-connected-workers.png | Bin 0 -> 19790 bytes .../query-workers-spec-all-workers.png | Bin 0 -> 26557 bytes .../query-workers-spec-option.png | Bin 0 -> 29054 bytes .../queues_and_workers/queue-info-no-vars.png | Bin 0 -> 31548 bytes .../queue-info-with-vars.png | Bin 0 -> 33009 bytes .../queues-example-all-workers.png | Bin 0 -> 27058 bytes .../queues-example-filtered-workers.png | Bin 0 -> 24183 bytes .../specific-queues-active.png | Bin 0 -> 21955 bytes .../specific-queues-inactive.png | Bin 0 -> 21874 bytes .../specification-option.png | Bin 0 -> 26768 bytes .../queues_and_workers/steps-option.png | Bin 0 -> 25137 bytes .../workers-option-with-regex.png | Bin 0 -> 24539 bytes .../workers-option-with-worker-names.png | Bin 0 -> 25095 bytes .../monitoring/status_cmds/ascii-error.png | Bin 0 -> 30682 bytes .../images/monitoring/status_cmds/cb-help.png | Bin 0 -> 31247 bytes .../monitoring/status_cmds/disable-pager.png | Bin 0 -> 82645 bytes .../monitoring/status_cmds/disable-theme.png | Bin 0 -> 52574 bytes .../monitoring/status_cmds/dump-csv.png | Bin 0 -> 44922 bytes .../monitoring/status_cmds/dump-json.png | Bin 0 -> 55088 bytes .../status_cmds/filter-max-tasks.png | Bin 0 -> 70916 bytes .../monitoring/status_cmds/filter-prompt.png | Bin 0 -> 46241 bytes .../status_cmds/filter-return-code.png | Bin 0 -> 43465 bytes .../monitoring/status_cmds/filter-steps.png | Bin 0 -> 59783 bytes .../status_cmds/filter-task-queues.png | Bin 0 -> 76736 bytes .../status_cmds/filter-task-status.png | Bin 0 -> 59832 bytes .../monitoring/status_cmds/filter-workers.png | Bin 0 -> 92673 bytes .../monitoring/status_cmds/inside-pager.png | Bin 0 -> 60180 bytes .../monitoring/status_cmds/layout-table.png | Bin 0 -> 103436 bytes .../status_cmds/max-tasks-prompt.png | Bin 0 -> 55625 bytes .../status_cmds/multiple-studies.png | Bin 0 -> 20539 bytes .../monitoring/status_cmds/outside-pager.png | Bin 0 -> 24756 bytes .../images/monitoring/status_cmds/prompt.png | Bin 0 -> 25376 bytes .../images/monitoring/status_cmds/status.png | Bin 0 -> 49713 bytes .../running_studies/current-node-launch.png | Bin 0 -> 46785 bytes .../running_studies/iterative-diagram.png | Bin 0 -> 106628 bytes .../running_studies/merlin-run-diagram.png | Bin 0 -> 120345 bytes .../running_studies/parallel-launch.png | Bin 0 -> 61759 bytes .../producer-consumer-model.png | Bin 0 -> 89461 bytes .../worker-server-communication.png | Bin 0 -> 60915 bytes .../advanced_topics/cumulative_results.png | Bin .../images/tutorial}/hello_world/dag1.png | Bin .../images/tutorial}/hello_world/dag2.png | Bin .../images/tutorial}/hello_world/dag3.png | Bin .../images/tutorial}/hello_world/dag4.png | Bin .../tutorial}/hello_world/merlin_output.png | Bin .../tutorial}/hello_world/merlin_output2.png | Bin .../introduction}/central_coordination.png | Bin .../introduction}/external_coordination.png | Bin .../introduction}/internal_coordination.png | Bin .../tutorial/introduction}/merlin_run.png | Bin .../introduction}/task_creation_rate.png | Bin .../run_simulation/lid-driven-stable.png | Bin .../tutorial}/run_simulation/openfoam_dag.png | Bin .../run_simulation/openfoam_wf_output.png | Bin .../tutorial}/run_simulation/prediction.png | Bin .../images/tutorial}/run_simulation/setup.png | Bin docs/assets/javascripts/swap_lp_image.js | 26 + docs/assets/stylesheets/extra.css | 8 + docs/contact.md | 21 + docs/examples/feature_demo.md | 3 + docs/examples/flux.md | 3 + docs/examples/hello.md | 734 ++++++++++ docs/examples/hpc.md | 3 + docs/examples/index.md | 48 + docs/examples/iterative.md | 3 + docs/examples/lsf.md | 3 + docs/examples/restart.md | 3 + docs/examples/slurm.md | 3 + docs/faq.md | 453 ++++++ docs/gen_ref_pages.py | 40 + docs/index.md | 226 +++ docs/make.bat | 36 - docs/requirements.in | 4 - docs/requirements.txt | 10 +- docs/source/.gitignore | 4 - docs/source/_static/custom.css | 39 - docs/source/_static/custom.js | 25 - docs/source/app_config/app_amqp.yaml | 58 - docs/source/celery_overview.rst | 125 -- docs/source/conf.py | 183 --- docs/source/docker.rst | 93 -- docs/source/faq.rst | 476 ------ docs/source/getting_started.rst | 156 -- docs/source/index.rst | 83 -- docs/source/merlin_commands.rst | 478 ------ docs/source/merlin_config.rst | 303 ---- docs/source/merlin_developer.rst | 175 --- docs/source/merlin_server.rst | 72 - docs/source/merlin_specification.rst | 346 ----- docs/source/merlin_variables.rst | 397 ----- docs/source/merlin_workflows.rst | 49 - .../advanced_topics/advanced_requirements.txt | 3 - .../advanced_topics/advanced_topics.rst | 415 ------ .../modules/advanced_topics/faker_demo.yaml | 116 -- docs/source/modules/before.rst | 47 - docs/source/modules/contribute.rst | 48 - docs/source/modules/hello_world/.gitignore | 1 - docs/source/modules/hello_world/celery.txt | 27 - docs/source/modules/hello_world/hello.yaml | 24 - .../modules/hello_world/hello_world.rst | 353 ----- docs/source/modules/hello_world/local_out.txt | 31 - docs/source/modules/hello_world/run_out.txt | 25 - .../modules/hello_world/run_workers_out.txt | 21 - .../modules/hello_world/stop_workers.txt | 24 - .../installation/app_docker_rabbit.yaml | 10 - .../installation/app_docker_redis.yaml | 11 - .../modules/installation/app_local_redis.yaml | 11 - .../modules/installation/docker-compose.yml | 23 - .../installation/docker-compose_rabbit.yml | 46 - .../docker-compose_rabbit_redis_tls.yml | 38 - .../modules/installation/installation.rst | 340 ----- docs/source/modules/introduction.rst | 476 ------ docs/source/modules/port_your_application.rst | 70 - .../modules/run_simulation/run_simulation.rst | 429 ------ docs/source/server/commands.rst | 94 -- docs/source/server/configuration.rst | 75 - docs/source/spack.rst | 131 -- docs/source/virtualenv.rst | 54 - docs/tutorial/0_prerequisites.md | 36 + docs/tutorial/1_introduction.md | 242 ++++ docs/tutorial/2_installation.md | 516 +++++++ docs/tutorial/3_hello_world.md | 478 ++++++ docs/tutorial/4_run_simulation.md | 331 +++++ docs/tutorial/5_advanced_topics.md | 430 ++++++ docs/tutorial/6_contribute.md | 112 ++ docs/tutorial/7_port_application.md | 57 + .../tutorial.rst => tutorial/index.md} | 30 +- docs/user_guide/celery.md | 205 +++ docs/user_guide/command_line.md | 907 ++++++++++++ .../configuration/external_server.md | 407 ++++++ docs/user_guide/configuration/index.md | 266 ++++ .../user_guide/configuration/merlin_server.md | 177 +++ docs/user_guide/contributing.md | 193 +++ docs/user_guide/docker.md | 332 +++++ docs/user_guide/index.md | 23 + docs/user_guide/installation.md | 271 ++++ docs/user_guide/interpreting_output.md | 1072 ++++++++++++++ docs/user_guide/monitoring/index.md | 33 + .../monitoring/monitor_for_allocation.md | 661 +++++++++ .../monitoring/queues_and_workers.md | 608 ++++++++ docs/user_guide/monitoring/status_cmds.md | 702 +++++++++ docs/user_guide/running_studies.md | 520 +++++++ docs/user_guide/specification.md | 725 ++++++++++ docs/user_guide/variables.md | 254 ++++ merlin/__init__.py | 4 +- merlin/ascii_art.py | 2 +- merlin/celery.py | 2 +- merlin/common/__init__.py | 2 +- merlin/common/abstracts/__init__.py | 2 +- merlin/common/abstracts/enums/__init__.py | 2 +- merlin/common/dumper.py | 151 ++ merlin/common/openfilelist.py | 2 +- merlin/common/opennpylib.py | 2 +- merlin/common/sample_index.py | 2 +- merlin/common/sample_index_factory.py | 2 +- merlin/common/security/__init__.py | 2 +- merlin/common/security/encrypt.py | 2 +- .../security/encrypt_backend_traffic.py | 2 +- merlin/common/tasks.py | 227 ++- merlin/common/util_sampling.py | 2 +- merlin/config/__init__.py | 2 +- merlin/config/broker.py | 2 +- merlin/config/celeryconfig.py | 3 +- merlin/config/configfile.py | 2 +- merlin/config/results_backend.py | 2 +- merlin/config/utils.py | 2 +- merlin/data/celery/__init__.py | 2 +- merlin/display.py | 321 +++- merlin/examples/__init__.py | 2 +- merlin/examples/examples.py | 2 +- merlin/examples/generator.py | 2 +- .../null_spec/scripts/launch_jobs.py | 4 +- ....yaml => openfoam_wf_docker_template.yaml} | 0 .../workflows/openfoam_wf/requirements.txt | 2 +- .../openfoam_wf/scripts/cavity_setup.sh | 6 +- .../openfoam_wf_singularity/openfoam_wf.yaml | 62 + ... => openfoam_wf_singularity_template.yaml} | 0 .../openfoam_wf_singularity/requirements.txt | 2 +- merlin/exceptions/__init__.py | 26 +- merlin/log_formatter.py | 2 +- merlin/main.py | 324 ++++- merlin/merlin_templates.py | 2 +- merlin/router.py | 221 ++- merlin/server/__init__.py | 2 +- merlin/server/server_commands.py | 5 +- merlin/server/server_config.py | 2 +- merlin/server/server_util.py | 2 +- merlin/spec/__init__.py | 2 +- merlin/spec/all_keys.py | 2 +- merlin/spec/defaults.py | 2 +- merlin/spec/expansion.py | 5 +- merlin/spec/override.py | 2 +- merlin/spec/specification.py | 193 ++- merlin/study/__init__.py | 2 +- merlin/study/batch.py | 2 +- merlin/study/celeryadapter.py | 287 +++- merlin/study/dag.py | 18 +- merlin/study/script_adapter.py | 2 +- merlin/study/status.py | 1056 ++++++++++++++ merlin/study/status_constants.py | 44 + merlin/study/status_renderers.py | 412 ++++++ merlin/study/step.py | 320 +++- merlin/study/study.py | 33 +- merlin/utils.py | 143 +- mkdocs.yml | 131 ++ requirements/dev.txt | 2 + setup.py | 2 +- tests/README.md | 152 ++ tests/celery_test_workers.py | 234 +++ tests/conftest.py | 209 +++ tests/integration/conditions.py | 2 +- .../{test_definitions.py => definitions.py} | 2 +- tests/integration/run_tests.py | 11 +- tests/unit/config/old_test_configfile.py | 1 + tests/unit/config/old_test_results_backend.py | 1 + tests/unit/config/utils.py | 1 + tests/unit/study/__init__.py | 29 + .../status_test_files/combine_status_files.py | 113 ++ .../study/status_test_files/shared_tests.py | 307 ++++ .../status_test_files/status_test_spec.yaml | 79 + .../dummy.txt | 1 + .../cancel_step/MERLIN_STATUS.json | 17 + .../cancel_step/status.lock | 0 .../fail_step/MERLIN_STATUS.json | 17 + .../fail_step/status.lock | 0 .../MERLIN_STATUS.json | 21 + .../GREET.hello.LEAVE.goodbye/status.lock | 0 .../GREET.hola.LEAVE.adios/MERLIN_STATUS.json | 21 + .../GREET.hola.LEAVE.adios/status.lock | 0 .../just_samples/MERLIN_STATUS.json | 45 + .../just_samples/status.lock | 0 .../merlin_info/samples.csv | 5 + .../status_test_spec.expanded.yaml | 112 ++ .../merlin_info/status_test_spec.orig.yaml | 80 + .../merlin_info/status_test_spec.partial.yaml | 118 ++ .../GREET.hello/MERLIN_STATUS.json | 47 + .../GREET.hello/status.lock | 0 .../GREET.hola/MERLIN_STATUS.json | 47 + .../params_and_samples/GREET.hola/status.lock | 0 .../status_test_variables.py | 620 ++++++++ tests/unit/study/test_celeryadapter.py | 521 +++++++ tests/unit/study/test_detailed_status.py | 1288 +++++++++++++++++ tests/unit/study/test_status.py | 329 +++++ tests/unit/study/test_study.py | 1 + 274 files changed, 19438 insertions(+), 6474 deletions(-) delete mode 100644 docs/Makefile create mode 100644 docs/README.md create mode 100644 docs/api_reference/index.md create mode 100644 docs/assets/images/hello-samples-tree.png create mode 100644 docs/assets/images/interpreting_output/basic-merlin-info-workspace.png create mode 100644 docs/assets/images/interpreting_output/basic-step-workspace.png create mode 100644 docs/assets/images/interpreting_output/merlin-info-with-samples.png create mode 100644 docs/assets/images/interpreting_output/modified-hierarchy-structure.png create mode 100644 docs/assets/images/interpreting_output/two-level-sample-hierarchy.png create mode 100644 docs/assets/images/interpreting_output/workspace-with-params-and-samples.png create mode 100644 docs/assets/images/interpreting_output/workspace-with-params.png create mode 100644 docs/assets/images/interpreting_output/workspace-with-samples.png rename docs/{ => assets}/images/merlin_arch.png (100%) rename docs/{images/merlin.png => assets/images/merlin_banner.png} (100%) create mode 100644 docs/assets/images/merlin_banner_white.png rename docs/{ => assets}/images/merlin_icon.png (100%) create mode 100644 docs/assets/images/monitoring/monitor_for_allocation/monitor-flowchart.png create mode 100644 docs/assets/images/monitoring/monitor_for_allocation/status-step-2-incomplete.png create mode 100644 docs/assets/images/monitoring/monitor_for_allocation/status-success.png create mode 100644 docs/assets/images/monitoring/monitor_for_allocation/steps-demo.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/active-queues.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/connected-workers.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/dump-csv.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/dump-json.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/no-active-queues.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/no-connected-workers.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/query-workers-spec-all-workers.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/query-workers-spec-option.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/queue-info-no-vars.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/queue-info-with-vars.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/queues-example-all-workers.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/queues-example-filtered-workers.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/specific-queues-active.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/specific-queues-inactive.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/specification-option.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/steps-option.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/workers-option-with-regex.png create mode 100644 docs/assets/images/monitoring/queues_and_workers/workers-option-with-worker-names.png create mode 100644 docs/assets/images/monitoring/status_cmds/ascii-error.png create mode 100644 docs/assets/images/monitoring/status_cmds/cb-help.png create mode 100644 docs/assets/images/monitoring/status_cmds/disable-pager.png create mode 100644 docs/assets/images/monitoring/status_cmds/disable-theme.png create mode 100644 docs/assets/images/monitoring/status_cmds/dump-csv.png create mode 100644 docs/assets/images/monitoring/status_cmds/dump-json.png create mode 100644 docs/assets/images/monitoring/status_cmds/filter-max-tasks.png create mode 100644 docs/assets/images/monitoring/status_cmds/filter-prompt.png create mode 100644 docs/assets/images/monitoring/status_cmds/filter-return-code.png create mode 100644 docs/assets/images/monitoring/status_cmds/filter-steps.png create mode 100644 docs/assets/images/monitoring/status_cmds/filter-task-queues.png create mode 100644 docs/assets/images/monitoring/status_cmds/filter-task-status.png create mode 100644 docs/assets/images/monitoring/status_cmds/filter-workers.png create mode 100644 docs/assets/images/monitoring/status_cmds/inside-pager.png create mode 100644 docs/assets/images/monitoring/status_cmds/layout-table.png create mode 100644 docs/assets/images/monitoring/status_cmds/max-tasks-prompt.png create mode 100644 docs/assets/images/monitoring/status_cmds/multiple-studies.png create mode 100644 docs/assets/images/monitoring/status_cmds/outside-pager.png create mode 100644 docs/assets/images/monitoring/status_cmds/prompt.png create mode 100644 docs/assets/images/monitoring/status_cmds/status.png create mode 100644 docs/assets/images/running_studies/current-node-launch.png create mode 100644 docs/assets/images/running_studies/iterative-diagram.png create mode 100644 docs/assets/images/running_studies/merlin-run-diagram.png create mode 100644 docs/assets/images/running_studies/parallel-launch.png create mode 100644 docs/assets/images/running_studies/producer-consumer-model.png create mode 100644 docs/assets/images/running_studies/worker-server-communication.png rename docs/{source/modules => assets/images/tutorial}/advanced_topics/cumulative_results.png (100%) rename docs/{source/modules => assets/images/tutorial}/hello_world/dag1.png (100%) rename docs/{source/modules => assets/images/tutorial}/hello_world/dag2.png (100%) rename docs/{source/modules => assets/images/tutorial}/hello_world/dag3.png (100%) rename docs/{source/modules => assets/images/tutorial}/hello_world/dag4.png (100%) rename docs/{source/modules => assets/images/tutorial}/hello_world/merlin_output.png (100%) rename docs/{source/modules => assets/images/tutorial}/hello_world/merlin_output2.png (100%) rename docs/{images => assets/images/tutorial/introduction}/central_coordination.png (100%) rename docs/{images => assets/images/tutorial/introduction}/external_coordination.png (100%) rename docs/{images => assets/images/tutorial/introduction}/internal_coordination.png (100%) rename docs/{images => assets/images/tutorial/introduction}/merlin_run.png (100%) rename docs/{images => assets/images/tutorial/introduction}/task_creation_rate.png (100%) rename docs/{source/modules => assets/images/tutorial}/run_simulation/lid-driven-stable.png (100%) rename docs/{source/modules => assets/images/tutorial}/run_simulation/openfoam_dag.png (100%) rename docs/{source/modules => assets/images/tutorial}/run_simulation/openfoam_wf_output.png (100%) rename docs/{source/modules => assets/images/tutorial}/run_simulation/prediction.png (100%) rename docs/{source/modules => assets/images/tutorial}/run_simulation/setup.png (100%) create mode 100644 docs/assets/javascripts/swap_lp_image.js create mode 100644 docs/assets/stylesheets/extra.css create mode 100644 docs/contact.md create mode 100644 docs/examples/feature_demo.md create mode 100644 docs/examples/flux.md create mode 100644 docs/examples/hello.md create mode 100644 docs/examples/hpc.md create mode 100644 docs/examples/index.md create mode 100644 docs/examples/iterative.md create mode 100644 docs/examples/lsf.md create mode 100644 docs/examples/restart.md create mode 100644 docs/examples/slurm.md create mode 100644 docs/faq.md create mode 100644 docs/gen_ref_pages.py create mode 100644 docs/index.md delete mode 100644 docs/make.bat delete mode 100644 docs/requirements.in delete mode 100644 docs/source/.gitignore delete mode 100644 docs/source/_static/custom.css delete mode 100644 docs/source/_static/custom.js delete mode 100644 docs/source/app_config/app_amqp.yaml delete mode 100644 docs/source/celery_overview.rst delete mode 100644 docs/source/conf.py delete mode 100644 docs/source/docker.rst delete mode 100644 docs/source/faq.rst delete mode 100644 docs/source/getting_started.rst delete mode 100644 docs/source/index.rst delete mode 100644 docs/source/merlin_commands.rst delete mode 100644 docs/source/merlin_config.rst delete mode 100644 docs/source/merlin_developer.rst delete mode 100644 docs/source/merlin_server.rst delete mode 100644 docs/source/merlin_specification.rst delete mode 100644 docs/source/merlin_variables.rst delete mode 100644 docs/source/merlin_workflows.rst delete mode 100644 docs/source/modules/advanced_topics/advanced_requirements.txt delete mode 100644 docs/source/modules/advanced_topics/advanced_topics.rst delete mode 100644 docs/source/modules/advanced_topics/faker_demo.yaml delete mode 100644 docs/source/modules/before.rst delete mode 100644 docs/source/modules/contribute.rst delete mode 100644 docs/source/modules/hello_world/.gitignore delete mode 100644 docs/source/modules/hello_world/celery.txt delete mode 100644 docs/source/modules/hello_world/hello.yaml delete mode 100644 docs/source/modules/hello_world/hello_world.rst delete mode 100644 docs/source/modules/hello_world/local_out.txt delete mode 100644 docs/source/modules/hello_world/run_out.txt delete mode 100644 docs/source/modules/hello_world/run_workers_out.txt delete mode 100644 docs/source/modules/hello_world/stop_workers.txt delete mode 100644 docs/source/modules/installation/app_docker_rabbit.yaml delete mode 100644 docs/source/modules/installation/app_docker_redis.yaml delete mode 100644 docs/source/modules/installation/app_local_redis.yaml delete mode 100644 docs/source/modules/installation/docker-compose.yml delete mode 100644 docs/source/modules/installation/docker-compose_rabbit.yml delete mode 100644 docs/source/modules/installation/docker-compose_rabbit_redis_tls.yml delete mode 100644 docs/source/modules/installation/installation.rst delete mode 100644 docs/source/modules/introduction.rst delete mode 100644 docs/source/modules/port_your_application.rst delete mode 100644 docs/source/modules/run_simulation/run_simulation.rst delete mode 100644 docs/source/server/commands.rst delete mode 100644 docs/source/server/configuration.rst delete mode 100644 docs/source/spack.rst delete mode 100644 docs/source/virtualenv.rst create mode 100644 docs/tutorial/0_prerequisites.md create mode 100644 docs/tutorial/1_introduction.md create mode 100644 docs/tutorial/2_installation.md create mode 100644 docs/tutorial/3_hello_world.md create mode 100644 docs/tutorial/4_run_simulation.md create mode 100644 docs/tutorial/5_advanced_topics.md create mode 100644 docs/tutorial/6_contribute.md create mode 100644 docs/tutorial/7_port_application.md rename docs/{source/tutorial.rst => tutorial/index.md} (51%) create mode 100644 docs/user_guide/celery.md create mode 100644 docs/user_guide/command_line.md create mode 100644 docs/user_guide/configuration/external_server.md create mode 100644 docs/user_guide/configuration/index.md create mode 100644 docs/user_guide/configuration/merlin_server.md create mode 100644 docs/user_guide/contributing.md create mode 100644 docs/user_guide/docker.md create mode 100644 docs/user_guide/index.md create mode 100644 docs/user_guide/installation.md create mode 100644 docs/user_guide/interpreting_output.md create mode 100644 docs/user_guide/monitoring/index.md create mode 100644 docs/user_guide/monitoring/monitor_for_allocation.md create mode 100644 docs/user_guide/monitoring/queues_and_workers.md create mode 100644 docs/user_guide/monitoring/status_cmds.md create mode 100644 docs/user_guide/running_studies.md create mode 100644 docs/user_guide/specification.md create mode 100644 docs/user_guide/variables.md create mode 100644 merlin/common/dumper.py rename merlin/examples/workflows/openfoam_wf/{openfoam_wf_template.yaml => openfoam_wf_docker_template.yaml} (100%) create mode 100644 merlin/examples/workflows/openfoam_wf_singularity/openfoam_wf.yaml rename merlin/examples/workflows/openfoam_wf_singularity/{openfoam_wf_singularity.yaml => openfoam_wf_singularity_template.yaml} (100%) create mode 100644 merlin/study/status.py create mode 100644 merlin/study/status_constants.py create mode 100644 merlin/study/status_renderers.py create mode 100644 mkdocs.yml create mode 100644 tests/README.md create mode 100644 tests/celery_test_workers.py create mode 100644 tests/conftest.py rename tests/integration/{test_definitions.py => definitions.py} (99%) create mode 100644 tests/unit/study/__init__.py create mode 100644 tests/unit/study/status_test_files/combine_status_files.py create mode 100644 tests/unit/study/status_test_files/shared_tests.py create mode 100644 tests/unit/study/status_test_files/status_test_spec.yaml create mode 100644 tests/unit/study/status_test_files/status_test_study_20230713-000000/dummy.txt create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/cancel_step/MERLIN_STATUS.json create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/cancel_step/status.lock create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/fail_step/MERLIN_STATUS.json create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/fail_step/status.lock create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/just_parameters/GREET.hello.LEAVE.goodbye/MERLIN_STATUS.json create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/just_parameters/GREET.hello.LEAVE.goodbye/status.lock create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/just_parameters/GREET.hola.LEAVE.adios/MERLIN_STATUS.json create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/just_parameters/GREET.hola.LEAVE.adios/status.lock create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/just_samples/MERLIN_STATUS.json create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/just_samples/status.lock create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/merlin_info/samples.csv create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/merlin_info/status_test_spec.expanded.yaml create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/merlin_info/status_test_spec.orig.yaml create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/merlin_info/status_test_spec.partial.yaml create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/params_and_samples/GREET.hello/MERLIN_STATUS.json create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/params_and_samples/GREET.hello/status.lock create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/params_and_samples/GREET.hola/MERLIN_STATUS.json create mode 100644 tests/unit/study/status_test_files/status_test_study_20230717-162921/params_and_samples/GREET.hola/status.lock create mode 100644 tests/unit/study/status_test_files/status_test_variables.py create mode 100644 tests/unit/study/test_celeryadapter.py create mode 100644 tests/unit/study/test_detailed_status.py create mode 100644 tests/unit/study/test_status.py diff --git a/.github/workflows/push-pr_workflow.yml b/.github/workflows/push-pr_workflow.yml index e2d9e164e..eecbf3eeb 100644 --- a/.github/workflows/push-pr_workflow.yml +++ b/.github/workflows/push-pr_workflow.yml @@ -56,9 +56,9 @@ jobs: - name: Lint with Black run: | - python3 -m black --check --line-length $MAX_LINE_LENGTH --target-version py36 merlin - python3 -m black --check --line-length $MAX_LINE_LENGTH --target-version py36 tests - python3 -m black --check --line-length $MAX_LINE_LENGTH --target-version py36 *.py + python3 -m black --check --line-length $MAX_LINE_LENGTH --target-version py38 merlin + python3 -m black --check --line-length $MAX_LINE_LENGTH --target-version py38 tests + python3 -m black --check --line-length $MAX_LINE_LENGTH --target-version py38 *.py - name: Lint with PyLint run: | @@ -129,7 +129,7 @@ jobs: - name: Run pytest over unit test suite run: | - python3 -m pytest tests/unit/ + python3 -m pytest -v --order-scope=module tests/unit/ - name: Run integration test suite for local tests run: | diff --git a/.readthedocs.yaml b/.readthedocs.yaml index ee62bcb5e..c3cfbbe07 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -5,11 +5,11 @@ build: tools: python: "3.8" -sphinx: - configuration: docs/source/conf.py - python: install: - requirements: docs/requirements.txt +mkdocs: + fail_on_warning: false + formats: [pdf] \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d0ef2ae6..c7862710a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,83 @@ All notable changes to Merlin will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.12.0] +### Added +- A new command `merlin queue-info` that will print the status of your celery queues + - By default this will only pull information from active queues + - There are options to look for specific queues (`--specific-queues`), queues defined in certain spec files (`--spec`; this is the same functionality as the `merlin status` command prior to this update), and queues attached to certain steps (`--steps`) + - Queue info can be dumped to outfiles with `--dump` +- A new command `merlin detailed-status` that displays task-by-task status information about your study + - This has options to filter by return code, task queues, task statuses, and workers + - You can set a limit on the number of tasks to display + - There are 3 options to modify the output display +- Docs for all of the monitoring commands +- New file `merlin/study/status.py` dedicated to work relating to the status command + - Contains the Status and DetailedStatus classes +- New file `merlin/study/status_renderers.py` dedicated to formatting the output for the detailed-status command +- New file `merlin/common/dumper.py` containing a Dumper object to help dump output to outfiles +- Study name and parameter info now stored in the DAG and MerlinStep objects +- Added functions to `merlin/display.py` that help display status information: + - `display_task_by_task_status` handles the display for the `merlin detailed-status` command + - `display_status_summary` handles the display for the `merlin status` command + - `display_progress_bar` generates and displays a progress bar +- Added new methods to the MerlinSpec class: + - get_worker_step_map() + - get_queue_step_relationship() + - get_tasks_per_step() + - get_step_param_map() +- Added methods to the MerlinStepRecord class to mark status changes for tasks as they run (follows Maestro's StepRecord format mostly) +- Added methods to the Step class: + - establish_params() + - name_no_params() +- Added a property paramater_labels to the MerlinStudy class +- Added two new utility functions: + - dict_deep_merge() that deep merges two dicts into one + - ws_time_to_dt() that converts a workspace timestring (YYYYMMDD-HHMMSS) to a datetime object +- A new celery task `condense_status_files` to be called when sets of samples finish +- Added a celery config setting `worker_cancel_long_running_tasks_on_connection_loss` since this functionality is about to change in the next version of celery +- Tests for the Status and DetailedStatus classes + - this required adding a decent amount of test files to help with the tests; these can be found under the tests/unit/study/status_test_files directory +- Pytest fixtures in the `conftest.py` file of the integration test suite + - NOTE: an export command `export LC_ALL='C'` had to be added to fix a bug in the WEAVE CI. This can be removed when we resolve this issue for the `merlin server` command +- Tests for the `celeryadapter.py` module +- New CeleryTestWorkersManager context to help with starting/stopping workers for tests + +### Changed +- Reformatted the entire `merlin status` command + - Now accepts both spec files and workspace directories as arguments + - Removed the --steps flag + - Replaced the --csv flag with the --dump flag + - New functionality: + - Shows step_by_step progress bar for tasks + - Displays a summary of task statuses below the progress bar +- Split the `add_chains_to_chord` function in `merlin/common/tasks.py` into two functions: + - `get_1d_chain` which converts a 2D list of chains into a 1D list + - `launch_chain` which launches the 1D chain +- Pulled the needs_merlin_expansion() method out of the Step class and made it a function instead +- Removed `tabulate_info` function; replaced with tabulate from the tabulate library +- Moved `verify_filepath` and `verify_dirpath` from `merlin/main.py` to `merlin/utils.py` +- The entire documentation has been ported to MkDocs and re-organized + - *Dark Mode* + - New "Getting Started" example for a simple setup tutorial + - More detail on configuration instructions + - There's now a full page on installation instructions + - More detail on explaining the spec file + - More detail with the CLI page + - New "Running Studies" page to explain different ways to run studies, restart them, and accomplish command line substitution + - New "Interpreting Output" page to help users understand how the output workspace is generated in more detail + - New "Examples" page has been added + - Updated "FAQ" page to include more links to helpful locations throughout the documentation + - Set up a place to store API docs + - New "Contact" page with info on reaching Merlin devs +- The Merlin tutorial defaults to using Singularity rather than Docker for the OpenFoam example. Minor tutorial fixes have also been applied. + +### Fixed +- The `merlin status` command so that it's consistent in its output whether using redis or rabbitmq as the broker +- The `merlin monitor` command will now keep an allocation up if the queues are empty and workers are still processing tasks +- Add the restart keyword to the specification docs +- Cyclical imports and config imports that could easily cause ci issues + ## [1.11.1] ### Fixed - Typo in `batch.py` that caused lsf launches to fail (`ALL_SGPUS` changed to `ALL_GPUS`) diff --git a/Makefile b/Makefile index 2f9db031b..0470b3332 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ # # LLNL-CODE-797170 # All rights reserved. -# This file is part of Merlin, Version: 1.11.1. +# This file is part of Merlin, Version: 1.12.0. # # For details, see https://github.com/LLNL/merlin. # @@ -87,7 +87,7 @@ install-dev: virtualenv install-merlin install-workflow-deps # tests require a valid dev install of merlin unit-tests: . $(VENV)/bin/activate; \ - $(PYTHON) -m pytest $(UNIT); \ + $(PYTHON) -m pytest -v --order-scope=module $(UNIT); \ # run CLI tests - these require an active install of merlin in a venv @@ -135,9 +135,9 @@ check-flake8: check-black: . $(VENV)/bin/activate; \ - $(PYTHON) -m black --check --line-length $(MAX_LINE_LENGTH) --target-version py36 $(MRLN); \ - $(PYTHON) -m black --check --line-length $(MAX_LINE_LENGTH) --target-version py36 $(TEST); \ - $(PYTHON) -m black --check --line-length $(MAX_LINE_LENGTH) --target-version py36 *.py; \ + $(PYTHON) -m black --check --line-length $(MAX_LINE_LENGTH) --target-version py38 $(MRLN); \ + $(PYTHON) -m black --check --line-length $(MAX_LINE_LENGTH) --target-version py38 $(TEST); \ + $(PYTHON) -m black --check --line-length $(MAX_LINE_LENGTH) --target-version py38 *.py; \ check-isort: @@ -179,9 +179,9 @@ fix-style: $(PYTHON) -m isort -w $(MAX_LINE_LENGTH) $(MRLN); \ $(PYTHON) -m isort -w $(MAX_LINE_LENGTH) $(TEST); \ $(PYTHON) -m isort -w $(MAX_LINE_LENGTH) *.py; \ - $(PYTHON) -m black --target-version py36 -l $(MAX_LINE_LENGTH) $(MRLN); \ - $(PYTHON) -m black --target-version py36 -l $(MAX_LINE_LENGTH) $(TEST); \ - $(PYTHON) -m black --target-version py36 -l $(MAX_LINE_LENGTH) *.py; \ + $(PYTHON) -m black --target-version py38 -l $(MAX_LINE_LENGTH) $(MRLN); \ + $(PYTHON) -m black --target-version py38 -l $(MAX_LINE_LENGTH) $(TEST); \ + $(PYTHON) -m black --target-version py38 -l $(MAX_LINE_LENGTH) *.py; \ # Increment the Merlin version. USE ONLY ON DEVELOP BEFORE MERGING TO MASTER. diff --git a/README.md b/README.md index 909ba869a..e0f1ca4ff 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ HPC batch systems, since it can scale to a very large number of jobs. The integrated system looks a little something like this: -a typical Merlin workflow +![A Typical Merlin Workflow](docs/assets/images/merlin_arch.png) In this example, here's how it all works: diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 662696c6f..000000000 --- a/docs/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SPHINXPROJ = Merlin -SOURCEDIR = source -BUILDDIR = build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -code-docs: - sphinx-apidoc -f -o source/ ../merlin/ - -view: code-docs html - firefox -new-instance build/html/index.html - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - pip install -r requirements.txt - echo $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -clean: - rm -rf build/ diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..cbddc54a4 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,44 @@ +# Guide to Merlin Documentation + +Merlin uses [MkDocs](https://www.mkdocs.org/) to generate its documentation and [Read the Docs](https://about.readthedocs.com/?ref=readthedocs.com) to host it. This README will detail important information on handling the documentation. + +## How to Build the Documentation + +Ensure you're at the root of the Merlin repository: + +```bash +cd /path/to/merlin/ +``` + +Install the documentation with: + +```bash +pip install -r docs/requirements.txt +``` + +Build the documentation with: + +```bash +mkdocs serve +``` + +Once up and running, MkDocs should provide a message telling you where your browser is connected (this is typically `http://127.0.0.1:8000/`). If you're using VSCode, you should be able to `ctrl+click` on the address to open the browser window. An example is shown below: + +```bash +(venv_name) [user@machine:merlin]$ mkdocs serve +INFO - Building documentation... +INFO - Cleaning site directory +WARNING - Excluding 'README.md' from the site because it conflicts with 'index.md'. +WARNING - A relative path to 'api_reference/' is included in the 'nav' configuration, which is not found in the documentation files. +INFO - Documentation built in 3.24 seconds +INFO - [09:16:00] Watching paths for changes: 'docs', 'mkdocs.yml' +INFO - [09:16:00] Serving on http://127.0.0.1:8000/ +``` + +## Configuring the Documentation + +MkDocs relies on an `mkdocs.yml` file for almost everything to do with configuration. See [their Configuration documentation](https://www.mkdocs.org/user-guide/configuration/) for more information. + +## How Do API Docs Work? + +Coming soon... diff --git a/docs/api_reference/index.md b/docs/api_reference/index.md new file mode 100644 index 000000000..6707457de --- /dev/null +++ b/docs/api_reference/index.md @@ -0,0 +1,5 @@ +# Merlin API Reference + +Coming soon! + + diff --git a/docs/assets/images/hello-samples-tree.png b/docs/assets/images/hello-samples-tree.png new file mode 100644 index 0000000000000000000000000000000000000000..8893874bfea5f52d7d04c0e123e6a9ec8cca352a GIT binary patch literal 124951 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%0Dh9eAw7nDOAr}znprA`_huh%llIq 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