diff --git a/docs/Makefile b/docs/Makefile index 1fa18aba7..2ef3093fa 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -228,6 +228,6 @@ pseudoxml: .PHONY: apidoc apidoc: - sphinx-apidoc -o api/ ../src/cosmic ../src/cosmic/tests --no-toc --separate --no-headings --force + sphinx-apidoc -o api/ ../src/cosmic ../src/cosmic/tests --no-toc --separate --no-headings @echo @echo "APIdoc run is complete." diff --git a/docs/_static/cosmic-docs.css b/docs/_static/cosmic-docs.css index d5e2d46c4..991d958f4 100644 --- a/docs/_static/cosmic-docs.css +++ b/docs/_static/cosmic-docs.css @@ -88,17 +88,6 @@ html[data-theme="dark"] .sd-card { background-color: #e193ca; } -@media (max-width: 720px) { - .toms-nav-container { - width: 100%; - height: auto; - grid-template-columns: 100%; - grid-template-rows: auto; - grid-column-gap: 0; - grid-row-gap: 20px; - } -} - #installation h2 { font-size: 1.2rem; } @@ -552,4 +541,59 @@ select.form-control { .link-white { color: white!important; +} + +.content:has(.tutorial-card) { + width: 100%; + padding: 0 7em; +} + +.content:has(.tutorial-card) h1 { + text-align: center; + margin-left: 100px; +} + +.center { + text-align: center; +} + +.tutorial-card { + text-align: center!important; +} + +.tutorial-card-title { + font-size: 2rem!important; + font-weight: bold!important; + margin-bottom: 0.5rem!important; +} + +.tutorial-card-link { + display: block; + padding: .25rem; + font-size: 1rem; + border-radius: 10px; + transition: 300ms; + margin: 0 !important; +} + +.tutorial-card-link:hover { + background-color: var(--color-brand-primary); +} +.tutorial-card-link:hover, .tutorial-card-link:hover a { + color: white!important; +} + +@media (max-width: 720px) { + .toms-nav-container { + width: 100%; + height: auto; + grid-template-columns: 100%; + grid-template-rows: auto; + grid-column-gap: 0; + grid-row-gap: 20px; + } + + .content:has(.tutorial-card) { + padding: 0 1em; + } } \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 90e373e09..d20d1ef71 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -76,6 +76,9 @@ 'remove_config_comments': True, } +import matplotlib +matplotlib.rcParams["savefig.dpi"] = 300 + # -- autosummary -------------------------------- autosummary_generate = True @@ -117,7 +120,7 @@ # General information about the project. project = u'cosmic' -copyright = u'2021, Katie Breivik' +copyright = u'2019-2026, Katie Breivik, Tom Wagg' author = u'Katie Breivik' # The version info for the project you're documenting, acts as replacement for diff --git a/docs/create_settings_html.py b/docs/create_settings_html.py index fba91f5bc..cd847e791 100644 --- a/docs/create_settings_html.py +++ b/docs/create_settings_html.py @@ -11,6 +11,7 @@ import json import pandas as pd from cosmic import __version__ as cosmic_version +from os.path import exists # how many versions in the past should we show as badges for when options were added? VERSION_CUTOFFS = { @@ -225,6 +226,15 @@ def version_is_recent(version_string): # append this group to the soup (guess who's a poet and didn't even know it) soup.select_one(".container-fluid").append(new_group) - # write the soup out to an HTML file for this category - with open(f"_generated/config_insert_{group['category']}.html", "w") as f: - f.write(str(soup)) + # write the soup out to an HTML file for this category (only if changed) + out_path = f"_generated/config_insert_{group['category']}.html" + content = str(soup) + if exists(out_path): + with open(out_path) as f: + if f.read() == content: + print(f" No changes to {out_path}, skipping write.") + continue + + print(f" Writing settings HTML for category '{group['category']}' to {out_path}...") + with open(out_path, "w") as f: + f.write(content) diff --git a/docs/create_whats_new.py b/docs/create_whats_new.py index 051afc213..76577c4ab 100644 --- a/docs/create_whats_new.py +++ b/docs/create_whats_new.py @@ -11,6 +11,7 @@ import re from pathlib import Path +from os.path import exists def parse_changelog(changelog_path): with open(changelog_path, 'r') as f: @@ -37,23 +38,30 @@ def parse_changelog(changelog_path): return parsed_sections def generate_rst(parsed_sections, output_path): - with open(output_path, 'w') as f: - f.write("What's New in COSMIC\n") - f.write("====================\n\n") + lines = ["What's New in COSMIC\n====================\n"] - for heading, features in parsed_sections: + for heading, features in parsed_sections: + lines.append(f"v{heading}\n{'-' * (len(heading) + 1)}\n") - f.write(f"v{heading}\n") - f.write(f"{'-' * (len(heading) + 1)}\n\n") + # Check if the heading is a version number (e.g., "4.1.0") + if re.match(r'^\d+\.\d+\.\d+$', heading): + github_link = f"https://github.com/COSMIC-PopSynth/COSMIC/releases/tag/v{heading}" + lines.append(f"`GitHub release <{github_link}>`_\n") - # Check if the heading is a version number (e.g., "4.1.0") - if re.match(r'^\d+\.\d+\.\d+$', heading): - github_link = f"https://github.com/COSMIC-PopSynth/COSMIC/releases/tag/v{heading}" - f.write(f"`GitHub release <{github_link}>`_\n\n") + for feature in features: + lines.append(feature) + lines.append("") - for feature in features: - f.write(feature + "\n") - f.write("\n") + content = "\n".join(lines) + if exists(output_path): + with open(output_path) as f: + if f.read() == content: + print(f" No changes to {output_path}, skipping write.") + return + + print(f" Writing 'What's New' content to {output_path}...") + with open(output_path, "w") as f: + f.write(content) if __name__ == "__main__": changelog_path = Path("../changelog.md") @@ -61,5 +69,3 @@ def generate_rst(parsed_sections, output_path): parsed_sections = parse_changelog(changelog_path) generate_rst(parsed_sections, output_path) - - print(f"Generated {output_path} from {changelog_path}") \ No newline at end of file diff --git a/docs/generate_default_bsedict.py b/docs/generate_default_bsedict.py index 310c00b12..33ac121d8 100644 --- a/docs/generate_default_bsedict.py +++ b/docs/generate_default_bsedict.py @@ -1,5 +1,6 @@ import json from pathlib import Path +from os.path import exists def get_default_BSE_settings(to_python=False): """Get a copy of the default BSE settings from the COSMIC settings JSON file""" @@ -45,33 +46,43 @@ def generate_rst_bsedict(MAX_LINE_LENGTH=80): indent_str = " " * 8 - with open("_generated/default_bsedict.rst", "w") as f: - lines = [ - ".. ipython:: python", - "", - " BSEDict = {", - ] - - first = True - current_line = indent_str - for k, v in defaults.items(): - entry = f'"{k}": {v}' - if not first: - entry = ", " + entry - first = False - - # check if adding this entry would exceed line length - if len(current_line) + len(entry) > MAX_LINE_LENGTH: - lines.append(current_line.rstrip() + ",") - current_line = indent_str + entry.lstrip(", ") - else: - current_line += entry + lines = [ + ".. ipython:: python", + "", + " BSEDict = {", + ] + + first = True + current_line = indent_str + for k, v in defaults.items(): + entry = f'"{k}": {v}' + if not first: + entry = ", " + entry + first = False + + # check if adding this entry would exceed line length + if len(current_line) + len(entry) > MAX_LINE_LENGTH: + lines.append(current_line.rstrip() + ",") + current_line = indent_str + entry.lstrip(", ") + else: + current_line += entry + + if current_line.strip(): + lines.append(current_line.rstrip()) + lines.append(" }") + + content = "\n".join(lines) + out_path = "_generated/default_bsedict.rst" - if current_line.strip(): - lines.append(current_line.rstrip()) - lines.append(" }") + if exists(out_path): + with open(out_path) as f: + if f.read() == content: + print(f" No changes to {out_path}, skipping write.") + return - f.write("\n".join(lines)) + with open(out_path, "w") as f: + print(f" Writing default BSE settings to {out_path}...") + f.write(content) if __name__ == "__main__": generate_rst_bsedict() \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 7b432791f..fb96e73cb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,17 +23,14 @@ Welcome to COSMIC! :alt: COSMIC logo .. toctree:: - :maxdepth: 2 + :maxdepth: 3 :hidden: pages/about pages/install pages/reference_material - pages/examples - pages/runpop - pages/fixedpop - pages/multiprocessing + pages/tutorials + pages/api pages/cite - pages/developers _generated/whats_new - pages/api + pages/developers diff --git a/docs/pages/inifile.rst b/docs/pages/config_and_output/inifile.rst similarity index 100% rename from docs/pages/inifile.rst rename to docs/pages/config_and_output/inifile.rst diff --git a/docs/pages/output_info.rst b/docs/pages/config_and_output/output_info.rst similarity index 100% rename from docs/pages/output_info.rst rename to docs/pages/config_and_output/output_info.rst diff --git a/docs/pages/examples.rst b/docs/pages/examples.rst deleted file mode 100644 index c35e30ff5..000000000 --- a/docs/pages/examples.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. _examples: - -################# -Evolving binaries -################# - -COSMIC can evolve binaries for several different use cases. Go through the examples below to learn more about these capabilities. - -.. toctree:: - :maxdepth: 1 - - evolve/single - evolve/multiple - evolve/grid - evolve/evolve_sample - evolve/resolution - evolve/rerun - evolve/restart - evolve/interface - diff --git a/docs/pages/reference_material.rst b/docs/pages/reference_material.rst index 618385cfe..fc172f54d 100644 --- a/docs/pages/reference_material.rst +++ b/docs/pages/reference_material.rst @@ -14,7 +14,7 @@ Configuration and output .. toctree:: :maxdepth: 1 - inifile.rst + config_and_output/inifile.rst Instructions for creating configurations files and explanations of each option. @@ -25,7 +25,7 @@ Configuration and output .. toctree:: :maxdepth: 1 - output_info.rst + config_and_output/output_info.rst Descriptions of the output files generated by COSMIC simulations. diff --git a/docs/pages/tutorials.rst b/docs/pages/tutorials.rst new file mode 100644 index 000000000..5d1892b40 --- /dev/null +++ b/docs/pages/tutorials.rst @@ -0,0 +1,129 @@ +:html_theme.sidebar_secondary.items: [] + +Tutorials +========= + +.. rst-class:: center + +This page contains of tutorials that show you how to use ``COSMIC`` and become an expert in its various features! The tutorials are split into different categories, which you can navigate to using the links below. + +.. grid:: 1 1 2 2 + :gutter: 3 + + .. grid-item-card:: + + .. container:: tutorial-card + + .. rubric:: Evolving binaries + :class: tutorial-card-title + + .. rst-class:: tutorial-card-link + + :ref:`evolve_single` + + .. rst-class:: tutorial-card-link + + :ref:`evolve_multiple` + + .. rst-class:: tutorial-card-link + + :ref:`evolve_grid` + + .. rst-class:: tutorial-card-link + + :ref:`evolve_sample` + + .. grid-item-card:: + + .. container:: tutorial-card + + .. rubric:: Sampling binaries + :class: tutorial-card-title + + .. rst-class:: tutorial-card-link + + :ref:`independent` + + .. rst-class:: tutorial-card-link + + :ref:`multidim` + + .. rst-class:: tutorial-card-link + + :ref:`cmc_sampling` + + + .. grid-item-card:: + + .. container:: tutorial-card + + .. rubric:: Converged populations + :class: tutorial-card-title + + .. rst-class:: tutorial-card-link + + :ref:`fixedpop` + + .. grid-item-card:: + + .. container:: tutorial-card + + .. rubric:: Re-running simulations + :class: tutorial-card-title + + .. rst-class:: tutorial-card-link + + :ref:`rerun_rerun` + + .. rst-class:: tutorial-card-link + + :ref:`rerun_restart` + + .. grid-item-card:: + + .. container:: tutorial-card + + .. rubric:: Modifying timesteps + :class: tutorial-card-title + + .. rst-class:: tutorial-card-link + + :ref:`timesteps_resolution` + + .. rst-class:: tutorial-card-link + + :ref:`timesteps_modifiers` + + .. grid-item-card:: + + .. container:: tutorial-card + + .. rubric:: Analysing simulations + :class: tutorial-card-title + + .. rst-class:: tutorial-card-link + + :ref:`analysis_interface` + + .. grid-item-card:: + + .. container:: tutorial-card + + .. rubric:: Miscellaneous + :class: tutorial-card-title + + .. rst-class:: tutorial-card-link + + :ref:`multiprocessing` + +.. toctree:: + :maxdepth: 2 + :hidden: + + tutorials/evolve + tutorials/sample + tutorials/convergence + tutorials/rerun + tutorials/timesteps + tutorials/analysis + tutorials/misc \ No newline at end of file diff --git a/docs/pages/tutorials/analysis.rst b/docs/pages/tutorials/analysis.rst new file mode 100644 index 000000000..c57f5db69 --- /dev/null +++ b/docs/pages/tutorials/analysis.rst @@ -0,0 +1,10 @@ +############### +Output analysis +############### + +These tutorials will cover how to easily analyse your ``COSMIC`` output data with a simple interface that can mask all of the output tables together. + +.. toctree:: + :maxdepth: 2 + + analysis/interface \ No newline at end of file diff --git a/docs/pages/evolve/interface.rst b/docs/pages/tutorials/analysis/interface.rst similarity index 96% rename from docs/pages/evolve/interface.rst rename to docs/pages/tutorials/analysis/interface.rst index 30ea70f7c..e54a91db7 100644 --- a/docs/pages/evolve/interface.rst +++ b/docs/pages/tutorials/analysis/interface.rst @@ -1,3 +1,5 @@ +.. _analysis_interface: + ************************** Analysing your simulations ************************** @@ -26,7 +28,7 @@ samples ~100 binaries and evolving them. import matplotlib.pyplot as plt import numpy as np -.. include:: ../../_generated/default_bsedict.rst +.. include:: ../../../_generated/default_bsedict.rst .. ipython:: python @@ -89,10 +91,6 @@ First, let's plot the initial mass distribution of the primary stars in our bina @savefig initial_mass_distribution.png plt.show() -.. image:: initial_mass_distribution.png - :alt: - :width: 100% - We could also compare this to the final mass distribution of the primary stars after evolution. .. ipython:: python @@ -103,10 +101,6 @@ We could also compare this to the final mass distribution of the primary stars a @savefig final_mass_distribution.png plt.show() -.. image:: final_mass_distribution.png - :alt: - :width: 100% - In addition to histograms, you can create scatter plots to visualize relationships between different parameters. .. ipython:: python @@ -120,14 +114,10 @@ In addition to histograms, you can create scatter plots to visualize relationshi ax.set_xscale("log") ax.set_yscale("log") ax.invert_xaxis() - + @savefig hrd.png plt.show() -.. image:: hrd.png - :alt: - :width: 100% - And we also can colour the points by the stellar type of the primary star at the end of evolution and get a custom colour map for these ones. @@ -141,14 +131,10 @@ colour map for these ones. ); ax.set_xscale("log") ax.set_yscale("log") - + @savefig m1_porb_kstar.png plt.show() -.. image:: m1_porb_kstar.png - :alt: - :width: 100% - Any column name from the ``bpp`` (or ``initC`` for initial conditions) DataFrames can be used for the x, y, and colour col names. Subselecting binaries @@ -204,10 +190,6 @@ Now clearly staring at the DataFrame isn't very helpful, so let's plot the evolu @savefig detailed_bh.png plt.show() -.. image:: detailed_bh.png - :alt: - :width: 100% - This shows the full evolution, but we can ignore any time a while after the binary forms a BH by setting a maximum time for the x-axis. @@ -223,10 +205,6 @@ by setting a maximum time for the x-axis. @savefig detailed_bh_limited.png plt.show() -.. image:: detailed_bh_limited.png - :alt: - :width: 100% - Re-running with new physics settings ==================================== diff --git a/docs/pages/tutorials/convergence.rst b/docs/pages/tutorials/convergence.rst new file mode 100644 index 000000000..5fd0fc30a --- /dev/null +++ b/docs/pages/tutorials/convergence.rst @@ -0,0 +1,10 @@ +##################### +Converged populations +##################### + +``COSMIC`` provides utilities for creating a converged population of binaries. Follow the tutorials below to learn how to do this! + +.. toctree:: + :maxdepth: 2 + + convergence/cosmic_pop \ No newline at end of file diff --git a/docs/pages/fixedpop.rst b/docs/pages/tutorials/convergence/cosmic_pop.rst similarity index 100% rename from docs/pages/fixedpop.rst rename to docs/pages/tutorials/convergence/cosmic_pop.rst diff --git a/docs/pages/tutorials/evolve.rst b/docs/pages/tutorials/evolve.rst new file mode 100644 index 000000000..323cf6d0c --- /dev/null +++ b/docs/pages/tutorials/evolve.rst @@ -0,0 +1,14 @@ +.. _examples: + +Evolving binaries +================= + +``COSMIC`` can evolve binaries for several different use cases. Go through the examples below to learn more about these capabilities. + +.. toctree:: + :maxdepth: 2 + + evolve/single + evolve/multiple + evolve/grid + evolve/evolve_sample \ No newline at end of file diff --git a/docs/pages/evolve/evolve_sample.rst b/docs/pages/tutorials/evolve/evolve_sample.rst similarity index 95% rename from docs/pages/evolve/evolve_sample.rst rename to docs/pages/tutorials/evolve/evolve_sample.rst index 9c5766aae..b995c84dd 100644 --- a/docs/pages/evolve/evolve_sample.rst +++ b/docs/pages/tutorials/evolve/evolve_sample.rst @@ -1,6 +1,9 @@ +.. _evolve_sample: + ***************************************** Evolving a Monte-Carlo sampled population ***************************************** + Once an initial binary population is sampled, it can be evolved using the ``Evolve`` class just as we've done so far. You can read more about sampling initial binary populations in the :ref:`runpop` page. @@ -34,7 +37,7 @@ about the independent sampler in the :ref:`independent` page. And finally, we can evolve the initial binary population using the Evolve class as we've done in the previous guides: -.. include:: ../../_generated/default_bsedict.rst +.. include:: ../../../_generated/default_bsedict.rst .. ipython:: python :okwarning: diff --git a/docs/pages/evolve/grid.rst b/docs/pages/tutorials/evolve/grid.rst similarity index 94% rename from docs/pages/evolve/grid.rst rename to docs/pages/tutorials/evolve/grid.rst index 8a0b8bb90..1538e5081 100644 --- a/docs/pages/evolve/grid.rst +++ b/docs/pages/tutorials/evolve/grid.rst @@ -1,3 +1,5 @@ +.. _evolve_grid: + ********************************* Evolving a fixed grid of binaries ********************************* @@ -12,7 +14,7 @@ setting up the BSEDict settings as we've done in the previous examples. from cosmic.evolve import Evolve -.. include:: ../../_generated/default_bsedict.rst +.. include:: ../../../_generated/default_bsedict.rst Here we evolve the same system that produces a GW150914-like binary, but run over several initial orbital diff --git a/docs/pages/evolve/multiple.rst b/docs/pages/tutorials/evolve/multiple.rst similarity index 96% rename from docs/pages/evolve/multiple.rst rename to docs/pages/tutorials/evolve/multiple.rst index 603b47a03..77739080c 100644 --- a/docs/pages/evolve/multiple.rst +++ b/docs/pages/tutorials/evolve/multiple.rst @@ -1,3 +1,5 @@ +.. _evolve_multiple: + ************************** Evolving multiple binaries ************************** @@ -18,7 +20,7 @@ And use the same SSE and BSE dictionaries as before: SSEDict = {'stellar_engine': 'sse'} -.. include:: ../../_generated/default_bsedict.rst +.. include:: ../../../_generated/default_bsedict.rst Below is an example for systems that could form GW150914 and GW170817 - like binaries. diff --git a/docs/pages/evolve/single.rst b/docs/pages/tutorials/evolve/single.rst similarity index 99% rename from docs/pages/evolve/single.rst rename to docs/pages/tutorials/evolve/single.rst index e71fbe797..cd1961673 100644 --- a/docs/pages/evolve/single.rst +++ b/docs/pages/tutorials/evolve/single.rst @@ -1,3 +1,5 @@ +.. _evolve_single: + ************************ Evolving a single binary ************************ @@ -82,7 +84,7 @@ advised to run the defaults from the COSMIC install which are consistent with `Breivik+2020 `_, though we don't promise that these are the most up-to-date prescriptions. -.. include:: ../../_generated/default_bsedict.rst +.. include:: ../../../_generated/default_bsedict.rst Running a binary diff --git a/docs/pages/tutorials/misc.rst b/docs/pages/tutorials/misc.rst new file mode 100644 index 000000000..5b96f7c25 --- /dev/null +++ b/docs/pages/tutorials/misc.rst @@ -0,0 +1,10 @@ +############# +Miscellaneous +############# + +And finally, here's everything else that didn't fit into the other categories! + +.. toctree:: + :maxdepth: 2 + + misc/multiprocessing \ No newline at end of file diff --git a/docs/pages/multiprocessing.rst b/docs/pages/tutorials/misc/multiprocessing.rst similarity index 100% rename from docs/pages/multiprocessing.rst rename to docs/pages/tutorials/misc/multiprocessing.rst diff --git a/docs/pages/tutorials/rerun.rst b/docs/pages/tutorials/rerun.rst new file mode 100644 index 000000000..f3b51e7c1 --- /dev/null +++ b/docs/pages/tutorials/rerun.rst @@ -0,0 +1,11 @@ +###################### +Re-running simulations +###################### + +``COSMIC`` can re-run binaries that have already been evolved previously. This allows users to either re-run identical simulations from others, re-run those same initial conditions with modified settings, or restart a binary midway through its evolution. Follow the tutorials below to learn how to do this! + +.. toctree:: + :maxdepth: 2 + + rerun/rerun + rerun/restart \ No newline at end of file diff --git a/docs/pages/evolve/rerun.rst b/docs/pages/tutorials/rerun/rerun.rst similarity index 98% rename from docs/pages/evolve/rerun.rst rename to docs/pages/tutorials/rerun/rerun.rst index 6b1013c02..441970ecb 100644 --- a/docs/pages/evolve/rerun.rst +++ b/docs/pages/tutorials/rerun/rerun.rst @@ -1,3 +1,5 @@ +.. _rerun_rerun: + ******************* Re-running binaries ******************* @@ -20,7 +22,7 @@ First, let's evolve a binary and save the initC table. from cosmic.sample.initialbinarytable import InitialBinaryTable from cosmic.evolve import Evolve -.. include:: ../../_generated/default_bsedict.rst +.. include:: ../../../_generated/default_bsedict.rst .. ipython:: python diff --git a/docs/pages/evolve/restart.rst b/docs/pages/tutorials/rerun/restart.rst similarity index 98% rename from docs/pages/evolve/restart.rst rename to docs/pages/tutorials/rerun/restart.rst index f90431c51..06944814a 100644 --- a/docs/pages/evolve/restart.rst +++ b/docs/pages/tutorials/rerun/restart.rst @@ -1,3 +1,5 @@ +.. _rerun_restart: + ******************* Restarting a binary ******************* @@ -27,7 +29,7 @@ started from the beginning and three different points in the evolution: SSEDict = {'stellar_engine': 'sse'} -.. include:: ../../_generated/default_bsedict.rst +.. include:: ../../../_generated/default_bsedict.rst .. ipython:: python :okwarning: diff --git a/docs/pages/runpop.rst b/docs/pages/tutorials/sample.rst similarity index 100% rename from docs/pages/runpop.rst rename to docs/pages/tutorials/sample.rst diff --git a/docs/pages/sample/cluster.rst b/docs/pages/tutorials/sample/cluster.rst similarity index 98% rename from docs/pages/sample/cluster.rst rename to docs/pages/tutorials/sample/cluster.rst index d175baf0b..f76e3bad6 100644 --- a/docs/pages/sample/cluster.rst +++ b/docs/pages/tutorials/sample/cluster.rst @@ -1,3 +1,5 @@ +.. _cmc_sampling: + ************************** ClusterMonteCarlo Sampling ************************** diff --git a/docs/pages/sample/independent.rst b/docs/pages/tutorials/sample/independent.rst similarity index 100% rename from docs/pages/sample/independent.rst rename to docs/pages/tutorials/sample/independent.rst diff --git a/docs/pages/sample/multidim.rst b/docs/pages/tutorials/sample/multidim.rst similarity index 99% rename from docs/pages/sample/multidim.rst rename to docs/pages/tutorials/sample/multidim.rst index a9e6f0651..fbe5999d9 100644 --- a/docs/pages/sample/multidim.rst +++ b/docs/pages/tutorials/sample/multidim.rst @@ -1,3 +1,5 @@ +.. _multidim: + ****************************** Multidimensional distributions ****************************** diff --git a/docs/pages/tutorials/timesteps.rst b/docs/pages/tutorials/timesteps.rst new file mode 100644 index 000000000..51e8e4c9c --- /dev/null +++ b/docs/pages/tutorials/timesteps.rst @@ -0,0 +1,11 @@ +######################## +Timesteps and resolution +######################## + +``COSMIC`` is flexible and allows users to modify both the internal timesteps used by the code, as well as the resolution of the output data. Follow the tutorials below to learn how to do this! + +.. toctree:: + :maxdepth: 2 + + timesteps/resolution + timesteps/modifiers \ No newline at end of file diff --git a/docs/pages/tutorials/timesteps/modifiers.rst b/docs/pages/tutorials/timesteps/modifiers.rst new file mode 100644 index 000000000..ef468dd23 --- /dev/null +++ b/docs/pages/tutorials/timesteps/modifiers.rst @@ -0,0 +1,195 @@ +.. _timesteps_modifiers: + +**************************** +Modifying internal timesteps +**************************** + +In addition to modifying the resolution of the output data, users can also modify the internal timesteps used by ``COSMIC``. + +Changing the default timesteps (not recommended) +------------------------------------------------ + +By default, ``COSMIC`` timesteps are set by the user-specified settings ``pts1``, ``pts2``, and ``pts3`` (see :ref:`inifile` for more details). These specify timesteps for different evolutionary phases of the binary. + +One could edit these directly in the BSEDict as we've seen in other tutorials. + +.. ipython:: python + + from cosmic.sample.initialbinarytable import InitialBinaryTable + from cosmic.evolve import Evolve + + single_binary = InitialBinaryTable.InitialBinaries( + m1=85.543645, m2=84.99784, porb=446.795757, + ecc=0.448872, tphysf=13700.0, + kstar1=1, kstar2=1, metallicity=0.002 + ) + + SSEDict = {'stellar_engine': 'sse'} + +.. include:: ../../../_generated/default_bsedict.rst + +.. ipython:: python + + bpp, bcm, initC, kick_info = Evolve.evolve( + initialbinarytable=single_binary, + BSEDict=BSEDict, + SSEDict=SSEDict + ) + + # take twice as large timesteps for all phases + BSEDict['pts1'] *= 2 + BSEDict['pts2'] *= 2 + BSEDict['pts3'] *= 2 + + bpp_mod, bcm_mod, initC_mod, kick_info_mod = Evolve.evolve( + initialbinarytable=single_binary, + BSEDict=BSEDict, + SSEDict=SSEDict + ) + +From inspection of the resulting tables, you may start to see strange behaviour if the timesteps are too large. + +Modifying timesteps as a function of mass (recommended) +------------------------------------------------------- + +Users can also specify additional modifiers to these timesteps that will be applied during the evolution. The original BSE timesteps can be modified for higher masses, where extrapolation beyond the original stellar grids can lead to unexpected behaviour. + +These modifiers are specified as ``dt_mass_modifiers`` in the :meth:`~cosmic.evolve.Evolve.evolve` function when performing evolution. They are written as a list of tuples of the form ``(min, max, modifier)``, where the fractional modifier is applied to the default timesteps for stars with masses between ``min`` and ``max``. + +For example, to take half as large timesteps for stars with masses between 50 and 100 solar masses, you can do: + +.. ipython:: python + + dt_mass_modifiers = [(50, 100, 0.5)] + + bpp_mod, bcm_mod, initC_mod, kick_info_mod = Evolve.evolve( + initialbinarytable=single_binary, + BSEDict=BSEDict, + SSEDict=SSEDict, + dt_mass_modifiers=dt_mass_modifiers + ) + +The default choice in ``COSMIC`` is to set ``dt_mass_modifiers = [(40, 70, 0.3), (70, np.inf, 0.1)]``, which we find leads to more numerically stable evolution for very massive stars. + +Example: BH masses +^^^^^^^^^^^^^^^^^^ + +Let's go through an example to see how these modifiers can affect the results of our simulations. Specifically, let's explore the relation between initial mass and BH mass for a high-mass grid. + +First, we can create a grid of 500 single stars with initial masses between 50 and 150 solar masses. + +.. ipython:: python + + import matplotlib.pyplot as plt + import numpy as np + + N_BINARIES = 500 + + # create an initial binary table of single stars + IBT_bh = InitialBinaryTable.InitialBinaries( + m1=np.linspace(50, 150, N_BINARIES), + m2=np.zeros(N_BINARIES), + porb=np.zeros(N_BINARIES), + ecc=np.zeros(N_BINARIES), + kstar1=[1] * N_BINARIES, + kstar2=[0] * N_BINARIES, + metallicity=[0.01] * N_BINARIES, + tphysf=[200] * N_BINARIES, + ) + +Now let's copy our BSEDict but turn of pair-instability supernovae (PISN) to simplify the top end of the plot and make sure any differences are just coming from timesteps. + +.. ipython:: python + + # switch off PISN to simplify the plot + no_PISN_BSEDict = BSEDict.copy() + no_PISN_BSEDict["pisn"] = 0 + + +We can set up 4 different choices of timesteps to compare: + +- The default choice in ``COSMIC``: ``dt_mass_modifiers = [(40, 70, 0.3), (70, np.inf, 0.1)]`` +- Half the default BSE timesteps: ``dt_mass_modifiers = [(0, np.inf, 0.5)]`` +- The default BSE timesteps: ``dt_mass_modifiers = []`` +- Double the default BSE timesteps: ``dt_mass_modifiers = [(0, np.inf, 2)]`` + +.. ipython:: python + + bpp_timesteps = [] + labels = ['Default dt_mass_modifiers', 'Half BSE', + 'BSE defaults', 'Double BSE'] + dt_mass_modifiers_vals = [ + [(40, 70, 0.3), (70, np.inf, 0.1)], + [(0, np.inf, 0.5)], + [], + [(0, np.inf, 2)] + ] + +Now let's use those to evolve our grid of single stars 4 different times and store the resulting BPPs. + +.. ipython:: python + + for dtmm in dt_mass_modifiers_vals: + bpp, _, _, _ = Evolve.evolve( + initialbinarytable=IBT_bh, + SSEDict=SSEDict, + BSEDict=BSEDict, + dt_mass_modifiers=dtmm, + ) + bpp_timesteps.append(bpp) + +With our evolution done, let's find the final BH mass and the corresponding initial mass in each grid for plotting. + +.. ipython:: python + + m_inits = [] + m_bhs = [] + + for bpp in bpp_timesteps: + bh = bpp.loc[bpp['kstar_1'] == 14].groupby(level=0).last() + init = IBT_bh.loc[bh.index, 'mass_1'] + m_init, m_bh = init.values, bh['mass_1'].values + m_inits.append(m_init) + m_bhs.append(m_bh) + +And finally, let's plot the results! + +.. ipython:: python + + plt.rc('font', family='serif') + plt.rcParams['text.usetex'] = False + fs = 24 + params = {'figure.figsize': (12, 8), + 'legend.fontsize': 0.7*fs, + 'legend.title_fontsize': 0.8*fs, + 'axes.labelsize': fs, + 'xtick.labelsize': 0.9 * fs, + 'ytick.labelsize': 0.9 * fs, + 'axes.linewidth': 1.1, + 'xtick.major.size': 7, + 'xtick.minor.size': 4, + 'ytick.major.size': 7, + 'ytick.minor.size': 4} + plt.rcParams.update(params) + + fig, axes = plt.subplots(2, 1, figsize=(14, 8), gridspec_kw={'height_ratios': [3, 1]}) + + for m_init, m_bh, label, zorder in zip(m_inits, m_bhs, labels, [100, 50, 25, 1]): + axes[0].plot(m_init, m_bh, zorder=zorder, label=label) + axes[0].legend(); + + for m_init, m_bh in zip(m_inits, m_bhs): + axes[1].plot(m_init, m_bh - m_bhs[0]) + axes[1].set_xlabel('Initial primary mass $M_1^\\mathrm{init}$ [$M_\\odot$]'); + + for ax in axes: + ax.grid(True, lw=0.4, alpha=0.4); + axes[0].set_ylabel('BH mass [$M_\\odot$]'); + axes[1].set_ylabel('Difference [$M_\\odot$]'); + + plt.tight_layout(); + + @savefig dt_mass_modifiers.png + plt.show() + +So we can see here that there are clear numerical artifacts in the relation between initial mass and BH mass when using the default BSE timesteps (and especially when they are larger!). This motivated our choice of the default ``dt_mass_modifiers`` in ``COSMIC`` to ensure more stable evolution for very massive stars. \ No newline at end of file diff --git a/docs/pages/evolve/resolution.rst b/docs/pages/tutorials/timesteps/resolution.rst similarity index 97% rename from docs/pages/evolve/resolution.rst rename to docs/pages/tutorials/timesteps/resolution.rst index 4882ab865..f5e565db5 100644 --- a/docs/pages/evolve/resolution.rst +++ b/docs/pages/tutorials/timesteps/resolution.rst @@ -1,3 +1,5 @@ +.. _timesteps_resolution: + *********************** Dynamic time resolution *********************** @@ -42,7 +44,7 @@ First, print all time steps during mass transfer SSEDict = {'stellar_engine': 'sse'} -.. include:: ../../_generated/default_bsedict.rst +.. include:: ../../../_generated/default_bsedict.rst .. ipython:: python :okwarning: